1. 程式人生 > >ArcGIS網路分析之Silverlight客戶端最近設施點分析(二)

ArcGIS網路分析之Silverlight客戶端最近設施點分析(二)

在上一篇中說了如何實現最近路徑分析,本篇將討論如何實現最近設施點分析。

最近設施點分析實際上和路徑分析有些相識,實現的過程基本一致,不同的是引數的設定,選用的分析圖層為最近設施點網路分析圖層,一般形式為:

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。

例如本文釋出的最近設施點的網路分析圖層地址為:

例項化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指導教程。

下一篇將講解服務區分析的實現過程,歡迎關注!

(版權所有,轉載請標明出處)  轉載於http://www.cnblogs.com/potential/archive/2012/11/17/2775141.html