24.9. Scrivere nuovi algoritmi di Processing tramite script Python

Ci sono due opzioni per scrivere algoritmi di Processing usando Python.

All’interno di QGIS, puoi usare Create new script nel menu Script in cima a Strumenti di Processing per aprire il Processing Script Editor dove puoi scrivere il tuo codice. Per semplificare il compito, puoi iniziare con un modello di script usando Crea Nuovo Script da Modello… dallo stesso menu. Questo apre un modello che estrae QgsProcessingAlgorithm.

Se salvi lo script nella cartella scripts (la posizione predefinita) con un’estensione .py, l’algoritmo diventerà disponibile in Strumenti di Processing.

24.9.1. Per estensione di QgsProcessingAlgorithm

Il seguente codice

  1. prende un layer vettoriale come input

  2. conta il numero di elementi

  3. esegue un’operazione di buffer

  4. genera un layer raster dal risultato dell’operazione di buffer

  5. restituisce il buffer layer, il layer raster e il numero di elementi

  1from qgis.PyQt.QtCore import QCoreApplication
  2from qgis.core import (QgsProcessing,
  3                       QgsProcessingAlgorithm,
  4                       QgsProcessingException,
  5                       QgsProcessingOutputNumber,
  6                       QgsProcessingParameterDistance,
  7                       QgsProcessingParameterFeatureSource,
  8                       QgsProcessingParameterVectorDestination,
  9                       QgsProcessingParameterRasterDestination)
 10from qgis import processing
 11
 12
 13class ExampleProcessingAlgorithm(QgsProcessingAlgorithm):
 14    """
 15    This is an example algorithm that takes a vector layer,
 16    creates some new layers and returns some results.
 17    """
 18
 19    def tr(self, string):
 20        """
 21        Returns a translatable string with the self.tr() function.
 22        """
 23        return QCoreApplication.translate('Processing', string)
 24
 25    def createInstance(self):
 26        # Must return a new copy of your algorithm.
 27        return ExampleProcessingAlgorithm()
 28
 29    def name(self):
 30        """
 31        Returns the unique algorithm name.
 32        """
 33        return 'bufferrasterextend'
 34
 35    def displayName(self):
 36        """
 37        Returns the translated algorithm name.
 38        """
 39        return self.tr('Buffer and export to raster (extend)')
 40
 41    def group(self):
 42        """
 43        Returns the name of the group this algorithm belongs to.
 44        """
 45        return self.tr('Example scripts')
 46
 47    def groupId(self):
 48        """
 49        Returns the unique ID of the group this algorithm belongs
 50        to.
 51        """
 52        return 'examplescripts'
 53
 54    def shortHelpString(self):
 55        """
 56        Returns a localised short help string for the algorithm.
 57        """
 58        return self.tr('Example algorithm short description')
 59
 60    def initAlgorithm(self, config=None):
 61        """
 62        Here we define the inputs and outputs of the algorithm.
 63        """
 64        # 'INPUT' is the recommended name for the main input
 65        # parameter.
 66        self.addParameter(
 67            QgsProcessingParameterFeatureSource(
 68                'INPUT',
 69                self.tr('Input vector layer'),
 70                types=[QgsProcessing.TypeVectorAnyGeometry]
 71            )
 72        )
 73        self.addParameter(
 74            QgsProcessingParameterVectorDestination(
 75                'BUFFER_OUTPUT',
 76                self.tr('Buffer output'),
 77            )
 78        )
 79        # 'OUTPUT' is the recommended name for the main output
 80        # parameter.
 81        self.addParameter(
 82            QgsProcessingParameterRasterDestination(
 83                'OUTPUT',
 84                self.tr('Raster output')
 85            )
 86        )
 87        self.addParameter(
 88            QgsProcessingParameterDistance(
 89                'BUFFERDIST',
 90                self.tr('BUFFERDIST'),
 91                defaultValue = 1.0,
 92                # Make distance units match the INPUT layer units:
 93                parentParameterName='INPUT'
 94            )
 95        )
 96        self.addParameter(
 97            QgsProcessingParameterDistance(
 98                'CELLSIZE',
 99                self.tr('CELLSIZE'),
100                defaultValue = 10.0,
101                parentParameterName='INPUT'
102            )
103        )
104        self.addOutput(
105            QgsProcessingOutputNumber(
106                'NUMBEROFFEATURES',
107                self.tr('Number of features processed')
108            )
109        )
110
111    def processAlgorithm(self, parameters, context, feedback):
112        """
113        Here is where the processing itself takes place.
114        """
115        # First, we get the count of features from the INPUT layer.
116        # This layer is defined as a QgsProcessingParameterFeatureSource
117        # parameter, so it is retrieved by calling
118        # self.parameterAsSource.
119        input_featuresource = self.parameterAsSource(parameters,
120                                                     'INPUT',
121                                                     context)
122        numfeatures = input_featuresource.featureCount()
123
124        # Retrieve the buffer distance and raster cell size numeric
125        # values. Since these are numeric values, they are retrieved
126        # using self.parameterAsDouble.
127        bufferdist = self.parameterAsDouble(parameters, 'BUFFERDIST',
128                                            context)
129        rastercellsize = self.parameterAsDouble(parameters, 'CELLSIZE',
130                                                context)
131        if feedback.isCanceled():
132            return {}
133        buffer_result = processing.run(
134            'native:buffer',
135            {
136                # Here we pass on the original parameter values of INPUT
137                # and BUFFER_OUTPUT to the buffer algorithm.
138                'INPUT': parameters['INPUT'],
139                'OUTPUT': parameters['BUFFER_OUTPUT'],
140                'DISTANCE': bufferdist,
141                'SEGMENTS': 10,
142                'DISSOLVE': True,
143                'END_CAP_STYLE': 0,
144                'JOIN_STYLE': 0,
145                'MITER_LIMIT': 10
146            },
147            # Because the buffer algorithm is being run as a step in
148            # another larger algorithm, the is_child_algorithm option
149            # should be set to True
150            is_child_algorithm=True,
151            #
152            # It's important to pass on the context and feedback objects to
153            # child algorithms, so that they can properly give feedback to
154            # users and handle cancelation requests.
155            context=context,
156            feedback=feedback)
157
158        # Check for cancelation
159        if feedback.isCanceled():
160            return {}
161
162        # Run the separate rasterization algorithm using the buffer result
163        # as an input.
164        rasterized_result = processing.run(
165            'qgis:rasterize',
166            {
167                # Here we pass the 'OUTPUT' value from the buffer's result
168                # dictionary off to the rasterize child algorithm.
169                'LAYER': buffer_result['OUTPUT'],
170                'EXTENT': buffer_result['OUTPUT'],
171                'MAP_UNITS_PER_PIXEL': rastercellsize,
172                # Use the original parameter value.
173                'OUTPUT': parameters['OUTPUT']
174            },
175            is_child_algorithm=True,
176            context=context,
177            feedback=feedback)
178
179        if feedback.isCanceled():
180            return {}
181
182        # Return the results
183        return {'OUTPUT': rasterized_result['OUTPUT'],
184                'BUFFER_OUTPUT': buffer_result['OUTPUT'],
185                'NUMBEROFFEATURES': numfeatures}

