在上一篇中說了如何實現最近路徑分析,本篇將討論如何實現最近設施點分析。
最近設施點分析實際上和路徑分析有些相識,實現的過程基本一致,不同的是參數的設置,選用的分析圖層為最近設施點網絡分析圖層,一般形式為:
http://<服務器名或ip地址>/ArcGIS/rest/services/<地圖服務名稱>/NAServer/<最近設施點分析圖層名稱>
在ArcGIS Api for Silverlight中,最近設施點分析的參數名稱為:RouteClosestFacilityParameters,同樣它也繼承自BaseRouteParameters。其主要的參數(屬性)有:
屬性名稱 | |
Incidents | 表示事件點 |
Facilities | 表示設施點 |
Barriers | 表示障礙點,還有線障礙:PolylineBarriers,面障礙:PolygonBarriers |
DefaultCutoff | 表示默認終斷值,即不會搜索超出該值的設施點(從事件點到設施點,反之同理) |
ReturnDirections | 表示是否返回方向指南 |
DirectionsLanguage | 表示返回方向指南使用的描述語言(默認與網絡分析圖層一致,NAServer中只有英語,其他語言需要自己安裝) |
DirectionsLengthUnits | 表示計算方向時使用的長度單位。默認與路徑網絡圖層的設置一致。可用的值包括esriFeet,esriKilometers, esriMeters,esriMile,esriNauticalMiles和esriYards |
ReturnRoutes | 表示是否返回設施點與事件點的路徑 |
ReturnFacilities | 表示是否返回設施點 |
ReturnIncidents | 表示是否返回事件點 |
TravelDirection | 表示路徑的方向(從設施點到事件點還是事件點到設施點) |
UseHierarchy | 表示是否啟用等級屬性 |
FacilityReturnType | 表示設施返回類型,默認為FacilityReturnType.ServerFacilityReturnAll |
DefaultTargetFacilityCount | 表示默認搜索的設施點個數 |
以上是最近設施點參數中一般用到的屬性說明。
下面我們來看一下實現的具體過程。
1.首先我們需要一個最近設施點的網絡分析圖層,並實例化一個RouteTask。
例如本文發布的最近設施點的網絡分析圖層地址為:
http://localhost/ArcGIS/rest/services/NetworkAnaysisMap/NAServer/ClosestFacility
實例化RouteTask
RouteTask closestFacilityTask = new RouteTask("http://qzj-pc/ArcGIS/rest/services/NetworkAnaysisMap/NAServer/ClosestFacility");//最近設施點Task
這里在之前的博文中已經說了網絡分析圖層的建立和發布。在此不再討論。
2.注冊RouteTask的完成和失敗事件
注冊事件:
closestFacilityTask.SolveClosestFacilityCompleted += new EventHandler<RouteEventArgs>(closestFacilityTask_SolveClosestFacilityCompleted); closestFacilityTask.Failed += new EventHandler<TaskFailedEventArgs>(Task_Failed);
事件完成響應函數:
private void closestFacilityTask_SolveClosestFacilityCompleted(object sender, RouteEventArgs e) { //獲取結果的代碼
} private void Task_Failed(object sender, TaskFailedEventArgs e) { MessageBox.Show("求解失敗" + e.Error.ToString()); }
3.設置最近設施點分析的參數,即RouteClosestFacilityParameters,例如:
RouteClosestFacilityParameters closestFacilityParameter = new RouteClosestFacilityParameters() { //設置事件點 Incidents = stopsGraphicsLayer.Graphics, //設置設置點 Facilities = gplayer.Graphics, //設置障礙點 Barriers = barriesGraphicsLayer.Graphics, ReturnDirections = true, DirectionsLanguage = new System.Globalization.CultureInfo("en-US"), ReturnRoutes = true, ReturnFacilities = true, ReturnBarriers = false, ReturnIncidents = true, ReturnPolygonBarriers = false, ReturnPolylineBarriers = false, DefaultCutoff = 100000, FacilityReturnType = FacilityReturnType.ServerFacilityReturnAll, DefaultTargetFacilityCount = Convert.ToInt32(ClosestFaciclityNumTextBox.Text), TravelDirection = FacilityTravelDirection.TravelDirectionToFacility, OutSpatialReference = MyMap.SpatialReference, };
以上過程省略了關於添加障礙點和事件點的過程,其過程和最短路徑分析的過程完全一致,所以在此不再多做解釋,具體過程可以參考前一篇的博文。
4.進行最近設施點分析
if (closestFacilityTask.IsBusy) closestFacilityTask.CancelAsync(); closestFacilityTask.SolveClosestFacilityAsync(closestFacilityParameter);
5.獲取分析結果,以及處理分析失敗的情況
最近設施點查詢返回的結果和最短路徑是一樣的,參數都是RouteEventArgs。所以這里我們取得RouteEventArgs中的RouteResults集合即可。
但是對結果的處理方式和最短路徑又有一點點小差別。因為最短路徑返回的結果只有一條路徑,而最近設施點的分析結果則根據查詢的設施點不同而不同,例如我們想查詢最近的3個設施點,如果查詢成功,並且找到最近的三個設施點,那么返回的路徑就有3條。
所以這里我們需要對設施點查詢返回的結果進行循環。然后剩下的工作就和最短路徑一樣了。
這里我們選擇用TreeView控件來顯示不同的路徑,最后生成的界面如下:
例如查詢附近4個最近的警察局,獲得四條路線,並可以展看查看每一天的詳情:
同時當選中一條路徑時(位置1-第二警局),高亮顯示。
示例代碼如下:
private void closestFacilityTask_SolveClosestFacilityCompleted(object sender, RouteEventArgs e) { //清空顯示方向的面板 DirectionStackPanel.Children.Clear(); //情況路線圖層(即上一次查詢的結果) RoutegraphicsLayer.Graphics.Clear(); //定義一個TreeView控件,將用於顯示路徑 TreeView RouteTree = new TreeView(); //注冊TreeView事件,當選擇不同的節點時,高亮顯示相應的路徑 RouteTree.SelectedItemChanged += new RoutedPropertyChangedEventHandler<object>(RouteTree_SelectedItemChanged); //遍歷返回的結果(路線) foreach (RouteResult SolvedRoute in e.RouteResults) { RouteResult routeResult = SolvedRoute; //定義路線樣式 routeResult.Route.Symbol = LayoutRoot.Resources["MyRouteLineSymbol"] as SimpleLineSymbol; //將路線添加到圖層中 RoutegraphicsLayer.Graphics.Add(routeResult.Route); //添加一個Item,即表示當前的路線。將路線以樹視圖的形式展示出來 TreeViewItem RouteItem = new TreeViewItem(); //樹視圖一級標題格式:<路線ID>.<路線名稱> RouteItem.Header = string.Format("{0}: {1}", SolvedRoute.Directions.RouteID, SolvedRoute.Directions.RouteName); //將TreeViewItem的Tag設置為相應路線的ID,以便之后高亮顯示其對應路線。 RouteItem.Tag = SolvedRoute.Directions.RouteID; int i = 1; foreach (Graphic g in routeResult.Directions) { StringBuilder direction = new StringBuilder(); direction.AppendFormat("{0}. {1}", i, g.Attributes["text"]); if (i > 1 && i < routeResult.Directions.Features.Count) { decimal Distance = (decimal)g.Attributes["length"]; direction.AppendFormat(" {0}米", Distance.ToString("#0.000")); decimal NeedTime = (decimal)g.Attributes["time"]; direction.AppendFormat(", {0}分鍾", NeedTime.ToString("#0.00")); } RouteItem.Items.Add(new TextBlock() { Text = direction.ToString(), Margin = new Thickness(4) }); i++; } //添加總時間和路程的屬性 RouteItem.Items.Add(new TextBlock() { Text = string.Format(" 總路程為:{0}千米\n\n 總時間為:{1}分鍾", (SolvedRoute.Directions.TotalLength).ToString("#0.000"),
SolvedRoute.Directions.TotalDriveTime.ToString("#0.00")) }); //遍歷一條路線結束,將該路線的信息添加到TreeView中,TreeView獲得一個節點。 RouteTree.Items.Add(RouteItem); } //遍歷路線結束,將路線結果添加到顯示方向的面板中。 DirectionStackPanel.Children.Add(RouteTree); }
高亮顯示當前選中的路線,並取消高亮上一次選擇的路線,示例代碼如下:
//記錄上一次點擊的是哪一個節點 int OldIndex = 0; private void RouteTree_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e) { //必須點擊節點才有效,節點下的TextBlock雖然也能觸發Changed事件,但是無效 if (e.NewValue.ToString() == typeof(TextBlock).ToString()) { return; } //如果舊值不為空,即不是第一次點擊,那么上一次點擊就有可能是節點還有可能是節點下的TextBlock。 //因為當點擊不同的節點時,我們需要將上一次高亮顯示的路線不高亮,而高亮顯示本次選中的路線 //所以在此需要處理 if (e.OldValue != null) { //如果上一次點擊的是TreeViewItem則直接將其還原成不高亮顯示 if (e.OldValue.ToString() == typeof(TreeViewItem).ToString()) { TreeViewItem treeViewItem = (TreeViewItem)e.OldValue; OldIndex = Convert.ToInt32(treeViewItem.Tag); //在Tag中1表示的是第一條路線,其對應Graphics的索引值為0,一次類推減1. RoutegraphicsLayer.Graphics[OldIndex - 1].Symbol = LayoutRoot.Resources["MyRouteLineSymbol"] as SimpleLineSymbol; }//如果上一次點擊的不是TreeView,則需要通過記錄上一次點擊的索引:Oldindex來確定上一次點擊的是那一個TreeView,並將其還原成不高亮 else { RoutegraphicsLayer.Graphics[OldIndex].Symbol = LayoutRoot.Resources["MyRouteLineSymbol"] as SimpleLineSymbol; } } //獲得當前點擊節點的索引 int currentIndex = Convert.ToInt32(((TreeViewItem)((TreeView)sender).SelectedItem).Tag); //高亮顯示當前選擇的路線 RoutegraphicsLayer.Graphics[currentIndex - 1].Symbol = LayoutRoot.Resources["RouteRenderer"] as SimpleLineSymbol; //將本次點擊的所以賦給OldIndex. OldIndex = currentIndex - 1; }
這樣所有的工作基本就已經完成了。下面是整體效果圖:
注:以上內容參考了ERSI官網例子,以及ESRI中國編寫的ArcGIS Api For Silverlight指導教程。
下一篇將講解服務區分析的實現過程,歡迎關注!
(版權所有,轉載請標明出處)