創建網絡數據集就得有各種數據和參數,這篇文章很長,慎入。
網絡分析依賴於網絡數據集的質量,這句話就在這里得到了驗證:復雜、精確定義。
本節目錄如下:
- 1. INetworkDataset與IDENetworkDataset對比
- 1.1 什么是INetworkDataset
- 1.2 兩者對比
- 2. 如何設置數據元素網絡數據集(IDENetworkDataset)的屬性以創建網絡數據集
- 2.1 涉及的接口、類、枚舉
- 2.2 創建數據元素網絡數據集(IDENetworkDataset)對象
- 2.3 添加網絡源
- 2.4 添加網絡屬性
- 2.5 添加導航/方向
- 2.6 為數據元素網絡數據集賦值並構建網絡數據集(INetworkDataset)
1. INetworkDataset與IDENetworkDataset的對比
挑簡單的先說,INetworkDataset與IDENetworkDataset的對比。
1.1 先說說INetworkDataset是個什么東西
網絡數據集是一個擁有網絡關系的要素類的容器。每個要素類都有自己的拓撲規則,每個網絡有可能有多個同樣拓撲規則的要素類。一個要素數據集可能有多個網絡數據集,但是一個要素類只能屬於一個網絡數據集或一個幾何網絡。一個屬於網絡數據集的要素類被稱為:網絡數據源,網絡數據集還擁有多個網絡屬性,這些屬性被用作解決網絡分析問題。
IDatasetContainer2接口用於創建或打開網絡數據集。INetworkBuild接口用作添加或刪除一個網絡數據集中的網絡數據源、網絡屬性,或者被用於構建網絡數據集。
再上一張INetworkDataset的屬性圖:
這些屬性全部都是只允許訪問的(都是get屬性)。
INetworkDataset更合適在分析部分解釋,它與INAContext有關。
1.2 二者對比
很容易與上一節的IDENetworkDataset做出對比,INetworkDataset更專注於處理與屬性、數據源的存取,而IDENetworkDataset更專注於數據的組織。
后者是數據的集合,是真正的網絡數據源、網絡屬性等的容器,而前者更合適稱為“分析對象”,它專注於網絡屬性和網絡數據源的訪問。
因為后者名中的“DE”就是DataElement的簡稱,所以IDENetworkDataset是“數據元素網絡數據集”。
2. 如何設置數據元素網絡數據集(IDENetworkDataset)的屬性以創建網絡數據集
再貼一張IDENetworkDataset的屬性圖(就上篇文章):
重點需要設置的屬性是:Attributes、Directions、Sources
這對應了桌面創建網絡數據集的三個重要步驟:網絡屬性、導航設置、網絡數據源。
其中網絡數據源又可分為三種:線要素、點要素、轉彎要素。
其他需要注意的屬性是:Buildable;
2.1 涉及的接口、類、枚舉
在接下來的介紹中,會用到的核心接口和類、枚舉先列出:
涉及的接口:共計18個
IDENetworkDataset、INetworkDataset、INetworkSource、INetworkAttribute、INetworkDirection、IEvaluatedNetworkAttribute、
INetworkSourceDirections、IStreetNameFields、IEdgeFeatureSource、INetworkFieldEvaluator、INetworkEvaluator、INetworkConstantEvaluator
IArray
INetworkBuild、IDEDataset、IDatasetContainer、IFeatureDatasetExtension、IFeatureDatasetExtensionContainer
涉及到的類:共計9個
DENetworkDatasetClass、StreetNameFieldsClass、NetworkSourceDirectionsClass
TurnFeatureSourceClass、EdgeFeatureSourceClass(INetworkSource的實現類)
EvaluatedNetworkAttributeClass、NetworkFieldEvaluatorClass、NetworkConstantEvaluatorClass
ArrayClass
涉及到的枚舉:共計6個
esriNetworkElementType、esriNetworkAttributeUnits、esriNetworkEdgeDirection、esriNetworkAttributeDataType、esriNetworkAttributeUsageType、esriNetworkEdgeConnectivityPolicy
別害怕,我會逐一解釋這些類對應桌面創建網絡數據集時,分別是什么。
2.2 創建一個IDENetworkDataset對象
為了創建一個裝着網絡數據集所有素材的“數據元素網絡數據集”,我們需要的東西是:一個IFeatureDataset(即桌面上的要素數據集)對象,網絡數據集的名稱。
我們創建一個這樣的方法:
public IDENetworkDataset CreateDENetworkDataset(IFeatureDataset featureDataset, string networkName) {
IDENetworkDataset deNetworkDataset = new DENetworkDatasetClass();
// ...設置數據要素網絡數據集的必須參數
return deNetworkDataset;
}
注意,這個時候並不需要這個要素數據集中有要素數據。而在桌面軟件中基於要素數據集創建網絡數據集,是要求要素數據集中存在最基本的點線要素的。
那是因為,在AO中,要創建數據元素網絡數據集,只需要獲取IFeatureDataset即可,至於網絡數據集中的點、線、轉彎,則是下一步添加Sources(網絡數據源)的事情。
我直接給出數據元素網絡數據集必須設置的屬性,和分別來自哪些接口:
從上圖可以看出為了創建DENetworkDataset這個類的實例,默認使用IDENetworkDataset接口來定義變量。
需要給的默認屬性有:
IDENetworkDataset接口下的Buildable屬性、NetworkType屬性
IDEGeoDataset接口下的Extent屬性、SpatialReference屬性
IDataElement接口下的Name屬性
其中,Buildable設置為true,表示可以構建;
NetworkType設置為枚舉值esriNetworkDatasetType.esriNDTGeodatabase,表示是基於數據庫的網絡數據集;
Extent和SpatialReference屬性表示網絡數據集的地理外接矩形和空間參考系,可以從傳入的要素數據集的父級接口IGeoDataset中獲取。
Name表示網絡數據集的名稱,由傳入參數給定。
完整的方法如下:

