Los fragmentos de código en esta página necesitan las siguientes adiciones si está fuera de la consola de pyqgis:

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

from qgis.core.additions.edit import edit

from qgis.PyQt.QtGui import (
    QColor,
)

6. Usar capas vectoriales

Esta sección sumariza varias acciones que pueden ser realizadas con las capas vectoriales

La mayor parte del trabajo acá expuesto está basado en los métodos de la clase QgsVectorLayer.

6.1. Recuperando información sobre atributos

Puede recuperar información sobre los campos asociados a una capa vectorial llamando el método fields()  de un objeto de la clase QgsVectorLayer

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

6.2. Iterando sobre la capa vectorial

a iteración de las entidades de una capa vectorial es una de las tareas más comunes. A continuación se muestra un ejemplo del código básico simple para realizar esta tarea y mostrar cierta información sobre cada característica. Se supone que la variable “”layer”” tiene un objeto QgsVectorLayer.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# "layer" is a QgsVectorLayer instance
layer = iface.activeLayer()
features = layer.getFeatures()

for feature in features:
    # retrieve every feature with its geometry and attributes
    print("Feature ID: ", feature.id())
    # fetch geometry
    # show some information about the feature geometry
    geom = feature.geometry()
    geomSingleType = QgsWkbTypes.isSingleType(geom.wkbType())
    if geom.type() == QgsWkbTypes.PointGeometry:
        # the geometry type can be of single or multi type
        if geomSingleType:
            x = geom.asPoint()
            print("Point: ", x)
        else:
            x = geom.asMultiPoint()
            print("MultiPoint: ", x)
    elif geom.type() == QgsWkbTypes.LineGeometry:
        if geomSingleType:
            x = geom.asPolyline()
            print("Line: ", x, "length: ", geom.length())
        else:
            x = geom.asMultiPolyline()
            print("MultiLine: ", x, "length: ", geom.length())
    elif geom.type() == QgsWkbTypes.PolygonGeometry:
        if geomSingleType:
            x = geom.asPolygon()
            print("Polygon: ", x, "Area: ", geom.area())
        else:
            x = geom.asMultiPolygon()
            print("MultiPolygon: ", x, "Area: ", geom.area())
    else:
        print("Unknown or invalid geometry")
    # fetch attributes
    attrs = feature.attributes()
    # attrs is a list. It contains all the attribute values of this feature
    print(attrs)
    # for this test only print the first feature
    break
Feature ID:  1
Point:  <QgsPointXY: POINT(7 45)>
[1, 'First feature']

6.3. Seleccionando objetos espaciales

En el escritorio QGIS, las entidades se pueden seleccionar de diferentes maneras: el usuario puede hacer clic en una entidad, dibujar un rectángulo en el lienzo del mapa o utilizar un filtro de expresión. Las entidades seleccionadas normalmente se resaltan en un color diferente (el valor predeterminado es el amarillo) para llamar la atención del usuario sobre la selección.

A veces puede ser útil seleccionar características mediante programación o cambiar el color predeterminado.

Para seleccionar todas las características, se puede utilizar el método selectAll()

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

Para seleccionar usando una expresión, utilice el 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 cambiar el color de selección puede utilizar el método setSelectionColor() de QgsMapCanvas como se muestra en el ejemplo siguiente:

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

Para agregar entidades a la lista de entidades seleccionada para una capa determinada, puede llamar a select() pasándole la lista de identificadores de las entidades:

1
2
3
4
5
6
7
8
9
selected_fid = []

# Get the first feature id from the layer
for feature in layer.getFeatures():
    selected_fid.append(feature.id())
    break

# Add these features to the selected list
layer.select(selected_fid)

Para borrar la selección:

layer.removeSelection()

6.3.1. Accediendo a atributos

Los atributos pueden ser referidos por su nombre:

print(feature['name'])
First feature

Alternativamente, se puede hacer referencia a los atributos por índice. Esto es un poco más rápido que usar el nombre. Por ejemplo, para obtener el segundo atributo:

print(feature[1])
First feature

6.3.2. Iterando sobre rasgos seleccionados

Si solo necesita entidades seleccionadas, puede utilizar el método selectedFeatures() de la capa vectorial:

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

6.3.3. Iterando sobre un subconjunto de rasgos

Si desea iterar sobre un subconjunto determinado de entidades de una capa, como las que se encuentran en un área determinada, debe agregar un objeto QgsFeatureRequest a la llamada de getFeatures(). Este es un ejemplo:

1
2
3
4
5
6
7
areaOfInterest = QgsRectangle(450290,400520, 450750,400780)

request = QgsFeatureRequest().setFilterRect(areaOfInterest)

for feature in layer.getFeatures(request):
    # do whatever you need with the feature
    pass

