01 前言
如果以后不是工作相关,我或许以后再难了解ArcEngine相关的GIS桌面软件二次开发的内容?
02 要求
- 创建窗体应用程序;(10分)
- 修改窗口标题为本人的“学号 姓名”;(5分)
- 添加主地图控件、图层树控件和数据表显示控件,并合理布局;(10分)
- 添加菜单和状态栏控件;(5分)
- 增加“打开地图文档”菜单功能,弹出对话框选择地图文档(*.mxd),在主地图控件中显示该地图;(10分)
- 鼠标在主地图控件中移动时,将鼠标对应的地图坐标显示在状态栏上;(10分)
- 地图显示范围发生改变时,将地图比例显示在状态栏上;(10分)
- 点击图层控件的图层项,将地图视图显示范围缩放到该图层范围;(10分)
- 在数据表显示控件中显示所选图层的属性;(20分)
- 撰写期末考查报告。(10分)
笔记本;WIndow11;VS2012;ArcEngine10.2
如果安装高版本的VS可能无法完美复现本博客,因为高版本VS如何创建基础版的应用,而是创建的完全0的空应用。
03 实现步骤和代码说明
3.1 创建窗体应用程序(创建项目)
初始界面如下:
如果不存在上述面板如下打开:
如此可以看到如下初始界面:
3.2 修改窗口标题为本人的“学号 姓名”
3.3 添加主地图控件、图层树控件和数据表显示控件,并合理布局
主地图控件和图层树控件已经在创建项目之后已经存在了,只是数据表(用于后续选中图层的属性信息显示)并没有,而且要与当前控件布局协调。
数据表使用DataGridView
工具。
3.3.1 添加数据表DataGridView
3.3.2 调整布局
但是目前布局不合理,我们需要调整DataGridView
和axMapControl1
(主地图控件)的空间位置。
修改DataGridView
的Dock
属性为Bottom
,这里就是用其他工具进行调整了,简单一些好了。
3.4 添加菜单和状态栏控件;
由于创建项目时已经帮助我们添加这些控件,因此我们无需重复操作,这里直接运行看看界面吧。
3.5 增加“打开地图文档”菜单功能,弹出对话框选择地图文档(*.mxd),在主地图控件中显示该地图;
这个功能实际上创建项目时也已经实现了,因此演示如下:
3.6 鼠标在主地图控件中移动时,将鼠标对应的地图坐标显示在状态栏上;
实际上也已经实现,这里不再说明
3.7 地图显示范围发生改变时,将地图比例显示在状态栏上
状态栏加一个用于显示比例尺:
监听范围更新事件:
事件代码如下:
private void axMapControl1_OnExtentUpdated(object sender, IMapControlEvents2_OnExtentUpdatedEvent e)
{
//当前比例尺
string strScale = " 1:" + ((long)axMapControl1.MapScale).ToString() + " ";
// 在状态栏中进行格式化显示
statusBarScale.Text = string.Format("{0}", strScale);
}
实现效果如下:
3.8 点击图层控件的图层项,将地图视图显示范围缩放到该图层范围
显然,需要监听TOC图层树的鼠标点击事件:
事件代码如下:
// 当鼠标点击图层时, 实现图层缩放和属性获取显示两个功能
private void axTOCControl1_OnMouseDown(object sender, ITOCControlEvents_OnMouseDownEvent e)
{
// 做鼠标点击位置检测-用于获取当前点击位置是否为图层以及获取点击的图层对象
esriTOCControlItem itemType = esriTOCControlItem.esriTOCControlItemNone; // 赋初值None
IBasicMap basicMap = null;
ILayer pCurLayer = null;
object unk = null;
object data = null;
axTOCControl1.HitTest(e.x, e.y, ref itemType, ref basicMap, ref pCurLayer, ref unk, ref data); // 检测点击位置, 返回给ref等
// 如果点击的不是图层对象, 那么不进行任何操作
if (itemType != esriTOCControlItem.esriTOCControlItemLayer)
{
return;
}
// 如果pCurLayer为空则不进行操作
if (pCurLayer == null)
{
return;
}
// 获取当前点击图层的感兴趣区域范围
IEnvelope env = pCurLayer.AreaOfInterest; // 获取当前点击图层的范围
// 当前选中图层的框为空, 那么不进行任何操作
if ((env.IsEmpty) || (env == null))
{
return;
}
// 将主视图的范围显示为当前右键选中图层的范围, 实现缩放到当前图层的功能
axMapControl1.Extent = env;
// 显示当前图层属性(自定义一个query_feature_class函数进行操作, 传入当前点击图层变量)
query_feature_class(pCurLayer);
}
上述代码最后部分加入了一个自定义函数:
// 显示当前图层属性(自定义一个query_feature_class函数进行操作, 传入当前点击图层变量)
query_feature_class(pCurLayer);
注意:这是后续功能(显示选中图层的属性信息)的实现。
3.9 在数据表显示控件中显示所选图层的属性
我们主要实现前面的query_feature_class
函数,代码如下:
// 实现选择所选图层属性功能
privatevoid query_feature_class(ILayer layer)
{
// 获取空间过滤对象
ISpatialFilter pSpatialFilter = new SpatialFilter();
pSpatialFilter.SpatialRel = esriSpatialRelEnum.esriSpatialRelIntersects;
pSpatialFilter.GeometryField = "Shape";
// 将当前传入该函数的 layer 进行过滤
IFeatureClass pFeatureClass = (layer as IFeatureLayer).FeatureClass;
IFeatureCursor pFeatureCursor = pFeatureClass.Search(pSpatialFilter, true);
if (pFeatureCursor == null)
{
dataGridView.DataSource = null;
}
else
{
// 将对象传入
dataGridView.DataSource = cursor2table(pFeatureCursor);
}
}
// 图层属性信息与DataGridView数据格式的转换
private DataTable cursor2table(IFeatureCursor pFeatureCursor)
{
// 为DataGridView创建表头并赋值
DataTable tbl = new DataTable();
for (int ix = 0; ix < pFeatureCursor.Fields.FieldCount; ix++)
{
tbl.Columns.Add(pFeatureCursor.Fields.get_Field(ix).Name);
}
// 将所有要素一行一行的迭代传入DataGridView中
IFeature pFeature = pFeatureCursor.NextFeature();
while (pFeature != null)
{
DataRow row = tbl.NewRow();
for (int ix = 0; ix < pFeatureCursor.Fields.FieldCount; ix++)
{
row[ix] = pFeature.get_Value(ix).ToString();
}
tbl.Rows.Add(row);
pFeature = pFeatureCursor.NextFeature();
}
return tbl; // 返回已经存有图层属性信息的DataGridView对象
}
04 完整代码和应用界面展示
如此所有功能实现,完整代码:
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.IO;
using System.Runtime.InteropServices;
using ESRI.ArcGIS.esriSystem;
using ESRI.ArcGIS.Carto;
using ESRI.ArcGIS.Controls;
using ESRI.ArcGIS.ADF;
using ESRI.ArcGIS.SystemUI;
using ESRI.ArcGIS.Geometry;
namespace DropCurtain
{
public sealed partial class MainForm : Form
{
#region class private members
private IMapControl3 m_mapControl = null;
private string m_mapDocumentName = string.Empty;
#endregion
#region class constructor
public MainForm()
{
InitializeComponent();
}
#endregion
private void MainForm_Load(object sender, EventArgs e)
{
//get the MapControl
m_mapControl = (IMapControl3)axMapControl1.Object;
//disable the Save menu (since there is no document yet)
menuSaveDoc.Enabled = false;
}
#region Main Menu event handlers
private void menuNewDoc_Click(object sender, EventArgs e)
{
//execute New Document command
ICommand command = new CreateNewDocument();
command.OnCreate(m_mapControl.Object);
command.OnClick();
}
private void menuOpenDoc_Click(object sender, EventArgs e)
{
//execute Open Document command
ICommand command = new ControlsOpenDocCommandClass();
command.OnCreate(m_mapControl.Object);
command.OnClick();
}
private void menuSaveDoc_Click(object sender, EventArgs e)
{
//execute Save Document command
if (m_mapControl.CheckMxFile(m_mapDocumentName))
{
//create a new instance of a MapDocument
IMapDocument mapDoc = new MapDocumentClass();
mapDoc.Open(m_mapDocumentName, string.Empty);
//Make sure that the MapDocument is not readonly
if (mapDoc.get_IsReadOnly(m_mapDocumentName))
{
MessageBox.Show("Map document is read only!");
mapDoc.Close();
return;
}
//Replace its contents with the current map
mapDoc.ReplaceContents((IMxdContents)m_mapControl.Map);
//save the MapDocument in order to persist it
mapDoc.Save(mapDoc.UsesRelativePaths, false);
//close the MapDocument
mapDoc.Close();
}
}
private void menuSaveAs_Click(object sender, EventArgs e)
{
//execute SaveAs Document command
ICommand command = new ControlsSaveAsDocCommandClass();
command.OnCreate(m_mapControl.Object);
command.OnClick();
}
private void menuExitApp_Click(object sender, EventArgs e)
{
//exit the application
Application.Exit();
}
#endregion
//listen to MapReplaced evant in order to update the statusbar and the Save menu
private void axMapControl1_OnMapReplaced(object sender, IMapControlEvents2_OnMapReplacedEvent e)
{
//get the current document name from the MapControl
m_mapDocumentName = m_mapControl.DocumentFilename;
//if there is no MapDocument, diable the Save menu and clear the statusbar
if (m_mapDocumentName == string.Empty)
{
menuSaveDoc.Enabled = false;
statusBarXY.Text = string.Empty;
}
else
{
//enable the Save manu and write the doc name to the statusbar
menuSaveDoc.Enabled = true;
statusBarXY.Text = System.IO.Path.GetFileName(m_mapDocumentName);
}
}
private void axMapControl1_OnMouseMove(object sender, IMapControlEvents2_OnMouseMoveEvent e)
{
statusBarXY.Text = string.Format("{0}, {1} {2}", e.mapX.ToString("#######.##"), e.mapY.ToString("#######.##"), axMapControl1.MapUnits.ToString().Substring(4));
}
private void axMapControl1_OnExtentUpdated(object sender, IMapControlEvents2_OnExtentUpdatedEvent e)
{
//当前比例尺
string strScale = " 1:" + ((long)axMapControl1.MapScale).ToString() + " ";
// 在状态栏中进行格式化显示
statusBarScale.Text = string.Format("{0}", strScale);
}
private void axTOCControl1_OnMouseDown(object sender, ITOCControlEvents_OnMouseDownEvent e)
{
// 做鼠标点击位置检测-用于获取当前点击位置是否为图层以及获取点击的图层对象
esriTOCControlItem itemType = esriTOCControlItem.esriTOCControlItemNone; // 赋初值None
IBasicMap basicMap = null;
ILayer pCurLayer = null;
object unk = null;
object data = null;
axTOCControl1.HitTest(e.x, e.y, ref itemType, ref basicMap, ref pCurLayer, ref unk, ref data); // 检测点击位置, 返回给ref等
// 如果点击的不是图层对象, 那么不进行任何操作
if (itemType != esriTOCControlItem.esriTOCControlItemLayer)
{
return;
}
// 如果pCurLayer为空则不进行操作
if (pCurLayer == null)
{
return;
}
// 获取当前点击图层的感兴趣区域范围
IEnvelope env = pCurLayer.AreaOfInterest; // 获取当前点击图层的范围
// 当前选中图层的框为空, 那么不进行任何操作
if ((env.IsEmpty) || (env == null))
{
return;
}
// 将主视图的范围显示为当前右键选中图层的范围, 实现缩放到当前图层的功能
axMapControl1.Extent = env;
// 显示当前图层属性(自定义一个query_feature_class函数进行操作, 传入当前点击图层变量)
query_feature_class(pCurLayer);
}
// 实现选择所选图层属性功能
private void query_feature_class(ILayer layer)
{
// 获取空间过滤对象
ISpatialFilter pSpatialFilter = new SpatialFilter();
pSpatialFilter.SpatialRel = esriSpatialRelEnum.esriSpatialRelIntersects;
pSpatialFilter.GeometryField = "Shape";
// 将当前传入该函数的 layer 进行过滤
IFeatureClass pFeatureClass = (layer as IFeatureLayer).FeatureClass;
IFeatureCursor pFeatureCursor = pFeatureClass.Search(pSpatialFilter, true);
if (pFeatureCursor == null)
{
dataGridView.DataSource = null;
}
else
{
// 将对象传入
dataGridView.DataSource = cursor2table(pFeatureCursor);
}
}
// 图层属性信息与DataGridView数据格式的转换
private DataTable cursor2table(IFeatureCursor pFeatureCursor)
{
// 为DataGridView创建表头并赋值
DataTable tbl = new DataTable();
for (int ix = 0; ix < pFeatureCursor.Fields.FieldCount; ix++)
{
tbl.Columns.Add(pFeatureCursor.Fields.get_Field(ix).Name);
}
// 将所有要素一行一行的迭代传入DataGridView中
IFeature pFeature = pFeatureCursor.NextFeature();
while (pFeature != null)
{
DataRow row = tbl.NewRow();
for (int ix = 0; ix < pFeatureCursor.Fields.FieldCount; ix++)
{
row[ix] = pFeature.get_Value(ix).ToString();
}
tbl.Rows.Add(row);
pFeature = pFeatureCursor.NextFeature();
}
return tbl; // 返回已经存有图层属性信息的DataGridView对象
}
}
}
实现界面:
打开地图文档功能:
图层缩放和选中图层的属性信息显示: