22.8. Escribir nuevos algoritmos de procesamiento como scripts de Python

Hay dos opciones para escribir algoritmos de procesamiento usando Python.

Dentro de QGIS, puede usar Crear nuevo script en el menú :guilabel:` Scripts` en la parte superior de Barra de Herramientas Procesos para abrir Editor de Scripst de Procesos donde puede escribir tu codigo. Para simplificar la tarea, puede comenzar con una plantilla de script usando Crear nuevo script desde plantilla del mismo menú. Esto abre una plantilla que se extiende QgsProcessingAlgorithm.

Si guarda el script en la carpeta: file: scripts (la ubicación predeterminada) con la extensión .py, el algoritmo estará disponible en Caja de Herramientas de Procesos.

22.8.1. Extendiendo QgsProcessingAlgorithm

El siguiente código

  1. toma una capa vectorial como entrada

  2. cuenta el número de objetos

  3. hace una operación de buffer

  4. crea una capa ráster a partir del resultado de la operación buffer

  5. devuelve la capa buffer, la capa ráster y el número de objetos

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
from qgis.PyQt.QtCore import QCoreApplication
from qgis.core import (QgsProcessing,
                       QgsProcessingAlgorithm,
                       QgsProcessingException,
                       QgsProcessingOutputNumber,
                       QgsProcessingParameterDistance,
                       QgsProcessingParameterFeatureSource,
                       QgsProcessingParameterVectorDestination,
                       QgsProcessingParameterRasterDestination)
from qgis import processing


class ExampleProcessingAlgorithm(QgsProcessingAlgorithm):
    """
    This is an example algorithm that takes a vector layer,
    creates some new layers and returns some results.
    """

    def tr(self, string):
        """
        Returns a translatable string with the self.tr() function.
        """
        return QCoreApplication.translate('Processing', string)

    def createInstance(self):
        # Must return a new copy of your algorithm.
        return ExampleProcessingAlgorithm()

    def name(self):
        """
        Returns the unique algorithm name.
        """
        return 'bufferrasterextend'

    def displayName(self):
        """
        Returns the translated algorithm name.
        """
        return self.tr('Buffer and export to raster (extend)')

    def group(self):
        """
        Returns the name of the group this algorithm belongs to.
        """
        return self.tr('Example scripts')

    def groupId(self):
        """
        Returns the unique ID of the group this algorithm belongs
        to.
        """
        return 'examplescripts'

    def shortHelpString(self):
        """
        Returns a localised short help string for the algorithm.
        """
        return self.tr('Example algorithm short description')

    def initAlgorithm(self, config=None):
        """
        Here we define the inputs and outputs of the algorithm.
        """
        # 'INPUT' is the recommended name for the main input
        # parameter.
        self.addParameter(
            QgsProcessingParameterFeatureSource(
                'INPUT',
                self.tr('Input vector layer'),
                types=[QgsProcessing.TypeVectorAnyGeometry]
            )
        )
        self.addParameter(
            QgsProcessingParameterVectorDestination(
                'BUFFER_OUTPUT',
                self.tr('Buffer output'),
            )
        )
        # 'OUTPUT' is the recommended name for the main output
        # parameter.
        self.addParameter(
            QgsProcessingParameterRasterDestination(
                'OUTPUT',
                self.tr('Raster output')
            )
        )
        self.addParameter(
            QgsProcessingParameterDistance(
                'BUFFERDIST',
                self.tr('BUFFERDIST'),
                defaultValue = 1.0,
                # Make distance units match the INPUT layer units:
                parentParameterName='INPUT'
            )
        )
        self.addParameter(
            QgsProcessingParameterDistance(
                'CELLSIZE',
                self.tr('CELLSIZE'),
                defaultValue = 10.0,
                parentParameterName='INPUT'
            )
        )
        self.addOutput(
            QgsProcessingOutputNumber(
                'NUMBEROFFEATURES',
                self.tr('Number of features processed')
            )
        )

    def processAlgorithm(self, parameters, context, feedback):
        """
        Here is where the processing itself takes place.
        """
        # First, we get the count of features from the INPUT layer.
        # This layer is defined as a QgsProcessingParameterFeatureSource
        # parameter, so it is retrieved by calling
        # self.parameterAsSource.
        input_featuresource = self.parameterAsSource(parameters,
                                                     'INPUT',
                                                     context)
        numfeatures = input_featuresource.featureCount()

        # Retrieve the buffer distance and raster cell size numeric
        # values. Since these are numeric values, they are retrieved
        # using self.parameterAsDouble.
        bufferdist = self.parameterAsDouble(parameters, 'BUFFERDIST',
                                            context)
        rastercellsize = self.parameterAsDouble(parameters, 'CELLSIZE',
                                                context)
        if feedback.isCanceled():
            return {}
        buffer_result = processing.run(
            'native:buffer',
            {
                # Here we pass on the original parameter values of INPUT
                # and BUFFER_OUTPUT to the buffer algorithm.
                'INPUT': parameters['INPUT'],
                'OUTPUT': parameters['BUFFER_OUTPUT'],
                'DISTANCE': bufferdist,
                'SEGMENTS': 10,
                'DISSOLVE': True,
                'END_CAP_STYLE': 0,
                'JOIN_STYLE': 0,
                'MITER_LIMIT': 10
            },
            # Because the buffer algorithm is being run as a step in
            # another larger algorithm, the is_child_algorithm option
            # should be set to True
            is_child_algorithm=True,
            #
            # It's important to pass on the context and feedback objects to
            # child algorithms, so that they can properly give feedback to
            # users and handle cancelation requests.
            context=context,
            feedback=feedback)

        # Check for cancelation
        if feedback.isCanceled():
            return {}

        # Run the separate rasterization algorithm using the buffer result
        # as an input.
        rasterized_result = processing.run(
            'qgis:rasterize',
            {
                # Here we pass the 'OUTPUT' value from the buffer's result
                # dictionary off to the rasterize child algorithm.
                'LAYER': buffer_result['OUTPUT'],
                'EXTENT': buffer_result['OUTPUT'],
                'MAP_UNITS_PER_PIXEL': rastercellsize,
                # Use the original parameter value.
                'OUTPUT': parameters['OUTPUT']
            },
            is_child_algorithm=True,
            context=context,
            feedback=feedback)

        if feedback.isCanceled():
            return {}

        # Return the results
        return {'OUTPUT': rasterized_result['OUTPUT'],
                'BUFFER_OUTPUT': buffer_result['OUTPUT'],
                'NUMBEROFFEATURES': numfeatures}

Funciones estándar del algoritmo de Porcesamiento:

  • createInstance (obligatorio)

    Debe devolver una nueva copia de su algoritmo. Si cambia el nombre de la clase, asegúrese de actualizar también el valor devuelto aquí para que coincida.

  • nombre (obligatorio)

    Devuelve el nombre exclusivo del algoritmo, que se utiliza para identificar el algoritmo.

  • displayName (obligatorio)

    Devuelve el nombre del algoritmo traducido.

  • grupo

    Devuelve el nombre del grupo al que pertenece este algoritmo.

  • groupId

    Devuelve el ID único del grupo al que pertenece este algoritmo.

  • shortHelpString

    Devuelve una cadena de ayuda corta localizada para el algoritmo.

  • initAlgorithm (obligatorio)

    Aquí definimos las entradas y salidas del algoritmo.

    INPUT y OUTPUT son nombres recomendados para los parámetros de entrada y salida principal, respectivamente.

    Si un parámetro depende de otro parámetro, se usa parentParameterName para especificar esta relación (podría ser el campo / banda de una capa o las unidades de distancia de una capa).

  • processAlgorithm (obligatorio)

    Aquí es donde tiene lugar el procesamiento.

    Los parámetros se recuperan mediante funciones especiales, por ejemplo, parameterAsSource y parameterAsDouble.

    processing.run se puede utilizar para ejecutar otros algoritmos de procesamiento desde un algoritmo de procesamiento. El primer parámetro es el nombre del algoritmo, el segundo es un diccionario de los parámetros del algoritmo. Is_child_algorithm normalmente se establece en True cuando se ejecuta un algoritmo desde dentro de otro algoritmo. El contexto y la retroalimentación informan al algoritmo sobre el entorno en el que se ejecutará y el canal para comunicarse con el usuario (capturando la solicitud de cancelación, reportando el progreso, proporcionando retroalimentación textual). Cuando se utilizan los parámetros del algoritmo (principal) como parámetros de los algoritmos «secundarios», se deben utilizar los valores de los parámetros originales (por ejemplo, parámetros ['OUTPUT']).

    ¡Es una buena práctica verificar el objeto de retroalimentación para la cancelación tanto como sea posible! Hacerlo permite una cancelación receptiva, en lugar de obligar a los usuarios a esperar a que ocurra un procesamiento no deseado.

    El algoritmo debe devolver valores para todos los parámetros de salida que ha definido como diccionario. En este caso, eso es el búfer y las capas de salida rasterizadas, y el recuento de entidades procesadas. Las claves del diccionario deben coincidir con los nombres de salida / parámetros originales.

