在PyQGIS中并行进行GIS操作?


15

GIS中的一个常见要求是将处理工具应用于多个文件,或者将一个文件中多个特征的处理应用于另一个文件。

这些操作中的许多操作都是令人尴尬地并行的,因为计算结果绝不会影响循环中的任何其他操作。不仅如此,而且输入文件通常每个都是不同的。

一个典型的例子就是将形状文件与包含多边形的文件进行平铺。

这是一个(经过测试的)经典过程方法,可通过QGIS的python脚本实现。(将临时内存文件输出为真实文件的时间比处理我的测试文件的时间减少了一半以上)

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多个多边形之外,这是可以的。在我的四核计算机上,完成的过程需要一周以上的时间。同时,三个核心都处于闲置状态。

我想到的解决方案是将进程导出到脚本文件,然后使用gnu parallel例如异步运行它们。但是,不得不退出QGIS进入特定于操作系统的解决方案,而不是使用QGIS python本身的东西似乎很可耻。所以我的问题是:

我可以在python QGIS内部对令人尴尬的并行地理操作进行并行处理吗?

如果不是,那么也许有人已经拥有将这类工作发送给异步Shell脚本的代码?


不熟悉QGIS中的多重处理,但是此特定于ArcGIS的示例可能有一定用途:gis.stackexchange.com/a/20352/753
blah238 2014年

看起来很有趣。我会看看我能做什么。
紫色先生2014年

Answers:


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进行分发,则可以进行个人安装,不需要root访问。这样做可以在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,stderr = PIPE)问题是我尚不知道如何正确地准备语法
紫色先生2014年

很棒的答案。之前我没有遇到过三(或四)冒号运算符(尽管我目前在edX上进行过Haskell mooc操作,所以毫无疑问会出现类似情况)。我同意您关于圣诞老人,鬼魂,仙女和神灵的意见,但绝对不是妖精:D
约翰·鲍威尔

@MrPurple我认为评论本身值得一个问题。答案肯定太长,无法发表评论。
Ole Tange 2014年

好的,谢谢您的链接。如果我使用gnu parallel制定答案,我将在此处发布。
紫色先生2014年


4

可以使用python mutliprocess模块而不是使用GNU Parallel方法来创建任务池并执行它们。我无权使用QGIS设置对其进行测试,但是Python 2.6中添加了多进程功能,因此,如果您使用的是2.6或更高版本,则应该可用。在线上有很多使用此模块的示例。


2
我尝试了多进程,但尚未看到它成功嵌入到QGIS的嵌入式python中。我尝试了一些问题。我可以将它们发布为单独的问题。据我所知,没有任何公开的例子可供以此开始的人使用。
紫色先生2014年

真是可惜。如果有人像我对gnu并行那样编写一个包含单个pyQGIS函数的多进程模块的示例,那么我们都可以将所选择的东西并行化。
紫色先生2014年

我同意,但是正如我所说,目前我无法访问QGIS。
史蒂夫·巴恩斯



3

这是gnu并行解决方案。经过精心设计,可以使大多数基于linux的并行ogr或saga算法在QGIS安装环境中与它一起运行。

显然,此解决方案需要安装gnu parallel。例如,要在Ubuntu中安装gnu parallel,请转到终端并输入

sudo apt-get -y install parallel

注意:我无法让并行shell命令在Popen或子进程中工作,而我本来希望这样做,所以我将导出的内容整理到了bash脚本中,而是使用Popen来运行。

这是我包装在python中的使用并行的特定shell命令

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

每个{1}被交换为{1..400}范围内的一个数字,然后由gnu并行管理四百个shell命令,以同时使用我的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”行,以便在并行运行剪辑之前,不会先按顺序运行剪辑。除此之外,这仅仅是将答案中的代码复制并粘贴到问题中的代码下方的问题。
紫色先生2014年

如果您只想运行大量处理命令(例如一组“ qgis:dissolve”)并行应用到不同的文件,则可以在purplelinux.co.nz/?p=190
Purple Purple
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.