本节代码片段需要导入以下模块:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 import os
from qgis.core import (
QgsGeometry ,
QgsMapSettings ,
QgsPrintLayout ,
QgsMapSettings ,
QgsMapRendererParallelJob ,
QgsLayoutItemLabel ,
QgsLayoutItemLegend ,
QgsLayoutItemMap ,
QgsLayoutItemPolygon ,
QgsLayoutItemScaleBar ,
QgsLayoutExporter ,
QgsLayoutItem ,
QgsLayoutPoint ,
QgsLayoutSize ,
QgsUnitTypes ,
QgsProject ,
QgsFillSymbol ,
QgsAbstractValidityCheck ,
check ,
)
from qgis.PyQt.QtGui import (
QPolygonF ,
QColor ,
)
from qgis.PyQt.QtCore import (
QPointF ,
QRectF ,
QSize ,
)
10 地图渲染和打印
当输入数据作为地图呈现时,通常有两种方法:使用QgsMapRendererJob 快速进行,或者使用QgsLayout
类组合地图来生成更精细的输出。
10.1 简单的渲染
渲染完成后,创建一个QgsMapSettings
对象来定义渲染选项,然后使用这些选项构建一个QgsMapRendererJob
类。然后使用后者来创建图像。
这是一个例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 image_location = os . path . join ( QgsProject . instance () . homePath (), "render.png" )
vlayer = iface . activeLayer ()
settings = QgsMapSettings ()
settings . setLayers ([ vlayer ])
settings . setBackgroundColor ( QColor ( 255 , 255 , 255 ))
settings . setOutputSize ( QSize ( 800 , 600 ))
settings . setExtent ( vlayer . extent ())
render = QgsMapRendererParallelJob ( options )
def finished ():
img = render . renderedImage ()
# 保存图像; e.g. img.save("/Users/myuser/render.png","png")
img . save ( image_location , "png" )
render . finished . connect ( finished )
# 开始渲染
render . start ()
# 通常不需要循环,因为这里是单独使用
from qgis.PyQt.QtCore import QEventLoop
loop = QEventLoop ()
render . finished . connect ( loop . quit )
loop . exec_ ()
10.2 使用不同的CRS渲染图层
如果你有多个图层并且它们具有不同的CRS,上面的简单示例可能不起作用:从范围计算中获取正确的值,你必须显式设置目标CRS
layers = [ iface . activeLayer ()]
settings = QgsMapSettings ()
settings . setLayers ( layers )
render . setDestinationCrs ( layers [ 0 ] . crs ())
10.3 使用打印布局输出
如果你想要比上面显示的简单渲染更复杂的输出,打印布局是一个非常方便的工具。可以创建复杂的地图布局,包括地图视图,标签,图例,表格以及通常出现在纸质地图上的其他元素。然后可以将布局导出为PDF,SVG,栅格图像或直接打印在打印机上。
布局由一堆类组成。它们都属于核心库。QGIS应用程序有一个方便的GUI布局元素,虽然它在GUI库中不可用。如果你不熟悉 Qt Graphics View框架 ,那么建议你查看文档,因为布局是基于它的。
布局的中心类是QgsLayout
类,它是从Qt QGraphicsScene 类派生的。让我们创建一个它的实例:
project = QgsProject . instance ()
layout = QgsPrintLayout ( project )
layout . initializeDefaults ()
这将使用一些默认设置初始化布局,将空的A4页添加到布局中。你可以在不调用initializeDefaults()
方法的情况下创建布局,但你需要自己向布局中添加页面。
之前的代码创建了在GUI中不可见的“临时”布局。它可以方便快速地添加某些项并导出,而不修改项本身,也不会向户公开这些更改。如果你希望将布局与项目一起保存或恢复,并使其在布局管理器中可用,添加:
layout . setName ( "MyLayout" )
project . layoutManager () . addLayout ( layout )
现在我们可以在布局中添加各种元素(map,label,...)。所有这些对象都继承自基类QgsLayoutItem
。
以下是可以添加到布局的一些主要布局项的说明。
地图—— 在这里,我们创建一个地图并将其拉伸到整个纸张大小
map = QgsLayoutItemMap ( layout )
# 设置地图项的位置和大小(默认是宽高都是0,位置在0,0)
map . attemptMove ( QgsLayoutPoint ( 5 , 5 , QgsUnitTypes . LayoutMillimeters ))
map . attemptResize ( QgsLayoutSize ( 200 , 200 , QgsUnitTypes . LayoutMillimeters ))
# 提供渲染范围
map . zoomToExtent ( iface . mapCanvas () . extent ())
layout . addItem ( map )
标签——允许显示标签。可以修改其字体,颜色,对齐和边距
label = QgsLayoutItemLabel ( layout )
label . setText ( "Hello world" )
label . adjustSizeToText ()
layout . addItem ( label )
legend = QgsLayoutItemLegend ( layout )
legend . setLinkedMap ( map ) # map是一个QgsLayoutItemMap实例
layout . addItem ( legend )
item = QgsLayoutItemScaleBar ( layout )
item . setStyle ( 'Numeric' ) # 可选择修改样式
item . setLinkedMap ( map ) # map是一个QgsLayoutItemMap实例
item . applyDefaultSize ()
layout . addItem ( item )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 polygon = QPolygonF ()
polygon . append ( QPointF ( 0.0 , 0.0 ))
polygon . append ( QPointF ( 100.0 , 0.0 ))
polygon . append ( QPointF ( 200.0 , 100.0 ))
polygon . append ( QPointF ( 100.0 , 200.0 ))
polygonItem = QgsLayoutItemPolygon ( polygon , layout )
layout . addItem ( polygonItem )
props = {}
props [ "color" ] = "green"
props [ "style" ] = "solid"
props [ "style_border" ] = "solid"
props [ "color_border" ] = "black"
props [ "width_border" ] = "10.0"
props [ "joinstyle" ] = "miter"
symbol = QgsFillSymbol . createSimple ( props )
polygonItem . setSymbol ( symbol )
将项添加到布局后,可以移动并调整其大小:
item . attemptMove ( QgsLayoutPoint ( 1.4 , 1.8 , QgsUnitTypes . LayoutCentimeters ))
item . attemptResize ( QgsLayoutSize ( 2.8 , 2.2 , QgsUnitTypes . LayoutCentimeters ))
默认情况下,每个项目周围都会绘制一个框架,你可以按如下方式删除它:
label . setFrameEnabled ( False )
除了手动创建布局项外,QGIS还支持布局模板,这些布局模板本质上是将所有项保存到.qpt文件中(使用XML语法)。
一旦组合准备就绪(布局项已经创建并添加到组合中),我们就可以继续生成栅格或者矢量输出。
10.3.1 检查布局有效性
布局是由一组互连的项组成的,在修改过程中可能会发生这些连接断开的情况(图例连接到已删除的地图、缺少源文件的图像项等),或者你可能希望对布局项应用自定义约束。QgsAbstractValidatyCheck 可帮助你实现这一点。
基本检查如下:
@check . register ( type = QgsAbstractValidityCheck . TypeLayoutCheck )
def my_layout_check ( context , feedback ):
results = ...
return results
这里有一个检查,每当布局地图项目设置为WEB墨卡托投影时,就会发出警告:
1
2
3
4
5
6
7
8
9
10
11
12
13 @check . register ( type = QgsAbstractValidityCheck . TypeLayoutCheck )
def layout_map_crs_choice_check ( context , feedback ):
layout = context . layout
results = []
for i in layout . items ():
if isinstance ( i , QgsLayoutItemMap ) and i . crs () . authid () == 'EPSG:3857' :
res = QgsValidityCheckResult ()
res . type = QgsValidityCheckResult . Warning
res . title = 'Map projection is misleading'
res . detailedDescription = 'The projection for the map item {} is set to <i>Web Mercator (EPSG:3857)</i> which misrepresents areas and shapes. Consider using an appropriate local projection instead.' . format ( i . displayName ())
results . append ( res )
return results
这里有一个更复杂的例子,如果任何布局地图项被设置为CRS,则会发出警告,该CRS仅在该地图项中显示的范围之外有效:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 @check . register ( type = QgsAbstractValidityCheck . TypeLayoutCheck )
def layout_map_crs_area_check ( context , feedback ):
layout = context . layout
results = []
for i in layout . items ():
if isinstance ( i , QgsLayoutItemMap ):
bounds = i . crs () . bounds ()
ct = QgsCoordinateTransform ( QgsCoordinateReferenceSystem ( 'EPSG:4326' ), i . crs (), QgsProject . instance ())
bounds_crs = ct . transformBoundingBox ( bounds )
if not bounds_crs . contains ( i . extent ()):
res = QgsValidityCheckResult ()
res . type = QgsValidityCheckResult . Warning
res . title = 'Map projection is incorrect'
res . detailedDescription = 'The projection for the map item {} is set to \' {} \' , which is not valid for the area displayed within the map.' . format ( i . displayName (), i . crs () . authid ())
results . append ( res )
return results
10.3.2 导出布局
导出布局,必须使用QgsLayoutExporter
类。
pdf_path = os . path . join ( QgsProject . instance () . homePath (), "output.pdf" )
exporter = QgsLayoutExporter ( layout )
exporter . exportToPdf ( pdf_path , QgsLayoutExporter . PdfExportSettings ())
如果要分别导出到SVG或图像文件,请使用exportToSvg()
或者exportToImage()
导出图像。
10.3.3 导出布局图集
如果要从布局中导出所有页面(在配置中启用了图集选项),则需要在导出器(QgsLayoutExporter )中使用altas() 方法,并进行少量调整。在以下示例中,页面导出为PNG图像:
exporter . exportToImage ( layout . atlas (), base_path , 'png' , QgsLayoutExporter . ImageExportSettings ())
请注意,输出保存在基本路径文件夹中,使用图集上配置的输出文件名表达式。