九、方向和路线

用于 JavaScript 方向和路由功能的 ArcGIS 应用编程接口由运行在 ArcGIS Server 上的网络分析服务提供。要创建和发布网络服务,您必须在 ArcGIS Pro 中拥有网络分析师扩展。但是,您可以在网络地图应用中使用第三方服务,例如我们在本章中提到的那些服务,而无需许可证。

这些服务使您能够对交通网络进行建模,以便您可以执行各种分析,例如找到从一个地址到另一个地址的最佳路线,找到离您家最近的学校,为新的交付部门确定潜在地点周围的服务区域,或者使用服务车队管理交付计划。

网络分析服务可以执行三项主要任务,您可以通过 ArcGIS for JavaScript API 中的相应任务对象来访问这些任务。这些是路线、最近的设施和服务区。我们将在本章中研究每一种服务类型,我们还将查看方向小部件,它使得在您的网络地图中加入导航功能变得很容易。

在本章中,我们将涵盖以下主题:

  • 路由任务
  • 路由练习时间
  • 关闭便利性任务
  • ServiceArea 任务
  • 使用方向小部件

路由任务

您可以使用RouteTask对象来查找两个或多个位置之间的路线,也可以选择获取行驶方向。RouteTask使用网络分析服务计算路线,既简单(从 A 到 B 最快的方法是什么?)和复杂(涉及多个停靠点、障碍和时间窗口)。RouteTask在网络中的多个位置之间使用成本最低的路径。网络阻抗可以包括时间和距离变量:

与我们在本书中研究的其他任务一样,路由是通过一系列对象完成的,包括RouteParametersRouteTaskRouteResult:

RouteParameters对象向RouteTask提供输入参数,后者执行路由操作。结果以RouteResult对象的形式从 ArcGIS Server 返回。

RouteParameters可以包括停车和障碍物位置、阻抗因素、是否返回行驶方向以及许多其他选项,这里详细介绍的太多了。我们将在本章中介绍基础知识,但是我们敦促您通过阅读 API 文档来深入研究路由任务的全部功能。

下面的代码示例演示了如何创建RouteParameters的实例、添加一系列停靠点以及定义输出空间参考:

routeParams = new RouteParameters(); 
routeParams.stops = new FeatureSet(); 
routeParams.outSpatialReference = {wkid:4326}; 
routeParams.stops.features.push(stop1); 
routeParams.stops.features.push(stop2);

一旦您正确设置了RouteParameters,您就可以实施您的RouteTask并指示它执行分析。

RouteTask的构造函数期望网络服务端点的网址用于分析。一旦实例化了RouteTask,就可以通过调用其solve()方法并向其传递RouteParameters来开始工作,这是一个用于成功完成的回调函数,也可以选择用于任何错误情况的回调:

routeParams = new RouteParameters(); 
routeParams.stops = new FeatureSet(); 
routeParams.outSpatialReference = {wkid:4326}; 
routeParams.stops.features.push(stop1); 
routeParams.stops.features.push(stop2); 
routeTask.solve(routeParams, routeSuccess, routeFailure);

如果操作成功,网络分析服务将向您在调用RouteTask.solve()时指定的回调函数返回一个RouteResult对象。您将在这里编写代码,将结果解包并显示给用户。

返回的数据类型很大程度上取决于您在RouteParameters对象中设置的选项。RouteParameters上最重要的属性之一是stops属性。这些是点与点之间最佳路线分析中要包含的点。停靠点被定义为DataLayerFeatureSet的实例,并且是要包含在分析中的一组停靠点。

屏障的概念在路由操作中也很重要。规划路线时,障碍物会限制移动。障碍可能包括车祸、街道上的建筑工程或其他更永久性的延误,如铁路道口。屏障被定义为FeatureSetDataLayer,并通过RouteParameters.barriers属性指定。下面的代码示例显示了如何编写代码在RouteParameters对象中创建障碍的示例:

