by 李远祥
上一章节已经介绍了如何去搭建AddIn的界面,接下来要实现具体的功能,则到了具体的编程环节。由于使用的是python语言进行编程,则开发者需要掌握一些基本的python编程技能。python部分的知识不是本章节的重点,开发人员可以通过百度及其他方式去获取相关的python知识。ArcGIS在其帮助文档中也为我们提供了一些python的推荐教程,以下灰色文字部分是ArcGIS官方帮助中的一些文字截取。
Python 是一种不受局限、跨平台的开源编程语言,它功能强大且简单易学。因而得到了广泛应用和支持。要了解有关 Python 的详细信息,请访问 python.org。
ArcGIS 9.0 社区中引入了 Python。此后,Python 被视为可供地理处理用户选择的脚本语言并得以不断发展。每个版本都进一步增强了 Python 体验,从而为您提供更多的功能以及更丰富、更友好的 Python 体验。
ESRI 已将 Python 完全纳入 ArcGIS 中,并将其视为可满足我们用户社区需求的语言。下面仅介绍 Python 的部分优势:
Python 已延伸到 ArcGIS 中,成为了一种用于进行数据分析、数据转换、数据管理和地图自动化的语言,因而有助于提高工作效率。
以下包含的信息并不是 Python 的语言参考。而是根据一些用于说明如何编写地理处理脚本的示例和概念来介绍某些 Python 语法和行为。
强烈建议您结合相应的 Python 参考书来进一步了解此处介绍的信息。对于 Python 初学者,建议使用以下两本书籍:《学习 Python》(Learning Python)(作者:Mark Lutz 和 David Ascher,出版社:O’Reilly & Associates)和《Python 核心编程》(Core Python Programming)(作者:Wesley J. Chun,出版社:Prentice Hall),这两本书都对该语言进行了详尽介绍,并且内容上不重叠。还有很多其他书籍也介绍了 Python 及其具体用途,其中有些新书会定期出版,因此可以探究可用的内容。Python 网站上提供了 Python 的完整文档,但内容很简明,主要是面向开发人员的。还有一个大型的 Python 在线社区,其中包含很多在线资源,您可通过 Python 主页访问这些资源。
如果您是 Python 的初学者,我们推荐您学习此处列出的一些外部教程。
此处所列的外部教程主要面向以前学习过其他编程语言(Perl、Visual Basic、C)的人员。
言归正传,在工程中如何进行编程。首先可以打开工程文件夹中的Instrall目录,用文本编辑器或者其他编辑工具打开.py文件。该py文件已经包含了一些初始化代码了,那我们则需要在事件或者界面初始化中加入适当的脚本即可。下图为设计器自动生成的代码
import arcpy import pythonaddins class ButtonClass5(object): """Implementation for mytools_addin.button (Button)""" def __init__(self): self.enabled = True self.checked = False def onClick(self): pass class ButtonClass6(object): """Implementation for mytools_addin.button_1 (Button)""" def __init__(self): self.enabled = True self.checked = False def onClick(self): pass class ComboBoxClass1(object): """Implementation for mytools_addin.combobox (ComboBox)""" def __init__(self): self.items = ["item1", "item2"] self.editable = True self.enabled = True self.dropdownWidth = 'WWWWWW' self.width = 'WWWWWW' def onSelChange(self, selection): pass def onEditChange(self, text): pass def onFocus(self, focused): pass def onEnter(self): pass def refresh(self): pass class ComboBoxClass2(object): """Implementation for mytools_addin.combobox_1 (ComboBox)""" def __init__(self): self.items = ["item1", "item2"] self.editable = True self.enabled = True self.dropdownWidth = 'WWWWWW' self.width = 'WWWWWW' def onSelChange(self, selection): pass def onEditChange(self, text): pass def onFocus(self, focused): pass def onEnter(self): pass def refresh(self): pass
可以看到该代码中已经为我们预留了两个combox的事件接口以及两个操作按钮的事件接口。
那么使用什么样的代码编辑器呢,笔者认为可以根据各自的习惯去确定编辑器,个人比较喜欢比较轻量级的NotePad++,可以通过安装python插件来进行python代码的编写。当然,使用windows自带的文本编辑器也是可以编写,但效果不是十分理想。如果希望有代码提示和可调试,可以安装微软的VS2015,安装时选择python编辑器,可以实现Python和ArcPy的代码提示,并且可以对程序进行调试,不过VS2015就比较庞大了。初学者或者简单的应用,不妨可以考虑NotePad++这个轻量级的编辑工具。
首选看到这个py文件中到了两个包,一个是ArcPy,另一个是pythonaddins,这两个都是ArcGIS自己维护的包。其中ArcPy允许我们对ArcGIS的功能进行编写,如ArcMap中所有的工具都能够在
ArcPy中调用,同时ArcPy也会对原始的Python进行了一些简单的封装,将一些特殊的对象封装进去,便于开发。例如双击ArcMap的【裁剪】工具,调出该工具,可以看到其中有【工具帮助】的按钮,如下图所示
点击该按钮,可以进入到该工具的帮助中。帮助里面除了说该工具的使用方法和原理,每个参数的说明之外,还具有ArcPy的调用。如下图
例如该帮助中说明了如何通过arcpy去调用裁剪工具,每一个参数是如何设置的。可以看到,arcpy的调用非常的简洁,而且参数的形式是多样化的,可以是要素类和图层的名称,也可以是直接的获取的变量。关于arcpy的其他介绍,可以参考ArcMap的中文帮助,里面有非常细致的说明,可以帮助我们更加快捷的去设计插件。
用NotePad++打开Install 目录下的.py文件,开始进行编辑。首选需要在说明字符集的问题,我们用到了中文字符,所以,必须在文件最顶出加入gbk字符集。然后需要定义一些变量,例如用作记录存储路径,裁剪数据,裁剪的图层等,如下代码
#coding=gbk import arcpy import pythonaddins class AB: workPath="" #记录保存路径 listLayer=[] #需要裁剪的图层列表 ClipFeat="" #用作裁剪的图层
这里定义了一个类,用作记录一些基本的信息,这些变量会在后面分别给予赋值。
接下来我们来看下来项怎么设置。插件的设想是在mxd里面获取一个图层作为裁剪用的范围图层,当然,这个图层是必须要先做好并加载到mxd中,combox的作用就是指定这个图层。但至于是哪个图层,那就需要先获取到地图文档中所有的图层,然后提供下拉列表。在列表中选中图层后,则将这个图层赋值给AB类的ClipFeat变量。这也是常规的编程做法了。看下面代码是怎么实现的。Combox已经定义后一些列的事件了,我们分别在事件里面加入python代码。
class ComboBoxClass1(object): #选择用于裁剪的图层 """Implementation for mytools_addin.combobox (ComboBox)""" def __init__(self): mxd = arcpy.mapping.MapDocument('current') df = arcpy.mapping.ListDataFrames(mxd, "图层")[0] for lyr in arcpy.mapping.ListLayers(mxd,"",df): AB.listLayer.append(lyr.name) self.items = AB.listLayer self.editable = True self.enabled = True self.dropdownWidth = 'WWWWWW' self.width = 'WWWWWW' def onSelChange(self, selection): AB.ClipFeat = selection print AB.ClipFeat pass def onEditChange(self, text): pass def onFocus(self, focused): pass def onEnter(self): pass def refresh(self): pass
上述代码可以看到,在combox的初始化中,利用arcpy.mapping的方式获取mxd文档中所有的图层,并将这些图层加入到AB类的listLayer数组中,用数组来记录这些图层,然后将这个数组赋值给combox的items,这样就能在combox中看到这个mxd的所有图层了。
onSelChange 事件是用来响应选择操作的,在这个combox中,需要从mxd里面选择一个图层来用作指定裁剪范围,可以看到,直接将选中的值赋值给 AB.ClipFeat。其他事件就不需要再给代码了。从这个例子可以看到,python就像积木一样,将arcgis的一些功能快速的堆积在一起,这就是我们选择使用python做开发的原因。
第二个combox是用于现实裁剪后的数据保存的路径。由于设计器中没有给出toolbox上的label或text控件,所以,只能使用combox来现实我们选择的保存路径。这个combox就什么都不用做,保留原来的代码就行了。我们会在选择后路径后会给予它路径值作为现实,其原始代码如下
class ComboBoxClass2(object): #用于现实路径 #用于显示路径 """Implementation for mytools_addin.combobox_1 (ComboBox)""" def __init__(self): self.editable = True self.enabled = True self.dropdownWidth = 'WWWWWW' self.width = 'WWWWWW' def onSelChange(self, selection): pass def onEditChange(self, text): pass def onFocus(self, focused): pass def onEnter(self): pass def refresh(self): pass
接下来就是在选择路径的按钮下弹出选择文件夹或者gdb的路径了(fileGDB也是个文件夹)。如果是选择到文件级别,那保存的数据就是shapefile,如果选择的文件夹刚好是fileGDB,arcpy则会自动将数据保存到gdb中去,这一点是非常人性化的,不需要些代码去识别和控制到底是文件夹还是gdb,更不用去控制导出的数据格式。看以下代码
class ButtonClass5(object): #选择存储的路径 """Implementation for mytools_addin.button (Button)""" def __init__(self): self.enabled = True self.checked = False def onClick(self): AB.workPath=pythonaddins.OpenDialog("选择存储路径") ComboBoxClass2.items=[AB.workPath,''] pass
在onclick事件中,使用 pythonaddins.OpenDialog 来打开windows路径选择器,这些界面操作都由pythonaddins 包实现了,我们只需要简单调用就行,选中路径后将路径赋值给A B.workPath ,先记录下来。然后不忘初衷的是,在 ComboBoxClass2.items 中显示指定的路径。
前面的这些代码都没能实现裁剪,都是一些交互性的脚本代码,那接下来这个裁剪功能才是核心的。在点击之行按钮后,我们需要调用裁剪功能去进行批量裁剪。那么,涉及到裁剪的各个变量,我们都已经在上述的交互中完成。来看看批量裁剪的功能,就是利用ClipFeat对listLayer每一个元素进行裁剪操作,并将结果保存在workpath中,命名的方式与图层名保持一致。
class ButtonClass6(object): #执行最终的操作 """Implementation for mytools_addin.button_1 (Button)""" def __init__(self): self.enabled = True self.checked = False def onClick(self): mxd = arcpy.mapping.MapDocument('current') df = arcpy.mapping.ListDataFrames(mxd, "图层")[0] for lyr in arcpy.mapping.ListLayers(mxd,"",df): outStr=u'正在裁剪'+lyr.name+u'...' resultFeats = AB.workPath+"/"+lyr.name arcpy.analysis.Clip(lyr.dataSource, AB.ClipFeat,resultFeats) #核心代码 print "裁剪结束" pass
可以看到,非常核心的裁剪代码就是arcpy.analysis.Clip 功能,这个功能就是我们上面说的toolbox工具箱里面的工具,早已经在arcgis里面实现,只需要一个语句进行调用即可。关键的部分我们使用了for循环来实现遍历,做成了批量裁剪。与toolbox工具里面的批量处理不一样的是,这个批量裁剪的工具裁剪出来的名称可以有我们自己来处理,这就避免了所有的结果带_clip的后缀。
下面来看这个工具的所有代码,非常简介的几行代码实现了一个简单而实用的工具。
#coding=gbk import arcpy import pythonaddins class AB: workPath="" #记录保存路径 listLayer=[] #需要裁剪的图层列表 ClipFeat="" #用作裁剪的图层 class ButtonClass5(object): #选择存储的路径 """Implementation for mytools_addin.button (Button)""" def __init__(self): self.enabled = True self.checked = False def onClick(self): AB.workPath=pythonaddins.OpenDialog("选择存储路径") ComboBoxClass2.items=[AB.workPath,''] pass class ButtonClass6(object): #执行最终的操作 """Implementation for mytools_addin.button_1 (Button)""" def __init__(self): self.enabled = True self.checked = False def onClick(self): mxd = arcpy.mapping.MapDocument('current') df = arcpy.mapping.ListDataFrames(mxd, "图层")[0] for lyr in arcpy.mapping.ListLayers(mxd,"",df): outStr=u'正在裁剪'+lyr.name+u'...' resultFeats = AB.workPath+"/"+lyr.name arcpy.analysis.Clip(lyr.dataSource, AB.ClipFeat,resultFeats) #核心代码 print "裁剪结束" pass class ComboBoxClass1(object): #选择用于裁剪的图层 """Implementation for mytools_addin.combobox (ComboBox)""" def __init__(self): mxd = arcpy.mapping.MapDocument('current') df = arcpy.mapping.ListDataFrames(mxd, "图层")[0] for lyr in arcpy.mapping.ListLayers(mxd,"",df): AB.listLayer.append(lyr.name) self.items = AB.listLayer self.editable = True self.enabled = True self.dropdownWidth = 'WWWWWW' self.width = 'WWWWWW' def onSelChange(self, selection): AB.ClipFeat = selection print AB.ClipFeat pass def onEditChange(self, text): pass def onFocus(self, focused): pass def onEnter(self): pass def refresh(self): pass class ComboBoxClass2(object): #用于现实路径 #用于显示路径 """Implementation for mytools_addin.combobox_1 (ComboBox)""" def __init__(self): self.editable = True self.enabled = True self.dropdownWidth = 'WWWWWW' self.width = 'WWWWWW' def onSelChange(self, selection): pass def onEditChange(self, text): pass def onFocus(self, focused): pass def onEnter(self): pass def refresh(self): pass
代码实现完后,确保没有问题,记得重新执行一下工程目录下的makeaddin.py 脚本,生成一个新的esriaddin文件。在安装这个新的插件之前,需要先在加载项管理器中删除原来的插件,再重新安装这个新的插件。直到这一步为止,这个插件基本就实现完成了。如果想更进一步,那就在执行完之后将mxd里面的所有图层的数据源设置为裁剪后的数据源,这样就实现所见即得的结果了。
插件源码和测试数据下载地址链接:http://pan.baidu.com/s/1i58tewX 密码:3cv3