En aras de la velocidad, la intersección a menudo se realiza solo con el cuadro delimitador de la entidad. Sin embargo, hay una bandera ExactIntersect que se asegura de que solo se devolverán las entidades que se cruzan:

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

Con setLimit() puede limitar el número de entidades solicitadas. Este es un ejemplo:

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

Si necesita un filtro basado en atributos en su lugar (o además) de uno espacial como se muestra en los ejemplos anteriores, puede crear un objeto QgsExpression y pasarlo al constructor QgsFeatureRequest. Este es un ejemplo:

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

Consulte Expresiones, Filtros y Calculando Valores para obtener detalles sobre la sintaxis admitida por QgsExpression.

La solicitud se puede utilizar para definir los datos recuperados para cada entidad, por lo que el iterador devuelve todas las entidades, pero devuelve datos parciales para cada una de ellas.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# Only return selected fields to increase the "speed" of the request
request.setSubsetOfAttributes([0,2])

# More user friendly version
request.setSubsetOfAttributes(['name','id'],layer.fields())

# Don't return geometry objects to increase the "speed" of the request
request.setFlags(QgsFeatureRequest.NoGeometry)

# Fetch only the feature with id 45
request.setFilterFid(45)

# The options may be chained
request.setFilterRect(areaOfInterest).setFlags(QgsFeatureRequest.NoGeometry).setFilterFid(45).setSubsetOfAttributes([0,2])

6.4. Modificación de capas vectoriales

La mayoría de los proveedores de datos vectoriales admiten la edición de datos de capa. A veces solo admiten un subconjunto de posibles acciones de edición. Utilice la función capabilities() para averiguar qué conjunto de funcionalidad es compatible.

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 obtener una lista de todas las capacidades disponibles, consulte la documentación API Documentation of QgsVectorDataProvider.

Para imprimir la descripción textual de las capacidades de las capas en una lista separada por comas, puede utilizar capabilitiesString() como en el ejemplo siguiente:

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

Mediante el uso de cualquiera de los métodos siguientes para la edición de capas vectoriales, los cambios se confirman directamente en el almacén de datos subyacente (un archivo, una base de datos, etc.). En caso de que desee realizar solo cambios temporales, vaya a la siguiente sección que explica cómo hacer modificaciones con la edición de cambios de búfer.

Nota

Si está trabajando dentro de QGIS (ya sea desde la consola o desde un complemento), podría ser necesario forzar un redibujo del lienzo del mapa para ver los cambios que ha realizado en la geometría, en el estilo o en los atributos:

1
2
3
4
5
6
# 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.triggerRepaint()
else:
    iface.mapCanvas().refresh()

6.4.1. Añadir Entidades

Cree algunas instancias de QgsFeature y pase una lista de ellas al método del proveedor addFeatures() Devolverá dos valores: resultado (verdadero/falso) y lista de características agregadas (su identificador lo establece el almacén de datos).

Para configurar los atributos de la entidad, puede inicializar la entidad pasando un objeto QgsFields (puede obtenerlo del método fields() de la capa vectorial) o llamar a initAttributes() pasando el número de campos que desea agregar.

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

6.4.2. Borrar Entidades

Para eliminar algunas entidades, solo tiene que proporcionar una lista de identificaciones de entidades.

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

6.4.3. Modificar los objetos espaciales

Es posible cambiar la geometría de la entidad o cambiar algunos atributos. En el ejemplo siguiente se cambian primero los valores de los atributos con los índices 0 y 1 y, a continuación, se cambia la geometría de la entidad.

1
2
3
4
5
6
7
8
9
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.fromPointXY(QgsPointXY(111,222))
    layer.dataProvider().changeGeometryValues({ fid : geom })

Truco

Favorecer la clase QgsVectorLayerEditUtils para ediciones de solo geometría

Si solo necesita cambiar geometrías, podría considerar el uso de QgsVectorLayerEditUtils que proporciona algunos métodos útiles para editar geometrías (trasladar, insertar o mover vértices, etc.).

6.4.4. Modificación de capas vectoriales con un búfer de edición

Al editar vectores dentro de la aplicación QGIS, primero tiene que comenzar el modo de edición para una capa en particular, luego hacer algunas modificaciones y finalmente confirmar (o revertir) los cambios. Todos los cambios que realice no se escribirán hasta que los confirme — ellos permanecen en el búfer de edición en memoria de la capa. Es posible utilizar esta funcionalidad también mediante programación — es sólo otro método para la edición de capas vectoriales que complementa el uso directo de proveedores de datos. Utilice esta opción al proporcionar algunas herramientas GUI para la edición de capas vectoriales, ya que esto permitirá al usuario decidir si desea confirmar/revertir y permite el uso de deshacer/rehacer. Cuando se confirman los cambios, todos los cambios del búfer de edición se guardan en el proveedor de datos.

