Viktigt
Översättning är en gemenskapsinsats du kan gå med i. Den här sidan är för närvarande översatt till 100.00%.
10. Rendering och utskrift av kartor
Råd
Kodsnuttarna på den här sidan behöver följande import:
1import os
2
3from qgis.core import (
4 QgsGeometry,
5 QgsMapSettings,
6 QgsPrintLayout,
7 QgsMapSettings,
8 QgsMapRendererParallelJob,
9 QgsLayoutItemLabel,
10 QgsLayoutItemLegend,
11 QgsLayoutItemMap,
12 QgsLayoutItemPolygon,
13 QgsLayoutItemScaleBar,
14 QgsLayoutExporter,
15 QgsLayoutItem,
16 QgsLayoutPoint,
17 QgsLayoutSize,
18 QgsUnitTypes,
19 QgsProject,
20 QgsFillSymbol,
21 QgsAbstractValidityCheck,
22 check,
23)
24
25from qgis.PyQt.QtGui import (
26 QPolygonF,
27 QColor,
28)
29
30from qgis.PyQt.QtCore import (
31 QPointF,
32 QRectF,
33 QSize,
34)
Det finns i allmänhet två tillvägagångssätt när indata ska återges som en karta: antingen gör du det snabbt med hjälp av QgsMapRendererJob eller så producerar du mer finjusterade utdata genom att komponera kartan med QgsLayout
-klassen.
10.1. Enkel rendering
Renderingen görs genom att skapa ett QgsMapSettings
-objekt för att definiera renderingsinställningarna och sedan konstruera ett QgsMapRendererJob
med dessa inställningar. Den senare används sedan för att skapa den resulterande bilden.
Här är ett exempel:
1image_location = os.path.join(QgsProject.instance().homePath(), "render.png")
2
3vlayer = iface.activeLayer()
4settings = QgsMapSettings()
5settings.setLayers([vlayer])
6settings.setBackgroundColor(QColor(255, 255, 255))
7settings.setOutputSize(QSize(800, 600))
8settings.setExtent(vlayer.extent())
9
10render = QgsMapRendererParallelJob(settings)
11
12def finished():
13 img = render.renderedImage()
14 # save the image; e.g. img.save("/Users/myuser/render.png","png")
15 img.save(image_location, "png")
16
17render.finished.connect(finished)
18
19# Start the rendering
20render.start()
21
22# The following loop is not normally required, we
23# are using it here because this is a standalone example.
24from qgis.PyQt.QtCore import QEventLoop
25loop = QEventLoop()
26render.finished.connect(loop.quit)
27loop.exec_()
10.2. Rendering av lager med olika CRS
Om du har mer än ett lager och de har olika CRS fungerar det enkla exemplet ovan förmodligen inte: för att få rätt värden från omfattningsberäkningarna måste du uttryckligen ange destinationens CRS
layers = [iface.activeLayer()]
settings = QgsMapSettings()
settings.setLayers(layers)
settings.setDestinationCrs(layers[0].crs())
10.3. Utskrift med hjälp av utskriftslayout
Print layout är ett mycket praktiskt verktyg om du vill göra en mer sofistikerad utskrift än den enkla rendering som visas ovan. Det är möjligt att skapa komplexa kartlayouter som består av kartvyer, etiketter, teckenförklaringar, tabeller och andra element som vanligtvis finns på papperskartor. Layouterna kan sedan exporteras till PDF, SVG, rasterbilder eller skrivas ut direkt på en skrivare.
Layouten består av ett antal klasser. De tillhör alla kärnbiblioteket. QGIS-applikationen har ett bekvämt GUI för placering av elementen, även om det inte finns tillgängligt i GUI-biblioteket. Om du inte är bekant med Qt Graphics View framework, uppmuntras du att kontrollera dokumentationen nu, eftersom layouten är baserad på den.
Den centrala klassen i layouten är QgsLayout
, som härstammar från Qt QGraphicsScene. Låt oss skapa en instans av den:
project = QgsProject.instance()
layout = QgsPrintLayout(project)
layout.initializeDefaults()
Detta initierar layouten med vissa standardinställningar, särskilt genom att lägga till en tom A4-sida i layouten. Du kan skapa layouter utan att anropa initializeDefaults()
-metoden, men du måste själv ta hand om att lägga till sidor i layouten.
Den föregående koden skapar en ”tillfällig” layout som inte är synlig i GUI. Det kan vara praktiskt att t.ex. snabbt lägga till några objekt och exportera utan att ändra själva projektet eller exponera dessa ändringar för användaren. Om du vill att layouten ska sparas/återställas tillsammans med projektet och vara tillgänglig i layouthanteraren, lägg då till:
layout.setName("MyLayout")
project.layoutManager().addLayout(layout)
Nu kan vi lägga till olika element (karta, etikett, …) i layouten. Alla dessa objekt representeras av klasser som ärver från basklassen QgsLayoutItem
.
Här följer en beskrivning av några av de viktigaste layoutobjekten som kan läggas till i en layout.
map — Här skapar vi en karta i en anpassad storlek och återger den aktuella kartbilden
1map = QgsLayoutItemMap(layout) 2# Set map item position and size (by default, it is a 0 width/0 height item placed at 0,0) 3map.attemptMove(QgsLayoutPoint(5,5, QgsUnitTypes.LayoutMillimeters)) 4map.attemptResize(QgsLayoutSize(200,200, QgsUnitTypes.LayoutMillimeters)) 5# Provide an extent to render 6map.zoomToExtent(iface.mapCanvas().extent()) 7layout.addLayoutItem(map)
label — gör det möjligt att visa etiketter. Det är möjligt att ändra dess teckensnitt, färg, justering och marginal
label = QgsLayoutItemLabel(layout) label.setText("Hello world") label.adjustSizeToText() layout.addLayoutItem(label)
legend
legend = QgsLayoutItemLegend(layout) legend.setLinkedMap(map) # map is an instance of QgsLayoutItemMap layout.addLayoutItem(legend)
skalstreck
1item = QgsLayoutItemScaleBar(layout) 2item.setStyle('Numeric') # optionally modify the style 3item.setLinkedMap(map) # map is an instance of QgsLayoutItemMap 4item.applyDefaultSize() 5layout.addLayoutItem(item)
nodbaserad form
1polygon = QPolygonF() 2polygon.append(QPointF(0.0, 0.0)) 3polygon.append(QPointF(100.0, 0.0)) 4polygon.append(QPointF(200.0, 100.0)) 5polygon.append(QPointF(100.0, 200.0)) 6 7polygonItem = QgsLayoutItemPolygon(polygon, layout) 8layout.addLayoutItem(polygonItem) 9 10props = {} 11props["color"] = "green" 12props["style"] = "solid" 13props["style_border"] = "solid" 14props["color_border"] = "black" 15props["width_border"] = "10.0" 16props["joinstyle"] = "miter" 17 18symbol = QgsFillSymbol.createSimple(props) 19polygonItem.setSymbol(symbol)
När ett objekt har lagts till i layouten kan det flyttas och ändra storlek:
item.attemptMove(QgsLayoutPoint(1.4, 1.8, QgsUnitTypes.LayoutCentimeters))
item.attemptResize(QgsLayoutSize(2.8, 2.2, QgsUnitTypes.LayoutCentimeters))
Som standard ritas en ram runt varje objekt. Du kan ta bort den på följande sätt:
# for a composer label
label.setFrameEnabled(False)
Förutom att skapa layoutobjekten för hand har QGIS stöd för layoutmallar som i princip är kompositioner med alla deras objekt sparade i en .qpt-fil (med XML-syntax).
När kompositionen är klar (layoutobjekten har skapats och lagts till i kompositionen) kan vi fortsätta med att producera en raster- och/eller vektorutskrift.
10.3.1. Kontroll av layoutens giltighet
En layout består av en uppsättning sammankopplade objekt och det kan hända att dessa kopplingar bryts under ändringar (en legend kopplad till en borttagen karta, ett bildobjekt med saknad källfil, …) eller så kanske du vill tillämpa anpassade begränsningar på layoutobjekten. QgsAbstractValidityCheck
hjälper dig att uppnå detta.
En grundläggande kontroll ser ut som följer:
@check.register(type=QgsAbstractValidityCheck.TypeLayoutCheck)
def my_layout_check(context, feedback):
results = ...
return results
Här är en kontroll som ger en varning när ett layoutkartobjekt är inställt på webbmerkuratorprojektionen:
1@check.register(type=QgsAbstractValidityCheck.TypeLayoutCheck)
2def layout_map_crs_choice_check(context, feedback):
3 layout = context.layout
4 results = []
5 for i in layout.items():
6 if isinstance(i, QgsLayoutItemMap) and i.crs().authid() == 'EPSG:3857':
7 res = QgsValidityCheckResult()
8 res.type = QgsValidityCheckResult.Warning
9 res.title = 'Map projection is misleading'
10 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())
11 results.append(res)
12
13 return results
Och här är ett mer komplext exempel, som ger en varning om något av kartobjekten i layouten är inställt på en CRS som endast är giltig utanför den utsträckning som visas i det kartobjektet:
1@check.register(type=QgsAbstractValidityCheck.TypeLayoutCheck)
2def layout_map_crs_area_check(context, feedback):
3 layout = context.layout
4 results = []
5 for i in layout.items():
6 if isinstance(i, QgsLayoutItemMap):
7 bounds = i.crs().bounds()
8 ct = QgsCoordinateTransform(QgsCoordinateReferenceSystem('EPSG:4326'), i.crs(), QgsProject.instance())
9 bounds_crs = ct.transformBoundingBox(bounds)
10
11 if not bounds_crs.contains(i.extent()):
12 res = QgsValidityCheckResult()
13 res.type = QgsValidityCheckResult.Warning
14 res.title = 'Map projection is incorrect'
15 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())
16 results.append(res)
17
18 return results
10.3.2. Exportera layouten
För att exportera en layout måste klassen QgsLayoutExporter
användas.
1base_path = os.path.join(QgsProject.instance().homePath())
2pdf_path = os.path.join(base_path, "output.pdf")
3
4exporter = QgsLayoutExporter(layout)
5exporter.exportToPdf(pdf_path, QgsLayoutExporter.PdfExportSettings())
Använd exportToSvg()
eller exportToImage()
om du vill exportera till en SVG- eller bildfil istället för en PDF-fil.
10.3.3. Exportera en layoutatlas
Om du vill exportera alla sidor från en layout som har atlasalternativet konfigurerat och aktiverat måste du använda metoden atlas()
i exportören (QgsLayoutExporter
) med små justeringar. I följande exempel exporteras sidorna till PNG-bilder:
exporter.exportToImage(layout.atlas(), base_path, 'png', QgsLayoutExporter.ImageExportSettings())
Observera att utdata sparas i mappen Base Path med hjälp av det uttryck för utdatafilnamn som konfigurerats på Atlas.