var routeParameters = new RouteParameters();

   //Add barriers as a FeatureSet
   routeParameters.barriers = new FeatureSet();
   routeParameters.barriers.features.push(
     map.graphics.add(
       new Graphic(
         evt.mapPoint,
         barrierSymbol
       )
     )
   );

如果您将RouteParameters.returnDirections设置为true,您可以在您的RouteResult中返回分步方向。您还可以设置影响方向输出语言(RouteParameters.directionsLanguage)、路段长度单位(RouteParameters.directionsLengthUnits)、方向详细程度(RouteParameters.directionsOutputType)等的属性。

除了方向,您的结果还可以包括点与点之间的路线、路线名称和停靠点数组。

您也可以使用RouteParameters.ignoreInvalidLocations属性定义如果一站无法到达,任务是否应该失败。该属性可以设置为truefalse。您也可以通过属性将时间引入分析,例如指定路线开始时间的RouteParameters.startTime和定义分析中应使用时间窗口的RouteParameters.useTimeWindows

Like we said, there's a lot you can do with the RouteTask and its RouteParameters. For more information, consult the online help: https://developers.arcgis.com/javascript/3/jsapi/routetask-amd.html.

路由练习时间

在本练习中,您将学习如何在应用中实现路由。您将创建一个RouteParameters实例,通过允许用户点击地图上的点来添加停靠点,并求解路线。返回的路线将在地图上显示为线符号:

  1. 打开https://developers . ArcGIS . com/JavaScript/3/Sandbox/Sandbox . html处的 JavaScript Sandbox。
  2. 删除<script>标签中的所有 JavaScript 内容,如下图所示:
<script> 
  var map; 

  require(["esri/map", "dojo/domReady!"], function(Map) { 
    map = new Map("map", { 
      basemap: "topo",  //For full list of ... 
      center: [-122.45, 37.75], // longitude, latitude 
      zoom: 13 
    }); 
  }); 
</script> 
  1. 创建您的require()功能并导入以下模块:
<script> 
    require([ 
        "esri/map", 
        "esri/urlUtils", 
        "esri/tasks/RouteParameters", 
        "esri/tasks/RouteTask", 
        "esri/tasks/FeatureSet", 
        "esri/symbols/SimpleMarkerSymbol", 
        "esri/symbols/SimpleLineSymbol", 
        "esri/graphic", 
        "dojo/_base/Color" 
    ], 
    function (Map, urlUtils, RouteParameters, RouteTask, 
        FeatureSet, SimpleMarkerSymbol, 
        SimpleLineSymbol, Graphic, Color) { 

    }); 
</script> 
  1. require()回调的主体中,添加以下几行代码。请注意,此应用需要访问不同于应用所服务的 web 服务器上的资源。一般来说,这是不允许的,并且会导致安全异常。但是如果远程服务器支持 CORS ( 跨产地资源共享)就没问题了。到目前为止,我们使用的托管地图服务的服务器都支持 CORS,但我们将在本练习中使用的网络服务不支持。因此,我们必须明确告诉我们的应用,访问主机服务器是可以的,我们可以使用esri/urlUtils模块,如下所示:
urlUtils.addProxyRule({ 
    urlPrefix: "route.arcgis.com", 
    proxyUrl: "/sproxy/" 
});

The usual approach for production applications is to download and install a proxy page on your local web server, and configure it to allow resource sharing with specific servers. For more information on this approach and for a detailed explanation of CORS, see the help topic: https://developers.arcgis.com/javascript/3/jshelp/ags_proxy.html.

  1. require()功能中,创建如下所示的Map对象,并定义变量来保存布线对象和用于显示目的的符号:
<script> 
    require([ 
        "esri/map", 
        "esri/urlUtils", 
        "esri/tasks/RouteParameters", 
        "esri/tasks/RouteTask", 
        "esri/tasks/FeatureSet", 
        "esri/symbols/SimpleMarkerSymbol", 
        "esri/symbols/SimpleLineSymbol", 
        "esri/graphic", 
        "dojo/_base/Color" 
    ], 
    function (Map, urlUtils, RouteParameters, RouteTask, 
        FeatureSet, SimpleMarkerSymbol, 
        SimpleLineSymbol, Graphic, Color) { 

        urlUtils.addProxyRule({ 
           urlPrefix: "route.arcgis.com", 
           proxyUrl: "/sproxy/" 
        }); 

        var map, routeTask, routeParams; 
        var stopSymbol, routeSymbol, lastStop; 

        map = new Map("mapDiv", { 
            basemap: "streets", 
            center: [-123.379, 48.418], //long, lat 
            zoom: 14 
        }); 

    }); 
</script>
  1. 就在创建Map对象的代码块下面,为Map.click()事件添加一个事件处理程序。这应该会触发一个名为addStop()的函数:
map = new Map("mapDiv", { 
    basemap: "streets", 
    center: [-123.379, 48.418], //long, lat 
    zoom: 14 
}); 
map.on("click", addStop); 
  1. 实例化RouteTask对象。它应该参考所示的网络服务:
routeTask = new RouteTask 
("http://sampleserver3.arcgisonline.com/ArcGIS/rest/services/Network/USA/NAServer"); 

There are some very useful routing services on ArcGIS Online. Find out more at: https://route.arcgis.com/arcgis/index.html.

  1. 实例化RouteParameters对象。对于其 stops 属性,创建并分配一个新的空的FeatureSet对象。将其outputSpatialReference属性设置为4326:
routeTask = new RouteTask("https://route.arcgis.com/arcgis/rest/services/World/Route/NAServer/Route_World"); 
routeParams = new RouteParameters(); 
routeParams.stops = new FeatureSet(); 
routeParams.outSpatialReference = {"wkid": 4326} 
  1. 为完成RouteTask.solve-complete()事件和RouteTask.error()事件添加事件处理程序。路由任务的成功完成应该会触发一个名为showRoute()的函数的执行。任何错误都应该触发名为errorHandler()的函数的执行:
routeParams = new RouteParameters(); 
routeParams.stops = new FeatureSet(); 
routeParams.outSpatialReference = { "wkid": 4326 }; 

routeTask.on("solve-complete", showRoute); 
routeTask.on("error", errorHandler);

We could specify these handlers as callback functions when we execute RouteTask.solve(). We've opted for this approach here just to make the code a little easier to read.

  1. 为路线的起点和终点以及定义这些点之间路线的线创建符号对象。在上一步中输入的代码行的正下方添加代码行:
stopSymbol = new SimpleMarkerSymbol().setStyle( 
    SimpleMarkerSymbol.STYLE_CROSS); 
stopSymbol.setSize(15); 
stopSymbol.outline.setWidth(4); 
routeSymbol = new SimpleLineSymbol(); 
routeSymbol.setColor( 
    new Color([0, 0, 255, 0.5])); 
routeSymbol.setWidth(5);      
  1. 创建addStop()功能,当用户点击地图时会触发该功能。该函数将接受一个Event对象作为其唯一参数。地图上点击的点可以从这个对象中提取出来。该函数将向地图添加一个点图形,将图形添加到RouteParameters.stops属性,在第二张地图上,单击它将调用RouteTask.solve()方法,传入一个RouteParameters实例:
function addStop(evt) { 
    var stop = map.graphics.add(new Graphic(evt.mapPoint, stopSymbol)); 
    routeParams.stops.features.push(stop); 

    if (routeParams.stops.features.length >= 2) { 
        routeTask.solve(routeParams); 
        lastStop = routeParams.stops.features.splice(0, 1)[0]; 
    } 
}
  1. 创建showRoute()函数,该函数接受RouteResult的一个实例。在此功能中,您唯一需要做的是将从RouteResult对象传递到此功能的路线显示为GraphicsLayer中的折线:
function showRoute(solveResult) { 
    map.graphics.add(solveResult.result.routeResults[0].route.setSymbol(routeSymbol)); 
}
  1. 最后,处理可能出现的任何错误情况。errorHandler()功能应向用户显示一条错误消息,并删除之前成功执行的任何遗留图形:
function errorHandler(err) { 
    alert("An error occured\n" + 
        err.message + "\n" + 
        err.details.join("\n")); 
    routeParams.stops.features.splice(0, 0, lastStop); 
} 
  1. 您可能需要查看您的ArcGISJavaScriptAPI文件夹中的解决方案文件(routing.html),以验证您的代码是否已正确编写。

  2. 单击刷新按钮。你应该会看到下面的地图。如果没有,您可能需要重新检查代码的准确性:

  1. 点击地图上的某个地方。你应该看到一个点标记。

  1. 单击地图上某处的另一个点。这将显示第二个标记以及两点之间的最佳路线,如下图所示:

方向小部件

现在,您已经看到了幕后发生的事情,让我们向您介绍一个非常有用的小部件,它在向应用添加路由功能时消除了一些复杂性。

默认情况下,Directions 小部件访问http://Route . ArcGIS . com/ArcGIS/rest/services/World/Route/NAServer/Route _ World网络服务以提供路由功能,但如果您愿意,可以将其配置为使用另一个网络服务。它还访问另一个服务来提供最新的交通信息。

小部件分析网络服务以确定它具有什么功能,并自动为您构建用户界面。它处理收集输入参数、执行任务、捕获结果并将其显示给用户。您所要做的就是将esri/dijit/Directions模块添加到您的应用中,并将其实例化如下:

var directions = new Directions({ 
    map: map, 
    showSaveButton: true 
},"dir"); 
directions.startup(); 

我在前面的构造函数中设置的最小选项导致了下面的相当复杂的界面,它提供了完整的方向和保存或打印结果的能力:

关闭设施任务

ClosestFacility任务帮助您找到网络上任何位置(称为事故)周围最近的设施。

在查找最近的设施时,您可以指定要查找多少个设施,以及行进方向是朝向还是远离这些设施。最近的设施解算器显示事故和设施之间的最佳路线,报告它们的旅行成本,并返回每个设施的方向。

解决最近设施操作所涉及的类包括 ClosestFacilityParameters、ClosestFacilityTask 和 ClosestFacilitySolveResults:

ClosestFacillityParameters包括输入参数,如默认截止时间、是否返回事故、路线、方向等。这些参数用作包含solve()方法的ClosestFacilityTask的输入。最后,结果以ClosestFacilitySolveResults对象的形式从 ArcGIS Server 传递回客户端。

ClosestFacilityParameters对象用作ClosestFacilityTask的输入。

让我们来看看ClosestFacilityParameters对象的一些比较常用的属性。

incidentsfacilities属性用于设置分析的位置。

任务返回的数据可以通过returnIncidentsreturnRoutesreturnDirections属性进行控制,这些属性只是表示结果中是否应该返回每个属性相关的信息的真值或假值。

travelDirection参数指定是否应该往返于该设施,并且defaultCutoff是截止值,超过该值,分析将停止穿越。下面的代码示例显示了如何创建ClosestFacilityParameters的实例并应用各种属性:

params = new ClosestFacilityParameters(); 
params.defaultCutoff = 3.0; 
params.returnIncidents = false; 
params.returnRoutes = true; 
params.returnDirections = true;

RouteTask一样,当您创建ClosestFacilityTask的新实例时,您将需要引用网络分析服务 URL。一旦创建,ClosestFacilityTask接受由ClosestFacilityParameters提供的输入参数,并使用solve()方法将它们提交给网络分析服务。

solve()方法也接受回调和错误回调函数。为简洁起见,仅使用成功回调函数processResults(),如下所示:

cfTask = new ClosestFacilityTask("http://<domain>/arcgis/rest/services/network/ClosestFacility"); 
params = new ClosestFacilityParameters(); 
params.defaultCutoff = 3.0; 
params.returnIncidents = false; 
params.returnRoutes = true; 
params.returnDirections = true; 
cfTask.solve(params, processResults);