Funzioni standard algoritmo di Processing:

  • createInstance (obbligatorio)

    Deve restituire una nuova copia del tuo algoritmo. Se cambi il nome della classe, assicurati di aggiornare qui anche il valore restituito per farlo corrispondere!

  • name (obbligatorio)

    Restituisce il nome univoco dell’algoritmo, usato per identificare l’algoritmo.

  • displayName (obbligatorio)

    Restituisce il nome tradotto dell’algoritmo.

  • group

    Restituisce il nome del gruppo a cui appartiene questo algoritmo.

  • groupId

    Restituisce l’ID univoco del gruppo a cui questo algoritmo appartiene.

  • shortHelpString

    Restituisce una breve stringa di aiuto per l’algoritmo localizzata.

  • initAlgorithm (obbligatorio)

    Qui definiamo gli ingressi e le uscite dell’algoritmo.

    INPUT e OUTPUT sono nomi raccomandati per i parametri in ingresso e in uscita principali, rispettivamente.

    Se un parametro dipende da un altro parametro, parentParameterName è usato per specificare questa relazione (potrebbe essere il campo/banda di un layer o le unità di distanza di un layer).

  • processAlgorithm (obbligatorio)

    Qui è dove avviene l’elaborazione.

    I parametri sono richiamati usando funzioni speciali, per esempio parameterAsSource e parameterAsDouble.

    processing.run può essere usato per eseguire altri algoritmi di elaborazione da un algoritmo di processing. Il primo parametro è il nome dell’algoritmo, il secondo è un dizionario dei parametri dell’algoritmo. is_child_algorithm è normalmente impostato a True quando si esegue un algoritmo da un altro algoritmo. context e feedback informano l’algoritmo circa l’ambiente in cui eseguire e il canale per comunicare con l’utente ( intercettare la richiesta di cancellazione, riportare i progressi, fornire un feedback testuale). Quando si usano i parametri dell’algoritmo (padre) come parametri di algoritmi «figli», si dovrebbero usare i valori originali dei parametri (ad esempio, parameters['OUTPUT']).

    È una buona pratica verificare l’oggetto feedback per cancellare il più possibile! Questo permette una cancellazione rapida, invece di costringere gli utenti ad aspettare che avvenga un processing indesiderato.

    L’algoritmo dovrebbe restituire valori per tutti i parametri in uscita che ha definito come dizionario. In questo caso, sono il buffer e i layer risultato rasterizzati, e il conteggio degli elementi processati. Le chiavi del dizionario devono corrispondere ai nomi originali dei parametri/output.

