6. Usando Camadas Vetoriais

Dica

Os trechos de código desta página precisam das seguintes importações se você estiver fora do console do pyqgis:

 1from qgis.core import (
 2  QgsApplication,
 3  QgsDataSourceUri,
 4  QgsCategorizedSymbolRenderer,
 5  QgsClassificationRange,
 6  QgsPointXY,
 7  QgsProject,
 8  QgsExpression,
 9  QgsField,
10  QgsFields,
11  QgsFeature,
12  QgsFeatureRequest,
13  QgsFeatureRenderer,
14  QgsGeometry,
15  QgsGraduatedSymbolRenderer,
16  QgsMarkerSymbol,
17  QgsMessageLog,
18  QgsRectangle,
19  QgsRendererCategory,
20  QgsRendererRange,
21  QgsSymbol,
22  QgsVectorDataProvider,
23  QgsVectorLayer,
24  QgsVectorFileWriter,
25  QgsWkbTypes,
26  QgsSpatialIndex,
27  QgsVectorLayerUtils
28)
29
30from qgis.core.additions.edit import edit
31
32from qgis.PyQt.QtGui import (
33    QColor,
34)

Esta seção lista várias operações que podem ser realizadas com camadas vetoriais.

A maioria dos trabalhos aqui é baseada nos métodos da classe QgsVectorLayer.

6.1. Recuperando informações sobre atributos

Você pode recuperar informações sobre os campos associados a uma camada vetorial chamando fields() em um objeto QgsVectorLayer:

vlayer = QgsVectorLayer("testdata/airports.shp", "airports", "ogr")
for field in vlayer.fields():
    print(field.name(), field.typeName())
1ID Integer64
2fk_region Integer64
3ELEV Real
4NAME String
5USE String

The displayField() and mapTipTemplate() methods of the QgsVectorLayer class provide information on the field and template used in the Display Properties tab.

When you load a vector layer, a field is always chosen by QGIS as the Display Name, while the HTML Map Tip is empty by default. With these methods you can easily get both:

vlayer = QgsVectorLayer("testdata/airports.shp", "airports", "ogr")
print(vlayer.displayField())
NAME

Nota

If you change the Display Name from a field to an expression, you have to use displayExpression() instead of displayField().

6.2. Iterando sobre Camada Vetorial

A iteração sobre as feições em uma camada vetorial é uma das tarefas mais comuns. Abaixo está um exemplo do código básico simples para executar esta tarefa e mostrando algumas informações sobre cada feição. Supõe-se que a variável layer tenha um objeto QgsVectorLayer.

 1# "layer" is a QgsVectorLayer instance
 2layer = iface.activeLayer()
 3features = layer.getFeatures()
 4
 5for feature in features:
 6    # retrieve every feature with its geometry and attributes
 7    print("Feature ID: ", feature.id())
 8    # fetch geometry
 9    # show some information about the feature geometry
10    geom = feature.geometry()
11    geomSingleType = QgsWkbTypes.isSingleType(geom.wkbType())
12    if geom.type() == QgsWkbTypes.PointGeometry:
13        # the geometry type can be of single or multi type
14        if geomSingleType:
15            x = geom.asPoint()
16            print("Point: ", x)
17        else:
18            x = geom.asMultiPoint()
19            print("MultiPoint: ", x)
20    elif geom.type() == QgsWkbTypes.LineGeometry:
21        if geomSingleType:
22            x = geom.asPolyline()
23            print("Line: ", x, "length: ", geom.length())
24        else:
25            x = geom.asMultiPolyline()
26            print("MultiLine: ", x, "length: ", geom.length())
27    elif geom.type() == QgsWkbTypes.PolygonGeometry:
28        if geomSingleType:
29            x = geom.asPolygon()
30            print("Polygon: ", x, "Area: ", geom.area())
31        else:
32            x = geom.asMultiPolygon()
33            print("MultiPolygon: ", x, "Area: ", geom.area())
34    else:
35        print("Unknown or invalid geometry")
36    # fetch attributes
37    attrs = feature.attributes()
38    # attrs is a list. It contains all the attribute values of this feature
39    print(attrs)
40    # for this test only print the first feature
41    break
Feature ID:  1
Point:  <QgsPointXY: POINT(7 45)>
[1, 'First feature']

6.3. Selecionando características

Na área de trabalho do QGIS, as feições podem ser selecionadas de diferentes maneiras: o usuário pode clicar em uma feição, desenhar um retângulo na tela do mapa ou usar um filtro de expressão. As feições selecionadas normalmente são destacadas em uma cor diferente (o padrão é amarelo) para chamar a atenção do usuário na seleção.

Às vezes, pode ser útil selecionar recursos programaticamente ou alterar a cor padrão.

Para selecionar todas as feições, o método selectAll() pode ser usado:

# Get the active layer (must be a vector layer)
layer = iface.activeLayer()
layer.selectAll()

Para selecionar usando uma expressão, use o método selectByExpression():

# Assumes that the active layer is points.shp file from the QGIS test suite
# (Class (string) and Heading (number) are attributes in points.shp)
layer = iface.activeLayer()
layer.selectByExpression('"Class"=\'B52\' and "Heading" > 10 and "Heading" <70', QgsVectorLayer.SetSelection)

Para alterar a cor da seleção, você pode usar o método setSelectionColor() de QgsMapCanvas, como mostrado no exemplo a seguir:

iface.mapCanvas().setSelectionColor( QColor("red") )

Para adicionar recursos à lista de recursos selecionados para uma determinada camada, você pode chamar select() passando para ele a lista de IDs de recursos:

1selected_fid = []
2
3# Get the first feature id from the layer
4feature = next(layer.getFeatures())
5if feature:
6    selected_fid.append(feature.id())
7
8# Add that features to the selected list
9layer.select(selected_fid)

Para limpar a seleção:

layer.removeSelection()

6.3.1. Acessando atributos

Os atributos podem ser referidos pelo seu nome:

print(feature['name'])
First feature

Como alternativa, os atributos podem ser referidos pelo índice. Isso é um pouco mais rápido do que usar o nome. Por exemplo, para obter o segundo atributo:

print(feature[1])
First feature

6.3.2. Iteração sobre os feições selecionadas

Se você precisar apenas de feições selecionadas, poderá usar o método selectedFeatures() da camada vetorial:

selection = layer.selectedFeatures()
for feature in selection:
    # do whatever you need with the feature
    pass

6.3.3. Iterando sobre um subconjunto de feições

Se você deseja iterar sobre um determinado subconjunto de feições em uma camada, como aquelas dentro de uma determinada área, você deve adicionar um objeto QgsFeatureRequest ao getFeatures() chamado. Aqui está um exemplo:

1areaOfInterest = QgsRectangle(450290,400520, 450750,400780)
2
3request = QgsFeatureRequest().setFilterRect(areaOfInterest)
4
5for feature in layer.getFeatures(request):
6    # do whatever you need with the feature
7    pass

Por uma questão de velocidade, a interseção geralmente é feita apenas usando a caixa delimitadora da feição. No entanto, existe um sinalizador ExactIntersect que garante que apenas as feições que se intersectam serão retornadas:

request = QgsFeatureRequest().setFilterRect(areaOfInterest) \
                             .setFlags(QgsFeatureRequest.ExactIntersect)

Com setLimit() você pode limitar o número de feições solicitadas. Aqui está um exemplo:

request = QgsFeatureRequest()
request.setLimit(2)
for feature in layer.getFeatures(request):
    print(feature)
<qgis._core.QgsFeature object at 0x7f9b78590948>
<qgis._core.QgsFeature object at 0x7faef5881670>

Se você precisar de um filtro baseado em atributo em vez (ou adicionalmente) de um filtro espacial, como mostrado nos exemplos acima, poderá criar um objeto QgsExpression e passá-lo para o construtor :classe:`QgsFeatureRequest <qgis.core.QgsFeatureRequest>`. Aqui está um exemplo:

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

Veja expression para obter detalhes sobre a sintaxe suportada por QgsExpression.

A solicitação pode ser usada para definir os dados recuperados para cada feição, portanto, o iterador retorna todos as feições, mas retorna dados parciais para cada uma delas.

 1# Only return selected fields to increase the "speed" of the request
 2request.setSubsetOfAttributes([0,2])
 3
 4# More user friendly version
 5request.setSubsetOfAttributes(['name','id'],layer.fields())
 6
 7# Don't return geometry objects to increase the "speed" of the request
 8request.setFlags(QgsFeatureRequest.NoGeometry)
 9
10# Fetch only the feature with id 45
11request.setFilterFid(45)
12
13# The options may be chained
14request.setFilterRect(areaOfInterest).setFlags(QgsFeatureRequest.NoGeometry).setFilterFid(45).setSubsetOfAttributes([0,2])

6.4. Modificando Camadas Vetoriais

A maioria dos provedores de dados vetoriais suporta a edição de dados da camada. Às vezes, eles suportam apenas um subconjunto de possíveis ações de edição. Use a função capacidades() para descobrir qual conjunto de funcionalidades é suportado.

caps = layer.dataProvider().capabilities()
# Check if a particular capability is supported:
if caps & QgsVectorDataProvider.DeleteFeatures:
    print('The layer supports DeleteFeatures')
The layer supports DeleteFeatures

Para obter uma lista de todos os recursos disponíveis, consulte a API Documentation of QgsVectorDataProvider.

Para imprimir a descrição textual dos recursos da camada em uma lista separada por vírgulas, você pode usar capabilitiesString() como no exemplo a seguir:

1caps_string = layer.dataProvider().capabilitiesString()
2# Print:
3# 'Add Features, Delete Features, Change Attribute Values, Add Attributes,
4# Delete Attributes, Rename Attributes, Fast Access to Features at ID,
5# Presimplify Geometries, Presimplify Geometries with Validity Check,
6# Transactions, Curved Geometries'

Ao usar qualquer um dos métodos a seguir para edição da camada vetorial, as alterações são confirmadas diretamente no armazenamento de dados subjacente (um arquivo, banco de dados etc.). Caso você deseje fazer apenas alterações temporárias, pule para a próxima seção que explica como fazer modifications with editing buffer.

Nota

Se você estiver trabalhando no QGIS (no console ou em um complemento), pode ser necessário forçar um redesenho da tela do mapa para ver as alterações feitas na geometria, no estilo ou nos atributos:

1# If caching is enabled, a simple canvas refresh might not be sufficient
2# to trigger a redraw and you must clear the cached image for the layer
3if iface.mapCanvas().isCachingEnabled():
4    layer.triggerRepaint()
5else:
6    iface.mapCanvas().refresh()

6.4.1. Adicionar feições

Create some QgsFeature instances and pass a list of them to provider’s addFeatures() method. It will return two values: result (True or False) and list of added features (their ID is set by the data store).

Para configurar os atributos da feição, você pode inicializar a feição passando um objeto QgsFields (você pode obtê-lo no método fields() da camada vetorial) ou chame initAttributes() passando o número de campos que você deseja adicionar.

1if caps & QgsVectorDataProvider.AddFeatures:
2    feat = QgsFeature(layer.fields())
3    feat.setAttributes([0, 'hello'])
4    # Or set a single attribute by key or by index:
5    feat.setAttribute('name', 'hello')
6    feat.setAttribute(0, 'hello')
7    feat.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(123, 456)))
8    (res, outFeats) = layer.dataProvider().addFeatures([feat])

6.4.2. Excluir feições

Para excluir algumas feições, basta fornecer uma lista de seus IDs de feições.

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

6.4.3. Modificar Feições

É possível alterar a geometria da feição ou alterar alguns atributos. O exemplo a seguir altera primeiro os valores dos atributos com os índices 0 e 1, depois altera a geometria da feição.

1fid = 100   # ID of the feature we will modify
2
3if caps & QgsVectorDataProvider.ChangeAttributeValues:
4    attrs = { 0 : "hello", 1 : 123 }
5    layer.dataProvider().changeAttributeValues({ fid : attrs })
6
7if caps & QgsVectorDataProvider.ChangeGeometries:
8    geom = QgsGeometry.fromPointXY(QgsPointXY(111,222))
9    layer.dataProvider().changeGeometryValues({ fid : geom })

Dica

Preferindo a classe QgsVectorLayerEditUtils para edições somente de geometria

Se você precisar alterar apenas geometrias, considere usar QgsVectorLayerEditUtils, que fornece alguns métodos úteis para editar geometrias (traduzir, inserir ou mover vértices, etc.).

6.4.4. Modificando Camadas Vetoriais com um Buffer

When editing vectors within QGIS application, you have to first start editing mode for a particular layer, then do some modifications and finally commit (or rollback) the changes. All the changes you make are not written until you commit them — they stay in layer’s in-memory editing buffer. It is possible to use this functionality also programmatically — it is just another method for vector layer editing that complements the direct usage of data providers. Use this option when providing some GUI tools for vector layer editing, since this will allow user to decide whether to commit/rollback and allows the usage of undo/redo. When changes are committed, all changes from the editing buffer are saved to data provider.

