De codesnippers op deze pagina hebben de volgende import nodig als u buiten de console van PyQGIS bent:
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 | from qgis.core import (
QgsApplication,
QgsDataSourceUri,
QgsCategorizedSymbolRenderer,
QgsClassificationRange,
QgsPointXY,
QgsProject,
QgsExpression,
QgsField,
QgsFields,
QgsFeature,
QgsFeatureRequest,
QgsFeatureRenderer,
QgsGeometry,
QgsGraduatedSymbolRenderer,
QgsMarkerSymbol,
QgsMessageLog,
QgsRectangle,
QgsRendererCategory,
QgsRendererRange,
QgsSymbol,
QgsVectorDataProvider,
QgsVectorLayer,
QgsVectorFileWriter,
QgsWkbTypes,
QgsSpatialIndex,
QgsVectorLayerUtils
)
from qgis.core.additions.edit import edit
from qgis.PyQt.QtGui import (
QColor,
)
|
6. Vectorlagen gebruiken¶
Dit gedeelte beschrijft verschillende acties die kunnen worden uitgevoerd met vectorlagen.
Het meeste werk hier is gebaseerd op de methoden van de klasse QgsVectorLayer
.
6.1. Informatie over attributen ophalen¶
U kunt informatie ophalen over de velden die zijn geassocieerd met een vectorlaag door fields()
aan te roepen op een object QgsVectorLayer
:
vlayer = QgsVectorLayer("testdata/airports.shp", "airports", "ogr")
for field in vlayer.fields():
print(field.name(), field.typeName())
1 2 3 4 5 | ID Integer64
fk_region Integer64
ELEV Real
NAME String
USE String
|
De methoden displayField()
en mapTipTemplate()
van de klasse QgsVectorLayer
verschaffen informatie over het veld en de gebruikte sjabloon op de tab Tonen.
Wanneer u een vectorlaag laadt, wordt altijd een veld gekozen door QGIS als de Weergavenaam
, terwijl de HTML kaarttip
standaard leeg is. Met deze methoden kunt u gemakkelijk beide verkrijgen:
vlayer = QgsVectorLayer("testdata/airports.shp", "airports", "ogr")
print(vlayer.displayField())
NAME
Notitie
Als u de Weergavenaam
wijzigt van een veld naar een expressie, moet u displayExpression()
gebruiken in plaats van displayField()
.
6.2. Itereren over vectorlagen¶
Het doorlopen van de objecten in een vectorlaag is één van de meest voorkomende taken. Hieronder staat een voorbeeld van eenvoudige basiscode om deze taak uit te voeren en enige informatie weer te geven over elk object. Voor de variabele layer
wordt aangenomen dat die een object QgsVectorLayer
heeft
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 35 36 37 38 39 40 41 | # "layer" is a QgsVectorLayer instance
layer = iface.activeLayer()
features = layer.getFeatures()
for feature in features:
# retrieve every feature with its geometry and attributes
print("Feature ID: ", feature.id())
# fetch geometry
# show some information about the feature geometry
geom = feature.geometry()
geomSingleType = QgsWkbTypes.isSingleType(geom.wkbType())
if geom.type() == QgsWkbTypes.PointGeometry:
# the geometry type can be of single or multi type
if geomSingleType:
x = geom.asPoint()
print("Point: ", x)
else:
x = geom.asMultiPoint()
print("MultiPoint: ", x)
elif geom.type() == QgsWkbTypes.LineGeometry:
if geomSingleType:
x = geom.asPolyline()
print("Line: ", x, "length: ", geom.length())
else:
x = geom.asMultiPolyline()
print("MultiLine: ", x, "length: ", geom.length())
elif geom.type() == QgsWkbTypes.PolygonGeometry:
if geomSingleType:
x = geom.asPolygon()
print("Polygon: ", x, "Area: ", geom.area())
else:
x = geom.asMultiPolygon()
print("MultiPolygon: ", x, "Area: ", geom.area())
else:
print("Unknown or invalid geometry")
# fetch attributes
attrs = feature.attributes()
# attrs is a list. It contains all the attribute values of this feature
print(attrs)
# for this test only print the first feature
break
|
Feature ID: 1
Point: <QgsPointXY: POINT(7 45)>
[1, 'First feature']
6.3. Objecten selecteren¶
In QGIS desktop kunnen objecten op verschillende manieren worden geselecteerd, de gebruiker kan klikken op een object, een rechthoek in het kaartvenster tekenen of een expressie-filter gebruiken. Geselecteerde objecten worden normaal gesproken geaccentueerd in een andere kleur (standaard is geel) om de aandacht van de gebruiker naar de selectie te trekken.
Sometimes it can be useful to programmatically select features or to change the default color.
De methode selectAll()
kan worden gebruikt om alle objecten te selecteren:
# Get the active layer (must be a vector layer)
layer = iface.activeLayer()
layer.selectAll()
Gebruik de methode selectByExpression()
om te selecteren met een expressie:
# Assumes that the active layer is points.shp file from the QGIS test suite
# (Class (string) and Heading (number) are attributes in points.shp)
layer = iface.activeLayer()
layer.selectByExpression('"Class"=\'B52\' and "Heading" > 10 and "Heading" <70', QgsVectorLayer.SetSelection)
U kunt, om de kleur van de selectie te wijzigen, de methode setSelectionColor()
van QgsMapCanvas
gebruiken, zoals weergegeven in het volgende voorbeeld:
iface.mapCanvas().setSelectionColor( QColor("red") )
U kunt, om objecten toe te voegen aan de lijst met geselecteerde objecten voor een bepaalde laag, select()
aanroepen, die de lijst met ID’s voor de objecten doorgeeft aan de lijst:
1 2 3 4 5 6 7 8 9 | selected_fid = []
# Get the first feature id from the layer
for feature in layer.getFeatures():
selected_fid.append(feature.id())
break
# Add these features to the selected list
layer.select(selected_fid)
|
De selectie opheffen:
layer.removeSelection()
6.3.1. Toegang tot attributen¶
Naar attributen kan worden verwezen door middel van hun naam:
print(feature['name'])
First feature
Als alternatief kan naar attributen worden verwezen door middel van een index. Dit is iets sneller dan het gebruiken van de naam. Bijvoorbeeld om het tweede attribuut te krijgen:
print(feature[1])
First feature
6.3.2. Itereren over geselecteerde objecten¶
Als u alleen geselecteerde objecten nodig hebt, kunt u de methode selectedFeatures()
gebruiken van de vectorlaag:
selection = layer.selectedFeatures()
for feature in selection:
# do whatever you need with the feature
pass
6.3.3. Itereren over een deel van de objecten¶
Wanneer u een deel van de objecten in een laag wilt doorlopen, zoals bijvoorbeeld alleen de objecten in een opgegeven gebied, dan dient een object QgsFeatureRequest
te worden toegevoegd aan de aanroep getFeatures()
. Hier is een voorbeeld:
1 2 3 4 5 6 7 | areaOfInterest = QgsRectangle(450290,400520, 450750,400780)
request = QgsFeatureRequest().setFilterRect(areaOfInterest)
for feature in layer.getFeatures(request):
# do whatever you need with the feature
pass
|
Omwille van de snelheid wordt het kruisen vaak gedaan door alleen het begrenzingsvak van het object te gebruiken. Er is echter een vlag ExactIntersect
dat er voor zorgt dat alleen kruisende objecten zullen worden teruggegeven:
request = QgsFeatureRequest().setFilterRect(areaOfInterest) \
.setFlags(QgsFeatureRequest.ExactIntersect)
Met setLimit()
kunt u het aantal gezochte objecten beperken. Hier is een voorbeeld:
request = QgsFeatureRequest()
request.setLimit(2)
for feature in layer.getFeatures(request):
print(feature)
<qgis._core.QgsFeature object at 0x7f9b78590948>
Als u in plaats daarvan een op attributen gebaseerd filter nodig heeft (of als aanvulling) van een ruimtelijke zoals weergegeven in de voorbeelden hierboven, kunt u een object QgsExpression
bouwen en dat doorgeven aan de constructor QgsFeatureRequest
. Hier is een voorbeeld:
# The expression will filter the features where the field "location_name"
# contains the word "Lake" (case insensitive)
exp = QgsExpression('location_name ILIKE \'%Lake%\'')
request = QgsFeatureRequest(exp)
Bekijk Expressies, filteren en waarden berekenen voor de details over de door QgsExpression
ondersteunde syntaxis.
Het verzoek kan worden gebruikt om de gegevens per opgehaald object te definiëren, zodat de doorloop alle objecten retourneert, maar slechts een deel van de gegevens van elk daarvan teruggeeft.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | # Only return selected fields to increase the "speed" of the request
request.setSubsetOfAttributes([0,2])
# More user friendly version
request.setSubsetOfAttributes(['name','id'],layer.fields())
# Don't return geometry objects to increase the "speed" of the request
request.setFlags(QgsFeatureRequest.NoGeometry)
# Fetch only the feature with id 45
request.setFilterFid(45)
# The options may be chained
request.setFilterRect(areaOfInterest).setFlags(QgsFeatureRequest.NoGeometry).setFilterFid(45).setSubsetOfAttributes([0,2])
|
6.4. Vectorlagen bewerken¶
De meeste vector gegevensproviders ondersteunen het bewerken van gegevens van de laag. Soms ondersteunen zij slechts een subset van mogelijke acties voor bewerken. Gebruik de functie capabilities()
om uit te zoeken welke set voor functionaliteiten wordt ondersteund.
caps = layer.dataProvider().capabilities()
# Check if a particular capability is supported:
if caps & QgsVectorDataProvider.DeleteFeatures:
print('The layer supports DeleteFeatures')
The layer supports DeleteFeatures
Bekijk, voor een lijst van alle beschikbare capabilities, de API Documentation of QgsVectorDataProvider
.
U kunt, om de tekstuele beschrijving van de capabilities van de laag af te drukken naar een kommagescheiden lijst, capabilitiesString()
gebruiken, zoals in het volgende voorbeeld:
1 2 3 4 5 6 | caps_string = layer.dataProvider().capabilitiesString()
# Print:
# 'Add Features, Delete Features, Change Attribute Values, Add Attributes,
# Delete Attributes, Rename Attributes, Fast Access to Features at ID,
# Presimplify Geometries, Presimplify Geometries with Validity Check,
# Transactions, Curved Geometries'
|
Bij het gebruiken van de volgende methodes voor het bewerken van vectorlagen worden de wijzigingen direct opgeslagen in de onderliggende gegevensbron (een bestand, database etc.). Voor het geval u slechts tijdelijke wijzigingen wilt uitvoeren, ga dan naar het volgende gedeelte waarin uitgelegd wordt hoe aanpassingen kunnen worden uitgevoerd met een bewerkingsbuffer.
Notitie
Als u werkt binnen QGIS (ofwel vanuit de console of vanuit een plug-in), zou het nodig kunnen zijn het opnieuw tekenen van het kaartvenster te forceren om de wijzigingen te kunnen zien die u heeft gemaakt aan de geometrie, aan de stijl of aan de attributen:
1 2 3 4 5 6 | # If caching is enabled, a simple canvas refresh might not be sufficient
# to trigger a redraw and you must clear the cached image for the layer
if iface.mapCanvas().isCachingEnabled():
layer.triggerRepaint()
else:
iface.mapCanvas().refresh()
|
6.4.1. Objecten toevoegen¶
Maak enkele instances QgsFeature
en geef daar een lijst van door aan de methode addFeatures()
van de provider. Het zal twee waarden teruggeven: resultaat (True
of False
) en een lijst van toegevoegde objecten (hun ID wordt ingesteld door de opslag van de gegevens).
U kunt, om de attributen in te stellen, ofwel het object initialiseren door een object QgsFields
door te geven (u kunt dat verkrijgen vanuit de methode fields()
van de vectorlaag) of initAttributes()
aan te roepen en het aantal velden op te geven die wilt hebben toegevoegd.
1 2 3 4 5 6 7 8 | if caps & QgsVectorDataProvider.AddFeatures:
feat = QgsFeature(layer.fields())
feat.setAttributes([0, 'hello'])
# Or set a single attribute by key or by index:
feat.setAttribute('name', 'hello')
feat.setAttribute(0, 'hello')
feat.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(123, 456)))
(res, outFeats) = layer.dataProvider().addFeatures([feat])
|
6.4.2. Objecten verwijderen¶
Geef eenvoudigweg een lijst van hun object-ID’s op om enkele objecten te verwijderen.
if caps & QgsVectorDataProvider.DeleteFeatures:
res = layer.dataProvider().deleteFeatures([5, 10])
6.4.3. Objecten bewerken¶
Het is mogelijk om de geometrie van objecten te wijzigen of enkele attributen. Het volgende voorbeeld wijzigt eerst waarden van attributen met de index 0 en 1, en wijzigt dan de geometrie van het object.
1 2 3 4 5 6 7 8 9 | fid = 100 # ID of the feature we will modify
if caps & QgsVectorDataProvider.ChangeAttributeValues:
attrs = { 0 : "hello", 1 : 123 }
layer.dataProvider().changeAttributeValues({ fid : attrs })
if caps & QgsVectorDataProvider.ChangeGeometries:
geom = QgsGeometry.fromPointXY(QgsPointXY(111,222))
layer.dataProvider().changeGeometryValues({ fid : geom })
|
Tip
Voorkeur voor klasse QgsVectorLayerEditUtils voor bewerken van alleen de geometrie
Als u alleen geometrieën wilt wijzigen, kunt u overwegen QgsVectorLayerEditUtils
te gebruiken wat enkele nuttige methoden verschaft om geometrieën te bewerken (vertalen, invoegen of punten verplaatsen etc.)
6.4.4. Vectorlagen bewerken met een bewerkingsbuffer¶
Bij het bewerken van vectoren binnen de toepassing QGIS, moet u eerst de modus Bewerken starten voor een bepaalde laag, dan enige aanpassingen te doen en tenslotte de wijzigingen vastleggen (of terugdraaien). Alle aanpassingen die u doet worden niet weggeschreven totdat u ze vastlegt — zij blijven in de bewerkingsbuffer van het geheugen van de laag. Het is mogelijk om deze functionaliteit ook programmatisch te gebruiken — het is simpelweg een andere methode voor het bewerken van vectorlagen die het direct gebruik van providers van gegevens aanvult. Gebruik deze optie bij het verschaffen van enkele gereedschappen voor de GUI voor het bewerken van vectorlagen, omdat dit de gebruiker in staat zal stellen te bepalen om vast te leggen/terug te draaien en maakt het gebruiken van Ongedaan maken/Opnieuw mogelijk. Bij het vastleggen van wijzigingen worden alle aanpassingen in de bewerkingsbuffer opgeslagen in de provider van de gegevens.
De methoden zijn soortgelijk aan die welke we hebben gezien in de provider, maar zij worden in plaats daarvan aangeroepen op het object QgsVectorLayer
.
De laag met in de modus Bewerken staan om deze methoden te kunnen laten werken. Gebruikt de methode startEditing()
om de modus Bewerken te starten. Gebruik de methoden commitChanges()
of rollBack()
om het bewerken te stoppen. De eerste zal al uw wijzigingen vastleggen in de gegevensbron, terwijl de tweede ze zal negeren en de gegevensbron in het geheel niet zal wijzigen.
Gebruik de methode isEditable()
om te weten te komen of een laag in de modus Bewerken staat.
Hier zijn enkele voorbeelden die demonstreren hoe deze methoden voor bewerken te gebruiken.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | from qgis.PyQt.QtCore import QVariant
feat1 = feat2 = QgsFeature(layer.fields())
fid = 99
feat1.setId(fid)
# add two features (QgsFeature instances)
layer.addFeatures([feat1,feat2])
# delete a feature with specified ID
layer.deleteFeature(fid)
# set new geometry (QgsGeometry instance) for a feature
geometry = QgsGeometry.fromWkt("POINT(7 45)")
layer.changeGeometry(fid, geometry)
# update an attribute with given field index (int) to a given value
fieldIndex =1
value ='My new name'
layer.changeAttributeValue(fid, fieldIndex, value)
# add new field
layer.addAttribute(QgsField("mytext", QVariant.String))
# remove a field
layer.deleteAttribute(fieldIndex)
|
De hierboven vermelde aanroepen moeten zijn opgenomen in opdrachten Ongedaan maken om er voor te zorgen dat Ongedaan maken/Opnieuw juist werkt. (Als Ongedaan maken/Opnieuw voor u niet van belang is en u wilt dat de wijzigingen onmiddellijk worden opgeslagen, dan zult u gemakkelijker werken met bewerken met gegevensprovider.)
Hier staat hoe u de functionaliteit Ongedaan maken kunt gebruiken:
1 2 3 4 5 6 7 8 9 10 11 12 | layer.beginEditCommand("Feature triangulation")
# ... call layer's editing methods ...
if problem_occurred:
layer.destroyEditCommand()
# ... tell the user that there was a problem
# and return
# ... more editing ...
layer.endEditCommand()
|
De methode beginEditCommand()
zal een interne “actieve” opdracht maken en zal opvolgende wijzigingen in de vectorlaag opnemen. Met de aanroep naar endEditCommand()
wordt de opdracht doorgegeven aan de stapel Ongedaan maken en de gebruiker zal in staat zijn om Ongedaan maken/Opnieuw uit te voeren vanuit de GUI. Voor het geval er iets verkeerd gaat bij het maken van de wijzigingen, zal de methode destroyEditCommand()
de opdracht verwijderen en de wijzigingen terugdraaien die al werden gemaakt toen deze opdracht actief was.
U kunt ook het argument with edit(layer)
-gebruiken om commit en rollback in een meer semantisch codeblok op te nemen zoals weergegeven in het voorbeeld hieronder:
with edit(layer):
feat = next(layer.getFeatures())
feat[0] = 5
layer.updateFeature(feat)
Dit zal aan het einde automatisch commitChanges()
aanroepen. Indien er een uitzondering optreedt, zal het rollBack()
alle wijzigingen. In het geval dat een probleem wordt tegengekomen binnen commitChanges()
(als de methode False teruggeeft) zal een uitzondering QgsEditError
optreden.
6.4.5. Velden toevoegen en verwijderen¶
U moet een lijst met definities voor velden opgeven om velden toe te voegen (attributen). Geef een lijst met indexen van velden op om velden te verwijderen.
1 2 3 4 5 6 7 8 9 | from qgis.PyQt.QtCore import QVariant
if caps & QgsVectorDataProvider.AddAttributes:
res = layer.dataProvider().addAttributes(
[QgsField("mytext", QVariant.String),
QgsField("myint", QVariant.Int)])
if caps & QgsVectorDataProvider.DeleteAttributes:
res = layer.dataProvider().deleteAttributes([0])
|
1 2 3 4 5 6 7 8 9 10 11 12 | # Alternate methods for removing fields
# first create temporary fields to be removed (f1-3)
layer.dataProvider().addAttributes([QgsField("f1",QVariant.Int),QgsField("f2",QVariant.Int),QgsField("f3",QVariant.Int)])
layer.updateFields()
count=layer.fields().count() # count of layer fields
ind_list=list((count-3, count-2)) # create list
# remove a single field with an index
layer.dataProvider().deleteAttributes([count-1])
# remove multiple fields with a list of indices
layer.dataProvider().deleteAttributes(ind_list)
|
Na het verwijderen of toevoegen van velden in de gegevensprovider moeten de velden van de laag worden bijgewerkt omdat de wijzigingen niet automatisch worden doorgevoerd.
layer.updateFields()
Tip
Wijzigingen direct opslaan met op with
gebaseerde opdracht
Gebruiken van with edit(layer):
de wijzigingen zullen automatisch worden vastgelegd door het aanroepen van commitChanges()
aan het einde. Indien er een uitzondering optreedt, zal het rollBack()
alle wijzigingen. Bekijk Vectorlagen bewerken met een bewerkingsbuffer.
6.5. Ruimtelijke index gebruiken¶
Ruimtelijke indexen kunnen de uitvoering van uw code enorm verbeteren als u frequent query’s moet uitvoeren op een vectorlaag. Stel u bijvoorbeeld voor dat u een algoritme voor interpolatie schrijft, en dat voor een bepaalde locatie u de 10 dichtstbijzijnde punten van een puntenlaag wilt weten om die punten te gebruiken voor het berekenen van de waarde voor de interpolatie. Zonder een ruimtelijke index is de enige manier waarop QGIS die 10 punten kan vinden is door de afstand vanaf elk punt tot de gespecificeerde locatie te berekenen en dan die afstanden te vergelijken. Dit kan een zeer tijdrovende taak zijn, speciaal als het moet worden herhaald voor verschillende locaties. Als er een ruimtelijke index bestaat voor de laag, is de bewerking veel effectiever.
Denk aan een laag zonder ruimtelijke index als aan een telefoonboek waarin telefoonnummers niet zijn gesorteerd of geïndexeerd. De enige manier om het telefoonnummer van een bepaald persoon te vinden is door vanaf het begin te lezen totdat u het vindt.
Ruimtelijke indexen worden niet standaard gemaakt voor een vectorlaag in QGIS, maar u kunt ze eenvoudig maken. Dit is wat u dan moet doen:
ruimtelijke index maken met de klasse
QgsSpatialIndex
:index = QgsSpatialIndex()
voeg objecten aan de index toe — index neemt object
QgsFeature
en voegt dat toe aan de interne gegevensstructuur. U kunt het object handmatig maken of er een gebruiken uit een eerdere aanroep naargetFeatures()
van de provider.index.addFeature(feat)
als alternatief kunt u alle objecten van een laag in één keer laden met behulp van bulk laden
index = QgsSpatialIndex(layer.getFeatures())
als de ruimtelijke index eenmaal is gevuld met enkele waarden, kunt u enkele query’s uitvoeren
1 2 3 4 5
# returns array of feature IDs of five nearest features nearest = index.nearestNeighbor(QgsPointXY(25.4, 12.7), 5) # returns array of IDs of features which intersect the rectangle intersect = index.intersects(QgsRectangle(22.5, 15.3, 23.1, 17.2))
U kunt ook de ruitemlijke index QgsSpatialIndexKDBush
gebruiken. Deze index is soortgelijk aan de standaard QgsSpatialIndex
maar:
ondersteunt alleen zelfstandige puntobjecten
is statisch (geen aanvullende objecten kunnen aan de index worden toegevoegd na het construeren)
is veel sneller!
maakt direct ophalen van de originele punten van het object mogelijk, zonder aanvullende verzoeken aan het object nodig te hebben
ondersteunt ware op afstand gebaseerde zoekacties, d.i. geeft alle punten terug binnen een straal vanaf een zoekpunt
6.6. De klasse QgsVectorLayerUtils¶
De klasse QgsVectorLayerUtils
bevat enkele zeer nuttige methoden die u kunt gebruiken met vectorlagen.
Bijvoorbeeld de methode createFeature()
prepareert een QgsFeature
om te worden toegevoegd aan een vectorlaag, met behoud van alle eventueel bestaande beperkingen en standaardwaarden van elk veld:
vlayer = QgsVectorLayer("testdata/airports.shp", "airports", "ogr")
feat = QgsVectorLayerUtils.createFeature(vlayer)
De methode getValues()
stelt u in staat snel de waarden van een veld of expressie op te halen:
1 2 3 4 5 | vlayer = QgsVectorLayer("testdata/airports.shp", "airports", "ogr")
# select only the first feature to make the output shorter
vlayer.selectByIds([1])
val = QgsVectorLayerUtils.getValues(vlayer, "NAME", selectedOnly=True)
print(val)
|
(['AMBLER'], True)
6.7. Vectorlagen maken¶
Er zijn verschillende manieren om een gegevensset uit een vectorlaag te maken:
de klasse
QgsVectorFileWriter
: Een handige klasse voor het schrijven van vectorbestanden naar schijf, ofwel met een statische aanroep naarwriteAsVectorFormat()
die de gehele vectorlaag opslaat of een instance van de klasse maken en aanroepen uitvoeren naaraddFeature()
. Deze klasse ondersteunt alle indelingen voor vector die OGR ondersteunt (GeoPackage, Shapefile, GeoJSON, KML en andere).de klasse
QgsVectorLayer
: instantieert een gegevensprovider die het opgegeven pad (URL) van de gegevensbron interpreteert om te verbinden met en toegang te verschaffen tot de gegevens. Het kan worden gebruikt om tijdelijke, op geheugen gebaseerde lagen (memory
), te maken en te verbinden met gegevenssets van OGR (ogr
), databases (postgres
,spatialite
,mysql
,mssql
) en meer (wfs
,gpx
,delimitedtext
…).
6.7.1. Vanuit een instance van QgsVectorFileWriter
¶
1 2 3 4 5 6 7 8 9 10 11 12 | # SaveVectorOptions contains many settings for the writer process
save_options = QgsVectorFileWriter.SaveVectorOptions()
transform_context = QgsProject.instance().transformContext()
# Write to a GeoPackage (default)
error = QgsVectorFileWriter.writeAsVectorFormatV2(layer,
"testdata/my_new_file.gpkg",
transform_context,
save_options)
if error[0] == QgsVectorFileWriter.NoError:
print("success!")
else:
print(error)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 | # Write to an ESRI Shapefile format dataset using UTF-8 text encoding
save_options = QgsVectorFileWriter.SaveVectorOptions()
save_options.driverName = "ESRI Shapefile"
save_options.fileEncoding = "UTF-8"
transform_context = QgsProject.instance().transformContext()
error = QgsVectorFileWriter.writeAsVectorFormatV2(layer,
"testdata/my_new_shapefile",
transform_context,
save_options)
if error[0] == QgsVectorFileWriter.NoError:
print("success again!")
else:
print(error)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | # Write to an ESRI GDB file
save_options = QgsVectorFileWriter.SaveVectorOptions()
save_options.driverName = "FileGDB"
# if no geometry
save_options.overrideGeometryType = QgsWkbTypes.Unknown
save_options.actionOnExistingFile = QgsVectorFileWriter.CreateOrOverwriteLayer
save_options.layerName = 'my_new_layer_name'
transform_context = QgsProject.instance().transformContext()
gdb_path = "testdata/my_example.gdb"
error = QgsVectorFileWriter.writeAsVectorFormatV2(layer,
gdb_path,
transform_context,
save_options)
if error[0] == QgsVectorFileWriter.NoError:
print("success!")
else:
print(error)
|
U kunt ook velden converteren om ze uitwisselbaar te maken met verschillende indelingen door de klasse FieldValueConverter
te gebruiken. Bijvoorbeeld om typen arryvariabelen (bijv. in Postgres) te converteren naar een type tekst, kunt u het volgende doen:
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 | LIST_FIELD_NAME = 'xxxx'
class ESRIValueConverter(QgsVectorFileWriter.FieldValueConverter):
def __init__(self, layer, list_field):
QgsVectorFileWriter.FieldValueConverter.__init__(self)
self.layer = layer
self.list_field_idx = self.layer.fields().indexFromName(list_field)
def convert(self, fieldIdxInLayer, value):
if fieldIdxInLayer == self.list_field_idx:
return QgsListFieldFormatter().representValue(layer=vlayer,
fieldIndex=self.list_field_idx,
config={},
cache=None,
value=value)
else:
return value
def fieldDefinition(self, field):
idx = self.layer.fields().indexFromName(field.name())
if idx == self.list_field_idx:
return QgsField(LIST_FIELD_NAME, QVariant.String)
else:
return self.layer.fields()[idx]
converter = ESRIValueConverter(vlayer, LIST_FIELD_NAME)
opts = QgsVectorFileWriter.SaveVectorOptions()
opts.fieldValueConverter = converter
|
Een doel-CRS mag ook worden gespecificeerd — als een geldige instance van QgsCoordinateReferenceSystem
wordt doorgegeven als de vierde parameter, wordt de laag naar dat CRS getransformeerd.
Roep voor geldige namen van stuurprogramma’s de methode supportedFiltersAndFormats()
aan of raadpleeg de door OGR ondersteunde indelingen — u zou de waarde in de kolom “Code” moeten doorgeven als de naam van het stuurprogramma.
Optioneel kunt u instellen of of u alleen geselecteerde objecten wilt exporteren, meer driver-specifieke opties voor maken wilt doorgeven of de schrijven wilt vertellen om geen attributen aan te maken… Er zijn een aantal andere (optionele) parameters; bekijk de documentatie voor QgsVectorFileWriter
voor details.
6.7.2. Direct uit objecten¶
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 35 36 37 38 39 40 41 42 43 44 45 | from qgis.PyQt.QtCore import QVariant
# define fields for feature attributes. A QgsFields object is needed
fields = QgsFields()
fields.append(QgsField("first", QVariant.Int))
fields.append(QgsField("second", QVariant.String))
""" create an instance of vector file writer, which will create the vector file.
Arguments:
1. path to new file (will fail if exists already)
2. field map
3. geometry type - from WKBTYPE enum
4. layer's spatial reference (instance of
QgsCoordinateReferenceSystem)
5. coordinate transform context
6. save options (driver name for the output file, encoding etc.)
"""
crs = QgsProject.instance().crs()
transform_context = QgsProject.instance().transformContext()
save_options = QgsVectorFileWriter.SaveVectorOptions()
save_options.driverName = "ESRI Shapefile"
save_options.fileEncoding = "UTF-8"
writer = QgsVectorFileWriter.create(
"testdata/my_new_shapefile.shp",
fields,
QgsWkbTypes.Point,
crs,
transform_context,
save_options
)
if writer.hasError() != QgsVectorFileWriter.NoError:
print("Error when creating shapefile: ", writer.errorMessage())
# add a feature
fet = QgsFeature()
fet.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(10,10)))
fet.setAttributes([1, "text"])
writer.addFeature(fet)
# delete the writer to flush features to disk
del writer
|
6.7.3. Vanuit een instance van QgsVectorLayer
¶
Laten we, naast alle gegevensproviders die worden ondersteund door de klasse QgsVectorLayer
, ons focussen op de op geheugen gebaseerde lagen. Memory-provider is bedoeld om hoofdzakelijk te worden gebruikt door plug-ins of ontwikkelaars voor 3e partijen. Het slaat geen gegevens op de schijf op, wat ontwikkelaars in staat stelt het te gebruiken als snel backend voor enkele tijdelijke lagen.
De provider ondersteunt velden string, int en double.
De memory-provider ondersteunt ook ruimtelijke indexen, wat wordt ingeschakeld door de functie van de provider createSpatialIndex()
aan te roepen. Als de ruimtelijke index eenmaal is gemaakt zult u in staat zijn objecten in kleinere regio’s sneller te doorlopen (omdat het niet nodig is door alle objecten te gaan, alleen die in de gespecificeerde rechthoek).
Een memory-provider wordt gemaakt door "memory"
door te geven als de string voor de provider string aan de constructor QgsVectorLayer
.
De constructor accepteert ook een URI die het type geometrie van de laag definieert, één van: "Point"
, "LineString"
, "Polygon"
, "MultiPoint"
, "MultiLineString"
, "MultiPolygon"
of "None"
.
De URI mag ook het coördinaten referentiesysteem specificeren, velden, en indexeren van de memory-provider in de URI. De syntaxis is:
- crs=definition
Specificeert het coördinaten referentiesysteem, waar definition een van de vormen kan zijn die worden geaccepteerd door
QgsCoordinateReferenceSystem.createFromString()
- index=yes
Specificeert dat de provider een ruimtelijke index zal gebruiken
- field=name:type(length,precision)
Specificeert een attribuut van de laag. Het attribuut heeft een naam en, optioneel, een type (integer, double of string), lengte en precisie. Er kunnen meerdere definities voor velden zijn.
Het volgende voorbeeld van een URI bevat al deze opties
"Point?crs=epsg:4326&field=id:integer&field=name:string(20)&index=yes"
De volgende voorbeeldcode illustreert het maken en vullen van een memory-provider
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | from qgis.PyQt.QtCore import QVariant
# create layer
vl = QgsVectorLayer("Point", "temporary_points", "memory")
pr = vl.dataProvider()
# add fields
pr.addAttributes([QgsField("name", QVariant.String),
QgsField("age", QVariant.Int),
QgsField("size", QVariant.Double)])
vl.updateFields() # tell the vector layer to fetch changes from the provider
# add a feature
fet = QgsFeature()
fet.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(10,10)))
fet.setAttributes(["Johny", 2, 0.3])
pr.addFeatures([fet])
# update layer's extent when new features have been added
# because change of extent in provider is not propagated to the layer
vl.updateExtents()
|
Laten we tenslotte controleren of alles goed ging
1 2 3 4 5 6 7 8 9 10 | # show some stats
print("fields:", len(pr.fields()))
print("features:", pr.featureCount())
e = vl.extent()
print("extent:", e.xMinimum(), e.yMinimum(), e.xMaximum(), e.yMaximum())
# iterate over features
features = vl.getFeatures()
for fet in features:
print("F:", fet.id(), fet.attributes(), fet.geometry().asPoint())
|
fields: 3
features: 1
extent: 10.0 10.0 10.0 10.0
F: 1 ['Johny', 2, 0.3] <QgsPointXY: POINT(10 10)>
6.8. Uiterlijk (symbologie) van vectorlagen¶
Wanneer een vectorlaag wordt gerenderd wordt het uiterlijk van de gegevens verschaft door de renderer en symbolen geassocieerd met de laag. Symbolen zijn klassen die zorg dragen voor het tekenen van visuele weergaven van objecten, terwijl renderers bepalen welk symbool zal worden gebruikt voor een bepaald object.
De renderer voor een bepaalde laag kan worden verkregen zoals hieronder is weergegeven:
renderer = layer.renderer()
En met die verwijzing, laten we het een beetje verkennen
print("Type:", renderer.type())
Type: singleSymbol
Er zijn verschillende bekende typen renderer beschikbaar in de bron-bibliotheek van QGIS:
Type |
Klasse |
Omschrijving |
---|---|---|
singleSymbol |
Rendert alle objecten met hetzelfde symbool |
|
categorizedSymbol |
Rendert objecten door een ander symbool voor elke categorie te gebruiken |
|
graduatedSymbol |
Rendert objecten door een ander symbool voor elke bereik van waarden te gebruiken |
Er kunnen ook enkele aangepaste typen renderer zijn, dus doe nooit de aanname dat alleen deze typen beschikbaar zijn. U kunt het QgsRendererRegistry
van de toepassing bevragen om de huidige beschikbare renderers te achterhalen:
print(QgsApplication.rendererRegistry().renderersList())
['nullSymbol', 'singleSymbol', 'categorizedSymbol', 'graduatedSymbol', 'RuleRenderer', 'pointDisplacement', 'pointCluster', 'invertedPolygonRenderer', 'heatmapRenderer', '25dRenderer']
Het is mogelijk om een dump te verkrijgen van de inhoud van een renderer in de vorm van tekst — kan handig zijn bij debuggen
renderer.dump()
SINGLE: MARKER SYMBOL (1 layers) color 190,207,80,255
6.8.1. Renderer Enkel symbool¶
U kunt het voor de rendering gebruikte symbool verkrijgen door de methode symbol()
aan te roepen en die te wijzigen met de methode setSymbol()
(opmerking voor ontwikkelaars in C++: de renderer wordt eigenaar van het symbool.)
U kunt het symbool dat wordt gebruikt door een bepaalde vectorlaag wijzigen door setSymbol()
aan te roepen die een instance doorgeeft van de toepasselijke symbool instance. Symbolen voor lagen punt, lijn en polygoon kunnen worden gemaakt door het aanroepen van de functie createSimple()
van de overeenkomende klassen QgsMarkerSymbol
, QgsLineSymbol
en QgsFillSymbol
.
Het aan createSimple()
doorgegeven woordenboek stelt de eigenschappen voor de stijl van het symbool in.
U kunt bijvoorbeeld het gebruikte symbool voor een bepaalde punt-laag wijzigen door setSymbol()
aan te roepen die een instance doorgeeft van een :class:`QgsMarkerSymbol <qgis.core.QgsMarkerSymbol> zoals in het volgende voorbeeld van code:
symbol = QgsMarkerSymbol.createSimple({'name': 'square', 'color': 'red'})
layer.renderer().setSymbol(symbol)
# show the change
layer.triggerRepaint()
name
geeft de vorm van de markering aan, en kan één van de volgende zijn:
circle
square
cross
rectangle
diamond
pentagon
triangle
equilateral_triangle
star
regular_star
arrow
filled_arrowhead
x
U kunt de voorbeeldcode volgen om een volledige lijst met eigenschappen te verkrijgen van de eerste symboollaag van een instance symbool :
print(layer.renderer().symbol().symbolLayers()[0].properties())
{'angle': '0', 'color': '255,0,0,255', 'horizontal_anchor_point': '1', 'joinstyle': 'bevel', 'name': 'square', 'offset': '0,0', 'offset_map_unit_scale': '3x:0,0,0,0,0,0', 'offset_unit': 'MM', 'outline_color': '35,35,35,255', 'outline_style': 'solid', 'outline_width': '0', 'outline_width_map_unit_scale': '3x:0,0,0,0,0,0', 'outline_width_unit': 'MM', 'scale_method': 'diameter', 'size': '2', 'size_map_unit_scale': '3x:0,0,0,0,0,0', 'size_unit': 'MM', 'vertical_anchor_point': '1'}
Dit kan nuttig zijn als u enkele eigenschappen wilt wijzigen:
1 2 3 4 5 6 7 8 9 10 | # You can alter a single property...
layer.renderer().symbol().symbolLayer(0).setSize(3)
# ... but not all properties are accessible from methods,
# you can also replace the symbol completely:
props = layer.renderer().symbol().symbolLayer(0).properties()
props['color'] = 'yellow'
props['name'] = 'square'
layer.renderer().setSymbol(QgsMarkerSymbol.createSimple(props))
# show the changes
layer.triggerRepaint()
|
6.8.2. Renderer symbool Categoriën¶
Bij het gebruiken van een renderer Categorieën kunt u het attribuut dat is gebruikt voor de classificatie bevragen en instellen: gebruik de methoden classAttribute()
en setClassAttribute()
.
Een lijst categorieën verkrijgen
1 2 3 4 5 6 7 8 9 | categorized_renderer = QgsCategorizedSymbolRenderer()
# Add a few categories
cat1 = QgsRendererCategory('1', QgsMarkerSymbol(), 'category 1')
cat2 = QgsRendererCategory('2', QgsMarkerSymbol(), 'category 2')
categorized_renderer.addCategory(cat1)
categorized_renderer.addCategory(cat2)
for cat in categorized_renderer.categories():
print("{}: {} :: {}".format(cat.value(), cat.label(), cat.symbol()))
|
1: category 1 :: <qgis._core.QgsMarkerSymbol object at 0x7f378ffcd9d8>
2: category 2 :: <qgis._core.QgsMarkerSymbol object at 0x7f378ffcd9d8>
Waar value()
de waarde is die wordt gebruikt voor het onderscheiden van de categorieën, label()
is een tekst die gebruikt wordt voor de omschrijving van de categorie en de methode symbol()
geeft het toegewezen symbool terug.
De renderer slaat gewoonlijk ook het originele symbool en de kleurenbalk op die voor de classificatie werden gebruikt: methoden sourceColorRamp()
en sourceSymbol()
.
6.8.3. Renderer symbool Gradueel¶
Deze renderer lijkt erg veel op de renderer voor het symbool van de categorieën, hierboven beschreven, maar in plaats van één attribuutwaarde per klasse, werkt het met bereiken van waarden en kan dus alleen gebruikt worden met numerieke attributen.
Meer te weten komen over gebruikte bereiken in de renderer
1 2 3 4 5 6 7 8 9 10 11 12 | graduated_renderer = QgsGraduatedSymbolRenderer()
# Add a few categories
graduated_renderer.addClassRange(QgsRendererRange(QgsClassificationRange('class 0-100', 0, 100), QgsMarkerSymbol()))
graduated_renderer.addClassRange(QgsRendererRange(QgsClassificationRange('class 101-200', 101, 200), QgsMarkerSymbol()))
for ran in graduated_renderer.ranges():
print("{} - {}: {} {}".format(
ran.lowerValue(),
ran.upperValue(),
ran.label(),
ran.symbol()
))
|
0.0 - 100.0: class 0-100 <qgis._core.QgsMarkerSymbol object at 0x7f8bad281b88>
101.0 - 200.0: class 101-200 <qgis._core.QgsMarkerSymbol object at 0x7f8bad281b88>
U kunt opnieuw de methoden classAttribute()
(om de naam van het attribuut voor classificatie te zoeken), sourceSymbol()
en sourceColorRamp()
gebruiken. Aanvullend is er de methode mode()
die bepaalt hoe de bereiken werden gemaakt: met behulp van gelijke intervallen, kwantielen of een andere methode.
Als u uw eigen renderer voor symbolen Gradueel wilt maken, kunt u dat doen zoals is geïllustreerd in het voorbeeldsnippet hieronder (wat een eenvoudige schikking in twee klassen maakt)
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 | from qgis.PyQt import QtGui
myVectorLayer = QgsVectorLayer("testdata/airports.shp", "airports", "ogr")
myTargetField = 'ELEV'
myRangeList = []
myOpacity = 1
# Make our first symbol and range...
myMin = 0.0
myMax = 50.0
myLabel = 'Group 1'
myColour = QtGui.QColor('#ffee00')
mySymbol1 = QgsSymbol.defaultSymbol(myVectorLayer.geometryType())
mySymbol1.setColor(myColour)
mySymbol1.setOpacity(myOpacity)
myRange1 = QgsRendererRange(myMin, myMax, mySymbol1, myLabel)
myRangeList.append(myRange1)
#now make another symbol and range...
myMin = 50.1
myMax = 100
myLabel = 'Group 2'
myColour = QtGui.QColor('#00eeff')
mySymbol2 = QgsSymbol.defaultSymbol(
myVectorLayer.geometryType())
mySymbol2.setColor(myColour)
mySymbol2.setOpacity(myOpacity)
myRange2 = QgsRendererRange(myMin, myMax, mySymbol2, myLabel)
myRangeList.append(myRange2)
myRenderer = QgsGraduatedSymbolRenderer('', myRangeList)
myClassificationMethod = QgsApplication.classificationMethodRegistry().method("EqualInterval")
myRenderer.setClassificationMethod(myClassificationMethod)
myRenderer.setClassAttribute(myTargetField)
myVectorLayer.setRenderer(myRenderer)
|
6.8.4. Werken met symbolen¶
Voor het weergeven van symbolen is er de basisklasse QgsSymbol
met drie afgeleide klassen:
QgsMarkerSymbol
— voor objecten puntQgsLineSymbol
— voor objecten lijnQgsFillSymbol
— voor objecten polygoon
Elk symbool bestaat uit één of meer symboollagen (klassen afgeleid van QgsSymbolLayer
). De symboollagen doen de actuele rendering, de symboolklasse zelf dient alleen als een container voor de symboollagen.
Met een instance van een symbool (bijv. van een renderer), is het mogelijk om het te verkennen: de methode type()
zegt of het een symbool markering, lijn of vulling is. Er is de methode dump()
wat een korte omschrijving van het symbool teruggeeft. Een lijst van symboollagen verkrijgen:
marker_symbol = QgsMarkerSymbol()
for i in range(marker_symbol.symbolLayerCount()):
lyr = marker_symbol.symbolLayer(i)
print("{}: {}".format(i, lyr.layerType()))
0: SimpleMarker
Gebruik de methode color()
om de kleur van het symbool vast te stellen en setColor()
en angle()
, voor lijnsymbolen geeft de methode width()
de dikte van de lijn terug.
Grootte en breedte zijn standaard in millimeters, hoeken zijn in graden.
6.8.4.1. Werken met symboollagen¶
Zoals eerder gezegd bepalen symboollagen (subklassen van QgsSymbolLayer
) het uiterlijk van de objecten. Er zijn verscheidene basisklassen voor symboollagen voor algemeen gebruik. Het is mogelijk om nieuwe typen symboollagen te implementeren en dus willekeurig aan te passen hoe objecten zullen worden gerenderd. De methode layerType()
identificeert uniek de klasse van de symboollaag — de basis en standaard zijn de typen symboollagen SimpleMarker
, SimpleLine
en SimpleFill
.
U kunt een volledige lijst van de typen symboollagen, die u voor een bepaalde klasse van een symboollaag kunt maken, verkrijgen met de volgende code:
1 2 3 4 5 | from qgis.core import QgsSymbolLayerRegistry
myRegistry = QgsApplication.symbolLayerRegistry()
myMetadata = myRegistry.symbolLayerMetadata("SimpleFill")
for item in myRegistry.symbolLayersForType(QgsSymbol.Marker):
print(item)
|
1 2 3 4 5 6 7 8 9 | EllipseMarker
FilledMarker
FontMarker
GeometryGenerator
MaskMarker
RasterMarker
SimpleMarker
SvgMarker
VectorField
|
Klasse QgsSymbolLayerRegistry
beheert een database van alle beschikbare typen symboollagen.
Gebruik zijn methode properties()
om toegang te verkrijgen tot de gegevens van de symboollaag, die een woordenboek met paren van sleutels-waarden teruggeeft van eigenschappen die het uiterlijk bepalen. Elke type symboollaag heeft een specifieke set eigenschappen die het gebruikt. Aanvullend zijn er de generieke methoden color()
, size()
, angle()
en width()
met hun tegenhangers om ze in te stellen. Natuurlijk zijn size() en angle() alleen beschikbaar voor symboollagen voor markeringen en width() voor lijn-symboollagen.
6.8.4.2. Aangepaste typen voor symboollagen maken¶
Veronderstel dat u de manier waarop gegevens worden gerenderd wilt aanpassen. U kunt uw eigen klasse voor de symboollaag maken dat de objecten op exact de wijze die u wilt tekent. Hier is een voorbeeld van een markering die rode cirkels met een gespecificeerde straal tekent
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 | from qgis.core import QgsMarkerSymbolLayer
from qgis.PyQt.QtGui import QColor
class FooSymbolLayer(QgsMarkerSymbolLayer):
def __init__(self, radius=4.0):
QgsMarkerSymbolLayer.__init__(self)
self.radius = radius
self.color = QColor(255,0,0)
def layerType(self):
return "FooMarker"
def properties(self):
return { "radius" : str(self.radius) }
def startRender(self, context):
pass
def stopRender(self, context):
pass
def renderPoint(self, point, context):
# Rendering depends on whether the symbol is selected (QGIS >= 1.5)
color = context.selectionColor() if context.selected() else self.color
p = context.renderContext().painter()
p.setPen(color)
p.drawEllipse(point, self.radius, self.radius)
def clone(self):
return FooSymbolLayer(self.radius)
|
De methode layerType()
bepaalt de naam van de symboollaag, die moet uniek zijn voor alle symboollagen. De methode properties()
wordt gebruikt voor het behouden van attributen. De methode clone()
moet een kopie teruggeven van de symboollaag met exact dezelfde attributen. Tenslotte zijn er methoden voor renderen: startRender()
wordt aangeroepen vóór het renderen van het eerste object, stopRender()
als het renderen is voltooid en de methode renderPoint()
wordt aangeroepen om het renderen uit te voeren. De coördinaten van de punt(en) zijn al getransformeerd naar de coördinaten voor uitvoer.
Voor polylijnen en polygonen zou het enige verschil liggen in de methode van renderen: u zou renderPolyline()
gebruiken, welke een lijst met lijnen zou ontvangen, terwijl renderPolygon()
een lijst van punten op de buitenste ring als de eerste parameter ontvangt en een lijst van binnenringen (of None) als een tweede parameter.
Gewoonlijk is het handig om een GUI toe te voegen voor het instellen van attributen voor het type symboollaag om het voor gebruikers mogelijk te maken het uiterlijk aan te passen: in het geval van ons voorbeeld hierboven kunnen we de gebruiker de straal van de cirkel laten instellen. De volgende code implementeert een dergelijk widget
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 | from qgis.gui import QgsSymbolLayerWidget
class FooSymbolLayerWidget(QgsSymbolLayerWidget):
def __init__(self, parent=None):
QgsSymbolLayerWidget.__init__(self, parent)
self.layer = None
# setup a simple UI
self.label = QLabel("Radius:")
self.spinRadius = QDoubleSpinBox()
self.hbox = QHBoxLayout()
self.hbox.addWidget(self.label)
self.hbox.addWidget(self.spinRadius)
self.setLayout(self.hbox)
self.connect(self.spinRadius, SIGNAL("valueChanged(double)"), \
self.radiusChanged)
def setSymbolLayer(self, layer):
if layer.layerType() != "FooMarker":
return
self.layer = layer
self.spinRadius.setValue(layer.radius)
def symbolLayer(self):
return self.layer
def radiusChanged(self, value):
self.layer.radius = value
self.emit(SIGNAL("changed()"))
|
Deze widget kan worden ingebed in het dialoogvenster van de eigenschappen voor het symbool. Wanneer het type symboollaag wordt geselecteerd in het dialoogvenster van de eigenschappen voor het symbool, maakt het een instance van de symboollaag en een instance van de widget van de symboollaag. Dan roept het de methode setSymbolLayer()
aan om de symboollaag toe te wijzen aan de widget. In die methode zou de widget de UI moeten bijwerken om de attributen van de symboollaag weer te geven. De methode symbolLayer()
wordt gebruikt om de symboollaag opnieuw op te halen bij het dialoogvenster Eigenschappen om het voor het symbool te gebruiken.
Bij elke wijziging van attributen zou de widget een signaal changed()
moeten uitzenden om het dialoogvenster Eigenschappen het voorbeeld van het symbool bij te laten werken.
Nu missen we alleen nog de uiteindelijke lijm: om QGIS zich bewust te laten worden van deze nieuwe klassen. Dit wordt gedaan door de symboollaag toe te voegen aan het register. Het is mogelijk om de symboollaag ook te gebruiken zonder die toe te voegen aan het register, maar sommige functionaliteit zal niet werken: bijv. het laden van projectbestanden met de aangepaste symboollagen of de mogelijkheid om de attributen van de laag te bewerken in de GUI.
We zullen metadata moeten maken voor de symboollaag
1 2 3 4 5 6 7 8 9 10 11 12 | from qgis.core import QgsSymbol, QgsSymbolLayerAbstractMetadata, QgsSymbolLayerRegistry
class FooSymbolLayerMetadata(QgsSymbolLayerAbstractMetadata):
def __init__(self):
super().__init__("FooMarker", "My new Foo marker", QgsSymbol.Marker)
def createSymbolLayer(self, props):
radius = float(props["radius"]) if "radius" in props else 4.0
return FooSymbolLayer(radius)
fslmetadata = FooSymbolLayerMetadata()
|
QgsApplication.symbolLayerRegistry().addSymbolLayerType(fslmetadata)
U zou het type laag (hetzelfde als welke wordt teruggegeven door de laag) en type symbool (markering/lijn/vulling) moeten doorgeven aan de constructor van de bovenliggende klasse. De methode createSymbolLayer()
zorgt voor het maken van een instance van de symboollaag met attributen die zijn gespecificeerd in het woordenboek props. En er is de methode createSymbolLayerWidget()
die de instellingen voor de widget teruggeeft voor dit type symboollaag.
De laatste stap is om deze symboollaag toe te voegen aan het register — en we zijn klaar.
6.8.5. Aangepaste renderers maken¶
Het zou handig kunnen zijn om een nieuwe implementatie voor de renderer te maken als u de regels voor het selecteren van symbolen voor het renderen van objecten zou willen aanpassen. Sommige gebruiken gevallen waarin u dit zou willen doen: symbool wordt bepaald uit een combinatie van velden, grootte van symbolen wijzigt, afhankelijk van hun huidige schaal etc.
De volgende code geeft een eenvoudige aangepaste renderer weer die twee markeringssymbolen maakt en er, willekeurig, één kiest voor elk object
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 | import random
from qgis.core import QgsWkbTypes, QgsSymbol, QgsFeatureRenderer
class RandomRenderer(QgsFeatureRenderer):
def __init__(self, syms=None):
super().__init__("RandomRenderer")
self.syms = syms if syms else [
QgsSymbol.defaultSymbol(QgsWkbTypes.geometryType(QgsWkbTypes.Point)),
QgsSymbol.defaultSymbol(QgsWkbTypes.geometryType(QgsWkbTypes.Point))
]
def symbolForFeature(self, feature, context):
return random.choice(self.syms)
def startRender(self, context, fields):
super().startRender(context, fields)
for s in self.syms:
s.startRender(context, fields)
def stopRender(self, context):
super().stopRender(context)
for s in self.syms:
s.stopRender(context)
def usedAttributes(self, context):
return []
def clone(self):
return RandomRenderer(self.syms)
|
De constructor van de bovenliggende klasse QgsFeatureRenderer
heeft de naam van de renderer nodig (die uniek moet zijn voor alle renderers). De methode symbolForFeature()
is die welke bepaalt welk symbool zal worden gebruikt voor een bepaald object. startRender()
en stopRender
zorgen voor initialisatie/finalisatie van het renderen van het symbool. De methode usedAttributes()
kan een lijst met veldnamen teruggeven waarvan de renderer verwacht dat die aanwezig is. Tenslotte zou de methode clone()
een kopie van de renderer moeten teruggeven.
Net als met symboollagen is het mogelijk een GUI toe te voegen voor de configuratie van de renderer. Die moet worden afgeleid uit QgsRendererWidget
. De volgende voorbeeldcode maakt een knop die de gebruiker in staat stelt het eerste symbool in te stellen
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 | from qgis.gui import QgsRendererWidget, QgsColorButton
class RandomRendererWidget(QgsRendererWidget):
def __init__(self, layer, style, renderer):
super().__init__(layer, style)
if renderer is None or renderer.type() != "RandomRenderer":
self.r = RandomRenderer()
else:
self.r = renderer
# setup UI
self.btn1 = QgsColorButton()
self.btn1.setColor(self.r.syms[0].color())
self.vbox = QVBoxLayout()
self.vbox.addWidget(self.btn1)
self.setLayout(self.vbox)
self.btn1.colorChanged.connect(self.setColor1)
def setColor1(self):
color = self.btn1.color()
if not color.isValid(): return
self.r.syms[0].setColor(color)
def renderer(self):
return self.r
|
De constructor ontvangt instances van de actieve laag (QgsVectorLayer
), de globale opmaak (QgsStyle
) en huidige renderer. Indien er geen renderer is of de renderer heeft een ander type, zal die worden vervangen door onze nieuwe renderer, anders zullen we de huidige renderer gebruiken (die al het type heeft dat we nodig hebben). De inhoud van de widget zou moeten worden bijgewerkt om de huidige staat van de renderer weer te geven. Wanneer het dialoogvenster van de renderer wordt geaccepteerd, wordt de methode voor de widget renderer()
aangeroepen om de huidige renderer te verkrijgen — die zal worden toegewezen aan de laag.
Het laatste ontbrekende gedeelte zijn de metadata voor de renderer en het registreren in het register, anders zal het laden van de lagen met de renderer niet werken en zal de gebruiker niet in staat zijn die te selecteren uit de lijst met renderers. Laten we ons voorbeeld RandomRenderer voltooien
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | from qgis.core import (
QgsRendererAbstractMetadata,
QgsRendererRegistry,
QgsApplication
)
class RandomRendererMetadata(QgsRendererAbstractMetadata):
def __init__(self):
super().__init__("RandomRenderer", "Random renderer")
def createRenderer(self, element):
return RandomRenderer()
def createRendererWidget(self, layer, style, renderer):
return RandomRendererWidget(layer, style, renderer)
rrmetadata = RandomRendererMetadata()
|
QgsApplication.rendererRegistry().addRenderer(rrmetadata)
Soortgelijk als met de symboollagen, verwacht de constructor voor abstracte metadata de naam van de renderer, de zichtbare naam voor de gebruikers en optioneel de naam van het pictogram voor de renderer. De methode createRenderer()
geeft de instance QDomElement
door die kan worden gebruikt om de status van de renderer opnieuw op te slaan in de boom van de DOM. De methode createRendererWidget()
maakt het widget voor de configuratie. Die hoeft niet aanwezig te zijn of mag None
teruggeven als de renderer geen GUI heeft.
U kunt, om een pictogram te associëren met de renderer, die toewijzen in de constructor QgsRendererAbstractMetadata
als een derde (optioneel) argument — de basis klassse-constructor in de functie __init__()
van de RandomRendererMetadata wordt
QgsRendererAbstractMetadata.__init__(self,
"RandomRenderer",
"Random renderer",
QIcon(QPixmap("RandomRendererIcon.png", "png")))
Het pictogram kan ook op een later tijdstip worden geassocieerd met de methode setIcon()
van de klasse van de metadata. Het pictogram kan worden geladen vanuit een bestand (zoals hierboven weergegeven) of kan worden geladen vanuit een Qt resource (PyQt5 bevat .qrc compiler voor Python).
6.9. Meer onderwerpen¶
TODO:
symbolen maken/aanpassen
werken met stijl (
QgsStyle
)werken met kleurverlopen (
QgsColorRamp
)symboollaag en registraties van renderer verkennen