24.9.2. Il decoratore @alg

Usando il decoratore @alg, puoi creare i tuoi algoritmi scrivendo il codice Python e aggiungendo qualche riga in più per fornire informazioni aggiuntive necessarie a renderlo un algoritmo di Processing corretto. Questo semplifica la creazione di algoritmi e la specificazione di input e output.

Una limitazione importante con l’approccio del decoratore è che gli algoritmi creati in questo modo saranno sempre aggiunti al provider Processing Scripts di un utente – non è possibile aggiungere questi algoritmi ad un provider personalizzato, ad esempio per l’uso nei plugin.

Il seguente codice usa il decorativo @alg per

  1. usare un layer vettoriale come input

  2. contare il numero di elementi

  3. fare un’operazione di buffer

  4. creare un layer raster dal risultato dell’operazione di buffer

  5. restituisce il buffer layer, il layer raster e il numero di elementi

 1from qgis import processing
 2from qgis.processing import alg
 3from qgis.core import QgsProject
 4
 5@alg(name='bufferrasteralg', label='Buffer and export to raster (alg)',
 6     group='examplescripts', group_label='Example scripts')
 7# 'INPUT' is the recommended name for the main input parameter
 8@alg.input(type=alg.SOURCE, name='INPUT', label='Input vector layer')
 9# 'OUTPUT' is the recommended name for the main output parameter
