Es de sobra conocido que Python y QGIS se complementan a la perfección, permitiéndonos automatizar procesos con nuestras capas a partir de código. Normalmente, escribimos el código y lo ejecutamos directamente desde el editor de código de QGIS.

No obstante, es posible generar una herramienta que ejecute nuestro código, para que de esta manera podamos ejecutar el proceso sin necesidad de cambiar algunas líneas, como por ejemplo el directorio en el que se encuentran las capas que queremos utilizar, o el directorio de salida.

En el ejemplo de hoy vamos a generar una herramienta que utilice el mismo algoritmo que el que usamos en la entrada “Cómo recortar todos los ficheros ráster de una misma carpeta con PyQGIS”, solo que esta vez una vez creado el código no tendremos que volver a editarlo, pudiendo seleccionar las capas desde la interfaz.

Para el tutorial utilizaremos también los mismos datos que en la otra entrada, que puedes descargar desde este enlace. Una vez descargados, guardaremos los ráster en la carpeta que queramos. Como máscara de recorte de los ráster, vamos a utilizar también la misma capa con la provincia de Cádiz. La capa de provincias se puede descargar del Centro de Descargas del IGN (CNIG), dentro de “Información Geográfica de Referencia”, extrayendo posteriormente mediante una selección por atributos la provincia de Cádiz.

Una vez tenemos las capas nos ponemos con el código. Para editar el código, vamos a utilizar en este ejemplo el editor de scripts de procesamiento de QGIS. Para crear el script vamos a la caja de herramientas de procesos y le damos a “Crear script nuevo”. Guardamos el código donde queramos.

Lo primero es importar todas las librerías que vamos a utilizar, tanto de QGIS como de rutas de archivos. Después, crearemos una clase con el algoritmo de procesamiento, que herede de la clase QgsProcessingAlgorithm. Definiremos el input, la máscara y el output, al igual que la ruta de base, que será la carpeta de usuario (Path.home()).

/
from PyQt5.QtCore import QCoreApplication, QVariant
from qgis.core import   (QgsProject,
                        QgsLayerTreeLayer,
                        QgsProcessingParameterFile,
                        QgsProcessingAlgorithm,
                        QgsProcessingParameterVectorLayer,
                        QgsProcessingParameterFolderDestination,
                        QgsRasterLayer
                        )
import processing
import glob
from pathlib import Path
import os

class ClipProcessingAlgorithm(QgsProcessingAlgorithm):
    INPUT = 'INPUT'
    OUTPUT = 'OUTPUT'
    MASK = 'OVERLAY'
    basepath = Path().home()
/

Dentro de la clase definimos algunas funciones para almacenar el nombre propio de la herramienta (“name”), el nombre que aparecerá al abrir la interfaz (“displayName”), o el texto que aparecerá dentro de la interfaz (“tr”).

/
    def tr(self, string):
        return QCoreApplication.translate('Processing', string)

    def createInstance(self):
        return ClipProcessingAlgorithm()

    def name(self):
        return 'recorte_capa'

    def displayName(self):
        return self.tr('Recortar capa')
/

Una función importante es “initAlgorithm()”, dentro de la cual definimos los parámetros de la herramienta. Como INPUT utilizaremos en este caso QgsProcessingParemeterFile(), definiendo el texto (“Input folder”) y especificando “behavior = 1”, para que de esta manera se haga una selección por carpeta, en vez de por archivo (en ese caso sería 0).

Como segundo parámetro utilizaremos QgsProcessingParameterVectorLayer(), pues la máscara que vamos a utilizar va a ser una capa vectorial. Definimos que la máscara será la variable “MASK”. Por último, añadiremos un tercer parámetro, que será el directorio de salida. Para ello utilizamos QgsProcessingParameterFolderDestination(), especificando la variable “OUTPUT”, el texto, y como valor predeterminado la carpeta de usuario.

/
    def initAlgorithm(self, config=None):
        self.addParameter(
            QgsProcessingParameterFile(
                self.INPUT,
                self.tr('Input folder'),
                behavior=1
            )
        )
        self.addParameter(
            QgsProcessingParameterVectorLayer(
                self.MASK,
                self.tr('Input layer mask')
            )
        )
        self.addParameter(
            QgsProcessingParameterFolderDestination(
                self.OUTPUT,
                self.tr('Output folder'),
                defaultValue = self.basepath.as_posix()
            )
        )
