PyQGIS开发--普通地图布局案例
创始人
2025-05-29 07:54:14

前言

我一直在断断续续地尝试使用Python为QGIS3创建和导出打印布局。或简称PyQGIS 3。我终于弄清楚了这个过程的来龙去脉,希望这能为其他人节省大量的精力和时间。

由于一个打印布局可以进行几乎无限的定制,我将详细介绍很多小细节,并举例说明。你可以根据自己的需要进行混搭。我还将深入到文档中,向您展示我如何引用各种类方法、属性等。

  1. 布局内容

人们添加到打印布局中的常见内容包括地图、图例、标签、比例尺、北箭头等。在本例中,我将介绍地图、图例和标签。这里是对QgsLayoutItem类文档的参考。您将看到这个类是所有QgsLayoutItems的构造函数,其中有很多。下面是继承图:

  1. 新建布局

project = QgsProject.instance()         manager = project.layoutManager()       layout = QgsPrintLayout(project)        layoutName = "PrintLayout"#initializes default settings for blank print layout canvaslayout.initializeDefaults()  layout.setname(layoutName)manager.addLayout(layout)

我创建了一个项目,它是对当前Qgs项目的引用。manager对象包含当前项目的布局管理器(layoutManager)。我创建一个布局并初始化打印布局的默认设置(大小、布局等)。

如果您运行上述代码,请在“项目”>“打印布局”下查看,您将看到一个新的打印布局,该布局当前为空。

在我继续之前,上面的代码是有效的,但只有一次。再次看到我创建了一个名为layoutName的打印布局对象。如果我再次运行此代码,将收到一条错误消息。一种解决方法是重命名打印布局(例如“PrintLayout2”),但我觉得这很烦人。此外,在测试中,您可能会多次运行此代码,创建一个不断增长的测试打印布局列表,稍后您必须清理这些列表。因此,让我们解决这个问题:

layouts_list = manager.printLayouts()for layout in layouts_list:if layout.name() == layoutName:manager.removeLayout(layout)

此快速修复程序查看管理器对象中的打印布局,并将其存储在layouts_list中。然后我循环浏览每个布局,如果layoutName==是现有布局的名称,请删除它。现在我可以创建一个新的打印布局,有效地覆盖我所拥有的内容。

每个项目都有自己的属性、方法等,这些都很难导航(至少对我来说)。让我们从向打印布局添加地图对象开始。请参见以下代码:

    map = QgsLayoutItemMap(layout)#I have no idea what this does, but it is necessarymap.setRect(20, 20, 20, 20)                                     #Set Map Extent#defines map extent using map coordinatesrectangle = QgsRectangle(1355502, -46398, 1734534, 137094)map.setExtent(rectangle)layout.addLayoutItem(map)#Move & Resize map on print layout canvasmap.attemptMove(QgsLayoutPoint(5, 27, QgsUnitTypes.LayoutMillimeters))map.attemptResize(QgsLayoutSize(239, 178, QgsUnitTypes.LayoutMillimeters))

所以在前面的代码中我创建了一个地图,它是一个 QgsLayoutItemMap 对象。 我很抱歉,但 map.setRect() 方法让我感到困惑,老实说,我无法解释它的作用或为什么它在那里。 但地图必须存在。 参数 (20, 20, 20, 20) 似乎对地图没有影响,但如果不调用此方法,您会收到错误消息。 接下来,我设置地图的范围。 我已经创建了一个 QgsRectangle 对象,并使用我项目中的地图坐标手动定义了范围。 您也可以使用以下方法定义范围,将范围设置为界面地图画布的范围,从而允许您动态更改它。

canvas = iface.mapCanvas()map.setExtent(canvas.extent())layout.addLayoutItem(map)

最后,我移动地图对象并调整其大小。 这两种方法的论点都是不言自明的。 我以毫米为单位移动和调整地图对象的大小。 如果你想做这样的事情,你可以选择以英寸或其他单位工作:

map.attemptMove(QgsLayoutPoints(1, 2, QgsUnitTypes.LayoutInches))
map.attemptResize(QgsLayoutSize(239, 178, QgsUnitTypes.LayoutMillimeters))

现在我得到了一张漂亮的地图,它被框起来并位于打印布局中我想要的位置。 我的看起来像这样:

让我们继续布局项目。 与地图一样,创建我的布局非常棘手。 进行通用布局的基本代码如下所示:

    legend = QgsLayoutItemLegend(layout)legend.setTitle("Legend")layout.addLayoutItem(legend)legend.attemptMove(QgsLayoutPoint(246, 5, QgsUnitTypes.LayoutMillimeters))
  1. 创建图例

与地图项一样,您必须首先创建一个 QgsLayoutItem 并指定要应用它的打印布局。 在这种情况下,我们的项目是一个图例,所以我们使用 QgsLayoutItemLegend()。 我添加了一个标题,这是可选的。 然后将图例项添加到打印布局中。 最后,我将图例移动到我希望它在地图上的位置。 这会在地图中添加一个包含所有图层的图例,包括那些已关闭的图层、附加表格等。根据您的 QGIS 项目,您可能会得到如下结果:

如您所见,图例溢出屏幕。 在我的 QGIS 项目中,我有很多图层。 他们中的大多数是不活跃的,不需要在图例中。 我真的只是想要一些东西。 所以我修改了我的脚本以在我的项目中只包含活动层。 让我们回顾一下(在我创建图例对象之前)。

