Why the findBestSequence doesn't get the best optimized result from ArcGIS Online data?

761
2
Jump to solution
09-25-2023 07:59 PM
LeoDeng
Frequent Contributor

Hi All,

    The requirement is solve a route with best sequence by the given stops. However, when solve with the service "https://route-api.arcgis.com/arcgis/rest/services/World/Route/NAServer/Route_World", the result doesn't make sense for me.

This is the original sequence stops and route.

LeoDeng_0-1695696422837.png

This green route is the response which findBestSequence is true.

LeoDeng_1-1695696492390.png

 

The red route is the expectation result.

LeoDeng_3-1695697009531.png

 




Attach is the code implemented by ArcGIS Map SDKs for JavaScript version 4.27

<!DOCTYPE html lang="en">
  <head>
  <meta charset="utf-8" />
  <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no" />
  <title>Network - Find Best Sequence</title>
  
  <style>
    html,
    body,
    #viewDiv { padding: 0; margin: 0; height: 100%; width: 100%; };

    .buttons { position: absolute; top: 50px; right: 50px; z-index: 1; }
    
    .best-sequence { top: 50px; right: 50px; z-index: 1; };

    .optimize-layers { top: 100px; right: 50px; z-index: 1; };
  </style>
  <link rel="stylesheet" href="https://js.arcgis.com/4.27/esri/themes/light/main.css">
  <style>
    /* overwrite esri default css */
    .esri-view .esri-view-surface--inset-outline:focus::after {
      outline: none;
    };
  </style>
  <script src="https://js.arcgis.com/4.27/"></script>
  <script>
    let _findBestSequence = null, _toggleOptimizeLayers = null;

    require([
      "esri/config", "esri/intl", "esri/Map", "esri/views/MapView", "esri/geometry/Point", "esri/geometry/Polyline",
      "esri/symbols/SimpleLineSymbol", "esri/Graphic", "esri/layers/GraphicsLayer", "esri/rest/support/FeatureSet",
      "esri/symbols/PictureMarkerSymbol", "esri/rest/route", "esri/rest/support/RouteParameters"],
    function(
      esriConfig, esriIntl, Map, MapView, Point, Polyline,
      SimpleLineSymbol, Graphic, GraphicsLayer, FeatureSet,
      PictureMarkerSymbol, route, RouteParameters) {

      esriConfig.apiKey = "<API_KEY_WITH_OPTIMIZED_ROUTING>";

      esriIntl.setLocale("en");

      let _map, _stopLayer, _routeLayer, _bestStopLayer, _bestRouteLayer;
      let _stop1, _stop2, _stop3, _stop4, _stop5, _stop6, _stop7, _stop8, _stop9;

      const CURB_APPROACH = {
        EITHER_SIDE: 0,
        RIGHT_SIDE: 1,
        LEFT_SIDE: 2,
        NO_U_TURN: 3
      };

      const initMap = () =>
      {
        _map = new Map({
          basemap: "arcgis-streets" // Basemap layer service
        });

        const CREEK_VIEW_PLZ = [-73.924596, 42.784273];
        const CONSTRAINT_EXTENT = {
          type: "extent",
          xmin: -180,
          ymin: -70,
          xmax: 180,
          ymax: 70
        };

        const view = new MapView({
          map: _map,
          center: CREEK_VIEW_PLZ,
          constraints: {
            geometry: CONSTRAINT_EXTENT,
            minZoom: 3,
            maxZoom: 18
          },
          zoom: 13,
          container: "viewDiv"
        });

        _stopLayer = new GraphicsLayer({ id: "stop-layer" });
        _routeLayer = new GraphicsLayer({ id: "route-layer" });
        _bestStopLayer = new GraphicsLayer({ id: "best-stop-layer" });
        _bestRouteLayer = new GraphicsLayer({ id: "best-route-layer" });

        _map.addMany([_routeLayer, _stopLayer, _bestRouteLayer, _bestStopLayer]);
      };

      const stopSymbol = (sequence, fillColor, textColor) =>
      {
        const svgString = '<svg xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" width="28" height="28">' +
          '<g>' +
          `<circle r="12" cy="14" cx="14" stroke-linecap="butt" stroke="#000000" stroke-width="2" fill="${fillColor}" />` +
          `<text text-anchor="middle" font-size="12" x="50%" y="50%" dy=".3em" stroke-width="0" fill="${textColor}" >${sequence}</text>` +
          '</g>' +
          '</svg >';
        const svg = `data&colon;image/svg+xml;charset=UTF-8;base64,${btoa(svgString)}`;
        return new PictureMarkerSymbol({ url: svg, height: 28, width: 28 });
      };

      const createStopGraphic = (location, sequence, curbApproach) =>
      {
        const { longitude, latitude } = location;
        const geometry = new Point({
          type: "point",
          x: longitude,
          y: latitude
        });
        const symbol = stopSymbol(sequence, "#0000FF", "#FFFFFF");
        const attributes = {
          CurbApproach: curbApproach,
          Name: sequence,
          Sequence: sequence
        };

        const graphic = new Graphic({ geometry, symbol, attributes });
        return graphic;
      };

      const addStops = () =>
      {
        const location1 = { longitude: -73.946891, latitude: 42.808323 };
        const location2 = { longitude: -73.938998, latitude: 42.783614 };
        const location3 = { longitude: -73.937482, latitude: 42.77529 };
        const location4 = { longitude: -73.921461, latitude: 42.773114 };
        const location5 = { longitude: -73.900918, latitude: 42.774755 };
        const location6 = { longitude: -73.932357, latitude: 42.764845 };
        const location7 = { longitude: -73.904294, latitude: 42.788485 };
        const location8 = { longitude: -73.897857, latitude: 42.781619 };
        const location9 = { longitude: -73.89135, latitude: 42.763158 };

        _stop1 = createStopGraphic(location1, 1, CURB_APPROACH.RIGHT_SIDE);
        _stop2 = createStopGraphic(location2, 2, CURB_APPROACH.RIGHT_SIDE);
        _stop3 = createStopGraphic(location3, 3, CURB_APPROACH.RIGHT_SIDE);
        _stop4 = createStopGraphic(location4, 4, CURB_APPROACH.RIGHT_SIDE);
        _stop5 = createStopGraphic(location5, 5, CURB_APPROACH.RIGHT_SIDE);
        _stop6 = createStopGraphic(location6, 6, CURB_APPROACH.RIGHT_SIDE);
        _stop7 = createStopGraphic(location7, 7, CURB_APPROACH.RIGHT_SIDE);
        _stop8 = createStopGraphic(location8, 8, CURB_APPROACH.RIGHT_SIDE);
        _stop9 = createStopGraphic(location9, 9, CURB_APPROACH.RIGHT_SIDE);

        _stopLayer.addMany([_stop1, _stop2, _stop3, _stop4, _stop5, _stop6, _stop7, _stop8, _stop9]);
      };

      const calculateRoute = async (parameters) =>
      {
        const url = "https://route-api.arcgis.com/arcgis/rest/services/World/Route/NAServer/Route_World";
        const defaultParameters = {
          directionsLengthUnits: "kilometers",
          findBestSequence: false,
          ignoreInvalidLocations: false,
          impedanceAttribute: null,
          outputGeometryPrecision: 0,
          outputGeometryPrecisionUnits: "feet",
          outputLines: "true-shape",
          outSpatialReference: "102100",
          pointBarriers: null,
          polylineBarriers: null,
          polygonBarriers: null,
          preserveFirstStop: true,
          preserveLastStop: true,
          restrictionAttributes: [],
          restrictUTurns: "at-dead-ends-only",
          returnBarriers: false,
          returnDirections: true,
          returnPolygonBarriers: false,
          returnPolylineBarriers: false,
          returnPointBarriers: false,
          returnTraversedEdges: false,
          returnTraversedJunctions: false,
          returnTraversedTurns: false,
          returnRoutes: true,
          returnStops: true,
          returnZ: false,
          startTime: new Date()
        };

        const params = Object.assign({}, defaultParameters, parameters);
        const routeParameters = new RouteParameters(params);

        return route.solve(url, routeParameters).then((response) => {
          results = response?.routeResults[0];
          return results;
        }).catch((error) => {
          console.log(error);
        });
      };

      const addRoutes = (routeResults, routeColor, routeLayer) =>
      {
        const route = routeResults.route;
        route.symbol = {
          type: "simple-line",
          style: "solid",
          color: routeColor,
          width: 5
        };

        routeLayer.removeAll();
        routeLayer.add(route);
      };

      const addBestStops = (routeResults) =>
      {
        const stops = routeResults.stops;
        stops.forEach(item =>
        {
          item.symbol = stopSymbol(item.attributes.Sequence, "#00FF00", "#000000");
        });

        _bestStopLayer.addMany(stops);
      }

      _findBestSequence = () =>
      {
        if (_bestRouteLayer.graphics.length > 0)
        {
          return;
        }

        // THE MOCKUP DATA IS CALCULATED BY route-api.arcgis.com
        // const routeResults = _getMockupRouteResult();
        // if (routeResults)
        // {
        //  addRoutes(routeResults, "#00FF00", _bestRouteLayer);

        //  addBestStops(routeResults);
        //  return;
        //}


        const featureSet = new FeatureSet({ features: [_stop1, _stop2, _stop3, _stop4, _stop5, _stop6, _stop7, _stop8, _stop9]});
        const parameters = {
          stops: featureSet,
          findBestSequence: true,
        };

        calculateRoute(parameters).then((routeResults) =>
        {
          addRoutes(routeResults, "#00FF00", _bestRouteLayer);

          addBestStops(routeResults);
        });
      };

      _toggleOptimizeLayers = () =>
      {
        _bestRouteLayer.visible = !_bestRouteLayer.visible;
        _bestStopLayer.visible = !_bestStopLayer.visible;
      };

      const _getMockupRouteResult = () =>
      {
        const routeResultString = `73.88902999999993,42.76705000000004],[-73.88870999999995,42.766770000000065],[-73.88854999999995,42.76663000000008],[-73.88834999999995,42.766440000000046],[-73.88830999999993,42.76640000000003],[-73.88828999999998,42.76638000000003],[-73.88818999999995,42.76628000000005],[-73.88808999999998,42.76619000000005],[-73.88774999999998,42.765870000000064],[-73.88760999999994,42.76574000000005],[-73.88750999999996,42.76565000000005],[-73.88741999999996,42.765570000000025],[-73.88697999999994,42.76517000000007],[-73.88711999999998,42.76508000000007],[-73.88724999999994,42.76499000000007],[-73.88742999999994,42.76487000000003],[-73.88757999999996,42.764770000000055],[-73.88771999999994,42.76469000000003],[-73.88783999999998,42.76461000000006],[-73.88880999999998,42.76400000000007],[-73.88968999999997,42.76352000000003],[-73.89104508699995,42.762671209000075]]]},"symbol":null,"attributes":{"ObjectID":1,"Name":"1 - 9","FirstStopID":1,"LastStopID":9,"StopCount":9,"StartTime":1695679888215,"EndTime":1695682127523,"StartTimeUTC":1695694288215,"EndTimeUTC":1695696527523,"Total_TravelTime":37.32144021498387,"Total_Miles":14.305202978168186,"Total_Kilometers":23.02205399019508,"Shape_Length":0.23989589944373005},"popupTemplate":null},"routeName":"1 - 9","stops":[{"aggregateGeometries":null,"geometry":{"spatialReference":{"latestWkid":4326,"wkid":4326},"x":-73.94689099999994,"y":42.80832300000003},"symbol":null,"attributes":{"ObjectID":1,"Name":"1","RouteName":null,"Sequence":1,"TimeWindowStart":null,"TimeWindowEnd":null,"ArriveCurbApproach":null,"DepartCurbApproach":1,"ArriveTime":1695679888215,"DepartTime":1695679888215,"ArriveTimeUTC":1695694288215,"DepartTimeUTC":1695694288215,"LocationType":0,"SourceID":1,"SourceOID":63147648,"PosAlong":0.34237244765307046,"SideOfEdge":2,"CurbApproach":1,"Status":0,"SnapX":-73.94672033928657,"SnapY":42.80822427066172,"SnapZ":0,"DistanceToNetworkInMeters":17.749139007493312,"Attr_Minutes":0,"Attr_TravelTime":0,"Attr_Miles":0,"Attr_Kilometers":0,"Attr_TimeAt1KPH":0,"Attr_WalkTime":0,"Attr_TruckMinutes":0,"Attr_TruckTravelTime":0,"Cumul_TravelTime":0,"Cumul_Miles":0,"Cumul_Kilometers":0},"popupTemplate":null},{"aggregateGeometries":null,"geometry":{"spatialReference":{"latestWkid":4326,"wkid":4326},"x":-73.93899799999997,"y":42.78361400000006},"symbol":null,"attributes":{"ObjectID":2,"Name":"2","RouteName":null,"Sequence":4,"TimeWindowStart":null,"TimeWindowEnd":null,"ArriveCurbApproach":1,"DepartCurbApproach":1,"ArriveTime":1695680734292,"DepartTime":1695680734292,"ArriveTimeUTC":1695695134292,"DepartTimeUTC":1695695134292,"LocationType":0,"SourceID":1,"SourceOID":63146091,"PosAlong":0.5636337747364589,"SideOfEdge":2,"CurbApproach":1,"Status":0,"SnapX":-73.93886363519862,"SnapY":42.78365781460919,"SnapZ":0,"DistanceToNetworkInMeters":12.012903327686413,"Attr_Minutes":0,"Attr_TravelTime":0,"Attr_Miles":0,"Attr_Kilometers":0,"Attr_TimeAt1KPH":0,"Attr_WalkTime":0,"Attr_TruckMinutes":0,"Attr_TruckTravelTime":0,"Cumul_TravelTime":14.101278520945046,"Cumul_Miles":6.0195674111523445,"Cumul_Kilometers":9.687540583087975},"popupTemplate":null},{"aggregateGeometries":null,"geometry":{"spatialReference":{"latestWkid":4326,"wkid":4326},"x":-73.93748199999999,"y":42.77529000000004},"symbol":null,"attributes":{"ObjectID":3,"Name":"3","RouteName":null,"Sequence":3,"TimeWindowStart":null,"TimeWindowEnd":null,"ArriveCurbApproach":1,"DepartCurbApproach":1,"ArriveTime":1695680526217,"DepartTime":1695680526217,"ArriveTimeUTC":1695694926217,"DepartTimeUTC":1695694926217,"LocationType":0,"SourceID":1,"SourceOID":63138337,"PosAlong":0.45030927837121815,"SideOfEdge":1,"CurbApproach":1,"Status":0,"SnapX":-73.9374952783505,"SnapY":42.77526012371141,"SnapZ":0,"DistanceToNetworkInMeters":3.4972226978186014,"Attr_Minutes":0,"Attr_TravelTime":0,"Attr_Miles":0,"Attr_Kilometers":0,"Attr_TimeAt1KPH":0,"Attr_WalkTime":0,"Attr_TruckMinutes":0,"Attr_TruckTravelTime":0,"Cumul_TravelTime":10.633365513763083,"Cumul_Miles":5.046930037515004,"Cumul_Kilometers":8.122276568847795},"popupTemplate":null},{"aggregateGeometries":null,"geometry":{"spatialReference":{"latestWkid":4326,"wkid":4326},"x":-73.92146099999997,"y":42.77311400000008},"symbol":null,"attributes":{"ObjectID":4,"Name":"4","RouteName":null,"Sequence":5,"TimeWindowStart":null,"TimeWindowEnd":null,"ArriveCurbApproach":1,"DepartCurbApproach":1,"ArriveTime":1695681105343,"DepartTime":1695681105343,"ArriveTimeUTC":1695695505343,"DepartTimeUTC":1695695505343,"LocationType":0,"SourceID":1,"SourceOID":63138761,"PosAlong":0.4422169742862514,"SideOfEdge":2,"CurbApproach":1,"Status":0,"SnapX":-73.92093689562441,"SnapY":42.77326540793081,"SnapZ":0,"DistanceToNetworkInMeters":46.023911694552666,"Attr_Minutes":0,"Attr_TravelTime":0,"Attr_Miles":0,"Attr_Kilometers":0,"Attr_TimeAt1KPH":0,"Attr_WalkTime":0,"Attr_TruckMinutes":0,"Attr_TruckTravelTime":0,"Cumul_TravelTime":20.285097158798607,"Cumul_Miles":8.172199575981388,"Cumul_Kilometers":13.151826698408081},"popupTemplate":null},{"aggregateGeometries":null,"geometry":{"spatialReference":{"latestWkid":4326,"wkid":4326},"x":-73.90091799999993,"y":42.77475500000003},"symbol":null,"attributes":{"ObjectID":5,"Name":"5","RouteName":null,"Sequence":6,"TimeWindowStart":null,"TimeWindowEnd":null,"ArriveCurbApproach":1,"DepartCurbApproach":1,"ArriveTime":1695681424473,"DepartTime":1695681424473,"ArriveTimeUTC":1695695824473,"DepartTimeUTC":1695695824473,"LocationType":0,"SourceID":1,"SourceOID":63139130,"PosAlong":0.8245841679039545,"SideOfEdge":1,"CurbApproach":1,"Status":0,"SnapX":-73.90102890910511,"SnapY":42.77492064346879,"SnapZ":0,"DistanceToNetworkInMeters":20.54614900282448,"Attr_Minutes":0,"Attr_TravelTime":0,"Attr_Miles":0,"Attr_Kilometers":0,"Attr_TimeAt1KPH":0,"Attr_WalkTime":0,"Attr_TruckMinutes":0,"Attr_TruckTravelTime":0,"Cumul_TravelTime":25.603939080683475,"Cumul_Miles":9.868476490319022,"Cumul_Kilometers":15.881658860608237},"popupTemplate":null},{"aggregateGeometries":null,"geometry":{"spatialReference":{"latestWkid":4326,"wkid":4326},"x":-73.93235699999997,"y":42.76484500000004},"symbol":null,"attributes":{"ObjectID":6,"Name":"6","RouteName":null,"Sequence":2,"TimeWindowStart":null,"TimeWindowEnd":null,"ArriveCurbApproach":1,"DepartCurbApproach":1,"ArriveTime":1695680344685,"DepartTime":1695680344685,"ArriveTimeUTC":1695694744685,"DepartTimeUTC":1695694744685,"LocationType":0,"SourceID":1,"SourceOID":63138157,"PosAlong":0.1916291835017726,"SideOfEdge":2,"CurbApproach":1,"Status":0,"SnapX":-73.93245806896545,"SnapY":42.76460917241386,"SnapZ":0,"DistanceToNetworkInMeters":27.52091696371127,"Attr_Minutes":0,"Attr_TravelTime":0,"Attr_Miles":0,"Attr_Kilometers":0,"Attr_TimeAt1KPH":0,"Attr_WalkTime":0,"Attr_TruckMinutes":0,"Attr_TruckTravelTime":0,"Cumul_TravelTime":7.607832029575178,"Cumul_Miles":4.0238698180457675,"Cumul_Kilometers":6.475802534674298},"popupTemplate":null},{"aggregateGeometries":null,"geometry":{"spatialReference":{"latestWkid":4326,"wkid":4326},"x":-73.90429399999994,"y":42.78848500000004},"symbol":null,"attributes":{"ObjectID":7,"Name":"7","RouteName":null,"Sequence":8,"TimeWindowStart":null,"TimeWindowEnd":null,"ArriveCurbApproach":1,"DepartCurbApproach":1,"ArriveTime":1695681710194,"DepartTime":1695681710194,"ArriveTimeUTC":1695696110194,"DepartTimeUTC":1695696110194,"LocationType":0,"SourceID":1,"SourceOID":63149967,"PosAlong":0.24651097243960704,"SideOfEdge":1,"CurbApproach":1,"Status":0,"SnapX":-73.90429443322716,"SnapY":42.788485495521385,"SnapZ":0,"DistanceToNetworkInMeters":0,"Attr_Minutes":0,"Attr_TravelTime":0,"Attr_Miles":0,"Attr_Kilometers":0,"Attr_TimeAt1KPH":0,"Attr_WalkTime":0,"Attr_TruckMinutes":0,"Attr_TruckTravelTime":0,"Cumul_TravelTime":30.36594730808806,"Cumul_Miles":11.373253179390383,"Cumul_Kilometers":18.303373785743467},"popupTemplate":null},{"aggregateGeometries":null,"geometry":{"spatialReference":{"latestWkid":4326,"wkid":4326},"x":-73.89785699999999,"y":42.781619000000035},"symbol":null,"attributes":{"ObjectID":8,"Name":"8","RouteName":null,"Sequence":7,"TimeWindowStart":null,"TimeWindowEnd":null,"ArriveCurbApproach":1,"DepartCurbApproach":1,"ArriveTime":1695681550048,"DepartTime":1695681550048,"ArriveTimeUTC":1695695950048,"DepartTimeUTC":1695695950048,"LocationType":0,"SourceID":1,"SourceOID":63149749,"PosAlong":0.4030693159607203,"SideOfEdge":1,"CurbApproach":1,"Status":0,"SnapX":-73.89795730532335,"SnapY":42.78176045622528,"SnapZ":0,"DistanceToNetworkInMeters":17.751683431579593,"Attr_Minutes":0,"Attr_TravelTime":0,"Attr_Miles":0,"Attr_Kilometers":0,"Attr_TimeAt1KPH":0,"Attr_WalkTime":0,"Attr_TruckMinutes":0,"Attr_TruckTravelTime":0,"Cumul_TravelTime":27.69685045415899,"Cumul_Miles":10.536587654594042,"Cumul_Kilometers":16.956892682908844},"popupTemplate":null},{"aggregateGeometries":null,"geometry":{"spatialReference":{"latestWkid":4326,"wkid":4326},"x":-73.89134999999999,"y":42.76315800000003},"symbol":null,"attributes":{"ObjectID":9,"Name":"9","RouteName":null,"Sequence":9,"TimeWindowStart":null,"TimeWindowEnd":null,"ArriveCurbApproach":1,"DepartCurbApproach":null,"ArriveTime":1695682127523,"DepartTime":1695682127523,"ArriveTimeUTC":1695696527523,"DepartTimeUTC":1695696527523,"LocationType":0,"SourceID":1,"SourceOID":63138941,"PosAlong":0.2554466608713769,"SideOfEdge":2,"CurbApproach":1,"Status":7,"SnapX":-73.89104508707719,"SnapY":42.76267120919345,"SnapZ":0,"DistanceToNetworkInMeters":59.64467303334708,"Attr_Minutes":0,"Attr_TravelTime":0,"Attr_Miles":0,"Attr_Kilometers":0,"Attr_TimeAt1KPH":0,"Attr_WalkTime":0,"Attr_TruckMinutes":0,"Attr_TruckTravelTime":0,"Cumul_TravelTime":37.32144021498387,"Cumul_Miles":14.305202978168186,"Cumul_Kilometers":23.02205399019508},"popupTemplate":null}]}'`;
        return JSON.parse(routeResultString);
      };

      initMap();
      addStops();

      const featureSet = new FeatureSet({ features: [_stop1, _stop2, _stop3, _stop4, _stop5, _stop6, _stop7, _stop8, _stop9]});
      const parameters = {
        stops: featureSet
      };
      calculateRoute(parameters).then((routeResults) =>
      {
        addRoutes(routeResults, "#0000FF", _routeLayer);
      });
    });

    const findBestSequence = () => _findBestSequence();
    const toggleOptimizeLayers = () => _toggleOptimizeLayers();
  </script>

  </head>
  <body>
    <div class="buttons">
      <button class="best-sequence" onclick="findBestSequence()">Find Best Sequence</button>
      <button class="optimize-layers" onclick="toggleOptimizeLayers()">Show/Hide Best Sequence Layers</button>
    </div>
    <div id="viewDiv"></div>
  </body>
</html>

 

 

0 Kudos
1 Solution

Accepted Solutions
LeoDeng
Frequent Contributor

As the document said, "As a result, the services revert to hierarchy if the straight-line distance between the stops is greater than 50 miles (80.46 kilometers), even if you have specified to find the route without using hierarchy."

reference

View solution in original post

0 Kudos
2 Replies
LeoDeng
Frequent Contributor

One of the possible explanation I guess is that ArcGIS Online prefer useHierarchy.

However, when set the route parameters useHierarchy to false, the result is also incomprehensible for me.
LeoDeng_0-1695699815586.png

 




0 Kudos
LeoDeng
Frequent Contributor

As the document said, "As a result, the services revert to hierarchy if the straight-line distance between the stops is greater than 50 miles (80.46 kilometers), even if you have specified to find the route without using hierarchy."

reference

0 Kudos