7. Afhandeling van geometrie
Hint
De codesnippers op deze pagina hebben de volgende import nodig als u buiten de console van PyQGIS bent:
1from qgis.core import (
2 QgsGeometry,
3 QgsGeometryCollection,
4 QgsPoint,
5 QgsPointXY,
6 QgsWkbTypes,
7 QgsProject,
8 QgsFeatureRequest,
9 QgsVectorLayer,
10 QgsDistanceArea,
11 QgsUnitTypes,
12 QgsCoordinateTransform,
13 QgsCoordinateReferenceSystem
14)
Naar punten, lijnen en polygonen die een ruimtelijk object weergeven wordt gewoonlijk verwezen als geometrieën. In QGIS worden zij weergegeven door de klasse QgsGeometry
.
Soms is één geometrie in feite een verzameling van enkele (ééndelige) geometrieën. Een dergelijke geometrie wordt een geometrie met meerdere delen genoemd. Als het slechts één type eenvoudige geometrie bevat, noemen we het multi-punt, multi-lijn of multi-polygoon. Een land dat bijvoorbeeld bestaat uit meerdere eilanden kan worden weergegeven als een multi-polygoon.
De coördinaten van geometrieën kunnen in elk coördinaten referentiesysteem (CRS) staan. Bij het ophalen van objecten vanaf een laag, zullen de geassocieerde geometrieën in coördinaten in het CRS van de laag staan.
Beschrijving en specificaties van alle mogelijke constructies van geometrieën en relaties zijn beschikbaar in de OGC Simple Feature Access Standards voor uitgebreide details.
7.1. Construeren van geometrie
PyQGIS verschaft verscheidene opties voor het maken van een geometrie:
uit coördinaten
1gPnt = QgsGeometry.fromPointXY(QgsPointXY(1,1)) 2print(gPnt) 3gLine = QgsGeometry.fromPolyline([QgsPoint(1, 1), QgsPoint(2, 2)]) 4print(gLine) 5gPolygon = QgsGeometry.fromPolygonXY([[QgsPointXY(1, 1), 6 QgsPointXY(2, 2), QgsPointXY(2, 1)]]) 7print(gPolygon)
Coördinaten worden opgegeven met behulp van de klassen
QgsPoint
ofQgsPointXY
. Het verschil tussen deze klassen is datQgsPoint
dimensies M en Z ondersteunt.Een Polylijn (Lijn) wordt weergegeven door een lijst met punten.
Een polygoon wordt weergegeven als een lijst van lineaire ringen (d.i. gesloten lijnen). De eerste ring is de buitenste ring (grens), optionele volgende ringen zijn gaten in de polygoon. Onthoud dat, anders dan andere programma’s, QGIS de ring voor u zal sluiten dus is er geen reden om het eerste punt als laatste te dupliceren.
Geometrieën die bestaan uit meerdere delen gaan een niveau verder: multi-punt is een lijst van punten, multi-lijnen zijn een lijst van lijnen en multi-polygoon is een lijst van polygonen.
uit bekende tekst (WKT)
geom = QgsGeometry.fromWkt("POINT(3 4)") print(geom)
uit bekende binaire (WKB)
1g = QgsGeometry() 2wkb = bytes.fromhex("010100000000000000000045400000000000001440") 3g.fromWkb(wkb) 4 5# print WKT representation of the geometry 6print(g.asWkt())
7.2. Toegang tot geometrie
Als eerste zou u het type geometrie moeten zoeken, de methode wkbType()
is die om te gebruiken. Het geeft een waarde uit de enumeratie QgsWkbTypes.Type
terug.
1if gPnt.wkbType() == QgsWkbTypes.Point:
2 print(gPnt.wkbType())
3 # output: 1 for Point
4if gLine.wkbType() == QgsWkbTypes.LineString:
5 print(gLine.wkbType())
6 # output: 2 for LineString
7if gPolygon.wkbType() == QgsWkbTypes.Polygon:
8 print(gPolygon.wkbType())
9 # output: 3 for Polygon
Als alternatief kan men de methode type()
gebruiken die een waarde teruggeeft uit de enumeratie van de klasse QgsWkbTypes.GeometryType
.
U kunt de functie displayString()
gebruiken om een voor mensen leesbaar type geometrie te verkrijgen.
1print(QgsWkbTypes.displayString(gPnt.wkbType()))
2# output: 'Point'
3print(QgsWkbTypes.displayString(gLine.wkbType()))
4# output: 'LineString'
5print(QgsWkbTypes.displayString(gPolygon.wkbType()))
6# output: 'Polygon'
Point
LineString
Polygon
Er is ook een hulpfunctie isMultipart()
om uit te zoeken of een geometrie meerdelig is of niet.
Voor elk type vector zijn er functies voor toegang om informatie uit de geometrie op te halen. Hier is een voorbeeld hoe deze functies te gebruiken:
1print(gPnt.asPoint())
2# output: <QgsPointXY: POINT(1 1)>
3print(gLine.asPolyline())
4# output: [<QgsPointXY: POINT(1 1)>, <QgsPointXY: POINT(2 2)>]
5print(gPolygon.asPolygon())
6# output: [[<QgsPointXY: POINT(1 1)>, <QgsPointXY: POINT(2 2)>, <QgsPointXY: POINT(2 1)>, <QgsPointXY: POINT(1 1)>]]
Notitie
De tuples (x,y) zijn geen echte tuples, zij zijn objecten QgsPoint
, de waarden zijn toegankelijk met de methoden x()
en y()
.
Voor meerdelige geometrieën zijn er soortgelijke functies voor toegang: asMultiPoint()
, asMultiPolyline()
en asMultiPolygon()
.
Het is mogelijk door alle delen van een geometrie te gaan, ongeacht het type geometrie. Bijv.
geom = QgsGeometry.fromWkt( 'MultiPoint( 0 0, 1 1, 2 2)' )
for part in geom.parts():
print(part.asWkt())
Point (0 0)
Point (1 1)
Point (2 2)
geom = QgsGeometry.fromWkt( 'LineString( 0 0, 10 10 )' )
for part in geom.parts():
print(part.asWkt())
LineString (0 0, 10 10)
gc = QgsGeometryCollection()
gc.fromWkt('GeometryCollection( Point(1 2), Point(11 12), LineString(33 34, 44 45))')
print(gc[1].asWkt())
Point (11 12)
Het is ook mogelijk elk deel van de geometrie aan te passen met de methode QgsGeometry.parts()
.
1geom = QgsGeometry.fromWkt( 'MultiPoint( 0 0, 1 1, 2 2)' )
2for part in geom.parts():
3 part.transform(QgsCoordinateTransform(
4 QgsCoordinateReferenceSystem("EPSG:4326"),
5 QgsCoordinateReferenceSystem("EPSG:3111"),
6 QgsProject.instance())
7 )
8
9print(geom.asWkt())
MultiPoint ((-10334728.12541878595948219 -5360106.25905461423099041),(-10462135.16126426123082638 -5217485.4735023295506835),(-10589399.84444035589694977 -5072021.45942386891692877))
7.3. Predicaten en bewerking voor geometrieën
QGIS gebruikt de bibliotheek GEOS voor geavanceerde bewerkingen met geometrieën, zoals de predicaten voor geometrieën (contains()
, intersects()
, …) en het instellen van bewerkingen (combine()
, difference()
, …). Het kan ook geometrische eigenschappen van geometrieën berekenen, zoals gebied (in het geval van polygonen) of lengten (voor polygonen en lijnen).
Laten we een voorbeeld bekijken dat het doorlopen van de objecten op een laag combineert met het uitvoeren van enkele geometrische berekeningen, gebaseerd op hun geometrieën. De onderstaande code zal het gebied en de perimeter van elk land op de laag countries
in ons project in de handleiding voor QGIS berekenen en afdrukken.
De volgende code gaat ervan uit dat layer
een object QgsVectorLayer
is.
1# let's access the 'countries' layer
2layer = QgsProject.instance().mapLayersByName('countries')[0]
3
4# let's filter for countries that begin with Z, then get their features
5query = '"name" LIKE \'Z%\''
6features = layer.getFeatures(QgsFeatureRequest().setFilterExpression(query))
7
8# now loop through the features, perform geometry computation and print the results
9for f in features:
10 geom = f.geometry()
11 name = f.attribute('NAME')
12 print(name)
13 print('Area: ', geom.area())
14 print('Perimeter: ', geom.length())
1Zambia
2Area: 62.822790653431014
3Perimeter: 50.65232014052552
4Zimbabwe
5Area: 33.41113559136511
6Perimeter: 26.608288555013935
Nu hebt u de gebieden en perimeters van de geometrieën berekend en afgedrukt. Het zal u echter snel opvallen dat de waarden vreemd zijn. Dat komt omdat gebieden en perimeters geen rekening houden met het CRS bij het berekenen met behulp van de methoden area()
en length()
uit de klasse QgsGeometry
. Voor een meer krachtiger berekening van gebied en afstand kan de klasse QgsDistanceArea
worden gebruikt, die op ellipsoïde gebaseerde berekeningen kan uitvoeren:
De volgende code gaat ervan uit dat layer
een object QgsVectorLayer
is.
1d = QgsDistanceArea()
2d.setEllipsoid('WGS84')
3
4layer = QgsProject.instance().mapLayersByName('countries')[0]
5
6# let's filter for countries that begin with Z, then get their features
7query = '"name" LIKE \'Z%\''
8features = layer.getFeatures(QgsFeatureRequest().setFilterExpression(query))
9
10for f in features:
11 geom = f.geometry()
12 name = f.attribute('NAME')
13 print(name)
14 print("Perimeter (m):", d.measurePerimeter(geom))
15 print("Area (m2):", d.measureArea(geom))
16
17 # let's calculate and print the area again, but this time in square kilometers
18 print("Area (km2):", d.convertAreaMeasurement(d.measureArea(geom), QgsUnitTypes.AreaSquareKilometers))
1Zambia
2Perimeter (m): 5539361.250294601
3Area (m2): 751989035032.9031
4Area (km2): 751989.0350329031
5Zimbabwe
6Perimeter (m): 2865021.3325076113
7Area (m2): 389267821381.6008
8Area (km2): 389267.8213816008
Als alternatief zou u misschien de afstand tussen twee punten willen weten.
1d = QgsDistanceArea()
2d.setEllipsoid('WGS84')
3
4# Let's create two points.
5# Santa claus is a workaholic and needs a summer break,
6# lets see how far is Tenerife from his home
7santa = QgsPointXY(25.847899, 66.543456)
8tenerife = QgsPointXY(-16.5735, 28.0443)
9
10print("Distance in meters: ", d.measureLine(santa, tenerife))
U kunt zoeken naar vele voorbeelden van algoritmes die zijn opgenomen in QGIS en die methoden gebruiken om vectorgegevens te analyseren en te transformeren. Hier zijn enkele koppelingen naar de code van sommige ervan.
Afstand en gebied gebruiken de klasse
QgsDistanceArea
: Algoritme Afstandsmatrix