Dit gedeelte beschrijft verschillende acties die kunnen worden uitgevoerd met vectorlagen.
You can retrieve informations about the fields associated with a vector layer by calling pendingFields() on a QgsVectorLayer instance:
# "layer" is a QgsVectorLayer instance
for field in layer.pendingFields():
print field.name(), field.typeName()
In QGIS desktop, features can be selected in different ways, the user can click on a feature, draw a rectangle on the map canvas or use an expression filter. Selected fatures are normally higlighted in a different color (default is yellow) to draw user’s attention on the selection. Sometimes can be useful to programmatically select features or to change the default color.
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 setSelectedFeatures() aanroepen om objecten toe te voegen aan de lijst met geselecteerde objecten voor ene bepaalde laag, die de lijst met ID’s voor de objecten doorgeeft aan de lijst:
# Get the active layer (must be a vector layer)
layer = iface.activeLayer()
# Get the first feature from the layer
feature = layer.getFeatures().next()
# Add this features to the selected list
layer.setSelectedFeatures([feature.id()])
Geef eenvoudigweg een lege lijst door om de selectie leeg te maken:
layer.setSelectedFeatures([])
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
iter = layer.getFeatures()
for feature in iter:
# retrieve every feature with its geometry and attributes
# fetch geometry
geom = feature.geometry()
print "Feature ID %d: " % feature.id()
# show some information about the feature
if geom.type() == QGis.Point:
x = geom.asPoint()
print "Point: " + str(x)
elif geom.type() == QGis.Line:
x = geom.asPolyline()
print "Line: %d points" % len(x)
elif geom.type() == QGis.Polygon:
x = geom.asPolygon()
numPts = 0
for ring in x:
numPts += len(ring)
print "Polygon: %d rings with %d points" % (len(x), numPts)
else:
print "Unknown"
# fetch attributes
attrs = feature.attributes()
# attrs is a list. It contains all the attribute values of this feature
print attrs
Naar attributen kan worden verwezen door middel van hun naam.
print feature['name']
Als alternatief kan naar attributen worden verwezen door middel van een index. Dit zal iets sneller zijn dan het gebruiken van de naam. Bijvoorbeeld; het eerste attribuut verkrijgen:
print feature[0]
als u alleen objecten moet selecteren, kunt u de methode selectedFeatures() gebruiken uit de vectorlaag:
selection = layer.selectedFeatures()
print len(selection)
for feature in selection:
# do whatever you need with the feature
Een andere optie is de methode features() van Processing:
import processing
features = processing.features(layer)
for feature in features:
# do whatever you need with the feature
Standaard worden hiermee alle objecten in een laag doorlopen indien er geen selectie actief is, of anders over de geselecteerde objecten. Onthoud dat dit gedrag kan worden gewijzigd in de opties voor Processing om selecties te negeren.
Wanneer u een deel van de objecten in een laag wilt doorlopen, zoals bijvoorbeeld alleen de objecten in een gegeven gebied, dan dient een object QgsFeatureRequest te worden toegevoegd aan de aanroep getFeatures(). Hier is een voorbeeld:
request = QgsFeatureRequest()
request.setFilterRect(areaOfInterest)
for feature in layer.getFeatures(request):
# do whatever you need with the feature
Als u in plaats daarvan een op attributen gebaseerd filter nodig heeft (of als aanvulling) van een ruimtelijke zoals weergegeven in het voorbeeld 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)
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.
# Only return selected fields
request.setSubsetOfAttributes([0,2])
# More user friendly version
request.setSubsetOfAttributes(['name','id'],layer.pendingFields())
# Don't return geometry objects
request.setFlags(QgsFeatureRequest.NoGeometry)
Tip
If you only need a subset of the attributes or you don’t need the geometry informations, you can significantly increase the speed of the features request by using QgsFeatureRequest.NoGeometry flag or specifying a subset of attributes (possibly empty) like shown in the example above.
De meeste vector gegevensproviders ondersteunen het bewerken van gegevens van de laag. Soms ondersteunen zij slechts een subset van mogelijk acties voor bewerken. Gebruik de functie capabilities() om uit te zoeken welke set voor functionaliteiten wordt ondersteund
caps = layer.dataProvider().capabilities()
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:
# 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.setCacheImage(None)
else:
iface.mapCanvas().refresh()
Create some QgsFeature instances and pass a list of them to provider’s addFeatures() method. It will return two values: result (true/false) and list of added features (their ID is set by the data store)
if caps & QgsVectorDataProvider.AddFeatures:
feat = QgsFeature()
feat.addAttribute(0, 'hello')
feat.setGeometry(QgsGeometry.fromPoint(QgsPoint(123, 456)))
(res, outFeats) = layer.dataProvider().addFeatures([feat])
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])
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
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.fromPoint(QgsPoint(111,222))
layer.dataProvider().changeGeometryValues({ fid : geom })
Tip
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.)
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.
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])
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()
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.
To find out whether a layer is in editing mode, use isEditing() — the editing functions work only when the editing mode is turned on. Usage of editing functions
# 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
layer.changeGeometry(fid, geometry)
# update an attribute with given field index (int) to given value (QVariant)
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.) Hoe de functionaliteit Ongedaan maken te gebruiken
layer.beginEditCommand("Feature triangulation")
# ... call layer's editing methods ...
if problem_occurred:
layer.destroyEditCommand()
return
# ... more editing ...
layer.endEditCommand()
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.
To start editing mode, there is startEditing() method, to stop editing there are commitChanges() and rollback() — however normally you should not need these methods and leave this functionality to be triggered by the user.
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.
Spatial indexes are not created by default for a QGIS vector layer, but you can create them easily. This is what you have to do.
ruimtelijke index maken — de volgende code maakt een lege index
index = QgsSpatialIndex()
add features to index — index takes QgsFeature object and adds it to the internal data structure. You can create the object manually or use one from previous call to provider’s nextFeature()
index.insertFeature(feat)
als de ruimtelijke index eenmaal is gevuld met enkele waarden, kunt u enkele query’s uitvoeren
# returns array of feature IDs of five nearest features
nearest = index.nearestNeighbor(QgsPoint(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 bestanden voor vectorlagen schrijven met behulp van de klasse QgsVectorFileWriter. Het ondersteunt elke andere soort vectorbestand dat wordt ondersteunt door OGR (shapefiles, GeoJSON, KML en andere).
Er zijn twee mogelijkheden voor het exporteren van een vectorlaag:
vanuit een instance van QgsVectorLayer
error = QgsVectorFileWriter.writeAsVectorFormat(layer, "my_shapes.shp", "CP1250", None, "ESRI Shapefile")
if error == QgsVectorFileWriter.NoError:
print "success!"
error = QgsVectorFileWriter.writeAsVectorFormat(layer, "my_json.json", "utf-8", None, "GeoJSON")
if error == QgsVectorFileWriter.NoError:
print "success again!"
The third parameter specifies output text encoding. Only some drivers need this for correct operation - shapefiles are one of those — however in case you are not using international characters you do not have to care much about the encoding. The fourth parameter that we left as None may specify destination CRS — if a valid instance of QgsCoordinateReferenceSystem is passed, the layer is transformed to that CRS.
For valid driver names please consult the supported formats by OGR — you should pass the value in the “Code” column as the driver name. Optionally you can set whether to export only selected features, pass further driver-specific options for creation or tell the writer not to create attributes — look into the documentation for full syntax.
direct uit objecten
# define fields for feature attributes. A list of QgsField objects is needed
fields = [QgsField("first", QVariant.Int),
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. encoding of the attributes
# 3. field map
# 4. geometry type - from WKBTYPE enum
# 5. layer's spatial reference (instance of
# QgsCoordinateReferenceSystem) - optional
# 6. driver name for the output file
writer = QgsVectorFileWriter("my_shapes.shp", "CP1250", fields, QGis.WKBPoint, None, "ESRI Shapefile")
if writer.hasError() != QgsVectorFileWriter.NoError:
print "Error when creating shapefile: ", writer.hasError()
# add a feature
fet = QgsFeature()
fet.setGeometry(QgsGeometry.fromPoint(QgsPoint(10,10)))
fet.setAttributes([1, "text"])
writer.addFeature(fet)
# delete the writer to flush features to disk (optional)
del writer
Memory-provider is bedoeld om te worden gebruikt door voornamelijk plug-in of ontwikkelaars voor 3e partijen. Het slaat geen gegevens op op de schijf, 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", of "MultiPolygon".
De URI mag ook het coördinaten referentiesysteem specificeren, velden, en indexeren van de memory-provider in de URI. De syntaxis is:
Specificeert het coördinaten referentiesysteem, waar definition een van de vormen kan zijn die worden geaccepteerd door QgsCoordinateReferenceSystem.createFromString()
Specificeert dat de provider een ruimtelijke index zal gebruiken
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
# 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.fromPoint(QgsPoint(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
# show some stats
print "fields:", len(pr.fields())
print "features:", pr.featureCount()
e = layer.extent()
print "extent:", e.xMiniminum(), e.yMinimum(), e.xMaximum(), e.yMaximum()
# iterate over features
f = QgsFeature()
features = vl.getFeatures()
for f in features:
print "F:", f.id(), f.attributes(), f.geometry().asPoint()
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.rendererV2()
En met die verwijzing, laten we het een beetje verkennen
print "Type:", rendererV2.type()
Er zijn verschillende bekende typen renderer beschikbaar in de bron-bibliotheek van QGIS:
Type | Klasse |
Omschrijving |
---|---|---|
singleSymbol | QgsSingleSymbolRendererV2 | Rendert alle objecten met hetzelfde symbool |
categorizedSymbol | QgsCategorizedSymbolRendererV2 | Rendert objecten door een ander symbool voor elke categorie te gebruiken |
graduatedSymbol | QgsGraduatedSymbolRendererV2 | 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 slechts deze typen beschikbaar zijn. U kunt singleton QgsRendererV2Registry bevragen om de huidige beschikbare renderers te achterhalen:
QgsRendererV2Registry.instance().renderersList()
# Prints:
[u'singleSymbol',
u'categorizedSymbol',
u'graduatedSymbol',
u'RuleRenderer',
u'pointDisplacement',
u'invertedPolygonRenderer',
u'heatmapRenderer']
Het is mogelijk om een dump te verkrijgen van de inhoud van een renderer in de vorm van tekst — kan handig zijn bij debuggen
print rendererV2.dump()
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 QgsMarkerSymbolV2, QgsLineSymbolV2 en QgsFillSymbolV2.
Het aan createSimple() doorgegeven woordenboek stelt de eigenschappen voor de stijl van het symbool in.
For example you can change the symbol used by a particular point layer by calling setSymbol() passing an instance of a QgsMarkerSymbolV2 as in the following code example:
symbol = QgsMarkerSymbolV2.createSimple({'name': 'square', 'color': 'red'})
layer.rendererV2().setSymbol(symbol)
name geeft de vorm van de markering aan, en kan één van de volgende zijn:
U kunt de naam van het attribuut, dat gebruikt wordt voor de classificatie, bevragen en instellen: gebruik de methoden: classAttribute() en setClassAttribute().
Een lijst categorieën verkrijgen
for cat in rendererV2.categories():
print "%s: %s :: %s" % (cat.value().toString(), cat.label(), str(cat.symbol()))
Waar value() de waarde is die wordt gebruikt voor de verdeling in 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().
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
for ran in rendererV2.ranges():
print "%f - %f: %s %s" % (
ran.lowerValue(),
ran.upperValue(),
ran.label(),
str(ran.symbol())
)
U kunt opnieuw classAttribute() gebruiken om de naam van het attribuut voor classificatie te zoeken, methoden sourceSymbol() en sourceColorRamp(). 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)
from qgis.core import *
myVectorLayer = QgsVectorLayer(myVectorPath, myName, 'ogr')
myTargetField = 'target_field'
myRangeList = []
myOpacity = 1
# Make our first symbol and range...
myMin = 0.0
myMax = 50.0
myLabel = 'Group 1'
myColour = QtGui.QColor('#ffee00')
mySymbol1 = QgsSymbolV2.defaultSymbol(myVectorLayer.geometryType())
mySymbol1.setColor(myColour)
mySymbol1.setAlpha(myOpacity)
myRange1 = QgsRendererRangeV2(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 = QgsSymbolV2.defaultSymbol(
myVectorLayer.geometryType())
mySymbol2.setColor(myColour)
mySymbol2.setAlpha(myOpacity)
myRange2 = QgsRendererRangeV2(myMin, myMax, mySymbol2 myLabel)
myRangeList.append(myRange2)
myRenderer = QgsGraduatedSymbolRendererV2('', myRangeList)
myRenderer.setMode(QgsGraduatedSymbolRendererV2.EqualInterval)
myRenderer.setClassAttribute(myTargetField)
myVectorLayer.setRendererV2(myRenderer)
QgsMapLayerRegistry.instance().addMapLayer(myVectorLayer)
Voor het weergeven van symbolen is er de basisklasse QgsSymbolV2 met drie afgeleide klassen:
QgsMarkerSymbolV2 — voor objecten punt
QgsLineSymbolV2 — voor objecten lijn
QgsFillSymbolV2 — voor objecten polygoon
Elk symbool bestaat uit één of meer symboollagen (klassen afgeleid van QgsSymbolLayerV2). 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
for i in xrange(symbol.symbolLayerCount()):
lyr = symbol.symbolLayer(i)
print "%d: %s" % (i, lyr.layerType())
Gebruik de methode color() om de kleur van het symbool vast te stellen en setColor() om die kleur te wijzigen. Met aanvullende markeringssymbolen kunt u vragen naar de grootte en rotatie van het symbool met de methoden size() en angle(), voor lijnsymbolen is er de methode width() die de dikte van de lijn teruggeeft.
Grootte en breedte zijn standaard in millimeters, hoeken zijn in graden.
Zoals eerder gezegd bepalen symboollagen (subklassen van QgsSymbolLayerV2) 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 kunt maken voor een bepaalde klasse van een symboollaag, verkrijgen op deze manier
from qgis.core import QgsSymbolLayerV2Registry
myRegistry = QgsSymbolLayerV2Registry.instance()
myMetadata = myRegistry.symbolLayerMetadata("SimpleFill")
for item in myRegistry.symbolLayersForType(QgsSymbolV2.Marker):
print item
Uitvoer
EllipseMarker
FontMarker
SimpleMarker
SvgMarker
VectorField
Klasse QgsSymbolLayerV2Registry beheert een database van alle beschikbare typen symboollagen.
Gebruik zijn methode properties() om toegang te verkrijgen tot de gegevens van de symbollaag, 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(), width() met hun tegenhangers om ze in te stellen. Natuurlijk zijn grootte en hoek alleen beschikbaar voor symboollagen voor markeringen en breedte voor lijn-symboollagen.
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
class FooSymbolLayer(QgsMarkerSymbolLayerV2):
def __init__(self, radius=4.0):
QgsMarkerSymbolLayerV2.__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. Eigenschappen worden 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() die het renderen uitvoert. 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, resp. renderPolygon() welke een lijst van punten op de buitenste ring als een 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
class FooSymbolLayerWidget(QgsSymbolLayerV2Widget):
def __init__(self, parent=None):
QgsSymbolLayerV2Widget.__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 functie 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 de voorvertoning 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
class FooSymbolLayerMetadata(QgsSymbolLayerV2AbstractMetadata):
def __init__(self):
QgsSymbolLayerV2AbstractMetadata.__init__(self, "FooMarker", QgsSymbolV2.Marker)
def createSymbolLayer(self, props):
radius = float(props[QString("radius")]) if QString("radius") in props else 4.0
return FooSymbolLayer(radius)
def createSymbolLayerWidget(self):
return FooSymbolLayerWidget()
QgsSymbolLayerV2Registry.instance().addSymbolLayerType(FooSymbolLayerMetadata())
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. createSymbolLayer() zorgt voor het maken van een instance van de symboollaag met attributen die zijn gespecificeerd in het woordenboek props. (Let op: de sleutels zijn QString instances, geen “str”-objecten). En er is de methode createSymbolLayerWidget() die 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.
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
import random
class RandomRenderer(QgsFeatureRendererV2):
def __init__(self, syms=None):
QgsFeatureRendererV2.__init__(self, "RandomRenderer")
self.syms = syms if syms else [QgsSymbolV2.defaultSymbol(QGis.Point), QgsSymbolV2.defaultSymbol(QGis.Point)]
def symbolForFeature(self, feature):
return random.choice(self.syms)
def startRender(self, context, vlayer):
for s in self.syms:
s.startRender(context)
def stopRender(self, context):
for s in self.syms:
s.stopRender(context)
def usedAttributes(self):
return []
def clone(self):
return RandomRenderer(self.syms)
De constructor van de bovenliggende klasse QgsFeatureRendererV2 heeft de naam van de renderer nodig (moet uniek 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 ene lijst met veldnamen teruggeven waarvan de renderer verwacht dat die aanwezig is. Tenslotte zou de functie 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 QgsRendererV2Widget. De volgende voorbeeldcode maakt een knop die de gebruiker in staat stelt het symbool in te stellen van het eerste symbool
class RandomRendererWidget(QgsRendererV2Widget):
def __init__(self, layer, style, renderer):
QgsRendererV2Widget.__init__(self, layer, style)
if renderer is None or renderer.type() != "RandomRenderer":
self.r = RandomRenderer()
else:
self.r = renderer
# setup UI
self.btn1 = QgsColorButtonV2("Color 1")
self.btn1.setColor(self.r.syms[0].color())
self.vbox = QVBoxLayout()
self.vbox.addWidget(self.btn1)
self.setLayout(self.vbox)
self.connect(self.btn1, SIGNAL("clicked()"), self.setColor1)
def setColor1(self):
color = QColorDialog.getColor(self.r.syms[0].color(), self)
if not color.isValid(): return
self.r.syms[0].setColor(color);
self.btn1.setColor(self.r.syms[0].color())
def renderer(self):
return self.r
De constructor ontvangt instances van de actieve laag (QgsVectorLayer), de globale opmaak (QgsStyleV2) en huidige renderer. Indien er geen renderer is of de renderer heeft een andere 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 — het 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
class RandomRendererMetadata(QgsRendererV2AbstractMetadata):
def __init__(self):
QgsRendererV2AbstractMetadata.__init__(self, "RandomRenderer", "Random renderer")
def createRenderer(self, element):
return RandomRenderer()
def createRendererWidget(self, layer, style, renderer):
return RandomRendererWidget(layer, style, renderer)
QgsRendererV2Registry.instance().addRenderer(RandomRendererMetadata())
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 QgsRendererV2AbstractMetadata als een derde (optioneel) argument — de basis klassse-constructor in de functie __init__() van de RandomRendererMetadata wordt
QgsRendererV2AbstractMetadata.__init__(self,
"RandomRenderer",
"Random renderer",
QIcon(QPixmap("RandomRendererIcon.png", "png")))
Het pictogram kan ook op een later tijdstip worden geassocieerd met behulp van 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 (PyQt4 bevat .qrc compiler voor Python).
maken/aanpassen van symbolen die werken met stijl (QgsStyleV2) werken met kleurbalken (QgsVectorColorRampV2) op regels gebaseerde-renderer (zie deze blogpost) die de symboollaag en registreren van de renderer verkent