ClosestFacilityTask操作以ClosestFacilitySolveResult对象的形式返回其结果。该对象包含各种属性,如incidents(表示事件的点数组)、facilities(表示设施的点数组)和routes(表示每条路线的图形数组,当ClosestFacilityParameters.returnRoutes设置为true时返回。

directions属性包含一组DirectionsFeatureSet对象。每个都包括描述从事故地点到设施的转弯方向的文本,以及路线的几何形状。该对象中包含的其他信息是路线的长度、沿路线行驶的时间以及预计到达时间。

ClosestFacilitySolveResults中包含的其他属性包括表示返回路线的折线数组、计算中包含的障碍数组以及任务在其操作过程中返回的任何消息。

ServiceArea 任务

ServiceArea任务计算在指定的时间内,您可以在输入位置周围行驶多远。当调查新仓库或类似设施的可能位置时,这是一个特别有用的分析,了解你的操作人员可以服务的区域有多广非常重要。

服务区以分钟为单位定义,是一个包含该时间段内所有可到达街道的区域:

服务区操作中涉及的类包括服务区参数、服务区任务和服务区解决方案结果:

ServiceAreaParameters对象指定任务的输入参数,包括默认时间段的属性、涉及的设施集、分析中使用的任何障碍或其他限制等。

为了执行任务,你实例化一个ServiceAreaTask对象并将ServiceAreaParameters对象传递到它的solve()方法中。结果以ServiceAreaSolveResults对象的形式从 ArcGIS Server 传递回客户端。

ServiceAreaParameters对象上一些比较常用的属性讨论如下。

defaultBreaks属性是定义服务区域的数字数组。例如,在下面的代码示例中,提供了单个值2,表示我们想要返回设施周围的 2 分钟服务区。

当设置为真时,returnFacilities属性指示设施应与结果一起返回。

可以在“障碍”属性中指定点、多段线或多边形形式的障碍。

您可以通过设置travelDirection属性来分析进出设施。还有许多其他属性可以在ServiceAreaParameters上设置,我们敦促您阅读文档以更好地了解任务的功能。

下面的代码示例演示了ClosestFacilityTask的使用:

var params = new ServiceAreaParameters(); 
params.defaultBreaks= [2]; 
params.outSpatialReference = map.spatialReference; 
params.returnFacilities = false; 

var serviceAreaTask = new ServiceAreaTask("https://sampleserver3.arcgisonline.com/ArcGIS/rest/services/Network/USA/NAServer/Service Area"); 

serviceAreaTask.solve(params,function(solveResult){ 
    var polygonSymbol = new SimpleFillSymbol( 
        "solid",   
        new SimpleLineSymbol("solid", new Color([232,104,80]), 2), 
        new Color([232,104,80,0.25]) 
    ); 
arrayUtils.forEach(solveResult.serviceAreaPolygons, function(serviceArea){ 
    serviceArea.setSymbol(polygonSymbol); 
    map.graphics.add(serviceArea); 
}); 

}, function(err){ 
    console.log(err.message); 
});  

摘要

本章向您介绍了从应用中访问网络服务所提供的各种功能。您学习了如何使用RouteTask从一个点找到一个或多个其他点的方向。您看到了方向小部件是如何使其更容易实现,并为您的用户提供一个很好的界面的。然后,您看到了如何使用ClosestFacilityTask从一个或多个起点(事故)找到最近的兴趣点(设施),或者使用ServiceAreaTask计算指定时间段内的行驶时间,这可能有助于您确定响应时间非常重要的停车场或其他设施的位置。

我们在这一章和前几章中看到的所有任务都很棒,但它们的功能仅限于 Esri 实现的任务,有时这并不能解决问题。在下一章中,您将学习如何使用地理处理任务,您可以自己构建、作为服务发布,然后在网络地图应用中使用。