PyQGISでのGIS操作の並列化?


15

GISの一般的な要件は、処理ツールを多数のファイルに適用するか、1つのファイルの多数のフィーチャのプロセスを別のファイルに適用することです。

これらの演算の多くは、計算結果がループ内の他の演算に影響を与えないという点で、恥ずかしいほど並行しています。それだけでなく、多くの場合、入力ファイルはそれぞれ異なります。

典型的な例は、シェイプファイルを、ポリゴンを含むファイルに対してクリップします。

これは、QGISのPythonスクリプトでこれを達成するための(テスト済みの)古典的な手続き型の方法です。(テストファイルを処理する時間を半分にしたよりも、実際のファイルへの一時メモリファイルの出力をfyi)

import processing
import os
input_file="/path/to/input_file.shp"
clip_polygons_file="/path/to/polygon_file.shp"
output_folder="/tmp/test/"
input_layer = QgsVectorLayer(input_file, "input file", "ogr")
QgsMapLayerRegistry.instance().addMapLayer(input_layer)
tile_layer  = QgsVectorLayer(clip_polygons_file, "clip_polys", "ogr")
QgsMapLayerRegistry.instance().addMapLayer(tile_layer)
tile_layer_dp=input_layer.dataProvider()
EPSG_code=int(tile_layer_dp.crs().authid().split(":")[1])
tile_no=0
clipping_polygons = tile_layer.getFeatures()
for clipping_polygon in clipping_polygons:
    print "Tile no: "+str(tile_no)
    tile_no+=1
    geom = clipping_polygon.geometry()
    clip_layer=QgsVectorLayer("Polygon?crs=epsg:"+str(EPSG_code)+\
    "&field=id:integer&index=yes","clip_polygon", "memory")
    clip_layer_dp = clip_layer.dataProvider()
    clip_layer.startEditing()
    clip_layer_feature = QgsFeature()
    clip_layer_feature.setGeometry(geom)
    (res, outFeats) = clip_layer_dp.addFeatures([clip_layer_feature])
    clip_layer.commitChanges()
    clip_file = os.path.join(output_folder,"tile_"+str(tile_no)+".shp")
    write_error = QgsVectorFileWriter.writeAsVectorFormat(clip_layer, \
    clip_file, "system", \
    QgsCoordinateReferenceSystem(EPSG_code), "ESRI Shapefile")
    QgsMapLayerRegistry.instance().addMapLayer(clip_layer)
    output_file = os.path.join(output_folder,str(tile_no)+".shp")
    processing.runalg("qgis:clip", input_file, clip_file, output_file)
    QgsMapLayerRegistry.instance().removeMapLayer(clip_layer.id())

入力ファイルが2GBで、ポリゴンクリッピングファイルに400以上のポリゴンが含まれていることを除いて、これは問題ありません。結果として生じるプロセスは、クアッドコアマシンで1週間以上かかります。その間、3つのコアはアイドル状態にあります。

私の頭の中にある解決策は、プロセスをスクリプトファイルにエクスポートし、たとえばgnu parallelを使用して非同期で実行することです。ただし、QGIS pythonにネイティブなものを使用するのではなく、QGISからOS固有のソリューションにドロップする必要があるのは残念に思えます。だから私の質問は:

Python QGIS内で、恥ずかしいほどに並列な地理的操作をネイティブに並列化できますか?

そうでない場合、おそらく誰かがすでにこの種の作業を非同期シェルスクリプトに送信するコードを持っていますか?


QGISでのマルチプロセッシングには精通していませんが、このArcGIS固有の例は役に立つ
blah238

面白そうです。私はそれで何ができるか見ていきます。
ミスターパープル14年

回答:


11

コマンドラインからファイル名を読み取るようにプログラムを変更し、入力ファイルを小さなチャンクに分割する場合、GNU Parallelを使用して次のようなことができます。

parallel my_processing.py {} /path/to/polygon_file.shp ::: input_files*.shp

これにより、コアごとに1つのジョブが実行されます。

