6. Usar capas vectoriales

Consejo

Los fragmentos de código en esta página necesitan las siguientes adiciones si está fuera de la consola de 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 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())
1ID Integer64
2fk_region Integer64
3ELEV Real
4NAME String
5USE String

Los métodos displayField() y mapTipTemplate() de la clase QgsVectorLayer proveen información sobre el campo y plantilla usada en la pestaña Propiedades a mostrar.

Cuando carga una capa vectorial, un campo es siempre escogido por QGIS como el Nombre de Visualización, mientras que el Aviso Mapa HTML está vacío de forma predeterminada. Con estos métodos puede fácilmente conseguir ambos:

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

Nota

Si cambia el Nombre de Visualización de un campo a una expresión, tiene que usar displayExpression() en vez de displayField().

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# "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. 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:

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 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:

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

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>
<qgis._core.QgsFeature object at 0x7faef5881670>

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

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'

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# 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. Añadir Entidades

Crea algunos ejemplos QgsFeature y pasa una lista de ellos al método del proveedor addFeatures(). Devolverá dos valores: resultado (True o False) y una lista de entidades agregadas (su ID 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.

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

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

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 debe iniciar el modo de edición para una capa en particular, luego hacer algunas modificaciones y finalmente confirmar (o deshacer) los cambios. Todos los cambios que realiza no se escriben hasta que los confirma; permanecen en el búfer de edición en memoria de la capa. Es posible utilizar esta funcionalidad también de forma programática, es solo otro método para la edición de capas vectoriales que complementa el uso directo de los proveedores de datos. Use esta opción cuando proporcione algunas herramientas GUI para la edición de capas vectoriales, ya que esto permitirá al usuario decidir si confirmar / deshacer 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.

 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 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:

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

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.

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)

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 serán automàticamente enviados a commitChanges() al final. si ocurre alguna excepción, serán rollBack() todos los cambios. Ver 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 usando la clase 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# 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))
    

También puede usar el índice espacial QgsSpatialIndexKDBush. Este índice es similar al estándar QgsSpatialIndex pero:

  • soporta solo entidades de único punto

  • es estática (no se pueden agregar entidades adicionales al índice después de la construcción)

  • es ¡mas rápida!

  • permite la recuperación directa de los puntos de la entidad original, sin requerir solicitudes de entidades adicionales

  • admite búsquedas verdaderas basadas en la distancia, es decir, devuelve todos los puntos dentro de un radio desde un punto de búsqueda

6.6. La clase QgsVectorLayerUtils

La clase QgsVectorLayerUtils contiene algunos métodos muy útiles que puede utilizar con capas vectoriales.

Por ejemplo el método createFeature() prepara una QgsFeature para ser agregada a una capa vectorial manteniendo todas las eventuales restricciones y valores predeterminados de cada campo:

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

El método getValues() le permite obtener rápidamente los valores de un campo o expresión:

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. Creación de capas vectoriales

Hay varias maneras de generar un dataset de capa vectorial:

  • la clase QgsVectorFileWriter: una clase conveniente para escribir archivos vectoriales en el disco, usando una llamada estática a writeAsVectorFormatV3() 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 admite 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.7.1. Desde una instancia 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)

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:

 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

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 obtener nombres de controladores 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.7.2. Directamente desde las funciones

 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. 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 definición 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

 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 a comprobar si todo salió bien

 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. 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', 'mergedFeatureRenderer', 'invertedPolygonRenderer', 'heatmapRenderer', '25dRenderer', 'embeddedSymbol']

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.8.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', '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'}

Esto puede ser útil si desea modificar algunas propiedades:

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

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>

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

 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>

puedes volver a usar los métodos classAttribute() (para encontrar el nombre del atributo de clasificación), sourceSymbol() y sourceColorRamp() . Adicionalmente está el método mode() que determina como son creados los rangos: usando intervalos iguales, cuanties 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)

 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. 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 marcador, línea o símbolo de 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 color del símbolo use el método color() y setColor() para cmabiar 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 símbolos lineales el método width() devuelve la anchura de la línea.

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

6.8.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 “”.

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

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ímbolos, 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.8.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

 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)

El layerType() el método determina el nombre de la capa de símbolo; tiene que ser único entre todas las capas de símbolos. El método properties() es usada para la persistencia de sus atributos. El método clone() debe devolver una copia de la capa de símbolo con todos los atributos exactamente iguales. Finalmente, existen métodos de renderizado: startRender() se llama antes de renderizar la primera entidad, stopRender() cuando el renderizado está terminado, y renderPoint() es llamado para hacer el renderizado. Las coordenadas de los puntos ya están transformadas a las coordenadas de salida.

Para polilíneas y polígonos, la única diferencia estaría en el método de representación: que usaría renderPolyline () que recibe una lista de líneas, mientras que :meth:`renderPolygon() <qgis.core.QgsFillSymbolLayer.renderPolygon> `recibe una lista de puntos en el anillo exterior como primer parámetro y una lista de anillos interiores (o Ninguno) como segundo parámetro.