Os métodos são semelhantes aos que vimos no provedor, mas são chamados no objeto QgsVectorLayer.

Para que esses métodos funcionem, a camada deve estar no modo de edição. Para iniciar o modo de edição, use o método startEditing(). Para parar a edição, use os métodos commitChanges() ou rollBack(). O primeiro confirmará todas as suas alterações na fonte de dados, enquanto o segundo as descartará e não modificará a fonte de dados.

Para descobrir se uma camada está no modo de edição, use o método isEditable().

Aqui você tem alguns exemplos que demonstram como usar esses métodos de edição.

 1from qgis.PyQt.QtCore import QVariant
 2
 3feat1 = feat2 = QgsFeature(layer.fields())
 4fid = 99
 5feat1.setId(fid)
 6
 7# add two features (QgsFeature instances)
 8layer.addFeatures([feat1,feat2])
 9# delete a feature with specified ID
10layer.deleteFeature(fid)
11
12# set new geometry (QgsGeometry instance) for a feature
13geometry = QgsGeometry.fromWkt("POINT(7 45)")
14layer.changeGeometry(fid, geometry)
15# update an attribute with given field index (int) to a given value
16fieldIndex =1
17value ='My new name'
18layer.changeAttributeValue(fid, fieldIndex, value)
19
20# add new field
21layer.addAttribute(QgsField("mytext", QVariant.String))
22# remove a field
23layer.deleteAttribute(fieldIndex)

Para desfazer/refazer o trabalho corretamente, os chamados acima mencionados têm de ser envoltos em comandos de desfazer. (Se você não se importa com desfazer/refazer e quer ter as mudanças armazenadas imediatamente, então você vai ter o trabalho mais fácil editing with data provider.)

Aqui está como você pode usar a funcionalidade desfazer:

 1layer.beginEditCommand("Feature triangulation")
 2
 3# ... call layer's editing methods ...
 4
 5if problem_occurred:
 6  layer.destroyEditCommand()
 7  # ... tell the user that there was a problem
 8  # and return
 9
10# ... more editing ...
11
12layer.endEditCommand()

O método beginEditCommand() cria um comando interno “ativo” e registra as alterações subsequentes na camada vetorial. Com o chamado para endEditCommand() o comando é enviado para a pilha de desfazer e o usuário poderá desfazê-lo/refazê-lo da GUI. Caso algo dê errado ao fazer as alterações, o método destroyEditCommand() removerá o comando e reverterá todas as alterações feitas enquanto este comando estava ativo.

Você também pode usar o with edit(layer) -statement para agrupar commit e rollback em um bloco de código mais semântico, como mostrado no exemplo abaixo:

with edit(layer):
  feat = next(layer.getFeatures())
  feat[0] = 5
  layer.updateFeature(feat)

Isso chamará automaticamente commitChanges() no final. Se ocorrer alguma exceção, ele irá rollBack() todas as alterações. Caso seja encontrado um problema em commitChanges() (quando o método retornar Falso) a exceção a QgsEditError será gerada.

6.4.5. Adicionando e Removendo Campos

Para adicionar campos (atributos), você precisa especificar uma lista de definições de campos. Para exclusão de campos, forneça apenas uma lista de índices de campos.

1from qgis.PyQt.QtCore import QVariant
2
3if caps & QgsVectorDataProvider.AddAttributes:
4    res = layer.dataProvider().addAttributes(
5        [QgsField("mytext", QVariant.String),
6        QgsField("myint", QVariant.Int)])
7
8if caps & QgsVectorDataProvider.DeleteAttributes:
9    res = layer.dataProvider().deleteAttributes([0])
 1# Alternate methods for removing fields
 2# first create temporary fields to be removed (f1-3)
 3layer.dataProvider().addAttributes([QgsField("f1",QVariant.Int),QgsField("f2",QVariant.Int),QgsField("f3",QVariant.Int)])
 4layer.updateFields()
 5count=layer.fields().count() # count of layer fields
 6ind_list=list((count-3, count-2)) # create list
 7
 8# remove a single field with an index
 9layer.dataProvider().deleteAttributes([count-1])
10
11# remove multiple fields with a list of indices
12layer.dataProvider().deleteAttributes(ind_list)

Após adicionar ou remover campos no provedor de dados, os campos da camada precisam ser atualizados porque as alterações não são propagadas automaticamente.

layer.updateFields()

Dica

Salvando as alterações diretamente usando o comando baseado em **``with`` **

Using with edit(layer): the changes will be committed automatically calling commitChanges() at the end. If any exception occurs, it will rollBack() all the changes. See Modificando Camadas Vetoriais com um Buffer.

6.5. Utilizando Índices Espaciais

Os índices espaciais podem melhorar drasticamente o desempenho do seu código, se você precisar fazer consultas frequentes a uma camada vetorial. Imagine, por exemplo, que você esteja escrevendo um algoritmo de interpolação e que, para um determinado local, precise conhecer os 10 pontos mais próximos de uma camada de pontos, a fim de usá-los para calcular o valor interpolado. Sem um índice espacial, a única maneira de o QGIS encontrar esses 10 pontos é calcular a distância de cada ponto até o local especificado e comparar essas distâncias. Isso pode ser uma tarefa que consome muito tempo, especialmente se precisar ser repetida em vários locais. Se existir um índice espacial para a camada, a operação será muito mais eficaz.

Pense em uma camada sem um índice espacial como uma lista telefônica na qual os números de telefone não são ordenados ou indexados. A única maneira de encontrar o número de telefone de uma determinada pessoa é ler desde o início até encontrá-lo.

Os índices espaciais não são criados por padrão para uma camada vetorial QGIS, mas você pode criá-los facilmente. Isto é o que você precisa fazer:

  • create spatial index using the QgsSpatialIndex class:

    index = QgsSpatialIndex()
    
  • adicione feições ao índice — o indíce pega o objeto QgsFeature e o adiciona à estrutura de dados interna. Você pode criar o objeto manualmente ou usar um de uma chamada anterior para o método getFeatures().

    index.addFeature(feat)
    
  • Como alternativa, você pode carregar todas as feições de uma camada ao mesmo tempo usando o carregamento em massa

    index = QgsSpatialIndex(layer.getFeatures())
    
  • uma vez que o índice espacial é preenchido com alguns valores, você pode fazer algumas consultas

    1# returns array of feature IDs of five nearest features
    2nearest = index.nearestNeighbor(QgsPointXY(25.4, 12.7), 5)
    3
    4# returns array of IDs of features which intersect the rectangle
    5intersect = index.intersects(QgsRectangle(22.5, 15.3, 23.1, 17.2))
    