すべての新しいコンピューターには複数のコアがありますが、ほとんどのプログラムは本質的にシリアルであるため、複数のコアを使用しません。ただし、多くのタスクは非常に並列化可能です。

  • 多くのファイルで同じプログラムを実行する
  • ファイル内のすべての行に対して同じプログラムを実行する
  • ファイル内のすべてのブロックに対して同じプログラムを実行します

GNU Parallelは一般的な並列化ツールであり、同じマシン上またはsshにアクセスできる複数のマシン上でジョブを簡単に並列実行できます。

4つのCPUで実行する32の異なるジョブがある場合、並列化する簡単な方法は、各CPUで8つのジョブを実行することです。

シンプルなスケジューリング

代わりに、GNU Parallelは終了時に新しいプロセスを生成します-CPUをアクティブに保ち、時間を節約します:

GNU並列スケジューリング

設置

GNU Parallelがディストリビューション用にパッケージ化されていない場合は、ルートアクセスを必要としないパーソナルインストールを実行できます。これを行うことにより、10秒で実行できます。

(wget -O - pi.dk/3 || curl pi.dk/3/ || fetch -o - http://pi.dk/3) | bash

他のインストールオプションについては、http://git.savannah.gnu.org/cgit/parallel.git/tree/READMEを参照してください。

もっと詳しく知る

他の例を参照してください:http : //www.gnu.org/software/parallel/man.html

イントロビデオを見る:https : //www.youtube.com/playlist?list=PL284C9FF2488BC6D1

チュートリアルをご覧くださいhttp : //www.gnu.org/software/parallel/parallel_tutorial.html

サポートを受けるには、メーリングリストにサインアップしてくださいhttps : //lists.gnu.org/mailman/listinfo/parallel


これは私が試みようとしたもののようなものですが、すべてPython内に残しておく必要があります。たとえば、Popenを使用するには、行を書き換える必要があります...サブプロセスインポートPopenから、PIPE p = Popen(["parallel"、 "ogr2ogr"、 "-clipsrc"、 "clip_file * .shp"、 "output * .shpの「input.shp」]、STDIN = PIPE、STDOUT = PIPE、標準エラー出力= PIPE)トラブルは、私が正しく構文を準備する方法を知って、まだいけないです
氏パープル

素晴らしい答え。以前にトリプル(または4つの)コロン演算子に出くわしたことはありませんでした(現在、edXでHaskell moocをやっているので、間違いなく似たようなものが来るでしょう)。私はサンタ、幽霊、妖精や神々についてのあなたに同意し、間違いなくないゴブリン:D
ジョン・パウエルを

@MrPurpleコメントはそれ自体で質問を正当化すると思います。答えは間違いなく長すぎてコメントを入れることができません。
オレ丹下

OK、リンクをありがとう。gnu parallelを使用して回答を作成する場合は、ここに投稿します。
ミスターパープル14年

あなたのための良い製剤my_processing.pyで発見することができgis.stackexchange.com/a/130337/26897
氏パープル

4

GNU Parallelメソッドを使用する代わりに、python mutliprocessモジュールを使用してタスクのプールを作成し、実行できます。QGISセットアップにアクセスしてテストすることはできませんが、Python 2.6でマルチプロセスが追加されたので、2.6以降を使用している場合は利用可能になります。このモジュールの使用に関するオンラインの例はたくさんあります。


2
マルチプロセスを試してみましたが、QGISの組み込みpythonで正常に実装されるのをまだ見ていません。試してみたところ、いくつかの問題にぶつかりました。それらを個別の質問として投稿する場合があります。私が知る限り、これを始めた人がアクセスできる公開例はありません。
ミスターパープル14年

それは本当に残念です。誰かが私がgnu parallelで行ったように単一のpyQGIS関数をラップするマルチプロセスモジュールの例を書くことができれば、選択したものをすべてオフにして並列化できます。
ミスターパープル14年

同意しますが、私が言ったように、現時点ではQGISにアクセスできません。
スティーブバーンズ14年

この質問と回答は、windows、gis.stackexchange.com/questions/35279/…の
Steve Barnes

@MrPurpleおよびこれはgis.stackexchange.com/questions/114260/…例を示します
スティーブバーンズ14年

3

これがGNU並列ソリューションです。いくつかの注意を払えば、ほとんどの並外れた並列LinuxベースのogrまたはsagaアルゴリズムをQGISインストール内で実行することができます。

明らかに、このソリューションには、gnu parallelのインストールが必要です。たとえば、Ubuntuにgnu parallelをインストールするには、ターミナルに移動して次のように入力します。

sudo apt-get -y install parallel

注:並列シェルコマンドをPopenまたはサブプロセスで動作させることはできませんでしたが、これはbashスクリプトへのエクスポートをハッキングし、代わりにPopenで実行しました。

ここに私がPythonでラップしたパラレルを使用した特定のシェルコマンドがあります

parallel ogr2ogr -skipfailures -clipsrc tile_{1}.shp output_{1}.shp input.shp ::: {1..400}

各{1}は{1..400}の範囲の数値に交換され、400個のシェルコマンドがgnu parallelによって管理され、i7のすべてのコアが同時に使用されます:)。

以下は、私が投稿した問題の例を解決するために書いた実際のpythonコードです。問題のコードの終わりの直後に貼り付けることができます。

import stat
from subprocess import Popen
from subprocess import PIPE
feature_count=tile_layer.dataProvider().featureCount()
subprocess_args=["parallel", \
"ogr2ogr","-skipfailures","-clipsrc",\
os.path.join(output_folder,"tile_"+"{1}"+".shp"),\
os.path.join(output_folder,"output_"+"{1}"+".shp"),\
input_file,\
" ::: ","{1.."+str(feature_count)+"}"]
#Hacky part where I write the shell command to a script file
temp_script=os.path.join(output_folder,"parallelclip.sh")
f = open(temp_script,'w')
f.write("#!/bin/bash\n")
f.write(" ".join(subprocess_args)+'\n')
f.close()
st = os.stat(temp_script)
os.chmod(temp_script, st.st_mode | stat.S_IEXEC)
#End of hacky bash script export
p = Popen([os.path.join(output_folder,"parallelclip.sh")],\
stdin=PIPE, stdout=PIPE, stderr=PIPE)
#Below is the commented out Popen line I couldn't get to work
#p = Popen(subprocess_args, stdin=PIPE, stdout=PIPE, stderr=PIPE)
output, err = p.communicate(b"input data that is passed to subprocess' stdin")
rc = p.returncode
print output
print err

#Delete script and old clip files
os.remove(os.path.join(output_folder,"parallelclip.sh"))
for i in range(feature_count):
    delete_file = os.path.join(output_folder,"tile_"+str(i+1)+".shp")
    nosuff=os.path.splitext(delete_file)[0]
    suffix_list=[]
    suffix_list.append('.shx')
    suffix_list.append('.dbf')
    suffix_list.append('.qpj')
    suffix_list.append('.prj')
    suffix_list.append('.shp')
    suffix_list.append('.cpg')
    for suffix in suffix_list:
        try:
            os.remove(nosuff+suffix)
        except:
            pass

すべてのコアが完全なノイズまで発火するのを見ると、それは本当に何かを教えてください:)。OleとGnu Parallelを構築したチームに感謝します。

クロスプラットフォームソリューションがあればいいのですが、qgis組み込みpythonのマルチプロセッシングpythonモジュールを見つけられたらいいのですが、そうではありませんでした。

とにかく、このソリューションは私とおそらくあなたに役立つでしょう。


明らかに、最初のコードの「processing.runalg」行をコメントアウトして、クリップが並行して実行される前に順番に実行されないようにする必要があります。それ以外は、問題のコードの下にある回答からコードをコピーして貼り付けるだけです。
ミスターパープル14

一連の「qgis:dissolve」などの多くの処理コマンドを異なるファイルに並行して実行する場合は、purplelinux.co.nz /?p
Mr Purple
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.