ArcGIS Pro 二次开发简述

本文基于 Windows7 + VS2019 + .NET Framework 4.8 + ArcGIS Pro 2.5.22081 开发和撰写。

开发环境配置

获取ArcGIS Pro

ArcGIS Pro可在Esri官网申请21天试用。

安装VS2019

VS2019的安装十分简单,在微软官网下载VS2019社区版安装程序,双击安装即可,具体可参考该博客

安装ArcGIS Pro SDK

关闭VS拓展自动更新

首先打开安装好的VS2019,点击“菜单栏-工具-选项”,在选项中找到“环境-拓展”,关闭拓展自动更新,如下图所示。关闭拓展自动更新可以防止拓展自动更新后与当前环境不匹配,如我的环境为ArcGIS Pro2.5,如果不关闭自动更新,则下次打开VS时ArcGIS Pro SDK插件将自动更新为2.6版本,与Pro版本不匹配,无法使用。

关闭VS拓展自动更新

正式安装插件

接下来开始安装ArcGIS Pro SDK for .NET,需要点击“菜单栏-拓展-管理拓展”,在弹出的窗口中切换至联机,搜索“ArcGIS Pro”,找到“ArcGIS Pro SDK for .NET”和“ArcGIS Pro SDK for .NET(Utilities)”两个插件,安装并禁用自动更新。点击安装后,重启VS插件即安装完毕,至此,开发环境配置完成。

需安装VS插件

创建第一个Pro Add-in

使用模板创建Pro Add-in项目

打开VS2019,选择“创建新项目”,将“项目类型筛选”设置为“ArcGIS Pro SDK”,找到“ArcGIS Pro 模块加载项”创建项目即可,注意选择语言为C#而非VB。

使用模板创建新项目

添加一个button

Pro的插件及配置使用DAML文件,即项目下的“config.daml”进行声明。

daml文件

手动添加button

添加button至显示

首先,我们来尝试手动添加一个button。打开“config.daml”文件,在controls标签下添加一个button标签。

<controls>
    <button id="AddOneButton" caption="Add one button" className="AddOneButton" loadOnClick="false" smallImage="Images\AddInDesktop16.png" largeImage="Images\AddInDesktop32.png" keytip="AOB">
        <tooltip>Add one button</tooltip>
    </button>
</controls> 

添加完button标签后,该控件并不会显示,只有当控件被某个group引用时,才会显示在菜单中,应用方式如下,其中refID为创建button标签时的id。

<groups>
    <group id="DJ_SuspectTrackingSystem_Group1" caption="Group 1" appearsOnAddInTab="true">
        <button refID="AddOneButton"/>
    </group>
</groups>

至此,当Pro加载时即会在“菜单栏-加载项-Group”中显示该控件。修改完成后的“config.daml”文件和Pro中显示效果如下图所示。

修改后daml文件
按钮加载效果

为button添加逻辑代码

修改daml后,仅实现了在Pro中显示button,下面来为button添加逻辑代码。

在项目中新建一个类,类名为刚才daml文件中对应button的className属性,并使其继承自Pro SDK中的Button类。然后重写Button的相关方法,如点击时触发的OnClick方法,在其中实现逻辑代码即可,如下图。

实现button逻辑代码

自动添加button

在我们熟悉了daml文件之后,日常开发即可直接使用VS提供的快捷添加控件的方式。

在项目上“右击-添加-新建项”,在弹出的窗口左侧选择“ArcGIS Pro Add-ins”中进行筛选后,选择“ArcGIS Pro 按钮”,点击添加,VS即会自动添加一个button类至项目中,在daml文件的controls中添加button定义,并在默认group中引用该button。

添加button项

添加后daml

生成Addin文件

插件配置完成后,在解决方案上“右键-重新生成解决方案”,待解决方案生成完毕后,在解决方案文件夹\bin\Debug目录下找到*.esriAddinX文件,该文件即为插件安装文件,双击即可为Pro安装该插件。

Addin安装文件

调试插件

如果不希望直接为Pro安装插件,而是在开发过程中需要测试插件效果,直接按快捷键F5或点击运行,VS将启动Pro,并在Pro中加载修改后的插件,可以在其中对插件进行测试。

开发小tip

  1. 当项目是从其他电脑拷贝而来时,引用地址可能不正确,此时,可以在解决方案上右击,找到“修复 Pro 引用”选项,点击后,插件会自动更新引用。若要手动更新引用,dll文件通常存放在Pro安装路径\binPro安装路径\bin\Extensions目录下。
  2. 提示"未能解析主引用***,因为它是针对“.NETFramework,Version=v4.8”框架生成的。该框架版本高于当前目标框架"错误信息,则说明生成使用的Framework版本与当前项目应该使用的Framework版本不匹配,在项目上“右击-属性-生成程序”,将“目标框架”切换为当前项目版本即可。
    切换框架版本
  3. 不知道什么原因,对dockpane UI的修改,需要进行以下操作才能生效:
    1. Pro中卸载该加载项
    2. 重新打开Pro,发现加载项没有再加载(必须重新再打开一次)
    3. 再解决方案上“右击-重新生成解决方案”
    4. 再次打开Pro,修改即生效

