說明:
1. 本文主要用於演示網絡分析服務的搭建過程。所以在此不會深入討論網絡分析服務的每一個細節,本文的目的就是讓初學者學會使用網絡分析服務進行基本的分析(主要針對后續的WEB開發):路徑分析,最近設施點分析,以及服務區分析。
2.關於OD成本矩陣分析,多路徑配送,位置分配分析不會在本文中討論(注:REST GIS服務不支持OD成本矩陣分析,多路徑配送,位置分配,只能在ArcMap中進行分析)。
3.本文部分內容參考了ArcGIS幫助文檔,想詳細了解GIS網絡分析可查閱官方幫助文檔,並在此強烈推薦。
4.為簡化分析過程,本文使用的數據為作者零時制作,所以實際的表現效果會沒有真實的數據美觀,但是對於網絡分析服務的內容沒有任何影響,讀者可以參閱官方給出的網絡分析服務數據(注:需安裝ArcGIS Toturial 10.0,這里基本提供了官方文檔中用到的所有數據)。
1.准備網絡分析的數據
首先我們制作表示道路的線要素,線要素之后將用於構建我們的網絡數據集。
a.第一步
建立如下所示文件夾結構:
解釋:Scratch文件夾用來存儲我們的零時數據,ToolData用於存儲我們永久的服務數據。一般我們將地理要素及要素集存放在地里數據庫中。
這里我們新建了一個名叫NetworkFeatureDataSet的要素集,這是因為存放於地理數據庫中的線要素如果用於構建網絡數據集,則必須放在要素集中。
b.第二步
新建各種要素(表示設施點的點要素,以及表示道路的線要素),並添加相關的屬性
(說明:Network_ND和Network_ND_Junctions是之前已構建好的網絡數據集和結點。在此我們只需要新建三個點要素和一個線要素即可)
分別配置一下屬性:
線要素:
屬性說明:
Shape_Length:新建要素時默認生成的屬性,表示線要素的長度,其值和單位和選擇的坐標系有關,由於我們考慮到之后的Web開發,因此這里采用Web Mecator坐標系。
Length:表示道路的長度,這里的值就等於Shape_Length。(其實這里我們直接用Shape_Length這個屬性就可以,可以不添加該屬性)
Level:表示道路的等級,不同的等級所允許的行駛速度不同這里我們設置的等級和速度的對應關系如下:
等級 |
速度的最大值 |
1 |
130 km/h |
2 |
120 km/h |
3 |
80 km/h |
4 |
60 km/h |
這里還需要說明的一點是:在Web的開發過程中,默認情況下是啟用了道路的等級屬性(也就是說:無論你的網絡數據集有沒有設置等級,都會在Web開發中啟用等級屬性)。因此如果我們構建的網絡數據集中不包含等級屬性,那么采用默認的設置時,在Web開發中就會出錯,當然在Web開發時,我們也可以將啟用等級屬性設置為False,這樣也可以實現網絡分析的功能,具體過程將在下一篇Web開發中到。在此有個基本概念即可。
Speed:表示該公路運行行駛的最大速度,及按照如上所示的表格進行設置。
Time:表示行駛時間,這里我們沒有添加任何數據。
Name:即表示公路的名稱,注意線要素一定要有類型為文本類型的屬性,這樣才能夠在構建網絡數據集的時候添加方向設置,否則構建的網絡數據集中將不包含方向指南。因此這里我們添加了道路的名稱,目的用於之后生成方向指南。
點要素(以表示學校的點要素為例,其他點要素雷同)
c.第三步
編輯要素,構建具體的地圖,這里我們制作了一個簡單的,如下示例的地圖:
需要注意的是:由於道路是聯通的,所以我們需要將所有相交的線要素進行打斷處理。在此可以用拓補工具進行批處理。
以上過程完成之后,數據的准備階段就已完成,下面開始網絡數據集的構建工作。
2.構建網絡數據集
再次強調,構建網絡數據集的線要素必須位於要素集中,否則無法構建。
右鍵要素集——新建——網絡數據集
點擊將如下圖所示
設置網絡數據集的名稱,點擊下一步。
設置連通性,這里有節點和端點兩種,默認情況下為端點,這里我們選擇默認即可,然后點擊下一步。
設置高程字段,當涉及到高程時,在此設置,本文沒有涉及,所以選擇無。然后點擊下一步。
設置網絡數據集的屬性,我們指定了如上所示的屬性。具體的添加過程:
點擊添加,然后設置名稱,用法和單位,然后點擊賦值器,可以指定該屬性是字段,函數,常量以及VB腳本。
這里我們設置Distance屬性為字段,對應線要素的Length屬性(也可以用表達式,將單位換成千米即:Length/1000,默認的長度單位是米)。
Speed屬性對於線要素的Speed屬性。如下圖所示:
DriverTime屬性,表示行駛時間。這里需要注意的是DriveTime屬性我們采用的是表達式,而不是直接將線要素的Time屬性賦給它。(因為之前在新建要素的時候,我們沒有給Time屬性賦值,這里我們想根據線要素的長度和最大行駛速度來設置通過該要素所需最小時間)
因為速度是千米/時,默認長度單位是米,所以我們將要素長度除以一千。
以上屬性設置好以后,點擊下一步,設置網絡方向屬性。
這里我們設置長度屬性為Distance,並設置單位及時間屬性,如上圖所示,在街道名稱中設置名稱為Name字段。
然后點擊完成。如果以上過程沒有出錯,那么恭喜你,我們自己構建的網絡數據集已經生成了,這個時候,你的要素集中的內容應該如下:
接下來我們驗證生成的網絡數據集。
3.驗證網絡數據集
點擊自定義調出網絡分析工具條:
點擊NetworkAnalyst,可以選擇分析項。
a.最短路徑查詢
點擊新建路徑,然后點擊網絡分析工具條中帶有+號的按鈕。在地圖中添加兩個點或者更多,如下圖所示添加了四個點:
然后點擊求解工具,則會生成相應的路徑。
下面是生成的方向指南:
同理服務區的分析過程雷同,具體過程可參考官方的文檔。在此不再敘述。下一篇將會講述發布我們的網絡數據集,從而提供Web客戶端(Silveright)的GIS網絡分析服務。
在上一篇中講述了如何構建網絡分析數據集,本篇將講解如何發布網絡分析服務。本文將使用上一篇中建立的網絡數據集,下載地址在上一篇博文的最后已給出。
之前我們已經實現了基於ArcMap中的網絡分析,但是僅僅支持本地是萬萬不夠的,這里我們的目的就是將我們建好的網絡分析圖層發布,以供我們無論在在合適何地都能進行網絡分析功能,需要說明的是,基於Web的網絡分析目前還不支持OD成本矩陣分析,多路徑配送,位置分配分析。這里目前只支持最短路徑分析,最近設施點分析,和服務區分析。
發布網絡分析服務的過程很簡單。大致步驟如下:
1.准備數據
准備用於分析的網絡數據集,這里我們在上一篇的博文中已經建好了,所以直接下一步。
2.新建MXD文檔
MXD文檔將在之后發布成GIS服務,並且在GIS服務中啟用網絡分析功能。
MXD文檔必須包含的內容:
a.用於最短路徑分析的路徑分析圖層
b.用於最近設施點分析的最近設施點分析圖層
c.用於服務區分析的服務區分析圖層
d.網絡數據集(即我們在上一篇中已經構建好的網絡數據集)
為了增加表現的效果,我們還可以添加設施點圖層(例如:學校,警察局,醫院等)
關於網絡分析圖層(路徑分析圖層,最近設施點分析圖層,服務區分析圖層)可以通過ArcToolBox新建。如下圖:
通過ArcToolBox提供的工具可以直接創建網絡分析圖層。例如打開創建路徑分析圖層工具,界面如下:
點擊下一步,即可新建路徑分析圖層。其他分析圖層過程雷同。
最后MXD中的文檔內容如下:
所需圖層准備好之后,保存MXD文檔,然后發布。
3.發布網絡分析服務
我們可以直接在ArcMap中發布也可在ArcCatalog中發布。右鍵MXD地圖文檔——發布到ArcGIS Server
設置服務器和GIS服務名稱,點擊下一步:
啟用網絡分析功能,點擊下一步,完成網絡分析服務的發布。
發布成功之后,可以在本地的瀏覽器中打開該網絡分析服務的地址.即可查看該網絡分析服務的參數和相關功能。
括號為NAServer就表示該服務為網絡分析服務。打開可以查看該服務的具體信息。
由上圖可以看見該網絡分析服務包含了路徑分析服務,服務區分析服務,以及最近設施點分析服務,點擊各服務可以查看服務的具體信息。
到此網絡分析服務的發布工作已經完成。
ArcGIS網絡分析之Silverlight客戶端路徑分析(三)
首先貼上最終的效果圖:
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網絡分析之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。
例如本文發布的最近設施點的網絡分析圖層地址為:
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指導教程。
ArcGIS網絡分析之Silverlight客戶端服務區分析(五)
服務區分析實現的主要功能是判斷在一定時間內所能夠到達的區域,例如,從某一點出發,我們想知道在30分鍾之內能夠達到的范圍有多大,那么我們就可以借助服務區分析來實現。一下是本文最后實現的效果圖:
下面就來說一下具體的實現過程。
服務區分析算是在Web端實現網絡分析的最后一個部分,之前已經講解了關於網絡數據集的制作,網絡分析服務的發布,以及最近路徑,最近設施點查詢,今天來講述最后一個部分,服務區分析。
如果您已經學完了最短路徑以及最近設施點的分析,那么對於服務區分析,肯定也是小菜一碟了。這里也沒有什么新內容,實現的過程依然和之前的路徑分析以及設施點分析類似,不同的依然是參數不同,同時這里我們同樣需要發布一個服務區分析圖層。
假設我們已經構建好了網絡分析服務(具體的發布網絡分析服務的過程在之前的博文有詳細的介紹,在此不再說明),打開我們的網絡分析服務的地址,地址格式還是和之前的一樣。例如本文的地址為:
http://qzj-pc/ArcGIS/rest/services/NetworkAnaysisMap/NAServer/ServiceArea
之前說服務區的參數和之前的其他分析不同,那么服務區的參數又有那些呢?
在 ArcGIS Silverlight 中,最近設施分析使用RouteServiceAreaParameters作為參數,RouteServiceAreaParameters的重要屬性如下:
參數名稱 | 參數含義 |
Facilities | 表示設施點,即出發點 |
DefaultBreaks | 表示默認中斷值,string字符串,注意用逗號分割,例如"10,20,30"(假如時間是min)則表示搜索10分鍾20分鍾30分鍾之內分別能夠達到的區域 |
ExcludeSourcesFromPolygons | 由逗號分割的字符串名稱,表示分析要排除的源要素類 |
TrimPolygonDistance | 表示修剪多邊形的容差距離。 |
TrimPolygonDistanceUnits | 表示修剪多邊形的容差距離單位。 |
SplitPolygonsAtBreaks | 表示從中斷處(不同區域等級)拆分多邊形,這樣可得到不同時間內到達的區域多邊形 |
SplitLineAtBreaks | 表示從中斷處(不同區域等級)拆分線。 |
OverlapLines | 表示多個設施點生成的線是否相互覆蓋 |
OverlapPolygons | 表示多個設施點生成的多邊形是否相互覆蓋 |
ReturnFacilities | 表示是否返回設施點 |
MergeSimilarPolygonRanges | 表示是否合幵相似中斷值(等級)的多邊形范圍 |
OutputPolygons | 表示生成的多邊形類型,默認由網絡圖層指定 |
TravelDirection | 表示路徑方向,例如以設施為起點或終點為起點 |
以上為RouteServiceAreaParameters參數的一些重要屬性。下面看看代碼的聲明:
RouteServiceAreaParameters serviceAreaParameter = new RouteServiceAreaParameters() { //獲得設施點,即出發點 Facilities = stopsGraphicsLayer.Graphics, //設置終端值,breakString為聲明的字符串變量 DefaultBreaks = breakString, //設置容差 TrimPolygonDistance = 10000, //是否返回設施點(出發點) ReturnFacilities = true, SplitPolygonsAtBreaks = true, SplitLineAtBreaks = false, OverlapLines = false, OverlapPolygons = true, MergeSimilarPolygonRanges = true, OutSpatialReference = MyMap.SpatialReference, };
以上的示例代碼聲明了一個RouteServiceAreaParameters變量。breakString為文本框的值,其中時間單位有小時和分鍾,因此在此需要判斷用戶選擇的時間單位是小時還是分鍾(默認設置和網絡分析圖層一致,本文是小時)。
因此這里需要做相應的轉換,比如用戶輸入的是30分鍾,那么需要將其轉換為0.5小時,因此我們定義了一個breakString。示例代碼如下:
string breakString = ""; if (TimeUnitcomboBox.SelectedIndex == 0) { breakString = ServiceAreaBreakTextBox.Text; } else if(TimeUnitcomboBox.SelectedIndex==1) { string[] TimeString = ServiceAreaBreakTextBox.Text.Split(','); for (int i = 0; i < TimeString.Length-1;i++ ) { breakString += (Convert.ToDouble(TimeString[i]) / 60).ToString(); breakString += ","; } breakString += (Convert.ToDouble(TimeString[TimeString.Length - 1]) / 60).ToString(); }
在完成參數的定義之后,下面我們就可以開始進行網絡分析服務的查詢了。當然,首先我們需要定義一個RouteTask,以及查詢成功和失敗的事件相應函數。這和最近設施點分析以及路徑分析也是一樣的:
定義RouteTask,將url指向服務區網絡分析圖層:
RouteTask ServiceAreaTask = new RouteTask("http://qzj-pc/ArcGIS/rest/services/NetworkAnaysisMap/NAServer/ServiceArea");//服務區Task
注冊事件的響應函數:
ServiceAreaTask.SolveServiceAreaCompleted += new EventHandler<RouteEventArgs>(ServiceAreaTask_SolveServiceAreaCompleted); ServiceAreaTask.Failed += new EventHandler<TaskFailedEventArgs>(Task_Failed);
private void ServiceAreaTask_SolveServiceAreaCompleted(object sender, RouteEventArgs e) { } private void Task_Failed(object sender, TaskFailedEventArgs e) { MessageBox.Show("求解失敗" + e.Error.ToString()); }
基本的准備工作都已完畢,接下來就可以通過該RouteTask來查詢服務區了。
if (ServiceAreaTask.IsBusy) ServiceAreaTask.CancelAsync(); ServiceAreaTask.SolveServiceAreaAsync(serviceAreaParameter);
到這里你應該也會想到,下一步就是獲取查詢的結果了,如果你細心,你會發現這和之其他的分析實現過程基本一致。 那么服務區分析的結果是什么呢?
我們知道服務區是一個面,那么肯定得到的結果是面要素。但是不同的是服務區分析的結果不再是RouteResult了,直接通過事件參數e的ServiceAreaPolygons屬性即可得到服務區面要素。下面來看一下代碼的實現獲取結果的過程:
private void ServiceAreaTask_SolveServiceAreaCompleted(object sender, RouteEventArgs e) { int i = 1; foreach (Graphic g in e.ServiceAreaPolygons) { //這里我們輸入的中斷值一般是3個,所以結果會有三個面要素,分別表示不同時間內能夠達到的區域 switch (i) { case 1: g.Symbol = LayoutRoot.Resources["MyServiceArea1"] as SimpleFillSymbol; break; case 2: g.Symbol = LayoutRoot.Resources["MyServiceArea2"] as SimpleFillSymbol; break; case 3: g.Symbol = LayoutRoot.Resources["MyServiceArea3"] as SimpleFillSymbol; break; } i++; serviceAreaLayer.Graphics.Add(g); } }
這里的MyServiceArea1,MyServiceArea2,MyServiceArea3為在XAML中定義的面要素資源樣式,參考示例代碼:
<esri:SimpleFillSymbol x:Name="MyServiceArea1" Fill="Red" BorderBrush="Yellow" BorderThickness="3"/> <esri:SimpleFillSymbol x:Name="MyServiceArea2" Fill="Yellow" BorderBrush="Green" BorderThickness="3"/> <esri:SimpleFillSymbol x:Name="MyServiceArea3" Fill="Green" BorderBrush="Blue" BorderThickness="3"/>
到此,所有的工作算是結束了,如果一切順利,那么你也會得到和本文開始給出的那張照片。
Web端的網絡分析的全部功能算是講解結束了,歡迎大家相互交流!
網絡分析系列的源程序以及網絡分析數據下載地址: