23.9. 新たなプロセシングアルゴリズムをPythonスクリプトで作成する

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

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

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

23.9.1. QgsProcessingAlgorithmを拡張する

以下のコードの内容です

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

  2. 地物の数を数えます

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

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

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

  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}

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

  • createInstance (必須)

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

  • name (必須)

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

  • displayName (必須)

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

  • group

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

  • groupId

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

  • shortHelpString

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

  • initAlgorithm (必須)

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

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

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

  • processAlgorithm (必須)

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

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

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

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

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

23.9.2. @alg デコレータ

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

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

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

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

  2. 地物の数を数えます

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

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

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

 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}

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

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

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

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

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

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

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

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

23.9.3.1. 入力タイプ

クラス

Alg定数

説明

QgsProcessingParameterAuthConfig

alg.AUTH_CFG

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

QgsProcessingParameterBand

alg.BAND

ラスタレイヤのバンド

QgsProcessingParameterBoolean

alg.BOOL

ブール値

QgsProcessingParameterColor

alg.COLOR

QgsProcessingParameterCoordinateOperation

alg.COORDINATE_OPERATION

A coordinate operation (for CRS transformations)

QgsProcessingParameterCrs

alg.CRS

座標参照系

QgsProcessingParameterDatabaseSchema

alg.DATABASE_SCHEMA

A database schema

QgsProcessingParameterDatabaseTable

alg.DATABASE_TABLE

A database table

QgsProcessingParameterDateTime

alg.DATETIME

A datetime (or a pure date or time)

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

A folder (destination folder)

QgsProcessingParameterNumber

alg.INT

整数

QgsProcessingParameterLayout

alg.LAYOUT

レイアウト

QgsProcessingParameterLayoutItem

alg.LAYOUT_ITEM

レイアウトアイテム

QgsProcessingParameterMapLayer

alg.MAPLAYER

マップレイヤ

QgsProcessingParameterMapTheme

alg.MAP_THEME

A project map theme

QgsProcessingParameterMatrix

alg.MATRIX

配列

QgsProcessingParameterMeshLayer

alg.MESH_LAYER

メッシュレイヤ

QgsProcessingParameterMultipleLayers

alg.MULTILAYER

レイヤのセット

QgsProcessingParameterNumber

alg.NUMBER

数値

QgsProcessingParameterPoint

alg.POINT

QgsProcessingParameterProviderConnection

alg.PROVIDER_CONNECTION

An available connection for a database provider

QgsProcessingParameterRange

alg.RANGE

数値の範囲

QgsProcessingParameterRasterLayer

alg.RASTER_LAYER

ラスターレイヤー

QgsProcessingParameterRasterDestination

alg.RASTER_LAYER_DEST

ラスターレイヤー

QgsProcessingParameterScale

alg.SCALE

地図の縮尺

QgsProcessingParameterFeatureSink

alg.SINK

地物シンク

QgsProcessingParameterFeatureSource

alg.SOURCE

地物ソース

QgsProcessingParameterString

alg.STRING

テキストストリング

QgsProcessingParameterVectorLayer

alg.VECTOR_LAYER

ベクターレイヤー

QgsProcessingParameterVectorDestination

alg.VECTOR_LAYER_DEST

ベクターレイヤー

23.9.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

ベクターレイヤー

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

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

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

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

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

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

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

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

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

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

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

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

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

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

23.9.7. フラグ

You can override the flags() method of QgsProcessingAlgorithm to tell QGIS more about your algorithm. You can for instance tell QGIS that the script shall be hidden from the modeler, that it can be canceled, that it is not thread safe, and more.

ちなみに

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

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

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

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

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

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

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