/

Tras definir los parámetros, entramos de lleno al procesamiento del algoritmo. Creamos una función para el procesamiento: “processAlgorithm()”. Como argumentos a la función es importante pasarle los parámetros, pero también el “context” y el “feedback” para pasar información a los algoritmos hijos y para informar al usuario del proceso. Dentro de la función recuperamos los parámetros definidos en la anterior función. El primer parámetro lo recuperamos con “parameterAsFile()”, el segundo al ser una capa vectorial con “parameterAsVectorLayer()”, y la salida con “parameterAsFileOutput()”.

/
    def processAlgorithm(self, parameters, context, feedback):
        raster_folder = self.parameterAsFile(
            parameters,
            'INPUT',
            context)

    overlay = self.parameterAsVectorLayer(
        parameters,
        'OVERLAY',
        context)

    output = self.parameterAsFileOutput(
        parameters,
        'OUTPUT',
        context)
/

Lo siguiente es generar una variable con la ruta absoluta de la carpeta donde tenemos los ráster de entrada y crear un array vacío denominado “recortes”, donde vamos a almacenar las rutas de cada uno de los ráster recortados. También generamos una variable de búsqueda de los ficheros que tengan formato “*.tif” dentro de la ruta de entrada especificada. Un aspecto importante a la hora de incorporar posteriormente las capas del proyecto es que debemos acceder al árbol de capas del proyecto para añadirle específicamente las capas.

/
    ruta = os.path.abspath(raster_folder)
    
    recortes = []
    
    glob_path = os.path.join(ruta, '*.tif')
    project = QgsProject.instance()
    layerTree = project.layerTreeRoot()
/

Lo siguiente es crear un bucle “for” para iterar dentro de la carpeta de entrada donde se encuentran los ráster a recortar. Definimos una variable para almacenar el nombre del fichero, que en este será la ruta unida al nombre de la capa de la máscara y el nombre del ráster. En la consola de procesamiento aparecerá la ruta. También generaremos una condición para que, si existe el fichero con el mismo nombre, lo elimine previamente.

/
    for lyr in glob.glob(glob_path):
        output_path = os.path.join(output, "{}_{}".format(overlay.name(), os.path.basename(lyr)))
        feedback.pushConsoleInfo(output_path)
        if os.path.isfile(output_path):
            os.remove(output_path)
/

Usamos el algoritmo con ID “gdal:cliprasterbymasklayer” y especificamos los parámetros. Al array que hemos creado antes, con la función append() le iremos añadiendo las salidas del algoritmo. Creamos una variable para almacenar la capa ráster con QgsRasterLayer(). El nombre de la capa del proyecto será el mismo resultante de la herramienta, eliminándole “.tif”. Todo esto va incluido dentro de un try/except.

/
    try:
        recorte = processing.run("gdal:cliprasterbymasklayer", {
            'ALPHA_BAND': False,
            'CROP_TO_CUTLINE': True,
            'DATA_TYPE': 0,
            'INPUT': lyr,
            'KEEP_RESOLUTION': False,
            'MASK': overlay,
            'MULTITHREADING': False,
            'OPTIONS': '',
            'OUTPUT': output_path,
            'SET_RESOLUTION': False,
            'SOURCE_CRS': None,
            'TARGET_CRS': None,
            'X_RESOLUTION': None,
            'Y_RESOLUTION': None
        }, context=context, feedback=feedback, is_child_algorithm=True)['OUTPUT']
        recortes.append(recorte)
        raster_layer = QgsRasterLayer(recorte, os.path.basename(recorte)[:-4], 'gdal')
    except:
        feedback.pushConsoleInfo("Error")
/

Después, generaremos una condición “if” para saber si la capa es válida. Si es válida, se mostrará la ruta de la capa y se añadirá al proyecto con QgsProject.instance().addMapLayer(), e insertaremos dentro del árbol da capa cada una de las capas ráster. Por último, devolvemos los resultados del algoritmo como un diccionario con “return”.

/
    for recorte in recortes:
        raster_layer = QgsRasterLayer(recorte, os.path.basename(recorte)[:-4], 'gdal')
        if raster_layer.isValid():
            feedback.pushConsoleInfo("{} is valid".format(os.path.basename(recorte)))
            project.instance().addMapLayer(raster_layer, addToLegend=True)
            layerTree.insertChildNode(-1, QgsLayerTreeLayer(raster_layer))

    return {self.OUTPUT: recortes}
