Importante
La traducción es un esfuerzo comunitario puede unirse. Esta página está actualmente traducida en |progreso de traducción|.
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()
proporcionan información sobre el campo y la plantilla utilizados 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 algunas instancias QgsFeature
y pasa una lista de ellas al método QgsVectorDataProvider
addFeatures()
. Devolverá dos valores: resultado (True
o False
) y lista de objetos espaciales añadidos (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étodogetFeatures()
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 práctica para escribir archivos vectoriales en disco, utilizando una llamada estática awriteAsVectorFormatV3()
que guarda toda la capa vectorial o creando una instancia de la clase y emitiendo llamadas a la clase heredadaaddFeature()
. Esta clase soporta todos los formatos vectoriales que soporta GDAL (GeoPackage, archivo Shape, GeoJSON, KML y otros).la clase
QgsVectorLayer
: crea una instancia de un proveedor de datos que interpreta la ruta seleccionada (url) de la fuente de datos para conectar y acceder a los datos. Se puede usar para crear capas temporales, basadas en la memoria (memoria
) y conectar a conjunto de datos vectoriales GDAL (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 |
Representa todas las entidades con el mismo símbolo |
|
categorizedSymbol |
Representa entidades utilizando un símbolo diferente para cada categoría |
|
graduatedSymbol |
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:
QgsMarkerSymbol
— para entidades de puntoQgsLineSymbol
— para entidades de líneaQgsFillSymbol
— para entidades de polígono
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)
1AnimatedMarker
2EllipseMarker
3FilledMarker
4FontMarker
5GeometryGenerator
6MaskMarker
7RasterMarker
8SimpleMarker
9SvgMarker
10VectorField
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