Inleiding
In een eerder blog heb ik de batchverwerking in Python voor ArcGIS besproken,Batchverwerking in Python voor ArcGIS en Batchverwerking in Python voor ArcGIS - Implementatievoorbeeld genoemd, waarbij ik batchverwerking met ArcPy en Python introduceerde, met voorbeelden van best practices en codepatronen.Deze keer voeg ik een uitleg toe over het updaten van codepatronen voor Python 3.3 en hoger, evenals een voorbeeld van integratie met geoprocessing tools in ArcGIS Pro.
Let op, de code die in dit artikel wordt uitgelegd is al gepubliceerd als een Farmland polygon conversion tools en is direct bruikbaar als een Python toolbox. Raadpleeg deze als referentie.
Codepatroon
In het vorige artikel werden drie punten behandeld als codepatroon:
1) Worker functie: batch_convert
2) Wrapper functie voor worker: multi_run_batch_convert * Deze is nu niet nodig
3) Batchverwerkingsfunctie: exec_batch_convert
Echter, vanaf Python 3.3 is de starmap-methode toegevoegd aan de Pool-klasse van het multiprocessing-module, waarmee meerdere argumenten kunnen worden doorgegeven. Daarom is de wrapper functie niet meer nodig in de huidige ArcGIS Pro-omgeving en kan de code eenvoudiger worden gemaakt.
Hieronder leg ik uit hoe je code schrijft rond deze punten, gebaseerd op een voorbeeld.
Voorbeeldcode (conversie van landbouwperceel polygonen in GeoJSON-formaat)
De data die wordt geconverteerd komt uit dezelfde bron als het vorige artikel, namelijk de "Landbouwperceel afbakening informatie (polygonen)" dataset die door het Ministerie van Landbouw, Bosbouw en Visserij openbaar is gemaakt.Als voorbeeld gebruiken we deze dataset.
* Omdat de structuur van de polygonen anders is dan in het vorige artikel, is het voorbeeld dat hier wordt gepresenteerd gebaseerd op code die converteert naar de nieuwste polygonen (GeoJSON-formaat).Het voorbeeld (MP_Farmland_JsonToFeatureClass.py) verwerkt één "prefectuur folder" na het uitpakken van ZIP-bestanden met polygonen.
Na uitpakken bevat deze folder meerdere GeoJSON-bestanden per stad/district/dorp onder de "prefectuur folder".
Input folder: prefectuur folder met GeoJSON-bestanden per stad/district/dorp
Voorbeeld)
|-2024_02
|-2024_022012.json # GeoJSON per stad/district/dorp
|-2024_022021.json
|-2024_022039.json
.....
In dit voorbeeldprogramma wordt deze prefectuur folder als input gebruikt en worden de volgende stappen uitgevoerd:
1. Maak een geodatabase (FGDB) voor elke stad/district/dorp en converteer landbouwperceel polygonen naar feature classes (dit gebeurt in batchverwerking)
2. Maak een geodatabase (FGDB) voor de prefectuur folder en merge alle feature classes van stap 1 tot één feature class
3. Verwijder de individuele geodatabases per stad/district/dorp
Als resultaat blijft alleen één samengevoegde Farmland feature class over in de prefectuur FGDB (zie onderstaande afbeelding).
Output folder: locatie waar de samengevoegde prefectuur FGDB wordt aangemaakt
Voorbeeld)
|-2024_02_filegdb
|-2024_022012.gdb # FGDB per stad/district/dorp wordt verwijderd na merge in prefectuur FGDB
| c_2024_022012
|-2024_022021.gdb
| c_2024_022021
.....
|-2024_02_filegdb.gdb # Prefectuur FGDB waarin gemergde feature classes worden opgeslagen
| Farmland # Naam van feature class (vast binnen code)
Hieronder leg ik uit hoe je code schrijft rond batchverwerking.
1) Worker functie : batch_convert
Als codepatroon beschrijf ik eerst de worker functie zoals gebruikelijk bij ArcPy-verwerking. Let hierbij op dat:om te zorgen dat verwerking overeenkomt met proces 1 in Pro, moet je bij meerdere processen (meerdere worker functies) toegang krijgen via een enterprise geodatabase of ervoor zorgen dat dezelfde file geodatabase niet door meerdere processen tegelijk wordt gebruikt.
def batch_convert(in_jsonfile :str, outws :str) -> str:
'"'
'Verwerking uitgevoerd in 1 proces:',' field 63':' 1) Schrijven naar FGDB kan niet vanuit meerdere processen tegelijk',' field 64':' Converteer elk GeoJSON bestand per stad/district/dorp naar een feature class binnen 1 stad/district/dorp.gdb',' field 65':'',' field 66':'in_jsonfile : pad naar GeoJSON bestand per stad/district/dorp',' field 67':'outws : output gdb',' field 68':'"'"'',' field 69':'if not arcpy.Exists(outws):',' field 70':' outfolder = u"{0}".format(os.path.dirname(outws))',' field 71':' foldername= u"{0}".format(os.path.basename(outws))',' field 72':' arcpy.management.CreateFileGDB(outfolder, foldername, "CURRENT")',' field 73':'filename = os.path.basename(in_jsonfile)',' field 74':'in_json_file = os.path.splitext(filename)[0]',' field 75':'newfc = u"c_{0}".format(in_json_file) # Bestandnaam beginnend met cijfer kan niet direct worden geconverteerd, prefix c toevoegen',' field 76':'# Converteer met eigen FarmlandGeojsonToFeaturesEx klasse',' field 77':'#arcpy.conversion.JSONToFeatures(in_jsonfile, os.path.join(outws, newfc))',' field 78':'convGeojson = FarmlandGeojsonToFeaturesEx()',' field 79':'convGeojson.geojson_to_features(in_jsonfile, os.path.join(outws, newfc))',' field 80':'del convGeojson',' field 81':'return u" Conversie voltooid: {0}".format(outws)',' field 82':
1) FGDBへの書込みは仕様で複数プロセスで書込みできないため
1市区町村のGeoJSONファイルを 独自のFarmlandGeojsonToFeaturesEx クラスで1市区町村.gdb内のフィーチャクラスに変換
in_jsonfile : 市区町村のGeoJSONファイルへのパス
outws : 出力するgdb
'''
if not arcpy.Exists(outws):
outfolder = u"{0}".format(os.path.dirname(outws))
foldername= u"{0}".format(os.path.basename(outws))
arcpy.management.CreateFileGDB(outfolder, foldername, "CURRENT")
filename = os.path.basename(in_jsonfile)
in_json_file = os.path.splitext(filename)[0]
newfc = u"c_{0}".format(in_json_file) #数値で始まるファイル名はそのまま変換できないので接頭にc_を入れる
#独自のFarmlandGeojsonToFeaturesEx クラスで変換
#arcpy.conversion.JSONToFeatures(in_jsonfile, os.path.join(outws, newfc))
convGeojson = FarmlandGeojsonToFeaturesEx()
convGeojson.geojson_to_features(in_jsonfile, os.path.join(outws, newfc))
del convGeojson
return u" 変換済:{0}".format(outws)
2)ワーカー関数のラッパー
上述のように、現在のArcGIS Pro の環境で利用する際は不要のため、定義していません。
3)マルチプロセスの処理 : exec_batch_convert
各プロセス用のpythonw.exe へのパスを、multiprocessing.set_executable() で設定し、ワーカー関数へ渡す2つの引数(in_jsonfile, outws)を params にリスト化して入れます。その後、pool = multiprocessing.Pool(cpu_cnt) で確保した pool にresults = pool.starmap(batch_convert, params) で関数名 (batch_convert) と params を指定します。また、各ワーカー関数での処理が終了したら、
pool.close()
pool.join()
を呼び出します。
def exec_batch_convert(infolder :str, outfolder :str, cpu_cnt :int):
'''
マルチプロセスでの処理:
'''
try:
start = datetime.datetime.now()
arcpy.AddMessage(u"-- Strat: MP_Farmland_JsonToFeatureClass --:{0}".format(start))
#a) 各プロセス用の pythonw.exe を設定
python_path = sys.exec_prefix
multiprocessing.set_executable(os.path.join(python_path,'pythonw.exe'))
#multiprocessing.set_executable(os.path.join(python_path,'python.exe')) #CMDプロンプトの画面が起動するので'pythonw.exe'を使う
#b) 各プロセスに渡すパラメータをリスト化
arcpy.AddMessage(u" Convert each GeoJSON files : multiprocessing")
arcpy.env.workspace = infolder
infiles = arcpy.ListFiles("*.json")
params=[]
for infile in infiles:
param1 = os.path.join(infolder,infile) #市区町村のGeoJSONファイル
filename = os.path.basename(infile)
gdbname = u"{0}.gdb".format(os.path.splitext(filename)[0])
param2 = os.path.join(outfolder,gdbname) # 出力する市区町村ファイルジオデータベース
params.append((param1, param2))
if len(infiles) < cpu_cnt: # 処理ファイル数がCPUコアより少ない場合無駄なプロセスを起動不要
cpu_cnt = len(infiles)
pool = multiprocessing.Pool(cpu_cnt) # cpu数分プロセス作成
results = pool.starmap(batch_convert, params) # 割り当てプロセスで順次実行される(Python3.3で追加されたstarmapは複数の引数に対応)
pool.close()
pool.join()
# 各プロセスでの処理結果を出力
for r in results:
arcpy.AddMessage(u"{0}".format(r))
#c) 各プロセスで変換されたフィーチャクラスを都道府県のFGDBへマージしたものを作成("Farmland")
#~省略~
# フィールドエイリアスを設定
#~省略~
# コード値ドメインを設定
#~省略~
#d) マージが終わったので後片付け 各市区町村のFGDBを削除
#~省略~
fin = datetime.datetime.now()
arcpy.AddMessage(u"-- Finish: MP_Farmland_JsonToFeatureClass --:{0}".format(fin))
arcpy.AddMessage(u" Elapsed time:{0}".format(fin-start))
except:
arcpy.AddError(u"Exception:{0}".format(sys.exc_info()[2]))
ジオプロセシングツールへの組み込み
De conversieverwerking van landbouwpercelen in GeoJSON-formaat is voltooid tot aan de bovenstaande code, dus hierna,Python toolbox zal ik uitleggen over het aanroepen vanuit de gedefinieerde geoprocessing toolbox in Python.
In de Python toolbox, zoals vermeld in de helpsectie van Wat is een Python toolbox?, worden geoprocessing toolboxes en geoprocessing tools gedefinieerd als klassen.
1) Definitie en toevoeging van toolboxes
Twee geoprocessing tools gedefinieerd als class AgrilandJsonConvTool en class AgrilandShpConvTool worden toegevoegd tijdens de initialisatie van class Toolbox,
self.tools = [AgrilandJsonConvTool, AgrilandShpConvTool]
toegevoegd.
# Importeren van modules die multi-processing ondersteunen
from MP_Farmland_JsonToFeatureClass import exec_batch_convert as exec_batch_json_convert
from MP_Farmland_ShapefileToFeatureClass import exec_batch_convert as exec_batch_shp_convert
# Definitie van Python toolbox
class Toolbox:
def __init__(self):
self.label = "Landbouwperceel polygon conversie sample toolbox"
self.alias = ""
self.tools = [AgrilandJsonConvTool, AgrilandShpConvTool]
# Definitie van elke geoprocessing tool
class AgrilandJsonConvTool:
# ~ samenvatting ~
class AgrilandShpConvTool:
# ~ samenvatting ~
2) Definitie van tools
Elke geoprocessing tool moet minimaal __init__, getParameterInfo en execute definiëren om als geoprocessing tool in ArcGIS Pro te functioneren.
Het punt deze keer is dat alleen in execute wordt aangeroepen wat is gedefinieerd in MP_Farmland_JsonToFeatureClass.py, namelijk exec_batch_convert (om conflicten met functies met dezelfde naam in shapefile-formaat te voorkomen, wordt bij importeren een alias gebruikt als exec_batch_json_convert).
class AgrilandJsonConvTool:
def __init__(self):
self.label = "01_MultiProcessSample_Landbouwperceel polygon (GeoJSON formaat)_conversietool"
self.description = ""
def getParameterInfo(self):
param0 = arcpy.Parameter(
displayName="Invoermap (map na uitpakken)",
name="input_folder",
datatype="DEFolder",
parameterType="Required",
direction="Input")
# ~ samenvatting ~
params = [param0, param1, param2]
return params
def execute(self, parameters, messages):
infolder = parameters[0].valueAsText
outfolder = parameters[1].valueAsText
cpu_cnt = int(parameters[2].valueAsText)
exec_batch_json_convert(infolder, outfolder, cpu_cnt)
return
3) Uitvoerscherm
Dit is het uitvoerscherm van de daadwerkelijke "Farmland polygon conversion tools".
'Omake' (Conversie van GeoJSON-bestand)
Om GeoJSON-bestanden te converteren in ArcGIS Pro wordt meestal geconverteerd met "JSON → Features (JSON To Features)", maar vanwege beperkingen wordt deze keer gereageerd met een eigen klasse (class FarmlandGeojsonToFeaturesEx).'JSON → Features (JSON To Features)'