22.8.2. El decorador @alg

Con el decorador @alg, puede crear sus propios algoritmos escribiendo el código Python y agregando algunas líneas adicionales para proporcionar la información adicional necesaria para convertirlo en un algoritmo de procesamiento adecuado. Esto simplifica la creación de algoritmos y la especificación de entradas y salidas.

Una limitación importante con el enfoque del decorador es que los algoritmos creados de esta manera siempre se agregarán al proveedor de Scripts de procesamiento de un usuario; no es posible agregar estos algoritmos a un proveedor personalizado, p. Ej. para su uso en complementos.

EL siguiente ejemplo usa el decorador @alg a

  1. usa una capa vectorial como entrada

  2. cuenta el número de objetos

  3. hace una operación buffer

  4. crea una capa ráster a partir del resultado de la operación buffer

  5. devuelve la capa buffer, la capa ráster y el número de objetos

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
from qgis import processing
from qgis.processing import alg
from qgis.core import QgsProject

@alg(name='bufferrasteralg', label='Buffer and export to raster (alg)',
     group='examplescripts', group_label='Example scripts')
# 'INPUT' is the recommended name for the main input parameter
@alg.input(type=alg.SOURCE, name='INPUT', label='Input vector layer')
# 'OUTPUT' is the recommended name for the main output parameter
@alg.input(type=alg.RASTER_LAYER_DEST, name='OUTPUT',
           label='Raster output')
@alg.input(type=alg.VECTOR_LAYER_DEST, name='BUFFER_OUTPUT',
           label='Buffer output')
@alg.input(type=alg.DISTANCE, name='BUFFERDIST', label='BUFFER DISTANCE',
           default=1.0)
@alg.input(type=alg.DISTANCE, name='CELLSIZE', label='RASTER CELL SIZE',
           default=10.0)
@alg.output(type=alg.NUMBER, name='NUMBEROFFEATURES',
            label='Number of features processed')

def bufferrasteralg(instance, parameters, context, feedback, inputs):
    """
    Description of the algorithm.
    (If there is no comment here, you will get an error)
    """
    input_featuresource = instance.parameterAsSource(parameters,
                                                     'INPUT', context)
    numfeatures = input_featuresource.featureCount()
    bufferdist = instance.parameterAsDouble(parameters, 'BUFFERDIST',
                                            context)
    rastercellsize = instance.parameterAsDouble(parameters, 'CELLSIZE',
                                                context)
    if feedback.isCanceled():
        return {}
    buffer_result = processing.run('native:buffer',
                               {'INPUT': parameters['INPUT'],
                                'OUTPUT': parameters['BUFFER_OUTPUT'],
                                'DISTANCE': bufferdist,
                                'SEGMENTS': 10,
                                'DISSOLVE': True,
                                'END_CAP_STYLE': 0,
                                'JOIN_STYLE': 0,
                                'MITER_LIMIT': 10
                                },
                               is_child_algorithm=True,
                               context=context,
                               feedback=feedback)
    if feedback.isCanceled():
        return {}
    rasterized_result = processing.run('qgis:rasterize',
                               {'LAYER': buffer_result['OUTPUT'],
                                'EXTENT': buffer_result['OUTPUT'],
                                'MAP_UNITS_PER_PIXEL': rastercellsize,
                                'OUTPUT': parameters['OUTPUT']
                               },
                               is_child_algorithm=True, context=context,
                               feedback=feedback)
    if feedback.isCanceled():
        return {}
    return {'OUTPUT': rasterized_result['OUTPUT'],
            'BUFFER_OUTPUT': buffer_result['OUTPUT'],
            'NUMBEROFFEATURES': numfeatures}

Como puede ver, involucra dos algoritmos (“native:buffer” y “qgis:rasterize”). El último (“qgis:rasterize”) crea una capa ráster a partir de la capa de búfer que fue generada por el primero (“native:buffer”).

La parte del código donde tiene lugar este procesamiento no es difícil de entender si ha leído el capítulo anterior. Sin embargo, las primeras líneas necesitan una explicación adicional. Proporcionan la información que se necesita para convertir su código en un algoritmo que se puede ejecutar desde cualquiera de los componentes de la GUI, como la caja de herramientas o el modelador gráfico.

