ArcGIS の Python でのマルチプロセス処理 - 実装例

Document created by shinji_katayaesrij-esridist Employee on Oct 24, 2019Last modified by hiromu_nakamuraesrij-esridist on Oct 25, 2019
Version 5Show Document
  • View in full screen mode

はじめに

前回の ArcGIS の Python でのマルチプロセス処理 の記事で、ArcGIS プラットフォームの拡張手段として定着している ArcPy と Python でのマルチプロセス処理をとりあげ、ベスト プラクティスや、コーディング パターンをサンプルを交えながら紹介させて頂きました。
詳細な解説は、前回の記事をご参照いただきたいですが、コーティング パターンのポイントとしてあげたのは以下の3つです。
  1) ワーカー関数:batch_convert
  2) ワーカー関数のラッパー:multi_run_batch_convert
  3) マルチプロセスの処理:exec_batch_convert
今回の記事では農林水産省が公開している「農地の区画情報(筆ポリゴン)」を例に、前回の記事で取り上げたコーディング パターンに従って、処理を書いてみたコードを公開します

 

サンプルコード

今回のサンプルは、筆ポリゴンのZIP 解凍後の1つの「都道府県フォルダ」の筆ポリゴンを処理するものです。
1つの「都道府県フォルダ」の下に「各市区町村フォルダ」があり、その下に1つの「シェープファイル」が入っています。
それを「都道府県_filegdbフォルダ」の下に「各市区町村ファイル ジオデータベース」を作成後、「シェープファイル」から「フィーチャクラス」への変換と「市区町村コードと市区町村名を属性に格納する処理」をマルチプロセスで実行します。
また、最終的に各市区町村に分かれている「フィーチャクラス」のマージをシングルプロセスで実行するコードや、マージ後に不要となった「各市区町村ファイル ジオデータベース」の削除も書いています。
#coding:utf-8
#---------------------------------------------------------------------
# Name:       Multiprocess_Forge_ShapefileToFeatureClass_uft8.py
# Purpose:    農地の筆界をマルチプロセスでフィーチャクラスに変換
#             自治体コードや自治体名をフィールドに入れる
#             フォルダ内のフィーチャクラスをマージしたフィーチャクラスを作成
# Author:     Kataya @ ESRI Japan
# Created:    xx/10/2019
# Copyright:   (c) ESRI Japan Corporation
# ArcGIS Version:   10.x
# Python Version:   2.7
#---------------------------------------------------------------------
import arcpy,os
import sys
import multiprocessing
import datetime
import traceback

reload(sys)
sys.setdefaultencoding('cp932')

def split_citycode_cityname(wsname):
    '''
    農地筆の自治体コードと自治体名からそれぞれ抽出して返す
    フォルダ名の例) 02201青森市2019
              02202弘前市2019
    '''

    l = len(wsname)
    citycode = "'{0}'".format(wsname[:5])    #自治体コードだけを抽出
    cityname = '"' + "{0}".format(wsname[5:l-4]) + '"' #自治体名だけを抽出
    return citycode, cityname

def multi_run_batch_convert(args):
    '''
    batch_convertのwrapper:
      複数の引数を実行処理に渡すために必要なラッパー
    '''

    return batch_convert(*args)

def batch_convert(inws, outws):
    '''
    1プロセスで実行する処理:
      1) FGDBへの書込みは仕様で複数プロセスで書込みできないため
           1市区町村フォルダ下のシェープファイルを
           1市区町村のFGDB下のフィーチャクラスに変換
      2) すべてのフィーチャクラスを統合したとき識別しやすいように
           フォルダ名から市区町村コードと、自治体名を作成しフィールドに値を格納
    '''

    print(u"Convert: {0} ⇒ {1}\n".format(inws,outws))
    if not arcpy.Exists(outws):
        outfolder = u"{0}".format(os.path.dirname(outws))
        foldername= u"{0}".format(os.path.basename(outws))
        arcpy.CreateFileGDB_management(outfolder, foldername, "CURRENT")
    arcpy.env.workspace = inws
    fieldname1 = "CITYCODE"
    fieldname2 = "CITYNAME"
    fcs = arcpy.ListFeatureClasses()
    for fc in fcs:
        infc=os.path.splitext(fc)[0]
        newfc = u"c_{0}".format(infc) #シェープファイル名が数値ではじまり、FGDBへそのまま変換できないので接頭にc_を入れる
        wsname = os.path.basename(inws)
        citycode, cityname = split_citycode_cityname(wsname) #フォルダ名から自治体コードと自治体名を抽出
        ## 座標系はそのまま変換
        if arcpy.Exists(os.path.join(outws,newfc)):
            outfc=os.path.join(outws,newfc)
            arcpy.Append_management(fc,outfc)
        else:
            arcpy.FeatureClassToFeatureClass_conversion(fc,outws,newfc)
            outfc=os.path.join(outws,newfc)
            arcpy.AddField_management(outfc,fieldname1,"TEXT",field_length=5)
            arcpy.AddField_management(outfc,fieldname2,"TEXT",field_length=30)
        # citycode, cityname をフィールド値にいれる
        calfc = os.path.join(outws,newfc)
        arcpy.CalculateField_management(calfc, fieldname1, citycode, "PYTHON_9.3", "#")
        arcpy.CalculateField_management(calfc, fieldname2, cityname, "PYTHON_9.3", "#")
   
    del fcs
    return u"  変換済:{0}".format(outws)

