Introducción al desarrollo con PyQGIS

Germán Carrillo

Introducción al desarrollo con PyQGIS

2020

Sobre mí...

MSc. en Geoinformática (Münster, Alemania)
Colaborador oficial de QGIS desde 2009!
Co-administrador de GeoTux
Coordinador general de QGIS Colombia (2018-2020)
¿Dónde me encuentras?

Curso de introducción a PyQGIS

Introducción

"Creemos que por lo menos algunos analistas realmente necesitarán aprender a programar.

Piensa en ello como aprendiendo a cocinar. Puedes preferir pasta fresca a macarrones gratinados en caja, pero si no quieres quedar atrapado comiendo la última, debes aprender a cocinar o pagar a alguien más para que lo haga por ti.

Aprender a programar es como aprender a cocinar de otra manera: puede ser un proceso muy gradual. Un día estás sentado comiendo tus macarrones gratinados y decides mejorarlos con un poco de Tabasco, mostaza o salsa inglesa. Bingo! Pronto estarás poniéndole además queso rallado. (Continúa...)

Curso de introducción a PyQGIS

Introducción

(...) Descubres que los ingredientes que compras para un plato pueden ser mezclados para hacer otro. Empiezas a quedarte un rato en el pasillo de las especias en el supermercado. La gente empieza a comprarte utensilios de cocina. Llegas al punto donde eres capaz de experimentar con recetas.

Aunque pocas personas se conviertan en chefs, muchos aprenden a cocinar suficientemente bien para satisfacer sus propias necesidades.


Si no programas, tu proceso de investigación siempre estará a merced de aquellos que lo hacen."

Traducido de http://sgillies.net

Curso de introducción a PyQGIS

Introducción

Así como en la cocina, en programación se cometen errores...

Curso de introducción a PyQGIS

Introducción

Así como en la cocina, en programación es normal olvidar ingredientes, y es normal recurrir a la documentación, o incluso al código de otros...

Curso de introducción a PyQGIS

Materiales del curso

Curso de introducción a PyQGIS

Python

Curso de introducción a PyQGIS

Introducción a Python

Guido van Rossum, principios de 90's
Inspirado en 'Monthy Python'
Lenguaje interpretado (semi)
Tipado dinámico y fuertemente tipado
Multiplataforma
Sintaxis sencilla que lo hace muy legible

Curso de introducción a PyQGIS

Bases de Python

¡Escribamos algo de Python!

Intérprete de Python

Tipos de datos

Condicionales y ciclos

Funciones

Curso de introducción a PyQGIS

PyQGIS

Curso de introducción a PyQGIS

Introducción

Curso de introducción a PyQGIS

Recursos y documentación

Curso de introducción a PyQGIS

¿Dónde usar Python en QGIS?

Curso de introducción a PyQGIS

¿Dónde usar Python en QGIS?

1. Consola de Python y editor de scripts

Ctrl

+

Alt

+

P

Curso de introducción a PyQGIS

¿Dónde usar Python en QGIS?

2. Macros del proyecto

Proyecto --> Propiedades...

Curso de introducción a PyQGIS

¿Dónde usar Python en QGIS?

3. Acciones

Propiedades de la capa --> Acciones

Curso de introducción a PyQGIS

¿Dónde usar Python en QGIS?

4. Expresiones personalizadas en Python

Diálogo de expresiones --> Editor de Funciones

Curso de introducción a PyQGIS

¿Dónde usar Python en QGIS?

5. Algoritmos de Geoprocesamiento

Ventana de Procesamiento --> Scripts

Curso de introducción a PyQGIS

¿Dónde usar Python en QGIS?

6. Plugins

Curso de introducción a PyQGIS

¿Dónde usar Python en QGIS?

7. Scripts 'standalone' (sin la GUI de QGIS)

Curso de introducción a PyQGIS

¡¡¡Empecemos!!!

1. Consola de Python y editor de scripts

Curso de introducción a PyQGIS

Pero antes de empezar...

¡Necesitamos saber qué es una clase y qué es un objeto!

