22.8. 新しいプロセッシングアルゴリズムをPythonスクリプトで書く

Pythonを使ってプロセッシングアルゴリズムを書くためには、ふたつの選択肢があります。

QGIS内では、 プロセシングツールボックス の上部にある スクリプト メニューの 新しいスクリプトを作成 を使用して、自分のコードを書ける プロセシングスクリプトエディタ を開いて作成できます。タスクを簡素化するには、同じメニューの テンプレートから新しいスクリプトを作成する を使用して、スクリプトテンプレートから開始できます。これにより QgsProcessingAlgorithm を拡張するテンプレートが開きます。

スクリプトを scripts フォルダ(デフォルトの場所) にwith a .py という拡張子で保存するとアルゴリズムは Processing Toolbox で利用可能になります.

22.8.1. QgsProcessingAlgorithmを拡張する

以下のコードの内容です

  1. ベクタレイヤを入力に指定します

  2. 地物の数を数えます

  3. バッファの操作を行います

  4. バッファ操作の結果を使ってラスタレイヤを作ります

  5. バッファレイヤ,ラスタレイヤと地物の数を返します

  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}

プロセッシングアルゴリズムの標準関数:

  • createInstance (必須)

    あなたのアルゴリズムの新しいコピーを返さなければいけません。もしあなたがクラスの名前を変更した場合はここで返す値が合致しているかどうか気を付けて下さい!

  • name (必須)

    アルゴリズムのユニークな名前を返します,アルゴリズムの識別に使います.

  • displayName (必須)

    変換されたアルゴリズム名を返します.

  • group

    このアルゴリズムが所属しているグループ名を返します.

  • groupId

    このアルゴリズムが所属しているグループのユニークIDを返します.

  • shortHelpString

    このアルゴリズムの翻訳された短縮ヘルプ文字列を返します.

  • initAlgorithm (必須)

    ここでアルゴリズムの入力と出力を定義します.

    INPUTOUTPUT はそれぞれメインの入力と出力パラメータ用の推奨名称です.

    もしパラメータが他のパラメータに依存する場合, parentParameterName がこの関係を示します (フィールド/レイヤのバンドまたはレイヤの距離単位は利用できません).

  • processAlgorithm (必須)

    ここで処理が実行されます.

    パラメータはインスタンスによって parameterAsSourceparameterAsDouble のような特別な用途の関数を使って取得されます.

    processing.run を使うとプロセッシングアルゴリズムから他のアルゴリズムを実行できます. 最初のパラメータはアルゴリズムの名前です, 2つめのパラメータはそのアルゴリズム用のパラメータの辞書です. 他のアルゴリズム内で実行される場合 is_child_algorithm は通常 True に設定します. contextfeedback は実行環境とユーザとのやりとりチャンネルについてアルゴリズムに伝えます (キャンセルリクエストのキャッチ, 実行状況のレポート, テキストによるフィードバックの提供). (親) アルゴリズムのパラメータが "子" アルゴリズムのパラメータとして利用される場合オリジナルの値を利用する必要があります(例. parameters['OUTPUT']).

    可能な限りキャンセルのためにフィードバックオブジェクトをチェックすることをお勧めします!そうすることで、不要な処理が発生するまでユーザーを待たせる代わりに、レスポンシブなキャンセルが可能になります。

    アルゴリズムは、辞書として定義したすべての出力パラメータの値を返す必要があります。この場合、それはバッファとラスタライズされた出力レイヤ、および処理された地物数です。辞書キーは、元のパラメータ/出力名と一致する必要があります。

22.8.2. @alg デコレータ

@algデコレータを使ってPythonコードを書き,適切な処理アルゴリズムにするための追加情報を提供する数行を追加することでであなた独自のアルゴリズムをつくることができます.この手法はアルゴリズムの作成を簡素にして、また入力と出力の定義ができます.

アルゴリズム作成にデコレータを使う手法には一つの重要な制約があります.それは作成されたアルゴリズムが常にユーザープロセッシングスクリプトプロバイダーに追加されることです--アルゴリズムを(たとえばプラグイン内で使う)カスタムプロバイダーに追加することはできません.

以下のコードは @alg デコレータを使って次の機能を実装してます

  1. ベクタレイヤを入力として利用します

  2. 地物の数を数えます

  3. バッファ操作を実行します

  4. バッファ操作の結果を使ってラスタレイヤを作ります

  5. バッファレイヤ,ラスタレイヤと地物の数を返します

 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}

ご覧の通りこのコードには2つのアルゴリズム('native:buffer' と 'qgis:rasterize')が含まれています. 後の物 ('qgis:rasterize') は最初のもの ('native:buffer') が作成したバッファレイヤを利用してラスタレイヤを作成します.

この処理を行うコードの一部分はあなたが前の章を読んでいてば理解するのはむつかしくないです.ただし最初の行には追加の説明が必要です.これらはあなたのコードをアルゴリズムに変換するために必要な情報を提供しています,それによってコードはツールボックスやグラフィカルモデラーのようなGUIコンポーネントから実行できます.

これらの行は @alg デコレータ関数を呼び出しています.その結果アルゴリズムを簡単にコーディングできます.

  • @alg デコレータはツールボックス内でのアルゴリズムの名前と場所を定義するのに利用されます.

  • @alg.input デコレータはアルゴリズムの入力を定義するのにつかわれます.

  • @alg.output デコレータはアルゴリズムの出力を定義するのにつかわれます.

22.8.3. プロセッシングアルゴリズムのための入力と出力の型

プロセッシングの algデコレータ定数でサポートされている入力と出力タイプのリストです (algfactory.py にはalg定数の完全なリストが含まれます). クラス名でソートされています.

22.8.3.1. 入力タイプ

クラス

Alg定数

説明

QgsProcessingParameterAuthConfig

alg.AUTH_CFG

ユーザに利用可能な認証構成を選択させるか新しい認証構成を作成させます

QgsProcessingParameterBand

alg.BAND

ラスタレイヤのバンド

QgsProcessingParameterBoolean

alg.BOOL

ブール値

QgsProcessingParameterColor

alg.COLOR

QgsProcessingParameterCrs

alg.CRS

座標参照系

QgsProcessingParameterDistance

alg.DISTANCE

距離の値用の倍精度数値パラメータ

QgsProcessingParameterEnum

alg.ENUM

事前定義された複数の値からの選択を許す列挙

QgsProcessingParameterExpression

alg.EXPRESSION

QgsProcessingParameterExtent

alg.EXTENT

xmin, xmax, ymin, ymaxで定義された空間領域

QgsProcessingParameterField

alg.FIELD

ベクタレイヤ属性テーブルのフィールド

QgsProcessingParameterFile

alg.FILE

既存ファイルのファイル名

QgsProcessingParameterFileDestination

alg.FILE_DEST

新たに作成された出力ファイルのファイル名

QgsProcessingParameterFolderDestination

alg.FOLDER_DEST

フォルダ

QgsProcessingParameterNumber

alg.INT

整数

QgsProcessingParameterLayout

alg.LAYOUT

レイアウト

QgsProcessingParameterLayoutItem

alg.LAYOUT_ITEM

レイアウトアイテム

QgsProcessingParameterMapLayer

alg.MAPLAYER

マップレイヤ

QgsProcessingParameterMatrix

alg.MATRIX

配列

QgsProcessingParameterMeshLayer

alg.MESH_LAYER

メッシュレイヤ

QgsProcessingParameterMultipleLayers

alg.MULTILAYER

レイヤのセット

QgsProcessingParameterNumber

alg.NUMBER

数値

QgsProcessingParameterPoint

alg.POINT

QgsProcessingParameterRange

alg.RANGE

数値の範囲

QgsProcessingParameterRasterLayer

alg.RASTER_LAYER

ラスターレイヤー

QgsProcessingParameterRasterDestination

alg.RASTER_LAYER_DEST

ラスターレイヤー

QgsProcessingParameterScale

alg.SCALE

地図の縮尺

QgsProcessingParameterFeatureSink

alg.SINK

地物シンク

QgsProcessingParameterFeatureSource

alg.SOURCE

地物ソース

QgsProcessingParameterScale

地図の縮尺

QgsProcessingParameterString

alg.STRING

テキストストリング

QgsProcessingParameterVectorLayer

alg.VECTOR_LAYER

ベクターレイヤー

QgsProcessingParameterVectorDestination

alg.VECTOR_LAYER_DEST

ベクターレイヤー

22.8.3.2. 出力タイプ

クラス

Alg定数

説明

QgsProcessingOutputBoolean

alg.BOOL

ブール値

QgsProcessingOutputNumber

alg.DISTANCE

距離の値用の倍精度数値パラメータ

QgsProcessingOutputFile

alg.FILE

既存ファイルのファイル名

QgsProcessingOutputFolder

alg.FOLDER

フォルダ

QgsProcessingOutputHtml

alg.HTML

HTML

QgsProcessingOutputNumber

alg.INT

整数

QgsProcessingOutputLayerDefinition

alg.LAYERDEF

レイヤ定義

QgsProcessingOutputMapLayer

alg.MAPLAYER

マップレイヤ

QgsProcessingOutputMultipleLayers

alg.MULTILAYER