Los métodos son similares a los que hemos visto en el proveedor, pero se llaman en el objeto QgsVectorLayer en su lugar.

Para que estos métodos funcionen, la capa debe estar en modo de edición. Para iniciar el modo de edición, utilice el método startEditing() Para detener la edición, utilice los métodos commitChanges() o rollBack(). El primero confirmará todos los cambios en el origen de datos, mientras que el segundo los descartará y no modificará el origen de datos en absoluto.

Para averiguar si una capa está en modo de edición, utilice el método isEditable().

Aquí tiene algunos ejemplos que muestran cómo utilizar estos métodos de edición.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from qgis.PyQt.QtCore import QVariant

feat1 = feat2 = QgsFeature(layer.fields())
fid = 99
feat1.setId(fid)

# 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
geometry = QgsGeometry.fromWkt("POINT(7 45)")
layer.changeGeometry(fid, geometry)
# update an attribute with given field index (int) to a given value
fieldIndex =1
value ='My new name'
layer.changeAttributeValue(fid, fieldIndex, value)

# add new field
layer.addAttribute(QgsField("mytext", QVariant.String))
# remove a field
layer.deleteAttribute(fieldIndex)

Para hacer que deshacer/rehacer trabaje correctamente, las llamadas mencionadas arriba tienen que ser envueltas en los comandos undo. (Si no le importa deshacer/rehacer y desea que los cambios se almacenen inmediatamente, entonces tendrá un trabajo más fácil por editando con proveedor de datos.)

Así es cómo usted puede utilizar la funcionalidad de deshacer:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
layer.beginEditCommand("Feature triangulation")

# ... call layer's editing methods ...

if problem_occurred:
  layer.destroyEditCommand()
  # ... tell the user that there was a problem
  # and return

# ... more editing ...

layer.endEditCommand()

El método beginEditCommand() creará un comando interno de «activo» y registrará los cambios posteriores en la capa vectorial. Con la llamada a el comando endEditCommand() se inserta en la pila de deshacer y el usuario podrá deshacer/rehacerlo desde la GUI. En caso de que algo saliera mal al realizar los cambios, el método destroyEditCommand() quitará el comando y revertirá todos los cambios realizados mientras este comando estaba activo.

También puede utilizar la instrucción with edit(layer)- para encapsular la confirmación y la reversión en un bloque de código más semántico, como se muestra en el ejemplo siguiente:

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

Esto llamará automáticamente a commitChanges() al final. Si ocurre alguna excepción, hará rollBack() a todos los cambios. En caso de que se encuentre un problema dentro de commitChanges() (cuando el método devuelve False) se producirá una excepción QgsEditError.

6.4.5. Agregando y Removiendo Campos

Para agregar campos (atributos), usted necesita especificar una lista de definiciones de campo. Para la eliminación de campos sólo proporcione una lista de índices de campo.

1
2
3
4
5
6
7
8
9
from qgis.PyQt.QtCore import QVariant

if caps & QgsVectorDataProvider.AddAttributes:
    res = layer.dataProvider().addAttributes(
        [QgsField("mytext", QVariant.String),
        QgsField("myint", QVariant.Int)])

if caps & QgsVectorDataProvider.DeleteAttributes:
    res = layer.dataProvider().deleteAttributes([0])
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# Alternate methods for removing fields
# first create temporary fields to be removed (f1-3)
layer.dataProvider().addAttributes([QgsField("f1",QVariant.Int),QgsField("f2",QVariant.Int),QgsField("f3",QVariant.Int)])
layer.updateFields()
count=layer.fields().count() # count of layer fields
ind_list=list((count-3, count-2)) # create list

# remove a single field with an index
layer.dataProvider().deleteAttributes([count-1])

# remove multiple fields with a list of indices
layer.dataProvider().deleteAttributes(ind_list)

Después de agregar o quitar campos en el proveedor de datos, los campos de la capa deben actualizarse porque los cambios no se propagan automáticamente.

layer.updateFields()

Truco

Guarde directamente los cambios usando el comando basado en with

Usando with edit(layer): los cambios se confirmarán automáticamente llamando a commitChanges() al final. Si se produce alguna excepción, hará un rollBack() de todos los cambios. Consulte Modificación de capas vectoriales con un búfer de edición.

6.5. Usar índice espacial

