GIS中的查询分析功能是非常重要的,本节将实现这些功能。
在Forms文件夹右击点击“添加”—>“Windows窗体”,添加两个窗体,分别用于空间查询和属性查询,参数设置如下表。
窗体名称(Name) |
Text属性 |
描述 |
SpatialQueryForm |
空间查询 |
用于空间查询参数设置 |
AttributeQueryForm |
属性查询 |
用于属性查询参数设置 |
同时在主窗体上的菜单项上添加一二级菜单。
查询(menuQuery)
……属性查询(menuAttributeQuery)
……空间查询(menuSpatialQuery)
实现属性查询,首先打开“属性查询”窗体的设计器。添加三个Label控件,两个ComboBox,两个Button和一个TextBox。各控件属性设置如下:
名称(Name) |
Text属性 |
描述 |
lblLayer |
选择图层: |
标签 |
lblField |
字段名称: |
标签 |
lblFind |
查找内容: |
标签 |
cboLayer |
MapControl中的图层名称 |
|
cboField |
cboLayer选中图层的所有字段名称 |
|
txtValue |
输入的查询对象名称 |
|
btnOk |
查找 |
查询按钮 |
btnCancel |
取消 |
取消查询按钮 |
界面效果如下:
进入窗体的代码编辑界面,首先添加三个引用:
using ESRI.ArcGIS.Controls;
using ESRI.ArcGIS.Carto;
using ESRI.ArcGIS.Geodatabase;
然后定义两个成员变量,一个用于存储地图数据,一个用于存储当前选中图层,如下
//地图数据
private AxMapControl mMapControl;
//选中图层
private IFeatureLayer mFeatureLayer;
然后修改其构造函数,构造函数中添加一个参数MapControl,用于获取MapControl中的数据,如下所示:
public AttributeQueryForm(AxMapControl mapControl)
{
InitializeComponent();
this.mMapControl = mapControl;
}
在窗体的Load事件中添加代码,用于初始化cboLayer,获取MapControl中的图层名称,如下:
//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.cboLayer.Items.Add(strLayerName);
}
//默认显示第一个选项
this.cboLayer.SelectedIndex = 0;
在CboLayer的SelectedIndexChanged事件中添加代码,当选中图层发生变化时,cboField中的字段名称重新获取,代码如下:
//获取cboLayer中选中的图层
mFeatureLayer = mMapControl.get_Layer(cboLayer.SelectedIndex) as IFeatureLayer;
IFeatureClass pFeatureClass = mFeatureLayer.FeatureClass;
//字段名称
string strFldName;
for (int i = 0; i < pFeatureClass.Fields.FieldCount;i++ )
{
strFldName = pFeatureClass.Fields.get_Field(i).Name;
//图层名称加入cboField
this.cboField.Items.Add(strFldName);
}
//默认显示第一个选项
this.cboField.SelectedIndex = 0;
查找按钮添加事件:
private void button1_Click(object sender, EventArgs e)
{
//定义图层,要素游标,查询过滤器,要素
IFeatureCursor pFeatureCursor;
IQueryFilter pQueryFilter;
IFeature pFeature;
IPoint pPoint;
IEnvelope pEnv;
pEnv = mMapControl.ActiveView.Extent;
pPoint = new PointClass();
pPoint.X = pEnv.XMin + pEnv.Width / 2;
pPoint.Y = pEnv.YMin + pEnv.Height / 2;
if (this.mMapControl.LayerCount <= 0)
return;
//获取图层
mFeatureLayer = mMapControl.get_Layer(cboLayer.SelectedIndex) as IFeatureLayer;
//清除上次查询结果
this.mMapControl.Map.ClearSelection();
this.mMapControl.ActiveView.Refresh();
//pQueryFilter的实例化
pQueryFilter = new QueryFilterClass();
//设置查询过滤条件
pQueryFilter.WhereClause = cboField.Text + "=" + txtValue.Text;
//查询
pFeatureCursor = mFeatureLayer.Search(pQueryFilter, true);
//获取查询到的要素
pFeature = pFeatureCursor.NextFeature();
//判断是否获取到要素
if (pFeature != null)
{
//选择要素
this.mMapControl.Map.SelectFeature(mFeatureLayer, pFeature);
//放大到要素
pFeature.Shape.Envelope.CenterAt(pPoint);
this.mMapControl.Extent = pFeature.Shape.Envelope;
}
else
{
//没有得到pFeature的提示
MessageBox.Show("没有找到相关要素!", "提示");
}
}
取消按钮添加事件:
private void btnCancel_Click(object sender, EventArgs e)
{
this.Close();
}
点击运行,这样就实现了属性查询的功能。运行效果如下图:
这一小结,我们进一步实现空间查询窗体的设计实现,我们的设想是通过该窗体选择查询的图层和查询的方式,然后将这两个参数传递给主窗体,主窗体实现查询,将查询得到的要素的属性显示在DataGridView控件中,下面开始动手吧。
首先打开“属性查询”窗体的设计器。添加两个Label控件,两个ComboBox,两个Button。各控件属性设置如下:
名称(Name) |
Text属性 |
描述 |
lblLayer |
选择图层: |
标签 |
lblMode |
查询方式: |
标签 |
cboLayer |
|
MapControl中的图层名称 |
cboMode |
|
空间查询的方式 |
btnOk |
确定 |
确定查询按钮 |
btnCancel |
取消 |
取消查询按钮 |
进入窗体的代码编辑界面,首先添加三个引用:
using ESRI.ArcGIS.Controls;
using ESRI.ArcGIS.Carto;
然后定义两个成员变量,一个用于存储地图数据,一个用于存储当前选中图层,如下
//获取主界面的MapControl对象
private AxMapControl mMapControl;
//查询方式
public int mQueryMode;
//图层索引
public int mLayerIndex;
然后修改其构造函数,构造函数中添加一个参数MapControl,用于获取MapControl中的数据,如下所示:
public SpatialQueryForm (AxMapControl mapControl)
{
InitializeComponent();
this.mMapControl = mapControl;
}
在窗体的Load事件中添加代码,用于初始化cboLayer,获取MapControl中的图层名称,并初始化查询方式,代码如下:
//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;
//图层名称加入ComboBox
this.cboLayer.Items.Add(strLayerName);
}
//加载查询方式
this.cboMode.Items.Add("矩形查询");
this.cboMode.Items.Add("线查询");
this.cboMode.Items.Add("点查询");
this.cboMode.Items.Add("圆查询");
//初始化ComboBox默认值
this.cboLayer.SelectedIndex = 0;
this.cboMode.SelectedIndex = 0;
在“确定”按钮添加代码如下:
//设置鼠标点击时窗体的结果
this.DialogResult = DialogResult.OK;
//判断是否存在图层
if (this.cboLayer.Items.Count <= 0)
{
MessageBox.Show("当前MapControl没有添加图层!","提示");
return;
}
//获取选中的查询方式和图层索引
this.mLayerIndex = this.cboLayer.SelectedIndex;
this.mQueryMode = this.cboMode.SelectedIndex;
这样我们就完成了空间查询窗体的设计。由于空间查询的结果需要借助于DataGridView进行显示,我们首先需要添加一个方法LoadQueryResult(AxMapControl mapControl, IFeatureLayer featureLayer, IGeometry geometry),用于获取空间查询得到的要素的属性。在这个方法的参数中,IGeometry是用于空间查询的几何对象,IFeatureLayer是查询要素所在的要素图层,AxMapControl是当前MapControl。我们使用DataTable来存储要素的属性,然后将DataTable中的数据添加到DataGridView进行显示。为了显示效果在主窗体上添加一个Panel控件,把DataGridView控件添加上去,并在Panel控件上添加一个“关闭”按钮。然后把Panel控件Visible属性设置为False。
在这个方法实现过程中,首先利用IFeatureClass的属性字段初始化DataTable,然后利用IGeometry对IFeatureLayer图层进行空间查询返回到要素游标IFeatureCursor中,然后逐个变量要素,将值添加到DataTable。代码如下:
private DataTable LoadQueryResult(AxMapControl mapControl, IFeatureLayer featureLayer, IGeometry geometry)
{
IFeatureClass pFeatureClass = featureLayer.FeatureClass;
//根据图层属性字段初始化DataTable
IFields pFields = pFeatureClass.Fields;
DataTable pDataTable = new DataTable();
for (int i = 0; i < pFields.FieldCount; i++)
{
string strFldName;
strFldName = pFields.get_Field(i).AliasName;
pDataTable.Columns.Add(strFldName);
}
//空间过滤器
ISpatialFilter pSpatialFilter = new SpatialFilterClass();
pSpatialFilter.Geometry = geometry;
//根据图层类型选择缓冲方式
switch (pFeatureClass.ShapeType)
{
case esriGeometryType.esriGeometryPoint:
pSpatialFilter.SpatialRel = esriSpatialRelEnum.esriSpatialRelContains;
break;
case esriGeometryType.esriGeometryPolyline:
pSpatialFilter.SpatialRel = esriSpatialRelEnum.esriSpatialRelCrosses;
break;
case esriGeometryType.esriGeometryPolygon:
pSpatialFilter.SpatialRel = esriSpatialRelEnum.esriSpatialRelIntersects;
break;
}
//定义空间过滤器的空间字段
pSpatialFilter.GeometryField = pFeatureClass.ShapeFieldName;
IQueryFilter pQueryFilter;
IFeatureCursor pFeatureCursor;
IFeature pFeature;
//利用要素过滤器查询要素
pQueryFilter = pSpatialFilter as IQueryFilter;
pFeatureCursor = featureLayer.Search(pQueryFilter, true);
pFeature = pFeatureCursor.NextFeature();
while (pFeature != null)
{
string strFldValue = null;
DataRow dr = pDataTable.NewRow();
//遍历图层属性表字段值,并加入pDataTable
for (int i = 0; i < pFields.FieldCount; i++)
{
string strFldName = pFields.get_Field(i).Name;
if (strFldName == "Shape")
{
strFldValue = Convert.ToString(pFeature.Shape.GeometryType);
}
else
strFldValue = Convert.ToString(pFeature.get_Value(i));
dr[i] = strFldValue;
}
pDataTable.Rows.Add(dr);
//高亮选择要素
mapControl.Map.SelectFeature((ILayer)featureLayer, pFeature);
mapControl.ActiveView.Refresh();
pFeature = pFeatureCursor.NextFeature();
}
return pDataTable;
}
定义两个成员变量,分别用于标记空间查询的查询方式和选中图层的索引(Index)。
//空间查询的查询方式
private int mQueryMode;
//图层索引
private int mLayerIndex;
然后单击菜单中的“查询”选项,选择“空间查询”,双击进入代码编辑界面,添加代码如下:
//初始化空间查询窗体
SpatialQueryForm spatialQueryForm = new SpatialQueryForm(this.axMapControl1);
if (spatialQueryForm.ShowDialog() == DialogResult.OK)
{
//标记为“空间查询”
this.mTool = "SpaceQuery";
//获取查询方式和图层
this.mQueryMode = spatialQueryForm.mQueryMode;
this.mLayerIndex = spatialQueryForm.mLayerIndex;
//定义鼠标形状
this.axMapControl1.MousePointer = ESRI.ArcGIS.Controls.esriControlsMousePointer.esriPointerCrosshair;
}
然后进入MapControl的OnMouseDown事件,添加代码如下:
this.axMapControl1.Map.ClearSelection();
//获取当前视图
IActiveView pActiveView = this.axMapControl1.ActiveView;
//获取鼠标点
IPoint pPoint = pActiveView.ScreenDisplay.DisplayTransformation.ToMapPoint(e.x, e.y);
switch (mTool)
{
case "ZoomIn":
this.mZoomIn.OnMouseDown(e.button, e.shift, e.x, e.y);
break;
case "ZoomOut":
this.mZoomOut.OnMouseDown(e.button, e.shift, e.x, e.y);
break;
case "Pan":
//设置鼠标形状
this.axMapControl1.MousePointer = esriControlsMousePointer.esriPointerPanning;
this.mPan.OnMouseDown(e.button, e.shift, e.x, e.y);
break;
case "SpaceQuery":
panel1.Visible = true;
IGeometry pGeometry = null;
if (this.mQueryMode == 0)//矩形查询
{
pGeometry = this.axMapControl1.TrackRectangle();
}
else if (this.mQueryMode == 1)//线查询
{
pGeometry = this.axMapControl1.TrackLine();
}
else if (this.mQueryMode == 2)//点查询
{
ITopologicalOperator pTopo;
IGeometry pBuffer;
pGeometry=pPoint;
pTopo = pGeometry as ITopologicalOperator;
//根据点位创建缓冲区,缓冲半径为0.1,可修改
pBuffer = pTopo.Buffer(0.1);
pGeometry = pBuffer.Envelope;
}
else if (this.mQueryMode == 3)//圆查询
{
pGeometry = this.axMapControl1.TrackCircle();
}
IFeatureLayer pFeatureLayer=this.axMapControl1.get_Layer(this.mLayerIndex) as IFeatureLayer;
DataTable pDataTable = this.LoadQueryResult(this.axMapControl1,pFeatureLayer, pGeometry);
this.dataGridView1.DataSource = pDataTable.DefaultView;
this.dataGridView1.Refresh();
break;
default:
break;
}
最后在“关闭”按钮函数下写入panel.Visible=false;至此,我们完成了空间查询代码的编写,运行效果如下:
我们在使用缓冲区分析时,需要设定原始的图层,缓冲半径以及生成缓冲区的保存路径。添加控件打开项目ZZUMap,在ZZUMap的主菜单添加一个新的菜单项“分析”,并添加子菜单“缓冲区分析”,Name属性修改为“menuBuffer”。
项目中添加一个新的窗体,名称为“BufferForm”,Name属性设为“缓冲区分析”,添加四个Label、一个ComboBox、两个TextBox、三个Button控件,控件属性设置如下:
控件类型 |
Name属性 |
Text属性 |
控件说明 |
Label |
|
选择图层: |
|
Label |
|
缓冲半径: |
|
Label |
lblUnit |
地图单位 |
标示当前地图的地图单位 |
Label |
|
输出图层: |
|
ComboBox |
cboLayers |
|
所有图层的名称 |
TextBox |
txtBufferDistance |
1.0 |
生成缓冲区的缓冲半径 |
TextBox |
txtOutputPath |
|
缓冲区文件的输出路径,其ReadOnly属性设为True |
Button |
btnOutputLayer |
… |
选择缓冲区文件的输出路径 |
Button |
btnBuffer |
分析 |
进行缓冲区分析 |
Button |
btnCancel |
取消 |
取消 |
该项目需添加如下引用:
using ESRI.ArcGIS.Controls;
using ESRI.ArcGIS.Geoprocessor;
using ESRI.ArcGIS.Carto;
using ESRI.ArcGIS.Geoprocessing;
using ESRI.ArcGIS.esriSystem;
首先声明两个成员变量,用于保存地图数据和输出文件的路径。
//接收MapControl中的数据
private IHookHelper mHookHelper = new HookHelperClass();
//缓冲区文件输出路径
public string strOutputPath;
重写BufferForm的构造函数,添加一个参数,用于接收MapControl中的数据。
//重写构造函数,添加参数hook,用于传入MapControl中的数据
public BufferForm(object hook)
{
InitializeComponent();
this.mHookHelper.Hook = hook;
}
添加一个自定义函数,用于根据图层名称获取要素图层并返回。
private IFeatureLayer GetFeatureLayer(string layerName)
{
IFeatureLayer pFeatureLayer = null;
//遍历图层,获取与名称匹配的图层
for (int i = 0; i < this.mHookHelper.FocusMap.LayerCount; i++)
{
ILayer pLayer = this.mHookHelper.FocusMap.get_Layer(i);
if (pLayer.Name == layerName)
{
pFeatureLayer = pLayer as IFeatureLayer;
}
}
if (pFeatureLayer != null)
return pFeatureLayer;
else
return null;
}
BufferForm在载入时需要加载当前MapControl中的图层名称到cboLayers,读取当前地图的地图单位,设置缓冲区文件的默认输出路径,这里我们将默认输出路径设为“D:\Temp\”。
private void BufferForm_Load(object sender, EventArgs e)
{
//传入数据为空时返回
if (null == mHookHelper || null == mHookHelper.Hook || 0 == mHookHelper.FocusMap.LayerCount)
return;
//获取图层名称并加入cboLayers
for (int i = 0; i < this.mHookHelper.FocusMap.LayerCount; i++)
{
ILayer pLayer = this.mHookHelper.FocusMap.get_Layer(i);
cboLayers.Items.Add(pLayer.Name);
}
//cboLayers控件中默认显示第一个选项
if (cboLayers.Items.Count > 0)
cboLayers.SelectedIndex = 0;
//设置生成文件的默认输出路径和名称
string tempDir = @"D:\Temp\";
txtOutputPath.Text = System.IO.Path.Combine(tempDir, ((string)cboLayers.SelectedItem + "_buffer.shp"));
//设置默认地图单位
lblUnits.Text = Convert.ToString(mHookHelper.FocusMap.MapUnits);
}
双击路径设置按钮,进入代码编辑界面,添加如下代码:
private void btnOutputLayer_Click(object sender, EventArgs e)
{
//定义输出文件路径
SaveFileDialog saveDlg = new SaveFileDialog();
//检查路径是否存在
saveDlg.CheckPathExists = true;
saveDlg.Filter = "Shapefile (*.shp)|*.shp";
//保存时覆盖同名文件
saveDlg.OverwritePrompt = true;
saveDlg.Title = "输出路径";
//对话框关闭前还原当前目录
saveDlg.RestoreDirectory = true;
saveDlg.FileName = (string)cboLayers.SelectedItem + "_buffer.shp";
//读取文件输出路径到txtOutputPath
DialogResult dr = saveDlg.ShowDialog();
if (dr == DialogResult.OK)
txtOutputPath.Text = saveDlg.FileName;
}
双击“分析”按钮,添加代码如下:
private void btnBuffer_Click(object sender, EventArgs e)
{
//缓冲距离
double bufferDistance;
//输入的缓冲距离转换为double
double.TryParse(txtBufferDistance.Text.ToString(),out bufferDistance);
//判断输出路径是否合法
if (!System.IO.Directory.Exists(System.IO.Path.GetDirectoryName(txtOutputPath.Text)) ||
".shp" != System.IO.Path.GetExtension(txtOutputPath.Text))
{
MessageBox.Show("输出路径错误!");
return;
}
//判断图层个数
if (mHookHelper.FocusMap.LayerCount == 0)
return;
//获取图层
IFeatureLayer pFeatureLayer = GetFeatureLayer((string)cboLayers.SelectedItem);
if (null == pFeatureLayer)
{
MessageBox.Show("图层" + (string)cboLayers.SelectedItem + "不存在!\r\n");
return;
}
//获取一个geoprocessor的实例
Geoprocessor gp = new Geoprocessor();
//OverwriteOutput为真时,输出图层会覆盖当前文件夹下的同名图层
gp.OverwriteOutput = true;
//缓冲区保存路径
strOutputPath = txtOutputPath.Text;
//创建一个Buffer工具的实例
ESRI.ArcGIS.AnalysisTools.Buffer buffer = new ESRI.ArcGIS.AnalysisTools.Buffer(pFeatureLayer, strOutputPath, bufferDistance.ToString());
//执行缓冲区分析
IGeoProcessorResult results = null;
results = (IGeoProcessorResult)gp.Execute(buffer, null);
//判断缓冲区是否成功生成
if (results.Status != esriJobStatus.esriJobSucceeded)
MessageBox.Show("图层" + pFeatureLayer.Name + "缓冲区生成失败!");
else
{
this.DialogResult = DialogResult.OK;
MessageBox.Show("缓冲区生成成功!");
}
}
双击“取消”按钮,添加代码如下:
private void btnCancel_Click(object sender, EventArgs e)
{
this.Dispose();
}
进入ZZUMap的主窗体,双击菜单中的“缓冲区分析”,添加代码如下:
BufferForm bufferForm = new BufferForm(this.axMapControl1.Object);
if (bufferForm.ShowDialog() == DialogResult.OK)
{
//获取输出文件路径
string strBufferPath = bufferForm.strOutputPath;
//缓冲区图层载入到MapControl
int index = strBufferPath.LastIndexOf("\\");
this.axMapControl1.AddShapeFile(strBufferPath.Substring(0, index), strBufferPath.Substring(index));
}
至此,代码编辑完成,运行程序,添加数据选择图层,设置缓冲区半径,点击“分析”,效果如下图所示。
这一小节我们将实现叠置分析中三种最常用的叠置方式,Union(叠置求并)、Intersect(叠置求交)和Identify(叠置标识)。Intersect(叠置求交)在上节已经介绍,下面简要介绍一下Union(叠置求并)和Identify(叠置标识)。
叠置求并(Union)保留了两个叠置图层的空间图形和属性信息,进行叠置求和的两个图层须是多边形图层。输入图层的一个多边形被叠加图层中的多边形弧段分割成多个多边形,输出图层综合了两个图层的属性。所有要素都将被写入到输出要素类,输出结果具有来自与其叠置的输入要素的属性。
Identify(叠置标识)是以输入图层为界,保留边界以内两个多变形的所有多边形,出入图层切割后的多边形也被赋予叠加图层的属性。如下图所示。
在通过ArcEngine中的Geoprocessor实现这三种叠置分析时,我们将实现输入图层和叠置图层的可选设置,叠置方式的可选设置,输出路径的可选设置。
打开项目在主菜单 “分析”中添加子菜单“叠置分析”,Name属性修改为“menuOverlay”。项目中添加一个新的窗体,名称为“OverlayForm”,Name属性设为“叠置分析”,添加四个Label、一个ComboBox、四个TextBox、五个Button控件和一个GroupBox,控件属性设置如下:
控件类型 |
Name属性 |
Text属性 |
Readonly属性 |
控件说明 |
Label |
|
输入要素: |
|
|
Label |
|
叠置要素: |
|
|
Label |
|
叠置方式: |
|
|
Label |
|
输出图层: |
|
|
TextBox |
txtInputFeat |
|
True |
保存输入要素路径 |
TextBox |
txtOverlayFeat |
|
True |
保存叠置要素路径 |
TextBox |
txtOutputPath |
|
True |
叠置结果的输出路径 |
TextBox |
txtMessage |
|
True |
叠置分析处理过程消息,Multiline属性设为True,ScrollBars属性设为Vertical Dock属性设为Fill |
ComboBox |
cboOverLay |
|
|
叠置分析的方式 |
Button |
btnInputFeat |
… |
|
选择输入要素 |
Button |
btnOverlayFeat |
… |
|
选择叠置要素 |
Button |
btnOutputLayer |
… |
|
选择叠置分析结果的输出路径 |
Button |
btnBuffer |
分析 |
|
进行叠置分析 |
Button |
btnCancel |
取消 |
|
取消 |
GroupBox |
|
处理过程消息 |
|
作为txtMessage的容器 |
该工程需要添加如下引用:
using ESRI.ArcGIS.Controls;
using ESRI.ArcGIS.AnalysisTools;
using ESRI.ArcGIS.Geoprocessing;
首先声明一个成员变量,用于保存叠置分析输出文件的路径。
public string strOutputPath;
OverlayForm在载入时需要加载三种叠置方式到cboOverlay中,并且需要设置缓冲区文件的默认输出路径,这里我们将默认输出路径设为“D:\Temp\”。
private void OverlayForm_Load(object sender, EventArgs e)
{
//加载叠置方式
this.cboOverLay.Items.Add("求交(Intersect)");
this.cboOverLay.Items.Add("求并(Union)");
this.cboOverLay.Items.Add("标识(Identity)");
this.cboOverLay.SelectedIndex = 0;
//设置默认输出路径
string tempDir = @"D:\Temp\";
txtOutputPath.Text = tempDir;
}
双击输入要素的文件选择按钮,进入代码编辑界面,添加代码如下:
private void btnInputFeat_Click(object sender, EventArgs e)
{
//定义OpenfileDialog
OpenFileDialog openDlg = new OpenFileDialog();
openDlg.Filter = "Shapefile (*.shp)|*.shp";
openDlg.Title = "选择第一个要素";
//检验文件和路径是否存在
openDlg.CheckFileExists=true;
openDlg.CheckPathExists=true;
//初试化初试打开路径
openDlg.InitialDirectory = @"D:\Temp\";
//读取文件路径到txtFeature1中
if (openDlg.ShowDialog()==DialogResult.OK)
{
this.txtInputFeat.Text=openDlg.FileName;
}
}
类似的,双击叠置要素的文件选择按钮,添加代码如下;
private void btnOverlayFeat_Click(object sender, EventArgs e)
{
//定义OpenfileDialog
OpenFileDialog openDlg = new OpenFileDialog();
openDlg.Filter = "Shapefile (*.shp)|*.shp";
openDlg.Title = "选择第二个要素";
//检验文件和路径是否存在
openDlg.CheckFileExists = true;
openDlg.CheckPathExists = true;
//初试化初试打开路径
openDlg.InitialDirectory = @"D:\Temp\";
//读取文件路径到txtFeature2中
if (openDlg.ShowDialog() == DialogResult.OK)
{
this.txtOverlayFeat.Text = openDlg.FileName;
}
}
双击输出图层的路径选择按钮,添加代码如下:
private void btnOutputLayer_Click(object sender, EventArgs e)
{
//定义输出文件路径
SaveFileDialog saveDlg = new SaveFileDialog();
//检查路径是否存在
saveDlg.CheckPathExists = true;
saveDlg.Filter = "Shapefile (*.shp)|*.shp";
//保存时覆盖同名文件
saveDlg.OverwritePrompt = true;
saveDlg.Title = "输出路径";
//对话框关闭前还原当前目录
saveDlg.RestoreDirectory = true;
saveDlg.FileName = (string)cboOverLay.SelectedItem + ".shp";
//读取文件输出路径到txtOutputPath
DialogResult dr = saveDlg.ShowDialog();
if (dr == DialogResult.OK)
txtOutputPath.Text = saveDlg.FileName;
}
双击“分析”按钮,添加代码如下。
private void btnOverLay_Click(object sender, EventArgs e)
{
//判断是否选择要素
if (this.txtInputFeat.Text==""||this.txtInputFeat.Text==null||
this.txtOverlayFeat.Text==""||this.txtOverlayFeat.Text==null)
{
txtMessage.Text="请设置叠置要素!";
return;
}
ESRI.ArcGIS.Geoprocessor.Geoprocessor gp = new ESRI.ArcGIS.Geoprocessor.Geoprocessor();
//OverwriteOutput为真时,输出图层会覆盖当前文件夹下的同名图层
gp.OverwriteOutput = true;
//设置参与叠置分析的多个对象
object inputFeat = this.txtInputFeat.Text;
object overlayFeat = this.txtOverlayFeat.Text;
IGpValueTableObject pObject = new GpValueTableObjectClass();
pObject.SetColumns(2);
pObject.AddRow(ref inputFeat);
pObject.AddRow(ref overlayFeat);
//获取要素名称
string str = System.IO.Path.GetFileName(this.txtInputFeat.Text);
int index = str.LastIndexOf(".");
string strName = str.Remove(index);
//设置输出路径
strOutputPath = txtOutputPath.Text;
//叠置分析结果
IGeoProcessorResult result = null;
//创建叠置分析实例,执行叠置分析
string strOverlay=cboOverLay.SelectedItem.ToString();
try
{
//添加处理过程消息
txtMessage.Text = "开始叠置分析……"+"\r\n";
switch (strOverlay)
{
case "求交(Intersect)":
Intersect intersectTool = new Intersect();
//设置输入要素
intersectTool.in_features = pObject;
//设置输出路径
strOutputPath += strName + "_" + "_intersect.shp";
intersectTool.out_feature_class = strOutputPath;
//执行求交运算
result = gp.Execute(intersectTool, null) as IGeoProcessorResult;
break;
case "求并(Union)":
Union unionTool = new Union();
//设置输入要素
unionTool.in_features = pObject;
//设置输出路径
strOutputPath += strName + "_" + "_union.shp";
unionTool.out_feature_class = strOutputPath;
//执行联合运算
result = gp.Execute(unionTool, null) as IGeoProcessorResult;
break;
case "标识(Identity)":
Identity identityTool = new Identity();
//设置输入要素
identityTool.in_features = inputFeat;
identityTool.identity_features = overlayFeat;
//设置输出路径
strOutputPath += strName + "_" + "_identity.shp";
identityTool.out_feature_class = strOutputPath;
//执行标识运算
result = gp.Execute(identityTool, null) as IGeoProcessorResult;
break;
}
}
catch (System.Exception ex)
{
//添加处理过程消息
txtMessage.Text += "叠置分析过程出现错误:" + ex.Message+"\r\n";
}
//判断叠置分析是否成功
if (result.Status != ESRI.ArcGIS.esriSystem.esriJobStatus.esriJobSucceeded)
txtMessage.Text+="叠置失败!";
else
{
this.DialogResult = DialogResult.OK;
txtMessage.Text += "叠置成功!";
}
}
细心的同学可能会发现,Union和Intersect设置输入要素和叠置要素的方式是一致的,它们是将两种要素读入到IGpValueTableObject中,然后赋值给in_features,而Identity工具是针对in_features和 identity_features分别赋值。因为在ArcGIS的叠置分析中Union和Intersect两种工具可以针对两个以上的图层进行叠置运算,而Identity工具是针对两个要素的运算,其实质是使用叠置要素对输入要素进行更新的一个过程。另外,Identity工具需要本机中具有ArcInfo级别的Licence权限,如果你的当前电脑没有安装ArcInfo,请在实现的过程中将Identity的相关代码进行屏蔽,如果装有ArcInfo,在运行程序之前,首先需要打开ArcGIS LicenceManager的服务。我们通过以下方式设置Licence权限。进入到ZZUMap的MainForm窗体的设计器界面,右键单击LicenceControl,选择菜单中的“属性”选项。选择Products中的ArcInfo选项。
最后在主菜单的“叠置分析”按钮下添加函数:
private void 叠置分析ToolStripMenuItem_Click(object sender, EventArgs e)
{
menuOverlay menuoverlay = new menuOverlay();
//menuoverlay.ShowDialog();
if (menuoverlay.ShowDialog() == DialogResult.OK)
{
//获取输出文件路径
string strBufferPath = menuoverlay.strOutputPath;
//缓冲区图层载入到MapControl
int index = strBufferPath.LastIndexOf("\\");
this.axMapControl1.AddShapeFile(strBufferPath.Substring(0, index), strBufferPath.Substring(index));
}
}
运行程序,点击菜单“叠置分析”,弹出叠置分析参数设置窗口,添加叠置分析要素文件,并设置输出路径如下。
点击分析,可得到结果。
本节我们系统讲解了叠置分析功能的开发并进一步完善了ZZUMap系统。当然,我们的工程中的叠置分析的功能还存在一些不足之处,比如叠置求交和叠置联合是针对两个或以上要素类进行的操作,我们这里仅实现了两个要素类的操作。如果您有兴趣,可以自己动手进行扩展。
我们下面动手实现一个简单的最短路径分析。用户通过在MapControl点击选择路径经过的节点,双击完成节点的选择,然后最短路径会以红色线条显示到MapControl中。添加如下引用。
using ESRI.ArcGIS.Geodatabase;
using ESRI.ArcGIS.DataSourcesGDB;
using ESRI.ArcGIS.Carto;
using ESRI.ArcGIS.Geometry;
using ESRI.ArcGIS.NetworkAnalysis;
using ESRI.ArcGIS.Display;
添加如下成员变量。
//几何网络
private IGeometricNetwork mGeometricNetwork;
//给定点的集合
private IPointCollection mPointCollection;
//获取给定点最近的Network元素
private IPointToEID mPointToEID;
//返回结果变量
private IEnumNetEID mEnumNetEID_Junctions;
private IEnumNetEID mEnumNetEID_Edges;
private double mdblPathCost;
编写一个自定义函数,用于实现路径分析。在ArcEngine中,使用ITraceFlowSolverGEN接口实现路径分析计算,通过查询接口INetSolver获取几何网络的Network,使用INetFlag获取路径求解的边(PutEdgeOrigins)或点(PutJunctionOrigins),最后使用该接口的FindPath方法执行路径分析。实现代码如下:
private void SolvePath(string weightName)
{
//创建ITraceFlowSolverGEN
ITraceFlowSolverGEN pTraceFlowSolverGEN = new TraceFlowSolverClass();
INetSolver pNetSolver = pTraceFlowSolverGEN as INetSolver;
//初始化用于路径计算的Network
INetwork pNetWork = mGeometricNetwork.Network;
pNetSolver.SourceNetwork = pNetWork;
//获取分析经过的点的个数
int intCount = mPointCollection.PointCount;
if (intCount < 1)
return;
INetFlag pNetFlag;
//用于存储路径计算得到的边
IEdgeFlag[] pEdgeFlags = new IEdgeFlag[intCount];
IPoint pEdgePoint = new PointClass();
int intEdgeEID;
IPoint pFoundEdgePoint;
double dblEdgePercent;
//用于获取几何网络元素的UserID, UserClassID,UserSubID
INetElements pNetElements = pNetWork as INetElements;
int intEdgeUserClassID;
int intEdgeUserID;
int intEdgeUserSubID;
for (int i = 0; i < intCount; i++)
{
pNetFlag = new EdgeFlagClass();
//获取用户点击点
pEdgePoint = mPointCollection.get_Point(i);
//获取距离用户点击点最近的边
mPointToEID.GetNearestEdge(pEdgePoint, out intEdgeEID, out pFoundEdgePoint, out dblEdgePercent);
if (intEdgeEID <= 0)
continue;
//根据得到的边查询对应的几何网络中的元素UserID, UserClassID,UserSubID
pNetElements.QueryIDs(intEdgeEID, esriElementType.esriETEdge,
out intEdgeUserClassID, out intEdgeUserID, out intEdgeUserSubID);
if (intEdgeUserClassID <= 0 || intEdgeUserID <= 0)
continue;
pNetFlag.UserClassID = intEdgeUserClassID;
pNetFlag.UserID = intEdgeUserID;
pNetFlag.UserSubID = intEdgeUserSubID;
pEdgeFlags[i] = pNetFlag as IEdgeFlag;
}
//设置路径求解的边
pTraceFlowSolverGEN.PutEdgeOrigins(ref pEdgeFlags);
//路径计算权重
INetSchema pNetSchema = pNetWork as INetSchema;
INetWeight pNetWeight = pNetSchema.get_WeightByName(weightName);
if (pNetWeight == null)
return;
//设置权重,这里双向的权重设为一致
INetSolverWeights pNetSolverWeights = pTraceFlowSolverGEN as INetSolverWeights;
pNetSolverWeights.ToFromEdgeWeight = pNetWeight;
pNetSolverWeights.FromToEdgeWeight = pNetWeight;
object[] arrResults = new object[intCount - 1];
//执行路径计算
pTraceFlowSolverGEN.FindPath(esriFlowMethod.esriFMConnected, esriShortestPathObjFn.esriSPObjFnMinSum,
out mEnumNetEID_Junctions, out mEnumNetEID_Edges, intCount - 1, ref arrResults);
//获取路径计算总代价(cost)
mdblPathCost = 0;
for (int i = 0; i < intCount - 1; i++)
mdblPathCost += (double)arrResults[i];
}
编写自定义函数,实现路径分析结果到几何要素的转换,用于地图的显示。主要通过IEIDHelper接口完成转换,首先使用该接口获取几何网络和空间参考,然后查询组成路径的几何要素的信息返回到接口IEnumEIDInfo中,最后通过IEnumEIDInfo接口获取要素的几何要素。代码如下:
private IPolyline PathToPolyLine()
{
IPolyline pPolyLine = new PolylineClass();
IGeometryCollection pNewGeometryCollection = pPolyLine as IGeometryCollection;
if (mEnumNetEID_Edges == null)
return null;
IEIDHelper pEIDHelper = new EIDHelperClass();
//获取几何网络
pEIDHelper.GeometricNetwork = mGeometricNetwork;
//获取地图空间参考
ISpatialReference pSpatialReference = this.axMapControl1.Map.SpatialReference;
pEIDHelper.OutputSpatialReference = pSpatialReference;
pEIDHelper.ReturnGeometries = true;
//根据边的ID获取边的信息
IEnumEIDInfo pEnumEIDInfo = pEIDHelper.CreateEnumEIDInfo(mEnumNetEID_Edges);
int intCount = pEnumEIDInfo.Count;
pEnumEIDInfo.Reset();
IEIDInfo pEIDInfo;
IGeometry pGeometry;
for (int i = 0; i < intCount;i++ )
{
pEIDInfo = pEnumEIDInfo.Next();
//获取边的几何要素
pGeometry = pEIDInfo.Geometry;
pNewGeometryCollection.AddGeometryCollection((IGeometryCollection)pGeometry);
}
return pPolyLine;
}
下面开始网络分析的实现。首先是网络数据的读取与加载,我们在“网络分析”菜单按钮事件中实现。此处直接加载我们在上一节自己生成的几何网络数据,首先获取工作空间“USA_Highway_Network_GDB.mdb”,然后获取其中的要素数据集合“high”,进而得到其中的几何网络数据“high_net”,使用接口IGeometricNetwork获取。然后使用IFeatureClassContainer接口获取几何网络中的要素类,添加到MapControl中。值得注意的是,路径分析中,IPointToEID的源地图和几何网络以及容差在此完成设置。代码如下:
private void 网络分析ToolStripMenuItem_Click(object sender, EventArgs e)
{
//获取几何网络文件路径
//注意修改此路径为当前存储路径
string strPath = @"E:\GIS设计与开发\例子数据\Network\USA_Highway_Network_GDB.mdb";
//打开工作空间
IWorkspaceFactory pWorkspaceFactory = new AccessWorkspaceFactory();
IFeatureWorkspace pFeatureWorkspace = pWorkspaceFactory.OpenFromFile(strPath, 0) as IFeatureWorkspace;
//获取要素数据集
//注意名称的设置要与上面创建保持一致
IFeatureDataset pFeatureDataset = pFeatureWorkspace.OpenFeatureDataset("high");
//获取network集合
INetworkCollection pNetWorkCollection = pFeatureDataset as INetworkCollection;
//获取network的数量,为零时返回
int intNetworkCount = pNetWorkCollection.GeometricNetworkCount;
if (intNetworkCount < 1)
return;
//FeatureDataset可能包含多个network,我们获取指定的network
//注意network的名称的设置要与上面创建保持一致
mGeometricNetwork = pNetWorkCollection.get_GeometricNetworkByName("high_net");
//将Network中的每个要素类作为一个图层加入地图控件
IFeatureClassContainer pFeatClsContainer = mGeometricNetwork as IFeatureClassContainer;
//获取要素类数量,为零时返回
int intFeatClsCount=pFeatClsContainer.ClassCount;
if (intFeatClsCount < 1)
return;
IFeatureClass pFeatureClass;
IFeatureLayer pFeatureLayer;
for (int i = 0; i < intFeatClsCount;i++ )
{
//获取要素类
pFeatureClass = pFeatClsContainer.get_Class(i);
pFeatureLayer = new FeatureLayerClass();
pFeatureLayer.FeatureClass = pFeatureClass;
pFeatureLayer.Name = pFeatureClass.AliasName;
//加入地图控件
this.axMapControl1.AddLayer((ILayer)pFeatureLayer, 0);
}
//计算snap tolerance为图层最大宽度的1/100
//获取图层数量
int intLayerCount=this.axMapControl1.LayerCount;
IGeoDataset pGeoDataset;
IEnvelope pMaxEnvelope=new EnvelopeClass();
for (int i = 0; i < intLayerCount;i++ )
{
//获取图层
pFeatureLayer = this.axMapControl1.get_Layer(i) as IFeatureLayer;
pGeoDataset = pFeatureLayer as IGeoDataset;
//通过Union获得较大图层范围
pMaxEnvelope.Union(pGeoDataset.Extent);
}
double dblWidth = pMaxEnvelope.Width;
double dblHeight = pMaxEnvelope.Height;
double dblSnapTol;
if (dblHeight < dblWidth)
dblSnapTol = dblWidth * 0.01;
else
dblSnapTol = dblHeight * 0.01;
//设置源地图,几何网络以及捕捉容差
mPointToEID = new PointToEIDClass();
mPointToEID.SourceMap = this.axMapControl1.Map;
mPointToEID.GeometricNetwork = mGeometricNetwork;
mPointToEID.SnapTolerance = dblSnapTol;
}
下面添加MapControl的OnMouseDown事件中 switch (mTool)代码下,完成用户单击时的响应。用户单击时获取用户单击的点,并存储到点集中注意在“网络分析”菜单按钮事件中添加代码mTool = "Network";。
case "Network":
//记录鼠标点击的点
IPoint pNewPoint = new PointClass();
pNewPoint.PutCoords(e.x, e.y);
if (mPointCollection == null)
mPointCollection = new MultipointClass();
//添加点,before和after标记添加点的索引,这里不定义
object before = Type.Missing;
object after = Type.Missing;
mPointCollection.AddPoint(pNewPoint, ref before, ref after);
break;
最后添加,MapControl的OnDoubleClick事件的响应,完成路径分析,并将所得路径转换为几何要素绘制到屏幕上。代码如下:
private void axMapControl1_OnDoubleClick(object sender, ESRI.ArcGIS.Controls.IMapControlEvents2_OnDoubleClickEvent e)
{
try
{
//路径计算
//注意权重名称与设置保持一致
SolvePath("LENGTH");
//路径转换为几何要素
IPolyline pPolyLineResult = PathToPolyLine();
//获取屏幕显示
IActiveView pActiveView = this.axMapControl1.ActiveView;
IScreenDisplay pScreenDisplay = pActiveView.ScreenDisplay;
//设置显示符号
ILineSymbol pLineSymbol = new CartographicLineSymbolClass();
IRgbColor pColor = new RgbColorClass();
pColor.Red = 255;
pColor.Green = 0;
pColor.Blue = 0;
//设置线宽
pLineSymbol.Width =4;
//设置颜色
pLineSymbol.Color = pColor as IColor;
//绘制线型符号
pScreenDisplay.StartDrawing(0, 0);
pScreenDisplay.SetSymbol((ISymbol)pLineSymbol);
pScreenDisplay.DrawPolyline(pPolyLineResult);
pScreenDisplay.FinishDrawing();
}
catch (System.Exception ex)
{
MessageBox.Show("路径分析出现错误:" + "\r\n" + ex.Message);
}
//点集设为空
mPointCollection = null;
}
至此,我们已经完成了路径分析的功能,编译程序并运行,通过鼠标单击选择若干网络节点,最后通过双击完成节点选择。生成结果如图所示。