You can also use the QgsSpatialIndexKDBush spatial index. This index is similar to the standard QgsSpatialIndex but:

  • supports only single point features

  • is static (no additional features can be added to the index after the construction)

  • is much faster!

  • allows direct retrieval of the original feature’s points, without requiring additional feature requests

  • supports true distance based searches, i.e. return all points within a radius from a search point

6.6. The QgsVectorLayerUtils class

The QgsVectorLayerUtils class contains some very useful methods that you can use with vector layers.

For example the createFeature() method prepares a QgsFeature to be added to a vector layer keeping all the eventual constraints and default values of each field:

vlayer = QgsVectorLayer("testdata/airports.shp", "airports", "ogr")
feat = QgsVectorLayerUtils.createFeature(vlayer)

The getValues() method allows you to quickly get the values of a field or expression:

1vlayer = QgsVectorLayer("testdata/airports.shp", "airports", "ogr")
2# select only the first feature to make the output shorter
3vlayer.selectByIds([1])
4val = QgsVectorLayerUtils.getValues(vlayer, "NAME", selectedOnly=True)
5print(val)
(['AMBLER'], True)

6.7. Criando Camadas Vetoriais

Existem várias maneiras de gerar um conjunto de dados de camada vetorial:

  • the QgsVectorFileWriter class: A convenient class for writing vector files to disk, using either a static call to writeAsVectorFormatV3() which saves the whole vector layer or creating an instance of the class and issue calls to addFeature(). This class supports all the vector formats that OGR supports (GeoPackage, Shapefile, GeoJSON, KML and others).

  • a classe QgsVectorLayer: instancia um provedor de dados que interpreta o caminho fornecido (url) da fonte de dados para conectar e acessar os dados. Ele pode ser usado para criar camadas temporárias baseadas em memória (memory) e conectar-se a conjuntos de dados OGR (ogr), bancos de dados (postgres, spatialite, mysql, mssql) e mais (wfs, gpx, delimitedtext…).

6.7.1. De uma instância de QgsVectorFileWriter

 1# SaveVectorOptions contains many settings for the writer process
 2save_options = QgsVectorFileWriter.SaveVectorOptions()
 3transform_context = QgsProject.instance().transformContext()
 4# Write to a GeoPackage (default)
 5error = QgsVectorFileWriter.writeAsVectorFormatV3(layer,
 6                                                  "testdata/my_new_file.gpkg",
 7                                                  transform_context,
 8                                                  save_options)
 9if error[0] == QgsVectorFileWriter.NoError:
10    print("success!")
11else:
12  print(error)
 1# Write to an ESRI Shapefile format dataset using UTF-8 text encoding
 2save_options = QgsVectorFileWriter.SaveVectorOptions()
 3save_options.driverName = "ESRI Shapefile"
 4save_options.fileEncoding = "UTF-8"
 5transform_context = QgsProject.instance().transformContext()
 6error = QgsVectorFileWriter.writeAsVectorFormatV3(layer,
 7                                                  "testdata/my_new_shapefile",
 8                                                  transform_context,
 9                                                  save_options)
10if error[0] == QgsVectorFileWriter.NoError:
11    print("success again!")
12else:
13  print(error)
 1# Write to an ESRI GDB file
 2save_options = QgsVectorFileWriter.SaveVectorOptions()
 3save_options.driverName = "FileGDB"
 4# if no geometry
 5save_options.overrideGeometryType = QgsWkbTypes.Unknown
 6save_options.actionOnExistingFile = QgsVectorFileWriter.CreateOrOverwriteLayer
 7save_options.layerName = 'my_new_layer_name'
 8transform_context = QgsProject.instance().transformContext()
 9gdb_path = "testdata/my_example.gdb"
10error = QgsVectorFileWriter.writeAsVectorFormatV3(layer,
11                                                gdb_path,
12                                                transform_context,
13                                                save_options)
14if error[0] == QgsVectorFileWriter.NoError:
15  print("success!")
16else:
17  print(error)

Você também pode converter campos para torná-los compatíveis com diferentes formatos usando FieldValueConverter. Por exemplo, para converter tipos de variáveis ​​de matriz (por exemplo, em Postgres) em um tipo de texto, você pode fazer o seguinte:

 1LIST_FIELD_NAME = 'xxxx'
 2
 3class ESRIValueConverter(QgsVectorFileWriter.FieldValueConverter):
 4
 5  def __init__(self, layer, list_field):
 6    QgsVectorFileWriter.FieldValueConverter.__init__(self)
 7    self.layer = layer
 8    self.list_field_idx = self.layer.fields().indexFromName(list_field)
 9
10  def convert(self, fieldIdxInLayer, value):
11    if fieldIdxInLayer == self.list_field_idx:
12      return QgsListFieldFormatter().representValue(layer=vlayer,
13                                                    fieldIndex=self.list_field_idx,
14                                                    config={},
15                                                    cache=None,
16                                                    value=value)
17    else:
18      return value
19
20  def fieldDefinition(self, field):
21    idx = self.layer.fields().indexFromName(field.name())
22    if idx == self.list_field_idx:
23      return QgsField(LIST_FIELD_NAME, QVariant.String)
24    else:
25      return self.layer.fields()[idx]
26
27converter = ESRIValueConverter(vlayer, LIST_FIELD_NAME)
28opts = QgsVectorFileWriter.SaveVectorOptions()
29opts.fieldValueConverter = converter

Um SRC de destino também pode ser especificado — se uma instância válida de QgsCoordinateReferenceSystem for passada como o quarto parâmetro, a camada será transformada nesse SRC.

For valid driver names please call the supportedFiltersAndFormats() method or consult the supported formats by OGR — you should pass the value in the “Code” column as the driver name.

Opcionalmente, você pode definir se deseja exportar apenas os recursos selecionados, passar outras opções específicas do driver para criação ou dizer ao gravador para não criar atributos… Há vários outros parâmetros (opcionais); veja a documentação de QgsVectorFileWriter para obter detalhes.

6.7.2. Diretamente das feições

 1from qgis.PyQt.QtCore import QVariant
 2
 3# define fields for feature attributes. A QgsFields object is needed
 4fields = QgsFields()
 5fields.append(QgsField("first", QVariant.Int))
 6fields.append(QgsField("second", QVariant.String))
 7
 8""" create an instance of vector file writer, which will create the vector file.
 9Arguments:
101. path to new file (will fail if exists already)
112. field map
123. geometry type - from WKBTYPE enum
134. layer's spatial reference (instance of
14   QgsCoordinateReferenceSystem)
155. coordinate transform context
166. save options (driver name for the output file, encoding etc.)
17"""
18
19crs = QgsProject.instance().crs()
20transform_context = QgsProject.instance().transformContext()
21save_options = QgsVectorFileWriter.SaveVectorOptions()
22save_options.driverName = "ESRI Shapefile"
23save_options.fileEncoding = "UTF-8"
24
25writer = QgsVectorFileWriter.create(
26  "testdata/my_new_shapefile.shp",
27  fields,
28  QgsWkbTypes.Point,
29  crs,
30  transform_context,
31  save_options
32)
33
34if writer.hasError() != QgsVectorFileWriter.NoError:
35    print("Error when creating shapefile: ",  writer.errorMessage())
36
37# add a feature
38fet = QgsFeature()
39
40fet.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(10,10)))
41fet.setAttributes([1, "text"])
42writer.addFeature(fet)
43
44# delete the writer to flush features to disk
45del writer

6.7.3. De uma instância de QgsVectorLayer

Entre todos os provedores de dados suportados pela classe QgsVectorLayer, vamos nos concentrar nas camadas baseadas em memória. O provedor de memória deve ser usado principalmente por desenvolvedores de complementos ou de 3os. Ele não armazena dados no disco, permitindo que os desenvolvedores os usem como um backend rápido para algumas camadas temporárias.

O provedor suporta campos string, int e double.

O provedor de memória também suporta a indexação espacial, que é ativada chamando a função createSpatialIndex(). Depois que o índice espacial for criado, você poderá iterar as feições em regiões menores mais rapidamente (já que não é necessário percorrer todos as feições, apenas aquelas no retângulo especificado).

Um provedor de memória é criado passando "memory" como a string do provedor para o construtor QgsVectorLayer.

O construtor também usa um URI que define o tipo de geometria da camada, um dos seguintes: "Point", "LineString", "Polygon", "MultiPoint", "MultiLineString",``”MultiPolygon”`` ou "None".

O URI também pode especificar o sistema de referência de coordenadas, campos e indexação do provedor de memória no URI. A sintaxe é:

crs=definição

Specifies the coordinate reference system, where definition may be any of the forms accepted by QgsCoordinateReferenceSystem.createFromString()

index=yes

Especifica que o provedor irá usar o index espacial

field=name:type(length,precision)

Especifica um atributo da camada. O atributo tem um nome e, opcionalmente, um tipo (número inteiro, duplo ou sequência), comprimento e precisão. Pode haver múltiplas definições de campo.

O exemplo seguinte de URL incorpora todas estas opções

"Point?crs=epsg:4326&field=id:integer&field=name:string(20)&index=yes"

O código de exemplo a seguir ilustra a criação e o preenchimento de um provedor de memória

 1from qgis.PyQt.QtCore import QVariant
 2
 3# create layer
 4vl = QgsVectorLayer("Point", "temporary_points", "memory")
 5pr = vl.dataProvider()
 6
 7# add fields
 8pr.addAttributes([QgsField("name", QVariant.String),
 9                    QgsField("age",  QVariant.Int),
10                    QgsField("size", QVariant.Double)])
11vl.updateFields() # tell the vector layer to fetch changes from the provider
12
13# add a feature
14fet = QgsFeature()
15fet.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(10,10)))
16fet.setAttributes(["Johny", 2, 0.3])
17pr.addFeatures([fet])
18
19# update layer's extent when new features have been added
20# because change of extent in provider is not propagated to the layer
21vl.updateExtents()

Finalmente, vamos verificar se tudo correu bem

 1# show some stats
 2print("fields:", len(pr.fields()))
 3print("features:", pr.featureCount())
 4e = vl.extent()
 5print("extent:", e.xMinimum(), e.yMinimum(), e.xMaximum(), e.yMaximum())
 6
 7# iterate over features
 8features = vl.getFeatures()
 9for fet in features:
10    print("F:", fet.id(), fet.attributes(), fet.geometry().asPoint())
fields: 3
features: 1
extent: 10.0 10.0 10.0 10.0
F: 1 ['Johny', 2, 0.3] <QgsPointXY: POINT(10 10)>

6.8. Aparencia (Simbologia) de Camadas de Vetor

Quando uma camada vetorial está sendo renderizada, a aparência dos dados é dada pelos renderizador e símbolos associados à camada. Símbolos são classes que cuidam do desenho da representação visual de recursos, enquanto os renderizadores determinam qual símbolo será usado para uma feição específica.

O renderizador para uma determinada camada pode ser obtido como mostrado abaixo:

renderer = layer.renderer()

E com essa referência, vamos explorar um pouco

print("Type:", renderer.type())
Type: singleSymbol

Existem vários tipos conhecidos de renderizadores disponíveis na biblioteca principal do QGIS:

Tipo

Classes

Descrição

singleSymbol

QgsSingleSymbolRenderer

Renderiza todas as características com o mesmo símbolo

categorizedSymbol

QgsCategorizedSymbolRenderer

Renderiza características usando um símbolo diferente para cada categoria

graduatedSymbol

QgsGraduatedSymbolRenderer

Renderiza caracter´sticas usando diferents símbolos para cada limite de valores

Também pode haver alguns tipos de renderizador personalizados, portanto, nunca assuma que existem apenas esses tipos. Você pode consultar QgsRendererRegistry do aplicativo para descobrir os renderizadores disponíveis no momento:

print(QgsApplication.rendererRegistry().renderersList())
['nullSymbol', 'singleSymbol', 'categorizedSymbol', 'graduatedSymbol', 'RuleRenderer', 'pointDisplacement', 'pointCluster', 'mergedFeatureRenderer', 'invertedPolygonRenderer', 'heatmapRenderer', '25dRenderer', 'embeddedSymbol']

É possível obter um dump do conteúdo do renderizador em forma de texto - pode ser útil para depuração

renderer.dump()
SINGLE: MARKER SYMBOL (1 layers) color 190,207,80,255

6.8.1. Renderizador de símbolo único

Você pode obter o símbolo usado para renderização chamando o método meth:symbol() <qgis.core.QgsSingleSymbolRenderer.symbol> e alterá-lo com o método setSymbol() (observação para desenvolvedores do C++: o renderizador assume a propriedade do símbolo.)

Você pode alterar o símbolo usado por uma camada vetorial específica chamando setSymbol() passando uma instância da instância de símbolo apropriada. Símbolos para as camadas de ponto, linha e polígono podem ser criados chamando a função createSimple() das classes correspondentes QgsMarkerSymbol, QgsLineSymbol e QgsFillSymbol.

O dicionário passado para createSimple() define as propriedades de estilo do símbolo.