Los índices espaciales pueden mejorar drásticamente el rendimiento del código si necesita realizar consultas frecuentes en una capa vectorial. Imagine, por ejemplo, que está escribiendo un algoritmo de interpolación, y que para una ubicación determinada necesita conocer los 10 puntos más cercanos de una capa de puntos, con el fin de utilizar esos puntos para calcular el valor interpolado. Sin un índice espacial, la única manera de que QGIS encuentre esos 10 puntos es calcular la distancia desde todos y cada uno de los puntos hasta la ubicación especificada y luego comparar esas distancias. Esto puede ser una tarea que consume mucho tiempo, especialmente si necesita repetirse para varias ubicaciones. Si existe un índice espacial para la capa, la operación es mucho más efectiva.

Piense en una capa sin un índice espacial como una guía telefónica en la que los números de teléfono no se ordenan ni indexan. La única manera de encontrar el número de teléfono de una persona determinada es leer desde el principio hasta que lo encuentres.

Los índices espaciales no se crean de forma predeterminada para una capa vectorial QGIS, pero puede crearlos fácilmente. Esto es lo que tienes que hacer:

  • crear índice espacial utilizando la calse QgsSpatialIndex():

    index = QgsSpatialIndex()
    
  • agregar entidades al índice — el índice toma el objeto QgsFeature y lo agrega a la estructura de datos interna. Puede crear el objeto manualmente o usar uno de una llamada anterior al método getFeatures() del proveedor.

    index.addFeature(feat)
    
  • alternativamente, puede cargar todas las entidades de una capa a la vez utilizando la carga masiva

    index = QgsSpatialIndex(layer.getFeatures())
    
  • Una vez que el índice espacial se llena con algunos valores, puede realizar algunas consultas

    1
    2
    3
    4
    5
    # returns array of feature IDs of five nearest features
    nearest = index.nearestNeighbor(QgsPointXY(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))
    

6.6. Creación de capas vectoriales

Hay varias maneras de generar un dataset de capa vectorial:

  • la clase QgsVectorFileWriter class: Una clase cómoda para escribir archivos vectoriales en el disco, utilizando una llamada estática a writeAsVectorFormat() que guarda toda la capa vectorial o crea una instancia de la clase y emite llamadas a addFeature(). Esta clase admite todos los formatos vectoriales que Soporta OGR (GeoPackage, Shapefile, GeoJSON, KML y otros).

  • la clase QgsVectorLayer: crea una instancia de un proveedor de datos que interpreta la ruta de acceso proporcionada (url) del origen de datos para conectarse a los datos y tener acceso a ellos. Se puede utilizar para crear capas temporales basadas en memoria (memory) y conectarse a datasets OGR (ogr), bases de datos (postgres, spatialite, mysql, mssql) y más (wfs, gpx, delimitedtext…).

6.6.1. Desde una instancia de QgsVectorFileWriter

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

También puede convertir campos para que sean compatibles con formatos diferentes usando FieldValueConverter. Por ejemplo, para convertir tipos de variable matriz (por ejemplo, en Postgres) en un tipo texto, puede hacer lo siguiente:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
LIST_FIELD_NAME = 'xxxx'

class ESRIValueConverter(QgsVectorFileWriter.FieldValueConverter):

  def __init__(self, layer, list_field):
    QgsVectorFileWriter.FieldValueConverter.__init__(self)
    self.layer = layer
    self.list_field_idx = self.layer.fields().indexFromName(list_field)

  def convert(self, fieldIdxInLayer, value):
    if fieldIdxInLayer == self.list_field_idx:
      return QgsListFieldFormatter().representValue(layer=vlayer,
                                                    fieldIndex=self.list_field_idx,
                                                    config={},
                                                    cache=None,
                                                    value=value)
    else:
      return value

  def fieldDefinition(self, field):
    idx = self.layer.fields().indexFromName(field.name())
    if idx == self.list_field_idx:
      return QgsField(LIST_FIELD_NAME, QVariant.String)
    else:
      return self.layer.fields()[idx]

converter = ESRIValueConverter(vlayer, LIST_FIELD_NAME)
opts = QgsVectorFileWriter.SaveVectorOptions()
opts.fieldValueConverter = converter

También se puede especificar un CRS de destino — si se pasa una instancia válida de QgsCoordinateReferenceSystem como cuarto parámetro, la capa se transforma a ese CRS.

