22.8. Écrire de nouveaux algorithmes sous la forme de scripts Python

Il existe deux options pour écrire des algorithmes de traitement à l’aide de Python.

Dans QGIS, vous pouvez utiliser Créer un nouveau script dans le menu Scripts en haut de Boîte à outils de traitement pour ouvrir Éditeur de script de traitement où vous pouvez écrire votre code. Pour simplifier la tâche, vous pouvez commencer avec un modèle de script en utilisant Créer un nouveau script à partir du modèle dans le même menu. Cela ouvre un modèle qui étend la classe: QgsProcessingAlgorithm <qgis.core.QgsProcessingAlgorithm> class.

Si vous enregistrez le script dans le dossier scripts (l’emplacement par défaut) avec l’extension .py, l’algorithme sera disponible dans Processing Toolbox.

22.8.1. Extension de QgsProcessingAlgorithm

Le code suivant

  1. prend une couche vectorielle en entrée

  2. compte le nombre d’entités

  3. effectue une opération tampon

  4. crée une couche raster à partir du résultat de l’opération de tampon

  5. renvoie la couche tampon, la couche raster et le nombre d’entités

  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}

Fonctions standard de l’algorithme de traitement:

  • createInstance (obligatoire)

    Doit renvoyer une nouvelle copie de votre algorithme. Si vous modifiez le nom de la classe, assurez-vous également de mettre à jour la valeur renvoyée ici pour correspondre!

  • nom (obligatoire)

    Renvoie un nom d’algorithme unique, utilisé pour identifier l’algorithme.

  • displayName (obligatoire)

    Renvoie le nom de l’algorithme traduit.

  • group

    Renvoie le nom du groupe auquel appartient cet algorithme.

  • groupId

    Renvoie l’ID unique du groupe auquel appartient cet algorithme.

  • shortHelpString

    Renvoie une courte chaîne d’aide localisée pour l’algorithme.

  • initAlgorithm (obligatoire)

    Nous définissons ici les entrées et sorties de l’algorithme.

    INPUT et OUTPUT sont des noms recommandés pour les paramètres d’entrée principale et de sortie principale, respectivement.

    Si un paramètre dépend d’un autre paramètre, parentParameterName est utilisé pour spécifier cette relation (peut être le champ / la bande d’une couche ou les unités de distance d’une couche).

  • processAlgorithm (obligatoire)

    C’est là que le traitement a lieu.

    Les paramètres sont récupérés à l’aide de fonctions spéciales, par exemple parameterAsSource et parameterAsDouble.

    processing.run peut être utilisé pour exécuter d’autres algorithmes de traitement à partir d’un algorithme de traitement. Le premier paramètre est le nom de l’algorithme, le second est un dictionnaire des paramètres de l’algorithme. is_child_algorithm et est normalement défini sur True lors de l’exécution d’un algorithme à partir d’un autre algorithme. context et feedback informent l’algorithme de l’environnement à exécuter et du canal de communication avec l’utilisateur (capture de la demande d’annulation, rapport de progression, retour d’informations textuelles). Lors de l’utilisation des paramètres de l’algorithme (parent) en tant que paramètres des algorithmes « enfants », les valeurs de paramètre d’origine doivent être utilisées (par exemple, parameters ['OUTPUT']).

    Il est recommandé de vérifier autant que possible l’objet de retour pour annulation! Cela permet une annulation réactive, au lieu de forcer les utilisateurs à attendre qu’un traitement indésirable se produise.

    L’algorithme doit renvoyer des valeurs pour tous les paramètres de sortie qu’il a définis en tant que dictionnaire. Dans ce cas, il s’agit du tampon et des couches de sortie tramées, ainsi que du nombre d’entités traitées. Les clés du dictionnaire doivent correspondre aux noms de paramètre/sortie d’origine.

22.8.2. The @alg decorator

En utilisant le @alg decorator, vous pouvez créer vos propres algorithmes en écrivant le code Python et en ajoutant quelques lignes supplémentaires pour fournir les informations supplémentaires nécessaires pour en faire un algorithme de traitement approprié. Cela simplifie la création d’algorithmes et la spécification des entrées et sorties.

Une limitation importante de l’approche décoratrice est que les algorithmes créés de cette manière seront toujours ajoutés au fournisseur de scripts de traitement d’un utilisateur - il n’est pas possible d’ajouter ces algorithmes à un fournisseur personnalisé, par ex. pour une utilisation dans les plugins.

Le code suivant utilise le @alg decorator pour

  1. utiliser une couche vectorielle comme entrée

  2. compter le nombre d” entites

  3. faire une opération buffer

  4. créer une couche raster à partir du résultat de l’opération buffer

  5. renvoie la couche tampon, la couche raster et le nombre d’entités

 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}

Comme vous pouvez le voir, cela implique deux algorithmes (“native: buffer” et “qgis: rasterize”). Le dernier (“qgis: rasterize”) crée une couche raster à partir de la couche tampon qui a été générée par la première (“native: buffer”).

La partie du code où s’effectue ce traitement n’est pas difficile à comprendre si vous avez lu le chapitre précédent. Les premières lignes nécessitent cependant quelques explications supplémentaires. Ils fournissent les informations nécessaires pour transformer votre code en un algorithme pouvant être exécuté à partir de n’importe quel composant de l’interface graphique, comme la boîte à outils ou le modeleur graphique.

Ces lignes sont toutes des appels aux fonctions @alg decorator qui aident à simplifier le codage de l’algorithme.

  • Le @alg decorator est utilisé pour définir le nom et l’emplacement de l’algorithme dans la boîte à outils.

  • Le @alg.input decorator est utilisé pour définir les entrées de l’algorithme.

  • Le @alg.output decorator est utilisé pour définir les sorties de l’algorithme.

22.8.3. Types d’entrée et de sortie pour les algorithmes de traitement

Voici la liste des types d’entrée et de sortie pris en charge dans Processing avec leurs constantes de décorateur correspondantes ( algfactory.py contient la liste complète des constantes). Trié par nom de classe.

22.8.3.1. Input types

Class

Constante Alg

Description

QgsProcessingParameterAuthConfig

alg.AUTH_CFG

Permet aux utilisateurs de sélectionner parmi les configurations d’authentification disponibles ou de créer de nouvelles configurations d’authentification

QgsProcessingParameterBand

alg.BAND

Une bande d’une couche raster

QgsProcessingParameterBoolean

alg.BOOL

Une valeur booléenne

QgsProcessingParameterColor

alg.COLOR

Une couleur

QgsProcessingParameterCrs

alg.CRS

Un système de référence de coordonnées

QgsProcessingParameterDistance

alg.DISTANCE

Un double paramètre numérique pour les valeurs de distance

QgsProcessingParameterEnum

alg.ENUM

Une énumération, permettant la sélection à partir d’un ensemble de valeurs prédéfinies

QgsProcessingParameterExpression

alg.EXPRESSION

Une expression

QgsProcessingParameterExtent

alg.EXTENT

Une étendue spatiale définie par xmin, xmax, ymin, ymax

QgsProcessingParameterField

alg.FIELD

Un champ dans la table attributaire d’une couche vectorielle

QgsProcessingParameterFile

alg.FILE

Un nom de fichier d’un fichier existant

QgsProcessingParameterFileDestination

alg.FILE_DEST

Un nom de fichier pour un fichier de sortie nouvellement créé

QgsProcessingParameterFolderDestination

alg.FOLDER_DEST

Un dossier

QgsProcessingParameterNumber

alg.INT

Un nombre entier

QgsProcessingParameterLayout

alg.LAYOUT

Une mise en page

QgsProcessingParameterLayoutItem

alg.LAYOUT_ITEM

Un élément de mise en page

QgsProcessingParameterMapLayer

alg.MAPLAYER

Une couche de carte

QgsProcessingParameterMatrix

alg.MATRIX

Une matrice

QgsProcessingParameterMeshLayer

alg.MESH_LAYER

Une couche maillée

QgsProcessingParameterMultipleLayers

alg.MULTILAYER

Un ensemble de couches

QgsProcessingParameterNumber

alg.NUMBER

Une valeur numérique

QgsProcessingParameterPoint

alg.POINT

Un point

QgsProcessingParameterRange

alg.RANGE

Une plage de valeurs

QgsProcessingParameterRasterLayer

alg.RASTER_LAYER

Une couche raster

QgsProcessingParameterRasterDestination

alg.RASTER_LAYER_DEST

Une couche raster

QgsProcessingParameterScale

alg.SCALE

Une échelle de carte

QgsProcessingParameterFeatureSink

alg.SINK

A feature sink

QgsProcessingParameterFeatureSource

alg.SOURCE

Une source d’entités

QgsProcessingParameterScale

Une échelle de carte

QgsProcessingParameterString

alg.STRING

Une chaîne de texte

QgsProcessingParameterVectorLayer

alg.VECTOR_LAYER

Une couche vecteur

QgsProcessingParameterVectorDestination

alg.VECTOR_LAYER_DEST

Une couche vecteur

22.8.3.2. Types de sortie

Class

Constante Alg

Description

QgsProcessingOutputBoolean

alg.BOOL

Une valeur booléenne

QgsProcessingOutputNumber

alg.DISTANCE

Un double paramètre numérique pour les valeurs de distance

QgsProcessingOutputFile

alg.FILE

Un nom de fichier d’un fichier existant

QgsProcessingOutputFolder

alg.FOLDER

Un dossier

QgsProcessingOutputHtml

alg.HTML

HTML

QgsProcessingOutputNumber

alg.INT

Un entier

QgsProcessingOutputLayerDefinition

alg.LAYERDEF

Une définition de couche

QgsProcessingOutputMapLayer

alg.MAPLAYER

Une couche de carte

QgsProcessingOutputMultipleLayers

alg.MULTILAYER

Un ensemble de couches

