Utilizarea straturilor vectoriale

Această secțiune rezumă diferitele acțiuni care pot fi efectuate asupra straturilor vectoriale.

Obținerea informaţiilor despre atribute

Puteți obține informațiile despre câmpurile asociate cu un strat vectorial prin apelarea pendingFields() pe o instanță QgsVectorLayer:

# "layer" is a QgsVectorLayer instance
for field in layer.pendingFields():
    print field.name(), field.typeName()

Selectarea entităților

În aplicația QGIS, entitățile pot fi selectate în diverse moduri, utilizatorul putând face clic pe o entitate, să traseze un dreptunghi pe canevasul hărții sau să folosească o expresie de filtrare. Entitățile selectate sunt în mod normal evidențiate printr-o culoare diferită (implicit este galben), pentru a atrage atenția utilizatorului asupra selecției. Uneori poate fi util să selectați programatic entitățile sau să schimbați culoarea implicită.

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([])

Iterații în straturile vectoriale

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

Accesarea atributelor

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]

Parcurgerea entităților selectate

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.

Parcurgerea unui subset de entități

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

Dacă aveți nevoie doar de un subset de atribute sau nu aveți nevoie de informațiile geometrice, puteți crește în mod semnificativ viteza cererii entităților prin utilizarea fanionului QgsFeatureRequest.NoGeometry, sau specificând un subset de atribute (eventual gol), așa cum se arată în exemplul de mai sus.

Modificarea straturilor vectoriale

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()

Adăugarea entităților

Creați câteva instanțe ale clasei QgsFeature și transmiteți o listă a acestora metodei furnizorului addFeatures(). Acesta va returna două valori: rezultatul (true/false) și lista entităților adăugate (ID-ul lor fiind stabilit de către depozitul de date)

if caps & QgsVectorDataProvider.AddFeatures:
    feat = QgsFeature()
    feat.addAttribute(0, 'hello')
    feat.setGeometry(QgsGeometry.fromPoint(QgsPoint(123, 456)))
    (res, outFeats) = layer.dataProvider().addFeatures([feat])

Ștergerea entităților

Pentru a șterge unele entități, e suficientă furnizarea unei liste cu ID-uri

if caps & QgsVectorDataProvider.DeleteFeatures:
    res = layer.dataProvider().deleteFeatures([5, 10])

Modificarea entităților

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.)

Adăugarea și eliminarea câmpurilor

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()

Modificarea straturi vectoriale prin editarea unui tampon de memorie

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.

Pentru a afla dacă un strat se află în modul de editare, utilizați isEditing() — funcțiile de editare funcționînd numai atunci când modul de editare este activat. Utilizarea funcțiilor de editare

# 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ă.

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.

Crearea unui index spațial

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.

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.

  1. creare index spațial — următorul cod creează un index vid

    index = QgsSpatialIndex()
    
  2. adăugare entități la index — indexul ia obiectul QgsFeature și-l adaugă la structura internă de date. Puteți crea obiectul manual sau puteți folosi unul dintre apelurile anterioare către funcția nextFeature() a furnizorului

    index.insertFeature(feat)
    
  3. 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))
    

Scrierea straturilor vectoriale

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!"
    

    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.

    Pentru denumirile valide ale driver-elor, vă rugăm să consultați formatele suportate de OGR — ar trebui să treceți valoarea în coloana “Code”, ca și nume de driver. Opțional, puteți stabili dacă se exportă numai entitățile selectate, transmițând opțiunile specifice driver-ului pentru creare sau indicând generatorului să nu creeze atribute — analizați documentația pentru sintaxa completă.

  • 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

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:

crs=definiție

Specificați sistemul de referință de coordonate, unde definiția poate fi oricare din formele acceptate de: QgsCoordinateReferenceSystem.createFromString()

index=yes

Specificați dacă furnizorul va utiliza un index spațial.

field=nume:tip(lungime,precizie)

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()

Aspectul (simbologia) straturilor vectoriale

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()

Render cu Simbol Unic

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.

De exemplu, puteți schimba simbolul folosit de un strat particular de tip punct, prin apelarea setSymbol(), transmițându-i o instanță QgsMarkerSymbolV2, ca în următorul exemplu de cod:

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

  • pentagon
  • triunghi

  • triunghi echilateral

  • stea

  • stea_regulată

  • săgeată

  • vârf_de_săgeată_plin

Render cu Simboluri Categorisite

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().

Render cu Simboluri Graduale

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)

Lucrul cu Simboluri

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.

Lucrul cu Straturile Simbolului

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.

Crearea unor Tipuri Personalizate de Straturi pentru Simboluri

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.

Crearea renderelor Personalizate

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).

Lecturi suplimentare

DE EFECTUAT:

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