/// <summary> /// 創建IDENetworkDataset(數據元素網絡數據集)對象 /// </summary> /// <param name="featureDataset">傳入:要素數據集</param> /// <param name="NetworkName">傳入:網絡數據集名稱</param> /// <returns>返回:數據元素網絡數據集</returns> public IDENetworkDataset CreateDENetworkDataset(IFeatureDataset featureDataset, string NetworkName) { //判斷傳入參數是否為空 if (string.IsNullOrEmpty(NetworkName) || null == featureDataset) { return null; } // 若傳入參數不為空,實例化數據元素網絡數據集對象 IDENetworkDataset deNetworkDataset = new DENetworkDatasetClass(); // 設置數據集類型、可以被構建 deNetworkDataset.Buildable = true; deNetworkDataset.NetworkType = esriNetworkDatasetType.esriNDTGeodatabase; // 設置數據集的空間參考、空間范圍 IDEGeoDataset deGeoDataset = deNetworkDataset as IDEGeoDataset; IGeoDataset geoDataset = featureDataset as IGeoDataset; deGeoDataset.Extent = geoDataset.Extent; deGeoDataset.SpatialReference = geoDataset.SpatialReference; // 設置名稱 IDataElement dataElement = deNetworkDataset as IDataElement; dataElement.Name = NetworkName; return deNetworkDataset; }
可以直接封裝在一個類里。
2.3 添加Sources屬性(網絡數據源)——添加邊線與轉彎
涉及到的接口:INetworkSource、IEdgeFeatureSource、IJunctionFeatureSource、ITurnFeatureSource、IArray
涉及到的類:EdgeFeatureSourceClass、JunctionFeatureSourceClass、TurnFeatureSourceClass、ArrayClass
還記得桌面端如何設置網絡數據集的數據源嗎?
就勾選點、線、轉彎要素即可。
這里對應的EdgeFeatureSourceClass、JunctionFeatureSourceClass、TurnFeatureSourceClass,以及他們的接口,就是他們的編程中的類。
畫一張類圖吧:
通過實例化不同的INetworkSource對象,設置其連通性和名稱,再添加到IArray容器中,就可以給IDENetworkDataset的Sources屬性賦值啦!
看代碼:

#region 邊源創建 //創建邊源 INetworkSource edgeNetworkSource = new EdgeFeatureSourceClass(); edgeNetworkSource.Name = "Streets";//就是添加到網絡數據集的要素類的名稱 edgeNetworkSource.ElementType = esriNetworkElementType.esriNETEdge; //設置邊源的連通性組 IEdgeFeatureSource edgeFeatureSource = edgeNetworkSource as IEdgeFeatureSource; // 不使用子類 edgeFeatureSource .UsesSubtypes = false; // 連通性組:只有1組 edgeFeatureSource .ClassConnectivityGroup = 1; // 連通性設置為:任意節點 edgeFeatureSource .ClassConnectivityPolicy = esriNetworkEdgeConnectivityPolicy.esriNECPAnyVertex; #endregion #region 邊源的方向 IStreetNameFields streetNameFields = new StreetNameFieldsClass(); streetNameFields.Priority = 1; streetNameFields.StreetNameFieldName = "FULL_NAME"; INetworkSourceDirections nsDirections = new NetworkSourceDirectionsClass(); IArray nsdArray = new ArrayClass(); nsdArray.Add(streetNameFields); nsDirections.StreetNameFields = nsdArray; edgeNetworkSource.NetworkSourceDirections = nsDirections; deNetworkDataset.SupportsTurns = true; #endregion #region 轉彎源創建 INetworkSource turnNetworkSource = new TurnFeatureSourceClass(); turnNetworkSource.Name = "ParisTurns";//就是添加到網絡數據集的要素類的名稱 turnNetworkSource.ElementType = esriNetworkElementType.esriNETTurn; #endregion #region 添加到IArray中 IArray sourceArray = new ArrayClass(); sourceArray.Add(edgeNetworkSource); sourceArray.Add(turnNetworkSource); #endregion
可以包裝成一個或者兩個方法,傳入參數即為網絡數據集創建的所在要素數據集中的要素類的名稱(string)。
返回一個IArray對象,此IArray對象即可賦值給IDENetworkDataset.Sources屬性。
2.4 添加Attributes屬性(網絡屬性)——以長度或時間為單位的屬性為例(成本屬性)
涉及的接口:INetworkAttribute3、IEvaluatedNetworkAttribute
涉及的類:NetworkAttributeClass、EvaluatedNetworkAttributeClass、NetworkConstantEvaluatorClass、NetworkFieldEvaluatorClass、NetworkScriptEvaluatorClass
這一步比較復雜。回憶一下在桌面軟件中是如何設置網絡屬性的?
對,要添加一個網絡屬性,要設置其類型(成本、限制等),要設置其單位,要設置各個要素給網絡屬性的賦值(字段、腳本等),十分復雜。
在這里,網絡屬性是INetworkAttribute3接口的變量,而網絡屬性的具體數據則由IEvaluatedNetworkAttribute去組織和存放,后者,叫作數據組織器。
這對接口的作用頗似INetworkDataset和IDENetworkDataset。
來看類圖:
將IEdgeNetworkSourceClass(即網絡邊源)和字段賦值器、常量賦值器賦予給網絡屬性賦值器的Evaluator和DefaultEvaluator兩個屬性(圖中藍色方框),由於EvaluatedNetworkAttributeClass實現了兩個接口,而這兩個屬性是這個類中IEvaluatedNetworkAttribute接口的一個屬性,所以將EvaluatedNetworkAttributeClass對象添加至IArray接口的對象中,即可對IDENetworkDataset的Attributes屬性進行賦值。
那么有人會想問了,什么是字段賦值器呢?什么是常量賦值器?什么是字段賦值器?
在10.4中,原本“賦值器”就被翻譯成了“評估者(Evaluator)”。其實就是賦值器(EvaluatedNetworkAttributeClass)。
在這里,作為長度屬性,它的評估者(賦值器),指定了“道路數據集”這個邊源(IEdgeNetworkSource)后,類型(=INetworkFieldEvaluator、INetworkConstantEvaluator)就可以是“字段”、“常量”等。其值就由具體的賦值器的類的SetExpression方法決定。
見代碼:

//網絡屬性:Meters // 網絡屬性類型:成本 // 網絡屬性數據類型:Double(雙精度) // 網絡屬性單位:米 // 默認啟用:否 // 網絡數據源賦值器: // 數據源Streets:字段 -[Meters] // 數據源Streets:字段 -[Meters] // 默認網絡屬性值: // 默認邊:常量-0 // 默認交點:常量-0 // 默認轉彎:常量-0 IArray attributeArray = new ArrayClass(); // 實例化一個網絡屬性賦值器,並轉化為INetworkAttribute2身份 // 並設置網絡屬性名稱、網絡屬性類型、網絡數據類型、網絡屬性單位和是否默認啟用 IEvaluatedNetworkAttribute metersAttribute = new EvaluatedNetworkAttributeClass (); INetworkAttribute2 metersNetworkAttribute2 = (INetworkAttribute2) metersAttribute; metersNetworkAttribute2.Name = "Meters"; metersNetworkAttribute2.UsageType = esriNetworkAttributeUsageType.esriNAUTCost; metersNetworkAttribute2.DataType = esriNetworkAttributeDataType.esriNADTDouble; metersNetworkAttribute2.Units = esriNetworkAttributeUnits.esriNAUMeters; metersNetworkAttribute2.UseByDefault = false; // 創建一個字段賦值器,將其身份轉化為INetworkEvaluator,將傳入的網絡邊源進行屬性賦值 INetworkFieldEvaluator metersNetworkFieldEvaluator = new NetworkFieldEvaluatorClass(); INetworkEvaluator metersNetworkEvaluator = (INetworkEvaluator) metersNetworkFieldEvaluator; metersNetworkFieldEvaluator.SetExpression("[Meters]", ""); metersAttribute.set_Evaluator(edgeNetworkSource, esriNetworkEdgeDirection.esriNEDAlongDigitized, etersNetworkEvaluator); metersAttribute.set_Evaluator(edgeNetworkSource, esriNetworkEdgeDirection.esriNEDAgainstDigitized, metersNetworkEvaluator); // 創建一個常量字段賦值器,將其身份轉化為INetworkEvaluator,將網絡屬性的默認值給定 INetworkConstantEvaluator metersNetworkConstantEvaluator = new NetworkConstantEvaluatorClass(); INetworkEvaluator metersConstantNetworkEvaluator = (INetworkEvaluator)metersNetworkConstantEvaluator; metersNetworkConstantEvaluator.ConstantValue = 0; metersAttribute.set_DefaultEvaluator(esriNetworkElementType.esriNETEdge, metersConstantNetworkEvaluator); metersAttribute.set_DefaultEvaluator(esriNetworkElementType.esriNETJunction, metersConstantNetworkEvaluator); metersAttribute.set_DefaultEvaluator(esriNetworkElementType.esriNETTurn, metersConstantNetworkEvaluator); // 將IEvaluatedNetworkAttribute對象添加到IArray對象中,完成網絡屬性的添加 attributeArray.Add(metersAttribute);

