基於ArcGIS的python編程 11、利用多進程優化根據Excel表格批量生成點數據,批量裁剪
前面《基於ArcGIS的python編程 10、根據Excel表格批量生成點數據,批量裁剪》的程序順利跑完大概要1小時20分,確實很慢,想起之前看到有利用python的多線程多進程去提高效率的案例,這裡嘗試利用多線程多進程對程序進行優化,提高程序的運行效率。自己對於多線程多進程的理解與應用都是一知半解,這裡就不再班門弄斧,分享一篇文章《進程與線程的一個簡單解釋》,我覺得這蠻好解釋了多進程多線程的含義。有興趣的可以去好好理解一下。
批量生成點數據工具的優化(多進程的運用):
思路還是與上一篇一樣,只不過把上一次的代碼進行功能拆分,拆分為一個個功能函數,然後通過多進程多線程去充分利用電腦的CPU,多核同時運行同一個功能函數,這樣就可以提高程序的效率。因為對多線程多進程不太熟悉,只能參考別人的案例,去模仿;雖然也應用了多線程多進程,但是程序還有很大的改善空間,剛剛接觸這個新的知識,只能接下來繼續去完善。
完整代碼如下:
#coding=utf-8nimport arcpynimport xlrdnimport timenimport multiprocessingnn#獲得表格的名字ndef gettablename(in_table):n tablenamelist=[]n tablepaths=in_table.split(;)n for path in tablepaths:n name=path.split()[-1].split(.)[0]n tablenamelist.append(name)n return tablenamelistn#根據表名,保存路徑,坐標參考新建點要素ndef CreateFeaturclass(tablename,savepath,spatial):n if arcpy.Exists(savepath + + tablename + .shp)==False:n arcpy.CreateFeatureclass_management(savepath,tablename,POINT,,,,spatial)n else:n passn#根據表的路徑,保存的路徑,為剛剛創建的點要素添加欄位ndef AddField(tablepath,savepath):n featurename=tablepath.split()[-1].split(.)[0]n data=xlrd.open_workbook(tablepath)n sh=data.sheets()[0]n ziduanNamelist=sh.row_values(0)n for ziduan in ziduanNamelist:n arcpy.AddField_management(savepath++featurename+.shp,ziduan,TEXT)n#根據表名,點要素的路徑,表裡的內容寫入點要素中ndef InsertRow(savepath,tablepath):n featurename = tablepath.split()[-1].split(.)[0]n data = xlrd.open_workbook(tablepath)n sh = data.sheets()[0]n ziduanNamelist = sh.row_values(0)n rownum = sh.nrowsn Insercur = arcpy.InsertCursor(savepath + + featurename + .shp)n for rowid in range(1,rownum):n newpnt = arcpy.Point()n newpnt.X = float(str(sh.cell(rowid, 2).value))n newpnt.Y = float(str(sh.cell(rowid, 1).value))nn pointGeo = arcpy.PointGeometry(newpnt)n newrow = Insercur.newRow()n newrow.shape = pointGeon for c in range(0, len(ziduanNamelist)):n newrow.setValue(ziduanNamelist[c],sh.cell(rowid,c).value)n Insercur.insertRow(newrow)nn#調用函數nif __name__=="__main__":n in_table = arcpy.GetParameterAsText(0)n savepath = arcpy.GetParameterAsText(1)n spatial = arcpy.GetParameterAsText(2)nn tablenamelist=gettablename(in_table)n tablpathlist = in_table.split(;)nn pool=multiprocessing.Pool(processes=4)#進程processes的個數根據電腦的核數,我的電腦是4核n #利用多進程創建要素n for tablename in tablenamelist:n pool.apply_async(CreateFeaturclass,(tablename,savepath,spatial,))n pool.close()n pool.join()n time.sleep(1)n #利用多進程添加欄位,注意多次運用多進程需要重新創建進程池Pooln pool = multiprocessing.Pool(processes=4)n for tablepath in tablpathlist:n pool.apply_async(AddField,(tablepath,savepath,))n pool.close()n pool.join()n time.sleep(1)n #利用多進程插入數據n pool = multiprocessing.Pool(processes=4)n for tablepath in tablpathlist:n pool.apply_async(InsertRow,(savepath,tablepath,))n pool.close()n pool.join()n
前面的文章有些網友問我要源代碼,其實源代碼我都已經全部帖子文章中了,只需要複製這些代碼下來保存為 .py文件,然後再Arcmap中創建腳本工具即可運行,前面的文章已有創建腳本工具的過程,詳細過程請參考前面的文章《基於ArcGIS的python編程 6、利用Arcpy製作自定義腳本工具——點線拓撲》;這裡不再詳細闡述,只是簡單的說明一下注意情況;
參數的設置:三個,注意參數的數據類型,是否多值就好。如下圖1
工具的界面如圖2
工具的運行模式:選擇第一個,這樣的話易於調試,另外如果選擇第二個,運行腳本時會不停的打開Arcmap這個程序,有點奇怪;如圖3
運行成功頁面如圖4;效率有點難以置信,上面一篇文章運行需要1小20分鐘左右,相當漫長,這裡利用多進程以後,只需要2分鐘左右,效率提高了40多倍,節省了很多時間,看來多進程是個好東西,要好好運用。
批量裁剪的工具的優化:
這裡添加的功能主要是使裁剪要素也可以輸入多個,然後利用循環的嵌套,以實現多對對的裁剪功能。因為很多時候我們都遇到過分類或者提取某個要素的需求;比如說我有梅州市的基礎數據(國道,省道,縣道,高速公路,水系,POI點等),現在我需要梅州行政區域內每個縣區的基礎數據,那麼我們就需要利用梅州市的各個縣區的行政區域(面要素,多個)區裁剪梅州市的基礎數據(國道,省道,縣道,高速公路,水系,POI點等),這樣就可以獲得每個縣區的基礎數據了。以前不會程序,也做過類似重複的工作,只能一個面與一個基礎數據去裁剪,工作量挺大的。
完整代碼如下:
# coding=UTF-8nimport arcpynnin_feature= arcpy.GetParameterAsText(0)nin_featurepath = in_feature.split(;)nnclip_feature = arcpy.GetParameterAsText(1)nclip_feature_paths = clip_feature.split(;)nnout_file = arcpy.GetParameterAsText(2) #保存路徑nfor clipfeature in clip_feature_paths:n for in_layer in in_featurepath:n name2 = clipfeature.split()[-1].split(.)[0]n name1=in_layer.split()[-1].split(.)[0]n name=name1+"_"+name2 n out_layer=out_file++namen arcpy.Clip_analysis(in_layer, clipfeature, out_layer)n
如果多對對裁剪的數量比較大,也需要一定的時間去運行的,如果想提高效率,也可參考上面程序的案例,巧用多進程即可,這個工具就不再詳細闡述了,讀者可以自行嘗試一下。
總結:
有點奇怪,在python的獨立環境中,讀取Excel的時候,路徑中不能出現中文,否則會報錯;但是在Arcgis腳本工具運行時卻可以包含中文,不知道什麼原因。另外,之前以為python的xlrd模塊只能操作.xls版本的表格,但是經過這兩次實驗,原來xlrd模塊可以操作.xls或者.xlsx版本的表格,之前的認知不夠。
程序中多次利用進程池Pool調用函數,因為對多進程不太熟悉,所以只創建了一次進程池pool=multiprocessing.Pool(processes=4),導致了第二次利用多進程運行AddField()函數總是報錯,後面每使用一次進程池就創建一個,這樣就解決了錯誤。
使用多進程會使電腦的使用內存瞬間爆滿,此時電腦很容易卡死或者崩潰;使用time模塊的sleep()函數睡眠幾秒鐘(time.sleep(1),讓程序睡眠/暫停1秒鐘),可以有效防止電腦由於運行過快,使用內存過高而崩潰。追求高效率的同時,也要注意,要不然電腦很容易吃不消。
另外,自己想在插入屬性值的時候(newrow.setValue(ziduanNamelist[c],sh.cell(rowid,c).value))使用多進程,因為如果有好幾萬個點,如果多進程運行這個函數,同時插入屬性值,那麼這樣效率就大大提高了;這個思路沒錯,但是忽略了游標里設有表鎖,就是防止多進程同時操作一張表,官網的表述如下(斜體字部分為從Esri官網摘錄):
游標和鎖定
插入和更新游標遵循由 ArcGIS 應用程序設置的表鎖。鎖能夠防止多個進程同時更改同一個表。有兩種鎖的類型:共享和排它。
- 只要訪問表或數據集就會應用共享鎖。同一表中可以存在多個共享鎖,但存在共享鎖時,將不允許存在排它鎖。應用共享鎖的示例包括:在 ArcMap 中顯示要素類時以及在 ArcCatalog 中預覽表時。
- 對錶或要素類進行更改時,將應用排它鎖。在 ArcGIS 中應用排它鎖的示例包括:在 ArcMap 中編輯和保存要素類時;在 ArcCatalog 中更改表的方案時;或者在 Python IDE(例如 PythonWin)中在要素類上使用插入游標時。
如果數據集上存在排它鎖,則無法為表或要素類創建更新和插入游標。UpdateCursor 或 InsertCursor 函數會因數據集上存在排它鎖而失敗。如果這些函數成功地創建了游標,它們將在數據集上應用排它鎖,從而使兩個腳本無法在同一數據集上創建更新和插入游標。
在 Python 中,在游標釋放前保持鎖定狀態。否則,將會阻止所有其他應用程序或腳本訪問數據集,而這是毫無必要的。可通過以下其中一種方法來釋放游標:
- 在 with 語句中加入游標,這樣可以確保無論游標是否成功完成,都將釋放鎖
- 在游標上調用 reset()
- 完成游標
- 使用 Python 的 del 語句顯示刪除游標
ArcMap 中的編輯會話將在其會話期間對數據應用共享鎖。保存編輯內容時將應用排它鎖。已經存在排它鎖時,數據集是不可編輯的。
歡迎大家一起交流,一起學習,一起進步!
參考:
淺談使用ArcPy執行大數據量處理任務
進程與線程的一個簡單解釋
Esri官網遊標幫助文檔
推薦閱讀:
※基於ArcGIS的python編程 9.python操作Excel與屬性表批處理
※Awesome GIS(GIS Tech Stack技術棧)
※為什麼cityengine中文版教程這麼少?cityengine相比其他建模軟有什麼優勢?