DAML配置

DAML是Desktop Application Markup Language的缩写。是ESRI基于XML标准自定义的UI配置文件。插件和配置的声明性部分是在DAML文件中定义的,其中包含了框架元素(主要是插件)的集合,还包括声明性部分(框架所需的信息,以便在适当的时候激活或创建相关的对象)。通过这种方式实现界面和功能的分离。

DAML介绍

具体的DAML介绍请查看DAML ID reference。以下仅介绍常用节点。

根节点

即ArcGIS标签下的节点,大多数情况下无需修改,使用默认即可,常用的有:

  1. defaultAssembly:该配置代表的插件所在的程序集名称。
  2. defaultNamespace:该配置代表的插件所在的命名空间名称。
    根节点

AddInInfo节点

AddInInfo节点用于声明插件的相关信息,如名称、描述、图标等

AddInInfo节点

modules节点

modules节点是最长访问的节点,插件元素的添加、描述等均在该节点下。modules节点可以包括的元素包括ribbon按钮、工具、画廊、组合框、编辑框、调色板和其他控件,以及应用程序窗格和对接窗格。

Pro 控件级别

Pro的控件级别从上到下分为Module-Tab-Group-Menu-Control,相互关系如下图所示。具体关系控件层级的概念请参考ProGuide

  1. TabTab
  2. GroupGroup
  3. MenuMenu
  4. ControlControl

一个原始的DAML文件如下图所示,开发时,先在controls标签下创建所有要使用的控件及其描述,然后在需要显示控件的group标签中引用对应的control即可。

原始DAML文件

将control在新tab中显示

默认情况下,control将添加至“菜单栏-加载项”中,如果需要在单独的Tab中显示,则需要在daml文件tabs标签下新建tab标签,并在其中添加对需要显示的group的引用,如下图。

添加Tab

常用DAML元素

下面只介绍常用的元素及其属性、方法等,各元素具体使用情景、属性、方法等请参见官方API文档。DAML中通常可以包括controls、categories、dockpanes、groups、menus、minitoolbars、panes、toolbars等等各种元素,具体元素可参见官方DAML ID Reference

控件control

控件常用属性

控件常用方法

控件常见方法有:

  1. OnClick():单击控件时触发

使用时,在控件类中重写对应的方法即可,如:

internal class AddOneButton : ArcGIS.Desktop.Framework.Contracts.Button
{
    protected override void OnClick()
        {
            MessageBox.Show("Hello World");
        }
}

常用控件

  1. button
  2. comboBox
  3. checkBox
  4. labelControl
  5. tool

停靠窗dockpane

dockpane类为停靠窗口类,其UI由对应的.xaml文件确定,逻辑代码由对应的.cs类文件实现。关于DockPane,具体可以参见ProGuide-Dockpanes

双击xaml文件即可进入设计窗口,可以通过拖拽的方式将各种控件添加至dockpane界面中,可以通过可视化或代码方式设置控件和dockpane的各种属性。

自定义DockPane停靠位置

使用dock,dockWith,autoHide属性可以设置DockPane的初始停靠状态。

  1. Dock:该属性用于设置初始停靠状态,可选值包括:
    1. left
    2. right
    3. top
    4. bottom
    5. float
    6. group
  2. dockWith:该属性可以指定要相对停靠的DockPane。

地理处理GeoProcess

可参考ProSnippets-GeoprocessingProConcepts-Geoprocessing

功能实现

很多功能实现可参考ArcGIS Pro SDK community samples

在Addin中直接打开自建的工具箱

若想要实现,点击插件上的按钮,自动打开对应的自建工具箱或系统工具箱,需要重写按钮的OnClick()方法,在其中使用ArcGIS.Desktop.Core.Geoprocessing.OpenToolDialog(toolPath, param_values)方法打开工具箱,示例如下。若想要不打开工具箱而直接执行工具,则使用ArcGIS.Desktop.Core.Geoprocessing.ExecuteToolAsync(toolPath,param_values)方法异步执行工具。

如果要使用系统工具箱,则直接用工具箱名\工具名调用即可,如 。如需使用自建工具箱,则使用工具箱路径\工具箱名(包括后缀)\工具名调用,如@"E:\ArcGIS\MyProject\sixTool\sixTool.pyt\OpenHDFTool"

