说明:
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端的网络分析的全部功能算是讲解结束了,欢迎大家相互交流!
网络分析系列的源程序以及网络分析数据下载地址: