Această secțiune rezumă diferitele acțiuni care pot fi efectuate asupra straturilor vectoriale.
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.
Pentru a schimba culoarea de selecție puteți utiliza metoda setSelectionColor() din QgsMapCanvas, așa cum se arată în exemplul următor:
iface.mapCanvas().setSelectionColor( QColor("red") )
Pentru a adăuga entitățile în lista de entități selectate ale unui strat dat, puteți apela setSelectedFeatures(), pasându-i lista de ID-uri a entităților:
# 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()])
Pentru a anula selecția, transmiteți doar o listă vidă:
layer.setSelectedFeatures([])
Parcurgerea elementelor dintr-un strat vectorial este una dintre cele mai obișnuite activități. Mai jos este prezentat un exemplu de cod de bază, simplu, pentru a efectua această sarcină și care arată unele informații despre fiecare entitate spațială. Variabila layer se consideră a conține un obiect QgsVectorLayer
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
Atributele pot fi apelate după numele lor.
print feature['name']
Alternativ, atributele pot fi menționate de index. Acesta va fi un pic mai rapid decât prin folosirea numelui. De exemplu, pentru a obține primul atribut:
print feature[0]
dacă aveți nevoie doar de entitățile selectate, puteți utiliza metoda selectedFeatures() din stratulvectorial:
selection = layer.selectedFeatures()
print len(selection)
for feature in selection:
# do whatever you need with the feature
O altă opțiune o constituie metoda Processing features():
import processing
features = processing.features(layer)
for feature in features:
# do whatever you need with the feature
În mod implicit se vor parcurge toate entitățile stratului, în cazul în care nu există o selecție, sau, în caz contrar, doar entitățile selectate. Rețineți că acest comportament poate fi schimbat în opțiunile Processing, pentru a ignora selecțiile.
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 feature in layer.getFeatures(request):
# do whatever you need with the feature
Dacă aveți nevoie de un filtru pe bază de atribut în locul unuia spațial (sau în plus față de acesta), așa cum se vede în exemplul de mai sus, puteți construi un obiect QgsExpression și să-i transmiteți constructorul QgsFeatureRequest. Iată un exemplu
# 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)
Cererea poate fi utilizată pentru a defini datele cerute pentru fiecare entitate, astfel încât iteratorul să întoarcă toate entitățile, dar să returneze datele parțiale pentru fiecare dintre ele.
# 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.
Cei mai mulți dintre furnizorii de date vectoriale suportă editarea datelor stratului. Uneori, aceștia acceptă doar un subset restrâns de acțiuni de editare. Utilizați funcția capabilities() pentru a afla care set de funcții este disponibil
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ă efectuarea modificărilor cu ajutorul tamponului de editare.
Note
Dacă lucrați în interiorul QGIS (fie din consola fie printr-un plugin), ar putea fi necesar să forțați o redesenare a canevasului hărții, pentru a vedea modificările aduse geometriei, stilului sau atributelor:
# 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])
Pentru a șterge unele entități, e suficientă furnizarea unei liste cu ID-uri
if caps & QgsVectorDataProvider.DeleteFeatures:
res = layer.dataProvider().deleteFeatures([5, 10])
Este posibilă, fie schimbarea geometriei unei entități, fie schimbarea unor atribute. În următorul exemplu are loc mai întâi schimbarea valorilor atributelor cu indexul 0 sau 1, iar mai apoi se schimbă geometria entității
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
Dacă trebuie doar să schimbați geometriile, ați putea lua în considerare utilizarea QgsVectorLayerEditUtils care oferă unele dintre metodele utile pentru a edita geometrii (traducere, introducere sau mutare vertex etc.)
Pentru a adăuga câmpuri (atribute), trebuie să specificați o listă de definiții pentru acestea. Pentru ștergerea de câmpuri e suficientă furnizarea unei liste de indecși pentru câmpuri.
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 modificările nu se propagă 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)
Pentru ca undo/redo să funcționeze în mod corespunzător, apelurile de mai sus trebuie să fie înglobate în comenzi undo. (Dacă nu vă pasă de undo/redo și doriți să stocați imediat modificările, atunci veți avea o sarcină mai ușoară prin :ref: folosirea <editorului> furnizorului de date.) Cum să utilizați funcționalitatea undo
layer.beginEditCommand("Feature triangulation")
# ... call layer's editing methods ...
if problem_occurred:
layer.destroyEditCommand()
return
# ... more editing ...
layer.endEditCommand()
beginEndCommand() va crea o comandă internă “activă” și va înregistra modificările ulterioare din stratul vectorial. Cu apelul către endEditCommand() comanda este împinsă pe stiva undo, iar utilizatorul va putea efectua undo/redo prin GUI. În cazul în care ceva nu a mers bine pe timpul efectuării schimbărilor, metoda destroyEditCommand() va elimina comanda și va da înapoi toate modificările făcute pe perioada când această comandă a fost activă.
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.
Indecșii spațiali pot îmbunătăți dramatic performanța codului dvs, în cazul în care este nevoie să interogați frecvent un strat vectorial. Imaginați-vă, de exemplu, că scrieți un algoritm de interpolare, și că, pentru o anumită locație, trebuie să aflați cele mai apropiate 10 puncte dintr-un strat, în scopul utilizării acelor puncte în calculul valorii interpolate. Fără un index spațial, singura modalitate pentru QGIS de a găsi cele 10 puncte, este de a calcula distanța tuturor punctelor față de locația specificată și apoi de a compara aceste distanțe. Această sarcină poate fi mare consumatoare de timp, mai ales în cazul în care trebuie să fie repetată pentru mai multe locații. Dacă pentru stratul respectiv există un index spațial, operațiunea va fi mult mai eficientă.
Gândiți-vă la un strat fără index spațial ca la o carte de telefon în care numerele de telefon nu sunt ordonate sau indexate. Singura modalitate de a afla numărul de telefon al unei anumite persoane este de a citi toate numerele, începând cu primul, până când îl găsiți.
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.
creare index spațial — următorul cod creează un index vid
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)
o dată ce ați introdus valori în indexul spațial, puteți efectua unele interogări
# 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:
dintr-o instanță a 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 din entități
# 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.
Următorul exemplu de URI încorporează toate aceste opțiuni
"Point?crs=epsg:4326&field=id:integer&field=name:string(20)&index=yes"
Următorul exemplu de cod ilustrează crearea și popularea unui furnizor de memorie
# 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()
În cele din urmă, să verificăm dacă totul a mers bine
# 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()
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()
Și cu acea referință, să explorăm un pic
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ă niciodată să nu presupuneți că există doar aceste tipuri. Puteți interoga singelton-ul QgsRendererV2Registry pentru a afla tipurile de rendere disponibile în prezent:
QgsRendererV2Registry.instance().renderersList()
# Prints:
[u'singleSymbol',
u'categorizedSymbol',
u'graduatedSymbol',
u'RuleRenderer',
u'pointDisplacement',
u'invertedPolygonRenderer',
u'heatmapRenderer']
Este posibilă obținerea conținutului renderului sub formă de text — lucru util pentru depanare
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 schimba simbolul utilizat de un strat vectorial, particular, prin apelarea setSymbol() și transmiterea unei instanțe corespunzătoare de instanță simbol. Simbolurile pentru straturile de tip punct, linie și poligon pot fi create prin apelarea funcției createSimple() din clasele corespunzătoare, QgsMarkerSymbolV2, QgsLineSymbolV2 și QgsFillSymbolV2.
Dicționarul transmis către createSimple() stabilește proprietățile de stil ale simbolului.
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)
nume: indică forma markerului, aceasta putând fi oricare dintre următoarele:
cerc
pătrat
dreptunghi
diamant
triunghi
triunghi echilateral
stea
stea_regulată
săgeată
vârf_de_săgeată_plin
Puteți interoga și seta numele atributului care este folosit pentru clasificare: folosiți metodele classAttribute() și setClassAttribute().
Pentru a obține o listă de categorii
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.
Pentru a afla mai multe despre gamele utilizate în render
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ă.
Dacă doriți să creați propriul render cu simbol gradual, puteți face acest lucru așa cum este ilustrat în fragmentul de mai jos (care creează un simplu aranjament cu două clase)
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)
Pentru reprezentarea simbolurilor există clasa de bază QgsSymbolV2, având trei clase derivate:
QgsMarkerSymbolV2 — pentru entități de tip punct
QgsLineSymbolV2 — pentru entități de tip linie
QgsFillSymbolV2 — pentru entități de tip poligon
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.
Având o instanță a unui simbol (de exemplu, de la un render), este posibil să o explorăm: metoda type() spunându-ne dacă acesta este un marker, o linie sau un simbol de umplere. Există și metoda dump() care returnează o scurtă descriere a simbolului. Pentru a obține o listă a straturilor simbolului
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.
Puteți obține, în modul următor, o listă completă a tipurilor de straturi pe care le puteți crea pentru o anumită clasă de simboluri
from qgis.core import QgsSymbolLayerV2Registry
myRegistry = QgsSymbolLayerV2Registry.instance()
myMetadata = myRegistry.symbolLayerMetadata("SimpleFill")
for item in myRegistry.symbolLayersForType(QgsSymbolV2.Marker):
print item
Rezultat
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.
Imaginați-vă că ați dori să personalizați modul în care se randează datele. Vă puteți crea propria dvs. clasă de strat de simbol, care va desena entitățile exact așa cum doriți. Iată un exemplu de marker care desenează cercuri roșii cu o rază specificată
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.
De obicei, este convenabilă adăugarea unui GUI pentru setarea atributelor tipului de strat pentru simboluri, pentru a permite utilizatorilor să personalizeze aspectul: în exemplul de mai sus, putem lăsa utilizatorul să seteze raza cercului. Codul de mai jos implementează un astfel de 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.
Va trebui să creăm metadate pentru stratul simbolului
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
Urmatorul cod prezintă o simplă randare personalizată, care creează două simboluri de tip marker și apoi alege aleatoriu unul dintre ele pentru fiecare entitate
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.
Ca și în cazul straturilor simbol, este posibilă atașarea unui GUI pentru configurarea renderului. Acesta trebuie să fie derivat din QgsRendererV2Widget. Următorul exemplu de cod creează un buton care permite utilizatorului setarea primului simbol
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.
Ultimul bit lipsă este cel al metadatelor renderului și înregistrarea în registru, altfel încărcarea straturilor cu renderul nu va funcționa, iar utilizatorul nu va fi capabil să-l selecteze din lista de rendere. Să finalizăm exemplul nostru de RandomRenderer
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.
Pentru a asocia o pictogramă renderului ați putea să o asignați în constructorul QgsRendererV2AbstractMetadata ca un al treilea argument (opțional) — constructorul clasei de bază din funcția __init__() a RandomRendererMetadata devine
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).
crearea/modificarea simbolurilor, modificarea stilului (QgsStyleV2), modificarea gamelor de culori (QgsVectorColorRampV2), rendere bazate pe reguli (citiți această postare pe blog), explorarea straturilor unui simbol și a regiștrilor renderelor