目录
一、利用ARCGIS Python 插件助手创建所需插件框架
ARCGIS插件即加载项(addin)是一种方便对ARCGIS进行二次开发的自定义项,它可以插入到 ArcGIS Desktop 应用程序(即 ArcMap、ArcCatalog、ArcGlobe 和 ArcScene)中,以提供对ARCGIS功能的扩展。
ArcGIS 10.1 之后ESRI公司将 Python 引入到创作Desktop加载项的语言列表中,为用户提供了一种简单地扩展 Desktop功能的解决方案。博主将以开发图形面积提示工具(涵盖工具条、组合框、按钮、工具按钮4种常用插件开发形式)为例,分享如何利用Python编程创建自己的ARCGIS自定义插件工具。要进行Python插件的开发,首先需要下载并使用Python addin_assistant工具(本站内有大量相关资源,用户可自行搜索下载)来创建插件工具框架。
一、利用ARCGIS Python 插件助手创建所需插件框架
1、运行Python 插件助手程序
addin_assistant工具是免安装的,下载好后先创建一个空的项目文件夹(软件硬性要求),名称最好不要出现中文,避免后续出现按钮图标文件路径无法识别的错误。文件夹建好后直接点击下图所示应用程序直接运行Pyhton插件助手工具。
2、创建项目文件夹
根据程序的向导,选择文件位置为刚才创建的空项目文件夹,然后进行项目名称、项目描述、插件图标等属性设置。
3、添加工具条插件作为容器
点击 Add-In Contents,在这里我们可以选择我们的插件类型,本案例是要开发一个工具条,里面的工具可以快速得到所选图层内面要素的矢量面积或净面积(争对耕地而言,对于国土相关从业者来说,是个非常实用的功能)。所以我们先要创建一个工具条作为容器,来储存我们需要的工具按钮。鼠标右键TOOLBARS,新建一个工具条,并设置好工具描述等相关属性。
4、添加各功能按钮
根据想要实现的功能,依次鼠标右键单击新建的工具条,向其内添加我们需要的工具类型。
按照之前的构想,博主依次添加了Combo Box(组合框,用于存放地图中已有的面要素图层名,供用户选择);
botton1(按钮1,点击按钮得到所选图层内面要素的矢量面积之和);
Tool1(工具按钮1,点击按钮,可在地图界面绘制一条范围线,双击鼠标后自动闭合该范围线得到所选图层内面要素与所绘范围交集内的矢量面积之和,并生成一个新的图层,存放此部分矢量);
botton2(按钮2,点击按钮得到所选图层内面要素的净面积之和,必须要有KCXS字段);
Tool2(工具按钮2,点击按钮,可在地图界面绘制一条范围线,双击鼠标后自动闭合该范围线得到所选图层内面要素与所绘范围交集内的净面积之和,并生成一个新的图层,存放此部分矢量,必须要有KCXS字段)。
按钮和工具按钮的区别在于,按钮只能实现点击功能,而工具按钮在点击之后,可以进行鼠标或键盘的下一步操作,根据不同操作结果,我们可以编写代码,得到想要实现的功能(这点在后面的代码中可以看到明显的分别)。
工具添加完成后,依次设置好每个工具的属性 ,点击右下方的保存按钮,即可完成插件的配置工作。
二、项目文件介绍
利用插件助手配置好插件框架后,会在选择的项目文件夹中生成如下结构的文件:
1、Images文件夹
Images文件夹用于存储之前我们选择的工具条内各按钮的图标图片文件。
2、Install文件夹
Install文件夹则存放着我们这个项目的核心文件,即实现插件各功能的Python代码文件。
3、config 文件
config.xml文件(可扩展标记语言文件)存放着我们插件工具的配置信息,不可随意更改;
4、makeaddin文件
makeaddin.py文件顾名思义,即是打包生成我们最终成果的代码文件。
在插件的各功能代码完成后,无需对此文件代码进行编辑,直接利用Pycharm或Python自带的IDLE,运行此文件,就会打包生成后缀为.esriaddin的插件安装文件,点击安装文件即可快速安装我们自己的插件啦。
三、功能实现代码
在了解了如何配置自定义插件及项目文件的构成后,下面就可以进行本次开发工作的核心工作了,编写实现各个按钮功能的代码。利用Pycharm或Python自带的IDLE打开Install文件夹内的Python文件,开始编写代码,关键部分的代码均已注释,有疑问可以评论区留言,互相讨论学习。
源码如下:
# -*- coding: cp936 -*-
import arcpy
import pythonaddins
import json
from arcpy import env
class ComboBoxClass1(object):
"""初始化组合框"""
def __init__(self):
self.editable = True
self.enabled = True
self.dropdownWidth = 'WWWWWWWWWWWW'# 设置组合框内下拉框的宽度,自行调试
self.width = 'WWWWWWWWWWWW' # 设置组合框的宽度,自行调试
def onSelChange(self, selection):
pass
def onEditChange(self, text):
pass
def onFocus(self, focused):
if focused: # 当组合框成为焦点及鼠标指向它时,实时更新下拉框内内容列表
mxd = arcpy.mapping.MapDocument("CURRENT")
layers = arcpy.mapping.ListLayers(mxd)
self.items = []
for layer in layers: #获取当前地图中数据形状类型为面的图层名,加入组合框的下拉框,方便进行选择
desc = arcpy.Describe(layer)
shape_type = desc.shapeType
if shape_type == "Polygon":
self.items.append(layer.name)
def onEnter(self):
pass
def refresh(self):
pass
class ButtonClass1(object):
"""统计完整图斑几何面积"""
def __init__(self):
self.enabled = True
self.checked = False
def onClick(self):
try:
mj = 0
with arcpy.da.SearchCursor(combobox.value, ['SHAPE@AREA'])as cursor: # 获取矢量面积
for row in cursor:
mj = mj + row[0]
# 弹窗显示总面积
pythonaddins.MessageBox('总面积:%.2f 平方米\n总面积:%.2f 亩\n总面积:%.4f 公顷'
% (round(mj, 2), round(mj / 666.667, 2), round(mj / 10000, 4)), '提示')
except Exception as e:
pythonaddins.MessageBox(e.message, '错误')
class ButtonClass2(object):
"""统计完整耕地图斑净面积"""
def __init__(self):
self.enabled = True
self.checked = False
def onClick(self):
try:
desc = arcpy.Describe(combobox.value)
field_names = [f.name for f in desc.fields] # 获得所选图层的字段名列表
tbdlmj = 0
if 'KCXS' in field_names: # 判断有无KCXS字段,是进行净面积计算的前提
with arcpy.da.SearchCursor(combobox.value, ['SHAPE@','KCXS'])as cursor:
for row in cursor:
tbmj = round(row[0].getArea('PRESERVE_SHAPE'),2) # 获取椭球面积
tbdlmj = tbdlmj + round(tbmj*(1-row[-1]),2)
pythonaddins.MessageBox('总净面积:%.2f 平方米\n总净面积:%.2f 亩\n总净面积:%.4f 公顷'
% (round(tbdlmj, 2), round(tbdlmj / 666.667, 2), round(tbdlmj / 10000, 4)), '提示')
else:
pythonaddins.MessageBox("无'KCXS'字段", '错误')
except Exception as e:
pythonaddins.MessageBox(e.message, '错误')
class ToolClass1(object):
"""统计选取范围内图斑几何面积"""
def __init__(self):
self.enabled = True
# 用画线,并闭合首尾的方式获取范围线,因为Rectangle只能绘制四边形,达不到任意范围目的
self.shape = "Line" # Can set to "Line", "Circle" or "Rectangle" for interactive shape drawing and to activate the onLine/Polygon/Circle event sinks.
self.cursor = 3 #设置鼠标样式为十字
def onMouseDown(self, x, y, button, shift):
pass
def onMouseDownMap(self, x, y, button, shift):
pass
def onMouseUp(self, x, y, button, shift):
pass
def onMouseUpMap(self, x, y, button, shift):
pass
def onMouseMove(self, x, y, button, shift):
pass
def onMouseMoveMap(self, x, y, button, shift):
pass
def onDblClick(self):
pass
def onKeyDown(self, keycode, shift):
pass
def onKeyUp(self, keycode, shift):
pass
def deactivate(self):
pass
def onCircle(self, circle_geometry):
pass
def onLine(self, line_geometry):
# 进行线绘制完后的操作
line = line_geometry
env.overwriteOutput = True # 将覆盖输出文件设置为真,避免报重复文件的错
env.addOutputsToMap = False # 不将输出文件自动添加到地图,临时文件没必要
try:
json_info = line.JSON # 获取所绘线的Json数据,包含图形坐标等
coord = json.loads(json_info)['paths'][0] # 获取绘制的范围线的坐标
coord.append(coord[0]) # 首尾闭合构面
feature = arcpy.Polygon(arcpy.Array([arcpy.Point(*pnts) for pnts in coord]))
arcpy.CopyFeatures_management(feature, 'temp1')
arcpy.Clip_analysis(combobox.value,'temp1','temp2')
env.addOutputsToMap = True # 将成果文件自动添加到地图
arcpy.MultipartToSinglepart_management('temp2','选取范围内图斑')
arcpy.Delete_management('temp1') #删除临时文件
arcpy.Delete_management('temp2')
mj = 0
with arcpy.da.SearchCursor('选取范围内图斑', ['SHAPE@AREA'])as cursor: # 获取选取范围内图斑的总面积
for row in cursor:
mj = mj + row[0]
pythonaddins.MessageBox('总面积:%.2f 平方米\n总面积:%.2f 亩\n总面积:%.4f 公顷'
% (round(mj, 2), round(mj/666.667, 2), round(mj/10000, 4)), '提示')
except Exception as e:
pythonaddins.MessageBox(e.message, '错误')
self.cursor = 0
def onRectangle(self, rectangle_geometry):
pass
class ToolClass2(object):
"""统计选取范围内耕地图斑净面积"""
def __init__(self):
self.enabled = True
self.shape = "Line" # Can set to "Line", "Circle" or "Rectangle" for interactive shape drawing and to activate the onLine/Polygon/Circle event sinks.
self.cursor = 3
def onMouseDown(self, x, y, button, shift):
pass
def onMouseDownMap(self, x, y, button, shift):
pass
def onMouseUp(self, x, y, button, shift):
pass
def onMouseUpMap(self, x, y, button, shift):
pass
def onMouseMove(self, x, y, button, shift):
pass
def onMouseMoveMap(self, x, y, button, shift):
pass
def onDblClick(self):
pass
def onKeyDown(self, keycode, shift):
pass
def onKeyUp(self, keycode, shift):
pass
def deactivate(self):
pass
def onCircle(self, circle_geometry):
pass
def onLine(self, line_geometry):
line = line_geometry
env.overwriteOutput = True
env.addOutputsToMap = False
try:
desc = arcpy.Describe(combobox.value)
field_names = [f.name for f in desc.fields]
if 'KCXS' in field_names: # 判断有无KCXS字段,是进行净面积计算的前提
json_info = line.JSON
coord = json.loads(json_info)['paths'][0]
coord.append(coord[0])
feature = arcpy.Polygon(arcpy.Array([arcpy.Point(*pnts) for pnts in coord]))
arcpy.CopyFeatures_management(feature, 'temp1')
arcpy.Clip_analysis(combobox.value, 'temp1', 'temp2')
env.addOutputsToMap = True
arcpy.MultipartToSinglepart_management('temp2', '选取范围内图斑')
arcpy.Delete_management('temp1')
arcpy.Delete_management('temp2')
tbdlmj = 0
with arcpy.da.SearchCursor('选取范围内图斑', ['SHAPE@', 'KCXS'])as cursor:
for row in cursor:
tbmj = round(row[0].getArea('PRESERVE_SHAPE'), 2) # 获取椭球面积
tbdlmj = tbdlmj + round(tbmj * (1 - row[-1]), 2)
pythonaddins.MessageBox('总净面积:%.2f 平方米\n总净面积:%.2f 亩\n总净面积:%.4f 公顷'
% (round(tbdlmj, 2), round(tbdlmj / 666.667, 2), round(tbdlmj / 10000, 4)),
'提示')
else:
pythonaddins.MessageBox("无'KCXS'字段", '错误')
except Exception as e:
pythonaddins.MessageBox(e.message, '错误')
self.cursor = 0
def onRectangle(self, rectangle_geometry):
pass
四、在Arcmap中配置已安装插件
1、工具条插件的配置
安装完成自定义插件之后,打开Arcmap点击顶部自定义菜单栏,在展开的选项中选择工具条,然后勾选我们自己开发的面积提示工具条,随后可将工具条放入菜单栏的任意位置。
2、按钮插件的配置
上面说了工具条插件在Arcmap中如何配置,接下来说说单独的按钮工具在Arcmap中如何进行配置。同样是展开顶部的自定义菜单,接着我们要选择自定义模式菜单。
打开自定义模式设置界面,选择顶部的命令,然后选择Add-In Controls,最后选择一个工具,按住鼠标左键不放,拖到菜单栏中任意的工具条上,即可完成配置。
五、成果展示
1、所选图层内面要素总矢量面积
可以利用Arcgis的选择工具或者在属性表内选择任意数量的图形,然后此工具得到的数据就是被选中的那部分矢量的总面积(其他按钮效果一样)。
2、所选图层内面要素总净面积
3、所选范围内面要素总矢量面积
4、所选范围内面要素总净面积
通过开发上述案例,相信大家对于如何利用Python创建自己的ARCGIS自定义插件工具已经有了一定的掌握,如果还有疑惑可以在评论区留言,大家一起探讨,共同进步。
插件成果资源已绑定,需要的自提,完结撒花!!!