Para los nombres de controlador válidos, llame al método supportedFiltersAndFormats o consulte los “formatos admitidos por OGR`_ — debe pasar el valor en la columna «Código» como el nombre del controlador.

Opcionalmente, puede establecer si desea exportar solo las entidades seleccionadas, pasar más opciones específicas del controlador para la creación o indicar al escritor que no cree atributos… Hay una serie de otros parámetros (opcionales); consulte la documentación de QgsVectorFileWriter para más detalles.

6.6.2. Directamente desde las funciones

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
from qgis.PyQt.QtCore import QVariant

# define fields for feature attributes. A QgsFields object is needed
fields = QgsFields()
fields.append(QgsField("first", QVariant.Int))
fields.append(QgsField("second", QVariant.String))

""" create an instance of vector file writer, which will create the vector file.
Arguments:
1. path to new file (will fail if exists already)
2. field map
3. geometry type - from WKBTYPE enum
4. layer's spatial reference (instance of
   QgsCoordinateReferenceSystem)
5. coordinate transform context
6. save options (driver name for the output file, encoding etc.)
"""

crs = QgsProject.instance().crs()
transform_context = QgsProject.instance().transformContext()
save_options = QgsVectorFileWriter.SaveVectorOptions()
save_options.driverName = "ESRI Shapefile"
save_options.fileEncoding = "UTF-8"

writer = QgsVectorFileWriter.create(
  "testdata/my_new_shapefile.shp",
  fields,
  QgsWkbTypes.Point,
  crs,
  transform_context,
  save_options
)

if writer.hasError() != QgsVectorFileWriter.NoError:
    print("Error when creating shapefile: ",  writer.errorMessage())

# add a feature
fet = QgsFeature()

fet.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(10,10)))
fet.setAttributes([1, "text"])
writer.addFeature(fet)

# delete the writer to flush features to disk
del writer

6.6.3. Desde una instancia de QgsVectorLayer

Entre todos los proveedores de datos admitidos por la clase QgsVectorLayer, vamos a centrarnos en las capas basadas en memoria. Proveedor de memoria está destinado a ser utilizado principalmente por plugins o desarrolladores de aplicaciones de 3as partes. No almacena datos en el disco, lo que permite a los desarrolladores utilizarlos como un backend rápido para algunas capas temporales.

El proveedor admite los campos string, int y double.

El proveedor de memoria también admite la indexación espacial, que se habilita llamando a la función createSpatialIndex() del proveedor. Una vez creado el índice espacial, podrá recorrer iterando sobre las entidades dentro de regiones más pequeñas más rápido (ya que no es necesario atravesar todas las entidades, solo las del rectángulo especificado)..

Un proveedor de memoria se crea pasando "memory" como la cadena del proveedor al constructor QgsVectorLayer.

El constructor también toma un URI que define el tipo de geometría de la capa, uno de: "Point", "LineString", "Polygon", "MultiPoint", "MultiLineString", "MultiPolygon" o "None".

El URI también puede especificar el sistema de referencia de coordenadas, los campos y la indexación del proveedor de memoria en el URI. La sintaxis es:

crs=definición

Especifica el sistema de referencia de coordenadas, donde la definition puede ser cualquiera de las formas aceptadas por QgsCoordinateReferenceSystem.createFromString

index=yes

Especifica que el proveedor utilizará un índice espacial

campo

Especifica un atributo de la capa. El atributo tiene un nombre y, opcionalmente, un tipo (entero, doble o cadena), longitud y precisión. Puede haber múltiples definiciones de campo

El siguiente ejemplo de una URI incorpora todas estas opciones

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

El siguiente código de ejemplo ilustra como crear y rellenar un proveedor de memoria

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
from qgis.PyQt.QtCore import QVariant

# create layer
vl = QgsVectorLayer("Point", "temporary_points", "memory")
pr = vl.dataProvider()

# add fields
pr.addAttributes([QgsField("name", QVariant.String),
                    QgsField("age",  QVariant.Int),
                    QgsField("size", QVariant.Double)])
vl.updateFields() # tell the vector layer to fetch changes from the provider

# add a feature
fet = QgsFeature()
fet.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(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()

Finalmente, vamos a comprobar si todo salió bien

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# show some stats
print("fields:", len(pr.fields()))
print("features:", pr.featureCount())
e = vl.extent()
print("extent:", e.xMinimum(), e.yMinimum(), e.xMaximum(), e.yMaximum())

# iterate over features
features = vl.getFeatures()
for fet in features:
    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.7. Apariencia (Simbología) de capas vectoriales

Cuando una capa vectorial se representa, la apariencia de los datos se indica por renderer y símbolos asociados a la capa. Los símbolos son clases que se encargan del dibujo de la representación visual de las entidades, mientras que los renderizadores determinan qué símbolo se utilizará para una entidad determinada.

El renderizador para una capa determinada se puede obtener como se muestra a continuación:

renderer = layer.renderer()

Y con esa referencia, vamos a explorar un poco

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

Hay varios tipos de renderizadores conocidos disponibles en la biblioteca principal de QGIS:

Tipo

Clase

Descripción

singleSymbol

QgsSingleSymbolRenderer

Representa todas las entidades con el mismo símbolo

categorizedSymbol

QgsCategorizedSymbolRenderer

Representa entidades utilizando un símbolo diferente para cada categoría

graduatedSymbol

QgsGraduatedSymbolRenderer

Representa entidades utilizando un símbolo diferente para cada rango de valores

También puede haber algunos tipos de presentadores personalizados, por lo que nunca haga una suposición de que solo hay estos tipos. Puede consultar el QgsRendererRegistry de la aplicación para encontrar los presentadores disponibles actualmente:

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

Es posible obtener un volcado del contenido de presentador en forma de texto — puede ser útil para la depuración

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

6.7.1. Representador de Símbolo Único

Puede obtener el símbolo utilizado para la representación llamando al método symbol() y cambiarlo con el método :meth:`setSymbol() <qgis.core.QgsSingleSymbolRenderer.setSymbol>`(nota para desarrolladores de C++:  el renderizado toma posesión del símbolo.)

Puede cambiar el símbolo utilizado por una capa vectorial determinada llamando a setSymbol() pasando una instancia de la instancia de símbolo apropiada. Los símbolos para las capas de point, line y polygon pueden ser creadas llamando a la función createSimple() de las clases correspondientes QgsMarkerSymbol, QgsLineSymbol y QgsFillSymbol.

El diccionario pasado a createSimple() establece las propiedades de estilo del símbolo.

Por ejemplo, puede reemplazar el símbolo utilizado por una capa point particular llamando a setSymbol() pasando una instancia de QgsMarkerSymbol, como en el siguiente ejemplo de código:

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

name indica la forma del marcador, y puede ser cualquiera de los siguientes:

  • circle

  • square

  • cross

  • rectangle

  • diamond

  • pentagon

  • triangle

  • equilateral_triangle

  • star

  • regular_star

  • arrow

  • filled_arrowhead

  • x

Para obtener la lista completa de propiedades de la primera capa de símbolos de una instancia de símbolo, puede seguir el código de ejemplo:

print(layer.renderer().symbol().symbolLayers()[0].properties())
{'angle': '0', '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'}

Esto puede ser útil si desea modificar algunas propiedades:

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

6.7.2. Representador de símbolo categorizado

Al utilizar un representador categorizado, puede consultar y establecer el atributo que se utiliza para la clasificación: utilice los métodos classAttribute() y setClassAttribute().

Para obtener una lista de categorías

1
2
3
4
5
6
7
8
9
categorized_renderer = QgsCategorizedSymbolRenderer()
# Add a few categories
cat1 = QgsRendererCategory('1', QgsMarkerSymbol(), 'category 1')
cat2 = QgsRendererCategory('2', QgsMarkerSymbol(), 'category 2')
categorized_renderer.addCategory(cat1)
categorized_renderer.addCategory(cat2)

for cat in categorized_renderer.categories():
    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>

Donde value() es el valor utilizado para la discriminación entre categorías, label() es el texto utilizado para la descripción de la categoría y el método symbol() devuelve el símbolo asignado.

El renderizador suele almacenar también el símbolo original y la rampa de color que se utilizaron para la clasificación: métodos sourceColorRamp() y sourceSymbol().

6.7.3. Renderizador de símbolo graduado

Este representador es muy similar al representador de símbolos categorizados descrito anteriormente, pero en lugar de un valor de atributo por clase trabaja con rangos de valores y, por lo tanto, solo se puede utilizar con atributos numéricos.

Para obtener más información sobre los rangos utilizados en el renderizador

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
graduated_renderer = QgsGraduatedSymbolRenderer()
# Add a few categories
graduated_renderer.addClassRange(QgsRendererRange(QgsClassificationRange('class 0-100', 0, 100), QgsMarkerSymbol()))
graduated_renderer.addClassRange(QgsRendererRange(QgsClassificationRange('class 101-200', 101, 200), QgsMarkerSymbol()))

for ran in graduated_renderer.ranges():
    print("{} - {}: {} {}".format(
        ran.lowerValue(),
        ran.upperValue(),
        ran.label(),
        ran.symbol()
      ))
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>

puede volver a utilizar los métodos classAttribute (para encontrar el nombre del atributo de clasificación), sourceSymbol y sourceColorRamp. Adicionalmente existe el método mode que determina cómo se crearon los intervalos: utilizando intervalos iguales, cuantiles o algún otro método.

Si desea crear su propio renderizador de símbolos graduados, puede hacerlo como se ilustra en el siguiente fragmento de ejemplo (que crea una sencilla disposición de dos clases)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
from qgis.PyQt import QtGui

myVectorLayer = QgsVectorLayer("testdata/airports.shp", "airports", "ogr")
myTargetField = 'ELEV'
myRangeList = []
myOpacity = 1
# Make our first symbol and range...
myMin = 0.0
myMax = 50.0
myLabel = 'Group 1'
myColour = QtGui.QColor('#ffee00')
mySymbol1 = QgsSymbol.defaultSymbol(myVectorLayer.geometryType())
mySymbol1.setColor(myColour)
mySymbol1.setOpacity(myOpacity)
myRange1 = QgsRendererRange(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 = QgsSymbol.defaultSymbol(
     myVectorLayer.geometryType())
mySymbol2.setColor(myColour)
mySymbol2.setOpacity(myOpacity)
myRange2 = QgsRendererRange(myMin, myMax, mySymbol2, myLabel)
myRangeList.append(myRange2)
myRenderer = QgsGraduatedSymbolRenderer('', myRangeList)
myClassificationMethod = QgsApplication.classificationMethodRegistry().method("EqualInterval")
myRenderer.setClassificationMethod(myClassificationMethod)
myRenderer.setClassAttribute(myTargetField)

myVectorLayer.setRenderer(myRenderer)

6.7.4. Trabajo con Símbolos

Para la representación de símbolos, hay esta clase base QgsSymbol con tres clases derivadas:

Cada símbolo consta de una o más capas de símbolos (clases derivadas de QgsSymbolLayer). Las capas de símbolos hacen la representación real, la propia clase de símbolo sirve sólo como un contenedor para las capas de símbolo.

Teniendo una instancia de un símbolo (por ejemplo, de un renderizador), es posible explorarlo: el método type dice si es un símbolo de marcador, línea o relleno. Hay un método dump que devuelve una breve descripción del símbolo. Para obtener una lista de capas de símbolos:

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

Para averiguar el método de uso del color del símbolo color y setColor para cambiar su color. Con los símbolos de marcador, además, puede consultar el tamaño y la rotación del símbolo con los métodos size y angle. Para los símbolos lineales el método width devuelve el grosor de la línea.

De forma predeterminada el tamaño y ancho están en milímetros, los ángulos en grados.

6.7.4.1. Trabajando con capas de símbolos

Como se dijo antes, las capas de símbolo (subclases de QgsSymbolLayer) determinar la apariencia de las entidades. Hay varias clases de capas de símbolos básicas para uso general. Es posible implementar nuevos tipos de capas de símbolos y así personalizar arbitrariamente cómo se renderizarán las entidades. El método layerType() identifica de forma única la clase de capa de símbolo — las básicas y predeterminadas son los tipos de capas de símbolo `` SimpleMarker “”, `` SimpleLine “” y `` SimpleFill “”.

identifica de forma única la clase de capa de símbolo — las básicas y predeterminadas son los tipos de capas de símbolo `` SimpleMarker “”, `` SimpleLine “” y `` SimpleFill “”.

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

La clase QgsSymbolLayerRegistry gestiona una base de datos de todos los tipos de capas de símbolos disponibles.

Para acceder a los datos de la capa de símbolo, use su método properties() que devuelve un diccionario de propiedades clave-valor que determina la apariencia. Cada tipo de capa de símbolo tiene un conjunto específico de propiedades que utiliza. Además, existen los métodos genéricos color, size, angle y width, con sus contrapartes setter. Por supuesto, el tamaño y el ángulo solo están disponibles para las capas de símbolo de marcador y el ancho para las capas de símbolo de línea.

6.7.4.2. Crear tipos de capas de símbolos personalizados

Imagine que le gustaría personalizar la forma en que se procesan los datos. Puede crear su propia clase de capa de símbolo que dibujará las entidades exactamente como lo desee. Aquí hay un ejemplo de un marcador que dibuja círculos rojos con un radio especificado

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
from qgis.core import QgsMarkerSymbolLayer
from qgis.PyQt.QtGui import QColor

class FooSymbolLayer(QgsMarkerSymbolLayer):

  def __init__(self, radius=4.0):
      QgsMarkerSymbolLayer.__init__(self)
      self.radius = radius
      self.color = QColor(255,0,0)

  def layerType(self):
     return "FooMarker"

  def properties(self):
      return { "radius" : str(self.radius) }

  def startRender(self, context):
    pass

  def stopRender(self, context):
      pass

  def renderPoint(self, point, context):
      # Rendering depends on whether the symbol is selected (QGIS >= 1.5)
      color = context.selectionColor() if context.selected() else self.color
      p = context.renderContext().painter()
      p.setPen(color)
      p.drawEllipse(point, self.radius, self.radius)

  def clone(self):
      return FooSymbolLayer(self.radius)

The layerType method determines the name of the symbol layer; it has to be unique among all symbol layers. 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.

Usually it is convenient to add a GUI for setting attributes of the symbol layer type to allow users to customize the appearance: in case of our example above we can let user set circle radius. The following code implements such widget

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
from qgis.gui import QgsSymbolLayerWidget

class FooSymbolLayerWidget(QgsSymbolLayerWidget):
    def __init__(self, parent=None):
        QgsSymbolLayerWidget.__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()"))

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.

On every change of attributes, the widget should emit the changed() signal to let the properties dialog update the symbol preview.

Now we are missing only the final glue: to make QGIS aware of these new classes. This is done by adding the symbol layer to registry. It is possible to use the symbol layer also without adding it to the registry, but some functionality will not work: e.g. loading of project files with the custom symbol layers or inability to edit the layer’s attributes in GUI.

We will have to create metadata for the symbol layer

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
from qgis.core import QgsSymbol, QgsSymbolLayerAbstractMetadata, QgsSymbolLayerRegistry

class FooSymbolLayerMetadata(QgsSymbolLayerAbstractMetadata):

  def __init__(self):
    super().__init__("FooMarker", "My new Foo marker", QgsSymbol.Marker)

  def createSymbolLayer(self, props):
    radius = float(props["radius"]) if "radius" in props else 4.0
    return FooSymbolLayer(radius)

QgsApplication.symbolLayerRegistry().addSymbolLayerType(FooSymbolLayerMetadata())

You should pass layer type (the same as returned by the layer) and symbol type (marker/line/fill) to the constructor of the parent class. The createSymbolLayer() method takes care of creating an instance of symbol layer with attributes specified in the props dictionary. And there is the createSymbolLayerWidget() method which returns the settings widget for this symbol layer type.

El último pase es adicionar esta capa símbolo al registro — y estamos listos.

6.7.5. Creating Custom Renderers

It might be useful to create a new renderer implementation if you would like to customize the rules how to select symbols for rendering of features. Some use cases where you would want to do it: symbol is determined from a combination of fields, size of symbols changes depending on current scale etc.

The following code shows a simple custom renderer that creates two marker symbols and chooses randomly one of them for every feature

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import random
from qgis.core import QgsWkbTypes, QgsSymbol, QgsFeatureRenderer


class RandomRenderer(QgsFeatureRenderer):
  def __init__(self, syms=None):
    super().__init__("RandomRenderer")
    self.syms = syms if syms else [
      QgsSymbol.defaultSymbol(QgsWkbTypes.geometryType(QgsWkbTypes.Point)),
      QgsSymbol.defaultSymbol(QgsWkbTypes.geometryType(QgsWkbTypes.Point))
    ]

  def symbolForFeature(self, feature, context):
    return random.choice(self.syms)

  def startRender(self, context, fields):
    super().startRender(context, fields)
    for s in self.syms:
      s.startRender(context, fields)

  def stopRender(self, context):
    super().stopRender(context)
    for s in self.syms:
      s.stopRender(context)

  def usedAttributes(self, context):
    return []

  def clone(self):
    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.

Like with symbol layers, it is possible to attach a GUI for configuration of the renderer. It has to be derived from QgsRendererWidget. The following sample code creates a button that allows the user to set the first symbol

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
from qgis.gui import QgsRendererWidget, QgsColorButton


class RandomRendererWidget(QgsRendererWidget):
  def __init__(self, layer, style, renderer):
    super().__init__(layer, style)
    if renderer is None or renderer.type() != "RandomRenderer":
      self.r = RandomRenderer()
    else:
      self.r = renderer
    # setup UI
    self.btn1 = QgsColorButton()
    self.btn1.setColor(self.r.syms[0].color())
    self.vbox = QVBoxLayout()
    self.vbox.addWidget(self.btn1)
    self.setLayout(self.vbox)
    self.btn1.colorChanged.connect(self.setColor1)

  def setColor1(self):
    color = self.btn1.color()
    if not color.isValid(): return
    self.r.syms[0].setColor(color)

  def renderer(self):
    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.

The last missing bit is the renderer metadata and registration in registry, otherwise loading of layers with the renderer will not work and user will not be able to select it from the list of renderers. Let us finish our RandomRenderer example

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
from qgis.core import (
  QgsRendererAbstractMetadata,
  QgsRendererRegistry,
  QgsApplication
)

class RandomRendererMetadata(QgsRendererAbstractMetadata):

  def __init__(self):
    super().__init__("RandomRenderer", "Random renderer")

  def createRenderer(self, element):
    return RandomRenderer()

  def createRendererWidget(self, layer, style, renderer):
    return RandomRendererWidget(layer, style, renderer)

QgsApplication.rendererRegistry().addRenderer(RandomRendererMetadata())

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.

To associate an icon with the renderer you can assign it in the QgsRendererAbstractMetadata constructor as a third (optional) argument — the base class constructor in the RandomRendererMetadata __init__() function becomes

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.8. Más Temas

PENDIENTE:

  • crear/modificar símbolos

  • working with style (QgsStyle)

  • working with color ramps (QgsColorRamp)

  • exploring symbol layer and renderer registries