// 網絡屬性Minutes: // 屬性類型:成本 // 屬性數據類型:雙精度(double) // 屬性單位:分鍾 // 是否默認啟用:是 // 網絡數據源屬性賦值器: // 網絡數據源Streets(From-To):字段 - [FT_Minutes] // 網絡數據源Streets(To-From):字段 - [FT_Minutes] // 默認網絡屬性值: // 邊的默認值:常量 - 0; // 交匯點的默認值:常量 - 0; // 轉彎的默認值:常量 - 0; // 創建一個網絡屬性賦值器,並轉化為網絡屬性接口,設置其名稱、網絡屬性類型、網絡數據類型、單位、默認是否啟用 IEvaluatedNetworkAttribute minutesAttribute = new EvaluatedNetworkAttributeClass(); INetworkAttribute2 minutesNetworkAttribute2 = (INetworkAttribute2) minutesAttribute; minutesNetworkAttribute2.Name = "Minutes"; minutesNetworkAttribute2.UsageType = esriNetworkAttributeUsageType.esriNAUTCost; minutesNetworkAttribute2.DataType = esriNetworkAttributeDataType.esriNADTDouble; minutesNetworkAttribute2.Units = esriNetworkAttributeUnits.esriNAUMinutes; minutesNetworkAttribute2.UseByDefault = true; // 創建網絡字段賦值器,並轉化為網絡賦值器,前者賦值表達式,后者給邊源賦予網絡字段賦值器 INetworkFieldEvaluator ftMinutesNetworkFieldEvaluator = new NetworkFieldEvaluatorClass(); INetworkFieldEvaluator tfMinutesNetworkFieldEvaluator = new NetworkFieldEvaluatorClass(); ftMinutesNetworkFieldEvaluator.SetExpression("[FT_Minutes]", ""); tfMinutesNetworkFieldEvaluator.SetExpression("[TF_Minutes]", ""); INetworkEvaluator ftMinutesNetworkEvaluator = (INetworkEvaluator) ftMinutesNetworkFieldEvaluator; INetworkEvaluator tfMinutesNetworkEvaluator = (INetworkEvaluator) tfMinutesNetworkFieldEvaluator; minutesAttribute.set_Evaluator(edgeNetworkSource, esriNetworkEdgeDirection.esriNEDAlongDigitized, ftMinutesNetworkEvaluator); minutesAttribute.set_Evaluator(edgeNetworkSource, esriNetworkEdgeDirection.esriNEDAgainstDigitized, tfMinutesNetworkEvaluator); // 創建網絡常量賦值器,並轉化為網絡賦值器,前者給默認值這個屬性賦予默認值,后者給邊源賦予默認值 INetworkConstantEvaluator minutesNetworkConstantEvaluator = new NetworkConstantEvaluatorClass(); minutesNetworkConstantEvaluator.ConstantValue = 0; INetworkEvaluator minutesConstantNetworkEvaluator = (INetworkEvaluator) minutesNetworkConstantEvaluator; minutesAttribute.set_DefaultEvaluator(esriNetworkElementType.esriNETEdge, minutesConstantNetworkEvaluator); minutesAttribute.set_DefaultEvaluator(esriNetworkElementType.esriNETJunction, minutesConstantNetworkEvaluator); minutesAttribute.set_DefaultEvaluator(esriNetworkElementType.esriNETTurn, minutesConstantNetworkEvaluator); // 添加網絡屬性到IArray對象中 attributeArray.Add(minutesAttribute);
兩段代碼均可包裝成C#的方法,參數可以傳遞需要賦值的字段名(string)、網絡邊源等,等下一篇博客將重點進行代碼梳理。
兩段代碼的最后一步,均為添加IEvaluatedNetworkAttribute的對象到IArray數組中,而這個IArray數組正是IDENetworkDataset.Attributes所需的。
2.5 設置Directions屬性(導航或方向)
涉及到的接口:INetworkDirections
涉及到的類:NetworkDirectionsClass
導航就比較容易了,導航需要的是:一個網絡邊源(其要素類必須有一個文本類型的字段),一個成本類型單位為長度類型的網絡屬性。
直接看代碼,這個沒什么問題:
/// <summary> /// 指定網絡數據集的導航屬性 /// </summary> /// <param name="deNetworkDataset">數據元素網絡數據集</param> /// <param name="UnitsType">單位類型</param> /// <param name="LengthAttribute"> 創建的長度屬性的名稱</param> /// <param name="TimeAttribute"> 創建的時間屬性名稱,可空</param> /// <param name="RoadClassAttribute">創建的道路類型屬性名稱,可空</param> public void SetNetworkDirction(IDENetworkDataset deNetworkDataset, esriNetworkAttributeUnits UnitsType, string LengthAttribute, string TimeAttribute, string RoadClassAttribute) { // 創建INetworkDirections對象 INetworkDirections networkDirections = new NetworkDirectionsClass(); networkDirections.DefaultOutputLengthUnits = UnitsType; //設置長度屬性 if (!string.IsNullOrEmpty(LengthAttribute)) { networkDirections.LengthAttributeName = LengthAttribute; } //設置時間屬性 if (!string.IsNullOrEmpty(TimeAttribute)) { networkDirections.TimeAttributeName = TimeAttribute; } //設置道路類型屬性 if (!string.IsNullOrEmpty(RoadClassAttribute)) { networkDirections.RoadClassAttributeName = RoadClassAttribute; } // 設置網絡數據集的方向屬性 deNetworkDataset.Directions = networkDirections; }
這一步對應桌面創建網絡數據集的這一步:
2.6 創建並構建INetworkDataset對象(大功告成!)
只能通過IDatasetContainer.CreateDataset()方法創建,傳入的參數是IDEDataset類型的變量,返回的是IDataset對象。
這一步,也是最后的一步,將數據集合(DENetworkDataset)轉化為分析對象(NetworkDataset)。
當然別忘了構建一下~
直接上代碼:

創建成功的結果如下:
我傳的網絡數據集名稱為STH_ND,結果就如上圖咯。
3. 流程圖
這是我做過最復雜的AO開發了,涉及到的類和接口實在太龐大...趁年輕多搞搞,提升一下邏輯組織能力。
在后階段的整合中,我會給出一個實例,就用本篇的各種方法,包裝成一個工具類,並完整地對比桌面創建網絡數據集做一個demo。