24.9. 新たなプロセシングアルゴリズムをPythonスクリプトで作成する
Pythonを使ってプロセシングアルゴリズムを書くためには、ふたつの選択肢があります。
QGIS内では、 プロセシングツールボックス の上部にある スクリプト メニューの 新しいスクリプトを作成 を使用して、自分のコードを書ける プロセシングスクリプトエディタ を開いて作成できます。タスクを簡素化するには、同じメニューの テンプレートから新しいスクリプトを作成する を使用して、スクリプトテンプレートから開始できます。これにより QgsProcessingAlgorithm
を拡張するテンプレートが開きます。
スクリプトを scripts
フォルダ(デフォルトの場所) にwith a .py
という拡張子で保存するとアルゴリズムは Processing Toolbox で利用可能になります.
24.9.1. QgsProcessingAlgorithmを拡張する
以下のコードの内容です
ベクタレイヤを入力に指定します
地物の数を数えます
バッファの操作を行います
バッファ操作の結果を使ってラスタレイヤを作ります
バッファレイヤ,ラスタレイヤと地物の数を返します
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 (必須)
ここでアルゴリズムの入力と出力を定義します.
INPUT
とOUTPUT
はそれぞれメインの入力と出力パラメータ用の推奨名称です.もしパラメータが他のパラメータに依存する場合,
parentParameterName
がこの関係を示します (フィールド/レイヤのバンドまたはレイヤの距離単位は利用できません).
- processAlgorithm (必須)
ここで処理が実行されます.
パラメータはインスタンスによって
parameterAsSource
やparameterAsDouble
のような特別な用途の関数を使って取得されます.processing.run
を使うとプロセシングアルゴリズムから他のアルゴリズムを実行できます. 最初のパラメータはアルゴリズムの名前です, 2つめのパラメータはそのアルゴリズム用のパラメータの辞書です. 他のアルゴリズム内で実行される場合is_child_algorithm
は通常True
に設定します.context
とfeedback
は実行環境とユーザとのやりとりチャンネルについてアルゴリズムに伝えます (キャンセルリクエストのキャッチ, 実行状況のレポート, テキストによるフィードバックの提供). (親) アルゴリズムのパラメータが "子" アルゴリズムのパラメータとして利用される場合オリジナルの値を利用する必要があります(例.parameters['OUTPUT']
).可能な限りキャンセルのためにフィードバックオブジェクトをチェックすることをお勧めします!そうすることで、不要な処理が発生するまでユーザーを待たせずに、すぐにキャンセルすることが可能になります。
アルゴリズムは、辞書として定義したすべての出力パラメータの値を返す必要があります。この場合、それはバッファとラスタライズされた出力レイヤ、および処理された地物数です。辞書キーは、元のパラメータ/出力名と一致する必要があります。
24.9.2. @alg デコレータ
@algデコレータを使用すると、Pythonコードを書き、適切な処理アルゴリズムにするための追加情報を提供する数行を追加することでで独自のアルゴリズムをつくることができます。この手法はアルゴリズムの作成、および入力と出力の指定を簡単にします。
アルゴリズム作成にデコレータを使う手法には一つの重要な制約があります.それは作成されたアルゴリズムが常にユーザープロセシングスクリプトプロバイダーに追加されることです--アルゴリズムを(たとえばプラグイン内で使う)カスタムプロバイダーに追加することはできません.
以下のコードは @alg デコレータを使って次の機能を実装してます
ベクタレイヤを入力として利用します
地物の数を数えます
バッファ操作を実行します
バッファ操作の結果を使ってラスタレイヤを作ります
バッファレイヤ,ラスタレイヤと地物の数を返します
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 デコレータはアルゴリズムの出力を定義するのにつかわれます.
24.9.3. プロセシングアルゴリズムのための入力と出力の型
対応するalgデコレータ定数を使用してProcessingでサポートされる入力タイプと出力タイプのリストを次に示します( algfactory.py ファイルにはalg定数の完全なリストが含まれています)。クラス名でソートされています。
24.9.3.1. 入力タイプ
クラス |
Alg定数 |
説明 |
---|---|---|
|
An annotation layer |
|
|
ユーザに利用可能な認証構成を選択させるか新しい認証構成を作成させます |
|
|
ラスタレイヤのバンド |
|
|
ブール値 |
|
|
色 |
|
|
A coordinate operation (for CRS transformations) |
|
|
座標参照系 |
|
|
A database schema |
|
|
A database table |
|
|
A datetime (or a pure date or time) |
|
|
距離の値用の倍精度数値パラメータ |
|
|
事前定義された複数の値からの選択を許す列挙 |
|
|
式 |
|
|
xmin, xmax, ymin, ymaxで定義された空間領域 |
|
|
ベクタレイヤ属性テーブルのフィールド |
|
|
既存ファイルのファイル名 |
|
|
新たに作成された出力ファイルのファイル名 |
|
|
A folder (destination folder) |
|
|
A geometry |
|
|
整数 |
|
|
レイアウト |
|
|
レイアウトアイテム |
|
|
マップレイヤ |
|
|
A project map theme |
|
|
配列 |
|
|
メッシュレイヤ |
|
|
レイヤのセット |
|
|
数値 |
|
|
点 |
|
|
A point cloud layer |
|
|
An available connection for a database provider |
|
|
数値の範囲 |
|
|
ラスターレイヤー |
|
|
ラスターレイヤー |
|
|
地図の縮尺 |
|
|
地物シンク |
|
|
地物ソース |
|
|
テキストストリング |
|
|
ベクターレイヤー |
|
|
ベクターレイヤー |
24.9.3.2. 出力タイプ
クラス |
Alg定数 |
説明 |
---|---|---|
|
ブール値 |
|
|
距離の値用の倍精度数値パラメータ |
|
|
既存ファイルのファイル名 |
|
|
フォルダ |
|
|
HTML |
|
|
整数 |
|
|
レイヤ定義 |
|
|
マップレイヤ |
|
|
レイヤのセット |
|
|
数値 |
|
|
ラスターレイヤー |
|
|
テキストストリング |
|
|
ベクターレイヤー |
24.9.4. アルゴリズムの出力を渡す
出力としてレイヤ(ラスタまたはベクタ)を指定すると、アルゴリズムは終了後にそれをQGISに追加しようとします。
ラスタレイヤ出力: QgsProcessingParameterRasterDestination / alg.RASTER_LAYER_DEST.
ベクタレイヤ出力: QgsProcessingParameterVectorDestination / alg.VECTOR_LAYER_DEST.
したがって processing.run()
メソッドが作成したレイヤをユーザの現在のプロジェクトに読み込まない場合でも2つの出力レイヤ (バッファとラスタバッファ)がロードされます. will be loaded,これらはユーザが入力した保存先 (またはユーザが保存先を指定しない場合は一時的な保存先)に保存されるからです.
レイヤがアルゴリズムの出力として作成される場合、そのように宣言する必要があります。そうしないと、宣言されたものがアルゴリズムが実際に作成するものと一致しないため、モデラーでアルゴリズムを適切に使用できません。
結果の辞書で文字列、数値などを指定することで返すことができます(「NUMBEROFFEATURES」で示したとおり)が、それらは常にアルゴリズムからの出力として明示的に定義する必要があります。アルゴリズムがモデルの一部として使用される場合、これらのアルゴリズムは後のアルゴリズムで使用するのに役立つ可能性があるため、アルゴリズムではできるだけ多くの有用な値を出力することをを推奨します。
24.9.5. ユーザーとやりとりする
あなたのアルゴリズムが処理に長い時間を必要とする場合ユーザに進捗状況を知らせるといいと思います. feedback
(QgsProcessingFeedback
) を利用するとそれを実現できます.
処理状況テキストとプログレスバーは2つのメソッドを使って更新できます: setProgressText(text)
と setProgress(percent)
です.
次のメソッドを使うとさらに多くの情報を提供できます pushCommandInfo(text)
, pushDebugInfo(text)
, pushInfo(text)
と reportError(text)
.
スクリプトに問題がある場合、それを処理する正しい方法は QgsProcessingException
を発生させることです。メッセージを引数として例外のコンストラクタに渡すことができます。プロセシングでは、アルゴリズムの実行元(ツールボックス、モデラー、Pythonコンソールなど)に応じてプロセシングとユーザーとの通信を処理します。
24.9.6. スクリプトのドキュメントを作成する
以下のものをオーバーロードするとあなたのスクリプトのドキュメントを作れます helpString()
と helpUrl()
methods of QgsProcessingAlgorithm
.
24.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フラグを返して、代わりにメインスレッドでアルゴリズムを実行するように強制します。
24.9.8. スクリプトアルゴリズムを書くためのベストプラクティス
スクリプトアルゴリズムを作成する際、特に他のQGISユーザーと共有したい場合に考慮すべきアイデアの簡単な概要を以下に示します。これらの単純な規則に従うことで、ツールボックス、モデラー、バッチ処理インターフェースなどのさまざまな処理要素間で一貫性が確保されます。
結果のレイヤはロードしないでください。結果はプロセッシングに処理させ、必要であればレイヤをロードしてください。
あなたのアルゴリズム作成する出力について常に宣言して下さい.
メッセージボックスを表示したり、スクリプトのGUI要素を使用したりしないでください。ユーザーと通信する場合は、フィードバックオブジェクトのメソッド(
QgsProcessingFeedback
)を使用するか、QgsProcessingException
をスローします。
QGISで利用できる多くのプロセッシングアルゴリズムがすでに存在します. https://github.com/qgis/QGIS/blob/release-3_22/python/plugins/processing/algs/qgis でソースコードを参照することができます.