重要

翻訳は あなたが参加できる コミュニティの取り組みです。このページは現在 100.00% 翻訳されています。

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

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

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

スクリプトを scripts フォルダ(デフォルトの場所) に .py という拡張子で保存するとそのアルゴリズムは プロセシングツールボックス で利用できるようになります。

27.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'])。

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

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

27.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 デコレータはアルゴリズムの出力を定義するのに使われます。

既存のパラメータとその対応については、プロセシングアルゴリズムのための入力と出力の型 を参照のこと。

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

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

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

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

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

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

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

27.9.4. ユーザーとコミュニケーションする

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

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

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

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

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

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

27.9.6. フラグ

QgsProcessingAlgorithmflags() メソッドをオーバーライドすると、QGISにアルゴリズムについてより詳しく伝えることができます。例えば、スクリプトをモデラーから隠すこと、キャンセルすること、スレッドセーフでないこと、などをQGISに伝えることができます。

Tip

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

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

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

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

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

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

QGISにはすでに多くのプロセシングアルゴリズムが用意されています。コードは QGIS のレポにあります。