Curso de introducción a PyQGIS

Programas más comprensibles y más fáciles de mantener.
¡Se basa en Objetos!
Los objetos:

Programación Orientada a Objetos

      Representan mejor la complejidad del mundo real.
      Tienen estado y comportamiento.
      Interactúan con otros objetos.

Curso de introducción a PyQGIS

Caso 1: Aplicaciones gráficas (Qt5).

     Los objetos son: ventanas, botones, diálogos, etc.
Caso 2: Aplicación para SIG (QGIS).

 


Conclusión:

Y... ¿Qué son los objetos?

"Un objeto es la representación de un concepto

en el sistema."

Los objetos son: tablas, capas vectoriales, capas ráster, un mapa, la simbología de una capa, botón 'Acercar', etc.

Curso de introducción a PyQGIS

Un objeto es una representación. Es concreto y particular.
    Ejemplo: Botón “Acercar mapa”, Botón "Panear".

Clases y objetos

Las clases se agrupan en librerías:
    Ejemplo: Librería Controles gráficos de usuario (GUI).

Incluso se puede hablar de cosas más generales, esto es,  

    de clases más abstractas. Ejemplo: Clase Botón.

Para conceptos (generales) se habla de Clases.
    Ejemplo: Clase Botón de navegación.

Curso de introducción a PyQGIS

Conceptos (abstractos) del sistema.
Pueden actuar como moldes o prototipos para crear objetos.
Tienen propiedades y métodos.

Clases

     La clase Botón puede tener...

         Propiedades como: Size, Text, Icon
         Métodos como: run()

     El objeto Botón "Acercar mapa" también tiene:

           Size (32x32), Text (""), Icon (acercar.png), run() (acercar.py)

Los objetos de una clase, heredan sus propiedades y métodos.

Curso de introducción a PyQGIS

Concluyendo sobre clases y objetos

Clase    (Concepto, plantilla, molde)
 

 

Objeto (Instancia de una clase)

Imágenes: https://www.iconfinder.com/icons/4835095/code_coding_page_programming_web_web_page_icon     

      https://www.iconfinder.com/icons/618630/bubble_dream_happy_idea_speech_talk_think_icon

Curso de introducción a PyQGIS

Comunicación con QGIS

Cuando abrimos QGIS, podemos acceder a sus objetos

QgisInterface
iface

Curso de introducción a PyQGIS

Escribiendo código en PyQGIS

Escribamos código Python para manipular información

geográfica...

 

 

 

Usando iface

Accediendo a capas y a sus propiedades

Creando campos

Análisis espacial (cruce de capas)

Modificando capas

Curso de introducción a PyQGIS

1. Consola de Python y editor de scripts

2. Macros del proyecto

¿Dónde usar Python en QGIS?

Curso de introducción a PyQGIS

Macros del proyecto

Fuente: https://definicion.de/macro/

"[...] secuencia de instrucciones almacenadas para posibilitar la ejecución organizada de una orden. Esto quiere decir que una macroinstrucción permite que una única orden desencadene una secuencia de varias instrucciones.

...

[...] Es posible almacenar una macro en el propio software en el que se ejecuta, ya sea mediante un botón o una cierta combinación de teclas."

Curso de introducción a PyQGIS

Macros del proyecto

openProject()

saveProject()

closeProject()

Curso de introducción a PyQGIS

Macros del proyecto: openProject()

Cuando se abra este proyecto... abrir la consola de Python!

Curso de introducción a PyQGIS

Macros del proyecto (discusión)

¿Otros casos de uso?

openProject()

saveProject()

closeProject()

¿Y si quiero una acción para todos mis proyectos?

Curso de introducción a PyQGIS

Script startup.py

Script para ejecutar código Python cada vez que inicia QGIS.

 

¿Dónde guardar el script?

/home/USER/.local/share/QGIS/QGIS3/

startup.py

C:\Users\USER\AppData\Roaming\QGIS\QGIS3\

Curso de introducción a PyQGIS

Script startup.py: Ejemplo 1

