Viktigt
Översättning är en gemenskapsinsats du kan gå med i. Den här sidan är för närvarande översatt till 100.00%.
6. Använda vektorlager
Råd
Kodsnuttarna på den här sidan behöver följande import om du befinner dig utanför pyqgis-konsolen:
1from qgis.core import (
2 QgsApplication,
3 QgsDataSourceUri,
4 QgsCategorizedSymbolRenderer,
5 QgsClassificationRange,
6 QgsPointXY,
7 QgsProject,
8 QgsExpression,
9 QgsField,
10 QgsFields,
11 QgsFeature,
12 QgsFeatureRequest,
13 QgsFeatureRenderer,
14 QgsGeometry,
15 QgsGraduatedSymbolRenderer,
16 QgsMarkerSymbol,
17 QgsMessageLog,
18 QgsRectangle,
19 QgsRendererCategory,
20 QgsRendererRange,
21 QgsSymbol,
22 QgsVectorDataProvider,
23 QgsVectorLayer,
24 QgsVectorFileWriter,
25 QgsWkbTypes,
26 QgsSpatialIndex,
27 QgsVectorLayerUtils
28)
29
30from qgis.core.additions.edit import edit
31
32from qgis.PyQt.QtGui import (
33 QColor,
34)
I detta avsnitt sammanfattas olika åtgärder som kan göras med vektorlager.
Det mesta arbetet här baseras på metoderna i klassen QgsVectorLayer
.
6.1. Hämta information om attribut
Du kan hämta information om de fält som associeras med ett vektorskikt genom att anropa fields()
på ett QgsVectorLayer
-objekt:
vlayer = QgsVectorLayer("testdata/data/data.gpkg|layername=airports", "Airports layer", "ogr")
for field in vlayer.fields():
print(field.name(), field.typeName())
1fid Integer64
2id Integer64
3scalerank Integer64
4featurecla String
5type String
6name String
7abbrev String
8location String
9gps_code String
10iata_code String
11wikipedia String
12natlscale Real
Metoderna displayField()
och mapTipTemplate()
ger information om det fält och den mall som används i fliken Egenskaper för display.
När du laddar ett vektorlager väljs alltid ett fält av QGIS som Display Name
, medan HTML Map Tip
är tomt som standard. Med dessa metoder kan du enkelt få båda:
vlayer = QgsVectorLayer("testdata/data/data.gpkg|layername=airports", "Airports layer", "ogr")
print(vlayer.displayField())
name
Observera
Om du ändrar Display Name
från ett fält till ett uttryck måste du använda displayExpression()
istället för displayField()
.
6.2. Iteration över vektorlager
Att iterera över funktionerna i ett vektorlager är en av de vanligaste uppgifterna. Nedan följer ett exempel på en enkel grundläggande kod för att utföra denna uppgift och visa lite information om varje funktion. Variabeln layer
antas ha ett objekt av QgsVectorLayer
.
1# "layer" is a QgsVectorLayer instance
2layer = iface.activeLayer()
3features = layer.getFeatures()
4
5for feature in features:
6 # retrieve every feature with its geometry and attributes
7 print("Feature ID: ", feature.id())
8 # fetch geometry
9 # show some information about the feature geometry
10 geom = feature.geometry()
11 geomSingleType = QgsWkbTypes.isSingleType(geom.wkbType())
12 if geom.type() == QgsWkbTypes.PointGeometry:
13 # the geometry type can be of single or multi type
14 if geomSingleType:
15 x = geom.asPoint()
16 print("Point: ", x)
17 else:
18 x = geom.asMultiPoint()
19 print("MultiPoint: ", x)
20 elif geom.type() == QgsWkbTypes.LineGeometry:
21 if geomSingleType:
22 x = geom.asPolyline()
23 print("Line: ", x, "length: ", geom.length())
24 else:
25 x = geom.asMultiPolyline()
26 print("MultiLine: ", x, "length: ", geom.length())
27 elif geom.type() == QgsWkbTypes.PolygonGeometry:
28 if geomSingleType:
29 x = geom.asPolygon()
30 print("Polygon: ", x, "Area: ", geom.area())
31 else:
32 x = geom.asMultiPolygon()
33 print("MultiPolygon: ", x, "Area: ", geom.area())
34 else:
35 print("Unknown or invalid geometry")
36 # fetch attributes
37 attrs = feature.attributes()
38 # attrs is a list. It contains all the attribute values of this feature
39 print(attrs)
40 # for this test only print the first feature
41 break
Feature ID: 1
Point: <QgsPointXY: POINT(7 45)>
[1, 'First feature']
6.3. Välja funktioner
I QGIS desktop kan funktioner väljas på olika sätt: användaren kan klicka på en funktion, rita en rektangel på kartbilden eller använda ett uttrycksfilter. Markerade funktioner är normalt markerade i en annan färg (standard är gul) för att dra användarens uppmärksamhet till markeringen.
Ibland kan det vara användbart att programmatiskt välja funktioner eller att ändra standardfärgen.
För att välja alla funktioner kan metoden selectAll()
användas:
# Get the active layer (must be a vector layer)
layer = iface.activeLayer()
layer.selectAll()
Om du vill välja med hjälp av ett uttryck använder du metoden selectByExpression()
:
# 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)
Om du vill ändra markeringsfärgen kan du använda metoden setSelectionColor()
i QgsMapCanvas
enligt följande exempel:
iface.mapCanvas().setSelectionColor( QColor("red") )
Om du vill lägga till funktioner i listan över valda funktioner för ett visst lager kan du anropa select()
och skicka listan över funktionernas ID:n till den:
1selected_fid = []
2
3# Get the first feature id from the layer
4feature = next(layer.getFeatures())
5if feature:
6 selected_fid.append(feature.id())
7
8# Add that features to the selected list
9layer.select(selected_fid)
För att rensa valet:
layer.removeSelection()
6.3.1. Tillgång till attribut
Attribut kan refereras till med sitt namn:
print(feature['name'])
First feature
Alternativt kan attribut refereras till med index. Detta är lite snabbare än att använda namnet. Till exempel, för att få det andra attributet:
print(feature[1])
First feature
6.3.2. Iteration över utvalda funktioner
Om du bara behöver utvalda funktioner kan du använda metoden selectedFeatures()
från vektorlagret:
selection = layer.selectedFeatures()
for feature in selection:
# do whatever you need with the feature
pass
6.3.3. Iteration över en delmängd av funktioner
Om du vill iterera över en given delmängd av funktioner i ett skikt, t.ex. de som finns inom ett givet område, måste du lägga till ett QgsFeatureRequest
-objekt till getFeatures()
-anropet. Här är ett exempel:
1areaOfInterest = QgsRectangle(450290,400520, 450750,400780)
2
3request = QgsFeatureRequest().setFilterRect(areaOfInterest)
4
5for feature in layer.getFeatures(request):
6 # do whatever you need with the feature
7 pass
För snabbhetens skull görs skärningen ofta bara med hjälp av funktionens avgränsningsbox. Det finns dock en flagga ExactIntersect
som ser till att endast korsande funktioner returneras:
request = QgsFeatureRequest().setFilterRect(areaOfInterest) \
.setFlags(QgsFeatureRequest.ExactIntersect)
Med setLimit()
kan du begränsa antalet begärda funktioner. Här är ett exempel:
request = QgsFeatureRequest()
request.setLimit(2)
for feature in layer.getFeatures(request):
print(feature)
<qgis._core.QgsFeature object at 0x7f9b78590948>
<qgis._core.QgsFeature object at 0x7faef5881670>
Om du behöver ett attributbaserat filter i stället för (eller utöver) ett rumsligt filter som visas i exemplen ovan kan du bygga ett QgsExpression
-objekt och skicka det till QgsFeatureRequest
-konstruktören. Här är ett exempel:
# 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)
Se Uttryck, filtrering och beräkning av värden för detaljer om den syntax som stöds av QgsExpression
.
Begäran kan användas för att definiera de data som hämtas för varje funktion, så att iteratorn returnerar alla funktioner, men returnerar partiella data för var och en av dem.
1# Only return selected fields to increase the "speed" of the request
2request.setSubsetOfAttributes([0,2])
3
4# More user friendly version
5request.setSubsetOfAttributes(['name','id'],layer.fields())
6
7# Don't return geometry objects to increase the "speed" of the request
8request.setFlags(QgsFeatureRequest.NoGeometry)
9
10# Fetch only the feature with id 45
11request.setFilterFid(45)
12
13# The options may be chained
14request.setFilterRect(areaOfInterest).setFlags(QgsFeatureRequest.NoGeometry).setFilterFid(45).setSubsetOfAttributes([0,2])
6.4. Modifiera vektorlager
De flesta vektordataleverantörer stöder redigering av lagerdata. Ibland stöder de bara en delmängd av möjliga redigeringsåtgärder. Använd funktionen capabilities()
för att ta reda på vilken uppsättning funktioner som stöds.
caps = layer.dataProvider().capabilities()
# Check if a particular capability is supported:
if caps & QgsVectorDataProvider.DeleteFeatures:
print('The layer supports DeleteFeatures')
The layer supports DeleteFeatures
För en lista över alla tillgängliga funktioner, se API Documentation of QgsVectorDataProvider
.
Om du vill skriva ut en textbeskrivning av skiktets kapacitet i en kommaseparerad lista kan du använda capabilitiesString()
som i följande exempel:
1caps_string = layer.dataProvider().capabilitiesString()
2# Print:
3# 'Add Features, Delete Features, Change Attribute Values, Add Attributes,
4# Delete Attributes, Rename Attributes, Fast Access to Features at ID,
5# Presimplify Geometries, Presimplify Geometries with Validity Check,
6# Transactions, Curved Geometries'
Genom att använda någon av följande metoder för redigering av vektorlager överförs ändringarna direkt till det underliggande datalagret (en fil, databas etc.). Om du bara vill göra tillfälliga ändringar kan du hoppa till nästa avsnitt som förklarar hur du gör modifieringar med redigeringsbuffert.
Observera
Om du arbetar i QGIS (antingen från konsolen eller från ett insticksprogram) kan det vara nödvändigt att tvinga fram en ny ritning av kartbilden för att se de ändringar du har gjort i geometrin, stilen eller attributen:
1# If caching is enabled, a simple canvas refresh might not be sufficient
2# to trigger a redraw and you must clear the cached image for the layer
3if iface.mapCanvas().isCachingEnabled():
4 layer.triggerRepaint()
5else:
6 iface.mapCanvas().refresh()
6.4.1. Lägg till funktioner
Skapa några QgsFeature
-instanser och skicka en lista över dem till providern QgsVectorDataProvider
addFeatures()
-metoden. Den returnerar två värden: resultat (True
eller False
) och en lista över tillagda funktioner (deras ID anges av datalagret).
För att ställa in attributen för funktionen kan du antingen initiera funktionen genom att skicka ett QgsFields
-objekt (du kan få det från metoden fields()
i vektorlagret) eller anropa initAttributes()
genom att skicka det antal fält du vill lägga till.
1if caps & QgsVectorDataProvider.AddFeatures:
2 feat = QgsFeature(layer.fields())
3 feat.setAttributes([0, 'hello'])
4 # Or set a single attribute by key or by index:
5 feat.setAttribute('name', 'hello')
6 feat.setAttribute(0, 'hello')
7 feat.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(123, 456)))
8 (res, outFeats) = layer.dataProvider().addFeatures([feat])
6.4.2. Ta bort funktioner
För att ta bort vissa funktioner behöver du bara ange en lista med deras funktions-ID.
if caps & QgsVectorDataProvider.DeleteFeatures:
res = layer.dataProvider().deleteFeatures([5, 10])
6.4.3. Modifiera funktioner
Det är möjligt att antingen ändra funktionens geometri eller att ändra vissa attribut. I följande exempel ändras först värdena för attribut med index 0 och 1 och därefter ändras objektets geometri.
1fid = 100 # ID of the feature we will modify
2
3if caps & QgsVectorDataProvider.ChangeAttributeValues:
4 attrs = { 0 : "hello", 1 : 123 }
5 layer.dataProvider().changeAttributeValues({ fid : attrs })
6
7if caps & QgsVectorDataProvider.ChangeGeometries:
8 geom = QgsGeometry.fromPointXY(QgsPointXY(111,222))
9 layer.dataProvider().changeGeometryValues({ fid : geom })
Tips
Fördela klassen QgsVectorLayerEditUtils för redigering av enbart geometri
Om du bara behöver ändra geometrier kan du överväga att använda QgsVectorLayerEditUtils
som tillhandahåller några användbara metoder för att redigera geometrier (översätta, infoga eller flytta vertex etc.).
6.4.4. Modifiera vektorlager med en redigeringsbuffert
När du redigerar vektorer i QGIS-programmet måste du först starta redigeringsläget för ett visst lager, sedan göra några ändringar och slutligen överföra (eller återställa) ändringarna. Alla ändringar som du gör skrivs inte förrän du överför dem - de ligger kvar i skiktets redigeringsbuffert i minnet. Det är möjligt att använda den här funktionen även programmatiskt — det är bara en annan metod för redigering av vektorlager som kompletterar den direkta användningen av dataleverantörer. Använd detta alternativ när du tillhandahåller några GUI-verktyg för redigering av vektorlager, eftersom det gör det möjligt för användaren att bestämma om han/hon vill göra en commit/rollback och tillåter användning av undo/redo. När ändringar bekräftas sparas alla ändringar från redigeringsbufferten till dataleverantören.
Metoderna liknar dem som vi har sett i providern, men de anropas på QgsVectorLayer
-objektet istället.
För att dessa metoder ska fungera måste lagret vara i redigeringsläge. För att starta redigeringsläget, använd metoden startEditing()
. För att stoppa redigeringen använder du metoderna commitChanges()
eller rollBack()
. Den första metoden överför alla dina ändringar till datakällan, medan den andra metoden kasserar dem och inte ändrar datakällan alls.
Om du vill ta reda på om ett lager är i redigeringsläge använder du metoden isEditable()
.
Här har du några exempel som visar hur du kan använda dessa redigeringsmetoder.
1from qgis.PyQt.QtCore import QMetaType
2
3feat1 = feat2 = QgsFeature(layer.fields())
4fid = 99
5feat1.setId(fid)
6
7# add two features (QgsFeature instances)
8layer.addFeatures([feat1,feat2])
9# delete a feature with specified ID
10layer.deleteFeature(fid)
11
12# set new geometry (QgsGeometry instance) for a feature
13geometry = QgsGeometry.fromWkt("POINT(7 45)")
14layer.changeGeometry(fid, geometry)
15# update an attribute with given field index (int) to a given value
16fieldIndex =1
17value ='My new name'
18layer.changeAttributeValue(fid, fieldIndex, value)
19
20# add new field
21layer.addAttribute(QgsField("mytext", QMetaType.Type.QString))
22# remove a field
23layer.deleteAttribute(fieldIndex)
För att undo/redo ska fungera korrekt måste de ovan nämnda anropen paketeras in i undokommandon. (Om du inte bryr dig om undo/redo och vill att ändringarna ska lagras omedelbart är det enklare att :ref:redigera med dataleverantör <editing>
.)
Så här kan du använda funktionen för att ångra:
1layer.beginEditCommand("Feature triangulation")
2
3# ... call layer's editing methods ...
4
5if problem_occurred:
6 layer.destroyEditCommand()
7 # ... tell the user that there was a problem
8 # and return
9
10# ... more editing ...
11
12layer.endEditCommand()
Metoden beginEditCommand()
skapar ett internt ”aktivt” kommando och registrerar efterföljande ändringar i vektorlagret. Med anropet till endEditCommand()
flyttas kommandot till ångra-stacken och användaren kommer att kunna ångra/återställa det från GUI. Om något gick fel när ändringarna gjordes kommer metoden destroyEditCommand()
att ta bort kommandot och återställa alla ändringar som gjorts medan kommandot var aktivt.
Du kan också använda with edit(layer)
-satsen för att packa in commit och rollback i ett mer semantiskt kodblock, som i exemplet nedan:
with edit(layer):
feat = next(layer.getFeatures())
feat[0] = 5
layer.updateFeature(feat)
Detta kommer automatiskt att anropa commitChanges()
i slutet. Om något undantag inträffar kommer det att rollBack()
alla ändringar. Om ett problem uppstår inom commitChanges()
(när metoden returnerar False) kommer ett QgsEditError
undantag att tas upp.
6.4.5. Lägga till och ta bort fält
Om du vill lägga till fält (attribut) måste du ange en lista med fältdefinitioner. För borttagning av fält behöver du bara ange en lista med fältindex.
1from qgis.PyQt.QtCore import QMetaType
2
3if caps & QgsVectorDataProvider.AddAttributes:
4 res = layer.dataProvider().addAttributes(
5 [QgsField("mytext", QMetaType.Type.QString),
6 QgsField("myint", QMetaType.Type.Int)])
7
8if caps & QgsVectorDataProvider.DeleteAttributes:
9 res = layer.dataProvider().deleteAttributes([0])
1# Alternate methods for removing fields
2# first create temporary fields to be removed (f1-3)
3layer.dataProvider().addAttributes([QgsField("f1", QMetaType.Type.Int),
4 QgsField("f2", QMetaType.Type.Int),
5 QgsField("f3", QMetaType.Type.Int)])
6layer.updateFields()
7count=layer.fields().count() # count of layer fields
8ind_list=list((count-3, count-2)) # create list
9
10# remove a single field with an index
11layer.dataProvider().deleteAttributes([count-1])
12
13# remove multiple fields with a list of indices
14layer.dataProvider().deleteAttributes(ind_list)
När du har lagt till eller tagit bort fält i dataleverantören måste skiktets fält uppdateras eftersom ändringarna inte sprids automatiskt.
layer.updateFields()
Tips
Spara ändringar direkt med hjälp av med
baserat kommando
Genom att använda with edit(layer):
kommer ändringarna att överföras automatiskt genom att anropa commitChanges()
i slutet. Om något undantag inträffar kommer det att rollBack()
alla ändringar. Se :ref:redigeringsbuffert
.
6.5. Använda rumsligt index
Spatiala index kan dramatiskt förbättra prestandan i din kod om du behöver göra frekventa förfrågningar till ett vektorskikt. Tänk dig till exempel att du skriver en interpoleringsalgoritm och att du för en given plats behöver veta de 10 närmaste punkterna från ett punktlager för att kunna använda dessa punkter för att beräkna det interpolerade värdet. Utan ett rumsligt index är det enda sättet för QGIS att hitta dessa 10 punkter att beräkna avståndet från varje punkt till den angivna platsen och sedan jämföra dessa avstånd. Detta kan vara en mycket tidskrävande uppgift, särskilt om den måste upprepas för flera platser. Om det finns ett rumsligt index för skiktet blir operationen mycket mer effektiv.
Tänk på ett lager utan rumsligt index som en telefonbok där telefonnumren inte är ordnade eller indexerade. Det enda sättet att hitta telefonnumret till en viss person är att läsa från början tills du hittar det.
Rumsliga index skapas inte som standard för ett QGIS-vektorlager, men du kan skapa dem enkelt. Det här är vad du behöver göra:
skapa ett spatialt index med hjälp av
QgsSpatialIndex
:index = QgsSpatialIndex()
add features to index — index tar
QgsFeature
objekt och lägger till det i den interna datastrukturen. Du kan skapa objektet manuellt eller använda ett från ett tidigare anrop till providernsgetFeatures()
-metod.index.addFeature(feat)
alternativt kan du ladda alla funktioner i ett lager på en gång med hjälp av bulkbelastning
index = QgsSpatialIndex(layer.getFeatures())
när det spatiala indexet har fyllts med vissa värden kan du göra några frågor
1# returns array of feature IDs of five nearest features 2nearest = index.nearestNeighbor(QgsPointXY(25.4, 12.7), 5) 3 4# returns array of IDs of features which intersect the rectangle 5intersect = index.intersects(QgsRectangle(22.5, 15.3, 23.1, 17.2))
Du kan också använda det spatiala indexet QgsSpatialIndexKDBush
. Detta index liknar det standardiserade QgsSpatialIndex
men:
stöder endast funktioner med en enda punkt
är statisk (inga ytterligare funktioner kan läggas till i indexet efter konstruktionen)
är mycket snabbare!
möjliggör direkt hämtning av den ursprungliga funktionens punkter, utan att ytterligare funktionsförfrågningar krävs
stöder verkliga avståndsbaserade sökningar, dvs. returnerar alla punkter inom en radie från en sökpunkt
6.6. Klassen QgsVectorLayerUtils
Klassen QgsVectorLayerUtils
innehåller några mycket användbara metoder som du kan använda med vektorlager.
Till exempel förbereder metoden createFeature()
en QgsFeature
som ska läggas till i ett vektorskikt med alla eventuella begränsningar och standardvärden för varje fält:
vlayer = QgsVectorLayer("testdata/data/data.gpkg|layername=airports", "Airports layer", "ogr")
feat = QgsVectorLayerUtils.createFeature(vlayer)
Med metoden getValues()
kan du snabbt få fram värdena för ett fält eller uttryck:
1vlayer = QgsVectorLayer("testdata/data/data.gpkg|layername=airports", "Airports layer", "ogr")
2# select only the first feature to make the output shorter
3vlayer.selectByIds([1])
4val = QgsVectorLayerUtils.getValues(vlayer, "NAME", selectedOnly=True)
5print(val)
(['Sahnewal'], True)
6.7. Skapa vektorlager
Det finns flera sätt att generera en vektorskiktsdatauppsättning:
klassen
QgsVectorFileWriter
: En praktisk klass för att skriva vektorfiler till disk, med antingen ett statiskt anrop tillwriteAsVectorFormatV3()
som sparar hela vektorlagret eller skapa en instans av klassen och utfärda anrop till ärvdaaddFeature()
. Den här klassen stöder alla vektorformat som GDAL stöder (GeoPackage, Shapefile, GeoJSON, KML och andra).klassen
QgsVectorLayer
: instansierar en dataleverantör som tolkar den angivna sökvägen (url) till datakällan för att ansluta till och komma åt data. Den kan användas för att skapa tillfälliga, minnesbaserade lager (memory
) och ansluta till GDAL-vektordataset (ogr
), databaser (postgres
,spatialite
,mysql
,mssql
) och mer (wfs
,gpx
,delimitedtext
…).
6.7.1. Från en instans av QgsVectorFileWriter
1# SaveVectorOptions contains many settings for the writer process
2save_options = QgsVectorFileWriter.SaveVectorOptions()
3transform_context = QgsProject.instance().transformContext()
4# Write to a GeoPackage (default)
5error = QgsVectorFileWriter.writeAsVectorFormatV3(layer,
6 "testdata/my_new_file.gpkg",
7 transform_context,
8 save_options)
9if error[0] == QgsVectorFileWriter.NoError:
10 print("success!")
11else:
12 print(error)
1# Write to an ESRI Shapefile format dataset using UTF-8 text encoding
2save_options = QgsVectorFileWriter.SaveVectorOptions()
3save_options.driverName = "ESRI Shapefile"
4save_options.fileEncoding = "UTF-8"
5transform_context = QgsProject.instance().transformContext()
6error = QgsVectorFileWriter.writeAsVectorFormatV3(layer,
7 "testdata/my_new_shapefile",
8 transform_context,
9 save_options)
10if error[0] == QgsVectorFileWriter.NoError:
11 print("success again!")
12else:
13 print(error)
1# Write to an ESRI GDB file
2save_options = QgsVectorFileWriter.SaveVectorOptions()
3save_options.driverName = "FileGDB"
4# if no geometry
5save_options.overrideGeometryType = QgsWkbTypes.Unknown
6save_options.actionOnExistingFile = QgsVectorFileWriter.CreateOrOverwriteLayer
7save_options.layerName = 'my_new_layer_name'
8transform_context = QgsProject.instance().transformContext()
9gdb_path = "testdata/my_example.gdb"
10error = QgsVectorFileWriter.writeAsVectorFormatV3(layer,
11 gdb_path,
12 transform_context,
13 save_options)
14if error[0] == QgsVectorFileWriter.NoError:
15 print("success!")
16else:
17 print(error)
Du kan också konvertera fält för att göra dem kompatibla med olika format genom att använda FieldValueConverter
. Om du t.ex. vill konvertera arrayvariabeltyper (t.ex. i Postgres) till en texttyp kan du göra följande:
1LIST_FIELD_NAME = 'xxxx'
2
3class ESRIValueConverter(QgsVectorFileWriter.FieldValueConverter):
4
5 def __init__(self, layer, list_field):
6 QgsVectorFileWriter.FieldValueConverter.__init__(self)
7 self.layer = layer
8 self.list_field_idx = self.layer.fields().indexFromName(list_field)
9
10 def convert(self, fieldIdxInLayer, value):
11 if fieldIdxInLayer == self.list_field_idx:
12 return QgsListFieldFormatter().representValue(layer=vlayer,
13 fieldIndex=self.list_field_idx,
14 config={},
15 cache=None,
16 value=value)
17 else:
18 return value
19
20 def fieldDefinition(self, field):
21 idx = self.layer.fields().indexFromName(field.name())
22 if idx == self.list_field_idx:
23 return QgsField(LIST_FIELD_NAME, QMetaType.Type.QString)
24 else:
25 return self.layer.fields()[idx]
26
27converter = ESRIValueConverter(vlayer, LIST_FIELD_NAME)
28opts = QgsVectorFileWriter.SaveVectorOptions()
29opts.fieldValueConverter = converter
Ett destinations-CRS kan också anges — om en giltig instans av QgsCoordinateReferenceSystem
skickas som den fjärde parametern, transformeras lagret till det CRS:et.
För giltiga drivrutinsnamn, anropa metoden supportedFiltersAndFormats()
eller konsultera supported formats by OGR — du bör skicka värdet i kolumnen ”Code” som drivrutinsnamn.
Alternativt kan du ange om du bara vill exportera utvalda funktioner, skicka ytterligare drivrutinsspecifika alternativ för skapande eller tala om för skrivaren att inte skapa attribut… Det finns ett antal andra (valfria) parametrar; se QgsVectorFileWriter
-dokumentationen för detaljer.
6.7.2. Direkt från funktioner
1from qgis.PyQt.QtCore import QMetaType
2
3# define fields for feature attributes. A QgsFields object is needed
4fields = QgsFields()
5fields.append(QgsField("first", QMetaType.Type.Int))
6fields.append(QgsField("second", QMetaType.Type.QString))
7
8""" create an instance of vector file writer, which will create the vector file.
9Arguments:
101. path to new file (will fail if exists already)
112. field map
123. geometry type - from WKBTYPE enum
134. layer's spatial reference (instance of
14 QgsCoordinateReferenceSystem)
155. coordinate transform context
166. save options (driver name for the output file, encoding etc.)
17"""
18
19crs = QgsProject.instance().crs()
20transform_context = QgsProject.instance().transformContext()
21save_options = QgsVectorFileWriter.SaveVectorOptions()
22save_options.driverName = "ESRI Shapefile"
23save_options.fileEncoding = "UTF-8"
24
25writer = QgsVectorFileWriter.create(
26 "testdata/my_new_shapefile.shp",
27 fields,
28 QgsWkbTypes.Point,
29 crs,
30 transform_context,
31 save_options
32)
33
34if writer.hasError() != QgsVectorFileWriter.NoError:
35 print("Error when creating shapefile: ", writer.errorMessage())
36
37# add a feature
38fet = QgsFeature()
39
40fet.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(10,10)))
41fet.setAttributes([1, "text"])
42writer.addFeature(fet)
43
44# delete the writer to flush features to disk
45del writer
6.7.3. Från en instans av QgsVectorLayer
Bland alla dataleverantörer som stöds av QgsVectorLayer
-klassen, låt oss fokusera på de minnesbaserade lagren. Memory provider är avsedd att användas främst av plugin- eller tredjepartsapputvecklare. Den lagrar inte data på disk, vilket gör att utvecklare kan använda den som en snabb backend för vissa tillfälliga lager.
Providern stöder string-, int- och double-fält.
Minnesprovidern stöder även spatial indexering, som aktiveras genom att anropa providerns createSpatialIndex()
-funktion. När det spatiala indexet har skapats kommer du att kunna iterera över funktioner inom mindre regioner snabbare (eftersom det inte är nödvändigt att traversera alla funktioner, bara de som finns i den angivna rektangeln).
En minnesprovider skapas genom att skicka "memory"
som providersträng till QgsVectorLayer
-konstruktören.
Konstruktorn tar också en URI som definierar geometritypen för lagret, en av följande: "Point"
, "LineString"
, "Polygon"
, "MultiPoint"
, "MultiLineString"
, "MultiPolygon"
eller "None"
.
URI:n kan också ange koordinatreferenssystem, fält och indexering för minnesprovidern i URI:n. Syntaxen är:
- crs=definition
Anger koordinatreferenssystemet, där definitionen kan vara någon av de former som accepteras av
QgsCoordinateReferenceSystem.createFromString()
- index=yes
Anger att leverantören kommer att använda ett rumsligt index
- field=name:type(length,precision)
Anger ett attribut för lagret. Attributet har ett namn och eventuellt en typ (heltal, dubbel eller sträng), längd och precision. Det kan finnas flera fältdefinitioner.
Följande exempel på en URI innehåller alla dessa alternativ
"Point?crs=epsg:4326&field=id:integer&field=name:string(20)&index=yes"
Följande exempel på kod illustrerar hur man skapar och fyller i en minnesprovider
1from qgis.PyQt.QtCore import QDateTime, QMetaType, Qt
2
3# create layer
4vl = QgsVectorLayer("Point", "temporary_points", "memory")
5pr = vl.dataProvider()
6
7# add fields
8pr.addAttributes([QgsField("name", QMetaType.Type.QString),
9 QgsField("age", QMetaType.Type.Int),
10 QgsField("size", QMetaType.Type.Double),
11 QgsField("birthday", QMetaType.Type.QDateTime)])
12vl.updateFields() # tell the vector layer to fetch changes from the provider
13
14# add a feature
15fet = QgsFeature()
16fet.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(10,10)))
17fmt = Qt.DateFormat.ISODate
18t = QDateTime.fromString("2000-01-01T12:00:00", fmt)
19fet.setAttributes(["Johnny", 2, 0.3, t])
20pr.addFeatures([fet])
21
22# update layer's extent when new features have been added
23# because change of extent in provider is not propagated to the layer
24vl.updateExtents()
Låt oss slutligen kontrollera om allt gick bra
1# show some stats
2print("fields:", len(pr.fields()))
3print("features:", pr.featureCount())
4e = vl.extent()
5print("extent:", e.xMinimum(), e.yMinimum(), e.xMaximum(), e.yMaximum())
6
7# iterate over features
8features = vl.getFeatures()
9for fet in features:
10 print("F:", fet.id(), fet.attributes(), fet.geometry().asPoint())
fields: 4
features: 1
extent: 10.0 10.0 10.0 10.0
F: 1 ['Johnny', 2, 0.3, PyQt5.QtCore.QDateTime(2000, 1, 1, 12, 0)] <QgsPointXY: POINT(10 10)>
6.8. Utseende (symbologi) för vektorskikt
När ett vektorlager renderas ges datans utseende av renderer och symboler som är associerade med lagret. Symboler är klasser som tar hand om ritning av visuell representation av funktioner, medan renderare bestämmer vilken symbol som ska användas för en viss funktion.
Renderingsprogrammet för ett visst lager kan erhållas enligt nedan:
renderer = layer.renderer()
Och med den referensen, låt oss utforska den lite
print("Type:", renderer.type())
Type: singleSymbol
Det finns flera kända renderingstyper tillgängliga i QGIS kärnbibliotek:
Typ |
Klass |
Beskrivning |
---|---|---|
singleSymbol |
Renderar alla funktioner med samma symbol |
|
categorizedSymbol |
Rendering av funktioner med olika symboler för varje kategori |
|
graduatedSymbol |
Renderar funktioner med en annan symbol för varje intervall av värden |
Det kan också finnas några anpassade renderingstyper, så gör aldrig ett antagande att det bara finns dessa typer. Du kan fråga applikationens QgsRendererRegistry
för att ta reda på vilka renderare som finns tillgängliga för närvarande:
print(QgsApplication.rendererRegistry().renderersList())
['nullSymbol', 'singleSymbol', 'categorizedSymbol', 'graduatedSymbol', 'RuleRenderer', 'pointDisplacement', 'pointCluster', 'mergedFeatureRenderer', 'invertedPolygonRenderer', 'heatmapRenderer', '25dRenderer', 'embeddedSymbol']
Det är möjligt att få en dumpning av en renderingsenhets innehåll i textform — kan vara användbart för felsökning
renderer.dump()
SINGLE: MARKER SYMBOL (1 layers) color 190,207,80,255
6.8.1. Renderare för en enda symbol
Du kan få symbolen som används för rendering genom att anropa symbol()
-metoden och ändra den med setSymbol()
-metoden (notering för C++-utvecklare: renderingsprogrammet tar äganderätten till symbolen.)
Du kan ändra symbolen som används av ett visst vektorlager genom att anropa setSymbol()
och skicka en instans av den lämpliga symbolinstansen. Symboler för punkt-, linje- och polygon-lager kan skapas genom att anropa createSimple()
-funktionen i motsvarande klasser QgsMarkerSymbol
, QgsLineSymbol
och QgsFillSymbol
.
Den ordbok som skickas till createSimple()
anger symbolens stilegenskaper.
Du kan t.ex. ersätta den symbol som används av ett visst punkt-lager genom att anropa setSymbol()
och skicka en instans av en QgsMarkerSymbol
, som i följande kodexempel:
symbol = QgsMarkerSymbol.createSimple({'name': 'square', 'color': 'red'})
layer.renderer().setSymbol(symbol)
# show the change
layer.triggerRepaint()
namn
anger markörens form och kan vara något av följande:
circle
square
cross
rectangle
diamond
pentagon
triangle
equilateral_triangle
star
regular_star
arrow
filled_arrowhead
x
För att få en fullständig lista över egenskaper för det första symbollagret i en symbolinstans kan du följa exempelkoden:
print(layer.renderer().symbol().symbolLayers()[0].properties())
{'angle': '0', 'cap_style': 'square', 'color': '255,0,0,255,rgb:1,0,0,1', '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,rgb:0.13725490196078433,0.13725490196078433,0.13725490196078433,1', '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'}
Detta kan vara användbart om du vill ändra vissa egenskaper:
1# You can alter a single property...
2layer.renderer().symbol().symbolLayer(0).setSize(3)
3# ... but not all properties are accessible from methods,
4# you can also replace the symbol completely:
5props = layer.renderer().symbol().symbolLayer(0).properties()
6props['color'] = 'yellow'
7props['name'] = 'square'
8layer.renderer().setSymbol(QgsMarkerSymbol.createSimple(props))
9# show the changes
10layer.triggerRepaint()
6.8.2. Kategoriserad symbolrendering
När du använder en kategoriserad renderare kan du fråga efter och ställa in attributet som används för klassificering: använd metoderna classAttribute()
och setClassAttribute()
.
För att få en lista över kategorier
1categorized_renderer = QgsCategorizedSymbolRenderer()
2# Add a few categories
3cat1 = QgsRendererCategory('1', QgsMarkerSymbol(), 'category 1')
4cat2 = QgsRendererCategory('2', QgsMarkerSymbol(), 'category 2')
5categorized_renderer.addCategory(cat1)
6categorized_renderer.addCategory(cat2)
7
8for cat in categorized_renderer.categories():
9 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>
Där value()
är värdet som används för diskriminering mellan kategorier, label()
är en text som används för kategoribeskrivning och symbol()
-metoden returnerar den tilldelade symbolen.
Renderaren lagrar vanligtvis även originalsymbolen och färgrampen som användes för klassificeringen: sourceColorRamp()
och sourceSymbol()
metoder.
6.8.3. Rendering av graderad symbol
Denna rendering är mycket lik den kategoriserade symbolrendering som beskrivs ovan, men i stället för ett attributvärde per klass arbetar den med värdeintervall och kan därför endast användas med numeriska attribut.
Om du vill veta mer om intervall som används i renderingsprogrammet
1graduated_renderer = QgsGraduatedSymbolRenderer()
2# Add a few categories
3graduated_renderer.addClassRange(QgsRendererRange(QgsClassificationRange('class 0-100', 0, 100), QgsMarkerSymbol()))
4graduated_renderer.addClassRange(QgsRendererRange(QgsClassificationRange('class 101-200', 101, 200), QgsMarkerSymbol()))
5
6for ran in graduated_renderer.ranges():
7 print("{} - {}: {} {}".format(
8 ran.lowerValue(),
9 ran.upperValue(),
10 ran.label(),
11 ran.symbol()
12 ))
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>
kan du återigen använda classAttribute()
(för att hitta klassificeringsattributets namn), sourceSymbol()
och sourceColorRamp()
metoder. Dessutom finns metoden mode()
som bestämmer hur intervallen skapades: med hjälp av lika intervall, kvantiler eller någon annan metod.
Om du vill skapa din egen graderade symbolrendering kan du göra det enligt exemplet nedan (som skapar ett enkelt tvåklassigt arrangemang)
1from qgis.PyQt import QtGui
2
3myVectorLayer = QgsVectorLayer("testdata/data/data.gpkg|layername=airports", "Airports layer", "ogr")
4myTargetField = 'scalerank'
5myRangeList = []
6myOpacity = 1
7# Make our first symbol and range...
8myMin = 0.0
9myMax = 50.0
10myLabel = 'Group 1'
11myColour = QtGui.QColor('#ffee00')
12mySymbol1 = QgsSymbol.defaultSymbol(myVectorLayer.geometryType())
13mySymbol1.setColor(myColour)
14mySymbol1.setOpacity(myOpacity)
15myRange1 = QgsRendererRange(myMin, myMax, mySymbol1, myLabel)
16myRangeList.append(myRange1)
17#now make another symbol and range...
18myMin = 50.1
19myMax = 100
20myLabel = 'Group 2'
21myColour = QtGui.QColor('#00eeff')
22mySymbol2 = QgsSymbol.defaultSymbol(
23 myVectorLayer.geometryType())
24mySymbol2.setColor(myColour)
25mySymbol2.setOpacity(myOpacity)
26myRange2 = QgsRendererRange(myMin, myMax, mySymbol2, myLabel)
27myRangeList.append(myRange2)
28myRenderer = QgsGraduatedSymbolRenderer('', myRangeList)
29myClassificationMethod = QgsApplication.classificationMethodRegistry().method("EqualInterval")
30myRenderer.setClassificationMethod(myClassificationMethod)
31myRenderer.setClassAttribute(myTargetField)
32
33myVectorLayer.setRenderer(myRenderer)
6.8.4. Arbeta med symboler
För representation av symboler finns QgsSymbol
basklass med tre härledda klasser:
QgsMarkerSymbol
— för punktfunktionerQgsLineSymbol
— för linjefunktionerQgsFillSymbol
— för polygonfunktioner
Varje symbol består av ett eller flera symbolskikt (klasser som härrör från QgsSymbolLayer
). Symbolskikten gör den faktiska renderingen, symbolklassen i sig fungerar bara som en behållare för symbolskikten.
Om du har en instans av en symbol (t.ex. från en renderare) är det möjligt att utforska den: metoden type()
anger om det är en markör-, linje- eller fyllningssymbol. Det finns en dump()
-metod som returnerar en kort beskrivning av symbolen. För att få en lista över symbollager:
marker_symbol = QgsMarkerSymbol()
for i in range(marker_symbol.symbolLayerCount()):
lyr = marker_symbol.symbolLayer(i)
print("{}: {}".format(i, lyr.layerType()))
0: SimpleMarker
För att ta reda på symbolens färg använd color()
metod och setColor()
för att ändra dess färg. Med markörsymboler kan du dessutom fråga efter symbolens storlek och rotation med metoderna size()
och angle()
. För linjesymboler returnerar metoden width()
linjebredden.
Storlek och bredd är i millimeter som standard, vinklar är i grader.
6.8.4.1. Arbeta med symbolskikt
Som tidigare nämnts är det symbolskikten (underklasser till QgsSymbolLayer
) som bestämmer hur funktionerna ska se ut. Det finns flera grundläggande symbolskiktsklasser för allmänt bruk. Det är möjligt att implementera nya symbolskiktstyper och därmed godtyckligt anpassa hur funktioner ska återges. Metoden layerType()
identifierar symbolskiktsklassen på ett unikt sätt — de grundläggande och standardiserade är symbolskiktstyperna SimpleMarker
, SimpleLine
och SimpleFill
.
Du kan få en fullständig lista över de typer av symbollager som du kan skapa för en viss symbollagerklass med följande kod:
1from qgis.core import QgsSymbolLayerRegistry
2myRegistry = QgsApplication.symbolLayerRegistry()
3myMetadata = myRegistry.symbolLayerMetadata("SimpleFill")
4for item in myRegistry.symbolLayersForType(QgsSymbol.Marker):
5 print(item)
1AnimatedMarker
2EllipseMarker
3FilledMarker
4FontMarker
5GeometryGenerator
6MaskMarker
7RasterMarker
8SimpleMarker
9SvgMarker
10VectorField
Klassen QgsSymbolLayerRegistry
hanterar en databas med alla tillgängliga symbolskiktstyper.
För att komma åt symbolskiktsdata använder du dess properties()
-metod som returnerar en nyckelvärdesordbok med egenskaper som bestämmer utseendet. Varje symbolskiktstyp har en specifik uppsättning egenskaper som den använder. Dessutom finns det de generiska metoderna color()
, size()
, angle()
och width()
, med deras motsvarigheter i setter. Naturligtvis är storlek och vinkel endast tillgängliga för markörsymbolskikt och bredd för linjesymbolskikt.
6.8.4.2. Skapa egna typer av symbolskikt
Tänk dig att du skulle vilja anpassa hur data återges. Du kan skapa din egen symbollagerklass som ritar funktionerna exakt som du vill. Här är ett exempel på en markör som ritar röda cirklar med angiven radie
1from qgis.core import QgsMarkerSymbolLayer
2from qgis.PyQt.QtGui import QColor
3
4class FooSymbolLayer(QgsMarkerSymbolLayer):
5
6 def __init__(self, radius=4.0):
7 QgsMarkerSymbolLayer.__init__(self)
8 self.radius = radius
9 self.color = QColor(255,0,0)
10
11 def layerType(self):
12 return "FooMarker"
13
14 def properties(self):
15 return { "radius" : str(self.radius) }
16
17 def startRender(self, context):
18 pass
19
20 def stopRender(self, context):
21 pass
22
23 def renderPoint(self, point, context):
24 # Rendering depends on whether the symbol is selected (QGIS >= 1.5)
25 color = context.selectionColor() if context.selected() else self.color
26 p = context.renderContext().painter()
27 p.setPen(color)
28 p.drawEllipse(point, self.radius, self.radius)
29
30 def clone(self):
31 return FooSymbolLayer(self.radius)
Metoden layerType()
bestämmer namnet på symbolskiktet; det måste vara unikt bland alla symbolskikt. Metoden properties()
används för att bevara attribut. Metoden clone()
måste returnera en kopia av symbolskiktet med alla attribut exakt lika. Slutligen finns det renderingsmetoder: startRender()
anropas innan den första funktionen renderas, stopRender()
när renderingen är klar, och renderPoint()
anropas för att göra renderingen. Koordinaterna för punkten/punkterna är redan transformerade till utdatakoordinaterna.
För polylinjer och polygoner skulle den enda skillnaden vara i renderingsmetoden: du skulle använda renderPolyline()
som tar emot en lista med linjer, medan renderPolygon()
tar emot en lista med punkter på den yttre ringen som första parameter och en lista med inre ringar (eller Ingen) som andra parameter.
Vanligtvis är det bekvämt att lägga till ett GUI för att ställa in attribut för symbollagertypen så att användarna kan anpassa utseendet: i vårt exempel ovan kan vi låta användaren ställa in cirkelradie. Följande kod implementerar en sådan widget
1from qgis.gui import QgsSymbolLayerWidget
2
3class FooSymbolLayerWidget(QgsSymbolLayerWidget):
4 def __init__(self, parent=None):
5 QgsSymbolLayerWidget.__init__(self, parent)
6
7 self.layer = None
8
9 # setup a simple UI
10 self.label = QLabel("Radius:")
11 self.spinRadius = QDoubleSpinBox()
12 self.hbox = QHBoxLayout()
13 self.hbox.addWidget(self.label)
14 self.hbox.addWidget(self.spinRadius)
15 self.setLayout(self.hbox)
16 self.connect(self.spinRadius, SIGNAL("valueChanged(double)"), \
17 self.radiusChanged)
18
19 def setSymbolLayer(self, layer):
20 if layer.layerType() != "FooMarker":
21 return
22 self.layer = layer
23 self.spinRadius.setValue(layer.radius)
24
25 def symbolLayer(self):
26 return self.layer
27
28 def radiusChanged(self, value):
29 self.layer.radius = value
30 self.emit(SIGNAL("changed()"))
Denna widget kan bäddas in i dialogrutan för symbolegenskaper. När symbollagertypen väljs i dialogrutan för symbolegenskaper skapas en instans av symbollagret och en instans av widgeten för symbollager. Sedan anropas metoden setSymbolLayer()
för att tilldela symbollagret till widgeten. I den metoden bör widgeten uppdatera användargränssnittet för att återspegla symbolskiktets attribut. Metoden symbolLayer()
används för att hämta symbolskiktet igen via dialogrutan för egenskaper för att använda det för symbolen.
Vid varje ändring av attribut bör widgeten sända ut signalen changed()
för att låta dialogrutan för egenskaper uppdatera förhandsgranskningen av symbolen.
Nu saknas bara det sista limmet: att göra QGIS medvetet om dessa nya klasser. Detta görs genom att lägga till symbolskiktet i registret. Det är möjligt att använda symbolskiktet även utan att lägga till det i registret, men vissa funktioner kommer inte att fungera: t.ex. laddning av projektfiler med de anpassade symbolskikten eller oförmåga att redigera skiktets attribut i GUI.
Vi kommer att behöva skapa metadata för symbolskiktet
1from qgis.core import QgsSymbol, QgsSymbolLayerAbstractMetadata, QgsSymbolLayerRegistry
2
3class FooSymbolLayerMetadata(QgsSymbolLayerAbstractMetadata):
4
5 def __init__(self):
6 super().__init__("FooMarker", "My new Foo marker", QgsSymbol.Marker)
7
8 def createSymbolLayer(self, props):
9 radius = float(props["radius"]) if "radius" in props else 4.0
10 return FooSymbolLayer(radius)
11
12fslmetadata = FooSymbolLayerMetadata()
QgsApplication.symbolLayerRegistry().addSymbolLayerType(fslmetadata)
Du bör skicka lagertyp (samma som returneras av lagret) och symboltyp (markör/linje/fyllning) till konstruktören för den överordnade klassen. Metoden createSymbolLayer()
tar hand om att skapa en instans av symbolskiktet med attribut som anges i ordlistan props. Och det finns metoden createSymbolLayerWidget()
som returnerar inställningswidgeten för denna symbolskiktstyp.
Det sista steget är att lägga till detta symbolskikt i registret — och vi är klara.
6.8.5. Skapa anpassade renderingar
Det kan vara bra att skapa en ny renderingsimplementering om du vill anpassa reglerna för hur symboler ska väljas för rendering av funktioner. Några användningsfall där du skulle vilja göra det: symbolen bestäms utifrån en kombination av fält, storleken på symbolerna ändras beroende på aktuell skala etc.
Följande kod visar en enkel anpassad renderare som skapar två markörsymboler och slumpmässigt väljer en av dem för varje objekt
1import random
2from qgis.core import QgsWkbTypes, QgsSymbol, QgsFeatureRenderer
3
4
5class RandomRenderer(QgsFeatureRenderer):
6 def __init__(self, syms=None):
7 super().__init__("RandomRenderer")
8 self.syms = syms if syms else [
9 QgsSymbol.defaultSymbol(QgsWkbTypes.geometryType(QgsWkbTypes.Point)),
10 QgsSymbol.defaultSymbol(QgsWkbTypes.geometryType(QgsWkbTypes.Point))
11 ]
12
13 def symbolForFeature(self, feature, context):
14 return random.choice(self.syms)
15
16 def startRender(self, context, fields):
17 super().startRender(context, fields)
18 for s in self.syms:
19 s.startRender(context, fields)
20
21 def stopRender(self, context):
22 super().stopRender(context)
23 for s in self.syms:
24 s.stopRender(context)
25
26 def usedAttributes(self, context):
27 return []
28
29 def clone(self):
30 return RandomRenderer(self.syms)
Konstruktören för den överordnade klassen QgsFeatureRenderer
behöver ett namn på renderingsprogrammet (som måste vara unikt bland renderingsprogrammen). Metoden symbolForFeature()
är den som bestämmer vilken symbol som ska användas för en viss funktion. startRender()
och stopRender()
tar hand om initialisering/avslutning av symbolrendering. Metoden usedAttributes()
kan returnera en lista med fältnamn som renderingsprogrammet förväntar sig ska finnas. Slutligen bör metoden clone()
returnera en kopia av renderingsprogrammet.
Precis som med symbolskikt är det möjligt att bifoga ett GUI för konfiguration av renderingsprogrammet. Det måste härledas från QgsRendererWidget
. Följande exempelkod skapar en knapp som gör det möjligt för användaren att ställa in den första symbolen
1from qgis.gui import QgsRendererWidget, QgsColorButton
2
3
4class RandomRendererWidget(QgsRendererWidget):
5 def __init__(self, layer, style, renderer):
6 super().__init__(layer, style)
7 if renderer is None or renderer.type() != "RandomRenderer":
8 self.r = RandomRenderer()
9 else:
10 self.r = renderer
11 # setup UI
12 self.btn1 = QgsColorButton()
13 self.btn1.setColor(self.r.syms[0].color())
14 self.vbox = QVBoxLayout()
15 self.vbox.addWidget(self.btn1)
16 self.setLayout(self.vbox)
17 self.btn1.colorChanged.connect(self.setColor1)
18
19 def setColor1(self):
20 color = self.btn1.color()
21 if not color.isValid(): return
22 self.r.syms[0].setColor(color)
23
24 def renderer(self):
25 return self.r
Konstruktören tar emot instanser av det aktiva lagret (QgsVectorLayer
), den globala stilen (QgsStyle
) och den aktuella renderingen. Om det inte finns någon renderare eller om renderaren har en annan typ kommer den att ersättas med vår nya renderare, annars kommer vi att använda den aktuella renderaren (som redan har den typ vi behöver). Widgetens innehåll bör uppdateras för att visa det aktuella läget för renderingsprogrammet. När dialogrutan för rendering accepteras anropas widgetens renderer()
-metod för att hämta den aktuella renderingen — den kommer att tilldelas lagret.
Det sista som saknas är metadata för renderingsprogrammet och registrering i registret, annars fungerar inte laddningen av lager med renderingsprogrammet och användaren kan inte välja det från listan över renderingsprogram. Låt oss avsluta vårt RandomRenderer-exempel
1from qgis.core import (
2 QgsRendererAbstractMetadata,
3 QgsRendererRegistry,
4 QgsApplication
5)
6
7class RandomRendererMetadata(QgsRendererAbstractMetadata):
8
9 def __init__(self):
10 super().__init__("RandomRenderer", "Random renderer")
11
12 def createRenderer(self, element):
13 return RandomRenderer()
14
15 def createRendererWidget(self, layer, style, renderer):
16 return RandomRendererWidget(layer, style, renderer)
17
18rrmetadata = RandomRendererMetadata()
QgsApplication.rendererRegistry().addRenderer(rrmetadata)
På samma sätt som med symbolskikt väntar konstruktören för abstrakta metadata på namn på renderingsprogrammet, namn som är synligt för användare och eventuellt namn på renderingsprogrammets ikon. Metoden createRenderer()
skickar en instans av QDomElement
som kan användas för att återställa renderarens tillstånd från DOM-trädet. Metoden createRendererWidget()
skapar konfigurationswidgeten. Den behöver inte vara närvarande eller kan returnera None
om renderaren inte har något GUI.
För att associera en ikon med renderingsprogrammet kan du tilldela den i QgsRendererAbstractMetadata
-konstruktören som ett tredje (valfritt) argument — basklassens konstruktör i RandomRendererMetadata __init__()
-funktionen blir
QgsRendererAbstractMetadata.__init__(self,
"RandomRenderer",
"Random renderer",
QIcon(QPixmap("RandomRendererIcon.png", "png")))
Ikonen kan också associeras vid ett senare tillfälle med hjälp av setIcon()
-metoden i metadataklassen. Ikonen kan laddas från en fil (som visas ovan) eller laddas från en Qt-resurs (PyQt5 innehåller .qrc-kompilatorn för Python).
6.9. Ytterligare ämnen
TODO:
skapa/ändra symboler
arbeta med stil (
QgsStyle
)arbeta med färgramper (
QgsColorRamp
)utforska symbolskikt och renderingsregister