Por exemplo, você pode substituir o símbolo usado por uma determinada camada de ponto chamando setSymbol() passando uma instância de QgsMarkerSymbol, como no seguinte exemplo de código:

symbol = QgsMarkerSymbol.createSimple({'name': 'square', 'color': 'red'})
layer.renderer().setSymbol(symbol)
# show the change
layer.triggerRepaint()

name indica a forma do marcador e pode ser um dos seguintes:

  • circle

  • square

  • cross

  • rectangle

  • diamond

  • pentagon

  • triangle

  • equilateral_triangle

  • star

  • regular_star

  • arrow

  • filled_arrowhead

  • x

Para obter a lista completa de propriedades da primeira camada de símbolo de uma instância de símbolo, você pode seguir o código de exemplo:

print(layer.renderer().symbol().symbolLayers()[0].properties())
{'angle': '0', 'cap_style': 'square', 'color': '255,0,0,255', 'horizontal_anchor_point': '1', 'joinstyle': 'bevel', 'name': 'square', 'offset': '0,0', 'offset_map_unit_scale': '3x:0,0,0,0,0,0', 'offset_unit': 'MM', 'outline_color': '35,35,35,255', 'outline_style': 'solid', 'outline_width': '0', 'outline_width_map_unit_scale': '3x:0,0,0,0,0,0', 'outline_width_unit': 'MM', 'scale_method': 'diameter', 'size': '2', 'size_map_unit_scale': '3x:0,0,0,0,0,0', 'size_unit': 'MM', 'vertical_anchor_point': '1'}

Isso pode ser útil se você quiser alterar algumas propriedades:

 1# You can alter a single property...
 2layer.renderer().symbol().symbolLayer(0).setSize(3)
 3# ... but not all properties are accessible from methods,
 4# you can also replace the symbol completely:
 5props = layer.renderer().symbol().symbolLayer(0).properties()
 6props['color'] = 'yellow'
 7props['name'] = 'square'
 8layer.renderer().setSymbol(QgsMarkerSymbol.createSimple(props))
 9# show the changes
10layer.triggerRepaint()

6.8.2. Renderizador de Símbolo Categorizado

Ao usar um renderizador categorizado, é possível consultar e definir o atributo usado para classificação usando classAttribute() e setClassAttribute().

Para obter uma lista de categorias

1categorized_renderer = QgsCategorizedSymbolRenderer()
2# Add a few categories
3cat1 = QgsRendererCategory('1', QgsMarkerSymbol(), 'category 1')
4cat2 = QgsRendererCategory('2', QgsMarkerSymbol(), 'category 2')
5categorized_renderer.addCategory(cat1)
6categorized_renderer.addCategory(cat2)
7
8for cat in categorized_renderer.categories():
9    print("{}: {} :: {}".format(cat.value(), cat.label(), cat.symbol()))
1: category 1 :: <qgis._core.QgsMarkerSymbol object at 0x7f378ffcd9d8>
2: category 2 :: <qgis._core.QgsMarkerSymbol object at 0x7f378ffcd9d8>

Em que value() é o valor usado para discriminação entre categorias, label() é um texto usado para a descrição da categoria e symbol() retorna o símbolo atribuído.

Geralmente, o renderizador também armazena símbolo e rampa de cores originais que foram usados ​​para a classificação: métodos sourceColorRamp() e sourceSymbol().

6.8.3. Renderizador de Símbolo Graduado

Esse renderizador é muito semelhante ao renderizador de símbolo categorizado descrito acima, mas, em vez de um valor de atributo por classe, ele trabalha com intervalos de valores e, portanto, pode ser usado apenas com atributos numéricos.

Para saber mais sobre os intervalos usados ​​no renderizador

 1graduated_renderer = QgsGraduatedSymbolRenderer()
 2# Add a few categories
 3graduated_renderer.addClassRange(QgsRendererRange(QgsClassificationRange('class 0-100', 0, 100), QgsMarkerSymbol()))
 4graduated_renderer.addClassRange(QgsRendererRange(QgsClassificationRange('class 101-200', 101, 200), QgsMarkerSymbol()))
 5
 6for ran in graduated_renderer.ranges():
 7    print("{} - {}: {} {}".format(
 8        ran.lowerValue(),
 9        ran.upperValue(),
10        ran.label(),
11        ran.symbol()
12      ))
0.0 - 100.0: class 0-100 <qgis._core.QgsMarkerSymbol object at 0x7f8bad281b88>
101.0 - 200.0: class 101-200 <qgis._core.QgsMarkerSymbol object at 0x7f8bad281b88>

you can again use the classAttribute() (to find the classification attribute name), sourceSymbol() and sourceColorRamp() methods. Additionally there is the mode() method which determines how the ranges were created: using equal intervals, quantiles or some other method.

Se você deseja criar seu próprio renderizador de símbolo graduado, pode fazê-lo como ilustrado no exemplo de trecho abaixo (que cria um arranjo simples de duas classes)

 1from qgis.PyQt import QtGui
 2
 3myVectorLayer = QgsVectorLayer("testdata/airports.shp", "airports", "ogr")
 4myTargetField = 'ELEV'
 5myRangeList = []
 6myOpacity = 1
 7# Make our first symbol and range...
 8myMin = 0.0
 9myMax = 50.0
10myLabel = 'Group 1'
11myColour = QtGui.QColor('#ffee00')
12mySymbol1 = QgsSymbol.defaultSymbol(myVectorLayer.geometryType())
13mySymbol1.setColor(myColour)
14mySymbol1.setOpacity(myOpacity)
15myRange1 = QgsRendererRange(myMin, myMax, mySymbol1, myLabel)
16myRangeList.append(myRange1)
17#now make another symbol and range...
18myMin = 50.1
19myMax = 100
20myLabel = 'Group 2'
21myColour = QtGui.QColor('#00eeff')
22mySymbol2 = QgsSymbol.defaultSymbol(
23     myVectorLayer.geometryType())
24mySymbol2.setColor(myColour)
25mySymbol2.setOpacity(myOpacity)
26myRange2 = QgsRendererRange(myMin, myMax, mySymbol2, myLabel)
27myRangeList.append(myRange2)
28myRenderer = QgsGraduatedSymbolRenderer('', myRangeList)
29myClassificationMethod = QgsApplication.classificationMethodRegistry().method("EqualInterval")
30myRenderer.setClassificationMethod(myClassificationMethod)
31myRenderer.setClassAttribute(myTargetField)
32
33myVectorLayer.setRenderer(myRenderer)

6.8.4. Trabalhando com Símbolos

Para representação de símbolos, existe a classe base QgsSymbol com três classes derivadas:

Todo símbolo consiste em uma ou mais camadas de símbolos (classes derivadas de QgsSymbolLayer). As camadas de símbolo fazem a renderização real, a própria classe de símbolo serve apenas como um contêiner para as camadas de símbolo.

Having an instance of a symbol (e.g. from a renderer), it is possible to explore it: the 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:

marker_symbol = QgsMarkerSymbol()
for i in range(marker_symbol.symbolLayerCount()):
    lyr = marker_symbol.symbolLayer(i)
    print("{}: {}".format(i, lyr.layerType()))
0: SimpleMarker

To find out symbol’s color use color() method and setColor() to change its color. With marker symbols additionally you can query for the symbol size and rotation with the size() and angle() methods. For line symbols the width() method returns the line width.

Por padrão, tamanho e largura são em milimetros e ângulos em graus.

6.8.4.1. Trabalhando com Camadas de Símbolos

Como dito anteriormente, as camadas de símbolos (subclasses de QgsSymbolLayer) determinam a aparência das feições. Existem várias classes básicas de camadas de símbolos para uso geral. É possível implementar novos tipos de camadas de símbolos e, assim, personalizar arbitrariamente como as feiçõe serão renderizadas. O método layerType() identifica exclusivamente a classe da camada de símbolo — os básicos e o padrão são SimpleMarker, SimpleLine e SimpleFill tipos de camadas de símbolos.

Você pode obter uma lista completa dos tipos de camadas de símbolos que pode criar para uma determinada classe de camadas de símbolos com o seguinte código:

1from qgis.core import QgsSymbolLayerRegistry
2myRegistry = QgsApplication.symbolLayerRegistry()
3myMetadata = myRegistry.symbolLayerMetadata("SimpleFill")
4for item in myRegistry.symbolLayersForType(QgsSymbol.Marker):
5    print(item)
1EllipseMarker
2FilledMarker
3FontMarker
4GeometryGenerator
5MaskMarker
6RasterMarker
7SimpleMarker
8SvgMarker
9VectorField

A classe QgsSymbolLayerRegistry gerencia um banco de dados de todos os tipos de camada de símbolo disponíveis.

To access symbol layer data, use its properties() method that returns a key-value dictionary of properties which determine the appearance. Each symbol layer type has a specific set of properties that it uses. Additionally, there are the generic methods color(), size(), angle() and width(), with their setter counterparts. Of course size and angle are available only for marker symbol layers and width for line symbol layers.

6.8.4.2. Criando Tipos de Camadas de Símbolos Personalizadas

Imagine que você gostaria de personalizar a maneira como os dados são renderizados. Você pode criar sua própria classe de camada de símbolo que desenhará as feições exatamente como você deseja. Aqui está um exemplo de um marcador que desenha círculos vermelhos com raio especificado

 1from qgis.core import QgsMarkerSymbolLayer
 2from qgis.PyQt.QtGui import QColor
 3
 4class FooSymbolLayer(QgsMarkerSymbolLayer):
 5
 6  def __init__(self, radius=4.0):
 7      QgsMarkerSymbolLayer.__init__(self)
 8      self.radius = radius
 9      self.color = QColor(255,0,0)
10
11  def layerType(self):
12     return "FooMarker"
13
14  def properties(self):
15      return { "radius" : str(self.radius) }
16
17  def startRender(self, context):
18    pass
19
20  def stopRender(self, context):
21      pass
22
23  def renderPoint(self, point, context):
24      # Rendering depends on whether the symbol is selected (QGIS >= 1.5)
25      color = context.selectionColor() if context.selected() else self.color
26      p = context.renderContext().painter()
27      p.setPen(color)
28      p.drawEllipse(point, self.radius, self.radius)
29
30  def clone(self):
31      return FooSymbolLayer(self.radius)

The layerType() method determines the name of the symbol layer; it has to be unique among all symbol layers. The properties() method is used for persistence of attributes. The clone() method must return a copy of the symbol layer with all attributes being exactly the same. Finally there are rendering methods: startRender() is called before rendering the first feature, stopRender() when the rendering is done, and renderPoint() is called to do the rendering. The coordinates of the point(s) are already transformed to the output coordinates.

For polylines and polygons the only difference would be in the rendering method: you would use renderPolyline() which receives a list of lines, while renderPolygon() receives a list of points on the outer ring as the first parameter and a list of inner rings (or None) as a second parameter.

Geralmente, é conveniente adicionar uma GUI para definir atributos do tipo de camada de símbolo para permitir que os usuários personalizem a aparência: no caso do nosso exemplo acima, podemos permitir que o usuário defina o raio do círculo. O código a seguir implementa esse widget

 1from qgis.gui import QgsSymbolLayerWidget
 2
 3class FooSymbolLayerWidget(QgsSymbolLayerWidget):
 4    def __init__(self, parent=None):
 5        QgsSymbolLayerWidget.__init__(self, parent)
 6
 7        self.layer = None
 8
 9        # setup a simple UI
10        self.label = QLabel("Radius:")
11        self.spinRadius = QDoubleSpinBox()
12        self.hbox = QHBoxLayout()
13        self.hbox.addWidget(self.label)
14        self.hbox.addWidget(self.spinRadius)
15        self.setLayout(self.hbox)
16        self.connect(self.spinRadius, SIGNAL("valueChanged(double)"), \
17            self.radiusChanged)
18
19    def setSymbolLayer(self, layer):
20        if layer.layerType() != "FooMarker":
21            return
22        self.layer = layer
23        self.spinRadius.setValue(layer.radius)
24
25    def symbolLayer(self):
26        return self.layer
27
28    def radiusChanged(self, value):
29        self.layer.radius = value
30        self.emit(SIGNAL("changed()"))

This widget can be embedded into the symbol properties dialog. When the symbol layer type is selected in symbol properties dialog, it creates an instance of the symbol layer and an instance of the symbol layer widget. Then it calls the setSymbolLayer() method to assign the symbol layer to the widget. In that method the widget should update the UI to reflect the attributes of the symbol layer. The symbolLayer() method is used to retrieve the symbol layer again by the properties dialog to use it for the symbol.

Em cada alteração de atributos, o widget deve emitir o sinal changed() para permitir que o diálogo de propriedades atualize a visualização do símbolo.

Agora falta apenas a cola final: fazer o QGIS entender essas novas classes. Isso é feito adicionando a camada de símbolo ao registro. É possível usar a camada de símbolo também sem adicioná-la ao registro, mas algumas funcionalidades não funcionarão: por exemplo carregamento de arquivos de projeto com as camadas de símbolos personalizados ou incapacidade de editar os atributos da camada na GUI.

Você terá que criar metadados para a camada de símbolos

 1from qgis.core import QgsSymbol, QgsSymbolLayerAbstractMetadata, QgsSymbolLayerRegistry
 2
 3class FooSymbolLayerMetadata(QgsSymbolLayerAbstractMetadata):
 4
 5  def __init__(self):
 6    super().__init__("FooMarker", "My new Foo marker", QgsSymbol.Marker)
 7
 8  def createSymbolLayer(self, props):
 9    radius = float(props["radius"]) if "radius" in props else 4.0
10    return FooSymbolLayer(radius)
11
12fslmetadata = FooSymbolLayerMetadata()
QgsApplication.symbolLayerRegistry().addSymbolLayerType(fslmetadata)

Você deve passar o tipo de camada (igual ao retornado pela camada) e o tipo de símbolo (marcador/linha/preenchimento) para o construtor da classe pai. O método createSymbolLayer() cuida da criação de uma instância da camada de símbolos com atributos especificados no dicionário props. E existe o método createSymbolLayerWidget() que retorna o widget de configurações para esse tipo de camada de símbolo.

O último passo para adicionar este símbolo de camada para o registro — e estamos prontos.

6.8.5. Criando Renderizadores Personalizados

Pode ser útil criar uma nova implementação de renderizador se você desejar personalizar as regras de como selecionar símbolos para renderização de recursos. Alguns casos de uso em que você deseja fazer isso: o símbolo é determinado a partir de uma combinação de campos, o tamanho dos símbolos muda dependendo da escala atual etc.

O código a seguir mostra um renderizador personalizado simples que cria dois símbolos de marcador e escolhe aleatoriamente um deles para cada feição

 1import random
 2from qgis.core import QgsWkbTypes, QgsSymbol, QgsFeatureRenderer
 3
 4
 5class RandomRenderer(QgsFeatureRenderer):
 6  def __init__(self, syms=None):
 7    super().__init__("RandomRenderer")
 8    self.syms = syms if syms else [
 9      QgsSymbol.defaultSymbol(QgsWkbTypes.geometryType(QgsWkbTypes.Point)),
10      QgsSymbol.defaultSymbol(QgsWkbTypes.geometryType(QgsWkbTypes.Point))
11    ]
12
13  def symbolForFeature(self, feature, context):
14    return random.choice(self.syms)
15
16  def startRender(self, context, fields):
17    super().startRender(context, fields)
18    for s in self.syms:
19      s.startRender(context, fields)
20
21  def stopRender(self, context):
22    super().stopRender(context)
23    for s in self.syms:
24      s.stopRender(context)
25
26  def usedAttributes(self, context):
27    return []
28
29  def clone(self):
30    return RandomRenderer(self.syms)

The constructor of the parent QgsFeatureRenderer class needs a renderer name (which has to be unique among renderers). The symbolForFeature() method is the one that decides what symbol will be used for a particular feature. startRender() and stopRender() take care of initialization/finalization of symbol rendering. The usedAttributes() method can return a list of field names that the renderer expects to be present. Finally, the clone() method should return a copy of the renderer.

Como nas camadas de símbolos, é possível anexar uma GUI para a configuração do renderizador. Ele deve ser derivado de QgsRendererWidget. O código de amostra a seguir cria um botão que permite ao usuário definir o primeiro símbolo

 1from qgis.gui import QgsRendererWidget, QgsColorButton
 2
 3
 4class RandomRendererWidget(QgsRendererWidget):
 5  def __init__(self, layer, style, renderer):
 6    super().__init__(layer, style)
 7    if renderer is None or renderer.type() != "RandomRenderer":
 8      self.r = RandomRenderer()
 9    else:
10      self.r = renderer
11    # setup UI
12    self.btn1 = QgsColorButton()
13    self.btn1.setColor(self.r.syms[0].color())
14    self.vbox = QVBoxLayout()
15    self.vbox.addWidget(self.btn1)
16    self.setLayout(self.vbox)
17    self.btn1.colorChanged.connect(self.setColor1)
18
19  def setColor1(self):
20    color = self.btn1.color()
21    if not color.isValid(): return
22    self.r.syms[0].setColor(color)
23
24  def renderer(self):
25    return self.r

The constructor receives instances of the active layer (QgsVectorLayer), the global style (QgsStyle) and the current renderer. If there is no renderer or the renderer has different type, it will be replaced with our new renderer, otherwise we will use the current renderer (which has already the type we need). The widget contents should be updated to show current state of the renderer. When the renderer dialog is accepted, the widget’s renderer() method is called to get the current renderer — it will be assigned to the layer.

A última parte ausente são os metadados e a inclusão do renderizador no registro, caso contrário, o carregamento de camadas com o renderizador não funcionará e o usuário não poderá selecioná-lo na lista de renderizadores. Vamos terminar o nosso exemplo RandomRenderer

 1from qgis.core import (
 2  QgsRendererAbstractMetadata,
 3  QgsRendererRegistry,
 4  QgsApplication
 5)
 6
 7class RandomRendererMetadata(QgsRendererAbstractMetadata):
 8
 9  def __init__(self):
10    super().__init__("RandomRenderer", "Random renderer")
11
12  def createRenderer(self, element):
13    return RandomRenderer()
14
15  def createRendererWidget(self, layer, style, renderer):
16    return RandomRendererWidget(layer, style, renderer)
17
18rrmetadata = RandomRendererMetadata()
QgsApplication.rendererRegistry().addRenderer(rrmetadata)

Similarly as with symbol layers, abstract metadata constructor awaits renderer name, name visible for users and optionally name of renderer’s icon. The createRenderer() method passes a QDomElement instance that can be used to restore the renderer’s state from the DOM tree. The createRendererWidget() method creates the configuration widget. It does not have to be present or can return None if the renderer does not come with GUI.

Para associar um ícone ao renderizador, você pode atribuí-lo no construtor QgsRendererAbstractMetadata como um terceiro argumento (opcional) — o construtor da classe base na função RandomRendererMetadata __init__`() se torna

QgsRendererAbstractMetadata.__init__(self,
       "RandomRenderer",
       "Random renderer",
       QIcon(QPixmap("RandomRendererIcon.png", "png")))

The icon can also be associated at any later time using the setIcon() method of the metadata class. The icon can be loaded from a file (as shown above) or can be loaded from a Qt resource (PyQt5 includes .qrc compiler for Python).

6.9. Outros Tópicos

TODO:

  • criando/modificando símbolos

  • trabalhando com estilo (QgsStyle)

  • trabalhando com rampa de cores (QgsColorRamp)

  • explorando a camada de símbolos e os registros do renderizador