10. Kaart renderen en afdrukken
Hint
De codesnippers op deze pagina hebben de volgende import nodig:
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)
Er zijn over het algemeen twee benaderingen wanneer ingevoerde gegevens zouden moeten worden gerenderd als een kaart: ofwel doe het op de snelle manier met behulp van QgsMapRendererJob of produceer een meer fijn afgestemde uitvoer door de kaart samen te stellen met behulp van de klasse QgsLayout
.
10.1. Eenvoudig renderen
Het renderen wordt gedaan door een object QgsMapSettings
te maken om de instellingen voor renderen te definiëren, en dan een QgsMapRendererJob
te construeren met deze instellingen. Het laatste wordt dan gebruikt om de resulterende afbeelding te maken.
Hier is een voorbeeld:
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. Lagen met een verschillend CRS renderen
Als u meer dan één laag hebt en zij hebben een verschillend CRS, zal het eenvoudige voorbeeld hierboven niet werken: om de juiste waarden uit de berekeningen van het bereik te krijgen dient u expliciet het doel-CRS in te stellen.
layers = [iface.activeLayer()]
settings = QgsMapSettings()
settings.setLayers(layers)
settings.setDestinationCrs(layers[0].crs())
10.3. Uitvoer door Afdruklay-out te gebruiken
Afdruklay-out is een zeer handig gereedschap als u een uitgebreidere uitvoer wilt dan de eenvoudige rendering van die welke hierboven is weergegeven. Het is mogelijk complexe lay-outs voor kaarten te maken, bestaande uit weergaven van kaarten, labels, legenda, tabellen en andere elementen die gewoonlijk aanwezig zijn op papieren kaarten. De lay-outs kunnen dan worden geëxporteerd naar PDF, SVG, rasterafbeeldingen of direct worden afgedrukt op een printer.
De lay-out bestaat uit een aantal klassen. Zij maken allemaal deel uit van de bronbibliotheek. De toepassing QGIS heeft een handige gebruikersinterface voor de plaatsing van de elementen, hoewel die niet beschikbaar is in de bibliotheek van de gebruikersinterface. Als u nog niet bekend bent met Qt Graphics View framework, wordt u aangeraden om nu de documentatie te bekijken, omdat de lay-out daarop is gebaseerd.
De centrale klasse van de afdruklay-out is de klasse QgsLayout
die is afgeleid van de klasse voor Qt QGraphicsScene. Laten we er een instantie van maken:
project = QgsProject.instance()
layout = QgsPrintLayout(project)
layout.initializeDefaults()
Dit initialiseert de lay-out met enkele standaard instellingen, in het bijzonder een lege pagina A4 aan de lay-out. U kunt lay-outs maken zonder de methode initializeDefaults()
aan te roepen, maar u dient zelf het toevoegen van pagina’s aan de lay-out te regelen.
De vorige code maakt een “tijdelijke” lay-out die niet zichtbaar is in de GUI. Het kan handig zijn om bijvoorbeeld snel enkele items toe te voegen en te exporteren, zonder het project aan te passen of deze wijzigingen aan de gebruiker te laten zien. Als u de lay-out wilt opslaan/herstellen naast het project en beschikbaar zijn in Lay-out beheren, voeg dan toe:
layout.setName("MyLayout")
project.layoutManager().addLayout(layout)
Nu kunnen we verschillende elementen (kaart, label, …) toevoegen aan de lay-out. Al deze objecten worden weergegeven door klassen die erven van de basisklasse QgsLayoutItem
.
Hier is een beschrijving van enkele van de belangrijkste items voor lay-out die aan een lay-out kunnen worden toegevoegd.
map — Hier maken we een kaart van een aangepaste grootte en renderen het huidige kaartvenster
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 — maakt het weergeven van labels mogelijk. Het is mogelijk het lettertype, de kleur, de uitlijning en marge aan te passen
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)
schaalbalk
1item = QgsLayoutItemScaleBar(layout) 2item.setStyle('Numeric') # optionally modify the style 3item.setLinkedMap(map) # map is an instance of QgsLayoutItemMap 4item.applyDefaultSize() 5layout.addLayoutItem(item)
op knopen gebaseerde vorm
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)
Als een item eenmaal is toegevoegd aan de lay-out kan het worden verplaatst en de grootte worden gewijzigd:
item.attemptMove(QgsLayoutPoint(1.4, 1.8, QgsUnitTypes.LayoutCentimeters))
item.attemptResize(QgsLayoutSize(2.8, 2.2, QgsUnitTypes.LayoutCentimeters))
Standaard wordt een kader rondom elk item getekend. U kunt dat als volgt verwijderen:
# for a composer label
label.setFrameEnabled(False)
Naast het handmatig maken van items voor afdruklay-out, heeft QGIS ondersteuning voor sjablonen van afdruklay-out wat in essentie lay-outs zijn met al hun items, opgeslagen als een bestand .qpt (met syntaxis XML).
Als de lay-out eenmaal gereed is (de items van afdruklay-out zijn gemaakt en toegevoegd aan de lay-out), kunnen we doorgaan en een raster- en/of vector-uitvoer produceren.
10.3.1. Geldigheid lay-out controleren
Een lay-out is gemaakt uit een set van onderling verbonden items en het kan gebeuren dat deze verbindingen defect raken tijdens aanpassingen (een legenda die is verbonden met een verwijderde kaart, een afbeeldingsitem met een ontbrekend bronbestand,…) of u wilt misschien aangepaste beperkingen toepassen op de items van de lay-out. De klasse QgsAbstractValidityCheck
helpt u dit te bereiken.
Een basiscontrole ziet eruit als dit:
@check.register(type=QgsAbstractValidityCheck.TypeLayoutCheck)
def my_layout_check(context, feedback):
results = ...
return results
Hier is een controle die een waarschuwing opwerpt, iedere keer als een kaartitem van een lay-out is ingesteld op de projectie Web Mercator:
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
En hier is een meer complexer voorbeeld, dat een waarschuwing opwerpt als een kaartitem is ingesteld op een CRS dat alleen geldig is buiten het in dat kaartitem weergegeven bereik:
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. Lay-out exporteren
De klasse QgsLayoutExporter
moet worden gebruikt om een lay-out te exporteren.
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())
Gebruik exportToSvg()
of exportToImage()
in het geval dat u wilt exporteren naar een SVG of afbeeldingsbestand in plaats van een bestand PDF.
10.3.3. Een afdrukatlas exporteren
Als u alle pagina’s wilt exporteren van een lay-out die de optie Atlas heeft geconfigureerd en ingeschakeld, dient u de methode atlas()
te gebruiken voor het exporteren (QgsLayoutExporter
) met enkele kleine aanpassingen. In het volgende voorbeeld worden de pagina’s geëxporteerd naar afbeeldingen PNG:
exporter.exportToImage(layout.atlas(), base_path, 'png', QgsLayoutExporter.ImageExportSettings())
Onthoud dat de uitvoer zal worden opgeslagen in de map voor het basispad, met de expressie voor de bestandsnaam voor de uitvoer die werd geconfigureerd in Atlas.