在ArcGIS中建立Python工具(三)
從 ArcGIS 10.1 版本開始,我們可以建立 python工具箱 來自定義指令碼工具,這種工具箱相比較上一篇提到的標準工具箱。有著獨特的優勢,具體二者的區別總結過,看這一篇。
認識 Python工具箱
Python 工具箱 (.pyt) 是一個簡單的文字檔案,可以在任何文字編輯器中或者任何 Python IDE 中建立、檢視和編輯。要確保 ArcGIS 正確識別 Python 工具箱,工具箱類的名稱必須是 Toolbox
。在 Toolbox
類的 __init__
方法中定義工具箱的屬性,這些屬性包括 alias、label 和 description,我們可以按照
如下程式碼中建立了包含一個工具(名為 Tool)的 Python 工具箱:
import arcpy
class Toolbox(object):
def __init__(self):
"""Define the toolbox (the name of the toolbox is the name of the
.pyt file)."""
self.label = "Toolbox"
self.alias = ""
# List of tool classes associated with this toolbox
self.tools = [Tool]
class Tool(object):
def __init__(self):
"""Define the tool (tool name is the name of the class)."""
self.label = "Tool"
self.description = ""
self.canRunInBackground = False
def getParameterInfo(self):
"""Define parameter definitions"""
params = None
return params
def isLicensed(self):
"""Set whether tool is licensed to execute."""
return True
def updateParameters(self, parameters):
"""Modify the values and properties of parameters before internal
validation is performed. This method is called whenever a parameter
has been changed."""
return
def updateMessages(self, parameters):
"""Modify the messages created by internal validation for each tool
parameter. This method is called after internal validation."""
return
def execute(self, parameters, messages):
"""The source code of the tool."""
return
動手做做
下面我就依據這個模板,寫一個簡單的指令碼工具箱。需求是批量裁剪,我希望我只提供一個資料夾或者資料庫等這樣的工作空間和一個裁剪區域面,就可以批量完成工作空間內全部資料的裁剪工作,並且無視柵格還是向量,一併裁剪。
Let the scripting begin ……
1 建立工具箱
工具箱的name就是 .pyt 檔案的名字,通常我們把工具新增到 ArcToolbox視窗中時會顯示工具箱的 label。在 Toolbox
類的 __init__
方法中定義屬性,例如: alias
、label
和 description
。
工具作為類被新增至 .pyt 中,工具箱的 tools 屬性必須設定為包含定義的所有工具類的列表。比如,需要做ATool,ATool,CTool三個工具,不是寫三個指令碼,而是建立三個類,然後將類名放入列表, self.tools = [ATool,ATool,CTool]
。
這裡,我僅定義一個工具類 ClipWorkspace,來說明構建過程即可 :
'''
Source Name: ClipWorkspace.pyt
Author: Kikita
Description: Python tool to clip spatial data in the same workspace by batch.
'''
import arcpy
# The class name must be "Toolbox" ...
class Toolbox(object):
def __init__(self):
self.label = "Clip Workspace Toolbox"
self.alias = ""
# List of tool classes associated with this toolbox
self.tools = [ClipWorkspace]
class ClipWorkspace(object):
……
在 ArcGIS Desktop 中已經可以看到這個工具箱的雛形:
2 定義工具
下面就是完善工具內部的程式碼。我就以 ClipVectorWorkspace 為例。
每個工具類應至少包括 __init__
和 execute
方法。此外,還可以選擇使用 getParameterInfo
、isLicensed
、updateParameters
和 updateMessages
方法向工具的行為中新增其他控制。
工具類中的 __init__
方法是標準 Python 類初始化方法。對於 Python 工具箱中的工具,__init__
方法用於設定該工具的屬性,例如工具的標註、描述、是否允許在後臺執行等。
下面的例子就建立了ClipVectorWorkspace這個工具:
class ClipWorkspace(object):
def __init__(self):
self.label = "Clip Workspace"
self.description = "clip spatial data in the same workspace by batch."
self.canRunInBackground = True
有了工具的建構函式,我們繼續看如何給工具定義引數。在 Python 工具箱 (.pyt) 中,可在工具類的 getParameterInfo
方法中建立 Parameter
物件,並設定物件的屬性來定義工具引數。Parameter
的屬性中datatype
包含的型別可以在幫助文件中查詢,點這裡。
此示例中的引數就是輸入工作空間(inWorkspace)、裁剪區域面(ClipArea)、輸出工作空間(outWorkspace)。
def getParameterInfo(self):
# Parameter Definitions
# First parameter - Input Workspace
param0 = arcpy.Parameter(
displayName="Input Workspace",
name="inWorkspace",
datatype="DEWorkspace",
parameterType="Required",
direction="Input")
# Second parameter - Clip Area
param1 = arcpy.Parameter(
displayName="Clip Area",
name="CLipArea",
datatype="DEFeatureClass",
parameterType="Required",
direction="Input")
# Third parameter - Output Workspace
param2 = arcpy.Parameter(
displayName="Output Workspace",
name="outWorkspace",
datatype="DEWorkspace",
parameterType="Required",
direction="Input")
params = [param0,param1,param2]
return params
PS : 在程式碼中,如果仔細看,或許你會疑惑,為何輸出工作空間的方向是 input ,而不是 output? 因為工具最終輸出的為 Feature Class 或 Raster,輸出工作空間也是作為輸入引數傳入工具使用的。如果不關心,也可以不在意這些細節…… 繼續向下瞭解工具的構建過程。
下面就是工具的具體執行部分了,當然裡面還加了些輔助瞭解工具執行狀態的訊息:
def execute(self, parameters, messages):
"""The source code of the tool."""
# Get tool parameters
inWorkspace = parameters[0].valueAsText
arcpy.AddMessage("###Input Workspace is {0}".format(inWorkspace))
ClipArea = parameters[1].valueAsText
arcpy.AddMessage("###Clip Area is {0}".format(ClipArea))
outWorkspace = parameters[2].valueAsText
arcpy.AddMessage("###Out Workspace is {0}".format(outWorkspace))
# Clip Feature by Batch
arcpy.env.workspace = inWorkspace
# Clip Vector
FeatureClasses = arcpy.ListFeatureClasses()
arcpy.AddMessage("Input Workspace contains {0}".format(FeatureClasses))
for fc in FeatureClasses:
arcpy.AddMessage(">> Clipping {0}".format(fc))
arcpy.Clip_analysis(fc,ClipArea, os.path.join(outWorkspace,fc))
arcpy.AddMessage("{0} has been clipped.".format(os.path.join(outWorkspace,fc)))
# Clip Raster
Rasters = arcpy.ListRasters()
arcpy.AddMessage("Input Workspace contains {0}".format(Rasters))
for Raster in Rasters:
arcpy.AddMessage(">> Clipping {0}".format(Raster))
arcpy.Clip_management(in_raster = Raster,
rectangle = "",
out_raster = os.path.join(outWorkspace,Raster),
in_template_dataset = ClipArea,
nodata_value = "",
clipping_geometry = "ClippingGeometry",
maintain_clipping_extent = "NO_MAINTAIN_EXTENT")
arcpy.AddMessage("{0} has been clipped.".format(os.path.join(outWorkspace,Raster)))
return
到這裡,工具的核心部分已經完成,執行下試試。
OK,應該是預期的結果:
3 完善
我們發現不像標準工具箱中的指令碼工具和指令碼檔案本身是雜湊儲存的,python工具箱以及其中的所有工具的程式碼都在這一個pyt檔案中,維護起來便利了不少。如果想在工具箱中繼續新增工具,只要繼續增加一個工具類即可。
經過前兩步的過程,工具已經可以拿去使用。如果為了工具更友好,還可以繼續調整下程式碼,以便遇到異常的時候,讓使用者瞭解更詳細的原因,這裡就再往下進行了。工具分享給別人,最後只差要豐富下工具文件了,同樣在 Python 工具的 Item Description 中編輯。