基于ArcEngine显示网络图的流向功能并不难,难的是快速显示。这节博客的内容在目前几乎所有的ArcEngine二次开发书籍中都未曾提到过,但却是一个真正成熟的商业软件开发所必需具备的。我说的快速,是比常规方法提高93.33倍的效率。
这是我的第一个有关ArcEngine开发的博客,并不是我放弃QGis了,而是现有工作环境中,还没有办法完全拥抱QGis,尤其在需要协同开发的任务上。
最近接到一个代码优化的任务,代码是基于ArcEngine使用C#二次开发的,其中有一项很主要的功能是显示网络图的流向,就像摘要里面显示的图一样。
随便上网一搜,这方面的资源也还不少,其中大家比较推崇的是一本牟乃夏老师的《ArcGIS Engine地理信息系统开发教程–基于C#.NET》书。今天我要讲的方法,在效率上比书里的代码实例要提高93.33倍左右(实测:同一台机器,同一个数据库,运行书里的代码大约需要2分20秒,本文方法只需要1.5秒)。当然这个数字并不准确,但是却说明了效率的显著提升,并且随着数据量的增大,效率提升越明显。实际上,本文的方法几乎与ArcMap中的相关功能效率一致。同时,要说明的是,牟乃夏老师的那本书很不错,值得初学者借鉴,里面很多代码可以直接使用。
定义
显示网络流向标识,首先要做的就是建立一个网络拓扑结构,这个我就不展开了,假定大家都已经正确建立了一个网络数据集。然后,就是遍历网络中的每一个线要素,判断其流向。最后,再给它赋上正确的标识符就好了。过程其实蛮简单的,关键是如何优化效率的问题。
普遍的方法
先借用牟乃夏老师书里的方法来具体说明一下实现。他的方法是
- 遍历网络中所有线要素
- 判断当前线要素的流向
- 根据流向类型,在屏幕适当位置画上不同的标识符。其中,画箭头标识符时还要计算线段起点与终端之间的角度。
下面是整体过程的代码,必要的地方我在注释里面说明。
/// <summary>
/// 显示流向
/// </summary>
private void showDir()
{
try
{
// 得到当前几何网络所在的FeatureDataset
IFeatureLayer featureLayer = m_layer as IFeatureLayer;
IFeatureDataset featureDataset = featureLayer.FeatureClass.FeatureDataset;
// 得到接口转换获得当前所加载的几何网络
INetworkCollection2 networkCollection2 = featureDataset as INetworkCollection2;
IGeometricNetwork geometricNetwork = networkCollection2.get_GeometricNetwork(0);
// 获取当前的逻辑网络
INetwork network = geometricNetwork.Network;
IUtilityNetworkGEN utilityNetworkGEN = network as IUtilityNetworkGEN;
// 这个是关键方法
ShowFlowForFeatureClass(featureLayer.FeatureClass, utilityNetworkGEN);
// 下面的这个MapControl改成你代码中的地图控件变量名称
m_app.MapControl.ActiveView.PartialRefresh(esriViewDrawPhase.esriViewGraphics, null, m_app.MapControl.ActiveView.FullExtent);
m_app.MapControl.Refresh();
}
catch
{
System.Windows.Forms.MessageBox.Show("渲染出错");
return;
}
}
然后是 ShowFlowForFeatureClass 这个方法:
/// <summary>
/// 使用绘制的方式显示流向,速度非常慢
/// </summary>
/// <param name="featureClass">所需绘制的图层</param>
/// <param name="utilityNetworkGEN">当前几何网络对应的逻辑网络</param>
private void ShowFlowForFeatureClass(IFeatureClass featureClass, IUtilityNetworkGEN utilityNetworkGEN)
{
// 使用INetElements接口查询网络要素的ElementID
INetElements netElements = utilityNetworkGEN as INetElements;
// 定义相关变量
esriFlowDirection flowDirection = new esriFlowDirection();
int currentEID = -1;
// 对整个图层进行遍历,从而对每个边要素进行绘制
IFeatureCursor featureCursor = featureClass.Search(null, false);
int featureClassID = featureClass.FeatureClassID;
IFeature feature = featureCursor.NextFeature();
while (feature != null)
{
currentEID = netElements.GetEID(featureClassID, feature.OID, 0, esriElementType.esriETEdge);
// 使用IUtilityNetworkGEN接口查询每个网络边要素的流向
flowDirection = utilityNetworkGEN.GetFlowDirection(currentEID);
// 进行绘制
DrawArrowElementForEdgeElement(feature, m_app.MapControl.Map, flowDirection);
feature = featureCursor.NextFeature();
}
}
最后是 DrawArrowElementForEdgeElement 方法,以及它使用到的 GetLineAngleFrom2Points 方法: