การดำเนินงานขนาน GIS ใน PyQGIS?


15

ข้อกำหนดทั่วไปใน GIS คือการใช้เครื่องมือประมวลผลกับไฟล์จำนวนหนึ่งหรือใช้กระบวนการสำหรับคุณสมบัติจำนวนหนึ่งในไฟล์หนึ่งไปยังไฟล์อื่น

การดำเนินการส่วนใหญ่เหล่านี้ขนานกันอย่างน่าอายซึ่งผลลัพธ์ของการคำนวณไม่ได้มีอิทธิพลต่อการดำเนินการอื่นใดในลูป ไม่เพียงแค่นั้น แต่บ่อยครั้งที่ไฟล์อินพุตนั้นแตกต่างกันไป

เคสแบบคลาสสิกคือไฟล์แบบเรียงต่อกันออกจากไฟล์ที่มีรูปหลายเหลี่ยมเพื่อคลิป

นี่คือวิธีการแบบคลาสสิก (ทดสอบ) เพื่อให้ได้สิ่งนี้ในสคริปต์ไพ ธ อนสำหรับ QGIS (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 รูป กระบวนการที่เกิดขึ้นใช้เวลาหนึ่งสัปดาห์ในเครื่อง quad core ของฉัน ทั้งหมดในขณะที่สามแกนเป็นเพียงการทำงาน

ทางออกที่ฉันมีอยู่ในหัวของฉันคือการส่งออกกระบวนการไปยังไฟล์สคริปต์และเรียกใช้แบบอะซิงโครนัสโดยใช้ gnu parallel ตัวอย่างเช่น อย่างไรก็ตามดูเหมือนว่าน่าละอายที่จะต้องเลื่อนออกจาก QGIS ไปสู่โซลูชั่นเฉพาะของ OS แทนที่จะใช้สิ่งที่มีมาจาก QGIS python ดังนั้นคำถามของฉันคือ:

ฉันสามารถขนานการดำเนินงานทางภูมิศาสตร์แบบขนานที่น่าอับอายใน python QGIS ได้หรือไม่?

ถ้าไม่เช่นนั้นอาจมีบางคนที่มีรหัสเพื่อส่งงานประเภทนี้ไปยังสคริปต์เชลล์แบบอะซิงโครนัส?


ไม่คุ้นเคยกับการประมวลผลหลายอย่างใน QGIS แต่ตัวอย่างเฉพาะ ArcGIS นี้อาจเป็นประโยชน์: gis.stackexchange.com/a/20352/753
blah238

ดูน่าสนใจ ฉันจะดูว่าฉันสามารถทำอะไรกับมัน
Mr Purple

คำตอบ:


11

หากคุณเปลี่ยนโปรแกรมของคุณเพื่ออ่านชื่อไฟล์จากบรรทัดคำสั่งและแยกไฟล์อินพุตของคุณเป็นกลุ่มขนาดเล็กคุณสามารถทำสิ่งนี้โดยใช้ GNU Parallel:

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

สิ่งนี้จะทำงาน 1 งานต่อคอร์

คอมพิวเตอร์เครื่องใหม่ทุกเครื่องมีหลายคอร์ แต่โปรแกรมส่วนใหญ่เป็นแบบอนุกรมและจะไม่ใช้หลายคอร์ อย่างไรก็ตามงานหลายอย่างขนานกันมาก:

  • เรียกใช้โปรแกรมเดียวกันกับไฟล์จำนวนมาก
  • รันโปรแกรมเดียวกันสำหรับทุกบรรทัดในไฟล์
  • เรียกใช้โปรแกรมเดียวกันสำหรับทุก ๆ บล็อกในไฟล์

GNU Parallel เป็นตัวปรับเทียบทั่วไปและทำให้การเรียกใช้งานแบบขนานบนเครื่องเดียวกันหรือบนเครื่องหลายเครื่องที่คุณเข้าถึงได้ง่าย

หากคุณมี 32 งานที่แตกต่างกันที่คุณต้องการเรียกใช้บน 4 CPU ทางตรงไปข้างหน้าเพื่อขนานคือการทำงาน 8 งานในแต่ละ CPU:

การตั้งเวลาอย่างง่าย

GNU Parallel แทนการวางกระบวนการใหม่เมื่อเสร็จสิ้น - ทำให้ซีพียูทำงานและประหยัดเวลา:

GNU Parallel Scheduling

การติดตั้ง

หาก 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


นี่คือสิ่งที่ฉันจะลองและพยายาม แต่ฉันต้องการให้ทุกคนอยู่ในหลาม บรรทัดต้องการการเขียนใหม่เพื่อใช้พูดคำว่า Popen เช่น ... สิ่งที่ต้องการ: จากการนำเข้า subprocess Popen, PIPE p = Popen (["ขนาน", "ogr2ogr", "- clipsrc", "clip_file * .shp", "output * .shp "input.shp"], stdin = PIPE, stdout = PIPE, stderr = PIPE) ปัญหาคือฉันยังไม่รู้วิธีเตรียมไวยากรณ์อย่างถูกต้อง
Mr Purple

คำตอบที่ยอดเยี่ยม ฉันไม่เคยเจอตัวดำเนินการลำไส้ใหญ่สามคน (หรือสี่เท่า) มาก่อน (แม้ว่าปัจจุบันฉันกำลังทำ Haskell mooc บน edX ดังนั้นไม่ต้องสงสัยเลยว่าจะมีสิ่งที่คล้ายกันเกิดขึ้น) ฉันเห็นด้วยกับคุณเกี่ยวกับซานตาผีนางฟ้าและเทพ แต่ไม่แน่นอนก็อบลิน: D
John Powell

@MrPurple ฉันคิดว่าความคิดเห็นรับประกันคำถามด้วยตัวเอง คำตอบนั้นยาวเกินไปที่จะใส่ความคิดเห็น
Ole Tange

ตกลงขอบคุณสำหรับลิงค์ หากฉันกำหนดคำตอบโดยใช้ gnu parallel ฉันจะโพสต์ไว้ที่นี่
Mr Purple

สูตรที่ดีสำหรับคุณmy_processing.pyสามารถพบได้ที่gis.stackexchange.com/a/130337/26897
Mr Purple

4

แทนที่จะใช้วิธี GNU Parallel คุณสามารถใช้โมดูลpython mutliprocessเพื่อสร้างกลุ่มของงานและดำเนินการได้ ฉันไม่สามารถเข้าถึงการตั้งค่า QGIS เพื่อทดสอบได้ แต่มีการเพิ่มมัลติโพรเซสใน Python 2.6 ดังนั้นหากคุณใช้ 2.6 หรือใหม่กว่านั้นควรพร้อมใช้งาน มีตัวอย่างจำนวนมากทางออนไลน์ที่ใช้โมดูลนี้


2
ฉันไปหลายขั้นตอน แต่ฉันยังไม่เห็นว่ามันประสบความสำเร็จใน implant ของหลามของ QGIS ฉันพบปัญหาหลายอย่างในขณะที่พยายาม ฉันอาจโพสต์คำถามเหล่านี้แยกต่างหาก เท่าที่ฉันสามารถบอกได้ว่าไม่มีใครสามารถเข้าถึงตัวอย่างสาธารณะที่เริ่มต้นด้วยสิ่งนี้
Mr Purple

มันเป็นความอัปยศที่แท้จริง หากใครบางคนสามารถเขียนตัวอย่างของโมดูลมัลติโพรเซสซึ่งห่อฟังก์ชัน pyQGIS เดียวอย่างที่ฉันทำกับ gnu parallel เราสามารถออกไปข้างนอกและขนานอะไรก็ได้ที่เราเลือก
Mr Purple

ฉันเห็นด้วย แต่ฉันบอกว่าฉันไม่สามารถเข้าถึง QGIS ได้ในขณะนี้
Steve Barnes

คำถามและคำตอบนี้อาจเป็นประโยชน์หากคุณใช้งานใน windows, gis.stackexchange.com/questions/35279/…
Steve Barnes

@MrPurple และหนึ่งgis.stackexchange.com/questions/114260/...ให้ตัวอย่าง
สตีฟบาร์นส์

3

นี่คือวิธีแก้ปัญหาแบบขนาน gnu ด้วยความระมัดระวังส่วนใหญ่อัลกอริทึม ogr หรือ saga ที่ทำงานโดยใช้ลินุกซ์แบบขนานส่วนใหญ่นั้นสามารถที่จะทำงานในการติดตั้ง QGIS ของคุณ

เห็นได้ชัดว่าวิธีนี้ต้องใช้การติดตั้งขนาน gnu หากต้องการติดตั้ง gnu parallel ใน Ubuntu ให้ไปที่เทอร์มินัลแล้วพิมพ์

sudo apt-get -y install parallel

หมายเหตุ: ฉันไม่สามารถรับคำสั่งเชลล์แบบขนานเพื่อทำงานใน Popen หรือ subprocess ซึ่งฉันต้องการได้ดังนั้นฉันจึงแฮ็กการส่งออกไปยังสคริปต์ทุบตีและวิ่งด้วย Popen แทน

นี่คือคำสั่งเชลล์เฉพาะที่ใช้ขนานที่ฉันหุ้มด้วยไพ ธ อน

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

แต่ละ {1} ถูกสลับเป็นตัวเลขจากช่วง {1..400} จากนั้นคำสั่งเชลล์สี่ร้อยคำสั่งจะได้รับการจัดการโดย gnu ขนานเพื่อใช้แกนทั้งหมดของ i7 ของฉันพร้อมกัน

นี่คือรหัสหลามจริงที่ฉันเขียนเพื่อแก้ปัญหาตัวอย่างที่ฉันโพสต์ หนึ่งสามารถวางในโดยตรงหลังจากสิ้นสุดรหัสในคำถาม

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

มันจะดีถ้ามี cross platform solution และมันจะดีถ้าฉันสามารถหาโมดูล python แบบมัลติโปรเซสเซอร์สำหรับ qgis ที่ฝัง python ได้ แต่ก็ไม่เป็นเช่นนั้น

ไม่ว่าทางออกนี้จะให้บริการฉันและบางทีคุณอาจจะเป็นอย่างดี


เห็นได้ชัดว่าเราควรคอมเม้นท์บรรทัด "processing.runalg" ในโค้ดชิ้นแรกเพื่อให้คลิปไม่ได้เรียงต่อกันเป็นลำดับแรกก่อนที่จะทำงานแบบขนาน นอกเหนือจากนั้นมันเป็นเพียงเรื่องของการคัดลอกและวางรหัสจากคำตอบใต้รหัสในคำถาม
Mr Purple

หากคุณเพียงแค่ต้องการรันคำสั่งประมวลผลจำนวนมากเช่นชุดของ "qgis: จางหาย" ที่ใช้กับไฟล์ต่าง ๆ ในแบบคู่ขนานคุณสามารถเห็นกระบวนการของฉันสำหรับเรื่องนี้ได้ที่purplelinux.co.nz/?p=190
Mr Purple
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.