Por lo general, es conveniente agregar una GUI para configurar los atributos del tipo de capa de símbolo para permitir a los usuarios personalizar la apariencia: en el caso de nuestro ejemplo anterior, podemos permitir que el usuario establezca el radio del círculo. El siguiente código implementa dicho 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()"))

Este widget se puede incrustar en el cuadro de diálogo de propiedades del símbolo. Cuando se selecciona el tipo de capa de símbolo en el cuadro de diálogo de propiedades de símbolo, crea una instancia de la capa de símbolo y una instancia del widget de capa de símbolo. Luego llama al método setSymbolLayer() para asignar la capa de símbolo al widget. En ese método, el widget debería actualizar la interfaz de usuario para reflejar los atributos de la capa de símbolo. El método symbolLayer() se usa para recuperar la capa de símbolo nuevamente mediante el diálogo de propiedades para usarla para el símbolo.

En cada cambio de atributos, el widget debe emitir la señal changed() para permitir que el diálogo de propiedades actualice la vista previa del símbolo.

Ahora solo nos falta el pegamento final: hacer que QGIS esté al tanto de estas nuevas clases. Esto se hace agregando la capa de símbolo al registro. También es posible utilizar la capa de símbolo sin agregarla al registro, pero algunas funciones no funcionarán: p. Ej. carga de archivos de proyecto con capas de símbolo personalizadas o incapacidad para editar los atributos de la capa en la GUI.

Tendremos que crear metadatos para la capa de símbolo.

 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)

Debe pasar el tipo de capa (el mismo que devuelve la capa) y el tipo de símbolo (marcador / línea / relleno) al constructor de la clase principal. El método createSymbolLayer() se encarga de crear una instancia de la capa de símbolo con los atributos especificados en el diccionario props. Y esta el método createSymbolLayerWidget() que devuelve el widget de configuración para este tipo de capa de símbolo.

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

6.8.5. Crear Renderizados personalizados

Puede ser útil crear una nueva implementación de renderizador si desea personalizar las reglas sobre cómo seleccionar símbolos para renderizar características. Algunos casos de uso en los que desearía hacerlo: el símbolo se determina a partir de una combinación de campos, el tamaño de los símbolos cambia según la escala actual, etc.

El siguiente código muestra un renderizador personalizado simple que crea dos símbolos de marcador y elige aleatoriamente uno de ellos para cada objeto.

 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)

El constructor de la clase padre QgsFeatureRenderer necesita un nombre de renderizador (que tiene que ser único entre los renderizadores). El método symbolForFeature() es el que decide qué símbolo se utilizará para una entidad en particular. startRender() y stopRender() se encargan de la inicialización/finalización de la representación de símbolos. El método usedAttributes() puede devolver una lista de nombres de campo que el renderizador espera que esté presente. Finalmente, el método clone() debería devolver una copia del renderizador.

Al igual que con las capas de símbolos, es posible adjuntar una GUI para la configuración del renderizador. Tiene que derivarse de QgsRendererWidget. El siguiente código de muestra crea un botón que permite al usuario establecer el primer 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

El constructor recibe instancias de la capa activa (QgsVectorLayer), el estilo global (QgsStyle) y el renderizador actual. Si no hay renderizador o el renderizador tiene un tipo diferente, será reemplazado por nuestro nuevo renderizador, de lo contrario usaremos el renderizador actual (que ya tiene el tipo que necesitamos). El contenido del widget debe actualizarse para mostrar el estado actual del renderizador. Cuando se acepta el cuadro de diálogo del renderizador, se llama al método del widget : meth:renderer() <qgis.gui.QgsRendererWidget.renderer> para obtener el renderizador actual — se asignará a la capa.

El último bit que falta son los metadatos del renderizador y el registro en el registro; de lo contrario, la carga de capas con el renderizador no funcionará y el usuario no podrá seleccionarlo de la lista de renderizadores. Terminemos nuestro ejemplo de 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)

Del mismo modo que con las capas de símbolo, el constructor de metadatos abstracto espera el nombre del renderizador, el nombre visible para los usuarios y, opcionalmente, el nombre del icono del renderizador. El método createRenderer() pasa una instancia QDomElement que puede usarse para restaurar el estado del renderizador desde el árbol DOM. El método createRendererWidget() crea el widget de configuración. No tiene que estar presente o puede devolver Ninguno si el renderizador no viene con GUI.

Para asociar un icono con el renderizador, puede asignarlo en el constructor QgsRendererAbstractMetadata como tercer argumento (opcional) — el constructor de la clase base en la función RandomRendererMetadata __init__() se convierte en

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

El icono también se puede asociar en cualquier momento posterior mediante el método setIcon() de la clase de metadatos. El icono se puede cargar desde un archivo (como se muestra arriba) o se puede cargar desde un recurso Qt (PyQt5 incluye el compilador .qrc para Pitón).

6.9. Más Temas

PENDIENTE:

  • crear/modificar símbolos

  • trabajandocon estilo (QgsStyle)

  • trabajando con rampas de color (QgsColorRamp)

  • explorar la capa de símbolo y los registros de renderizado