Todas estas líneas son llamadas a las funciones del decorador @alg que ayudan a simplificar la codificación del algoritmo.

  • El decorador @alg se utiliza para definir el nombre y la ubicación del algoritmo en la Caja de herramientas.

  • El decorador @alg.input se utiliza para definir las entradas del algoritmo.

  • El decorador @alg.output se utiliza para definir las salidas del algoritmo.

22.8.3. Tipos de entrada y salida para algoritmos de procesamiento

Aquí está la lista de tipos de entrada y salida que son compatibles con Procesos con sus correspondientes constantes de decorador de alg (algfactory.py contiene la lista completa de constantes de alg). Ordenado por nombre de clase.

22.8.3.1. Tipos de entrada

Clase

Alg constante

Descripción

QgsProcessingParameterAuthConfig

alg.AUTH_CFG

Permite a los usuarios seleccionar entre las configuraciones de autenticación disponibles o crear nuevas configuraciones de autenticación

QgsProcessingParameterBand

alg.BAND

Una banda de una capa ráster

QgsProcessingParameterBoolean

alg.BOOL

Un valor booleano

QgsProcessingParameterColor

alg.COLOR

Un color

QgsProcessingParameterCrs

alg.CRS

Un Sistema de Coordenadas de Referencia

QgsProcessingParameterDistance

alg.DISTANCE

Un parámetro numérico doble para valores de distancia

QgsProcessingParameterEnum

alg.ENUM

Una enumeración, permitiendo la selección de un conjunto de valores predefinidos

QgsProcessingParameterExpression

alg.EXPRESSION

Una expresión

QgsProcessingParameterExtent

alg.EXTENT

Una extensión espacial definida por xmin, xmax, ymin, ymax

QgsProcessingParameterField

alg.FIELD

Un campo en la tabla de atributos de una capa vectorial

QgsProcessingParameterFile

alg.FILE

Un nombre de archivo de un archivo existente

QgsProcessingParameterFileDestination

alg.FILE_DEST

Un nombre de archivo para un archivo de salida recién creado

QgsProcessingParameterFolderDestination

alg.FOLDER_DEST

Una carpeta

QgsProcessingParameterNumber

alg.INT

Un entero

QgsProcessingParameterLayout

alg.LAYOUT

Un diseño

QgsProcessingParameterLayoutItem

alg.LAYOUT_ITEM

Un elemento de diseño

QgsProcessingParameterMapLayer

alg.MAPLAYER

Una capa de mapa

QgsProcessingParameterMatrix

alg.MATRIX

Una matriz

QgsProcessingParameterMeshLayer

alg.MESH_LAYER

Una capa de malla

QgsProcessingParameterMultipleLayers

alg.MULTILAYER

Un conjunto de capas

QgsProcessingParameterNumber

alg.NUMBER

Un valor numérico

QgsProcessingParameterPoint

alg.POINT

Un punto

QgsProcessingParameterRange

alg.RANGE

Un rango de número

QgsProcessingParameterRasterLayer

alg.RASTER_LAYER

Una capa ráster.

QgsProcessingParameterRasterDestination

alg.RASTER_LAYER_DEST

Una capa ráster.

QgsProcessingParameterScale

alg.SCALE

Una escala de mapa

QgsProcessingParameterFeatureSink

alg.SINK

Un destino de objetos

QgsProcessingParameterFeatureSource

alg.SOURCE

Una fuente de objetos

QgsProcessingParameterScale

Una escala de mapa

QgsProcessingParameterString

alg.STRING

Una cadena de texto

QgsProcessingParameterVectorLayer

alg.VECTOR_LAYER

Una capa vectorial.

QgsProcessingParameterVectorDestination

alg.VECTOR_LAYER_DEST

Una capa vectorial.

22.8.3.2. Tipos de salida

Clase

Alg constante

Descripción

QgsProcessingOutputBoolean

alg.BOOL

Un valor booleano

QgsProcessingOutputNumber

alg.DISTANCE

Un parámetro numérico doble para valores de distancia

QgsProcessingOutputFile

alg.FILE

Un nombre de archivo de un archivo existente

QgsProcessingOutputFolder

alg.FOLDER

Una carpeta

QgsProcessingOutputHtml

alg.HTML

HTML

QgsProcessingOutputNumber

alg.INT

Un Entero

QgsProcessingOutputLayerDefinition

alg.LAYERDEF

Una definición de capa

QgsProcessingOutputMapLayer

alg.MAPLAYER

Una capa de mapa

QgsProcessingOutputMultipleLayers

alg.MULTILAYER

Un conjunto de capas

QgsProcessingOutputNumber

alg.NUMBER

Un valor numérico

QgsProcessingOutputRasterLayer

alg.RASTER_LAYER

Una capa ráster.

QgsProcessingOutputString

alg.STRING

Una cadena de texto

QgsProcessingOutputVectorLayer

alg.VECTOR_LAYER

Una capa vectorial.

22.8.4. Manejo de salida de algoritmo

Cuando declara una salida que representa una capa (ráster o vector), el algoritmo intentará agregarla a QGIS una vez que haya terminado.

  • Capa ráster saliente: QgsProcessingParameterRasterDestination / alg.RASTER_LAYER_DEST.

  • Capa vectorial saliente: QgsProcessingParameterVectorDestination / alg.VECTOR_LAYER_DEST.

Entonces, incluso si el método processing.run() no agrega las capas que crea al proyecto actual del usuario, se cargarán las dos capas de salida (búfer y búfer ráster), ya que se guardan en los destinos ingresados por el usuario (o a destinos temporales si el usuario no especifica destinos).

Si se crea una capa como salida de un algoritmo, debe declararse como tal. De lo contrario, no podrá utilizar correctamente el algoritmo en el modelador, ya que lo que se declara no coincidirá con lo que realmente crea el algoritmo.

Puede devolver cadenas, números y más especificándolos en el diccionario de resultados (como se demuestra para «NUMBEROFFEATURES»), pero siempre deben definirse explícitamente como salidas de su algoritmo. Alentamos a los algoritmos a generar tantos valores útiles como sea posible, ya que estos pueden ser valiosos para su uso en algoritmos posteriores cuando su algoritmo se usa como parte de un modelo.

22.8.5. La comunicación con el usuario

Si su algoritmo tarda mucho en procesarse, es una buena idea informar al usuario sobre el progreso. Puedes usar feedback (QgsProcessingFeedback) para esto.

El texto de progreso y la barra de progreso se pueden actualizar usando dos métodos: setProgressText(text) y setProgress(percent).

Puede proporcionar más información utilizando pushCommandInfo(text), pushDebugInfo(text), pushInfo(text) and reportError(text).

Si su guión tiene un problema, la forma correcta de manejarlo es generar una QgsProcessingException. Puede pasar un mensaje como argumento al constructor de la excepción. Procesos se encargará de manejarlo y comunicarse con el usuario, dependiendo de dónde se esté ejecutando el algoritmo (caja de herramientas, modelador, consola Python, …)

22.8.6. Documentando sus scripts

Puede documentar sus scripts sobrecargando el helpString() and helpUrl() methods of QgsProcessingAlgorithm.

22.8.7. Banderas

Puede suplantar el método flags de QgsProcessingAlgorithm para contarle a QGIS más sobre su algoritmo. Por ejemplo, puede decirle a QGIS que el script se ocultará al modelador, que se puede cancelar, que no es seguro para subprocesos y más.

Truco

De forma predeterminada, Procesos ejecuta algoritmos en un hilo separado para mantener la respuesta de QGIS mientras se ejecuta la tarea de procesamiento. Si su algoritmo falla regularmente, probablemente esté utilizando llamadas a la API que no son seguras de hacer en un hilo en segundo plano. Intente devolver el indicador QgsProcessingAlgorithm.FlagNoThreading del método flags() de su algoritmo para forzar a Processing a ejecutar su algoritmo en el hilo principal.

22.8.8. Las mejores practivas al escribir scripts de algoritmos

Aquí hay un resumen rápido de ideas a considerar al crear sus algoritmos de script y, especialmente, si desea compartirlos con otros usuarios de QGIS. Seguir estas sencillas reglas garantizará la coherencia entre los diferentes elementos de procesamiento, como la caja de herramientas, el modelador o la interfaz de procesamiento por lotes.

  • No cargar la capa resultante. Deje al Procesamiento manejar sus resultados y cargue sus capas si es necesario.

  • Siempre declare las salidas que crea su algoritmo.

  • No muestre cuadros de mensaje ni use ningún elemento GUI del script. Si desea comunicarse con el usuario, utilice los métodos del objeto de comentarios (QgsProcessingFeedback) o lance una QgsProcessingException.

Ya hay muchos algoritmos de procesamiento disponibles en QGIS. Puede encontrar el código en https://github.com/qgis/QGIS/blob/release-3_10/python/plugins/processing/algs/qgis.