此外,不论是打开工具还是执行工具,都必须输入参数,且创建参数时必须至少有一个输入,否则虽然不会抛出语法错误,但是工具不能正常执行,原因未明。

internal class FactorsCorrelationAnalysis : Button
{
    protected override void OnClick()
    {
        //创建工具参数
        //工具必须有参数
        string input_points = "";
        var param_values = Geoprocessing.MakeValueArray(input_points); //创建参数时必须至少有一个输入,否则工具不会正常显示

        //获取sixTools的绝对路径
        string nowPath = System.Windows.Forms.Application.StartupPath; //获取启动程序的可执行文件所在的目录
        string ArcGISPath = System.IO.Directory.GetParent(nowPath).FullName; //获取当前目录的父目录的绝对路径
        string toolRelativePath = @"Resources\ArcToolBox\Scripts\sixTool.pyt\FactorsCorrelationAnalysisTool";
        string toolPath = System.IO.Path.Combine(ArcGISPath, toolRelativePath);
        //MessageBox.Show(toolPath);

        //打开工具箱中的工具
        Geoprocessing.OpenToolDialog(toolPath, param_values);//必须输入工具参数
    }
}

点击弹出浮动窗口

若要实现点击按钮后弹出一个浮动窗口,如ArcGIS自带的查询工具,则在项目中添加一个Dockpane停靠窗类,并在“config.daml”文件中设置其“Dock”属性为“float”即可。

使用Python脚本

Pro中使用Python脚本,本质上就是通过cmd的方式,调用Pro对应的python解释器来执行脚本,并获取脚本执行结果。详细可参考官方sample CallScriptFromNet

//从环境变量中获取Pro对应的Python解释器的绝对路径
var pathProExe = System.IO.Path.GetDirectoryName((new System.Uri(Assembly.GetEntryAssembly().CodeBase)).AbsolutePath);
if (pathProExe == null) return;
pathProExe = Uri.UnescapeDataString(pathProExe);
pathProExe = System.IO.Path.Combine(pathProExe, @"Python\envs\arcgispro-py3");
System.Diagnostics.Debug.WriteLine(pathProExe);
var pathPython = System.IO.Path.GetDirectoryName((new System.Uri(Assembly.GetExecutingAssembly().CodeBase)).AbsolutePath);
if (pathPython == null) return;
pathPython = Uri.UnescapeDataString(pathPython);
System.Diagnostics.Debug.WriteLine(pathPython);

//使用cmd调用python解释器来执行python脚本
//创建用于执行cmd的字符串以及获取异常信息的对象
var myCommand = string.Format(@"/c """"{0}"" ""{1}""""",
    System.IO.Path.Combine(pathProExe, "python.exe"),
    System.IO.Path.Combine(pathPython, "test1.py"));
System.Diagnostics.Debug.WriteLine(myCommand);
var procStartInfo = new System.Diagnostics.ProcessStartInfo("cmd", myCommand);

procStartInfo.RedirectStandardOutput = true;
procStartInfo.RedirectStandardError = true;
procStartInfo.UseShellExecute = false;
procStartInfo.CreateNoWindow = true;

//开始执行脚本
System.Diagnostics.Process proc = new System.Diagnostics.Process();
proc.StartInfo = procStartInfo;
proc.Start();

//获取执行结果并显示
string result = proc.StandardOutput.ReadToEnd();
string error = proc.StandardError.ReadToEnd();
if (!string.IsNullOrEmpty(error)) result += string.Format("{0} Error: {1}", result, error);
System.Windows.MessageBox.Show(result);

创建要属类

创建要属类本质上是使用Toolbox中的创建要属类工具(CreateFeatureclass_management)来创建所需的要属类。详细可参考官方sample ConstructingGeometries_CSharp

//根据输入的要属类名称和类型在默认数据库中创建要属类的函数
private static async Task CreateLayer(string featureclassName, string featureclassType)
    {
      //创建要素时的参数list
      List<object> arguments = new List<object>
      {
        // store the results in the default geodatabase
        CoreModule.CurrentProject.DefaultGeodatabasePath,
        // name of the feature class
        featureclassName,
        // type of geometry
        featureclassType,
        // no template
        "",
        // no z values
        "DISABLED",
        // no m values
        "DISABLED"
      };
      //异步,为参数list添加坐标系统参数
      await QueuedTask.Run(() =>
      {
              // spatial reference
              arguments.Add(SpatialReferenceBuilder.CreateSpatialReference(3857));
      });

      //异步执行创建要属类工具,创建要素类
      IGPResult result = await Geoprocessing.ExecuteToolAsync("CreateFeatureclass_management", Geoprocessing.MakeValueArray(arguments.ToArray()));
    }
  }

