QGIS 3.4 PyQGISDeveloperCookbook Es
QGIS 3.4 PyQGISDeveloperCookbook Es
Versión 3.4
QGIS Project
15 de marzo de 2020
Contents
1 Introducción 1
1.1 Desarrollar scripts en la consola de Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.2 Plugins Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.3 Ejecutar código Python cuando QGIS se inicia. . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.4 Aplicaciones Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.5 Notas técnicas sobre PyQt y SIP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
2 Cargar proyectos 7
3 Cargar capas 9
3.1 Capas Vectoriales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
3.2 Capas ráster . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
3.3 Instancia QgsProject . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
6 Manejo de Geometría 41
6.1 Construcción de Geometría . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
6.2 Acceso a Geometría . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
6.3 Geometría predicados y Operaciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
7 Soporte de Proyecciones 45
7.1 Sistemas de coordenadas de referencia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
7.2 CRS Transformation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
i
8.4 Escribir herramientas de mapa personalizados . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
8.5 Escribir elementos de lienzo de mapa personalizado . . . . . . . . . . . . . . . . . . . . . . . . 54
13 Infraestructura de autenticación 69
13.1 Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
13.2 Glosario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
13.3 QgsAuthManager the entry point . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
13.4 Adapt plugins to use Authentication infrastructure . . . . . . . . . . . . . . . . . . . . . . . . . 73
13.5 Authentication GUIs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
ii
19.9 Algoritmos de procesamiento . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130
19.10 Decoradores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131
19.11 Fuentes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132
iii
iv
CHAPTER 1
Introducción
Este documento pretende ser tanto un tutorial y una guía de referencia. Aunque no enumera todos los casos de
uso posibles, debe proporcionar una buena visión general de la funcionalidad principal.
El soporte de Python se introdujo por primera vez en QGIS 0.9. Hay varias maneras de utilizar Python en QGIS
Desktop (cubierto en las siguientes secciones):
• Emita comandos en la consola de Python dentro de QGIS
• Crear y usar plugins
• Ejecute automáticamente código Python cuando se inicie QGIS
• Crear aplicaciones personalizadas basadas en la API de QGIS
Los enlaces de Python también están disponibles para QGIS Server, incluidos los plugins de Python (vea Comple-
mentos de Python de QGIS Server) y los enlaces de Python que se pueden usar para incrustar QGIS Server en una
aplicación de Python.
Esta es una complete QGIS API de referencia que documenta las clases de la librería QGIS. The Pythonic QGIS
API (pyqgis) es casi idéntica a la API de C++.
Un buen recurso para aprender a realizar tareas comunes es descargar complementos existentes desde el repositorio
de complementos y examinar su código.
1
PyQGIS developer cookbook, Versión 3.4
QGIS provee una consola Python integrada para scripting. Se puede abrir desde el menu: Complementos →
Consola de Python
La captura de pantalla anterior ilustra cómo obtener la capa seleccionada actualmente en la lista de capas, mostrar
su ID y opcionalmente, si se trata de una capa vectorial, mostrar el recuento de entidades. Para la interacción con el
entorno QGIS, hay una variable iface que es una instancia de la clase: QgisInterface <qgis.gui.QgisInterface>.
Esta interfaz permite el acceso al lienzo del mapa, menús, barras de herramientas y otras partes de la aplicación
QGIS.
Para mayor comodidad del usuario, las siguientes instrucciones se ejecutan cuando se inicia la consola (en el
futuro será posible establecer más comandos iniciales)
Para aquellos que utilizan la consola a menudo, puede ser útil establecer un acceso directo para activar la consola
(dentro de Configuración → Atajos de teclado. . . )
La funcionalidad de QGIS se puede ampliar utilizando complementos. Los complementos se pueden escribir en
Python. La principal ventaja sobre los complementos de C++ es la simplicidad de la distribución (sin compilación
para cada plataforma) y el desarrollo más fácil.
Muchos complementos que cubren diversas funciones se han escrito desde la introducción del soporte de Python.
El instalador de complemento permite a los usuarios buscar, actualizar y eliminar fácilmente complementos
Python. Consulte la página Complementos de Python para más información sobre complementos y desarrollo
de complementos.
Crear plugins con Python es simple, vea Developing Python Plugins para instrucciones detalladas.
Nota: Los plugins de Python están también disponibles para QGIS Server. Vea Complementos de Python de
QGIS Server para más detalles.
2 Chapter 1. Introducción
PyQGIS developer cookbook, Versión 3.4
Existen dos métodos distintos para ejecutar código Python cada vez que QGIS inicia.
1. Crear un script startup.py
2. Configurar la variable de entorno PYQGIS_STARTUP a un fichero Python existente
Cada vez que se inicia QGIS, el directorio de inicio de Python del usuario
• Linux: .local/share/QGIS/QGIS3
• Windows: AppData\Roaming\QGIS\QGIS3
• macOS: Library/Application Support/QGIS/QGIS3
se busca un archivos llamado startup.py. Si ese archivo existe, lo ejecuta el intérprete de Python incorporado.
Nota: La ruta predeterminada depende de cada sistema operativo. Para encontrar la ruta
que pueda funcionar en su caso, abra la consola de Python y ejecute QStandardPaths.
standardLocations(QStandardPaths.AppDataLocation) para ver el listado de directorios
predeterminados.
Al configurar la variable de entorno PYQGIS_STARTUP con la ruta de un fichero Python existente, puede ejecutar
código Python justo antes de que la inicialización de QGIS se haya completado.
Este código se ejecutará antes de que se complete la inicialización de QGIS. Este método es muy útil para la
limpieza de sys.path, que puede tener rutas no deseables, o para aislar/cargar el entorno inicial sin necesidad de
un entorno virtual, por ejemplo, homebrew o MacPorts se instala en Mac.
A menudo es útil crear scripts para automatizar procesos. Con PyQGIS, esto es perfectamente posible — importe
el módulo qgis.core, inicialícelo y estará listo para el procesamiento.
O puede que desee crear una aplicación interactiva que utiliza la funcionalidad de los SIG — realizar mediciones,
exportar un mapa en PDF . . . El módulo qgis.gui proporciona varios componentes de interfaz gráfica, sobre
todo el widget de lienzo de mapa que se puede incorporar a la aplicación con soporte para zoom, paneo y/o
cualquier otra herramienta de mapa personalizada.
Las aplicaciones personalizadas de PyQGIS o los scripts independientes deben configurarse para localizar los
recursos QGIS, como la información de proyección y los proveedores para leer capas vectoriales y ráster. Los
recursos de QGIS se inician añadiendo unas pocas líneas al principio de su aplicación o script. El código para
inicializar QGIS para aplicaciones personalizadas y scripts independientes es similar. A continuación se propor-
cionan ejemplos de cada uno de ellos.
Nota: No utilice qgis.py como nombre para su script. Python no podrá importar los enlaces, ya que el nombre
del script será su sombra.
Para iniciar un script independiente, inicialice los recursos QGIS al principio del script:
# Load providers
qgs.initQgis()
qgs.exitQgis()
Primero se importa el módulo qgis.core y configuramos el prefijo de ruta. El prefijo de ruta es la ubicación
donde QGIS está instalado en su sistema. Se configura en el script llamando al método setPrefixPath. El
segundo argumento de setPrefixPath se establece a True, especificando las rutas predeterminadas que se
deben utilizar.
La ruta de instalación de QGIS varía según la plataforma; la forma más fácil de encontrarlo para su sistema es
utilizar la Desarrollar scripts en la consola de Python desde dentro de QGIS y mirar la salida de la ejecución de
QgsApplication.prefixPath().
Después de configurar la ruta del prefijo, guardamos una referencia a QgsApplication en la variable qgs.
El segundo argumento se establece en False, especificando que no planeamos usar la GUI ya que estamos
escribiendo un script independiente. Con‘‘QgsApplication‘‘ configurado, cargamos los proveedores de datos
QGIS y el registro de capas llamando al método qgs.initQgis(). Con QGIS inicializado, estamos listos para
escribir el resto del script. Por último, terminamos llamando a qgs.exitQgis() para eliminar los proveedores
de datos y el registro de capas de la memoria.
La única diferencia entre Usando PyQGIS en scripts individuales y una aplicación PyQGIS personalizada es el
segundo argumento al crear instancias de QgsApplication. Pase True en lugar de False para indicar que
se planea usar una GUI.
# load providers
qgs.initQgis()
4 Chapter 1. Introducción
PyQGIS developer cookbook, Versión 3.4
# algorithms, etc.
Ahora puede trabajar con la API de QGIS - carga de capas y realizar algún procesamiento o encender una GUI
con un lienzo de mapa. Las posibilidades son infinitas :-)
Necesita indicar a su sistema dónde buscar las bibliotecas QGIS y módulos de Python apropiados si no están en
una ubicación conocida - de lo contrario Python se quejará:
Esto se puede solucionar estableciendo la variable de entorno PYTHONPATH . En los siguientes comandos,
<qgispath> deberá ser reemplazado con su ruta de instalación de QGIS actual:
• en Linux: export PYTHONPATH=/<qgispath>/share/qgis/python
• en Windows: set PYTHONPATH=c:\<qgispath>\python
• en macOS: export PYTHONPATH=/<qgispath>/Contents/Resources/python
Ahora, se conoce la ruta de acceso a los módulos PyQGIS, pero dependen de las bibliotecas qgis_core y
qgis_gui (los módulos de Python solo sirven como contenedores). La ruta de acceso a estas bibliotecas puede
ser desconocida para el sistema operativo, y luego obtendrá un error de importación de nuevo (el mensaje puede
variar dependiendo del sistema):
Para solucionar, agregar los directorios donde residen las bibliotecas QGIS a la ruta de búsqueda del en-
lazador dinámico:
• en Linux: export LD_LIBRARY_PATH=/<qgispath>/lib
• en Windows: set PATH=C:\<qgispath>\bin;C:\<qgispath>\apps\<qgisrelease>\bin;
%PATH% donde <qgisrelease> debe ser reemplazado por el tipo de enlace apuntado (por ejemplo:
qgis-ltr, qgis, qgis-dev)
Estos comandos se pueden poner en un script de arranque que se encargará del inicio. Al implementar aplicaciones
personalizadas con PyQGIS, normalmente hay dos posibilidades:
• requiere que el usuario instale QGIS antes de instalar la aplicación. El instalador de la aplicación debe
buscar ubicaciones predeterminadas de las bibliotecas QGIS y permitir al usuario establecer la ruta si no se
encuentra. Este enfoque tiene la ventaja de ser más sencillo, sin embargo, requiere que el usuario haga más
pasos.
• paquete QGIS junto con su aplicación. Lanzar la aplicación puede ser más difícil y el paquete será más
grande, pero el usuario se salvará de la carga de descargar e instalar piezas adicionales de software.
Los dos modelos de implementación pueden ser mixtos. Puede proporcionar aplicaciones independientes en
Windows y macOS, pero para Linux dejar la instalación de SIG en manos del usuario y su administrador de
paquetes.
Se ha decidido por Python, ya que es uno de los lenguajes más favoritos para el scripting. Los enlaces PyQGIS
en QGIS 3 dependen de SIP y PyQt5. La razón para usar SIP en lugar del ampliamente utilizado SWIG es que
el código QGIS depende de las bibliotecas Qt. Los enlaces de Python para Qt (PyQt) se realizan utilizando SIP y
esto permite la integración perfecta de PyQGIS con PyQt.
6 Chapter 1. Introducción
CHAPTER 2
Cargar proyectos
Algunas veces se necesita cargar un proyecto existente desde un complemento o (más a menudo) al desarrollar
una aplicación autónoma QGIS Python (vea : Aplicaciones Python).
Para cargar un proyecto en la aplicación QGIS actual, debe crear una instancia de la clase QgsProject. Esta es
una clase singleton, por lo tanto se debe usar el método instance() para realizarlo. Puede llamar su método
read() y pasar la ruta para que el proyecto sea cargado:
# If you are not inside a QGIS console you first need to import
# qgis and PyQt classes you will use in this script as shown below:
from qgis.core import QgsProject
# Get the project instance
project = QgsProject.instance()
# Print the current project file name (might be empty in case no projects have
˓→been loaded)
print(project.fileName())
'/home/user/projects/my_qgis_project.qgs'
# Load another project
project.read('/home/user/projects/my_other_qgis_project.qgs')
print(project.fileName())
'/home/user/projects/my_other_qgis_project.qgs'
Si necesita hacer modificaciones a su proyecto (por ejemplo añadir o remover algunas capas) y guardar los cambios
realizados, puede llamar el método write() de su instancia de proyecto. El método write() también acepta
una ruta opcional para salvar el proyecto en una nueva localización:
Las funciones read() y write() retornan un valor booleano que puede utilizar para verificar si la operación
fue exitosa.
Nota: Si está desarrollando una aplicación QGIS autónoma, para poder mantener la sincronización
entre el proyecto cargado y el lienzo, debe instanciar una :class:”QgsLayerTreeMapCanvasBridge
<qgis.gui.QgsLayerTreeMapCanvasBridge>” al igual que en el ejemplo:
7
PyQGIS developer cookbook, Versión 3.4
bridge = QgsLayerTreeMapCanvasBridge( \
QgsProject.instance().layerTreeRoot(), canvas)
# Now you can safely load your project and see it in the canvas
project.read('/home/user/projects/my_other_qgis_project.qgs')
Cargar capas
Los fragmentos de códigos expuestos en esta página requieren las siguientes importaciones:
• Capas Vectoriales
• Capas ráster
• Instancia QgsProject
Vamos a abrir algunas capas con datos. QGIS reconoce capas vectoriales y ráster. Además, están disponibles tipos
de capas personalizadas, pero no se va a discutir de ellas aquí.
Para crear una instancia de capa vectorial, especifique el identificador de la fuente de datos y nombre de la capa,
así como también el nombre del proveedor:
El identificador de la fuente de datos es una cadena y se especifica a cada proveedor de datos vectoriales. El
nombre de la capa se utiliza en el widget de la lista de capa. Es importante validar si la capa se ha cargado
satisfactoriamente. Si no fue así, se devuelve una instancia de capa no válida.
9
PyQGIS developer cookbook, Versión 3.4
La forma más rápida para abrir y visualizar una capa vectorial en QGIS es usar el método addVectorLayer()
perteneciente a QgisInterface:
Esto crea una nueva capa y la añade al actual proyecto QGIS (haciéndola aparecer en el listado de capas) en un
solo paso. La función retorna la instancia de capa o None‘ si es que no puede cargarla.
La siguiente lista muestra cómo acceder a varias fuentes de datos utilizando los proveedores de datos vectoriales:
• Librería OGR ( Shapefile y muchos otros formatos) — la fuente de datos es la ruta hacia el archivo:
– Para Shapefile:
– Para dxf (tenga en cuenta las opciones internas en la fuente de datos uri):
uri = "/path/to/dxffile/file.dxf|layername=entities|geometrytype=Point"
vlayer = QgsVectorLayer(uri, "layer_name_you_like", "ogr")
• Base de datos PostGIS - la fuente de datos es una cadena de texto con toda la información necesaria para
crear una conexión con la base de datos PostgreSQL.
La clase QgsDataSourceUri puede generar esta cadena de texto para usted. Tenga en cuenta que QGIS
debe compilarse con el soporte de Postgres, o de lo contrario, este proveedor no estará disponible:
uri = QgsDataSourceUri()
# set host name, port, database name, username and password
uri.setConnection("localhost", "5432", "dbname", "johny", "xxx")
# set database schema, table name, geometry column and optionally
# subset (WHERE clause)
uri.setDataSource("public", "roads", "the_geom", "cityid = 2643")
Nota: El argumento False pasado a uri.uri(False) previene la expansión de los parámetros de con-
figuración de la autenticación. En caso de que no esté utilizando ninguna configuración para autenticación,
este argumento no hará ninguna diferencia.
• CSV u otros archivos de texto delimitados — para abrir un archivo con un punto y coma como delimitador,
con el campo «x» para la coordenada X y el campo «y» para la coordenada Y, usaría algo como esto:
Nota: La cadena de proveedor está estructurada como una dirección URL, por lo que la ruta de acceso
debe ir precedida de file://. También permite geometrías en formato WKT (texto bien conocido) como
alternativa a los campos “”x”” y “”y”“, y permite especificar el sistema de referencia de coordenadas. Por
ejemplo:
uri = "file:///some/path/file.csv?delimiter={}&crs=epsg:4723&wktField={}".
˓→format(";", "shape")
• Los archivos GPX — el proveedor de datos «gpx» lee los caminos, rutas y puntos de interés desde archivos
GPX. Para abrir un archivo, el tipo (caminos/ruta/punto de interés) se debe especificar como parte de la url:
uri = "path/to/gpx/file.gpx?type=track"
vlayer = QgsVectorLayer(uri, "layer name you like", "gpx")
• La base de datos SpatiaLite — De forma similar a las bases de datos PostGIS, :class:”QgsDataSourceUri
<qgis.core.QgsDataSourceUri>” puede ser utilizado para la generación de identificador de origen de datos:
uri = QgsDataSourceUri()
uri.setDatabase('/home/martin/test-2.3.sqlite')
schema = ''
table = 'Towns'
geom_column = 'Geometry'
uri.setDataSource(schema, table, geom_column)
display_name = 'Towns'
vlayer = QgsVectorLayer(uri.uri(), display_name, 'spatialite')
• Las geometrias basadas en WKB de MySQL, a través de OGR — la fuente de datos es la cadena de conexión
a la tabla:
uri = "MySQL:dbname,host=localhost,port=3306,user=root,
˓→password=xxx|layername=my_table"
uri = "http://localhost:8080/geoserver/wfs?srsname=EPSG:23030&typename=union&
˓→version=1.0.0&request=GetFeature&service=WFS",
params = {
'service': 'WFS',
'version': '1.0.0',
'request': 'GetFeature',
'typename': 'union',
'srsname': "EPSG:23030"
}
uri = 'http://localhost:8080/geoserver/wfs?' + urllib.unquote(urllib.
˓→urlencode(params))
Nota: Puede cambiar el origen de datos de una capa existente llamando a setDataSource() en una instancia
de QgsVectorLayer, como en el siguiente ejemplo:
Para acceder a un archivo raster, se utiliza la librería GDAL. Esta soporta un amplio rango de formatos de archivo.
En caso de que tenga problemas al abrir algún archivo, compruebe si es que su GDAL tiene soporte para el formato
en particular (no todos los formatos están disponibles de forma predeterminada). Para cargar un raster desde un
archivo, especifique el nombre del archivo y su nombre de visualización:
if not rlayer.isValid():
print("Layer failed to load!")
De manera similar a las capas vectoriales, las capas raster pueden ser cargadas utilizando la función addRaster-
Layer de un objeto perteneciente a QgisInterface
Esto crea una nueva capa y la añade al proyecto actual (haciendo que aparezca en la lista) en un solo paso.
Las capas ráster también se pueden crear desde el servicio WCS:
layer_name = 'modis'
uri = QgsDataSourceUri()
uri.setParam('url', 'http://demo.mapserver.org/cgi-bin/wcs')
uri.setParam("identifier", layer_name)
rlayer = QgsRasterLayer(str(uri.encodedUri()), 'my wcs layer', 'wcs')
Aquí está una descripción de los parámetros que el WCS URI puede contener:
El WCS URI se compone de pares calve=valor separadas por &. Es el mismo formato que la cadena de consulta
en la URL, codificada de la misma manera. QgsDataSourceUri debe utilizarse para construir el URI para
garantizar que los caracteres especiales se codifican correctamente.
• url (requerido) : URL del servidor WCS. No utilice la VERSION en el URL, porque cada versión del WCS
está usando nombre de parámetro diferente para la versión de GetCapabilities vea la versión del parámetro.
• identifier (requerido) : Nombre de la Cobertura
• time (opcional) : posición de tiempo o período de tiempo (beginPosition/endPosition[/timeResolution])
• format (opcional) : Nombre de formato admitido. El valor predeterminado es el primer formato compatible
con el nombre en tif o el primer formato compatible.
• crs (opcional): CRS en el formato AUTORIDAD:IDENTIFICADOR, p. ej. EPSG:4326. El valor predeter-
minado es EPSG:4326, si es que es compatible, o si no el primer CRS compatible.
• nombre de usuario (opcional): Nombre de usuario para la autenticación básica.
• contraseña (opcional): Contraseña para la autenticación básica.
• IgnoreGetMapUrl (opcional, hack): si se especifica (establecido en 1), ignore la dirección URL de GetCov-
erage anunciada por GetCapabilities. Puede ser necesario si un servidor no está configurado correctamente.
urlWithParams = 'url=http://irs.gis-lab.info/?layers=landsat&styles=&format=image/
˓→jpeg&crs=EPSG:4326'
Si desea utilizar las capas abiertas para la representación, no olvide agregarlas a la instancia de QgsProject.
La instancia QgsProject toma la posesión de las capas y más adelante, se puede acceder desde cualquier parte
de la aplicación mediante su identificador único . Cuando la capa se elimina del proyecto, también se elimina.
Las capas pueden ser eliminadas por el usuario en la interfaz QGIS, o a través de Python usando el método
removeMapLayer().
Añadir una capa al proyecto actual, se puede realizar, utilizando el método addMapLayer() :
QgsProject.instance().addMapLayer(rlayer)
# QgsProject.instance().removeMapLayer(layer_id)
QgsProject.instance().removeMapLayer(rlayer.id())
En el código anterior, el identificador de la capa es pasado (puede obtenerlo llamando el método id() que
pertenece a la capa), pero también puede hacerlo pasando el objeto capa en si mismo.
Para una lista de capas cargadas y sus identificadores, use el método mapLayers() :
QgsProject.instance().mapLayers()
• Detalles de la capa
• Renderizador
– Rásters de una sola banda
– Rásters multibanda
• Valores de consulta
Los extractos de códigos en esta página, requieren las siguientes importaciones, si es que estás trabajando fuera
de la consola pyqgis:
Una capa ráster está compuesta por una o más bandas ráster — denominadas como raster de banda única o
multibanda. Una banda representa una matriz de valores. Una imagen a color (p. ej. una fotografía aérea) es un
ráster que está constituido por bandas roja, azul y verde. Los rásteres de banda única, representan generalmente
variables continuas (p. ej. elevación) o variables discretas (p. ej. uso del suelo). En algunos casos, una capa ráster
viene con una paleta y los valores ráster se refieren a los colores almacenados en la paleta.
El código a continuación, asume que rlayer es un objeto de QgsRasterLayer .
rlayer = QgsProject.instance().mapLayersByName('srtm')[0]
# get the resolution of the raster in layer unit
rlayer.width(), rlayer.height()
15
PyQGIS developer cookbook, Versión 3.4
(919, 619)
# get the extent of the layer as QgsRectangle
rlayer.extent()
<QgsRectangle: 20.06856808199999875 -34.27001076999999896, 20.83945284300000012 -
˓→33.75077500700000144>
rlayer.rasterType()
0
# get the total band count of the raster
rlayer.bandCount()
1
# get all the available metadata as a QgsLayerMetadata object
rlayer.metadata()
'<qgis._core.QgsLayerMetadata object at 0x13711d558>'
4.2 Renderizador
Cuando una capa ráster es cargada, recibe en base a su tipo, el valor del renderizador de forma predeterminada.
Esto puede ser modificado tanto en las propiedades de capa o mediante programación.
Para consultar el actual renderizador:
rlayer.renderer()
<qgis._core.QgsSingleBandGrayRenderer object at 0x7f471c1da8a0>
rlayer.renderer().type()
'singlebandgray'
Para configurar un renderizador, use el método setRenderer de la clase QgsRasterLayer. Hay varias
clases de renderizador (derivadas de QgsRasterRenderer):
• QgsMultiBandColorRenderer
• QgsPalettedRasterRenderer
• QgsSingleBandColorDataRenderer
• QgsSingleBandGrayRenderer
• QgsSingleBandPseudoColorRenderer
Las capas ráster de banda única pueden ser dibujadas tanto en colores grises (valor menor = negro, valor alto =
blanco) o con un algoritmo pseudocolor que asigna colores a los valores. Rásters de banda única con una paleta
puedes ser dibujados usando la paleta. Las capas multibanda generalmente se dibujan asignando las bandas a
colores RGB. Otra posibilidad es usar una sola banda para dibujar.
Supongamos que queremos renderizar una capa ráster de una sola banda con colores que van del verde al
amarillo (correspondiente a los valores de píxel de 0 a 255). En la primera etapa prepararemos un objeto
QgsRasterShader y configuraremos su función shader:
fcn = QgsColorRampShader()
fcn.setColorRampType(QgsColorRampShader.Interpolated)
lst = [ QgsColorRampShader.ColorRampItem(0, QColor(0,255,0)),
QgsColorRampShader.ColorRampItem(255, QColor(255,255,0)) ]
fcn.setColorRampItemList(lst)
shader = QgsRasterShader()
shader.setRasterShaderFunction(fcn)
El sombreador asigna los colores según lo especificado por su mapa de colores. El mapa de colores es proveído
como una lista de valores de pixeles con colores asociados. Hay tres modos de interpolación:
• lineal (Interpolated): el color es linealmente interpolado desde las entradas en el mapa de colores, que
están por encima y por debajo de el valor de pixel.
• discreto (Discrete): el color es tomado desde la entrada más cercana con igual o mayor valor en el mapa
de colores.
• exacto (Exact): el color no es interpolado, solo los pixeles con un valor igual a las entradas del mapa de
colores serán dibujados.
En el segundo paso asociaremos el sombreador con una capa ráster:
renderer = QgsSingleBandPseudoColorRenderer(rlayer.dataProvider(), 1, shader)
rlayer.setRenderer(renderer)
El número 1 en el código anterior es el número de la banda (bandas ráster son indexadas de uno).
Finalmente debemos utilizar el método triggerRepaint para ver los resultados:
rlayer.triggerRepaint()
De forma predeterminada, QGIS asigna las tres primeras bandas a rojo, verde y azul para crear una imagen de color
(este es el estilo de dibujo “”MultiBandColor”“). En algunos casos, es posible que desee omitir esta configuración.
El código siguiente intercambia la banda roja (1) y la banda verde (2):
rlayer_multi = QgsProject.instance().mapLayersByName('multiband')[0]
rlayer_multi.renderer().setGreenBand(1)
rlayer_multi.renderer().setRedBand(2)
En caso de que sea necesaria solo una banda para la visualización del ráster, se puede elegir el dibujo de una banda
única, ya sea niveles grises o pseudocolor.
Se tiene que usar triggerRepaint para actualizar el mapa y ver el resultado:
rlayer_multi.triggerRepaint()
Los valores ráster se pueden consultar mediante el método sample de la clase QgsRasterDataProvider.
Debe especificar un QgsPointXY y el número de banda de la capa ráster que desea consultar. El método devuelve
una tupla con el valor y “”True”” o “”False”” dependiendo de los resultados:
val, res = rlayer.dataProvider().sample(QgsPointXY(20.50, -34), 1)
Otra forma de consultar los valores ráster es usando el método identify, que retorna un objeto
QgsRasterIdentifyResult.
ident = rlayer.dataProvider().identify(QgsPointXY(20.5, -34), QgsRaster.
˓→IdentifyFormatValue)
if ident.isValid():
print(ident.results())
En este caso, el método results retorna un diccionario, que tiene el índice de la banda como clave y los valores
de la banda como valor. Por ejemplo, algo como {1: 323.0}.
19
PyQGIS developer cookbook, Versión 3.4
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.
Puede recuperar información sobre los campos asociados a una capa vectorial llamando el método fields() de
un objeto de la clase QgsVectorLayer
# "layer" is a QgsVectorLayer instance
for field in layer.fields():
print(field.name(), field.typeName())
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.
layer = iface.activeLayer()
features = layer.getFeatures()
# fetch attributes
attrs = feature.attributes()
# attrs is a list. It contains all the attribute values of this feature
print(attrs)
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 selec-
cionadas 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()
# 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)
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:
selected_fid = []
layer.removeSelection()
print(feature['name'])
Alternativamente, los atributos pueden ser referidos por índice. Esto es un poco más rápido que usar el nombre.
Por ejemplo, para obtener el primer atributo:
print(feature[0])
Si solo necesita entidades seleccionadas, puede utilizar el método selectedFeatures() de la capa vectorial:
selection = layer.selectedFeatures()
print(len(selection))
for feature in selection:
# do whatever you need with the feature
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:
areaOfInterest = QgsRectangle(450290,400520, 450750,400780)
request = QgsFeatureRequest().setFilterRect(areaOfInterest)
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)
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.
# Only return selected fields to increase the "speed" of the request
request.setSubsetOfAttributes([0,2])
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')
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:
caps_string = layer.dataProvider().capabilitiesString()
# Print:
# 'Add Features, Delete Features, Change Attribute Values, Add Attributes,
# Delete Attributes, Rename Attributes, Fast Access to Features at ID,
# Presimplify Geometries, Presimplify Geometries with Validity Check,
# Transactions, Curved Geometries'
Mediante el uso de cualquiera de los métodos siguientes para la edición de capas vectoriales, los cambios se
confirman directamente en el almacén de datos subyacente (un archivo, una base de datos, etc.). En caso de que
desee realizar solo cambios temporales, vaya a la siguiente sección que explica cómo hacer modificaciones con la
edición de cambios de búfer.
Nota: Si está trabajando dentro de QGIS (ya sea desde la consola o desde un complemento), podría ser necesario
forzar un redibujo del lienzo del mapa para ver los cambios que ha realizado en la geometría, en el estilo o en los
atributos:
Cree algunas instancias de QgsFeature y pase una lista de ellas al método del proveedor addFeatures()
Devolverá dos valores: resultado (verdadero/falso) y lista de características agregadas (su identificador lo establece
el almacén de datos).
Para configurar los atributos de la entidad, puede inicializar la entidad pasando un objeto QgsFields (puede
obtenerlo del método fields() de la capa vectorial) o llamar a initAttributes() pasando el número de
campos que desea agregar.
Para eliminar algunas entidades, solo tiene que proporcionar una lista de identificaciones de entidades.
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.
Al editar vectores dentro de la aplicación QGIS, primero tiene que comenzar el modo de edición para una capa en
particular, luego hacer algunas modificaciones y finalmente confirmar (o revertir) los cambios. Todos los cambios
que realice no se escribirán hasta que los confirme — ellos permanecen en el búfer de edición en memoria de la
capa. Es posible utilizar esta funcionalidad también mediante programación — es sólo otro método para la edición
de capas vectoriales que complementa el uso directo de proveedores de datos. Utilice esta opción al proporcionar
algunas herramientas GUI para la edición de capas vectoriales, ya que esto permitirá al usuario decidir si desea
confirmar/revertir y permite el uso de deshacer/rehacer. Cuando se confirman los cambios, todos los cambios del
búfer de edición se guardan en el proveedor de datos.
Los métodos son similares a los que hemos visto en el proveedor, pero se llaman en el objeto QgsVectorLayer
en su lugar.
Para que estos métodos funcionen, la capa debe estar en modo de edición. Para iniciar el modo de edición, utilice el
método startEditing() Para detener la edición, utilice los métodos commitChanges() o rollBack().
El primero confirmará todos los cambios en el origen de datos, mientras que el segundo los descartará y no
modificará el origen de datos en absoluto.
Para averiguar si una capa está en modo de edición, utilice el método isEditable().
Aquí tiene algunos ejemplos que muestran cómo utilizar estos métodos de edición.
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:
layer.beginEditCommand("Feature triangulation")
if problem_occurred:
layer.destroyEditCommand()
return
layer.endEditCommand()
El método beginEditCommand() creará un comando interno de «activo» y registrará los cambios posteriores
en la capa vectorial. Con la llamada a el comando endEditCommand() se inserta en la pila de deshacer y el
usuario podrá deshacer/rehacerlo desde la GUI. En caso de que algo saliera mal al realizar los cambios, el método
destroyEditCommand() quitará el comando y revertirá todos los cambios realizados mientras este comando
estaba activo.
También puede utilizar la instrucción with edit(layer)- para encapsular la confirmación y la reversión en
un bloque de código más semántico, como se muestra en el ejemplo siguiente:
with edit(layer):
feat = next(layer.getFeatures())
feat[0] = 5
layer.updateFeature(feat)
Esto llamará automáticamente a commitChanges() al final. Si ocurre alguna excepción, hará rollBack() a
todos los cambios. En caso de que se encuentre un problema dentro de commitChanges() (cuando el método
devuelve False) se producirá una excepción QgsEditError.
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.
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()
Los índices espaciales pueden mejorar drásticamente el rendimiento del código si necesita realizar consultas fre-
cuentes 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 especi-
ficada y luego comparar esas distancias. Esto puede ser una tarea que consume mucho tiempo, especialmente si
necesita repetirse para varias ubicaciones. Si existe un índice espacial para la capa, la operación es mucho más
efectiva.
Piense en una capa sin un índice espacial como una guía telefónica en la que los números de teléfono no se
ordenan ni indexan. La única manera de encontrar el número de teléfono de una persona determinada es leer
desde el principio hasta que lo encuentres.
Los índices espaciales no se crean de forma predeterminada para una capa vectorial QGIS, pero puede crearlos
fácilmente. Esto es lo que tienes que hacer:
• crear índice espacial utilizando la clase QgsSpatialIndex():
index = QgsSpatialIndex()
• agregar entidades al índice — el índice toma el objeto QgsFeature y lo agrega a la estructura de datos in-
terna. Puede crear el objeto manualmente o usar uno de una llamada anterior al método getFeatures()
del proveedor.
index.insertFeature(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
El tercer parámetro (obligatorio) especifica la codificación de texto de salida. Sólo algunos controladores necesitan
esto para el funcionamiento correcto - Shapefile es uno de ellos (otros controladores ignorarán este parámetro).
Especificar la codificación correcta es importante si utiliza caracteres internacionales (no US-ASCII).
opts.layerName = 'my_new_layer_name'
error = QgsVectorFileWriter.writeAsVectorFormat(layer=vlayer,
fileName=gdb_path,
options=opts)
if error[0] == QgsVectorFileWriter.NoError:
print("success!")
else:
print(error)
También puede convertir campos para que sean compatibles con formatos diferentes usando
FieldValueConverter. Por ejemplo, para convertir tipos de variable matriz (por ejemplo, en Post-
gres) en un tipo texto, puede hacer lo siguiente:
LIST_FIELD_NAME = 'xxxx'
class ESRIValueConverter(QgsVectorFileWriter.FieldValueConverter):
""" create an instance of vector file writer, which will create the vector file.
Arguments:
1. path to new file (will fail if exists already)
2. encoding of the attributes
3. field map
4. geometry type - from WKBTYPE enum
5. layer's spatial reference (instance of
QgsCoordinateReferenceSystem) - optional
6. driver name for the output file """
if writer.hasError() != QgsVectorFileWriter.NoError:
print("Error when creating shapefile: ", w.errorMessage())
# add a feature
fet = QgsFeature()
fet.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(10,10)))
fet.setAttributes([1, "text"])
writer.addFeature(fet)
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 provee-
dor de memoria en el URI. La sintaxis es:
crs=definición Especifica el sistema de referencia de coordenadas, donde la definition puede ser cualquiera de
las formas aceptadas por QgsCoordinateReferenceSystem.createFromString
index=yes Especifica que el proveedor utilizará un índice espacial
campo Especifica un atributo de la capa. El atributo tiene un nombre y, opcionalmente, un tipo (entero, doble o
cadena), longitud y precisión. Puede haber múltiples definiciones de campo
El siguiente ejemplo de una URI incorpora todas estas opciones
"Point?crs=epsg:4326&field=id:integer&field=name:string(20)&index=yes"
# create layer
vl = QgsVectorLayer("Point", "temporary_points", "memory")
pr = vl.dataProvider()
# add fields
pr.addAttributes([QgsField("name", QVariant.String),
QgsField("age", QVariant.Int),
QgsField("size", QVariant.Double)])
vl.updateFields() # tell the vector layer to fetch changes from the provider
# add a feature
fet = QgsFeature()
fet.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(10,10)))
fet.setAttributes(["Johny", 2, 0.3])
pr.addFeatures([fet])
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()
print("Type:", renderer.type())
There are several known renderer types available in the QGIS core library:
There might be also some custom renderer types, so never make an assumption there are just these types. You can
query the application’s QgsRendererRegistry to find out currently available renderers:
print(QgsApplication.rendererRegistry().renderersList())
# Print:
['nullSymbol',
'singleSymbol',
'categorizedSymbol',
'graduatedSymbol',
'RuleRenderer',
'pointDisplacement',
'pointCluster',
'invertedPolygonRenderer',
'heatmapRenderer',
'25dRenderer']
It is possible to obtain a dump of a renderer contents in text form — can be useful for debugging
print(renderer.dump())
You can get the symbol used for rendering by calling symbol() method and change it with setSymbol()
method (note for C++ devs: the renderer takes ownership of the symbol.)
You can change the symbol used by a particular vector layer by calling setSymbol() passing an instance
of the appropriate symbol instance. Symbols for point, line and polygon layers can be created by calling
the createSimple() function of the corresponding classes QgsMarkerSymbol, QgsLineSymbol and
QgsFillSymbol.
The dictionary passed to createSimple() sets the style properties of the symbol.
For example you can replace the symbol used by a particular point layer by calling setSymbol() passing an
instance of a QgsMarkerSymbol, as in the following code example:
name indicates the shape of the marker, and can be any of the following:
• circle
• cuadrado
• cross
• rectangle
• Diamante
• pentagon
• triángulo
• equilateral_triangle
• star
• regular_star
• arrow
• filled_arrowhead
• x
To get the full list of properties for the first symbol layer of a symbol instance you can follow the example code:
print(layer.renderer().symbol().symbolLayers()[0].properties())
# Prints
{'angle': '0',
'color': '0,128,0,255',
'horizontal_anchor_point': '1',
'joinstyle': 'bevel',
'name': 'circle',
'offset': '0,0',
'offset_map_unit_scale': '0,0',
'offset_unit': 'MM',
'outline_color': '0,0,0,255',
'outline_style': 'solid',
'outline_width': '0',
'outline_width_map_unit_scale': '0,0',
'outline_width_unit': 'MM',
'scale_method': 'area',
'size': '2',
'size_map_unit_scale': '0,0',
'size_unit': 'MM',
'vertical_anchor_point': '1'}
When using a categorized renderer, you can query and set the attribute that is used for classification: use the
classAttribute() and setClassAttribute() methods.
To get a list of categories
Where value() is the value used for discrimination between categories, label() is a text used for category
description and symbol() method returns the assigned symbol.
The renderer usually stores also original symbol and color ramp which were used for the classification:
sourceColorRamp() and sourceSymbol() methods.
This renderer is very similar to the categorized symbol renderer described above, but instead of one attribute value
per class it works with ranges of values and thus can be used only with numerical attributes.
To find out more about ranges used in the renderer
ran.upperValue(),
ran.label(),
ran.symbol()
))
you can again use the classAttribute (to find the classification attribute name), sourceSymbol and
sourceColorRamp methods. Additionally there is the mode method which determines how the ranges were
created: using equal intervals, quantiles or some other method.
If you wish to create your own graduated symbol renderer you can do so as illustrated in the example snippet
below (which creates a simple two class arrangement)
myVectorLayer.setRenderer(myRenderer)
QgsProject.instance().addMapLayer(myVectorLayer)
For representation of symbols, there is QgsSymbol base class with three derived classes:
• QgsMarkerSymbol — for point features
• QgsLineSymbol — for line features
• QgsFillSymbol — for polygon features
Every symbol consists of one or more symbol layers (classes derived from QgsSymbolLayer). The symbol
layers do the actual rendering, the symbol class itself serves only as a container for the symbol layers.
Having an instance of a symbol (e.g. from a renderer), it is possible to explore it: the type method says whether
it is a marker, line or fill symbol. There is a dump method which returns a brief description of the symbol. To get
a list of symbol layers:
for i in range(symbol.symbolLayerCount()):
lyr = symbol.symbolLayer(i)
print("{}: {}".format(i, lyr.layerType()))
To find out symbol’s color use color method and setColor to change its color. With marker symbols addi-
tionally you can query for the symbol size and rotation with the size and angle methods. For line symbols the
width method returns the line width.
De forma predeterminada el tamaño y ancho están en milímetros, los ángulos en grados.
As said before, symbol layers (subclasses of QgsSymbolLayer) determine the appearance of the features. There
are several basic symbol layer classes for general use. It is possible to implement new symbol layer types and thus
arbitrarily customize how features will be rendered. The layerType() method uniquely identifies the symbol
layer class — the basic and default ones are SimpleMarker, SimpleLine and SimpleFill symbol layers
types.
You can get a complete list of the types of symbol layers you can create for a given symbol layer class with the
following code:
Salida:
EllipseMarker
FilledMarker
FontMarker
GeometryGenerator
SimpleMarker
SvgMarker
VectorField
The QgsSymbolLayerRegistry class manages a database of all available symbol layer types.
To access symbol layer data, use its properties() method that returns a key-value dictionary of properties
which determine the appearance. Each symbol layer type has a specific set of properties that it uses. Additionally,
there are the generic methods color, size, angle and width, with their setter counterparts. Of course size
and angle are available only for marker symbol layers and width for line symbol layers.
Imagine you would like to customize the way how the data gets rendered. You can create your own symbol layer
class that will draw the features exactly as you wish. Here is an example of a marker that draws red circles with
specified radius
class FooSymbolLayer(QgsMarkerSymbolLayer):
def layerType(self):
return "FooMarker"
def properties(self):
return { "radius" : str(self.radius) }
def clone(self):
return FooSymbolLayer(self.radius)
The layerType method determines the name of the symbol layer; it has to be unique among all symbol layers.
The properties method is used for persistence of attributes. The clone method must return a copy of the
symbol layer with all attributes being exactly the same. Finally there are rendering methods: startRender is
called before rendering the first feature, stopRender when the rendering is done, and renderPoint is called
to do the rendering. The coordinates of the point(s) are already transformed to the output coordinates.
For polylines and polygons the only difference would be in the rendering method: you would use
renderPolyline which receives a list of lines, while renderPolygon receives a list of points on the outer
ring as the first parameter and a list of inner rings (or None) as a second parameter.
Usually it is convenient to add a GUI for setting attributes of the symbol layer type to allow users to customize the
appearance: in case of our example above we can let user set circle radius. The following code implements such
widget
from qgis.gui import QgsSymbolLayerWidget
class FooSymbolLayerWidget(QgsSymbolLayerWidget):
def __init__(self, parent=None):
QgsSymbolLayerWidget.__init__(self, parent)
self.layer = None
# setup a simple UI
self.label = QLabel("Radius:")
self.spinRadius = QDoubleSpinBox()
self.hbox = QHBoxLayout()
self.hbox.addWidget(self.label)
self.hbox.addWidget(self.spinRadius)
self.setLayout(self.hbox)
self.connect(self.spinRadius, SIGNAL("valueChanged(double)"), \
self.radiusChanged)
def symbolLayer(self):
return self.layer
This widget can be embedded into the symbol properties dialog. When the symbol layer type is selected in symbol
properties dialog, it creates an instance of the symbol layer and an instance of the symbol layer widget. Then it
calls the setSymbolLayer method to assign the symbol layer to the widget. In that method the widget should
update the UI to reflect the attributes of the symbol layer. The symbolLayer method is used to retrieve the
symbol layer again by the properties dialog to use it for the symbol.
On every change of attributes, the widget should emit the changed() signal to let the properties dialog update
the symbol preview.
Now we are missing only the final glue: to make QGIS aware of these new classes. This is done by adding the
symbol layer to registry. It is possible to use the symbol layer also without adding it to the registry, but some
functionality will not work: e.g. loading of project files with the custom symbol layers or inability to edit the
layer’s attributes in GUI.
We will have to create metadata for the symbol layer
from qgis.core import QgsSymbol, QgsSymbolLayerAbstractMetadata,
˓→QgsSymbolLayerRegistry
class FooSymbolLayerMetadata(QgsSymbolLayerAbstractMetadata):
def __init__(self):
QgsSymbolLayerAbstractMetadata.__init__(self, "FooMarker", QgsSymbol.Marker)
QgsApplication.symbolLayerRegistry().addSymbolLayerType(FooSymbolLayerMetadata())
You should pass layer type (the same as returned by the layer) and symbol type (marker/line/fill) to the constructor
of the parent class. The createSymbolLayer() method takes care of creating an instance of symbol layer
with attributes specified in the props dictionary. And there is the createSymbolLayerWidget() method
which returns the settings widget for this symbol layer type.
El último pase es adicionar esta capa símbolo al registro — y estamos listos.
It might be useful to create a new renderer implementation if you would like to customize the rules how to select
symbols for rendering of features. Some use cases where you would want to do it: symbol is determined from a
combination of fields, size of symbols changes depending on current scale etc.
The following code shows a simple custom renderer that creates two marker symbols and chooses randomly one
of them for every feature
import random
from qgis.core import QgsWkbTypes, QgsSymbol, QgsFeatureRenderer
class RandomRenderer(QgsFeatureRenderer):
def __init__(self, syms=None):
QgsFeatureRenderer.__init__(self, "RandomRenderer")
self.syms = syms if syms else [QgsSymbol.defaultSymbol(QgsWkbTypes.
˓→geometryType(QgsWkbTypes.Point))]
def usedAttributes(self):
return []
def clone(self):
return RandomRenderer(self.syms)
def setColor1(self):
color = QColorDialog.getColor(self.r.syms[0].color(), self)
if not color.isValid(): return
self.r.syms[0].setColor(color)
self.btn1.setColor(self.r.syms[0].color())
def renderer(self):
return self.r
The constructor of the parent QgsFeatureRenderer class needs a renderer name (which has to be unique
among renderers). The symbolForFeature method is the one that decides what symbol will be used for a
particular feature. startRender and stopRender take care of initialization/finalization of symbol rendering.
The usedAttributes method can return a list of field names that the renderer expects to be present. Finally,
the clone method should return a copy of the renderer.
Like with symbol layers, it is possible to attach a GUI for configuration of the renderer. It has to be derived from
QgsRendererWidget. The following sample code creates a button that allows the user to set the first symbol
class RandomRendererWidget(QgsRendererWidget):
def __init__(self, layer, style, renderer):
QgsRendererWidget.__init__(self, layer, style)
if renderer is None or renderer.type() != "RandomRenderer":
self.r = RandomRenderer()
else:
self.r = renderer
# setup UI
self.btn1 = QgsColorButton()
self.btn1.setColor(self.r.syms[0].color())
self.vbox = QVBoxLayout()
self.vbox.addWidget(self.btn1)
self.setLayout(self.vbox)
self.connect(self.btn1, SIGNAL("clicked()"), self.setColor1)
def setColor1(self):
color = QColorDialog.getColor(self.r.syms[0].color(), self)
if not color.isValid(): return
self.r.syms[0].setColor(color)
self.btn1.setColor(self.r.syms[0].color())
def renderer(self):
return self.r
The constructor receives instances of the active layer (QgsVectorLayer), the global style (QgsStyle) and the
current renderer. If there is no renderer or the renderer has different type, it will be replaced with our new renderer,
otherwise we will use the current renderer (which has already the type we need). The widget contents should be
updated to show current state of the renderer. When the renderer dialog is accepted, the widget’s renderer
method is called to get the current renderer — it will be assigned to the layer.
The last missing bit is the renderer metadata and registration in registry, otherwise loading of layers with the
renderer will not work and user will not be able to select it from the list of renderers. Let us finish our Random-
Renderer example
class RandomRendererMetadata(QgsRendererAbstractMetadata):
def __init__(self):
QgsRendererAbstractMetadata.__init__(self, "RandomRenderer", "Random renderer")
QgsApplication.rendererRegistry().addRenderer(RandomRendererMetadata())
Similarly as with symbol layers, abstract metadata constructor awaits renderer name, name visible for users and
optionally name of renderer’s icon. The createRenderer method passes a QDomElement instance that can
be used to restore the renderer’s state from the DOM tree. The createRendererWidget method creates the
configuration widget. It does not have to be present or can return None if the renderer does not come with GUI.
To associate an icon with the renderer you can assign it in the QgsRendererAbstractMetadata construc-
tor as a third (optional) argument — the base class constructor in the RandomRendererMetadata __init__()
function becomes
QgsRendererAbstractMetadata.__init__(self,
"RandomRenderer",
"Random renderer",
QIcon(QPixmap("RandomRendererIcon.png", "png")))
The icon can also be associated at any later time using the setIcon method of the metadata class. The icon can
be loaded from a file (as shown above) or can be loaded from a Qt resource (PyQt5 includes .qrc compiler for
Python).
PENDIENTE:
• crear/modificar símbolos
• working with style (QgsStyle)
• working with color ramps (QgsColorRamp)
• exploring symbol layer and renderer registries
Manejo de Geometría
• Construcción de Geometría
• Acceso a Geometría
• Geometría predicados y Operaciones
The code snippets on this page need the following imports if you’re outside the pyqgis console:
Points, linestrings and polygons that represent a spatial feature are commonly referred to as geometries. In QGIS
they are represented with the QgsGeometry class.
A veces una geometría es realmente una colección simple (partes simples) geométricas. Tal geometría se llama
geometría de múltiples partes. Si contiene un tipo de geometría simple, lo llamamos un punto múltiple, lineas
múltiples o polígonos múltiples. Por ejemplo, un país consiste en múltiples islas que se pueden representar como
un polígono múltiple.
Las coordenadas de las geometrías pueden estar en cualquier sistema de referencia de coordenadas (SRC). Cuando
extrae características de una capa, las geometrías asociadas tendrán sus coordenadas en el SRC de la capa.
Description and specifications of all possible geometries construction and relationships are available in the OGC
Simple Feature Access Standards for advanced details.
41
PyQGIS developer cookbook, Versión 3.4
• desde coordenadas
gPnt = QgsGeometry.fromPointXY(QgsPointXY(1,1))
print(gPnt)
gLine = QgsGeometry.fromPolyline([QgsPoint(1, 1), QgsPoint(2, 2)])
print(gLine)
gPolygon = QgsGeometry.fromPolygonXY([[QgsPointXY(1, 1),
QgsPointXY(2, 2), QgsPointXY(2, 1)]])
print(gPolygon)
Coordinates are given using QgsPoint class or QgsPointXY class. The difference between these classes
is that QgsPoint supports M and Z dimensions.
A Polyline (Linestring) is represented by a list of points.
A Polygon is represented by a list of linear rings (i.e. closed linestrings). The first ring is the outer ring
(boundary), optional subsequent rings are holes in the polygon. Note that unlike some programs, QGIS will
close the ring for you so there is no need to duplicate the first point as the last.
Las geometrías multi-parte van un nivel más allá: multi-punto es una lista de puntos, multi-linea es una lista
de polilíneas y multi-polígono es una lista de polígonos.
• desde well-known text (WKT)
g = QgsGeometry()
wkb = bytes.fromhex("010100000000000000000045400000000000001440")
g.fromWkb(wkb)
First, you should find out the geometry type. The wkbType() method is the one to use. It returns a value from
the QgsWkbTypes.Type enumeration.
if gPnt.wkbType() == QgsWkbTypes.Point:
print(gPnt.wkbType())
# output: 1 for Point
if gLine.wkbType() == QgsWkbTypes.LineString:
print(gLine.wkbType())
if gPolygon.wkbType() == QgsWkbTypes.Polygon:
print(gPolygon.wkbType())
# output: 3 for Polygon
As an alternative, one can use the type() method which returns a value from the QgsWkbTypes.
GeometryType enumeration.
You can use the displayString() function to get a human readable geometry type.
print(QgsWkbTypes.displayString(gPnt.wkbType()))
# output: 'Point'
print(QgsWkbTypes.displayString(gLine.wkbType()))
# output: 'LineString'
print(QgsWkbTypes.displayString(gPolygon.wkbType()))
# output: 'Polygon'
Point
LineString
Polygon
There is also a helper function isMultipart() to find out whether a geometry is multipart or not.
To extract information from a geometry there are accessor functions for every vector type. Here’s an example on
how to use these accessors:
print(gPnt.asPoint())
# output: <QgsPointXY: POINT(1 1)>
print(gLine.asPolyline())
# output: [<QgsPointXY: POINT(1 1)>, <QgsPointXY: POINT(2 2)>]
print(gPolygon.asPolygon())
# output: [[<QgsPointXY: POINT(1 1)>, <QgsPointXY: POINT(2 2)>, <QgsPointXY:
˓→POINT(2 1)>, <QgsPointXY: POINT(1 1)>]]
Nota: The tuples (x,y) are not real tuples, they are QgsPoint objects, the values are accessible with x() and
y() methods.
For multipart geometries there are similar accessor functions: asMultiPoint(), asMultiPolyline() and
asMultiPolygon().
QGIS uses GEOS library for advanced geometry operations such as geometry predicates (contains(),
intersects(), . . . ) and set operations (combine(), difference(), . . . ). It can also compute geometric
properties of geometries, such as area (in the case of polygons) or lengths (for polygons and lines).
Let’s see an example that combines iterating over the features in a given layer and performing some geometric
computations based on their geometries. The below code will compute and print the area and perimeter of each
country in the countries layer within our tutorial QGIS project.
The following code assumes layer is a QgsVectorLayer object that has Polygon feature type.
# let's filter for countries that begin with Z, then get their features
query = '"name" LIKE \'Z%\''
features = layer.getFeatures(QgsFeatureRequest().setFilterExpression(query))
# now loop through the features, perform geometry computation and print the results
for f in features:
geom = f.geometry()
name = f.attribute('NAME')
print(name)
print('Area: ', geom.area())
print('Perimeter: ', geom.length())
Now you have calculated and printed the areas and perimeters of the geometries. You may however quickly notice
that the values are strange. That is because areas and perimeters don’t take CRS into account when computed using
the area() and length() methods from the QgsGeometry class. For a more powerful area and distance
calculation, the QgsDistanceArea class can be used, which can perform ellipsoid based calculations:
The following code assumes layer is a QgsVectorLayer object that has Polygon feature type.
d = QgsDistanceArea()
d.setEllipsoid('WGS84')
layer = QgsProject.instance().mapLayersByName('countries')[0]
# let's filter for countries that begin with Z, then get their features
query = '"name" LIKE \'Z%\''
features = layer.getFeatures(QgsFeatureRequest().setFilterExpression(query))
for f in features:
geom = f.geometry()
name = f.attribute('NAME')
print(name)
print("Perimeter (m):", d.measurePerimeter(geom))
print("Area (m2):", d.measureArea(geom))
# let's calculate and print the area again, but this time in square kilometers
print("Area (km2):", d.convertAreaMeasurement(d.measureArea(geom), QgsUnitTypes.
˓→AreaSquareKilometers))
Alternatively, you may want to know the distance and bearing between two points.
d = QgsDistanceArea()
d.setEllipsoid('WGS84')
Puede encontrar muchos ejemplos de algoritmos que se incluyen en QGIS y utilizan estos métodos para analizar
y transformar los datos vectoriales. Aquí hay algunos enlaces al código de algunos de ellos.
• Distance and area using the QgsDistanceArea class: Distance matrix algorithm
• Lines to polygons algorithm
Soporte de Proyecciones
If you’re outside the pyqgis console, the code snippets on this page need the following imports:
Coordinate reference systems (CRS) are encapsulated by the QgsCoordinateReferenceSystem class. In-
stances of this class can be created in several different ways:
• especificar SRC por su ID
assert crs.isValid()
45
PyQGIS developer cookbook, Versión 3.4
• create an invalid CRS and then use one of the create* functions to initialize it. In the following example
we use a Proj4 string to initialize the projection.
crs = QgsCoordinateReferenceSystem()
crs.createFromProj4("+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs")
assert crs.isValid()
It’s wise to check whether creation (i.e. lookup in the database) of the CRS has been successful: isValid()
must return True.
Note that for initialization of spatial reference systems QGIS needs to look up appropriate values in its internal
database srs.db. Thus in case you create an independent application you need to set paths correctly with
QgsApplication.setPrefixPath(), otherwise it will fail to find the database. If you are running the
commands from the QGIS Python console or developing a plugin you do not care: everything is already set up for
you.
Accessing spatial reference system information:
crs = QgsCoordinateReferenceSystem(4326)
Output:
QGIS CRS ID: 3452
PostGIS SRID: 4326
Description: WGS 84
Projection Acronym: longlat
Ellipsoid Acronym: WGS84
Proj4 String: +proj=longlat +datum=WGS84 +no_defs
Is geographic: True
Map units: 6
You can do transformation between different spatial reference systems by using the
QgsCoordinateTransform class. The easiest way to use it is to create a source and destination CRS
and construct a QgsCoordinateTransform instance with them and the current project. Then just repeatedly
call transform() function to do the transformation. By default it does forward transformation, but it is capable
to do also inverse transformation.
crsSrc = QgsCoordinateReferenceSystem(4326) # WGS 84
crsDest = QgsCoordinateReferenceSystem(32633) # WGS 84 / UTM zone 33N
xform = QgsCoordinateTransform(crsSrc, crsDest, QgsProject.instance())
Output:
Advertencia: Despite our constant efforts, information beyond this line may not be updated for QGIS 3. Refer
to https://qgis.org/pyqgis/master for the python API documentation or, give a hand to update the chapters you
know about. Thanks.
El widget del lienzo del mapa es probablemente el widget más importante dentro de QGIS porque muestra el
mapa integrado de capas de mapas superpuestos y permite la interacción con el mapa y las capas. El lienzo
muestra siempre una parte del mapa definido por el alcance del lienzo actual. La interacción se realiza mediante
el uso de herramientas de mapa: hay herramientas para desplazamiento, zum, la identificación de las capas, de
medida, para editar vectores y otros. Al igual que en otros programas de gráficos, siempre hay una herramienta
activa y el usuario puede cambiar entre las herramientas disponibles.
The map canvas is implemented with the QgsMapCanvas class in the qgis.gui module. The implementation
is based on the Qt Graphics View framework. This framework generally provides a surface and a view where
custom graphics items are placed and user can interact with them. We will assume that you are familiar enough
with Qt to understand the concepts of the graphics scene, view and items. If not, please read the overview of the
framework.
Whenever the map has been panned, zoomed in/out (or some other action that triggers a refresh), the map is
rendered again within the current extent. The layers are rendered to an image (using the QgsMapRendererJob
class) and that image is displayed on the canvas. The QgsMapCanvas class also controls refreshing of the
rendered map. Besides this item which acts as a background, there may be more map canvas items.
Typical map canvas items are rubber bands (used for measuring, vector editing etc.) or vertex markers. The
canvas items are usually used to give visual feedback for map tools, for example, when creating a new polygon,
the map tool creates a rubber band canvas item that shows the current shape of the polygon. All map canvas items
49
PyQGIS developer cookbook, Versión 3.4
are subclasses of QgsMapCanvasItem which adds some more functionality to the basic QGraphicsItem
objects.
Para resumir, la arquitectura del lienzo de mapa consiste en tres conceptos:
• lienzo de mapa — para la visualización del mapa
• Los elementos de lienzo de mapa — los elementos adicionales que se pueden desplegar en un lienzo de
mapa
• herramientas de mapa — para interactuar con el lienzo del mapa
El lienzo de mapa es un widget como cualquier otro widget Qt, por lo que utilizarlo es tan sencillo como crearlo
y mostrarlo
canvas = QgsMapCanvas()
canvas.show()
Esto produce una ventana independiente con el lienzo de mapa. Puede también ser incrustado en un widget
existente o ventana. Al utilizar archivo ui y Qt Designer, coloque un QWidget sobre el formulario y promuévalo a
una nueva clase: establezca QgsMapCanvas como nombre de clase y qgis.gui como archivo de encabezado.
La utilidad pyuic5 se hará cargo de ella. Esta es una forma conveniente de incrustar el lienzo. La otra posibilidad
es escribir manualmente el código para construir el lienzo del mapa y otros widgets (como hijos de una ventana
principal o diálogo) y crea un diseño.
Por defecto, el lienzo de mapa tiene un fondo negro y no utiliza anti-aliasing. Para establecer el fondo blanco y
habilitar el anti-aliasing para suavisar la presentación
canvas.setCanvasColor(Qt.white)
canvas.enableAntiAliasing(True)
(En caso de que se esté preguntando, Qt viene del modulo PyQt.QtCore y Qt.white es uno de lo que
predefine las instancias QColor.)
Ahora es tiempo de añadir algunas capas. Primero, abriremos una capa y lo añadiremos al proyecto actual. A
continuación, vamos a establecer la extensión del lienzo y la lista de capas para el lienzo
path_to_ports_layer = os.path.join(QgsProject.instance().homePath(),
"data", "ports", "ports.shp")
Después de ejecutar estos comandos, el lienzo debe mostrar la capa que se ha cargado.
Para mostrar algunos datos adicionales en la parte superior del mapa en el lienzo, utilice los elementos del lienzo
de mapa. Es posible crear clases de elementos del lienzo personalizada (cubiertas más abajo), sin embargo,
hay dos clases de elementos de lienzo útiles para mayor comodidad QgsRubberBand para dibujar polilíneas o
polígonos, y QgsVertexMarker para dibujar puntos. Ambos trabajan con coordenadas de mapa, por lo que la
figura se mueve/ se escala de forma automática cuando el lienzo está siendo desplazado o haciendo zum.
Para mostrar una polilínea
Tenga en cuenta que los puntos de polígonos no es una lista simple: de hecho, es una lista de anillos que contienen
lista de anillos del polígono: el primer anillo es el borde exterior, anillos adicionales (opcional) corresponden a
los agujeros en el polígono.
Las bandas elásticas permiten algún tipo de personalizacion, es decir, para cambiar su color o ancho de línea
r.setColor(QColor(0, 0, 255))
r.setWidth(3)
The canvas items are bound to the canvas scene. To temporarily hide them (and show them again), use the hide()
and show() combo. To completely remove the item, you have to remove it from the scene of the canvas
canvas.scene().removeItem(r)
(en C++ es posible simplemente eliminar el elemento, sin embargo en Python del r sería simplemente suprimir
la referencia y el objeto aún existirá ya que es propiedad del lienzo)
Rubber band can be also used for drawing points, but the QgsVertexMarker class is better suited for this
(QgsRubberBand would only draw a rectangle around the desired point).
You can use the vertex marker like this:
m = QgsVertexMarker(canvas)
m.setCenter(QgsPointXY(10,40))
This will draw a red cross on position [10,45]. It is possible to customize the icon type, size, color and pen width
For temporary hiding of vertex markers and removing them from canvas, use the same methods as for rubber
bands.
The following example constructs a window that contains a map canvas and basic map tools for map panning
and zooming. Actions are created for activation of each tool: panning is done with QgsMapToolPan, zooming
in/out with a pair of QgsMapToolZoom instances. The actions are set as checkable and later assigned to the
tools to allow automatic handling of checked/unchecked state of the actions – when a map tool gets activated, its
action is marked as selected and the action of the previous map tool is deselected. The map tools are activated
using setMapTool() method.
class MyWnd(QMainWindow):
def __init__(self, layer):
QMainWindow.__init__(self)
self.canvas = QgsMapCanvas()
self.canvas.setCanvasColor(Qt.white)
self.canvas.setExtent(layer.extent())
self.canvas.setLayers([layer])
self.setCentralWidget(self.canvas)
self.actionZoomIn.setCheckable(True)
self.actionZoomOut.setCheckable(True)
self.actionPan.setCheckable(True)
self.actionZoomIn.triggered.connect(self.zoomIn)
self.actionZoomOut.triggered.connect(self.zoomOut)
self.actionPan.triggered.connect(self.pan)
self.pan()
def zoomIn(self):
self.canvas.setMapTool(self.toolZoomIn)
def zoomOut(self):
self.canvas.setMapTool(self.toolZoomOut)
def pan(self):
self.canvas.setMapTool(self.toolPan)
You can try the above code in the Python console editor. To invoke the canvas window, add the following lines to
instantiate the MyWnd class. They will render the currently selected layer on the newly created canvas
w = MyWnd(iface.activeLayer())
w.show()
Puede escribir sus herramientas personalizadas, para implementar un comportamiento personalizado a las acciones
realizadas por los usuarios en el lienzo.
Map tools should inherit from the QgsMapTool, class or any derived class, and selected as active tools in the
canvas using the setMapTool() method as we have already seen.
Aquí esta un ejemplo de una herramienta de mapa para definir una extensión rectangular haciendo clic y arras-
trando en el lienzo. Cuando se define el rectángulo, imprime su limite de coordenadas en la consola. Utiliza los
elementos de la banda elástica descrita antes para mostrar el rectángulo seleccionado ya que se esta definiendo.
class RectangleMapTool(QgsMapToolEmitPoint):
def __init__(self, canvas):
self.canvas = canvas
QgsMapToolEmitPoint.__init__(self, self.canvas)
self.rubberBand = QgsRubberBand(self.canvas, True)
self.rubberBand.setColor(Qt.red)
self.rubberBand.setWidth(1)
self.reset()
def reset(self):
self.startPoint = self.endPoint = None
self.isEmittingPoint = False
self.rubberBand.reset(True)
self.endPoint = self.toMapCoordinates(e.pos())
self.showRect(self.startPoint, self.endPoint)
self.rubberBand.addPoint(point1, False)
self.rubberBand.addPoint(point2, False)
self.rubberBand.addPoint(point3, False)
self.rubberBand.addPoint(point4, True) # true to update canvas
self.rubberBand.show()
def rectangle(self):
if self.startPoint is None or self.endPoint is None:
return None
elif (self.startPoint.x() == self.endPoint.x() or \
self.startPoint.y() == self.endPoint.y()):
return None
def deactivate(self):
QgsMapTool.deactivate(self)
self.deactivated.emit()
import sys
from qgis.core import QgsApplication
from qgis.gui import QgsMapCanvas
def init():
a = QgsApplication(sys.argv, True)
QgsApplication.setPrefixPath('/home/martin/qgis/inst', True)
QgsApplication.initQgis()
return a
def show_canvas(app):
canvas = QgsMapCanvas()
canvas.show()
app.exec_()
app = init()
show_canvas(app)
• Representación Simple
• Representando capas con diferente SRC
• Output using print layout
– Exporting the layout
– Exporting a layout atlas
There are generally two approaches when input data should be rendered as a map: either do it quick way using
QgsMapRendererJob or produce more fine-tuned output by composing the map with the QgsLayout class.
The rendering is done creating a QgsMapSettings object to define the rendering options, and then constructing
a QgsMapRendererJob with those options. The latter is then used to create the resulting image.
Here’s an example:
image_location = os.path.join(QgsProject.instance().homePath(), "render.png")
render = QgsMapRendererParallelJob(options)
def finished():
55
PyQGIS developer cookbook, Versión 3.4
img = render.renderedImage()
# save the image; e.g. img.save("/Users/myuser/render.png","png")
img.save(image_location, "png")
print("saved")
render.finished.connect(finished)
render.start()
If you have more than one layer and they have a different CRS, the simple example above will probably not work:
to get the right values from the extent calculations you have to explicitly set the destination CRS
settings.setLayers(layers)
render.setDestinationCrs(layers[0].crs())
Print layout is a very handy tool if you would like to do a more sophisticated output than the simple rendering
shown above. It is possible to create complex map layouts consisting of map views, labels, legend, tables and
other elements that are usually present on paper maps. The layouts can be then exported to PDF, raster images or
directly printed on a printer.
The layout consists of a bunch of classes. They all belong to the core library. QGIS application has a convenient
GUI for placement of the elements, though it is not available in the GUI library. If you are not familiar with Qt
Graphics View framework, then you are encouraged to check the documentation now, because the layout is based
on it.
The central class of the layout is the QgsLayout class, which is derived from the Qt QGraphicsScene class. Let
us create an instance of it:
p = QgsProject()
layout = QgsLayout(p)
layout.initializeDefaults()
Now we can add various elements (map, label, . . . ) to the layout. All these objects are represented by classes that
inherit from the base QgsLayoutItem class.
Here’s a description of some of the main layout items that can be added to a layout.
• mapa — este elemento dice a las bibliotecas dónde ponen el propio mapa. Aquí creamos un mapa y esti-
ramos sobre el tamaño de papel
map = QgsLayoutItemMap(layout)
layout.addItem(map)
• etiqueta — permite mostrar etiquetas. Es posible modificar su letra, color, alineación y margen.
label = QgsLayoutItemLabel(layout)
label.setText("Hello world")
label.adjustSizeToText()
layout.addItem(label)
• leyenda
legend = QgsLayoutItemLegend(layout)
legend.setLinkedMap(map) # map is an instance of QgsLayoutItemMap
layout.addItem(legend)
• barra de escala
item = QgsLayoutItemScaleBar(layout)
item.setStyle('Numeric') # optionally modify the style
item.setLinkedMap(map) # map is an instance of QgsLayoutItemMap
item.applyDefaultSize()
layout.addItem(item)
• flecha
• imagen
• basic shape
• nodes based shape
polygon = QPolygonF()
polygon.append(QPointF(0.0, 0.0))
polygon.append(QPointF(100.0, 0.0))
polygon.append(QPointF(200.0, 100.0))
polygon.append(QPointF(100.0, 200.0))
props = {}
props["color"] = "green"
props["style"] = "solid"
props["style_border"] = "solid"
props["color_border"] = "black"
props["width_border"] = "10.0"
props["joinstyle"] = "miter"
symbol = QgsFillSymbol.createSimple(props)
polygonItem.setSymbol(symbol)
• tabla
Once an item is added to the layout, it can be moved and resized:
A frame is drawn around each item by default. You can remove it as follows:
Besides creating the layout items by hand, QGIS has support for layout templates which are essentially composi-
tions with all their items saved to a .qpt file (with XML syntax).
Once the composition is ready (the layout items have been created and added to the composition), we can proceed
to produce a raster and/or vector output.
base_path = os.path.join(QgsProject.instance().homePath()
pdf_path = os.path.join(base_path, "output.pdf")
exporter = QgsLayoutExporter(layout)
exporter.exportToPdf(pdf_path, QgsLayoutExporter.PdfExportSettings())
Use the exportToImage() in case you want to export to an image instead of a PDF file.
If you want to export all pages from a layout that has the atlas option configured and enabled, you need to use the
atlas() method in the exporter (QgsLayoutExporter) with small adjustments. In the following example,
the pages are exported to PNG images:
Notice that the outputs will be saved in the base path folder, using the output filename expression configured on
atlas.
The code snippets on this page needs the following imports if you’re outside the pyqgis console:
• Análisis de expresiones
• Evaluar expresiones
– Expresiones Basicas
– Expresiones con caracteristicas
– Filtering a layer with expressions
• Handling expression errors
QGIS tiene apoyo para el análisis de expresiones parecidas al SQL. Solo se reconoce un pequeño subconjunto de
sintaxis SQL. Las expresiones pueden ser evaluados ya sea como predicados booleanos (regresando Verdadero o
Falso ) o como funciones (regresando un valor escalar). Vea vector_expressions en el Manual del usuario para
obtener una lista completa de las funciones disponibles.
Se le da apoyo a tres tipos:
• numero - números enteros y números con decimales, e.j. 123, 3.14
• cadena - se tiene que encerrar en comas individuales: 'hola mundo'
• columna de referencia - cuando se evalúa, la referencia se substituye con el valor actual del campo. Los
nombres no se escapan.
Los siguientes operadores están disponibles:
• operadores aritméticos: «+». «-«, «/», ^
• paréntesis: para hacer cumplir la precedencia del operador: (1 + 1) * 3
• unario mas y menos: -12, +5
• funciones matemáticas: sqrt, sin, cos, tan, asin, acos, atan
• funciones de conversión: to_int, to_real, to_string, to_date
• funciones geométricas: $area, $length
• funciones de manejo de geometría: $x, $y, $geometry, num_geometries, centroid
Se apoya las siguientes predicciones:
59
PyQGIS developer cookbook, Versión 3.4
The following example shows how to check if a given expression can be parsed correctly:
Expressions can be used in different contexts, for example to filter features or to compute new field values. In
any case, the expression has to be evaluated. That means that its value is computed by performing the specified
computational steps, which can range from simple arithmetic to aggregate expressions.
To evaluate an expression against a feature, a QgsExpressionContext object has to be created and passed to
the evaluate function in order to allow the expression to access the feature’s field values.
The following example shows how to create a feature with a field called «Column» and how to add this feature to
the expression context.
fields = QgsFields()
field = QgsField('Column')
fields.append(field)
feature = QgsFeature()
feature.setFields(fields)
feature.setAttribute(0, 99)
exp = QgsExpression('"Column"')
context = QgsExpressionContext()
context.setFeature(feature)
assert(exp.evaluate(context) == 99)
The following is a more complete example of how to use expressions in the context of a vector layer, in order to
compute new field values:
vl.updateExtents()
QgsProject.instance().addMapLayer(vl)
# QgsExpressionContextUtils.globalProjectLayerScopes() is a convenience
# function that adds the global, project, and layer scopes all at once.
# Alternatively, those scopes can also be added manually. In any case,
# it is important to always go from “most generic” to “most specific”
# scope, i.e. from global to project to layer
context = QgsExpressionContext()
context.appendScopes(QgsExpressionContextUtils.globalProjectLayerScopes(vl))
with edit(vl):
for f in vl.getFeatures():
context.setFeature(f)
f['Rev. per employee'] = expression1.evaluate(context)
f['Sum'] = expression2.evaluate(context)
f['Fun'] = expression3.evaluate(context)
vl.updateFeature(f)
print( f['Sum'])
El siguiente ejemplo se puede utilizar para filtra capas y regresar cualquier característica que empata con el predi-
cado.
layer = QgsVectorLayer("Point?field=Test:integer",
"addfeat", "memory")
layer.startEditing()
for i in range(10):
feature = QgsFeature()
feature.setAttributes([i])
assert(layer.addFeature(feature))
layer.commitChanges()
matches = 0
for f in layer.getFeatures(request):
matches += 1
assert(matches == 7)
value = exp.evaluate()
if exp.hasEvalError():
raise ValueError(exp.evalErrorString())
The code snippets on this page needs the following imports if you’re outside the pyqgis console:
Advertencia: Despite our constant efforts, information beyond this line may not be updated for QGIS 3. Refer
to https://qgis.org/pyqgis/master for the python API documentation or, give a hand to update the chapters you
know about. Thanks.
Muchas veces es útil para un complemento guardar algunas variables para que el usuario no tenga que introducir
o seleccionar de nuevo la próxima vez que el complemento se ejecute.
Estas variables se pueden guardar y recuperar con ayuda de Qt y QGIS API. Para cada variable, se debe escoger
una clave que será utilizada para acceder a la variable — para el color favorito del usuario podría utilizarse la
clave «favourite_color» o cualquier otra cadena que tenga sentido. Es recomendable dar un poco de estructura al
nombrar las claves.
We can differentiate between several types of settings:
• global settings — they are bound to the user at a particular machine. QGIS itself stores a lot of global
settings, for example, main window size or default snapping tolerance. Settings are handled using the
QgsSettings class, through for example the setValue() and value() methods.
Here you can see an example of how these methods are used.
def store():
s = QgsSettings()
s.setValue("myplugin/mytext", "hello world")
s.setValue("myplugin/myint", 10)
s.setValue("myplugin/myreal", 3.14)
def read():
s = QgsSettings()
mytext = s.value("myplugin/mytext", "default text")
myint = s.value("myplugin/myint", 123)
myreal = s.value("myplugin/myreal", 2.71)
nonexistent = s.value("myplugin/nonexistent", None)
print(mytext)
print(myint)
print(myreal)
print(nonexistent)
63
PyQGIS developer cookbook, Versión 3.4
The second parameter of the value() method is optional and specifies the default value that is returned if
there is no previous value set for the passed setting name.
• project settings — vary between different projects and therefore they are connected with a project file.
Map canvas background color or destination coordinate reference system (CRS) are examples — white
background and WGS84 might be suitable for one project, while yellow background and UTM projection
are better for another one.
An example of usage follows.
proj = QgsProject.instance()
# store values
proj.writeEntry("myplugin", "mytext", "hello world")
proj.writeEntry("myplugin", "myint", 10)
proj.writeEntry("myplugin", "mydouble", 0.01)
proj.writeEntry("myplugin", "mybool", True)
# read values (returns a tuple with the value, and a status boolean
# which communicates whether the value retrieved could be converted to
# its type, in these cases a string, an integer, a double and a boolean
# respectively)
As you can see, the writeEntry() method is used for all data types, but several methods exist for reading
the setting value back, and the corresponding one has to be selected for each data type.
• map layer settings — these settings are related to a particular instance of a map layer with a project. They
are not connected with underlying data source of a layer, so if you create two map layer instances of one
shapefile, they will not share the settings. The settings are stored inside the project file, so if the user opens
the project again, the layer-related settings will be there again. The value for a given setting is retrieved
using the customProperty() method, and can be set using the setCustomProperty() one.
vlayer = QgsVectorLayer()
# save a value
vlayer.setCustomProperty("mytext", "hello world")
Esta sección muestra algunos métodos y elementos que deberían utilizarse para comunicarse con el usuario, con
el objetivo de mantener la consistencia en la Interfaz del Usuario.
Utilizar las bandejas de mensajes puede ser una mala idea desde el punto de vista de la experiencia de un usuario.
Para mostrar una pequeña línea de información o mensajes de advertencia/error, la barrar de mensajes de QGIS
suele ser una mejor opción.
Utilizar la referencia a la interfaz objeto de QGIS, puede mostrar un mensaje en la barra de mensajes con el
siguiente código
65
PyQGIS developer cookbook, Versión 3.4
The examples above show an error bar, but the level parameter can be used to creating warning messages or
info messages, using the Qgis.MessageLevel enumeration. You can use up to 4 different levels:
0. Info
1. Warning
2. Critical
3. Success
Se puede añadir complementos a la barra de mensajes, como por ejemplo un botón para mostrar más información
def showError():
pass
Incluso puedes utilizar una barra de mensajes en tu propio cuadro de diálogo para no tener que mostrar la bandeja
de mensajes o si no tiene sentido mostrarla en la pantalla principal de QGIS.
class MyDialog(QDialog):
def __init__(self):
QDialog.__init__(self)
self.bar = QgsMessageBar()
self.bar.setSizePolicy( QSizePolicy.Minimum, QSizePolicy.Fixed )
self.setLayout(QGridLayout())
self.layout().setContentsMargins(0, 0, 0, 0)
self.buttonbox = QDialogButtonBox(QDialogButtonBox.Ok)
self.buttonbox.accepted.connect(self.run)
self.layout().addWidget(self.buttonbox, 0, 0, 2, 1)
self.layout().addWidget(self.bar, 0, 0, 1, 1)
def run(self):
self.bar.pushMessage("Hello", "World", level=Qgis.Info)
myDlg = MyDialog()
myDlg.show()
Las barras de progreso también pueden ponerse en la barra de Mensajes de QGIS, ya que, como hemos visto,
admite complementos. Este es un ejemplo que puedes probar en la consola.
import time
from qgis.PyQt.QtWidgets import QProgressBar
from qgis.PyQt.QtCore import *
progressMessageBar = iface.messageBar().createMessage("Doing something boring...")
progress = QProgressBar()
progress.setMaximum(10)
progress.setAlignment(Qt.AlignLeft|Qt.AlignVCenter)
progressMessageBar.layout().addWidget(progress)
iface.messageBar().pushWidget(progressMessageBar, Qgis.Info)
for i in range(10):
time.sleep(1)
progress.setValue(i + 1)
iface.messageBar().clearWidgets()
Also, you can use the built-in status bar to report progress, as in the next example:
vlayer = QgsProject.instance().mapLayersByName("countries")[0]
count = vlayer.featureCount()
features = vlayer.getFeatures()
iface.statusBarIface().showMessage("Processed {} %".format(int(percent)))
iface.statusBarIface().clearMessage()
12.3 Registro
Se puede utilizar el sistema de registro de QGIS para registrar toda la información de la ejecución de su código
que se quiera guardar.
Advertencia: Use of the Python print statement is unsafe to do in any code which may be multithreaded.
This includes expression functions, renderers, symbol layers and Processing algorithms (amongst others).
In these cases you should always use thread safe classes (QgsLogger or QgsMessageLog) instead.
Nota: You can see the output of the QgsMessageLog in the log_message_panel
Nota:
• QgsLogger is for messages for debugging / developers (i.e. you suspect they are triggered by some broken
code)
• QgsMessageLog is for messages to investigate issues by sysadmins (e.g. to help a sysadmin to fix con-
figurations)
Infraestructura de autenticación
• Introducción
• Glosario
• QgsAuthManager the entry point
– Init the manager and set the master password
– Populate authdb with a new Authentication Configuration entry
Advertencia: Despite our constant efforts, information beyond this line may not be updated for QGIS 3. Refer
to https://qgis.org/pyqgis/master for the python API documentation or, give a hand to update the chapters you
know about. Thanks.
69
PyQGIS developer cookbook, Versión 3.4
13.1 Introducción
La referencia de Usuario de la infraestructura de Autenticación puede ser leída en el Manual del Usuario en el
párrafo authentication_overview.
En este capítulo se describen las mejores prácticas para usar el sistema de autenticación desde una perspectiva de
desarrollador.
Most of the following snippets are derived from the code of Geoserver Explorer plugin and its tests. This is the
first plugin that used Authentication infrastructure. The plugin code and its tests can be found at this link. Other
good code reference can be read from the authentication infrastructure tests code.
13.2 Glosario
Here are some definition of the most common objects treated in this chapter.
Contraseña maestra Password to allow access and decrypt credential stored in the QGIS Authentication DB
Authentication Database A Master Password crypted sqlite db qgis-auth.db where Authentication Con-
figuration are stored. e.g user/password, personal certificates and keys, Certificate Authorities
Autenticación BD Authentication Database
Authentication Configuration A set of authentication data depending on Authentication Method. e.g Basic
authentication method stores the couple of user/password.
Configuración de autenticación Authentication Configuration
Método de autenticación A specific method used to get authenticated. Each method has its own protocol used
to gain the authenticated level. Each method is implemented as shared library loaded dynamically during
QGIS authentication infrastructure init.
The QgsAuthManager singleton is the entry point to use the credentials stored in the QGIS encrypted Authen-
tication DB, i.e. the qgis-auth.db file under the active user profile folder.
This class takes care of the user interaction: by asking to set master password or by transparently using it to access
crypted stored info.
The following snippet gives an example to set master password to open the access to the authentication settings.
Code comments are important to understand the snippet.
authMgr = QgsAuthManager.instance()
# check if QgsAuthManager has been already initialized... a side effect
# of the QgsAuthManager.init() is that AuthDbPath is set.
# QgsAuthManager.init() is executed during QGIS application init and hence
# you do not normally need to call it directly.
if authMgr.authenticationDbPath():
# already initilised => we are inside a QGIS app.
if authMgr.masterPasswordIsSet():
msg = 'Authentication master password not recognized'
assert authMgr.masterPasswordSame( "your master password" ), msg
else:
msg = 'Master password could not be set'
# The verify parameter check if the hash of the password was
# already saved in the authentication db
Any stored credential is a Authentication Configuration instance of the QgsAuthMethodConfig class accessed
using a unique string like the following one:
authcfg = 'fm1s770'
that string is generated automatically when creating an entry using QGIS API or GUI.
QgsAuthMethodConfig is the base class for any Authentication Method. Any Authentication Method sets
a configuration hash map where authentication informations will be stored. Hereafter an useful snippet to store
PKI-path credentials for an hypothetic alice user:
authMgr = QgsAuthManager.instance()
# set alice PKI data
p_config = QgsAuthMethodConfig()
p_config.setName("alice")
p_config.setMethod("PKI-Paths")
p_config.setUri("https://example.com")
p_config.setConfig("certpath", "path/to/alice-cert.pem" ))
p_config.setConfig("keypath", "path/to/alice-key.pem" ))
# check if method parameters are correctly set
assert p_config.isValid()
Authentication Methods are loaded dynamically during authentication manager init. The list of Authentication
method can vary with QGIS evolution, but the original list of available methods is:
1. Basic User and password authentication
2. Identity-Cert Identity certificate authentication
3. PKI-Paths PKI paths authentication
4. PKI-PKCS#12 PKI PKCS#12 Autenticación
The above strings are that identify authentication methods in the QGIS authentication system. In Development
section is described how to create a new c++ Authentication Method.
Populate Authorities
authMgr = QgsAuthManager.instance()
# add authorities
cacerts = QSslCertificate.fromPath( "/path/to/ca_chains.pem" )
assert cacerts is not None
# store CA
authMgr.storeCertAuthorities(cacerts)
# and rebuild CA caches
authMgr.rebuildCaCertsCache()
authMgr.rebuildTrustedCaCertsCache()
Advertencia: Due to QT4/OpenSSL interface limitation, updated cached CA are exposed to OpenSsl only
almost a minute later. Hope this will be solved in QT5 authentication infrastructure.
A convenience class to pack PKI bundles composed on SslCert, SslKey and CA chain is the QgsPkiBundle
class. Hereafter a snippet to get password protected:
We can remove an entry from Authentication Database using it’s authcfg identifier with the following snippet:
authMgr = QgsAuthManager.instance()
authMgr.removeAuthenticationConfig( "authCfg_Id_to_remove" )
The best way to use an Authentication Config stored in the Authentication DB is referring it with the unique
identifier authcfg. Expanding, means convert it from an identifier to a complete set of credentials. The best
practice to use stored Authentication Configs, is to leave it managed automatically by the Authentication manager.
The common use of a stored configuration is to connect to an authentication enabled service like a WMS or WFS
or to a DB connection.
Nota: Take into account that not all QGIS data providers are integrated with the Authentication infrastruc-
ture. Each authentication method, derived from the base class QgsAuthMethod and support a different set of
Providers. For example the certIdentity () method supports the following list of providers:
For example, to access a WMS service using stored credentials identified with authcfg = 'fm1s770', we
just have to use the authcfg in the data source URL like in the following snippet:
authCfg = 'fm1s770'
quri = QgsDataSourceURI()
quri.setParam("layers", 'usa:states')
quri.setParam("styles", '')
quri.setParam("format", 'image/png')
quri.setParam("crs", 'EPSG:4326')
quri.setParam("dpiMode", '7')
quri.setParam("featureCount", '10')
quri.setParam("authcfg", authCfg) # <---- here my authCfg url parameter
quri.setParam("contextualWMSLegend", '0')
quri.setParam("url", 'https://my_auth_enabled_server_ip/wms')
rlayer = QgsRasterLayer(quri.encodedUri(), 'states', 'wms')
In the upper case, the wms provider will take care to expand authcfg URI parameter with credential just before
setting the HTTP connection.
Advertencia: The developer would have to leave authcfg expansion to the QgsAuthManager, in this
way he will be sure that expansion is not done too early.
Usually an URI string, built using the QgsDataSourceURI class, is used to set a data source in the following
way:
Nota: The False parameter is important to avoid URI complete expansion of the authcfg id present in the
URI.
Other example can be read directly in the QGIS tests upstream as in test_authmanager_pki_ows or
test_authmanager_pki_postgres.
Many third party plugins are using httplib2 to create HTTP connections instead of integrating with
QgsNetworkAccessManager and its related Authentication Infrastructure integration. To facilitate this inte-
gration an helper python function has been created called NetworkAccessManager. Its code can be found
here.
This helper class can be used as in the following snippet:
try:
response, content = http.request( "my_rest_url" )
except My_FailedRequestError, e:
# Handle exception
pass
In this paragraph are listed the available GUIs useful to integrate authentication infrastructure in custom interfaces.
If it’s necessary to select a Authentication Configuration from the set stored in the Authentication DB it is available
in the GUI class QgsAuthConfigSelect <qgis.gui.QgsAuthConfigSelect>.
The above example is taken from the QGIS source code The second parameter of the GUI constructor refers to
data provider type. The parameter is used to restrict the compatible Authentication Methods with the specified
provider.
The complete GUI used to manage credentials, authorities and to access to Authentication utilities is managed by
the QgsAuthEditorWidgets class.
and can be used as in the following snippet:
14.1 Introducción
Background processing using threads is a way to maintain a responsive user interface when heavy processing is
going on. Tasks can be used to achieve threading in QGIS.
A task (QgsTask) is a container for the code to be performed in the background, and the task manager
(QgsTaskManager) is used to control the running of the tasks. These classes simplify background process-
ing in QGIS by providing mechanisms for signaling, progress reporting and access to the status for background
processes. Tasks can be grouped using subtasks.
The global task manager (found with QgsApplication.taskManager()) is normally used. This means
that your tasks may not be the only tasks that are controlled by the task manager.
There are several ways to create a QGIS task:
• Create your own task by extending QgsTask
class SpecialisedTask(QgsTask):
Advertencia: Any background task (regardless of how it is created) must NEVER perform any GUI based op-
erations, such as creating new widgets or interacting with existing widgets. Qt widgets must only be accessed
or modified from the main thread. Attempting to use them from background threads will result in crashes.
Dependencies between tasks can be described using the addSubTask function of QgsTask. When a depen-
dency is stated, the task manager will automatically determine how these dependencies will be executed. Wher-
ever possible dependencies will be executed in parallel in order to satisfy them as quickly as possible. If a task
77
PyQGIS developer cookbook, Versión 3.4
on which another task depends is canceled, the dependent task will also be canceled. Circular dependencies can
make deadlocks possible, so be careful.
If a task depends on a layer being available, this can be stated using the setDependentLayers function of
QgsTask. If a layer on which a task depends is not available, the task will be canceled.
Once the task has been created it can be scheduled for running using the addTask function of the task manager.
Adding a task to the manager automatically transfers ownership of that task to the manager, and the manager will
cleanup and delete tasks after they have executed. The scheduling of the tasks is influenced by the task priority,
which is set in addTask.
The status of tasks can be monitored using QgsTask and QgsTaskManager signals and functions.
14.2 Ejemplos
In this example RandomIntegerSumTask extends QgsTask and will generate 100 random integers between
0 and 500 during a specified period of time. If the random number is 42, the task is aborted and an exception
is raised. Several instances of RandomIntegerSumTask (with subtasks) are generated and added to the task
manager, demonstrating two types of dependencies.
import random
from time import sleep
MESSAGE_CATEGORY = 'RandomIntegerSumTask'
class RandomIntegerSumTask(QgsTask):
"""This shows how to subclass QgsTask"""
def __init__(self, description, duration):
super().__init__(description, QgsTask.CanCancel)
self.duration = duration
self.total = 0
self.iterations = 0
self.exception = None
def run(self):
"""Here you implement your heavy lifting.
Should periodically test for isCanceled() to gracefully
abort.
This method MUST return True or False.
Raising exceptions will crash QGIS, so we handle them
internally and raise them in self.finished
"""
QgsMessageLog.logMessage('Started task "{}"'.format(
self.description()),
MESSAGE_CATEGORY, Qgis.Info)
wait_time = self.duration / 100
for i in range(100):
sleep(wait_time)
# use setProgress to report progress
self.setProgress(i)
arandominteger = random.randint(0, 500)
self.total += arandominteger
self.iterations += 1
# check isCanceled() to handle cancellation
if self.isCanceled():
return False
14.2. Ejemplos 79
PyQGIS developer cookbook, Versión 3.4
longtask.addSubTask(shortestsubtask)
QgsApplication.taskManager().addTask(longtask)
QgsApplication.taskManager().addTask(shorttask)
QgsApplication.taskManager().addTask(minitask)
Create a task from a function (doSomething in this example). The first parameter of the function will hold the
QgsTask for the function. An important (named) parameter is on_finished, that specifies a function that will
be called when the task has completed. The doSomething function in this example has an additional named
parameter wait_time.
import random
from time import sleep
MESSAGE_CATEGORY = 'TaskFromFunction'
def stopped(task):
QgsMessageLog.logMessage(
'Task "{name}" was canceled'.format(
name=task.description()),
MESSAGE_CATEGORY, Qgis.Info)
Create a task that uses the algorithm qgis:randompointsinextent to generate 50000 random points inside a specified
extent. The result is added to the project in a safe way.
from functools import partial
from qgis.core import (QgsTaskManager, QgsMessageLog,
QgsProcessingAlgRunnerTask, QgsApplication,
QgsProcessingContext, QgsProcessingFeedback,
QgsProject)
MESSAGE_CATEGORY = 'AlgRunnerTask'
alg = QgsApplication.processingRegistry().algorithmById(
'qgis:randompointsinextent')
context = QgsProcessingContext()
feedback = QgsProcessingFeedback()
params = {
'EXTENT': '0.0,10.0,40,50 [EPSG:4326]',
'MIN_DISTANCE': 0.0,
'POINTS_NUMBER': 50000,
'TARGET_CRS': 'EPSG:4326',
14.2. Ejemplos 81
PyQGIS developer cookbook, Versión 3.4
• Escribiendo un complemento
– Ficheros de un complemento
• Plugin content
– Plugin metadata
– __init__.py
– mainPlugin.py
– Resource File
• Documentation
• Translation
– Software requirements
– Files and directory
* .pro file
* .ts file
* .qm file
– Translate using Makefile
– Load the plugin
• Tips and Tricks
– Plugin Reloader
– Accessing Plugins
– Log Messages
– Share your plugin
83
PyQGIS developer cookbook, Versión 3.4
Since the introduction of Python plugins in QGIS, a number of plugins have appeared. The QGIS team maintains
an Official Python plugin repository. You can use their source to learn more about programming with PyQGIS or
find out whether you are duplicating development effort.
Ficheros de un complemento
PYTHON_PLUGINS_PATH/
MyPlugin/
__init__.py --> *required*
mainPlugin.py --> *core code*
metadata.txt --> *required*
resources.qrc --> *likely useful*
resources.py --> *compiled version, likely useful*
form.ui --> *likely useful*
form.py --> *compiled version, likely useful*
Advertencia: If you plan to upload the plugin to the Official Python plugin repository you must check that
your plugin follows some additional rules, required for plugin Validation
Here you can find information and examples about what to add in each of the files in the file structure described
above.
Plugin metadata
First, the plugin manager needs to retrieve some basic information about the plugin such as its name, description
etc. File metadata.txt is the right place to put this information.
By default, plugins are placed in the Plugins menu (we will see in the next section how to add a menu entry for
your plugin) but they can also be placed the into Raster, Vector, Database and Web menus.
A corresponding «category» metadata entry exists to specify that, so the plugin can be classified accordingly. This
metadata entry is used as tip for users and tells them where (in which menu) the plugin can be found. Allowed
values for «category» are: Vector, Raster, Database or Web. For example, if your plugin will be available from
Raster menu, add this to metadata.txt
category=Raster
Nota: If qgisMaximumVersion is empty, it will be automatically set to the major version plus .99 when uploaded
to the Official Python plugin repository.
[general]
name=HelloWorld
[email protected]
author=Just Me
qgisMinimumVersion=3.0
description=This is an example plugin for greeting the world.
Multiline is allowed:
lines starting with spaces belong to the same
field, in this case to the "description" field.
HTML formatting is not allowed.
about=This paragraph can contain a detailed description
of the plugin. Multiline is allowed, HTML is not.
version=version 1.2
tracker=http://bugs.itopen.it
repository=http://www.itopen.it/repo
; end of mandatory metadata
; Tags are in comma separated value format, spaces are allowed within the
; tag name.
; Tags should be in English language. Please also check for existing tags and
; synonyms before creating a new one.
tags=wkt,raster,hello world
; deprecated flag (applies to the whole plugin and not only to the uploaded
˓→version)
deprecated=False
__init__.py
This file is required by Python’s import system. Also, QGIS requires that this file contains a classFactory()
function, which is called when the plugin gets loaded into QGIS. It receives a reference to the instance of
QgisInterface and must return an object of your plugin’s class from the mainplugin.py — in our case
it’s called TestPlugin (see below). This is how __init__.py should look like
def classFactory(iface):
from .mainPlugin import TestPlugin
return TestPlugin(iface)
mainPlugin.py
This is where the magic happens and this is how magic looks like: (e.g. mainPlugin.py)
class TestPlugin:
def initGui(self):
# create action that will start plugin configuration
self.action = QAction(QIcon(":/plugins/testplug/icon.png"), "Test plugin",
˓→self.iface.mainWindow())
self.action.setObjectName("testAction")
self.action.setWhatsThis("Configuration for test plugin")
self.action.setStatusTip("This is status tip")
self.action.triggered.connect(self.run)
def unload(self):
# remove the plugin menu item and icon
self.iface.removePluginMenu("&Test plugins", self.action)
self.iface.removeToolBarIcon(self.action)
def run(self):
# create and show a configuration dialog or something similar
print("TestPlugin: run called!")
The only plugin functions that must exist in the main plugin source file (e.g. mainPlugin.py) are:
• __init__ –> which gives access to QGIS interface
• initGui() –> called when the plugin is loaded
• unload() –> called when the plugin is unloaded
In the above example, addPluginToMenu is used. This will add the corresponding menu action to the Plugins
menu. Alternative methods exist to add the action to a different menu. Here is a list of those methods:
• addPluginToRasterMenu()
• addPluginToVectorMenu()
• addPluginToDatabaseMenu()
• addPluginToWebMenu()
All of them have the same syntax as the addPluginToMenu method.
Adding your plugin menu to one of those predefined method is recommended to keep consistency in how plugin
entries are organized. However, you can add your custom menu group directly to the menu bar, as the next example
demonstrates:
def initGui(self):
self.menu = QMenu(self.iface.mainWindow())
self.menu.setObjectName("testMenu")
self.menu.setTitle("MyMenu")
menuBar = self.iface.mainWindow().menuBar()
menuBar.insertMenu(self.iface.firstRightStandardMenu().menuAction(), self.menu)
def unload(self):
self.menu.deleteLater()
Don’t forget to set QAction and QMenu objectName to a name specific to your plugin so that it can be
customized.
Resource File
You can see that in initGui() we’ve used an icon from the resource file (called resources.qrc in our case)
<RCC>
<qresource prefix="/plugins/testplug" >
<file>icon.png</file>
</qresource>
</RCC>
It is good to use a prefix that will not collide with other plugins or any parts of QGIS, otherwise you might get
resources you did not want. Now you just need to generate a Python file that will contain the resources. It’s done
with pyrcc5 command:
Nota: In Windows environments, attempting to run the pyrcc5 from Command Prompt or Powershell will
probably result in the error «Windows cannot access the specified device, path, or file [. . . ]». The easiest solution
is probably to use the OSGeo4W Shell but if you are comfortable modifying the PATH environment variable
or specifiying the path to the executable explicitly you should be able to find it at <Your QGIS Install
Directory>\bin\pyrcc5.exe.
15.1.3 Documentation
The documentation for the plugin can be written as HTML help files. The qgis.utils module provides a
function, showPluginHelp() which will open the help file browser, in the same way as other QGIS help.
The showPluginHelp() function looks for help files in the same directory as the calling module. It will
look for, in turn, index-ll_cc.html, index-ll.html, index-en.html, index-en_us.html and
index.html, displaying whichever it finds first. Here ll_cc is the QGIS locale. This allows multiple transla-
tions of the documentation to be included with the plugin.
The showPluginHelp() function can also take parameters packageName, which identifies a specific plugin
for which the help will be displayed, filename, which can replace «index» in the names of files being searched,
and section, which is the name of an html anchor tag in the document on which the browser will be positioned.
15.1.4 Translation
With a few steps you can set up the environment for the plugin localization so that depending on the locale settings
of your computer the plugin will be loaded in different languages.
Software requirements
The easiest way to create and manage all the translation files is to install Qt Linguist. In a Debian-based
GNU/Linux environment you can install it typing:
When you create the plugin you will find the i18n folder within the main plugin directory.
All the translation files have to be within this directory.
.pro file
First you should create a .pro file, that is a project file that can be managed by Qt Linguist.
In this .pro file you have to specify all the files and forms you want to translate. This file is used to set up the
localization files and variables. A possible project file, matching the structure of our example plugin:
FORMS = ../form.ui
SOURCES = ../your_plugin.py
TRANSLATIONS = your_plugin_it.ts
Your plugin might follow a more complex structure, and it might be distributed across several files. If this is the
case, keep in mind that pylupdate5, the program we use to read the .pro file and update the translatable
string, does not expand wild card characters, so you need to place every file explicitly in the .pro file. Your
project file might then look like something like this:
Furthermore, the your_plugin.py file is the file that calls all the menu and sub-menus of your plugin in the
QGIS toolbar and you want to translate them all.
Finally with the TRANSLATIONS variable you can specify the translation languages you want.
Advertencia: Be sure to name the ts file like your_plugin_ + language + .ts otherwise the language
loading will fail! Use the 2 letter shortcut for the language (it for Italian, de for German, etc. . . )
.ts file
Once you have created the .pro you are ready to generate the .ts file(s) for the language(s) of your plugin.
Open a terminal, go to your_plugin/i18n directory and type:
pylupdate5 your_plugin.pro
.qm file
When you finish to translate your plugin (if some strings are not completed the source language for those strings
will be used) you have to create the .qm file (the compiled .ts file that will be used by QGIS).
Just open a terminal cd in your_plugin/i18n directory and type:
lrelease your_plugin.ts
now, in the i18n directory you will see the your_plugin.qm file(s).
Alternatively you can use the makefile to extract messages from python code and Qt dialogs, if you created your
plugin with Plugin Builder. At the beginning of the Makefile there is a LOCALES variable:
LOCALES = en
Add the abbreviation of the language to this variable, for example for Hungarian language:
LOCALES = en hu
Now you can generate or update the hu.ts file (and the en.ts too) from the sources by:
make transup
After this, you have updated .ts file for all languages set in the LOCALES variable. Use Qt Linguist to translate
the program messages. Finishing the translation the .qm files can be created by the transcompile:
make transcompile
In order to see the translation of your plugin just open QGIS, change the language (Settings → Options → Lan-
guage) and restart QGIS.
You should see your plugin in the correct language.
Advertencia: If you change something in your plugin (new UIs, new menu, etc..) you have to generate
again the update version of both .ts and .qm file, so run again the command of above.
Plugin Reloader
During development of your plugin you will frequently need to reload it in QGIS for testing. This is very easy
using the Plugin Reloader plugin. You can find it as an experimental plugin with the Plugin Manager.
Accessing Plugins
You can access all the classes of installed plugins from within QGIS using python, which can be handy for
debugging purposes.:
Log Messages
QGIS is hosting hundreds of plugins in the plugin repository. Consider sharing yours! It will extend the possibili-
ties of QGIS and people will be able to learn from your code. All hosted plugins can be found and installed from
within QGIS with the Plugin Manager.
Information and requirements are here: plugins.qgis.org.
Esta sección cuenta con fragmentos de código para facilitar el desarrollo de complementos.
self.iface.unregisterMainWindowAction(self.key_action)
def key_action_triggered(self):
QMessageBox.information(self.iface.mainWindow(),"Ok", "You pressed Ctrl+I")
Hay una API para acceder a las capas en la leyenda. Aquí hay un ejemplo que alterna la visibilidad de la capa
activa:
root = QgsProject.instance().layerTreeRoot()
node = root.findLayer(iface.activeLayer().id())
new_state = Qt.Checked if node.isVisible() == Qt.Unchecked else Qt.Unchecked
node.setItemVisibilityChecked(new_state)
def change_value(value):
"""Change the value in the second column for all selected features.
El método requiere un parámetro (el nuevo valor para el campo de atributo de los objeto(s) espaciales selecciona-
dos) y puede ser llamado por
changeValue(50)
Advertencia: Despite our constant efforts, information beyond this line may not be updated for QGIS 3. Refer
to https://qgis.org/pyqgis/master for the python API documentation or, give a hand to update the chapters you
know about. Thanks.
Si su complemento utiliza métodos propios para representar una capa de mapa, escribir su propio tipo de capa
basado en QgsPluginLayer puede ser la mejor forma de implementarla.
class WatermarkPluginLayer(QgsPluginLayer):
LAYER_TYPE="watermark"
def __init__(self):
QgsPluginLayer.__init__(self, WatermarkPluginLayer.LAYER_TYPE, "Watermark
˓→plugin layer")
self.setValid(True)
Métodos de lectura y escritura de información especifica para el archivo del proyecto también puede ser añadido
Al cargar un proyecto que contiene una capa de este tipo, se necesita una clase de fábrica
class WatermarkPluginLayerType(QgsPluginLayerType):
def __init__(self):
QgsPluginLayerType.__init__(self, WatermarkPluginLayer.LAYER_TYPE)
def createLayer(self):
return WatermarkPluginLayer()
Se puede también añadir código para mostrar información personalizada en las propiedades de la capa
Advertencia: Despite our constant efforts, information beyond this line may not be updated for QGIS 3. Refer
to https://qgis.org/pyqgis/master for the python API documentation or, give a hand to update the chapters you
know about. Thanks.
Although each programmer has his preferred IDE/Text editor, here are some recommendations for setting up
popular IDE’s for writing and debugging QGIS Python plugins.
On Linux there is no additional configuration needed to develop plugins. But on Windows you need to make sure
you that you have the same environment settings and use the same libraries and interpreter as QGIS. The fastest
way to do this, is to modify the startup batch file of QGIS.
If you used the OSGeo4W Installer, you can find this under the bin folder of your OSGeo4W install. Look for
something like C:\OSGeo4W\bin\qgis-unstable.bat.
For using Pyscripter IDE, here’s what you have to do:
• Make a copy of qgis-unstable.bat and rename it pyscripter.bat.
• Open it in an editor. And remove the last line, the one that starts QGIS.
• Add a line that points to your Pyscripter executable and add the command line argument that sets the version
of Python to be used
• Also add the argument that points to the folder where Pyscripter can find the Python dll used by QGIS, you
can find this under the bin folder of your OSGeoW install
@echo off
SET OSGEO4W_ROOT=C:\OSGeo4W
call "%OSGEO4W_ROOT%"\bin\o4w_env.bat
call "%OSGEO4W_ROOT%"\bin\gdal16.bat
@echo off
path %PATH%;%GISBASE%\bin
Start C:\pyscripter\pyscripter.exe --python25 --pythondllpath=C:\OSGeo4W\bin
Now when you double click this batch file it will start Pyscripter, with the correct path.
More popular than Pyscripter, Eclipse is a common choice among developers. In the following sections, we will be
explaining how to configure it for developing and testing plugins. To prepare your environment for using Eclipse
in Windows, you should also create a batch file and use it to start Eclipse.
To create that batch file, follow these steps:
• Locate the folder where qgis_core.dll resides in. Normally this is
C:\OSGeo4W\apps\qgis\bin, but if you compiled your own QGIS application this is in your
build folder in output/bin/RelWithDebInfo
• Locate your eclipse.exe executable.
• Create the following script and use this to start eclipse when developing QGIS plugins.
call "C:\OSGeo4W\bin\o4w_env.bat"
set PATH=%PATH%;C:\path\to\your\qgis_core.dll\parent\folder
C:\path\to\your\eclipse.exe
Installation
Preparing QGIS
There is some preparation to be done on QGIS itself. Two plugins are of interest: Remote Debug and Plugin
reloader.
• Go to Plugins → Manage and Install plugins. . .
• Search for Remote Debug ( at the moment it’s still experimental, so enable experimental plugins under the
Options tab in case it does not show up). Install it.
• Search for Plugin reloader and install it as well. This will let you reload a plugin instead of having to close
and restart QGIS to have the plugin reloaded.
Setting up Eclipse
In Eclipse, create a new project. You can select General Project and link your real sources later on, so it does not
really matter where you place this project.
Now right-click your new project and choose New → Folder.
Click Advanced and choose Link to alternate location (Linked Folder). In case you already have sources you want
to debug, choose these. In case you don’t, create a folder as it was already explained.
Now in the view Project Explorer, your source tree pops up and you can start working with the code. You already
have syntax highlighting and all the other powerful IDE tools available.
To get the debugger working, switch to the Debug perspective in Eclipse (Window → Open Perspective → Other
→ Debug).
Now start the PyDev debug server by choosing PyDev → Start Debug Server.
Eclipse is now waiting for a connection from QGIS to its debug server and when QGIS connects to the debug
server it will allow it to control the python scripts. That’s exactly what we installed the Remote Debug plugin for.
So start QGIS in case you did not already and click the bug symbol.
Now you can set a breakpoint and as soon as the code hits it, execution will stop and you can inspect the current
state of your plugin. (The breakpoint is the green dot in the image below, set one by double clicking in the white
space left to the line you want the breakpoint to be set).
A very interesting thing you can make use of now is the debug console. Make sure that the execution is currently
stopped at a break point, before you proceed.
Open the Console view (Window → Show view). It will show the Debug Server console which is not very inter-
esting. But there is a button Open Console which lets you change to a more interesting PyDev Debug Console.
Click the arrow next to the Open Console button and choose PyDev Console. A window opens up to ask you
which console you want to start. Choose PyDev Debug Console. In case its greyed out and tells you to Start the
debugger and select the valid frame, make sure that you’ve got the remote debugger attached and are currently on
a breakpoint.
You have now an interactive console which let’s you test any commands from within the current context. You can
manipulate variables or make API calls or whatever you like.
A little bit annoying is, that every time you enter a command, the console switches back to the Debug Server. To
stop this behavior, you can click the Pin Console button when on the Debug Server page and it should remember
this decision at least for the current debug session.
A very handy feature is to have Eclipse actually know about the QGIS API. This enables it to check your code for
typos. But not only this, it also enables Eclipse to help you with autocompletion from the imports to API calls.
To do this, Eclipse parses the QGIS library files and gets all the information out there. The only thing you have to
do is to tell Eclipse where to find the libraries.
Click Window → Preferences → PyDev → Interpreter → Python.
You will see your configured python interpreter in the upper part of the window (at the moment python2.7 for
QGIS) and some tabs in the lower part. The interesting tabs for us are Libraries and Forced Builtins.
First open the Libraries tab. Add a New Folder and choose the python folder of your QGIS installation. If you
do not know where this folder is (it’s not the plugins folder) open QGIS, start a python console and simply enter
qgis and press Enter. It will show you which QGIS module it uses and its path. Strip the trailing /qgis/
__init__.pyc from this path and you’ve got the path you are looking for.
You should also add your plugins folder here (it is in python/plugins under the user profile folder).
Next jump to the Forced Builtins tab, click on New. . . and enter qgis. This will make Eclipse parse the QGIS
API. You probably also want Eclipse to know about the PyQt4 API. Therefore also add PyQt4 as forced builtin.
That should probably already be present in your libraries tab.
Click OK and you’re done.
Nota: Every time the QGIS API changes (e.g. if you’re compiling QGIS master and the SIP file changed), you
should go back to this page and simply click Apply. This will let Eclipse parse all the libraries again.
If you do not use an IDE such as Eclipse, you can debug using PDB, following these steps.
First add this code in the spot where you would like to debug
$ ./Qgis
On macOS do:
$ /Applications/Qgis.app/Contents/MacOS/Qgis
And when the application hits your breakpoint you can type in the console!
TODO: Add testing information
Advertencia: Despite our constant efforts, information beyond this line may not be updated for QGIS 3. Refer
to https://qgis.org/pyqgis/master for the python API documentation or, give a hand to update the chapters you
know about. Thanks.
Once your plugin is ready and you think the plugin could be helpful for some people, do not hesitate to upload it
to Official Python plugin repository. On that page you can also find packaging guidelines about how to prepare
the plugin to work well with the plugin installer. Or in case you would like to set up your own plugin repository,
create a simple XML file that will list the plugins and their metadata.
Please take special care to the following suggestions:
• do not include generated file (ui_*.py, resources_rc.py, generated help files. . . ) and useless stuff (e.g. .git-
ignore) in repository
• add the plugin to the appropriate menu (Vector, Raster, Web, Database)
• when appropriate (plugins performing analyses), consider adding the plugin as a subplugin of Processing
framework: this will allow users to run it in batch, to integrate it in more complex workflows, and will free
you from the burden of designing an interface
• include at least minimal documentation and, if useful for testing and understanding, sample data.
Permissions
Trust management
Staff members can grant trust to selected plugin creators setting plugins.can_approve permission through the
front-end application.
The plugin details view offers direct links to grant trust to the plugin creator or the plugin owners.
Validation
Plugin’s metadata are automatically imported and validated from the compressed package when the plugin is
uploaded.
Here are some validation rules that you should aware of when you want to upload a plugin on the official reposi-
tory:
1. the name of the main folder containing your plugin must contain only ASCII characters (A-Z and a-z), digits
and the characters underscore (_) and minus (-), also it cannot start with a digit
2. metadata.txt is required
3. all required metadata listed in metadata table must be present
4. the version metadata field must be unique
Plugin structure
Following the validation rules the compressed (.zip) package of your plugin must have a specific structure to
validate as a functional plugin. As the plugin will be unzipped inside the users plugins folder it must have it’s
own directory inside the .zip file to not interfere with other plugins. Mandatory files are: metadata.txt
and __init__.py. But it would be nice to have a README and of course an icon to represent the plugin
(resources.qrc). Following is an example of how a plugin.zip should look like.
plugin.zip
pluginfolder/
|-- i18n
| |-- translation_file_de.ts
|-- img
| |-- icon.png
| `-- iconsource.svg
|-- __init__.py
|-- Makefile
|-- metadata.txt
|-- more_code.py
|-- main_code.py
|-- README
|-- resources.qrc
|-- resources_rc.py
`-- ui_Qt_user_interface_file.ui
It is possible to create plugins in the Python programming language. In comparison with classical plugins written
in C++ these should be easier to write, understand, maintain and distribute due to the dynamic nature of the Python
language.
Python plugins are listed together with C++ plugins in QGIS plugin manager. They are searched for in ~/
(UserProfile)/python/plugins and these paths:
• UNIX/Mac: (qgis_prefix)/share/qgis/python/plugins
• Windows: (qgis_prefix)/python/plugins
For definitions of ~ and (UserProfile) see core_and_external_plugins.
Nota: By setting QGIS_PLUGINPATH to an existing directory path, you can add this path to the list of paths
that are searched for plugins.
En función del tipo de complemento que vayas a desarrollar, puede ser más recomendable añadir la funcionalidad
en cuestión como un algoritmo de Procesamiento (o un conjunto de algoritmos). Esta opción permite una mejor
integración en QGIS, añadiendo la funcionalidad (ya que así podrá ser ejecutada dentro de cualquiera los compo-
nentes de menú Procesos, como el modelador gráfico o la interfaz de procesos por lotes), y optimizando el tiempo
de desarrollo (ya que el complemento de Procesos hará gran parte del trabajo).
To distribute those algorithms, you should create a new plugin that adds them to the Processing Toolbox. The
plugin should contain an algorithm provider, which has to be registered when the plugin is instantiated.
To create a plugin from scratch which contains an algorithm provider, you can follow these steps using the Plugin
Builder:
• Instala el complemento Plugin Builder.
• Crea un nuevo complemento, usando el Plugin Builder. En el cuadro de diálogo del Plugin Builder, selec-
ciona «Processing provider».
• The created plugin contains a provider with a single algorithm. Both the provider file and the algorithm
file are fully commented and contain information about how to modify the provider and add additional
algorithms. Refer to them for more information.
If you want to add your existing plugin to Processing, you need to add some code.
In your metadata.txt, you need to add a variable:
hasProcessingProvider=yes
In the Python file where your plugin is setup with the initGui method, you need to adapt some lines like this:
class YourPluginName():
def __init__(self):
self.provider = None
def initProcessing(self):
self.provider = Provider()
QgsApplication.processingRegistry().addProvider(self.provider)
103
PyQGIS developer cookbook, Versión 3.4
def initGui(self):
self.initProcessing()
def unload(self):
QgsApplication.processingRegistry().removeProvider(self.provider)
class Provider(QgsProcessingProvider):
def icon(self):
"""Should return a QIcon which is used for your provider inside
the Processing toolbox.
"""
return QgsProcessingProvider.icon(self)
Advertencia: Despite our constant efforts, information beyond this line may not be updated for QGIS 3. Refer
to https://qgis.org/pyqgis/master for the python API documentation or, give a hand to update the chapters you
know about. Thanks.
• Información general
• Contruir un gráfico
• Análisis gráfico
– Encontrar la ruta más corta
– Áreas de disponibilidad
A partir de la versión ee19294562 (QGIS >= 1.8) la nueva librería de análisis de redes se agregó a la librería de
análisis de nucleo de QGIS. La librería:
• Crear gráfico matemático de datos geográficos (capas vectoriales de polilínea)
• implementa métodos básicos de la teoría de grafos (actualmente sólo el algoritmo Dijkstra)
La librería de análisis de redes fue creada por funciones básicas de exportación del complemento núcleo Road-
Graph y ahora se puede utilizar los metodos en complementos o directamente de la consola Python.
105
PyQGIS developer cookbook, Versión 3.4
Lo primero que hay que hacer — es preparar la entrada de datos, que es convertir una capa vectorial en un gráfico.
Todas las acciones adicionales utilizarán esta gráfica, no la capa.
Como fuente podemos utilizar una capa vectorial de polilínea. Los nodos de las polilíneas se convierten en vértices
del gráfico, y los segmentos de la polilínea son bordes de gráfico. Si varios nodos tienen la misma coordenada
entonces ellos tienen el mimso vértice gráfico. Por lo que dos líneas que tienen un nodo en común se conectaran
entre si.
Además durante la creación del gráfico se puede «arreglar» («atar») a la capa vectorial de entrada cualquier número
de puntos adicionales. Para cada punto adicional se encontrará una coincidencia — el vértice gráfico más cercano
o el borde gráfico más cercano. En el último caso el borde será dividido y un nuevo vértice se añadirá.
Los atributos de la capa vectorial y la longitud de un borde se puede utilizar como las propiedades de un borde.
Converting from a vector layer to the graph is done using the Builder programming pattern. A graph is constructed
using a so-called Director. There is only one Director for now: QgsLineVectorLayerDirector. The director sets the
basic settings that will be used to construct a graph from a line vector layer, used by the builder to create the graph.
Currently, as in the case with the director, only one builder exists: QgsGraphBuilder, that creates QgsGraph
objects. You may want to implement your own builders that will build a graphs compatible with such libraries as
BGL or NetworkX.
To calculate edge properties the programming pattern strategy is used. For now only QgsDistanceArcProperter
strategy is available, that takes into account the length of the route. You can implement your own strategy that
will use all necessary parameters. For example, RoadGraph plugin uses a strategy that computes travel time using
edge length and speed value from attributes.
Es tiempo de sumergirse en el proceso.
Antes que nada, para utilizar esta librería debemos importar el modulo de análisis de redes
Para construir un director debemos pasar a una capa vectorial, que se utilizará como fuente para la estructura
gráfica y la información sobre el movimiento permitido en cada segmento de carretera (movimiento unidireccional
o bidireccional, dirección directa o inversa). La llamada se parece a esto
• directDirectionValue — el valor del campo de carreteras con dirección directa (mover desde la
primer punto de línea a la última). Un texto.
• reverseDirectionValue — valor del campo de carreteras con dirección inversa (mover del último
punto de línea al primero). Un texto.
• bothDirectionValue — valor de campo para carreteras bidireccionales (para cada carretera podemos
mover del primer punto al último y del último al primero). Un texto.
• defaultDirection — dirección de carretera predeterminada. Este valor se utilizará para esos caminos
donde el campo directionFieldId no esta establecido o tiene algun valore diferente de cualquiera de
los tres valores especificados anteriormente. Un entero. 1 indica la dirección directa, 2 la dirección inversa,
y 3 ambas direcciones.
Es necesario entonces crear una estrategia para calcular propiedades de borde
properter = QgsDistanceArcProperter()
director.addProperter(properter)
Now we can use the builder, which will create the graph. The QgsGraphBuilder class constructor takes several
arguments:
• src — sistema de referencia de coordenadas a utilizar. Argumento obligatorio.
• otfEnable — utilizar la reproyección “al vuelo” o no. Por defecto const:True (utilizar OTF).
• topologyTolerance — tolerancia topologica. Por defecto el valor es 0.
• ellipsoidID — ellipsoid a utilizar. Por defecto «WGS84».
También podemos definir varios puntos, que se utilizarán en el análisis. Por ejemplo
Ahora todo está en su lugar para que podamos construir el gráfico y «atar» a estos puntos
Construir el grafo puede tomar tiempo (que depende del número de elementos y tamaño de una capa).
tiedPoints es una lista con coordenadas de puntos «tied». Cuando la operación de construcción se finalizo
podemos obtener la gráfica y utilizarlo para el análisis
graph = builder.graph()
Con el siguiente código podemos obtener el índice del vértice de nuestros puntos
startId = graph.findVertex(tiedPoints[0])
endId = graph.findVertex(tiedPoints[1])
El análisis de redes es utilizado para encontrar respuestas a dos preguntas: que vértices estan conectados y cómo
encontrar la ruta más corta. Para resolver estos problemas la librería de análisis de redes proporciona el algoritmo
Dijkstra.
El algoritmo Dijkstra encuentra la ruta más corta de uno de los vértices del grafo a todos los otros y los valores de
los parámetros de optimización, El resultado puede ser representado como un árbol de la ruta más corta.
El árbol del cámino más corto es un grafo ponderado dirigido (o más precisamente – árbol) con las siguientes
propiedades:
• sólo un vértice no tiene bordes entrantes — la raíz del árbol
• todos los otros vértices sólo tienen un borde entrante
• Si el vértice B es accesible desde el vértice A, entonces el camino de A a B es la única ruta disponible y es
optima (más corta) en este grafo
To get the shortest path tree use the methods shortestTree and dijkstra of the QgsGraphAnalyzer
class. It is recommended to use the dijkstra method because it works faster and uses memory more efficiently.
The shortestTree method is useful when you want to walk around the shortest path tree. It always creates a
new graph object (QgsGraph) and accepts three variables:
• fuente — gráfico de entrada
• startVertexIdx — índice del punto en el árbol (la raíz del árbol)
• criterionNum — número de propiedad de borde a utilizar (iniciar de 0).
The dijkstra method has the same arguments, but returns two arrays. In the first array element i contains index
of the incoming edge or -1 if there are no incoming edges. In the second array element i contains distance from
the root of the tree to vertex i or DOUBLE_MAX if vertex i is unreachable from the root.
Here is some very simple code to display the shortest path tree using the graph created with the shortestTree
method (select linestring layer in Layers panel and replace coordinates with your own).
Advertencia: Use this code only as an example, it creates a lot of QgsRubberBand objects and may be
slow on large datasets.
vl = qgis.utils.iface.mapCanvas().currentLayer()
director = QgsLineVectorLayerDirector(vl, -1, '', '', '', 3)
properter = QgsDistanceArcProperter()
director.addProperter(properter)
crs = qgis.utils.iface.mapCanvas().mapRenderer().destinationCrs()
builder = QgsGraphBuilder(crs)
graph = builder.graph()
idStart = graph.findVertex(pStart)
i = 0;
vl = qgis.utils.iface.mapCanvas().currentLayer()
director = QgsLineVectorLayerDirector(vl, -1, '', '', '', 3)
properter = QgsDistanceArcProperter()
director.addProperter(properter)
crs = qgis.utils.iface.mapCanvas().mapRenderer().destinationCrs()
builder = QgsGraphBuilder(crs)
graph = builder.graph()
idStart = graph.findVertex(pStart)
To find the optimal path between two points the following approach is used. Both points (start A and end B) are
«tied» to the graph when it is built. Then using the shortestTree or dijkstra method we build the shortest
path tree with root in the start point A. In the same tree we also find the end point B and start to walk through the
tree from point B to point A. The whole algorithm can be written as
assign = B
while != A
add point to path
get incoming edge for point
look for point , that is start point of this edge
assign =
add point to path
En este punto tenemos la ruta, en el formulario de la lista invertida de vértices (los vértices están listados en orden
invertida del punto final al punto inicial) que serán visitados durante el viaje por este camino.
Here is the sample code for QGIS Python Console (you will need to select linestring layer in TOC and replace
coordinates in the code with yours) that uses the shortestTree method
vl = qgis.utils.iface.mapCanvas().currentLayer()
director = QgsLineVectorLayerDirector(vl, -1, '', '', '', 3)
properter = QgsDistanceArcProperter()
director.addProperter(properter)
crs = qgis.utils.iface.mapCanvas().mapRenderer().destinationCrs()
builder = QgsGraphBuilder(crs)
tStart = tiedPoints[0]
tStop = tiedPoints[1]
idStart = graph.findVertex(tStart)
tree = QgsGraphAnalyzer.shortestTree(graph, idStart, 0)
idStart = tree.findVertex(tStart)
idStop = tree.findVertex(tStop)
if idStop == -1:
print("Path not found")
else:
p = []
while (idStart != idStop):
l = tree.vertex(idStop).inArc()
if len(l) == 0:
break
e = tree.arc(l[0])
p.insert(0, tree.vertex(e.inVertex()).point())
idStop = e.outVertex()
p.insert(0, tStart)
rb = QgsRubberBand(qgis.utils.iface.mapCanvas())
rb.setColor(Qt.red)
for pnt in p:
rb.addPoint(pnt)
And here is the same sample but using the dijkstra method
from qgis.core import *
from qgis.gui import *
from qgis.networkanalysis import *
from qgis.PyQt.QtCore import *
from qgis.PyQt.QtGui import *
vl = qgis.utils.iface.mapCanvas().currentLayer()
director = QgsLineVectorLayerDirector(vl, -1, '', '', '', 3)
properter = QgsDistanceArcProperter()
director.addProperter(properter)
crs = qgis.utils.iface.mapCanvas().mapRenderer().destinationCrs()
builder = QgsGraphBuilder(crs)
tStart = tiedPoints[0]
tStop = tiedPoints[1]
idStart = graph.findVertex(tStart)
idStop = graph.findVertex(tStop)
if tree[idStop] == -1:
print("Path not found")
else:
p = []
curPos = idStop
while curPos != idStart:
p.append(graph.vertex(graph.arc(tree[curPos]).inVertex()).point())
curPos = graph.arc(tree[curPos]).outVertex();
p.append(tStart)
rb = QgsRubberBand(qgis.utils.iface.mapCanvas())
rb.setColor(Qt.red)
for pnt in p:
rb.addPoint(pnt)
El área de la disponibilidad para el vértice A es el subconjunto de vértices del grafo que son accesibles desde el
vértice A y el costo de los caminos de la A a estos vértices son no es mayor que cierto valor.
Más claramente esto se puede demostrar con el siguiente ejemplo: «Hay una estación de bomberos ¿Qué partes
de la ciudad puede un camión de bomberos alcanzar en 5 minutos? 10 minutos? 15 minutos?». Las respuestas a
estas preguntas son las zonas de la estación de bomberos de la disponibilidad.
To find the areas of availability we can use the dijkstra method of the QgsGraphAnalyzer class. It is
enough to compare the elements of the cost array with a predefined value. If cost[i] is less than or equal to a
predefined value, then vertex i is inside the area of availability, otherwise it is outside.
Un problema más difícil es conseguir los límites de la zona de disponibilidad. El borde inferior es el conjunto de
vértices que son todavía accesibles, y el borde superior es el conjunto de vértices que no son accesibles. De hecho
esto es simple: es la frontera disponibilidad basado en los bordes del árbol de ruta más corta para los que el vértice
origen del contorno es más accesible y el vértice destino del borde no lo es.
Aquí tiene un ejemplo
from qgis.core import *
from qgis.gui import *
from qgis.networkanalysis import *
from qgis.PyQt.QtCore import *
from qgis.PyQt.QtGui import *
vl = qgis.utils.iface.mapCanvas().currentLayer()
director = QgsLineVectorLayerDirector(vl, -1, '', '', '', 3)
properter = QgsDistanceArcProperter()
director.addProperter(properter)
crs = qgis.utils.iface.mapCanvas().mapRenderer().destinationCrs()
builder = QgsGraphBuilder(crs)
rb = QgsRubberBand(qgis.utils.iface.mapCanvas(), True)
rb.setColor(Qt.green)
rb.addPoint(QgsPoint(pStart.x() - delta, pStart.y() - delta))
rb.addPoint(QgsPoint(pStart.x() + delta, pStart.y() - delta))
rb.addPoint(QgsPoint(pStart.x() + delta, pStart.y() + delta))
rb.addPoint(QgsPoint(pStart.x() - delta, pStart.y() + delta))
idStart = graph.findVertex(tStart)
upperBound = []
r = 2000.0
i = 0
while i < len(cost):
if cost[i] > r and tree[i] != -1:
outVertexId = graph.arc(tree [i]).outVertex()
if cost[outVertexId] < r:
upperBound.append(i)
i = i + 1
for i in upperBound:
centerPoint = graph.vertex(i).point()
rb = QgsRubberBand(qgis.utils.iface.mapCanvas(), True)
rb.setColor(Qt.red)
rb.addPoint(QgsPoint(centerPoint.x() - delta, centerPoint.y() - delta))
rb.addPoint(QgsPoint(centerPoint.x() + delta, centerPoint.y() - delta))
rb.addPoint(QgsPoint(centerPoint.x() + delta, centerPoint.y() + delta))
rb.addPoint(QgsPoint(centerPoint.x() - delta, centerPoint.y() + delta))
Advertencia: Despite our constant efforts, information beyond this line may not be updated for QGIS 3. Refer
to https://qgis.org/pyqgis/master for the python API documentation or, give a hand to update the chapters you
know about. Thanks.
113
PyQGIS developer cookbook, Versión 3.4
– allowToEdit
– cacheKey
Server python plugins are loaded once when the FCGI application starts. They register one or more
QgsServerFilter (from this point, you might find useful a quick look to the server plugins API docs). Each
filter should implement at least one of three callbacks:
• requestReady()
• responseComplete()
• sendResponse()
All filters have access to the request/response object (QgsRequestHandler) and can manipulate all its prop-
erties (input/output) and raise exceptions (while in a quite particular way as we’ll see below).
Here is a pseudo code showing a typical server session and when the filter’s callbacks are called:
• Get the incoming request
– create GET/POST/SOAP request handler
– pass request to an instance of QgsServerInterface
– call plugins requestReady filters
– if there is not a response
* Si SERVICE es WMS/WFS/WCS
· create WMS/WFS/WCS server
call server’s executeRequest and possibly call sendResponse plugin filters
when streaming output or store the byte stream output and content type in the re-
quest handler
18.1.1 requestReady
This is called when the request is ready: incoming URL and data have been parsed and before entering the core
services (WMS, WFS etc.) switch, this is the point where you can manipulate the input and perform actions like:
• authentication/authorization
• redirije
18.1.2 sendResponse
This is called whenever output is sent to FCGI stdout (and from there, to the client), this is normally done after
core services have finished their process and after responseComplete hook was called, but in a few cases XML can
become so huge that a streaming XML implementation was needed (WFS GetFeature is one of them), in this case,
sendResponse is called multiple times before the response is complete (and before responseComplete is
called). The obvious consequence is that sendResponse is normally called once but might be exceptionally
called multiple times and in that case (and only in that case) it is also called before responseComplete.
sendResponse is the best place for direct manipulation of core service’s output and while
responseComplete is typically also an option, sendResponse is the only viable option in case of streaming
services.
18.1.3 responseComplete
This is called once when core services (if hit) finish their process and the request is ready to be sent to the client.
As discussed above, this is normally called before sendResponse except for streaming services (or other plugin
filters) that might have called sendResponse earlier.
responseComplete is the ideal place to provide new services implementation (WPS or custom services) and
to perform direct manipulation of the output coming from core services (for example to add a watermark upon a
WMS image).
Some work has still to be done on this topic: the current implementation can distinguish between
handled and unhandled exceptions by setting a QgsRequestHandler property to an instance of
QgsMapServiceException, this way the main C++ code can catch handled python exceptions and ignore
unhandled exceptions (or better: log them).
This approach basically works but it is not very «pythonic»: a better approach would be to raise exceptions from
python code and see them bubbling up into C++ loop for being handled there.
A server plugin is a standard QGIS Python plugin as described in Developing Python Plugins, that just provides
an additional (or alternative) interface: a typical QGIS desktop plugin has access to QGIS application through the
QgisInterface instance, a server plugin has also access to a QgsServerInterface.
To tell QGIS Server that a plugin has a server interface, a special metadata entry is needed (in metadata.txt)
server=True
The example plugin discussed here (with many more example filters) is available on github: QGIS HelloServer Ex-
ample Plugin. You could also find more examples at https://github.com/elpaso/qgis3-server-vagrant/tree/master/
resources/web/plugins or browsing the QGIS plugins repository.
PYTHON_PLUGINS_PATH/
HelloServer/
__init__.py --> *required*
HelloServer.py --> *required*
metadata.txt --> *required*
18.3.2 __init__.py
This file is required by Python’s import system. Also, QGIS Server requires that this file contains a
serverClassFactory() function, which is called when the plugin gets loaded into QGIS Server when the
server starts. It receives reference to instance of QgsServerInterface and must return instance of your
plugin’s class. This is how the example plugin __init__.py looks like:
def serverClassFactory(serverIface):
from HelloServer import HelloServerServer
return HelloServerServer(serverIface)
18.3.3 HelloServer.py
This is where the magic happens and this is how magic looks like: (e.g. HelloServer.py)
A server plugin typically consists in one or more callbacks packed into objects called QgsServerFilter.
Each QgsServerFilter implements one or more of the following callbacks:
• requestReady()
• responseComplete()
• sendResponse()
The following example implements a minimal filter which prints HelloServer! in case the SERVICE parameter
equals to “HELLO”:
class HelloFilter(QgsServerFilter):
def responseComplete(self):
request = self.serverInterface().requestHandler()
params = request.parameterMap()
if params.get('SERVICE', '').upper() == 'HELLO':
request.clearHeaders()
request.setHeader('Content-type', 'text/plain')
request.clearBody()
request.appendBody('HelloServer!')
The filters must be registered into the serverIface as in the following example:
class HelloServerServer:
def __init__(self, serverIface):
# Save reference to the QGIS server interface
self.serverIface = serverIface
serverIface.registerFilter( HelloFilter, 100 )
The second parameter of registerFilter sets a priority which defines the order for the callbacks with the
same name (the lower priority is invoked first).
By using the three callbacks, plugins can manipulate the input and/or the output of the server in many dif-
ferent ways. In every moment, the plugin instance has access to the QgsRequestHandler through the
QgsServerInterface. The QgsRequestHandler class has plenty of methods that can be used to al-
ter the input parameters before entering the core processing of the server (by using requestReady()) or after
the request has been processed by the core services (by using sendResponse()).
Los siguientes ejemplos cubren algunos casos comunes de uso:
The example plugin contains a test example that changes input parameters coming from the query string, in this
example a new parameter is injected into the (already parsed) parameterMap, this parameter is then visible by
core services (WMS etc.), at the end of core services processing we check that the parameter is still there:
from qgis.server import *
from qgis.core import *
class ParamsFilter(QgsServerFilter):
def requestReady(self):
request = self.serverInterface().requestHandler()
params = request.parameterMap( )
request.setParameter('TEST_NEW_PARAM', 'ParamsFilter')
def responseComplete(self):
request = self.serverInterface().requestHandler()
params = request.parameterMap( )
if params.get('TEST_NEW_PARAM') == 'ParamsFilter':
QgsMessageLog.logMessage("SUCCESS - ParamsFilter.responseComplete",
˓→'plugin', QgsMessageLog.INFO)
else:
QgsMessageLog.logMessage("FAIL - ParamsFilter.responseComplete",
˓→'plugin', QgsMessageLog.CRITICAL)
On the highlighted line the “SUCCESS” string indicates that the plugin passed the test.
The same technique can be exploited to use a custom service instead of a core one: you could for example skip
a WFS SERVICE request or any other core request just by changing the SERVICE parameter to something
different and the core service will be skipped, then you can inject your custom results into the output and send
them to the client (this is explained here below).
The watermark filter example shows how to replace the WMS output with a new image obtained by adding a
watermark image on the top of the WMS image generated by the WMS core service:
import os
class WatermarkFilter(QgsServerFilter):
def responseComplete(self):
request = self.serverInterface().requestHandler()
params = request.parameterMap( )
# Do some checks
if (request.parameter('SERVICE').upper() == 'WMS' \
and request.parameter('REQUEST').upper() == 'GETMAP' \
and not request.exceptionRaised() ):
QgsMessageLog.logMessage("WatermarkFilter.responseComplete: image
˓→ready {}".format(request.infoFormat()), 'plugin', QgsMessageLog.INFO)
p = QPainter(img)
p.drawImage(QRect( 20, 20, 40, 40), watermark)
p.end()
ba = QByteArray()
buffer = QBuffer(ba)
buffer.open(QIODevice.WriteOnly)
img.save(buffer, "PNG")
# Set the body
request.clearBody()
request.appendBody(ba)
In this example the SERVICE parameter value is checked and if the incoming request is a WMS GETMAP and
no exceptions have been set by a previously executed plugin or by the core service (WMS in this case), the WMS
generated image is retrieved from the output buffer and the watermark image is added. The final step is to clear the
output buffer and replace it with the newly generated image. Please note that in a real-world situation we should
also check for the requested image type instead of returning PNG in any case.
PYTHON_PLUGINS_PATH/
MyAccessControl/
__init__.py --> *required*
AccessControl.py --> *required*
metadata.txt --> *required*
18.4.2 __init__.py
This file is required by Python’s import system. As for all QGIS server plugins, this file contains a
serverClassFactory() function, which is called when the plugin gets loaded into QGIS Server at startup.
It receives a reference to an instance of QgsServerInterface and must return an instance of your plugin’s
class. This is how the example plugin __init__.py looks like:
def serverClassFactory(serverIface):
from MyAccessControl.AccessControl import AccessControl
return AccessControl(serverIface)
18.4.3 AccessControl.py
class AccessControl(QgsAccessControlFilter):
def cacheKey(self):
return super(QgsAccessControlFilter, self).cacheKey()
18.4.4 layerFilterExpression
Usado para agregar una Expresión para limitar los resultados, ej.:
18.4.5 layerFilterSubsetString
Same than the previous but use the SubsetString (executed in the database)
18.4.6 layerPermissions
18.4.7 authorizedLayerAttributes
18.4.8 allowToEdit
To be able to edit only feature that has the attribute role with the value user.
18.4.9 cacheKey
QGIS server maintain a cache of the capabilities then to have a cache per role you can return the role in this
method. Or return None to completely disable the cache.
Cambiar apariencia
app = QApplication.instance()
qss_file = open(r"/path/to/style/file.qss").read()
app.setStyleSheet(qss_file)
icon = QIcon(r"/path/to/logo/file.png")
iface.mainWindow().setWindowIcon(icon)
iface.mainWindow().setWindowTitle("My QGIS")
19.2 Configuración
Listar QSettings
qs = QSettings()
for k in sorted(qs.allKeys()):
print (k)
123
PyQGIS developer cookbook, Versión 3.4
toolbar = iface.helpToolBar()
parent = toolbar.parentWidget()
parent.removeToolBar(toolbar)
actions = iface.attributesToolBar().actions()
iface.attributesToolBar().clear()
iface.attributesToolBar().addAction(actions[4])
iface.attributesToolBar().addAction(actions[3])
19.4 Menús
Eliminar menú
19.5 Lienzo
Acceder al lienzo
canvas = iface.mapCanvas()
iface.mapCanvas().setCanvasColor(Qt.black)
iface.mapCanvas().refresh()
19.6 Capas
if not layer:
print("Layer failed to load!")
layer = iface.activeLayer()
QgsProject.instance().mapLayers().values()
layers_names = []
for layer in QgsProject.instance().mapLayers().values():
layers_names.append(layer.name())
De otra manera
Mostrar métodos
dir(layer)
feat = QgsFeature()
geom = QgsGeometry()
feat.setGeometry(geom)
feat.setFields(layer.fields())
pr = layer.dataProvider()
feat = QgsFeature()
feat.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(10,10)))
pr.addFeatures([feat])
for f in layer.getFeatures():
print (f)
for f in layer.selectedFeatures():
print (f)
selected_ids = layer.selectedFeatureIds()
print(selected_ids)
memory_layer = layer.materialize(QgsFeatureRequest().setFilterFids(layer.
˓→selectedFeatureIds()))
QgsProject.instance().addMapLayer(memory_layer)
Obtener geometría
# Point layer
for f in layer.getFeatures():
geom = f.geometry()
print ('%f, %f' % (geom.asPoint().y(), geom.asPoint().x()))
Mover geometría
geom.translate(100, 100)
poly.setGeometry(geom)
Establecer SRC
Ver SRC
Ocultar columna
from qgis.core import QgsEditorWidgetSetup
˓→88.82 34.99))")
poly.setGeometry(geom)
pr.addFeatures([poly])
layer.updateExtents()
QgsProject.instance().addMapLayers([layer])
fileName = "/path/to/gpkg/file.gpkg"
layer = QgsVectorLayer(fileName,"test","ogr")
subLayers =layer.dataProvider().subLayers()
urlWithParams = 'type=xyz&url=https://a.tile.openstreetmap.org/%7Bz%7D/%7Bx%7D/%7By
˓→%7D.png&zmax=19&zmin=0&crs=EPSG3857'
loadXYZ(urlWithParams, 'OpenStreetMap')
QgsProject.instance().removeAllMapLayers()
Eliminar todos
QgsProject.instance().clear()
iface.mapCanvas().layers()
ltv = iface.layerTreeView()
mp = ltv.menuProvider()
ltv.setMenuProvider(None)
# Restore
ltv.setMenuProvider(mp)
Nodo raíz
root = QgsProject.instance().layerTreeRoot()
print (root)
print (root.children())
child0 = root.children()[0]
print (child0.name())
print (type(child0))
print (isinstance(child0, QgsLayerTreeLayer))
print (isinstance(child0.parent(), QgsLayerTree))
def get_group_layers(group):
print('- group: ' + group.name())
for child in group.children():
if isinstance(child, QgsLayerTreeGroup):
# Recursive call to get nested groups
get_group_layers(child)
else:
print(' - layer: ' + child.name())
root = QgsProject.instance().layerTreeRoot()
Añadir capas
Añadir grupo
Eliminar capa
root.removeLayer(layer1)
Eliminar grupo
root.removeChildNode(node_group2)
Mover nodo
cloned_group1 = node_group.clone()
root.insertChildNode(0, cloned_group1)
root.removeChildNode(node_group)
cloned_group1.setName("Group X")
node_layer1.setName("Layer X")
Cambiar visibilidad
print (cloned_group1.isVisible())
cloned_group1.setItemVisibilityChecked(False)
node_layer1.setItemVisibilityChecked(False)
Expandir nodo
print (cloned_group1.isExpanded())
cloned_group1.setExpanded(False)
model = iface.layerTreeView().layerTreeModel()
ltv = iface.layerTreeView()
root = QgsProject.instance().layerTreeRoot()
Señales de nodo
root.willAddChildren.connect(onWillAddChildren)
root.addedChildren.connect(onAddedChildren)
root = QgsProject.instance().layerTreeRoot()
model = QgsLayerTreeModel(root)
view = QgsLayerTreeView()
view.setModel(model)
view.show()
De otra manera
def alglist():
s = ''
for i in QgsApplication.processingRegistry().algorithms():
l = i.displayName().ljust(50, "-")
r = i.id()
s += '{}--->{}\n'.format(l, r)
print(s)
import processing
processing.algorithmHelp("qgis:randomselection")
Ejecutar algoritmo
Para este ejemplo, el resultado se almacena en una capa en memoria que se agrega al proyecto.
import processing
result = processing.run("native:buffer", {'INPUT': layer, 'OUTPUT': 'memory:'})
QgsProject.instance().addMapLayer(result['OUTPUT'])
len(QgsApplication.processingRegistry().algorithms())
len(QgsApplication.processingRegistry().providers())
len(QgsExpression.Functions())
19.10 Decoradores
CopyRight
def _on_render_complete(p):
deviceHeight = p.device().height() # Get paint device height on which this
˓→painter is currently painting
size = text.size()
# RenderMillimeters
pixelsInchX = p.device().logicalDpiX()
pixelsInchY = p.device().logicalDpiY()
xOffset = pixelsInchX * INCHES_TO_MM * int(mMarginHorizontal)
yOffset = pixelsInchY * INCHES_TO_MM * int(mMarginVertical)
# Calculate positions
if case == 0:
# Top Left
add_copyright(p, text, xOffset, yOffset)
elif case == 1:
# Bottom Left
yOffset = deviceHeight - yOffset - size.height()
add_copyright(p, text, xOffset, yOffset)
elif case == 2:
# Top Right
xOffset = deviceWidth - xOffset - size.width()
add_copyright(p, text, xOffset, yOffset)
elif case == 3:
# Bottom Right
yOffset = deviceHeight - yOffset - size.height()
xOffset = deviceWidth - xOffset - size.width()
add_copyright(p, text, xOffset, yOffset)
elif case == 4:
# Top Center
xOffset = deviceWidth / 2
add_copyright(p, text, xOffset, yOffset)
else:
# Bottom Center
yOffset = deviceHeight - yOffset - size.height()
xOffset = deviceWidth / 2
add_copyright(p, text, xOffset, yOffset)
19.11 Fuentes