10@alg.input(type=alg.RASTER_LAYER_DEST, name='OUTPUT',
11           label='Raster output')
12@alg.input(type=alg.VECTOR_LAYER_DEST, name='BUFFER_OUTPUT',
13           label='Buffer output')
14@alg.input(type=alg.DISTANCE, name='BUFFERDIST', label='BUFFER DISTANCE',
15           default=1.0)
16@alg.input(type=alg.DISTANCE, name='CELLSIZE', label='RASTER CELL SIZE',
17           default=10.0)
18@alg.output(type=alg.NUMBER, name='NUMBEROFFEATURES',
19            label='Number of features processed')
20
21def bufferrasteralg(instance, parameters, context, feedback, inputs):
22    """
23    Description of the algorithm.
24    (If there is no comment here, you will get an error)
25    """
26    input_featuresource = instance.parameterAsSource(parameters,
27                                                     'INPUT', context)
28    numfeatures = input_featuresource.featureCount()
29    bufferdist = instance.parameterAsDouble(parameters, 'BUFFERDIST',
30                                            context)
31    rastercellsize = instance.parameterAsDouble(parameters, 'CELLSIZE',
32                                                context)
33    if feedback.isCanceled():
34        return {}
35    buffer_result = processing.run('native:buffer',
36                               {'INPUT': parameters['INPUT'],
37                                'OUTPUT': parameters['BUFFER_OUTPUT'],
38                                'DISTANCE': bufferdist,
39                                'SEGMENTS': 10,
40                                'DISSOLVE': True,
41                                'END_CAP_STYLE': 0,
42                                'JOIN_STYLE': 0,
43                                'MITER_LIMIT': 10
44                                },
45                               is_child_algorithm=True,
46                               context=context,
47                               feedback=feedback)
48    if feedback.isCanceled():
49        return {}
50    rasterized_result = processing.run('qgis:rasterize',
51                               {'LAYER': buffer_result['OUTPUT'],
52                                'EXTENT': buffer_result['OUTPUT'],
53                                'MAP_UNITS_PER_PIXEL': rastercellsize,
54                                'OUTPUT': parameters['OUTPUT']
55                               },
56                               is_child_algorithm=True, context=context,
57                               feedback=feedback)
58    if feedback.isCanceled():
59        return {}
60    return {'OUTPUT': rasterized_result['OUTPUT'],
61            'BUFFER_OUTPUT': buffer_result['OUTPUT'],
62            'NUMBEROFFEATURES': numfeatures}

Come puoi vedere, coinvolge due algoritmi (“native:buffer” e “qgis:rasterize”). L’ultimo (“qgis:rasterize”) crea un layer raster dal layer buffer che è stato generato dal primo (“native:buffer”).

La parte di codice in cui avviene questa elaborazione non è difficile da capire se hai letto il capitolo precedente. Le prime linee, tuttavia, hanno bisogno di qualche spiegazione aggiuntiva. Esse forniscono le informazioni necessarie per trasformare il tuo codice in un algoritmo che può essere eseguito da qualsiasi componente dell’interfaccia grafica GUI, come il toolbox o il modellatore grafico.

Queste linee sono tutte chiamate alle funzioni del decoratore @alg che aiutano a semplificare la codifica dell’algoritmo.

  • Il decoratore @alg è usato per definire il nome e la posizione dell’algoritmo negli Strumenti di Processing.

  • Il decoratore @alg.input è usato per definire quanto in ingresso dell’algoritmo.

  • Il decoratore @alg.output è usato per definire i risultati dell’algoritmo.

24.9.3. Tipi di input e output per algoritmi di processing

Ecco l’elenco dei tipi di input e output supportati da Processing con le corrispondenti costanti del decoratore alg (il file algfactory.py contiene l’elenco completo delle costanti alg). Ordinate in base al nome della classe.

24.9.3.1. Tipi in ingresso

Classe

Alg costanti

Descrizione

QgsProcessingParameterAnnotationLayer

alg.ANNOTATION_LAYER

Un layer annotazione

QgsProcessingParameterAuthConfig

alg.AUTH_CFG

Permette agli utenti di scegliere tra le configurazioni di autenticazione disponibili o di creare nuove configurazioni di autenticazione

QgsProcessingParameterBand

alg.BAND

Una banda di un layer raster

QgsProcessingParameterBoolean

alg.BOOL

Un valore booleano

QgsProcessingParameterColor

alg.COLOR

Un colore

QgsProcessingParameterCoordinateOperation

alg.COORDINATE_OPERATION

Un’operazione sulle coordinate (per le trasformazioni SR)

QgsProcessingParameterCrs

alg.CRS

Un sistema di riferimento delle coordinate

QgsProcessingParameterDatabaseSchema

alg.DATABASE_SCHEMA

Uno schema database

QgsProcessingParameterDatabaseTable

alg.DATABASE_TABLE

Una tabella database

QgsProcessingParameterDateTime

alg.DATETIME

Un datetime (o una data o un’ora)

QgsProcessingParameterDistance

alg.DISTANCE

Un doppio parametro numerico per i valori di distanza

QgsProcessingParameterEnum

alg.ENUM

Una numerazione, che permette la selezione da un insieme di valori predefiniti

QgsProcessingParameterExpression

alg.EXPRESSION

Una espressione

QgsProcessingParameterExtent

alg.EXTENT

Un’estensione spaziale definita da xmin, xmax, ymin, ymax

QgsProcessingParameterField

alg.FIELD

Un campo nella tabella degli attributi di un layer vettoriale

QgsProcessingParameterFile

alg.FILE

Un nome di un file esistente

QgsProcessingParameterFileDestination

alg.FILE_DEST

Un nome per un file in uscita appena creato

QgsProcessingParameterFolderDestination

alg.FOLDER_DEST

Una cartella (cartella di destinazione)

QgsProcessingParameterGeometry

alg.GEOMETRY

Una geometria

QgsProcessingParameterNumber

alg.INT

Un intero

QgsProcessingParameterLayout

alg.LAYOUT

Un layout

QgsProcessingParameterLayoutItem

alg.LAYOUT_ITEM

Un oggetto di layout

QgsProcessingParameterMapLayer

alg.MAPLAYER

Un layer mappa

QgsProcessingParameterMapTheme

alg.MAP_THEME

Un tema di mappa del progetto

QgsProcessingParameterMatrix

alg.MATRIX

Una matrice

QgsProcessingParameterMeshLayer

alg.MESH_LAYER

Un layer mesh

QgsProcessingParameterMultipleLayers

alg.MULTILAYER

Un insieme di layer

QgsProcessingParameterNumber

alg.NUMBER

Un valore numerico

QgsProcessingParameterPoint

alg.POINT

Un punto

QgsProcessingParameterPointCloudLayer

alg.POINT_CLOUD_LAYER

Un layer nuvola di punti

QgsProcessingParameterProviderConnection

alg.PROVIDER_CONNECTION

Una connessione disponibile per un fornitore di database

QgsProcessingParameterRange

alg.RANGE

Un intervallo di numeri

QgsProcessingParameterRasterLayer

alg.RASTER_LAYER

Un layer Raster

QgsProcessingParameterRasterDestination

alg.RASTER_LAYER_DEST

Un layer Raster

QgsProcessingParameterScale

alg.SCALE

Una scala per le mappe

QgsProcessingParameterFeatureSink

alg.SINK

Un elemento bacino

QgsProcessingParameterFeatureSource

alg.SOURCE

Un’origine delle funzionalità

QgsProcessingParameterString

alg.STRING

Una stringa di testo

QgsProcessingParameterVectorLayer

alg.VECTOR_LAYER

Un vettore

QgsProcessingParameterVectorDestination

alg.VECTOR_LAYER_DEST

Un vettore

24.9.3.2. Tipologie di output

Classe

Alg costanti

Descrizione

QgsProcessingOutputBoolean

alg.BOOL

Un valore booleano

QgsProcessingOutputNumber

alg.DISTANCE

Un doppio parametro numerico per i valori di distanza

QgsProcessingOutputFile

alg.FILE

Un nome di un file esistente

QgsProcessingOutputFolder

alg.FOLDER

Una cartella

QgsProcessingOutputHtml

alg.HTML

HTML

QgsProcessingOutputNumber

alg.INT

Un intero

QgsProcessingOutputLayerDefinition

alg.LAYERDEF

Una definizione layer

QgsProcessingOutputMapLayer

alg.MAPLAYER

Un layer mappa

QgsProcessingOutputMultipleLayers

alg.MULTILAYER

Un insieme di layer

QgsProcessingOutputNumber

alg.NUMBER

Un valore numerico

QgsProcessingOutputRasterLayer

alg.RASTER_LAYER

Un layer Raster

QgsProcessingOutputString

alg.STRING

Una stringa di testo

QgsProcessingOutputVectorLayer

alg.VECTOR_LAYER

Un vettore

24.9.4. Manipolazione del risultato dell’algoritmo