def exec_batch_convert(infolder,outfolder):
    '''
    マルチプロセスでの処理:
    '''

    try:
        start=datetime.datetime.now()
        print(u"-- Strat: Multiprocess_Forge_ShapefileToFeatureClass --:{0}".format(start))
        cpu_cnt=multiprocessing.cpu_count()
        arcpy.env.workspace = infolder
        inwss = arcpy.ListWorkspaces("*","Folder")
        # 各プロセスに渡すパラメータをリスト化
        params=[]
        for inws in inwss:
            param1=inws # 市区町村フォルダ(シェープファイルが入っている)
            gdbname=u"{0}.gdb".format(os.path.basename(inws))
            param2=os.path.join(outfolder,gdbname) # 市区町村ファイルジオデータベース
            params.append((param1,param2))
        if len(inwss) < cpu_cnt: # 処理フォルダ数CPUコアより少ない場合無駄なプロセスを起動不要
            cpu_cnt=len(inwss)
        pool = multiprocessing.Pool(cpu_cnt) # cpu数分プロセス作成
        results=pool.map(multi_run_batch_convert,params) # 割り当てプロセスで順次実行される
        pool.close()
        pool.join()
        # 各プロセスでの処理結果を出力
        for r in results:
            print(u"{0}".format(r))
       
        # 各プロセスからのマージ版を作成
        arcpy.env.workspace = outfolder
        outwss = arcpy.ListWorkspaces("*","FileGDB")
        foldername="{0}.gdb".format(os.path.basename(outfolder))
        forgefc = "forge"
        print(u"  Mearge to FeatureClass:{1} in FGDB:{0} ".format(foldername, forgefc))
        arcpy.CreateFileGDB_management(outfolder,foldername,"CURRENT")
        forgews = os.path.join(outfolder, foldername)
        for outws in outwss:
            arcpy.env.workspace = outws
            fc = arcpy.ListFeatureClasses()[0] #農地筆は1ファイルしかないので固定
            print(u"    merge: {0} ⇒ {1}".format(fc,forgefc))
            if arcpy.Exists(os.path.join(forgews, forgefc)):
                outfc=os.path.join(forgews,forgefc)
                arcpy.Append_management(fc,outfc)
            else:
                arcpy.FeatureClassToFeatureClass_conversion(fc,forgews,forgefc)
          
        # マージが終わったので後片付け 各市区町村のFGDBを削除 - 必要に応じてコメントアウトを外す
        #for outws in outwss:
          #     arcpy.Delete_management(outws)
          
        fin=datetime.datetime.now()
        print(u"-- Finish: Multiprocess_Forge_ShapefileToFeatureClass --:{0}".format(fin))
        print(u"     Elapsed time:{0}".format(fin-start))

    except:
        print(u"Exception:{0}".format(sys.exc_info()[2]))

def setup_batch_convert():
    '''
    コマンドプロンプトからの実行パラメータを設定の場合:
       市区町村別のシェープファイルが入った都道府県フォルダ :infolder
      例)
      |-02青森県2019
          |-02201青森市2019
          |   02201青森市2019_5.shp
          |-02202弘前市2019
          |   02202弘前市2019_5.shp
          |-02203八戸市2019
          |-02204黒石市2019          
            ・・・・・
      市区町村別のファイル ジオデータベースの作成先フォルダ :outfolder
      例)
      |-02青森県2019_filegdb
          |-02201青森市2019.gdb
          |   c_02201青森市2019_5
          |-02202弘前市2019.gdb
          |   c_02202弘前市2019_5
          |-02203八戸市2019
          |-02204黒石市2019          
            ・・・・・
          |-02青森県2019_filegdb.gdb # 市区町村別のフィーチャクラスをマージしたフィーチャクラスを格納するファイル ジオデータベース
          |   forge     
    '''
    infolder=ur"F:\Temp\農地の筆ポリゴン\02青森県2019" #市区町村別のシェープファイルが入った都道府県フォルダ(平面直角座標系ごと)
    outfolder=ur"F:\Temp\農地の筆ポリゴン\02青森県2019_filegdb" #市区町村別のファイル ジオデータベースの作成先フォルダ
    exec_batch_convert(infolder,outfolder)


if __name__ == '__main__':
    setup_batch_convert()

 

64-bit 環境でのPython スクリプトの実行例

前回の記事と同様、Pythonスクリプトの実行例としてArcGIS Desktop、ArcGIS Proでの例を載せておきます。

 

実行例)ArcGIS Desktop 10.7.1

>C:\Python27\ArcGISx6410.7\python.exe Multiprocess_Forge_ShapefileToFeatureClass_utf8.py

 

実行例)ArcGIS Pro 2.4

 

>"c:\Program Files\ArcGIS\Pro\bin\Python\scripts\propy.bat" Multiprocess_Forge_ShapefileToFeatureClass_utf8.py

 

参考

Attachments

    Outcomes