レイヤのセット

QgsProcessingOutputNumber

alg.NUMBER

数値

QgsProcessingOutputRasterLayer

alg.RASTER_LAYER

ラスターレイヤー

QgsProcessingOutputString

alg.STRING

テキストストリング

QgsProcessingOutputVectorLayer

alg.VECTOR_LAYER

ベクターレイヤー

22.8.4. アルゴリズムの出力を渡す

出力としてレイヤ(ラスタまたはベクタ)を指定すると、アルゴリズムは終了後にそれをQGISに追加しようとします。

  • ラスタレイヤ出力: QgsProcessingParameterRasterDestination / alg.RASTER_LAYER_DEST.

  • ベクタレイヤ出力: QgsProcessingParameterVectorDestination / alg.VECTOR_LAYER_DEST.

したがって processing.run() メソッドが作成したレイヤをユーザの現在のプロジェクトに読み込まない場合でも2つの出力レイヤ (バッファとラスタバッファ)がロードされます. will be loaded,これらはユーザが入力した保存先 (またはユーザが保存先を指定しない場合は一時的な保存先)に保存されるからです.

レイヤがアルゴリズムの出力として作成される場合、そのように宣言する必要があります。そうしないと、宣言されたものがアルゴリズムが実際に作成するものと一致しないため、モデラーでアルゴリズムを適切に使用できません。

結果の辞書で文字列、数値などを指定することで返すことができます(「NUMBEROFFEATURES」で示したとおり)が、それらは常にアルゴリズムからの出力として明示的に定義する必要があります。アルゴリズムがモデルの一部として使用される場合、これらのアルゴリズムは後のアルゴリズムで使用するのに役立つ可能性があるため、アルゴリズムではできるだけ多くの有用な値を出力することをを推奨します。

22.8.5. ユーザーとやりとりする

あなたのアルゴリズムが処理に長い時間を必要とする場合ユーザに進捗状況を知らせるといいと思います. feedback (QgsProcessingFeedback) を利用するとそれを実現できます.

処理状況テキストとプログレスバーは2つのメソッドを使って更新できます: setProgressText(text)setProgress(percent) です.

次のメソッドを使うとさらに多くの情報を提供できます pushCommandInfo(text), pushDebugInfo(text), pushInfo(text)reportError(text).

スクリプトに問題がある場合、それを処理する正しい方法は QgsProcessingException を発生させることです。メッセージを引数として例外のコンストラクタに渡すことができます。プロセシングでは、アルゴリズムの実行元(ツールボックス、モデラー、Pythonコンソールなど)に応じてプロセシングとユーザーとの通信を処理します。

22.8.6. スクリプトのドキュメントを作成する

以下のものをオーバーロードするとあなたのスクリプトのドキュメントを作れます helpString()helpUrl() methods of QgsProcessingAlgorithm.

22.8.7. フラグ

QgsProcessingAlgorithmflags メソッドをオーバーライドして、アルゴリズムについてQGISに通知できます。たとえば、スクリプトをモデラーから非表示にすること、キャンセルできること、スレッドセーフではないことなどをQGISに伝えることができます。

ちなみに

デフォルトでは、処理タスクの実行中にQGISの応答性を維持するために、Processingはアルゴリズムを別のスレッドで実行します。アルゴリズムが定期的にクラッシュする場合は、おそらくバックグラウンドスレッドで安全に実行できないAPI呼び出しを使用している可能性があります。アルゴリズムのflags() メソッドからQgsProcessingAlgorithm.FlagNoThreadingフラグを返して、代わりにメインスレッドでアルゴリズムを実行するように強制します。

22.8.8. スクリプトアルゴリズムを書くためのベストプラクティス

スクリプトアルゴリズムを作成する際、特に他のQGISユーザーと共有したい場合に考慮すべきアイデアの簡単な概要を以下に示します。これらの単純な規則に従うことで、ツールボックス、モデラー、バッチ処理インターフェースなどのさまざまな処理要素間で一貫性が確保されます。

  • 結果のレイヤはロードしないでください。結果はプロセッシングに処理させ、必要であればレイヤをロードしてください。

  • あなたのアルゴリズム作成する出力について常に宣言して下さい.

  • メッセージボックスを表示したり、スクリプトのGUI要素を使用したりしないでください。ユーザーと通信する場合は、フィードバックオブジェクトのメソッド( QgsProcessingFeedback )を使用するか、 QgsProcessingException をスローします。

QGISで利用できる多くのプロセッシングアルゴリズムがすでに存在します. https://github.com/qgis/QGIS/blob/release-3_10/python/plugins/processing/algs/qgis でソースコードを参照することができます.