首先貼上最終的效果圖:
a.路徑查詢
2.最近設施點查詢
3.服務區分析
說明:
1.以上的示例使用的數據是隨意在ArcMap中創建的數據,也就是之前博文新建的數據,這里的單位和比例尺並不是實際的單位和比例尺。所以和底圖的顯示不一致,這里的底圖只是為了增加顯示的效果。
2.以上所以的實現基於之前的兩篇關於網絡分析的博文,在此推薦看一看。
3.以上示例的具體細節將會分別為大家講解,歡迎大家相互交流,批評指正。
一.路徑分析服務概述
路徑分析服務可以為Silverlight WEBGIS提供最佳路徑的選擇功能,用戶指定兩個點便可以查詢出兩點之間的最佳路徑,同時用戶還可以考慮不同的因素來找到最佳的路徑,例如設置障礙點,阻抗等。使用路徑分析功能時需要使用ArcGIS Api for Silverlight中的TouteTask類。同時還需提供網絡分析服務中路徑分析圖層的地址(即上一篇博文中我們發布的網絡分析服務中路徑服務的地址。其地址的一般格式是:
http://<GIS服務器地址>/ArcGIS/rest/services/<網絡分析服務名稱>/NAServer/Route.
關於網絡分析服務的發布在之前的博文中已經詳細說明,讀者可參考之前的博文。
二、路徑分析服務的實現過程
這里我初步將實現的過程分為一下幾步:
a.實例化TouteTask變量,指定路徑服務的地址RouteTask是實現路徑分析的重中之重。同時還可以聲明三個圖層:一個圖層用來繪制事件點,一個用來繪制障礙(點,線,面),一個用來繪制所得的路徑,並進行實例化。
例如:
聲明一個RouteTask
RouteTask routeTask = new RouteTask("http://qzj-pc/ArcGIS/rest/services/NetworkAnaysisMap/NAServer/Route");//最短路徑服務Task
聲明三個GraphicsLayer:
GraphicsLayer stopsGraphicsLayer;//停靠點或事件點圖層 GraphicsLayer barriesGraphicsLayer;//障礙物圖層 GraphicsLayer RoutegraphicsLayer;//查詢返回的路徑圖層
在Map空間的Load事件中指定GraphicsLayer到相應的圖層:
private void MyMap_Loaded(object sender, RoutedEventArgs e) { stopsGraphicsLayer = MyMap.Layers["MyStopGraphicsLayer"] as GraphicsLayer; barriesGraphicsLayer = MyMap.Layers["MyBarriesGraphicsLayer"] as GraphicsLayer; RoutegraphicsLayer = MyMap.Layers["MyRouteLayer"] as GraphicsLayer; }
以上過程需要在在XAML中添加相應的GraphicsLayer聲明。
<esri:Map Background="White" HorizontalAlignment="Stretch" x:Name="MyMap" VerticalAlignment="Stretch" WrapAround="True"
Loaded="MyMap_Loaded" MouseClick="MyMap_MouseClick" IsLogoVisible="False"> <esri:Map.Layers> <esri:LayerCollection> <esri:ArcGISTiledMapServiceLayer ID="BaseMap"
Url="http://www.arcgisonline.cn/ArcGIS/rest/services/ChinaOnlineStreetGray/MapServer"/> <esri:ArcGISDynamicMapServiceLayer ID="ChinaBaseMap"
Url="http://qzj-pc/ArcGIS/rest/services/NetworkAnaysisMap/MapServer"
InitializationFailed="ArcGISDynamicMapServiceLayer_InitializationFailed" /> <esri:GraphicsLayer ID="MyRouteLayer"/> <esri:GraphicsLayer ID="MyStopGraphicsLayer"/> <esri:GraphicsLayer ID="MyBarriesGraphicsLayer"/> </esri:LayerCollection> </esri:Map.Layers> </esri:Map>
b.注冊RouteTask的SolveCompleted以及Failed事件例如:
注冊事件:
routeTask.SolveCompleted += new EventHandler<RouteEventArgs>(routeTask_SolveCompleted); routeTask.Failed += new EventHandler<TaskFailedEventArgs>(Task_Failed);
private void routeTask_SolveCompleted(object sender, RouteEventArgs e) { } private void Task_Failed(object sender, TaskFailedEventArgs e) { MessageBox.Show("求解失敗" + e.Error.ToString()); }
c.添加用於計算路徑的點,並設置相應的Symbol.
例如:我們點擊地圖就添加一個點,示例代碼如下:
private void MyMap_MouseClick(object sender, Map.MouseEventArgs e) { if (tabControl1.SelectedIndex != 0) StopsRadioButton.IsChecked = true; StopsGraphic = new Graphic(); if (StopsRadioButton.IsChecked == true) { StopsGraphic.Symbol = LayoutRoot.Resources["MyStopsMarkerSymbol"] as Symbol; StopsGraphic.Geometry = new MapPoint(e.MapPoint.X, e.MapPoint.Y); addFacilityName.Show(); } else { StopsGraphic.Symbol = LayoutRoot.Resources["MyBarriesMarkerSymbol"] as Symbol; StopsGraphic.Geometry = new MapPoint(e.MapPoint.X, e.MapPoint.Y); StopsGraphic.Selected = true; barriesGraphicsLayer.Graphics.Add(StopsGraphic); } }
代碼說明:addFacilityName表示的是一個ChildWindow,如下圖的所示:
addFacilityName是AddFacilitiesName的一個實例用來給添加的停靠點增加一個Name屬性,界面如下:
ChildWindow中的關鍵代碼:
在AddFacilitiesName.xaml.cs中定義一個事件:
public event EventHandler OnAddFacilityName;
該事件當用戶點擊確定是觸發,即表示用戶確定添加一個停靠點。
private void OKButton_Click(object sender, RoutedEventArgs e) { this.DialogResult = true; OnAddFacilityName(this, new EventArgs()); }
然后在MainPage中注冊OnAddFacilityName的事件,並在其事件的響應函數中完成停靠點的最終添加工作。
聲明AddFacilitiesName的一個實例:
public AddFacilitiesName addFacilityName = new AddFacilitiesName();
注冊OnAddFacilityName事件:
addFacilityName.OnAddFacilityName += new EventHandler(FacilityName_OnAddFacilityName);
在事件回調函數中完成點的添加工作:
private void FacilityName_OnAddFacilityName(object sender, EventArgs e) { //清空停靠點的屬性,否則當再次添加Name屬性時,則會因為存在Name屬性而報錯 StopsGraphic.Attributes.Clear(); //停靠點樣式中顯示的數字與該屬性進行綁定 StopsGraphic.Attributes.Add("StopNumber", stopsGraphicsLayer.Graphics.Count + 1); //獲取ChildWindow輸入的名稱,並添加Name屬性 StopsGraphic.Attributes.Add("Name", addFacilityName.NameTextBox.Text.ToString()); //將停靠點添加到當前圖層中 stopsGraphicsLayer.Graphics.Add(StopsGraphic); }
以上代碼使用到了在XAML中定義的資源文件,用來描述停靠點和障礙點的樣式。
示例代碼如下:
<esri:SimpleMarkerSymbol x:Key="MyStopsMarkerSymbol" Size="20" Style="Circle" Color="Salmon" > <esri:SimpleMarkerSymbol.ControlTemplate> <ControlTemplate> <Grid> <Ellipse Fill="{Binding Symbol.Color}" Width="{Binding Symbol.Size}" Height="{Binding Symbol.Size}" S
troke="Black" StrokeThickness="1" /> <TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Text="{Binding Path=Attributes[StopNumber]}" FontSize="9" Margin="0" FontWeight="Bold" Foreground="Black" /> </Grid> </ControlTemplate> </esri:SimpleMarkerSymbol.ControlTemplate> </esri:SimpleMarkerSymbol> <esri:SimpleMarkerSymbol x:Name="MyBarriesMarkerSymbol" Color="#FF833232" Style="Square" Size="18"/>
同時我們在這里再定義一下路徑的Graphic樣式:
<esri:SimpleLineSymbol x:Key="RouteRenderer"> <!--<esri:SimpleRenderer.Symbol>--> <!--<esri:LineSymbol>--> <esri:LineSymbol.ControlTemplate> <ControlTemplate> <Grid> <VisualStateManager.VisualStateGroups> <VisualStateGroup x:Name="CommonStates"> <VisualState x:Name="Normal"> <Storyboard RepeatBehavior="Forever"> <DoubleAnimation BeginTime="0:0:0" Storyboard.TargetName="Element" Storyboard.TargetProperty="StrokeDashOffset" To="1000" Duration="0:3:0" /> </Storyboard> </VisualState> </VisualStateGroup> </VisualStateManager.VisualStateGroups> <!--For polyline and polygon template, a Path element with the name "Element" is required--> <Path x:Name="Element" StrokeDashArray="2,1" StrokeDashOffset="0" Stroke="#990000FF" StrokeThickness="8" /> </Grid> </ControlTemplate> </esri:LineSymbol.ControlTemplate> <!--</esri:LineSymbol>--> <!--</esri:SimpleRenderer.Symbol>--> </esri:SimpleLineSymbol>
d.設置路徑服務的輸入參數
路徑服務的參數:RouteParameters,繼承自BaseRouteParameters。RouteParameters的主要成員有:
成員參數名 |
參數描述 |
DirectionsLanguage | 計算方向時使用的語言,默認與路徑網絢圖層的設置一致,但是NAServer只安裝了en_US,其他語言需要管理員自行安裝。 |
DirectionsLengthUnits | 計算方向時使用的長度單位。默認與路徑網絡圖層的設置一致。可用的值包括esriFeet,esriKilometers, esriMeters,esriMile,esriNauticalMiles和esriYards。 |
DirectionsTimeAttribute | 用於計算駕駛時間的網絢屬性。默認與路徑網絡圖層的設置一致。 |
ReturnDirections | 是否返回方向。 |
ReturnRoutes | 是否返回路徑 |
FindBestSequence | 如果為true,解析器將優化路徑中停靠點(Stop)的頇序(如果preserveFirstStop和preserveLastStop為true,則不考慮起點和終點)。 |
StartTime | 指定路徑(從第一個停靠點)開始的時間。 |
Stops | 路徑分析時的停靠點。可以使DataLayer或FeatureSet。 |
Barriers | 路徑分析時的障礙點。可以使DataLayer或FeatureSet。 |
UseHierarchy | 是否在分析中使用網絡的等級屬性。默認與路徑網絡圖層的設置一致。 |
示例代碼:
RouteParameters routeParameters = new RouteParameters() { Stops = stopsGraphicsLayer, Barriers = barriesGraphicsLayer, OutSpatialReference = MyMap.SpatialReference, ReturnDirections = true, FindBestSequence = true, PreserveFirstStop = true, PreserveLastStop = true, };
這里需要注意:如果在構建網絡時沒有構建等級屬性,這里一定不要使用等級屬性否則會報錯,當在構建網絡時設置了等級屬性,那么這里才可以使用等級設置.
e.開始計算路徑
if (routeTask.IsBusy) routeTask.CancelAsync(); routeTask.SolveAsync(routeParameters);
f. 在Completed事件的回調函數中獲取結果(包含路徑和方向指南)
當執行結果完成后RouteTask將最后的結果傳遞給RouteEventArgs的RouteResults屬性,RouteResults是RouteResult的集合,對於路徑查詢來說,最后的RouteResults集合只有一個RouteResult。RouteResult的主要成員如下:
主要成員參數名 |
成員描述 |
Directions | 如果RouteParameters.ReturnDirections為true,則迒回路徑方向(DirectionsFeatureSet類實例)。 |
Route | 如果returnRoutes為true並且outputLines屬性不是esriNAOutputLineNone,則將返回路徑圖形(polyline)。 |
RouteName | 路徑名稱。 |
Stops | 停靠點集合。 |
RouteResult中的Route表示一個Graphic,也就是我們最后的路徑結果。所以這里我們想獲得最佳的路徑結果,只需要取得Route,並設置其樣式,最后添加到地圖中即可。
RouteResult中的Directions表示路徑中包含的方向指南,Drirections的類型是DirectionsFeatureSet,其成員如下:
主要成員參數名 |
成員描述 |
Extent | 路徑的范圍。 |
RouteID | 獲得服務器返回的路徑ID |
RouteName | RouteParameters.Stops中指定的名稱。 |
TotalDriveTime | 路徑時間駕駛時間。 |
TotalLength | 路徑總長度。 |
TotalTime | 路徑總通行時間。 |
MergedGeometry | 表示整個路徑的單條線。 |
DirectionsFeatureSet繼承自FeatureSet。所以Drirections實際上也是一系列的Graphic.並且這些Graphic包含了三個重要的屬性:
Drirections中Graphic的屬性 |
屬性描述 |
text | 對某一段路徑的方向描述。 |
length | 表示某一段路徑的長度 |
time | 表示某一段路徑的所需的時間 |
所以我們現在只需要將上述所有信息收集起來並合理編排就能夠得到路徑和方向指南。
示例代碼如下:
private void routeTask_SolveCompleted(object sender, RouteEventArgs e) { DirectionStackPanel.Children.Clear();//清空路徑指南中的所有方向提示 RoutegraphicsLayer.Graphics.Clear();//清空之前的路徑圖層 RouteResult routeResult = e.RouteResults[0];//最佳路線查詢只有一個結果,因此數組的長度是1. routeResult.Route.Symbol = LayoutRoot.Resources["RouteRenderer"] as SimpleLineSymbol;//設置路徑的樣式 RoutegraphicsLayer.Graphics.Add(routeResult.Route);//將路徑添加到當前的地圖中 int i = 1; foreach (Graphic g in routeResult.Directions) { StringBuilder direction = new StringBuilder(); //設置方向描述的格式:<i>.<描述> direction.AppendFormat("{0}. {1}", i, g.Attributes["text"]); if (i > 1 && i < routeResult.Directions.Features.Count) { //由於當在起點或中間停靠點時,Graphic中的length屬性為0 //因此在執行強制轉換時會出錯, //同時也是格式化輸出,在停靠點不輸出時間和長度屬性,每一個停靠點都看作是一個起點 if (Convert.ToDecimal(g.Attributes["length"])!= 0) { //當length屬性不為0時,在路徑指南中顯示出來 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")); } } //顯示路線提示的Panel DirectionStackPanel.Children.Add(new TextBlock() { Text = direction.ToString(), Margin = new Thickness(4) }); i++; } DirectionStackPanel.Children.Add(new TextBlock() { Text = string.Format(" 總路程為:{0}千米\n\n 總時間為:{1}分鍾", routeResult.Directions.TotalLength.ToString("#0.000"),
routeResult.Directions.TotalDriveTime.ToString("#0.00")) }); }
到此位置所關於路徑服務就已結束。下一篇將講解如何實現最近設施點分析。
本文參考了ArcGIS API For Silverlight的官網的Network Analysis的例子,以及ESRI中國的ArcGIS Api for Silverlight的幫助文檔。
(版權所有,轉載請標明出處)