Quando si indica un risultato che rappresenta un layer (raster o vettoriale), l’algoritmo cercherà di aggiungerlo a QGIS una volta finito.

  • Output layer Raster: QgsProcessingParameterRasterDestination / alg.RASTER_LAYER_DEST.

  • Output layer Vettoriale: QgsProcessingParameterVectorDestination / alg.VECTOR_LAYER_DEST.

Così, anche se il metodo processing.run() non aggiunge i layer che crea al progetto corrente dell’utente, i due layer in uscita (buffer e raster buffer) saranno caricati, poiché sono salvati nelle destinazioni inserite dall’utente (o in destinazioni temporanee se l’utente non specifica destinazioni).

Se un layer viene creato come risultato di un algoritmo, dovrebbe essere dichiarato come tale. Altrimenti, non sarà possibile utilizzare correttamente l’algoritmo nel modellatore, poiché ciò che è dichiarato non corrisponderà a ciò che l’algoritmo crea realmente.

Puoi restituire stringhe, numeri e altro specificandoli nel dizionario dei risultati (come dimostrato per «NUMBEROFFEATURES»), ma dovrebbero sempre essere definiti esplicitamente come risultati del tuo algoritmo. È consigliabile che gli algoritmi restituiscano quanti più valori utili possibile, poiché questi possono essere preziosi per l’uso in algoritmi successivi, quando l’algoritmo viene usato come parte di un modello.

24.9.5. Comunicare con l’utente

Se il tuo algoritmo richiede molto tempo per essere processato, è una buona idea informare l’utente sul progresso. Puoi usare feedback (QgsProcessingFeedback) per questo.

Il testo di avanzamento e la barra di avanzamento possono essere aggiornati usando due metodi: setProgressText(text) e setProgress(percent).

Puoi ottenere più informazioni usando pushCommandInfo(text), pushDebugInfo(text), pushInfo(text) and reportError(text).

Se il tuo script ha un problema, il modo corretto di gestirlo è generare una QgsProcessingException. Puoi passare un messaggio come argomento al costruttore dell’eccezione. L’elaborazione si occuperà di gestirla e di comunicare con l’utente, a seconda di dove viene eseguito l’algoritmo (toolbox, modeler, console Python, …)

24.9.6. Documentare gli script

Puoi documentare i tuoi script sovrapponendo i metodi helpString() e helpUrl() di QgsProcessingAlgorithm.

24.9.7. Flag

Puoi sovrascrivere il metodo flags() di QgsProcessingAlgorithm per dire a QGIS di più sul tuo algoritmo. Puoi per esempio dire a QGIS che lo script deve essere nascosto al modellatore, che può essere annullato, che non è thread sicuro, e altro.

Suggerimento

Per impostazione predefinita, Processing esegue gli algoritmi in un thread separato in modo da mantenere QGIS reattivo mentre l’attività di elaborazione viene eseguita. Se il tuo algoritmo si blocca spesso, probabilmente stai usando chiamate API che non sono sicure da eseguire in un thread in background. Prova a ripristinare il flag QgsProcessingAlgorithm.FlagNoThreading dal metodo flags() del tuo algoritmo per forzare Processing ad eseguire il tuo algoritmo nel thread principale.

24.9.8. Le migliori tecniche per scrivere algoritmi script

Ecco un rapido riassunto delle cose da tenere in considerazione quando crei i tuoi algoritmi di script e, soprattutto, se vuoi condividerli con altri utenti di QGIS. Seguire queste semplici regole assicurerà la coerenza tra i diversi elementi di Processing come la casella degli strumenti, il modellatore o l’interfaccia di elaborazione batch.

  • Non caricare i layer risultanti. Lascia che Processing gestisca i tuoi risultati e carica i tuoi layer se necessario.

  • Dichiara sempre gli output che il tuo algoritmo crea.

  • Non mostrare finestre di messaggio o usare alcun elemento dell’interfaccia grafica dallo script. Se vuoi comunicare con l’utente, usa i metodi dell’oggetto feedback (QgsProcessingFeedback) o lancia una QgsProcessingException.

Ci sono già molti algoritmi di processing disponibili in QGIS. Puoi trovare il codice su https://github.com/qgis/QGIS/blob/release-3_22/python/plugins/processing/algs/qgis.