Dit gedeelte beschrijft verschillende acties die kunnen worden uitgevoerd met vectorlagen.
You can retrieve information 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()
Notitie
Starting from QGIS 2.12 there is also a fields() in QgsVectorLayer which is an alias to pendingFields().
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 features are normally highlighted 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.
To change the selection color you can use setSelectionColor() method of QgsMapCanvas as shown in the following example:
iface.mapCanvas().setSelectionColor( QColor("red") )
To add add features to the selected features list for a given layer, you can call setSelectedFeatures() passing to it the list of features IDs:
# 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()])
To clear the selection, just pass an empty list:
layer.setSelectedFeatures([])
Iterating over the features in a vector layer is one of the most common tasks. Below is an example of the simple basic code to perform this task and showing some information about each feature. the layer variable is assumed to have a QgsVectorLayer object
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
Attributes can be referred to by their name.
print feature['name']
Alternatively, attributes can be referred to by index. This is will be a bit faster than using the name. For example, to get the first attribute:
print feature[0]
if you only need selected features, you can use the selectedFeatures() method from vector layer:
selection = layer.selectedFeatures()
print len(selection)
for feature in selection:
# do whatever you need with the feature
Another option is the Processing features() method:
import processing
features = processing.features(layer)
for feature in features:
# do whatever you need with the feature
By default, this will iterate over all the features in the layer, in case there is no selection, or over the selected features otherwise. Note that this behavior can be changed in the Processing options to ignore selections.
If you want to iterate over a given subset of features in a layer, such as those within a given area, you have to add a QgsFeatureRequest object to the getFeatures() call. Here’s an example
request = QgsFeatureRequest()
request.setFilterRect(areaOfInterest)
for feature in layer.getFeatures(request):
# do whatever you need with the feature
With setLimit() you can limit the number of requested features. Here’s an example
request = QgsFeatureRequest()
request.setLimit(2)
for feature in layer.getFeatures(request):
# loop through only 2 features
If you need an attribute-based filter instead (or in addition) of a spatial one like shown in the examples above, you can build an QgsExpression object and pass it to the QgsFeatureRequest constructor. Here’s an example
# 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)
See Expressies, filteren en waarden berekenen for the details about the syntax supported by QgsExpression.
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
Speed features request
If you only need a subset of the attributes or you don’t need the geometry information, 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.
Most vector data providers support editing of layer data. Sometimes they support just a subset of possible editing actions. Use the capabilities() function to find out what set of functionality is supported
caps = layer.dataProvider().capabilities()
# Check if a particular capability is supported:
caps & QgsVectorDataProvider.DeleteFeatures
# Print 2 if DeleteFeatures is supported
For a list of all available capabilities, please refer to the API Documentation of QgsVectorDataProvider
To print layer’s capabilities textual description in a comma separated list you can use capabilitiesString() as in the following example:
caps_string = layer.dataProvider().capabilitiesString()
# Print:
# u'Add Features, Delete Features, Change Attribute Values,
# Add Attributes, Delete Attributes, Create Spatial Index,
# Fast Access to Features at ID, Change Geometries,
# Simplify Geometries with topological validation'
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).
To set up the attributes you can either initialize the feature passing a QgsFields instance or call initAttributes() passing the number of fields you want to be added.
if caps & QgsVectorDataProvider.AddFeatures:
feat = QgsFeature(layer.pendingFields())
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.fromPoint(QgsPoint(123, 456)))
(res, outFeats) = layer.dataProvider().addFeatures([feat])
To delete some features, just provide a list of their feature IDs
if caps & QgsVectorDataProvider.DeleteFeatures:
res = layer.dataProvider().deleteFeatures([5, 10])
It is possible to either change feature’s geometry or to change some attributes. The following example first changes values of attributes with index 0 and 1, then it changes the feature’s geometry
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
Favor QgsVectorLayerEditUtils class for geometry-only edits
If you only need to change geometries, you might consider using the QgsVectorLayerEditUtils which provides some of useful methods to edit geometries (translate, insert or move vertex etc.).
Tip
Directly save changes using with based command
Using with edit(layer): the changes will be commited automatically calling commitChanges() at the end. If any exception occurs, it will rollBack() all the changes. See Vectorlagen bewerken met een bewerkingsbuffer.
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.
from PyQt4.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])
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()
When editing vectors within QGIS application, you have to first start editing mode for a particular layer, then do some modifications and finally commit (or rollback) the changes. All the changes you do are not written until you commit them — they stay in layer’s in-memory editing buffer. It is possible to use this functionality also programmatically — it is just another method for vector layer editing that complements the direct usage of data providers. Use this option when providing some GUI tools for vector layer editing, since this will allow user to decide whether to commit/rollback and allows the usage of undo/redo. When committing changes, all changes from the editing buffer are saved to data provider.
To find out whether a layer is in editing mode, use isEditable() — the editing functions work only when the editing mode is turned on. Usage of editing functions
from PyQt4.QtCore import QVariant
# 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)
In order to make undo/redo work properly, the above mentioned calls have to be wrapped into undo commands. (If you do not care about undo/redo and want to have the changes stored immediately, then you will have easier work by editing with data provider.) How to use the undo functionality
layer.beginEditCommand("Feature triangulation")
# ... call layer's editing methods ...
if problem_occurred:
layer.destroyEditCommand()
return
# ... more editing ...
layer.endEditCommand()
The beginEditCommand() will create an internal “active” command and will record subsequent changes in vector layer. With the call to endEditCommand() the command is pushed onto the undo stack and the user will be able to undo/redo it from GUI. In case something went wrong while doing the changes, the destroyEditCommand() method will remove the command and rollback all changes done while this command was active.
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.
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 = layer.getFeatures().next()
feat[0] = 5
layer.updateFeature(feat)
This will automatically call commitChanges() in the end. If any exception occurs, it will rollBack() all the changes. In case a problem is encountered within commitChanges() (when the method returns False) a QgsEditError exception will be raised.
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:
create spatial index — the following code creates an empty 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 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
# 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))
You can write vector layer files using QgsVectorFileWriter class. It supports any other kind of vector file that OGR supports (shapefiles, GeoJSON, KML and others).
There are two possibilities how to export a vector layer:
from an instance of 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.
Voor geldige namen van stuurprogramma’s, bekijk supported formats by OGR — u zou de waarde in de kolom “Code” door moeten geven als de naam van het stuurprogramma. Optioneel kunt u instellen om alleen geselecteerde objecten te exporteren, nadere specifieke opties voor het stuurprogramma voor het maken door te geven of de schrijver vertellen geen attributen te maken — bekijk de documentatie voor de volledige syntaxis.
directly from features
from PyQt4.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. 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: ", w.errorMessage()
# 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
del writer
Memory provider is intended to be used mainly by plugin or 3rd party app developers. It does not store data on disk, allowing developers to use it as a fast backend for some temporary layers.
De provider ondersteunt velden string, int en double.
The memory provider also supports spatial indexing, which is enabled by calling the provider’s createSpatialIndex() function. Once the spatial index is created you will be able to iterate over features within smaller regions faster (since it’s not necessary to traverse all the features, only those in specified rectangle).
A memory provider is created by passing "memory" as the provider string to the QgsVectorLayer constructor.
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 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
from PyQt4.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.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.
The renderer for a given layer can obtained as shown below:
renderer = layer.rendererV2()
En met die verwijzing, laten we het een beetje verkennen
print "Type:", rendererV2.type()
There are several known renderer types available in QGIS core library:
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 |
There might be also some custom renderer types, so never make an assumption there are just these types. You can query QgsRendererV2Registry singleton to find out currently available renderers:
print QgsRendererV2Registry.instance().renderersList()
# Print:
[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()
You can get the symbol used for rendering by calling symbol() method and change it with setSymbol() method (note for C++ devs: the renderer takes ownership of the symbol.)
You can change the symbol used by a particular vector layer by calling setSymbol() passing an instance of the appropriate symbol instance. Symbols for point, line and polygon layers can be created by calling the createSimple() function of the corresponding classes QgsMarkerSymbolV2, QgsLineSymbolV2 and QgsFillSymbolV2.
The dictionary passed to createSimple() sets the style properties of the symbol.
For example you can replace 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:
To get the full list of properties for the first symbol layer of a simbol instance you can follow the example code:
print layer.rendererV2().symbol().symbolLayers()[0].properties()
# Prints
{u'angle': u'0',
u'color': u'0,128,0,255',
u'horizontal_anchor_point': u'1',
u'name': u'circle',
u'offset': u'0,0',
u'offset_map_unit_scale': u'0,0',
u'offset_unit': u'MM',
u'outline_color': u'0,0,0,255',
u'outline_style': u'solid',
u'outline_width': u'0',
u'outline_width_map_unit_scale': u'0,0',
u'outline_width_unit': u'MM',
u'scale_method': u'area',
u'size': u'2',
u'size_map_unit_scale': u'0,0',
u'size_unit': u'MM',
u'vertical_anchor_point': u'1'}
Dit kan nuttig zijn als u enkele eigenschappen wilt wijzigen:
# You can alter a single property...
layer.rendererV2().symbol().symbolLayer(0).setName('square')
# ... but not all properties are accessible from methods,
# you can also replace the symbol completely:
props = layer.rendererV2().symbol().symbolLayer(0).properties()
props['color'] = 'yellow'
props['name'] = 'square'
layer.rendererV2().setSymbol(QgsMarkerSymbolV2.createSimple(props))
You can query and set attribute name which is used for classification: use classAttribute() and setClassAttribute() methods.
Een lijst categorieën verkrijgen
for cat in rendererV2.categories():
print "%s: %s :: %s" % (cat.value().toString(), cat.label(), str(cat.symbol()))
Where value() is the value used for discrimination between categories, label() is a text used for category description and symbol() method returns assigned symbol.
The renderer usually stores also original symbol and color ramp which were used for the classification: sourceColorRamp() and sourceSymbol() methods.
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)
For representation of symbols, there is QgsSymbolV2 base class with three derived classes:
Every symbol consists of one or more symbol layers (classes derived from QgsSymbolLayerV2). The symbol layers do the actual rendering, the symbol class itself serves only as a container for the symbol layers.
Having an instance of a symbol (e.g. from a renderer), it is possible to explore it: type() method says whether it is a marker, line or fill symbol. There is a dump() method which returns a brief description of the symbol. To get a list of symbol layers
for i in xrange(symbol.symbolLayerCount()):
lyr = symbol.symbolLayer(i)
print "%d: %s" % (i, lyr.layerType())
To find out symbol’s color use color() method and setColor() to change its color. With marker symbols additionally you can query for the symbol size and rotation with size() and angle() methods, for line symbols there is width() method returning line width.
Grootte en breedte zijn standaard in millimeters, hoeken zijn in graden.
As said before, symbol layers (subclasses of QgsSymbolLayerV2) determine the appearance of the features. There are several basic symbol layer classes for general use. It is possible to implement new symbol layer types and thus arbitrarily customize how features will be rendered. The layerType() method uniquely identifies the symbol layer class — the basic and default ones are SimpleMarker, SimpleLine and SimpleFill symbol layers types.
You can get a complete list of the types of symbol layers you can create for a given symbol layer class like this
from qgis.core import QgsSymbolLayerV2Registry
myRegistry = QgsSymbolLayerV2Registry.instance()
myMetadata = myRegistry.symbolLayerMetadata("SimpleFill")
for item in myRegistry.symbolLayersForType(QgsSymbolV2.Marker):
print item
Output
EllipseMarker
FontMarker
SimpleMarker
SvgMarker
VectorField
QgsSymbolLayerV2Registry class manages a database of all available symbol layer types.
To access symbol layer data, use its properties() method that returns a key-value dictionary of properties which determine the appearance. Each symbol layer type has a specific set of properties that it uses. Additionally, there are generic methods color(), size(), angle(), width() with their setter counterparts. Of course size and angle is available only for marker symbol layers and width for line symbol layers.
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)
The layerType() method determines the name of the symbol layer, it has to be unique among all symbol layers. Properties are used for persistence of attributes. clone() method must return a copy of the symbol layer with all attributes being exactly the same. Finally there are rendering methods: startRender() is called before rendering first feature, stopRender() when rendering is done. And renderPoint() method which does the rendering. The coordinates of the point(s) are already transformed to the output coordinates.
For polylines and polygons the only difference would be in the rendering method: you would use renderPolyline() which receives a list of lines, resp. renderPolygon() which receives list of points on outer ring as a first parameter and a list of inner rings (or None) as a second 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())
You should pass layer type (the same as returned by the layer) and symbol type (marker/line/fill) to the constructor of parent class. createSymbolLayer() takes care of creating an instance of symbol layer with attributes specified in the props dictionary. (Beware, the keys are QString instances, not “str” objects). And there is createSymbolLayerWidget() method which returns settings widget for this symbol layer type.
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)
The constructor of parent QgsFeatureRendererV2 class needs renderer name (has to be unique among renderers). symbolForFeature() method is the one that decides what symbol will be used for a particular feature. startRender() and stopRender() take care of initialization/finalization of symbol rendering. usedAttributes() method can return a list of field names that renderer expects to be present. Finally clone() function should return a copy of the renderer.
Like with symbol layers, it is possible to attach a GUI for configuration of the renderer. It has to be derived from QgsRendererV2Widget. The following sample code creates a button that allows user to set symbol of the first symbol
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()
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
The constructor receives instances of the active layer (QgsVectorLayer), the global style (QgsStyleV2) and current renderer. If there is no renderer or the renderer has different type, it will be replaced with our new renderer, otherwise we will use the current renderer (which has already the type we need). The widget contents should be updated to show current state of the renderer. When the renderer dialog is accepted, widget’s renderer() method is called to get the current renderer — it will be assigned to the layer.
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.
To associate an icon with the renderer you can assign it in QgsRendererV2AbstractMetadata constructor as a third (optional) argument — the base class constructor in the RandomRendererMetadata __init__() function becomes
QgsRendererV2AbstractMetadata.__init__(self,
"RandomRenderer",
"Random renderer",
QIcon(QPixmap("RandomRendererIcon.png", "png")))
The icon can be associated also at any later time using setIcon() method of the metadata class. The icon can be loaded from a file (as shown above) or can be loaded from a Qt resource (PyQt4 includes .qrc compiler for Python).
TODO:
symbolen maken/aanpassen
symboollaag en registraties van renderer verkennen