地图编辑功能涉及到比较复杂的地图与鼠标的交互以及事件的响应,ArcGIS提供了强大的地图编辑的相关功能。本节我们将尝试实现一些简单的地图编辑功能,包括点、线、面要素形状的创建和移动。通过本节希望你能掌握ArcEngine实现地图编辑的机制以及常用的地图编辑的接口。
新建一个C#.Net项目,项目名称为MapEdit,添加MapControlLicenceControl、四个Button、一个ComboBox、一个Label等控件。如下图:
ArcEngine中的地图编辑使用IWorkspaceEdit接口来进行编辑状态的管理,在需要对指定的工作空间进行编辑时,首先使用IWorkspaceEdit获取该工作空间的数据,然后使用StartEditing方法开始编辑状态,StartEditOperation方法打开具体编辑的操作,编辑完成后,使用StopEditOperation方法关闭编辑操作,使用StopEditing方法关闭编辑状态,完成编辑。
在本例中,我们实现了新的点线面要素的创建和移动的功能,涉及到了比较复杂的鼠标与地图间的交互,这个功能的实现中,IDisplayFeedback是一个十分关键的接口,它具有涉及创建要素,移动要素、编辑节点等31个实现类,能够实现鼠标与地图交互中的事件的追踪,返回新的几何对象。
创建项目后注意把ESRI.ArcGIS.RuntimeManager.Bind(ESRI.ArcGIS.ProductCode.EngineOrDesktop);这句代码加入到Program.cs文件中。
using ESRI.ArcGIS.Carto;
using ESRI.ArcGIS.Controls;
using ESRI.ArcGIS.Geometry;
using ESRI.ArcGIS.Geodatabase;
using ESRI.ArcGIS.Display;
定义如下成员变量。
//操作类型
string strOperator = "";
//当前地图视图
IActiveView m_activeView = null;
//当前操作图层
IFeatureLayer m_FeatureLayer = null;
//当前操作实体
IFeature m_Feature = null;
//当前点移动反馈对象
IMovePointFeedback m_MovePointFeedback = new MovePointFeedbackClass();
//当前线移动反馈对象
IMoveLineFeedback m_MoveLineFeedback = new MoveLineFeedbackClass();
//当前面移动反馈对象
IMovePolygonFeedback m_MovePolygonFeedback = new MovePolygonFeedbackClass();
加载地图文档的函数代码:
private void button1_Click(object sender, EventArgs e)
{
//加载地图文档
loadMapDocument();
//将图层名填加到下拉列表框
for (int i = 0; i < this.axMapControl1.LayerCount; i++)
{
ILayer layer = this.axMapControl1.get_Layer(i);
this.comboBox1.Items.Add(layer.Name);
}
}
//加载地图文档
private void loadMapDocument()
{
System.Windows.Forms.OpenFileDialog openFileDialog;
openFileDialog = new OpenFileDialog();
openFileDialog.Title = "打开地图文档";
openFileDialog.Filter = "map documents(*.mxd)|*.mxd";
openFileDialog.ShowDialog();
string filePath = openFileDialog.FileName;
if (axMapControl1.CheckMxFile(filePath))
{
axMapControl1.MousePointer = esriControlsMousePointer.esriPointerHourglass;
axMapControl1.LoadMxFile(filePath, 0, Type.Missing);
axMapControl1.MousePointer = esriControlsMousePointer.esriPointerDefault;
}
else
{
MessageBox.Show(filePath + "不是有效的地图文档");
}
}
combobox1添加selectedchange事件。
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
if (this.comboBox1.Text != "")
{
for (int i = 0; i < this.axMapControl1.LayerCount; i++)
{
ILayer layer = this.axMapControl1.get_Layer(i);
if (layer.Name == this.comboBox1.Text.ToString())
{
m_FeatureLayer = layer as IFeatureLayer;
m_Feature = m_FeatureLayer.FeatureClass.GetFeature(0);
if (m_Feature != null)
{
this.axMapControl1.Map.ClearSelection();
this.axMapControl1.Map.SelectFeature(m_FeatureLayer, m_Feature);
this.axMapControl1.Refresh();
}
m_activeView = this.axMapControl1.ActiveView;
return;
}
}
}
}
移动要素按钮添加事件:
private void button2_Click(object sender, EventArgs e)
{
strOperator = "move";
m_MovePointFeedback = new MovePointFeedbackClass();
m_MoveLineFeedback = new MoveLineFeedbackClass();
m_MovePolygonFeedback = new MovePolygonFeedbackClass();
}
下面添加鼠标与地图的交互事件,包括移动要素时鼠标的PanMouseDown事件、MouseMove事件和MouseUp事件。代码如下:
创建要素时首先在MouseDown事件中获取鼠标点击的点位,若图层为点图层,则直接创建要素,若为线图层或面图层,则作为第一个节点,以后每次点击都会添加一个节点,直到双击鼠标完成要素的创建。创建要素时的MouseDown事件在这里定义为CreateMouseDown。代码如下:
private void axMapControl1_OnMouseDown(object sender, IMapControlEvents2_OnMouseDownEvent e)
{
IPoint point = new PointClass();
IFeatureClass featureClass = null;
if (m_Feature == null) return;
switch (strOperator)
{
case "move":
//将当前鼠标位置的点转换为地图上的坐标
point = m_activeView.ScreenDisplay.DisplayTransformation.ToMapPoint(e.x, e.y);
if (m_Feature.Shape.GeometryType == esriGeometryType.esriGeometryPoint)
{
//设置显示对象,并启动移动
m_MovePointFeedback.Display = m_activeView.ScreenDisplay;
m_MovePointFeedback.Start(m_Feature.Shape as IPoint, point);
}
else if (m_Feature.Shape.GeometryType == esriGeometryType.esriGeometryPolyline)
{
//设置显示对象,并启动移动
m_MoveLineFeedback.Display = m_activeView.ScreenDisplay;
m_MoveLineFeedback.Start(m_Feature.Shape as IPolyline, point);
}
else if (m_Feature.Shape.GeometryType == esriGeometryType.esriGeometryPolygon)
{
//设置显示对象,并启动移动
m_MovePolygonFeedback.Display = m_activeView.ScreenDisplay;
m_MovePolygonFeedback.Start(m_Feature.Shape as IPolygon, point);
}
break;
}
}
private void axMapControl1_OnMouseMove(object sender, IMapControlEvents2_OnMouseMoveEvent e)
{
IPoint point = new PointClass();
switch (strOperator)
{
case "move":
if (m_Feature.Shape.GeometryType == esriGeometryType.esriGeometryPoint)
{
if (m_MovePointFeedback != null)
{
//将当前鼠标位置的点转换为地图上的坐标
point = m_activeView.ScreenDisplay.DisplayTransformation.ToMapPoint(e.x, e.y);
//移动对象到当前鼠标位置
m_MovePointFeedback.MoveTo(point);
}
}
else if (m_Feature.Shape.GeometryType == esriGeometryType.esriGeometryPolyline)
{
if (m_MoveLineFeedback != null)
{
//将当前鼠标位置的点转换为地图上的坐标
point = m_activeView.ScreenDisplay.DisplayTransformation.ToMapPoint(e.x, e.y);
//移动对象到当前鼠标位置
m_MoveLineFeedback.MoveTo(point);
}
}
else if (m_Feature.Shape.GeometryType == esriGeometryType.esriGeometryPolygon)
{
if (m_MovePolygonFeedback != null)
{
//将当前鼠标位置的点转换为地图上的坐标
point = m_activeView.ScreenDisplay.DisplayTransformation.ToMapPoint(e.x, e.y);
//移动对象到当前鼠标位置
m_MovePolygonFeedback.MoveTo(point);
}
}
break;
}
}
private void axMapControl1_OnMouseUp(object sender, IMapControlEvents2_OnMouseUpEvent e)
{
if (m_Feature == null) return;
IGeometry resultGeometry = null;
switch (strOperator)
{
case "move":
if (m_Feature.Shape.GeometryType == esriGeometryType.esriGeometryPoint)
{
//停止移动
resultGeometry = m_MovePointFeedback.Stop() as IGeometry;
m_Feature.Shape = resultGeometry;
}
else if (m_Feature.Shape.GeometryType == esriGeometryType.esriGeometryPolyline)
{
//停止移动
resultGeometry = m_MoveLineFeedback.Stop() as IGeometry;
m_Feature.Shape = resultGeometry;
}
else if (m_Feature.Shape.GeometryType == esriGeometryType.esriGeometryPolygon)
{
//停止移动
resultGeometry = m_MovePolygonFeedback.Stop() as IGeometry;
m_Feature.Shape = resultGeometry;
}
IWorkspaceEdit workspaceEdit;
IWorkspace workspace;
IDataset dataset = m_FeatureLayer.FeatureClass as IDataset;
workspace = dataset.Workspace;
workspaceEdit = workspace as IWorkspaceEdit;
//开始编辑
workspaceEdit.StartEditing(true);
workspaceEdit.StartEditOperation();
//保存实体
m_Feature.Store();
//结束编辑
workspaceEdit.StopEditOperation();
workspaceEdit.StopEditing(true);
m_MovePointFeedback = null;
m_MoveLineFeedback = null;
m_MovePolygonFeedback = null;
break;
}
m_activeView.Refresh();
this.axMapControl1.Map.ClearSelection();
}
删除节点按钮事件的代码如下:
private void button3_Click(object sender, EventArgs e)
{
IPointCollection pointCollection;
if (m_Feature.Shape.GeometryType == esriGeometryType.esriGeometryPolyline)
{
pointCollection = new PolylineClass();
IPolyline polyline = m_Feature.Shape as IPolyline;
pointCollection = polyline as IPointCollection;
//如果点个数少于两个无法构成线
if (pointCollection.PointCount > 2)
{
//移除指定的节点
pointCollection.RemovePoints(pointCollection.PointCount - 1, 1);
}
}
else if (m_Feature.Shape.GeometryType == esriGeometryType.esriGeometryPolygon)
{
pointCollection = new PolygonClass();
IPolygon polygon = m_Feature.Shape as IPolygon;
pointCollection = polygon as IPointCollection;
//如果点个数少于三个无法构成面
if (pointCollection.PointCount > 3)
{
//移除指定的节点
pointCollection.RemovePoints(pointCollection.PointCount - 1, 1);
}
}
IWorkspaceEdit workspaceEdit;
IWorkspace workspace;
IDataset dataset = m_FeatureLayer.FeatureClass as IDataset;
workspace = dataset.Workspace;
workspaceEdit = workspace as IWorkspaceEdit;
//开始编辑
workspaceEdit.StartEditing(true);
workspaceEdit.StartEditOperation();
//保存数据
m_Feature.Store();
//结束编辑
workspaceEdit.StopEditOperation();
workspaceEdit.StopEditing(true);
m_activeView.Refresh();
}
双击“增加节点”添加如下代码:
private void button4_Click(object sender, EventArgs e)
{
IPointCollection pointCollection;
IPoint point = new PointClass();
IPoint fromPoint = new PointClass();
IPoint toPoint = new PointClass();
if (m_Feature.Shape.GeometryType == esriGeometryType.esriGeometryPolyline)
{
pointCollection = new PolylineClass();
IPolyline polyline = m_Feature.Shape as IPolyline;
object missing1 = Type.Missing;
object missing2 = Type.Missing;
pointCollection = polyline as IPointCollection;
//获取线对象的最后两个点
fromPoint = pointCollection.get_Point(pointCollection.PointCount - 2);
toPoint = pointCollection.get_Point(pointCollection.PointCount - 1);
//根据线最后两个点,创建一个新点
point.PutCoords((fromPoint.X + toPoint.X) / 2, (fromPoint.Y + toPoint.Y) / 2 + 50);
//将新点添加到线对象的点集合中
pointCollection.AddPoint(point, ref missing1, ref missing2);
}
else if (m_Feature.Shape.GeometryType == esriGeometryType.esriGeometryPolygon)
{
pointCollection = new PolygonClass();
IPolygon polygon = m_Feature.Shape as IPolygon;
object missing1 = Type.Missing;
object missing2 = Type.Missing;
pointCollection = polygon as IPointCollection;
//获取面对象点集最后两个点
fromPoint = pointCollection.get_Point(pointCollection.PointCount - 2);
toPoint = pointCollection.get_Point(pointCollection.PointCount - 1);
//根据线最后两个点,创建一个新点
point.PutCoords((fromPoint.X + toPoint.X) / 2, (fromPoint.Y + toPoint.Y) / 2 + 50);
//将新点添加到线对象的点集合中
pointCollection.AddPoint(point, ref missing1, ref missing2);
}
IWorkspaceEdit workspaceEdit;
IWorkspace workspace;
IDataset dataset = m_FeatureLayer.FeatureClass as IDataset;
workspace = dataset.Workspace;
workspaceEdit = workspace as IWorkspaceEdit;
//开始编辑
workspaceEdit.StartEditing(true);
workspaceEdit.StartEditOperation();
//保存数据
m_Feature.Store();
//结束编辑
workspaceEdit.StopEditOperation();
workspaceEdit.StopEditing(true);
m_activeView.Refresh();
}
至此,我们完成了代码的编写,效果如图所示。
地图编辑的GIS中比较复杂和困难的环节,涉及到的对象和接口非常多,上面实例只是实现了最基础的编辑的活动,如果读者对二次开发感兴趣,可以自己试着阅读GIS二次开发相关书籍,了解与编辑相关的接口和方法,自己实现。
以下演示了如何改变一个图层的空间参考。运行效果如下图:
先新建一个MapControl Application模板,添加菜单按钮,事件代码为:
private void 空间参考ToolStripMenuItem_Click(object sender, EventArgs e)
{
Transform attributeQueryForm = new Transform(this.axMapControl1);
attributeQueryForm.Show();
}
然后见一个窗体,命名为Transform,控件布局见上图。添加变量:
//地图数据
private AxMapControl mMapControl;
//选中图层
private IFeatureLayer mFeatureLayer;
构造函数修改为:
public Transform(AxMapControl mapControl)
{
InitializeComponent();
this.mMapControl = mapControl;
}
Load事件为:
private void Transform_Load(object sender, EventArgs e)
{
//MapControl中没有图层时返回
if (this.mMapControl.LayerCount <= 0)
return;
//获取MapControl中的全部图层名称,并加入ComboBox
//图层
ILayer pLayer;
//图层名称
string strLayerName;
for (int i = 0; i < this.mMapControl.LayerCount; i++)
{
pLayer = this.mMapControl.get_Layer(i);
strLayerName = pLayer.Name;
//图层名称加入cboLayer
this.comboBox1.Items.Add(strLayerName);
}
//默认显示第一个选项
this.comboBox1.SelectedIndex = 0;
}
comboBox1_SelectedIndexChanged事件为:
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
//获取cboLayer中选中的图层
mFeatureLayer = mMapControl.get_Layer(comboBox1.SelectedIndex) as IFeatureLayer;
}
添加改变图层的空间参考的函数:
///
/// 改变图层的空间参考
///
/// 图层
/// 空间参考类型
private void ChangeLayerRef(IFeatureLayer pFeatureLayer, int gcsType)
{
try
{
IFeatureClass pFeatureClass = pFeatureLayer.FeatureClass;
//QI到IGeoDataset
IGeoDataset pGeoDataset = pFeatureClass as IGeoDataset;
//QI到IGeoDatasetSchemaEdit
IGeoDatasetSchemaEdit pGeoDatasetSchemaEdit = pGeoDataset as IGeoDatasetSchemaEdit;
if (pGeoDatasetSchemaEdit.CanAlterSpatialReference == true)
{
//创建SpatialReferenceEnvironmentClass对象
ISpatialReferenceFactory2 pSpaRefFactory = new SpatialReferenceEnvironmentClass();
//创建地理坐标系对象
IGeographicCoordinateSystem pNewGeoSys = pSpaRefFactory.CreateGeographicCoordinateSystem(gcsType);//4214代表Beijing1954
pGeoDatasetSchemaEdit.AlterSpatialReference(pNewGeoSys);
}
}
catch (Exception Err)
{
MessageBox.Show(Err.Message, "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
在转换坐标按钮下添加事件:
private void button1_Click(object sender, EventArgs e)
{
ChangeLayerRef(mFeatureLayer, 4214);
MessageBox.Show("转换成功!");
}
运行就能成功了!