Această secțiune rezumă diferitele acțiuni care pot fi efectuate asupra straturilor vectoriale.
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:
# retreive 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 refered by index.
idx = layer.fieldNameIndex('name')
print feature.attributes()[idx]
For the above cases, and in case you need to consider selection in a vector layer in case it exist, you can use the features() method from the buil-in processing plugin, as follows:
import processing
features = processing.features(layer)
for feature in features:
#Do whatever you need with the feature
În acest mod se vor parcurge toate entitățile stratului, în cazul în care nu există o selecție, sau, în caz contrar, doar entitățile selectate.
dacă aveți nevoie doar de caracteristicile selectate, puteți utiliza metoda selectedFeatures() a stratului vectorial:
selection = layer.selectedFeatures()
print len(selection)
for feature in selection:
#Do whatever you need with the feature
Dacă doriți să parcurgeți un anumit subset de entități dintr-un strat, cum ar fi cele dintr-o anumită zonă, trebuie să adăugați un obiect QgsFeatureRequest la apelul funcției getFeatures(). Iată un exemplu
request=QgsFeatureRequest()
request.setFilterRect(areaOfInterest)
for f in layer.getFeatures(request):
...
Cererea poate fi utilizată pentru a defini datele cerute pentru fiecare entitate, astfel încât iteratorul să întoarcă toate caracteristicile, dar să returneze date parțiale pentru fiecare dintre ele.
request.setSubsetOfFields([0,2]) # Only return selected fields
request.setSubsetOfFields(['name','id'],layer.fields()) # More user friendly version
request.setFlags( QgsFeatureRequest.NoGeometry ) # Don't return geometry objects
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()
Utilizând oricare dintre următoarele metode de editare a straturilor vectoriale, schimbările sunt efectuate direct în depozitul de date (un fișier, o bază de date etc). În cazul în care doriți să faceți doar schimbări temporare, treceți la secțiunea următoare, care explică modul de editare al unei memorii tampon <editing-buffer>`.
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 ] )
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 })
To add fields (attributes), you need to specify a list of field defnitions. For deletion of fields just provide a list of field indexes.
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 ] )
După adăugarea sau eliminarea câmpurilor din furnizorul de date câmpurile stratului trebuie să fie actualizate, deoarece schimbările nu se propagă în mod automat.
layer.updateFields()
Când editați vectori în aplicația QGIS, în primul rând, trebuie să comutați în modul de editare pentru stratul în care lucrați, apoi să efectuați modificări pe care, în cele din urmă, să le salvați (sau să le anulați). Modificările nu vor fi scrise până când nu sunt salvate — ele rezidând în memorie, în tamponul de editare al stratului. De asemenea, este posibilă utilizarea programatică a acestei funcționalități — aceasta fiind doar o altă metodă pentru editarea straturilor vectoriale, care completează utilizarea directă a furnizorilor de date. Utilizați această opțiune atunci când furnizați unele instrumente GUI pentru editarea straturilor vectoriale, permițând utilizatorului să decidă dacă să salveze/anuleze, și punându-i la dispoziție facilitățile de undo/redo. Atunci când salvați modificările, acestea vor fi transferate din memoria tampon de editare în furnizorul de date.
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)
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 beginEndCommand() 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.
Pentru a activa modul de editare, este disponibilă metoda startEditing(), pentru a opri editarea există :func:commitChanges și rollback () — totuși, în mod normal, ar trebui să nu nevoie de aceste metode și să permiteți utilizatorului declanșarea acestor funcționalități.
Spatial indexes can dramatically improve the performance of your code if you need to do frequent queries to a vector layer. Imagin, for instance, that you are writing an interpolation algorithm, and that for a given location you need to know the 10 closest point from a points layer,, in order to use those point for calculating the interpolated value. Without a spatial index, the only way for QGIS to find those 10 points is to compute the distance from each and every point to the specified location and then compare those distances. This can be a very time consuming task, specilly if it needs to be repeated fro several locations. If a spatial index exists for the layer, the operation is much more effective.
Think of a layer withou a spatial index as a telephone book in which telephone number are not orderer or indexed. The only way to find the telephone number of a given person is to read from the beginning until you find it.
Indicii spațiali nu sunt creați în mod implicit pentru un strat vectorial QGIS, dar îi puteți realiza cu ușurință. Iată ce trebuie să faceți.
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)
once spatial index is filled with some values, you can do some queries:
# 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))
Puteți scrie în fișierele conținând straturi vectoriale folosind clasa QgsVectorFileWriter. Aceasta acceptă orice alt tip de fișier vector care suportă OGR (fișiere shape, GeoJSON, KML și altele).
Există două posibilități de a exporta un strat vectorial:
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!"
Al treilea parametru se referă la codificarea textului de ieșire. Doar unele formate au nevoie de acest lucru pentru o funcționare corectă - fișierul shape fiind printre ele — totuși, în cazul în care nu utilizați caractere internaționale nu trebuie să vă îngrijoreze codificarea. În al patrulea parametru, care acum are valoarea None
, se poate specifica destinația CRS — dacă este trecută o instanță validă a QgsCoordinateReferenceSystem, stratul este transformat pentru acel 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.
directly from features:
# 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
Furnizorul de memorie este destinat, în principal, dezvoltatorilor de plugin-uri sau de aplicații terț3. El nu stochează date pe disc, permițând dezvoltatorilor să-l folosească ca pe un depozit rapid pentru straturi temporare.
Furnizorul suportă câmpuri de tip string, int sau double.
Furnizorul de memorie suportă, de asemenea, indexarea spațială, care este activată prin apelarea furnizorului funcției createSpatialIndex(). O dată ce indexul spațial este creat, veți fi capabili de a parcurge mai rapid entitățile, în interiorul unor regiuni mai mici (din moment ce nu este necesar să traversați toate entitățile, ci doar pe cele din dreptunghiul specificat).
Un furnizor de memorie este creat prin transmiterea "memoriei"
ca șir furnizor către constructorul QgsVectorLayer.
Constructorul are, de asemenea, un URI care definește unul din următoarele tipuri de geometrie a stratului: "Point"
, "LineString"
, "Polygon"
, "MultiPoint"
, "MultiLineString"
sau "MultiPolygon"
.
URI poate specifica, de asemenea, sistemul de coordonate de referință, câmpurile, precum și indexarea furnizorului de memorie. Sintaxa este:
Specificați sistemul de referință de coordonate, unde definiția poate fi oricare din formele acceptate de: QgsCoordinateReferenceSystem.createFromString()
Specificați dacă furnizorul va utiliza un index spațial.
Specificați un atribut al stratului. Atributul are un nume și, opțional, un tip (integer, double sau string), lungime și precizie. Pot exista mai multe definiții de câmp.
The following example of a URI incorporates all these options:
"Point?crs=epsg:4326&field=id:integer&field=name:string(20)&index=yes"
The following example code illustrates creating and populating a 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) ] )
# 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()
Finally, let’s check whether everything went well:
# show some stats
print "fields:", len(pr.fields())
print "features:", pr.featureCount()
e = layer.extent()
print "extent:", e.xMin(),e.yMin(),e.xMax(),e.yMax()
# iterate over features
f = QgsFeature()
features = vl.getFeatures()
for f in features:
print "F:",f.id(), f.attributes(), f.geometry().asPoint()
Când un strat vector este randat, aspectul datelor este dat de render și de simbolurile asociate stratului. Simbolurile sunt clase care au grijă de reprezentarea vizuală a tuturor entităților, în timp ce un render determină ce simbol va fi folosit doar pentru anumite entități.
Tipul de render pentru un strat oarecare poate fi obținut astfel:
renderer = layer.rendererV2()
And with that reference, let us explore it a bit:
print "Type:", rendererV2.type()
Există mai multe tipuri de rendere disponibile în biblioteca de bază a QGIS:
Tipul |
Clasa |
Descrierea |
---|---|---|
singleSymbol | QgsSingleSymbolRendererV2 | Asociază tuturor entităților același simbol |
categorizedSymbol | QgsCategorizedSymbolRendererV2 | Asociază entităților un simbol diferit, în funcție de categorie |
graduatedSymbol | QgsGraduatedSymbolRendererV2 | Asociază fiecărei entități un simbol diferit pentru fiecare gamă de valori |
Ar mai putea exista, de asemenea, unele tipuri de randare personalizate, așa că să nu presupuneți că există numai aceste tipuri. Puteți interoga singelton-ul QgsRendererV2Registry pentru a afla tipurile de randare disponibile în prezent.
It is possible to obtain a dump of a renderer contents in text form — can be useful for debugging:
print rendererV2.dump()
Puteți obține simbolul folosit pentru randare apelând metoda simbol(), și-l puteți schimba cu ajutorul metodei setSymbol() (notă pentru dezvoltatorii C++: renderul devine proprietarul simbolului.)
Puteți interoga și seta numele atributului care este folosit pentru clasificare: folosiți metodele classAttribute() și setClassAttribute().
To get a list of categories:
for cat in rendererV2.categories():
print "%s: %s :: %s" % (cat.value().toString(), cat.label(), str(cat.symbol()))
În cazul în care value() reprezintă valoarea utilizată pentru discriminare între categorii, label() este un text utilizat pentru descrierea categorie iar metoda symbol() returnează simbolul asignat.
Renderul, de obicei, stochează atât simbolul original cât și gamele de culoare care au fost utilizate pentru clasificare: metodele sourceColorRamp() și sourceSymbol().
Acest render este foarte similar cu renderul cu simbol clasificat, descris mai sus, dar în loc de o singură valoare de atribut per clasă el lucrează cu intervale de valori, putând fi, astfel, utilizat doar cu atribute numerice.
To find out more about ranges used in the renderer:
for ran in rendererV2.ranges():
print "%f - %f: %s %s" % (
ran.lowerValue(),
ran.upperValue(),
ran.label(),
str(ran.symbol())
)
puteți folosi din nou classAttribute() pentru a afla numele atributului de clasificare, metodele sourceSymbol() și sourceColorRamp(). În plus, există metoda mode() care determină modul în care au fost create gamele: folosind intervale egale, cuantile sau o altă metodă.
If you wish to create your own graduated symbol renderer you can do so as illustrated in the example snippet below (which creates a simple two class arrangement):
from qgis.core import (QgsVectorLayer,
QgsMapLayerRegistry,
QgsGraduatedSymbolRendererV2,
QgsSymbolV2,
QgsRendererRangeV2)
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)
Pentru reprezentarea simbolurilor există clasa de bază QgsSymbolV2, având trei clase derivate:
- QgsMarkerSymbolV2 - for point features
- QgsLineSymbolV2 - for line features
- QgsFillSymbolV2 - for polygon features
Fiecare simbol este format din unul sau mai multe straturi (clase derivate din QgsSymbolLayerV2). Straturile simbolului realizează în mod curent randarea, clasa simbolului servind doar ca un container pentru acestea.
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())
Pentru a afla culoarea simbolului folosiți metoda color(), iar pentru a schimba culoarea setColor(). În cazul simbolurilor marker, în plus, puteți interoga pentru dimensiunea simbolului și unghiul de rotație cu metodele size() și angle(), iar pentru simbolurile linie există metoda width() care returnează lățimea liniei.
Dimensiunea și lățimea sunt în milimetri, în mod implicit, iar unghiurile sunt în grade.
Așa cum s-a arătat mai înainte, straturile simbolului (subclase ale QgsSymbolLayerV2), determină aspectul entităților. Există mai multe clase de strat simbol de bază, pentru uzul general. Este posibilă implementarea unor noi tipuri de strat simbol și, astfel, personalizarea în mod arbitrar a modului în care vor fi randate entitățile. Metoda layerType() identifică în mod unic clasa stratului simbol — tipurile de straturi simbol de bază și implicite sunt SimpleMarker, SimpleLine și SimpleFill.
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
clasa QgsSymbolLayerV2Registry gestionează o bază de date a tuturor tipurilor de straturi simbol disponibile.
Pentru a accesa datele stratului simbol, folosiți metoda properties() care returnează un dicționar cu valori-cheie ale proprietăților care îi determină aparența. Fiecare tip de strat simbol are un set specific de proprietăți pe care le utilizează. În plus, există metodele generice color(), size(), angle(), width() împreună cu cu omologii lor de setare. Desigur, mărimea și unghiul sunt disponibile doar pentru straturi simbol de tip marcer iar lățimea pentru straturi simbol de tip linie.
Imagine you would like to customize the way how the data gets rendered. You can create your own symbol layer class that will draw the features exactly as you wish. Here is an example of a marker that draws red circles with specified radius:
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)
Metoda layerType() determină numele stratului simbol, acesta trebuind să fie unic printre toate straturile simbol. Proprietățile sunt utilizate pentru persistența atributelor. Metoda clone() trebuie să returneze o copie a stratului simbol, având toate atributele exact la fel. În cele din urmă, mai există metodele de randare: startRender() care este apelată înainte de randarea primei entități, și stopRender() care oprește randarea. Efectiv, randarea are loc cu ajutorul metodei renderPoint(). Coordonatele punctului(punctelor) sunt deja transformate la coordonatele de ieșire.
Pentru polilinii și poligoane singura diferență constă în metoda de randare: ar trebui să utilizați renderPolyline() care primește o listă de linii, respectiv renderPolygon() care primește lista de puncte de pe inelul exterior ca prim parametru și o listă de inele interioare (sau nici unul), ca al doilea parametru.
Usually it is convenient to add a GUI for setting attributes of the symbol layer type to allow users to customize the appearance: in case of our example above we can let user set circle radius. The following code implements such 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()"))
Acest widget poate fi integrat în fereastra de proprietăți a simbolului. În cazul în care tipul de strat simbol este selectat în fereastra de proprietăți a simbolului, se creează o instanță a stratului simbol și o instanță a widget-ului stratului simbol. Apoi, se apelează metoda setSymbolLayer() pentru a aloca stratul simbol widget-ului. În acea metodă, widget-ul ar trebui să actualizeze UI pentru a reflecta atributele stratului simbol. Funcția symbolLayer() este utilizată la preluarea stratului simbol din fereastra de proprietăți, în scopul folosirii sale pentru simbol.
La fiecare schimbare de atribute, widget-ul ar trebui să emită semnalul changed() pentru a permite ferestrei de proprietăți să-și actualizeze previzualizarea simbolului.
Acum mai lipsește doar liantul final: pentru a face QGIS conștient de aceste noi clase. Acest lucru se face prin adăugarea stratului simbol la registru. Este posibilă utilizarea stratului simbol, de asemenea, fără a-l adăuga la registru, dar unele funcționalități nu vor fi disponibile: de exemplu, încărcarea de fișiere de proiect cu straturi simbol personalizate sau incapacitatea de a edita atributele stratului în GUI.
We will have to create metadata for the symbol layer:
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() )
Ar trebui să transmiteți tipul stratului (cel returnat de către strat) și tipul de simbol (marker/linie/umplere) către constructorul clasei părinte. createSymbolLayer() are grijă de a crea o instanță de strat simbol cu atributele specificate în dicționarul props. (Atenție, tastele reprezintă instanțe QString, nu obiecte “str”). Există, de asemenea, metoda createSymbolLayerWidget() care returnează setările widget-ului pentru acest tip de strat simbol.
Ultimul pas este de a adăuga acest strat simbol la registru — și am încheiat.
Ar putea fi utilă crearea unei noi implementări de render, dacă doriți să personalizați regulile de selectare a simbolurilor pentru randarea entităților. Unele cazuri de utilizare: simbolul să fie determinat de o combinație de câmpuri, dimensiunea simbolurilor să depindă în funcție de scara curentă, etc
The following code shows a simple custom renderer that creates two marker symbols and chooses randomly one of them for every feature:
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)
Constructorul clasei părinte QgsFeatureRendererV2 are nevoie de numele renderului (trebuie să fie unic printre rendere). Metoda symbolForFeature() este cea care decide ce simbol va fi folosit pentru o anumită entitate. startRender() și stopRender() vor avea grijă de inițializarea/finalizarea randării simbolului. Metoda usedAttributes() poate returna o listă de nume de câmpuri a căror prezență o așteaptă renderul. În cele din urmă clone() ar trebui să returneze o copie a renderului.
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("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
Constructorul primește instanțe ale stratului activ (QgsVectorLayer), stilul global (QgsStyleV2) și renderul curent. Dacă nu există un render sau renderul are alt tip, acesta va fi înlocuit cu noul nostru render, în caz contrar vom folosi renderul curent (care are deja tipul de care avem nevoie). Conținutul widget-ului ar trebui să fie actualizat pentru a arăta starea actuală a renderului. Când dialogul renderului este acceptat, metoda renderer() a widgetului este apelată pentru a obține renderul curent — acesta fiind atribuit stratului.
The last missing bit is the renderer metadata and registration in registry, otherwise loading of layers with the renderer will not work and user will not be able to select it from the list of renderers. Let us finish our RandomRenderer example:
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())
În mod similar cu straturile simbol, constructorul de metadate abstracte așteaptă numele renderului, nume vizibil pentru utilizatori și numele opțional al pictogramei renderului. Metoda createRenderer() transmite instanța QDomElement care poate fi folosită pentru a restabili starea renderului din arborele DOM. Metoda createRendererWidget() creează widget-ul de configurare. Aceasta nu trebuie să fie prezent sau ar putea returna None, dacă renderul nu vine cu GUI-ul.
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")) )
Pictograma poate fi asociată ulterior, de asemenea, în orice moment, folosind metoda setIcon() a clasei de metadate. Pictograma poate fi încărcată dintr-un fișier (așa cum s-a arătat mai sus), sau dintr-o resursă Qt (PyQt4 include compilatorul .qrc pentru Python).
re TODO:
- creating/modifying symbols
- working with style (QgsStyleV2)
- working with color ramps (QgsVectorColorRampV2)
- rule-based renderer (see .. _this blogpost: http://snorf.net/blog/2014/03/04/symbology-of-vector-layers-in-qgis-python-plugins)
- exploring symbol layer and renderer registries