Паралельні операції ГІС в PyQGIS?


15

Загальна вимога в ГІС - застосувати інструмент обробки до ряду файлів або застосувати процес для ряду функцій одного файлу до іншого файлу.

Значна частина цих операцій бентежно паралельна тим, що результати обчислень жодним чином не впливають на будь-яку іншу операцію в циклі. Мало того, але часто вхідні файли є різними.

Класичний випадок - це створення файлів із формами файлів, що містять багатокутники, для вирізання їх.

Ось (перевірений) класичний процедурний метод для досягнення цього в сценарії python для 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())

Це було б добре, за винятком того, що мій вхідний файл становить 2 Гб, а відсічний файл багатокутника містить 400+ полігонів. Отриманий процес займає більше тижня на моїй чотирьохядерній машині. Увесь час три ядра просто працюють.

У мене в голові рішення - експортувати процес у файли сценаріїв і запускати їх асинхронно, наприклад, паралельно з gnu. Однак видається прикро відмовитись від QGIS у специфічному для ОС рішенні, а не використовувати щось рідне для QGIS python. Отже, моє питання:

Чи можу я зніяковіло паралельно встановити паралельні географічні операції всередині python QGIS?

Якщо ні, то, можливо, у когось вже є код, щоб відправити таку роботу в асинхронні сценарії оболонки?


Не знайомий з багатопроцесорними процесами в QGIS, але цей специфічний для ArcGIS приклад може бути корисним: gis.stackexchange.com/a/20352/753
blah238

Виглядає цікаво. Я побачу, що я можу з цим зробити.
Містер Перпл,

Відповіді:


11

Якщо ви зміните програму, щоб прочитати ім'я файлу з командного рядка і розділили вхідний файл на менші шматки, ви можете зробити щось подібне за допомогою GNU Parallel:

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

Це запустить 1 завдання на ядро.

Усі нові комп’ютери мають декілька ядер, але більшість програм мають серійний характер і тому не використовуватимуть декілька ядер. Однак багато завдань є надзвичайно паралельними:

  • Запустіть ту саму програму на багатьох файлах
  • Запустіть одну і ту ж програму для кожного рядка у файлі
  • Запустіть одну і ту ж програму для кожного блоку у файлі

GNU Parallel є загальним паралелізатором і дозволяє легко виконувати завдання паралельно на одній машині або на декількох машинах, до яких ви маєте доступ ssh.

Якщо у вас є 32 різні завдання, які ви хочете запустити на 4 процесорах, прямим способом паралелізації є виконання 8 завдань на кожному процесорі:

Просте планування

GNU Parallel замість цього створює новий процес, коли закінчується - підтримуючи активні процесори та заощаджуючи час:

Паралельне планування 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


Це щось на кшталт того, що я збирався спробувати і спробувати, але мені це потрібно, щоб усі залишилися всередині пітона. Рядок потрібно переписати, щоб використовувати, наприклад, скажімо, Popen ... Щось на кшталт: з імпорту підпроцесу Popen, PIPE p = Popen (["паралельний", "ogr2ogr", "- clipsrc", "clip_file * .shp", "вихід *" .shp "input.shp"], stdin = PIPE, stdout = PIPE, stderr = PIPE) Проблема в тому, що я ще не знаю, як правильно підготувати синтаксис
пан Purple

Дивовижна відповідь. Я раніше не стикався з потрійними (або вчетверо) операторами двокрапки (хоча я зараз роблю Haskell mooc на edX, тому, без сумніву, щось подібне вийде). Я згоден з вами про Санту, привидів, феї та богів, але точно не гоблінів: D
Джон Пауелл

@MrPurple Я думаю, що коментар виправдовує питання самостійно. Відповідь, безумовно, занадто довга, щоб ставити коментар.
Оле Танге

Добре, дякую за посилання. Якщо я сформулюю відповідь за допомогою gnu паралельно, я опублікую її тут.
Містер Перплей

Хороший рецепт для вашого вмістуmy_processing.py можна знайти на сайті gis.stackexchange.com/a/130337/26897
Містер

4

Замість того, щоб використовувати метод GNU Parallel, ви можете використовувати модуль mutliprocess python для створення пулу завдань та їх виконання. У мене немає доступу до налаштування QGIS, щоб перевірити його, але багатопроцесорний доданий у Python 2.6, за умови, що ви використовуєте 2.6 або пізніші версії, вони повинні бути доступними. В Інтернеті є багато прикладів використання цього модуля.


2
Я дав багатопроцесорний процес, але я ще не бачив його успішно вбудованим пітоном QGIS. Коли я спробував це, я потрапив на ряд питань. Я можу розмістити їх як окремі запитання. Наскільки я можу сказати, немає публічних прикладів, доступних для когось, починаючи з цього.
Містер Пурпла

Це справжній сором. Якби хтось міг написати приклад багатопроцесорного модуля, що охоплює одну функцію pyQGIS, як я це робив з паралеллю gnu, тоді ми могли б все відключити і паралелізувати все, що б ми не вибрали.
Містер Пурпла

Я згоден, але, як я вже сказав, у мене немає доступу до QGIS на даний момент.
Стів Барнс

Це питання та відповідь можуть бути корисними,
Стів Барнс

@MrPurple і цей gis.stackexchange.com/questions/114260/… наводить приклад
Стів Барнс

3

Ось гну-паралельне рішення. З деякою обережністю можна зробити найбільш приємно паралельні алгоритми на основі Linux або OGG або Saga, які працюють з ним всередині вашої установки QGIS.

Очевидно, що це рішення вимагає встановлення гну паралельно. Наприклад, щоб встановити gnu паралельно в Ubuntu, наприклад, перейдіть до свого терміналу та введіть

sudo apt-get -y install parallel

NB: Я не міг отримати команду паралельної оболонки для роботи в Popen або підпроцесі, що я хотів би, тому я зламав разом експорт до bash-скрипту і запустив його замість Popen.

Ось конкретна команда оболонки, використовуючи паралель, яку я загорнув у python

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

Кожен {1} заміняється на число з діапазону {1..400}, і тоді чотириста команд оболонок управляються gnu паралельно, щоб одночасно використовувати всі ядра мого 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

Дозвольте сказати, що це дійсно щось, коли ви бачите, що всі сердечники стріляють до повного шуму :). Особлива подяка Оле та команді, яка побудувала Gnu Paralellel.

Було б непогано мати рішення для крос-платформи, і було б добре, якби я міг зрозуміти багатопроцесорний модуль python для вбудованого в qgis python, але, на жаль, цього не було.

Незалежно від цього рішення буде служити мені, і, можливо, ви чудово.


Очевидно, слід прокоментувати рядок "obdela.runalg" у першому фрагменті коду, щоб кліп не запускався послідовно першим перед тим, як його запустити паралельно. Крім цього, це просто питання копіювання та вставлення коду з відповіді під кодом у питанні.
Містер Пурпла

Якщо ви просто хочете виконувати багато команд обробки, як-от набір "qgis: розчинення", що застосовується до різних файлів паралельно, ви можете побачити мій процес цього на purplelinux.co.nz/?p=190
Містер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.