/

Esto es solo un ejemplo de cómo aplicar un algoritmo que ya existe en QGIS con nuestro código y utilizar una interfaz para la selección de capas, pero esto se puede ir complicando mucho más e ir añadiendo multitud de funcionalidades a nuestra herramienta.

Si quieres aprender otros muchos aspectos sobre PyQGIS tenemos a vuestra disposición nuestro curso de Python en QGIS. Para más información no dude en contactarnos.

Para terminar la entrada, os comparto el código completo del tutorial.

/
from PyQt5.QtCore import QCoreApplication, QVariant
from qgis.core import (QgsProject,
                        QgsLayerTreeLayer,
                        QgsProcessingParameterFile,
                        QgsProcessingAlgorithm,
                        QgsProcessingParameterVectorLayer,
                        QgsProcessingParameterFolderDestination,
                        QgsRasterLayer
                        )
import processing
import glob
from pathlib import Path
import os

class ClipProcessingAlgorithm(QgsProcessingAlgorithm):
    INPUT = 'INPUT'
    OUTPUT = 'OUTPUT'
    MASK = 'OVERLAY'
    basepath = Path().home()

    def tr(self, string):
        return QCoreApplication.translate('Processing', string)

    def createInstance(self):
        return ClipProcessingAlgorithm()

    def name(self):
        return 'recorte_capa'

    def displayName(self):
        return self.tr('Recortar capa')

    def initAlgorithm(self, config=None):
        self.addParameter(
            QgsProcessingParameterFile(
                self.INPUT,
                self.tr('Input folder'),
                behavior=1
            )
        )
        self.addParameter(
            QgsProcessingParameterVectorLayer(
                self.MASK,
                self.tr('Input layer mask')
            )
        )
        self.addParameter(
            QgsProcessingParameterFolderDestination(
                self.OUTPUT,
                self.tr('Output folder'),
                defaultValue = self.basepath.as_posix()
            )
        )

    def processAlgorithm(self, parameters, context, feedback):
        raster_folder = self.parameterAsFile(
            parameters,
            'INPUT',
            context)

        overlay = self.parameterAsVectorLayer(
            parameters,
            'OVERLAY',
            context)

        output = self.parameterAsFileOutput(
            parameters,
            'OUTPUT',
            context)

        ruta = os.path.abspath(raster_folder)
        feedback.pushConsoleInfo("Will crop raster layers in {}".format(raster_folder))

        recortes = []

        glob_path = os.path.join(ruta, '*.tif')
        project = QgsProject.instance()
        layerTree = project.layerTreeRoot()

        for lyr in glob.glob(glob_path):
            output_path = os.path.join(output, "{}_{}".format(overlay.name(), os.path.basename(lyr)))
            feedback.pushConsoleInfo(output_path)
            if os.path.isfile(output_path):
                os.remove(output_path)

            try:
                recorte = processing.run("gdal:cliprasterbymasklayer", {
                    'ALPHA_BAND': False,
                    'CROP_TO_CUTLINE': True,
                    'DATA_TYPE': 0,
                    'INPUT': lyr,
                    'KEEP_RESOLUTION': False,
                    'MASK': overlay,
                    'MULTITHREADING': False,
                    'OPTIONS': '',
                    'OUTPUT': output_path,
                    'SET_RESOLUTION': False,
                    'SOURCE_CRS': None,
                    'TARGET_CRS': None,
                    'X_RESOLUTION': None,
                    'Y_RESOLUTION': None
                }, context=context, feedback=feedback, is_child_algorithm=True)['OUTPUT']
                recortes.append(recorte)

            except:
                feedback.pushConsoleInfo("Error")

        for recorte in recortes:
            raster_layer = QgsRasterLayer(recorte, os.path.basename(recorte)[:-4], 'gdal')
            if raster_layer.isValid():
                feedback.pushConsoleInfo("{} is valid".format(os.path.basename(recorte)))
                project.instance().addMapLayer(raster_layer, addToLegend=True)
                layerTree.insertChildNode(-1, QgsLayerTreeLayer(raster_layer))

        return {self.OUTPUT: recortes}/

1 estrella2 estrellas3 estrellas4 estrellas5 estrellas (1 votos, promedio: 5,00 de 5)

Cargando…

Formación de calidad impartida por profesionales