#Checks layer tree objects and stores them in a list. This includes csv tableschecked_layers = [layer.name() for layer in QgsProject().instance().layerTreeRoot().children() if layer.isVisible()]print(f"Adding {checked_layers} to legend." )#get map layer objects of checked layers by matching their names and store those in a listlayersToAdd = [layer for layer in QgsProject().instance().mapLayers().values() if layer.name() in checked_layers]

在上面的代码片段中,我创建了一个名为 checked_layers 的列表。 在 checked_layers 中,我检查了 QgsProject().instance(),这是我正在使用的当前 QGIS 项目。然后查看 layerTreeRoot(),它包含项目中的所有图层。 最后,.children() 是 layerTreeRoot() 中的实际层本身。 我添加了一个打印语句来打印我正在添加的层,这些层将打印在控制台中。

现在我创建一个新对象 layersToAdd。 在这个对象中,我查看 QgsProject().instance().mapLayers(),它们是 QgsProject 实例中的所有层。 然后我添加 .mapLayers().values()(来自所有地图图层的值)如果这些对象在 checked_layers 中。 我认为这可能是捷径,但现在我有一个所有活动地图层的列表,存储在 layersToAdd 中。

现在,我们可以回去创建图例对象。 我们只想将活动层添加到图例中,所以让我们这样做:

legend = QgsLayoutItemLegend(layout)legend.setTitle("Legend")root = QgsLayerTree()for layer in layersToAdd:#add layer objects to the layer treeroot.addLayer(layer)legend.model().setRootGroup(root)layout.addLayoutItem(legend)legend.attemptMove(QgsLayoutPoint(246, 5, QgsUnitTypes.LayoutMillimeters))

所有行都是相同的,除了我创建一个根对象(它是一个 QgsLayerTree,保存层)并循环遍历 layersToAdd,将它们添加到根对象。 最后,我在图例上设置了 .setRootGroup,它重置了图例模型并使用了一个新模型。

那么让我们看看图例现在的样子,我只是在示例中打开了一层。 但是你可以看到,现在只有活动层被添加到图例中。 这真是太好了!

最后,我要在我的地图中添加一些标签项目。 我发现这些更容易使用,但仍然有它们自己的怪癖。 人们添加到地图的常见内容包括标题和副标题,所以让我们添加它们:

"""This adds labels to the map"""title = QgsLayoutItemLabel(layout)title.setText("Title Here")title.setFont(QFont("Arial", 28))title.adjustSizeToText()layout.addLayoutItem(title)title.attemptMove(QgsLayoutPoint(10, 4, QgsUnitTypes.LayoutMillimeters))subtitle = QgsLayoutItemLabel(layout)subtitle.setText("Subtitle Here")subtitle.setFont(QFont("Arial", 17))subtitle.adjustSizeToText()layout.addLayoutItem(subtitle)subtitle.attemptMove(QgsLayoutPoint(11, 20, QgsUnitTypes.LayoutMillimeters))   #allows moving text box

首先,请注意我们现在正在使用 QgsLayoutItemLabel。 每个 QgsLayoutItem 都有自己的类,如前面的文档继承图中所示。 我们使用非常明显的方法,如 .setText 来定义标签项的内容,并使用 .attemptMove 来定位打印布局上的标签。 我使用 .setFont 设置字体,选择字体样式和大小。 这里比较奇怪的是 .adjustSizeToText()。 基本上你必须定义布局项的大小。 这个 .adjustSizeToText 完全按照它说的去做,将大小设置为相应的文本。 我想不出为什么要将标签对象设置为任何其他大小的情况,但我确信存在这种情况。

让我们来看看我们的打印布局。 事情进展顺利。 我的看起来像这样:

我们的最后一步是将打印布局导出为 .pdf 或图像文件,您可以与他人共享。 我发现这是该过程中最简单的部分。

 """This exports a Print Layout as an image"""#this accesses a specific layout, by name (which is a string)layout = manager.layoutByName(layoutName)#this creates a QgsLayoutExporter objectexporter = QgsLayoutExporter(layout)                #this exports a pdf of the layout objectexporter.exportToPdf('/Users/ep9k/Desktop/TestLayout.pdf', QgsLayoutExporter.PdfExportSettings())      #this exports an image of the layout object#exporter.exportToImage('/Users/ep9k/Desktop/TestLayout.png', QgsLayoutExporter.ImageExportSettings()) 

我举例说明了如何导出为 .pdf 和图像文件 (.png)。 我提供了保存对象的桌面路径。

有关如何创建打印布局和导出最终地图的分步演练。 正如我在一开始所说的,这比我预期的要复杂得多,所以希望它能帮助你的学习开源GIS出专题图。

相关内容

热门资讯

起点订购交易平台非法期货,贵金...   在起点订购APP做交易的受害者很多,投诉无门!还有很多投资者根本不知道自己遭遇了一场骗局!现在互...
全新易购APP欺骗消费者,利用...   不要“充值送现”、“首单免赔”等广告给迷惑,这些宣传标语只是一个宣传语,诱导普通大众去参与投资,...
大吉订购、兴鑫淘金APP现货订...   大吉订购APP投资人亏损的资金能追回?该平台是否有期货经营资质?为什么一个现货订购平台能做高杠杆...
天龙白银APP订购白银亏损骗局...   白银贵金属投资,除了正规的期货交易公司,其他外面的这些交易软件都是骗人的!根本没有任何一家公司是...
这种防癌方式不仅有效,性价比也...   关注健康 预防癌症  癌症防治,关键在预防,核心在早筛。坚持关口前移是最经济有效的防癌策略。围绕...