import qgis
from console import console

def open_console():
    if not console._console:
        # Initialize the console (QGIS knows how)
        qgis.utils.iface.actionShowPythonDialog().trigger()
        
    console._console.setVisible(True)

qgis.utils.iface.initializationCompleted.connect(open_console)

Curso de introducción a PyQGIS

Siempre que se inicie QGIS... abrir la consola de Python

Script startup.py: Ejemplo 2

from qgis.utils import iface
from console import console

if not console._console:
    # We need to initialize the console (QGIS knows how)
    # and respect the opened/closed state from last session
    iface.actionShowPythonDialog().trigger()
    console._console.setVisible(console._console.isUserVisible())

console._console.console.shell.runcode("""
L = None
def clc(layer):
    global L
    L = layer
iface.currentLayerChanged.connect(clc)
""")
console._console.console.shellOut.clearConsole()

Curso de introducción a PyQGIS

Siempre que se inicie QGIS...

enlazar variable "L" con la capa activa

¿Dónde usar Python en QGIS?

1. Consola de Python y editor de scripts

2. Macros del proyecto

3. Acciones

Curso de introducción a PyQGIS

Acciones

Capa

Feature (objeto)

Lienzo (mapa)

Campo

Curso de introducción a PyQGIS

Acciones: Ejemplo 1, Wikipedia

Curso de introducción a PyQGIS

Acciones: Ejemplo 2, mismo valor

Curso de introducción a PyQGIS

Acciones: Ejemplo 3, Export to GPX

3ra Jornada de Talleres Internos

Curso de introducción a PyQGIS

1. Consola de Python y editor de scripts

2. Macros del proyecto

3. Acciones

4. Expresiones personalizadas en Python

¿Dónde usar Python en QGIS?

Curso de introducción a PyQGIS

Buena forma de automatizar en QGIS... ¡sin programar!

 

Conjunto de funciones disponibles para seleccionar, filtrar, combinar, formatear, convertir, calcular, ...,  y agregar valores en QGIS.

 

Hay condicionales y también variables globales, del proyecto, de la capa, del usuario, etc.

 

¡Están por todos lados en QGIS!

 

A veces (muy pocas veces) NO encontramos la que necesitamos.

¡Pero podemos crear nuestras propias funciones... con Python!

Expresiones

Curso de introducción a PyQGIS

Editor de funciones

Expresiones personalizadas

Diálogo de expresiones --> Editor de Funciones

Curso de introducción a PyQGIS

Ejemplo: Dado un punto, obtener su valor de altura desde DEM

Expresiones personalizadas

from qgis.core import (qgsfunction, QgsProject, QgsRaster)

@qgsfunction(args='auto', group='Custom', usesgeometry=True)
def get_elevation(feature, parent):
    layers = QgsProject.instance().mapLayersByName("DEM")
    if layers: # Any layer called DEM?
        rLayer = layers[0]
        geom = feature.geometry()
        geom.convertToSingleType()  # Single points are required
        ident = rLayer.dataProvider().identify(geom.asPoint(), 
                                               QgsRaster.IdentifyFormatValue)
        if ident.isValid():
            return ident.results()[1]  # Read from band 1
    return NULL
raster_value('DEM', 1, $geometry)

Tiempo después...

Curso de introducción a PyQGIS

Ejemplo 2: Obtener un código QR

                    (usado para los certificados de QGIS Colombia)

Expresiones personalizadas

from qgis.core import (QgsProject, NULL, qgsfunction)

@qgsfunction(args='auto', group='Custom')
def qr(evento, taller, cc, feature, parent):
  if evento and taller and cc:
    import os, qrcode
    base_dir = QgsProject.instance().fileInfo().path()
    qr_dir = os.path.join(base_dir, "qr")
    filename = "{}-{}-{}".format(evento, taller, cc.replace('.',).replace("'",))
    url = "http://qgisusers.co/media/{}.pdf".format(filename)
    if not os.path.exists(qr_dir):
	    os.makedirs(qr_dir)

    filepath = os.path.join(qr_dir, "qr-{}.png".format(filename))
    qr = qrcode.QRCode(box_size=10,border=2)
    qr.add_data(url)
    qr.make(fit=True)
    img = qr.make_image()
    img.save(filepath)

    return filepath
  else:
    return NULL

Curso de introducción a PyQGIS

1. Consola de Python y editor de scripts

2. Macros del proyecto

3. Acciones

4. Expresiones personalizadas en Python

5. Algoritmos de Geoprocesamiento

¿Dónde usar Python en QGIS?

Curso de introducción a PyQGIS

En C++ o en Python.

 

Usando Python tenemos 2 opciones: clases o decoradores.

 

Reciben entradas (capas, números, etc.) y retornan salidas.

 

Escribimos metadatos y QGIS crea el diálogo automáticamente.

 

Útiles para reusarlos en modelos complejos, mezclando algoritmos incluso de otros proveedores.

Algoritmos de geoprocesamiento

Curso de introducción a PyQGIS

Recursos y documentación

Curso de introducción a PyQGIS

Widgets

Processing agrega widgets dependiendo del tipo de entrada

que elijamos...

Curso de introducción a PyQGIS

Nota: Los scripts con decoradores no pueden ser agregados a proveedores

          personalizados. Esto implica que no pueden instalarse desde plugins.

Scripts con decoradores

from qgis.processing import alg

@alg(name='my_script', label='Rename field from vector layer',
     group='examplescripts', group_label='Example scripts')

# 'INPUT' is the recommended name for the main input parameter
@alg.input(type=alg.VECTOR_LAYER, name='INPUT', label='Input vector layer')

# 'OUTPUT' is the recommended name for the main output parameter
@alg.output(type=alg.VECTOR_LAYER, name='OUTPUT',
            label='Vector layer with renamed field')

def my_script(instance, parameters, context, feedback, inputs):
    ...

Decoradores: "Syntactic sugar". Actúan sobre métodos y/o clases.

Curso de introducción a PyQGIS

Escribamos un

script de geoprocesamiento

con decoradores...

Ejemplo: Renombrar un campo

 

    Entradas:

        Vector layer

        Campo a renombrar

        Nombre nuevo para el campo

    Salidas:

        Vector layer

Curso de introducción a PyQGIS

Salidas

Opciones de salida para capas vectoriales:

 

      Modificar la capa de entrada y retornarla

 

      Ver ejemplo anterior (rename field)

 

      Crear una capa nueva y retornarla (capa original intacta)

 

      Para ello se usan los Feature Sink

Curso de introducción a PyQGIS

Reusando scripts en modelos

Curso de introducción a PyQGIS

1. Consola de Python y editor de scripts

2. Macros del proyecto

3. Acciones

4. Expresiones personalizadas en Python

5. Algoritmos de Geoprocesamiento

6. Plugins

¿Dónde usar Python en QGIS?

Curso de introducción a PyQGIS

Escribamos un ejemplo de P.O.O.

Curso de introducción a PyQGIS

Más de PyQt5

Curso de introducción a PyQGIS

Recursos y documentación

Curso de introducción a PyQGIS

Principales módulos de PyQt5

Core

 

GUI

 

Widgets

qgis.PyQt.QtCore
qgis.PyQt.QtGui
qgis.PyQt.QtWidgets

Funcionalidad núcleo no gráfica.

QObject, QFile, QSettings, QRegExp, SIGNALS-SLOTS...

Funcionalidad núcleo gráfica.

QIcon, QColor, QFont, QCursor, QMouseEvent...

Controles gráficos de usuario para interfaces.

QWidget, QDialog, QPushButton, QMessageBox, QMenu...

Curso de introducción a PyQGIS

Qt-Designer

Aplicación para

diseñar

interfaces

gráficas de Qt

 

 

Genera archivos

.ui (XML)

 

 

Permite:

   + Definir propiedades de los widgets.

   + Agrupar widgets en layouts.

   + Usar widgets propios de QGIS (custom widgets)

   + Administrar recursos (archivos externos, ej. íconos).  

Curso de introducción a PyQGIS

Widgets de Qt

Curso de introducción a PyQGIS

Layouts

Agrupan y organizan

widgets en una interfaz

 

 

Layouts más usados:

   Horizontal/Vertical

   Grid

   Form

 

Curso de introducción a PyQGIS

QGIS custom widgets

Curso de introducción a PyQGIS

SIGNALS-SLOTS

Comunicación

entre objetos de Qt.

 

Los objetos no se conocen entre sí.

(Bajo acoplamiento)

 

Los objetos emiten señales, no saben si

alguien las recibe o no. (Encapsulamiento)

SIGNALS

  Se emiten cuando ocurre un evento.

  Objetos de Qt tienen SIGNALS predefinidas.

  Podemos crear nuevas SIGNALS y emitirlas.

SLOTS

  Función llamada en respuesta a una SIGNAL.

  Objetos de Qt tienen SLOTS predefinidos.

  Podemos crear nuevos SLOTS.

Curso de introducción a PyQGIS

SIGNALS-SLOTS

Las conexiones SIGNAL-SLOT

las hace el(la) desarrollador(a).

SIGNALS

  Pueden conectarse a cero o muchos SLOTS.

  Pueden conectarse a otras SIGNALS.

SLOTS

  Pueden conectarse a cero o muchas SIGNALS.

  Como son funciones, podemos llamarlas.

Curso de introducción a PyQGIS

QGIS Plugins

Curso de introducción a PyQGIS

Copialina...

Curso de introducción a PyQGIS

Arquitectura de los plugins de QGIS

Curso de introducción a PyQGIS

Funciones de los plugins de QGIS

__init__(iface)

 

initGUI()

 

 

unload()

 

 

run()

Crear GUI del plugin en QGIS (¿toolbar?, ¿menú?).

Definir conexiones SIGNAL-SLOT.

Remover GUI del plugin (toolbars, menús, etc.) de QGIS.

Desconectar SIGNAL-SLOTs (si es necesario).

Procesar datos o llamar diálogos del plugin.

(opcional)

Obtener acceso a objeto iface para interactuar con QGIS.

Curso de introducción a PyQGIS

Funciones de los plugins de QGIS

__init__(iface), initGUI(), unload(), run()

¡Veámoslas en acción!

Icon adapted from https://www.iconfinder.com/paomedia

Clic a la imagen :)

Curso de introducción a PyQGIS

Plugins para desarrolladores

Plugin reloader

First aid

Plugin builder

(Opcional)

Curso de introducción a PyQGIS

QGIS Minimal Plugin

1. Ve a la carpeta de plugins de QGIS (ver instrucciones).

 

2. En esa carpeta crea una carpeta llamada "minimal".

 

3. Ve a https://github.com/wonder-sk/qgis-minimal-plugin

 

4. Crea los archivos __init__.py y metadata.txt copiando

    el contenido del QGIS Minimal Plugin de Martin Dobias.

 

5. Reinicia QGIS y activa el plugin "Minimal".

Curso de introducción a PyQGIS

Plugin con Diálogo

Extendamos el Minimal Plugin...

Curso de introducción a PyQGIS

Plugin con QGIS Custom Widgets

Extendamos el plugin con diálogo...

Curso de introducción a PyQGIS

Plugin Spatial Join (demo)

Un plugin un poco más completo...

Curso de introducción a PyQGIS

1. Consola de Python y editor de scripts

2. Macros del proyecto

3. Acciones

4. Expresiones personalizadas en Python

5. Algoritmos de Geoprocesamiento

6. Plugins

7. Scripts 'standalone' (sin la GUI de QGIS)

¿Dónde usar Python en QGIS?

Curso de introducción a PyQGIS

Casos de uso de scripts standalone

Se busca generar productos diarios/semanales/mensuales (vectores, rásters, mapas, reportes, estadísticas) a partir de una BD que se alimenta con frecuencia.

 