为线要素类添加记录

执行的操作包括:

  1. 获取需要编辑的要属类
  2. 创建编辑器
  3. 创建一个MapPoint List,用于存储构建线记录的点
  4. 获取点要素类游标,迭代点要素类的所有记录,将构建线记录的点对象添加至MapPoint List对象中
  5. 使用PolylineBuilder.CreatePolyline()方法,基于MapPoint List对象构建Polyline对象
  6. 使用编辑器对象的Create()方法,基于Polyline对象为线要属类添加一条记录
  7. 执行编辑器操作,保存修改
  8. 完成

具体可参考官方sample ConstructingGeometries_CSharp

//为线要素类中添加记录的函数
//polylineLayer为需要添加记录的线要素图层;pointLayer为用于构建线要素的点图层,也可以执行构建点要素
private Task<bool> ConstructSamplePolylines(FeatureLayer polylineLayer, FeatureLayer pointLayer)
{
    // ()=>{...}或()=>functionNmae() 为.NET3.0以后的新特性 Lambda表达式
    // 也就是说,执行=>后面的函数,把函数返回值作为一个参数传给当前函数

    // execute the fine grained API calls on the CIM main thread
    return QueuedTask.Run(() =>
    {
        // get the underlying feature class for each layer
        // as 和is都是强制类型转换运算符。作用类似于C中的  (type)data;  一般使用as,具体不深究
        // 此处未将 polylineLayer.GetTable() 强制转换为 FeatureClass 类型后,赋值给 polylineFeatureClass 。
        var polylineFeatureClass = polylineLayer.GetTable() as FeatureClass;
        var pointFeatureClass = pointLayer.GetTable() as FeatureClass;

        // retrieve the feature class schema information for the feature classes
        var polylineDefinition = polylineFeatureClass.GetDefinition() as FeatureClassDefinition;
        var pointDefinition = pointFeatureClass.GetDefinition() as FeatureClassDefinition;

        // construct a cursor for all point features, since we want all feature there is no
        // QueryFilter required
        var pointCursor = pointFeatureClass.Search(null, false);
        var is3D = pointFeatureClass.GetDefinition().HasZ();

        // initialize a counter variable
        int pointCounter = 0;
        // initialize a list to hold 5 coordinates that are used as vertices for the polyline
        var lineMapPoints = new List<MapPoint>(5);

        // set up the edit operation for the feature creation
        var createOperation = new EditOperation()
        {
        Name = "Create polylines",
        SelectNewFeatures = false
        };

        // loop through the point features
        while (pointCursor.MoveNext())
        {
            pointCounter++;

            var pointFeature = pointCursor.Current as Feature;
            // add the feature point geometry as a coordinate into the vertex list of the line
            // - ensure that the projection of the point geometry is converted to match the spatial reference of the line
            lineMapPoints.Add(((MapPoint)GeometryEngine.Instance.Project(pointFeature.GetShape(), polylineDefinition.GetSpatialReference())));

            // for every 5 geometries, construct a new polyline and queue a feature create
            if (pointCounter % 5 == 0)
            {
                // construct a new polyline by using the 5 point coordinate in the current list
                var newPolyline = PolylineBuilder.CreatePolyline(lineMapPoints, polylineDefinition.GetSpatialReference());
                // queue the create operation as part of the edit operation
                createOperation.Create(polylineLayer, newPolyline);
                // reset the list of coordinates
                lineMapPoints = new List<MapPoint>(5);
            }
        }

        // execute the edit (create) operation
        return createOperation.ExecuteAsync();
    });
}

待续

ArcGIS Pro SDK 本来不在我的近期学习计划中,只有由于朋友让帮忙做一个小插件所以稍微学了一点,使用上面介绍的东西就搞定了。因此,本系列下一次更新可能遥遥无期……


本文参考:

  1. 博客 https://blog.csdn.net/xiangqiang2015/article/details/81741689
  2. 博客 https://blog.csdn.net/DynastyRumble/article/details/104683339
  3. Esri官方wiki https://github.com/esri/arcgis-pro-sdk/wiki
  4. Pro API reference https://pro.arcgis.com/en/pro-app/sdk/api-reference/index.html#topic10500.html
  5. ArcGIS Pro DAML ID Reference https://github.com/Esri/arcgis-pro-sdk/wiki/ArcGIS-Pro-DAML-ID-Reference
  6. ArcGIS Pro Addin guide https://github.com/Esri/arcgis-pro-sdk/wiki/ProGuide-Build-Your-First-Add-in
  7. ArcGIS Pro SDK 社区示例 https://github.com/Esri/arcgis-pro-sdk-community-samples
posted @ 2020-09-02 17:00  geoli91  阅读(6599)  评论(0编辑  收藏  举报