23.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.
23.9.1. Per estensione di QgsProcessingAlgorithm
Il seguente codice
prende un layer vettoriale come input
conta il numero di elementi
esegue un’operazione di buffer
genera un layer raster dal risultato dell’operazione di buffer
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
eOUTPUT
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
eparameterAsDouble
.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 aTrue
quando si esegue un algoritmo da un altro algoritmo.context
efeedback
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.
23.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
usare un layer vettoriale come input
contare il numero di elementi
fare un’operazione di buffer
creare un layer raster dal risultato dell’operazione di buffer
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.
23.9.3. Tipi di input e output per algoritmi di processing
Ecco la lista dei tipi degli input e output supportati in Processing con le loro corrispondenti costanti del decoratore alg (algfactory.py
contiene la lista completa delle costanti alg). Ordinati in base al nome della classe.
23.9.3.1. Tipi in ingresso
Classe |
Alg costanti |
Descrizione |
---|---|---|
|
Permette agli utenti di scegliere tra le configurazioni di autenticazione disponibili o di creare nuove configurazioni di autenticazione |
|
|
Una banda di un layer raster |
|
|
Un valore booleano |
|
|
Un colore |
|
|
Un’operazione sulle coordinate (per le trasformazioni SR) |
|
|
Un sistema di riferimento delle coordinate |
|
|
Uno schema database |
|
|
Una tabella database |
|
|
Un datetime (o una data o un’ora) |
|
|
Un doppio parametro numerico per i valori di distanza |
|
|
Una numerazione, che permette la selezione da un insieme di valori predefiniti |
|
|
Una espressione |
|
|
Un’estensione spaziale definita da xmin, xmax, ymin, ymax |
|
|
Un campo nella tabella degli attributi di un layer vettoriale |
|
|
Un nome di un file esistente |
|
|
Un nome per un file in uscita appena creato |
|
|
Una cartella (cartella di destinazione) |
|
|
Un intero |
|
|
Un layout |
|
|
Un oggetto di layout |
|
|
Un layer mappa |
|
|
Un tema di mappa del progetto |
|
|
Una matrice |
|
|
Un layer mesh |
|
|
Un insieme di layer |
|
|
Un valore numerico |
|
|
Un punto |
|
|
Una connessione disponibile per un fornitore di database |
|
|
Un intervallo di numeri |
|
|
Un layer Raster |
|
|
Un layer Raster |
|
|
Una scala per le mappe |
|
|
Un elemento bacino |
|
|
Un’origine delle funzionalità |
|
|
Una stringa di testo |
|
|
Un vettore |
|
|
Un vettore |
23.9.3.2. Tipologie di output
Classe |
Alg costanti |
Descrizione |
---|---|---|
|
Un valore booleano |
|
|
Un doppio parametro numerico per i valori di distanza |
|
|
Un nome di un file esistente |
|
|
Una cartella |
|
|
HTML |
|
|
Un intero |
|
|
Una definizione layer |
|
|
Un layer mappa |
|
|
Un insieme di layer |
|
|
Un valore numerico |
|
|
Un layer Raster |
|
|
Una stringa di testo |
|
|
Un vettore |
23.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.
23.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, …)
23.9.6. Documentare gli script
Puoi documentare i tuoi script sovrapponendo i metodi helpString()
e helpUrl()
di QgsProcessingAlgorithm
.
23.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.
23.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 unaQgsProcessingException
.
Ci sono già molti algoritmi di processing disponibili in QGIS. Puoi trovare il codice su https://github.com/qgis/QGIS/blob/release-3_16/python/plugins/processing/algs/qgis.