QgsProcessingOutputNumber

alg.NUMBER

Une valeur numérique

QgsProcessingOutputRasterLayer

alg.RASTER_LAYER

Une couche raster

QgsProcessingOutputString

alg.STRING

Une chaîne de texte

QgsProcessingOutputVectorLayer

alg.VECTOR_LAYER

Une couche vecteur

22.8.4. Sortie de l’algorithme

Lorsque vous déclarez une sortie représentant une couche (raster ou vecteur), l’algorithme essaie de l’ajouter à QGIS une fois qu’il est terminé.

  • Sortie de la couche raster: QgsProcessingParameterRasterDestination / alg.RASTER_LAYER_DEST.

  • Sortie de couche vectorielle: QgsProcessingParameterVectorDestination / alg.VECTOR_LAYER_DEST.

Donc, même si la méthode ``processing.run()”” n’ajoute pas les couches qu’elle crée au projet actuel de l’utilisateur, les deux couches de sortie (tampon et tampon raster) seront chargées, car elles sont enregistrées dans les destinations entrées par l’utilisateur (ou vers des destinations temporaires si l’utilisateur ne spécifie pas de destinations).

Si une couche est créée en sortie d’un algorithme, elle doit être déclarée comme telle. Sinon, vous ne pourrez pas utiliser correctement l’algorithme dans le modeleur, car ce qui est déclaré ne correspondra pas à ce que l’algorithme crée réellement.

Vous pouvez renvoyer des chaînes, des nombres et bien plus en les spécifiant dans le dictionnaire de résultats (comme illustré pour « NUMBEROFFEATURES »), mais ils doivent toujours être explicitement définis comme sorties de votre algorithme. Nous encourageons les algorithmes à générer autant de valeurs utiles que possible, car celles-ci peuvent être utiles pour une utilisation dans des algorithmes ultérieurs lorsque votre algorithme est utilisé dans le cadre d’un modèle.

22.8.5. Communiquer avec l’utilisateur

Si votre algorithme prend beaucoup de temps à être traité, c’est une bonne idée d’informer l’utilisateur de la progression. Pour cela, vous pouvez utiliser feedback ( QgsProcessingFeedback).

Le texte de progression et la barre de progression peuvent être mis à jour à l’aide de deux méthodes: setProgressText (text) et setProgress (percent).

Vous pouvez fournir plus d’informations en utilisant pushCommandInfo(text), pushDebugInfo(text), pushInfo(text) and reportError(text).

Si votre script a un problème, la manière correcte de le gérer consiste à déclencher une classe: QgsProcessingException <qgis.core.QgsProcessingException>. Vous pouvez passer un message comme argument au constructeur de l’exception. Le traitement se chargera de le gérer et de communiquer avec l’utilisateur, selon le lieu d’exécution de l’algorithme (boîte à outils, modeleur, console Python, …)

22.8.6. Documenter ses scripts

Vous pouvez documenter vos scripts en surchargeant les méthodes: helpString() et helpUrl() methode de la classe: QgsProcessingAlgorithm.

22.8.7. Flags

Vous pouvez remplacer la méthode: flags de: QgsProcessingAlgorithm pour en dire plus à QGIS sur votre algorithme. Vous pouvez par exemple dire à QGIS que le script doit être caché au modeleur, qu’il peut être annulé, qu’il n’est pas sûr pour les threads, et plus encore.

Astuce

Par défaut, Processing exécute des algorithmes dans un thread séparé afin de maintenir QGIS réactif pendant l’exécution de la tâche de traitement. Si votre algorithme plante régulièrement, vous utilisez probablement des appels d’API qui ne sont pas sûrs à faire dans un thread d’arrière-plan. Essayez de renvoyer l’indicateur QgsProcessingAlgorithm.FlagNoThreading de la méthode flags () de votre algorithme pour forcer Processing à exécuter à la place votre algorithme dans le thread principal.

22.8.8. Bonnes pratiques d’écriture de scripts d’algorithmes

Voici un rapide résumé des idées à retenir lorsque vous créez vos scripts d’algorithmes et que vous souhaitez les partager avec d’autres utilisateurs QGIS. En suivant ces quelques règles, vous vous assurerez de fournir des éléments constants sur toutes les interfaces du menu Traitements telles que la boîte à outils, le modeleur et l’interface de commande.

  • Ne chargez pas les couches de résultat. Laissez les Traitements gérer ces résultats et charger vos couches si besoin.

  • Déclarez toujours les sorties créées par votre algorithme.

  • N’affichez pas de boîtes de message et n’utilisez aucun élément GUI du script. Si vous souhaitez communiquer avec l’utilisateur, utilisez les méthodes de l’objet de rétroaction ( QgsProcessingFeedback) ou lancez une classe: QgsProcessingException.

Il existe déjà de nombreux algorithmes de traitement disponibles dans QGIS. Vous pouvez trouver du code sur: https://github.com/qgis/QGIS/blob/release-3_10/python / plugins / processing / algs / qgis.