Se busca ejecutar periódicamente un ETL, obteniendo datos de diferentes servicios web de fuentes oficiales para almacenarlos en una BD local.

 

Una entidad oficial obtiene datos de terceros mediante formulario web y quiere hacer una validación de calidad automática.

Curso de introducción a PyQGIS

Configuración scripts standalone

Windows

 

 

 

 

 

GNU/Linux

 

Open OSGeo4w Shell --> Run python-qgis-ltr.bat

Prepare environment with PYTHONPATH and LD_LIBRARY_PATH.
       export PYTHONPATH=/path/to/qgis/output/python/
       export LD_LIBRARY_PATH=/path/to/qgis/lib/

Curso de introducción a PyQGIS

¡Cuidado!

¡No tenemos iface!

Curso de introducción a PyQGIS

Script standalone 1: Layer info

from qgis.core import (QgsApplication, QgsVectorLayer)

# Details about the prefix: https://gis.stackexchange.com/a/155852/4972
# Hint: print(QgsApplication.showSettings())
QgsApplication.setPrefixPath("/docs/dev/qgis/QGIS/3_14_0/output/", True)
qgs = QgsApplication([], False)  # No GUI
qgs.initQgis()

airport_layer = QgsVectorLayer("/path/to/data.gpkg|layername=Airports", 
                               "airport", 
                               "ogr")

print("Layer name:", airport_layer.name())
print("Layer is valid:", airport_layer.isValid())

qgs.exit()

Curso de introducción a PyQGIS

Script standalone 2: Run algorithm

# Source: https://gis.stackexchange.com/a/279937/4972
import sys

from qgis.core import QgsApplication
from qgis.analysis import QgsNativeAlgorithms

QgsApplication.setPrefixPath('/docs/dev/qgis/QGIS/3_14_0/output', True)
qgs = QgsApplication([], False)
qgs.initQgis()

# Append the path where processing plugin can be found
sys.path.append('/docs/dev/qgis/QGIS/3_14_0/output/python/plugins')

import processing
from processing.core.Processing import Processing
Processing.initialize()
QgsApplication.processingRegistry().addProvider(QgsNativeAlgorithms())

params = {'POLYGONS':'/path/to/polygons.gpkg',
          'LINES':'/path/to/lines.gpkg',
          'LEN_FIELD':'LENGTH',
          'COUNT_FIELD':'COUNT',
          'OUTPUT':'TEMPORARY_OUTPUT'}
res = processing.run("native:sumlinelengths", params)

for feature in res['OUTPUT'].getFeatures():
    print(feature['name'], feature['LENGTH'])

qgs.exit()

Curso de introducción a PyQGIS

Out of topic: qgis_process

$ qgis_process run native:sumlinelengths 
    --POLYGONS="/path/to/polygons.gpkg"
    --LINES="/path/to/lines.gpkg" 
    --LEN_FIELD=LENGTH 
    --COUNT_FIELD=COUNT 
    --OUTPUT=/tmp/gpkg/output_sum.gpkg

Curso de introducción a PyQGIS

¿Ves alguna diferencia?

Hay otros escenarios en donde puedes usar Python con QGIS:

 

 

 

Otros escenarios (avanzados)

 Aplicaciones gráficas standalone           Plugins de QGIS Server

Curso de introducción a PyQGIS

PyQGIS samples:

    https://github.com/webgeodatavore/pyqgis-samples

Victor Olaya's QGIS Python Course:

    https://github.com/volaya/qgis-python-course

Diagrama de clases PyQGIS (cuidado, es para QGIS v2!):

    https://labs.webgeodatavore.com/partage/diagramme_principal.html

PyQGIS in a day:

    https://courses.spatialthoughts.com/pyqgis-in-a-day.html

Curso PyQGIS 3 impartido junto a M. Kuhn en Colombia:

    https://www.youtube.com/watch?v=QeT4iij4Jbo

Otros enlaces de interés...

Curso de introducción a PyQGIS

¡Ahora sigues tú!

Eso       es todo...

Eso no es todo...

Curso de introducción a PyQGIS

Made with Slides.com