Skip To Content ArcGIS for Developers Sign In Dashboard

Overview

You will learn: how to find optimized routes and directions for delivery and service vehicles with the ArcGIS Vehicle Routing service.

The ArcGIS Transportation Routing and Network Analytics Services can be used to solve transportation and routing problems such as finding the shortest path to locations, calculating drive times, and routing delivery vehicles to multiple destinations. One of the most powerful features is the SolveVehicleRoutingProblem function. This function allows you to solve complex multi-vehicle, multi-stop routing problems. For example, if you have a service center or fleet delivery center, you can determine the most optimum route and directions that each vehicle should take to visit all of the required stops each day. The function requires a number of inputs such as the stop locations, start and finish depots, number of vehicles, maximum number of stops, maximum time at each stop, and the maximum time allotted for each vehicle to complete each route. A wide range of other routing parameters and restrictions can be set as well. Once you have the results you can create delivery schedules, maps, and turn-by-turn directions for vehicles and drivers. To learn more about this service, please visit the documentation.

In this tutorial, you will use the Vehicle Routing Problem service to find optimized routes and directions for an appliance delivery company using two vehicles which start from a warehouse and return to the same location at the end of the working day.

Before you begin

Install Postman to execute HTTP requests. Go to this tutorial if you need an access token.

Steps

Create a request to the vehicle routing service

The vehicle routing service supports both synchronous and asynchronous requests. Since this is a long transaction, make an asynchronous request with the submitJob function. Learn more about synchronous and asynchronous requests here.

  1. Open Postman and click [+] in the tab bar to create a new request.

  2. In the new tab, set the following:

    • HTTP Method: POST
    • Request URL: https://logistics.arcgis.com/arcgis/rest/services/World/VehicleRoutingProblem/GPServer/SolveVehicleRoutingProblem/submitJob

Add parameters to define the depots, routes and orders

In this scenario, two vehicles will depart from a warehouse located in Santa Monica. Each vehicle has a capacity of 4 appliances. Vehicles are not allowed to perform uturns. There are a total of six restaurants to stop at for a maximum of ten minutes at each location. Each vehicle needs to return in 1 hour and can only visit a maximum of 3 locations total. Follow the steps below to define these parameters.

  1. Click on Body > x-www-form-urlencoded and add the following Key/Value pairs:

    • f: json
    • token: Go to this tutorial if you need an access token.
    • populate_directions: true
    • uturn_policy: NO_UTURNS
    • depots: The start and return location for the vehicles.
      {
        "type":"features",
        "features" : [{
          "attributes" : {
              "Name" : "Bay Cities Kitchens & Appliances"
          },
          "geometry" : {
              "x" : -118.469630,
              "y" : 34.037555
          }
        }]
      }
      
    • routes: The routing attributes and constraints for each vehicle.
      {
        "features": [{
            "attributes": {
                "Name": "Route 1",
                "Description": "vehicle 1",
                "StartDepotName": "Bay Cities Kitchens & Appliances",
                "EndDepotName": "Bay Cities Kitchens & Appliances",
                "Capacities": "4",
                "MaxOrderCount": 3,
                "MaxTotalTime": 60,
              }
            },
            {
          "attributes": {
                "Name": "Route 2",
                "Description": "vehicle 2",
                "StartDepotName": "Bay Cities Kitchens & Appliances",
                "EndDepotName": "Bay Cities Kitchens & Appliances",
                "Capacities": "4",
                "MaxOrderCount": 3,
                "MaxTotalTime": 60,
              }
            }
        ]
      }
      
    • orders: A list of the restaurants to stop at and the service time allocated at each location.
      {
        "type": "features",
        "features": [{
            "attributes": {
                "Name": "Father's Office",
                "ServiceTime": 10
            },
            "geometry": {
                "x": -118.498406,
                "y": 34.029445
            }
        },
        {
            "attributes": {
                "Name": "R+D Kitchen",
                "ServiceTime": 10
            },
            "geometry": {
                "x": -118.495788,
                "y": 34.032339
            }
        },
        {
            "attributes": {
                "Name": "Pono Burger",
                "ServiceTime": 10
            },
              "geometry": {
              "x": -118.489469,
              "y": 34.019000
            }
          },
        {
            "attributes": {
                "Name": "Il Ristorante di Giorgio Baldi",
                "ServiceTime": 10
            },
              "geometry": {
              "x": -118.518787,
              "y": 34.028508
            }
          },
        {
            "attributes": {
                "Name": "Milo + Olive",
                "ServiceTime": 10
            },
              "geometry": {
              "x": -118.476026,
              "y": 34.037572
            }
          },
        {
            "attributes": {
                "Name": "Dialogue",
                "ServiceTime": 10
            },
              "geometry": {
              "x": -118.495814,
              "y": 34.017042
            }
          }
        ]
      }
      
      

    Set the date to Saturday, April 14, 2018 in Unix time. The VRP service accepts default-date as Epoch Time (also known as Unix Time), which is the number of milliseconds that have elapsed since 00:00:00 Coordinated Universal Time on 1 January 1970. The time value for this date is 1523664000000 UTC.
    • default_date : 1523664000000

Execute the request

Make an asynchronous request to get the JobId. The JobId value will be used to create the remaining requests.

  1. Click Send to run the request.

  2. In the response window, click Pretty > JSON and it should look something like this since you are using the asynchronous execution mode:

    {
        "JobId": <JobId>,
        "jobStatus": "esriJobSubmitted"
    }
    
  3. Make note of the JobId value as it will be used to make additional requests to the server.

Check the job status

Create a new request with the JobId to determine if the job is complete. Once the job is complete you can use the JobId to retrieve the results.

  1. In the tab bar, click [+] to create a new request. Set the following to check the job status:

    • HTTP Method: POST
    • Request URL: https://logistics.arcgis.com/arcgis/rest/services/World/VehicleRoutingProblem/GPServer/SolveVehicleRoutingProblem/jobs/<JobId> (Replace the <JobId> with yours)

    Click on Body and add the following Key/Value pairs:

    • f: json
    • token: <your token>

    Click Send to run the request. You may need to send it a few times until the job completes.

    In the response window, change the view to JSON and it should look something like this:

    {
        "JobId": "j97f35afbb1764a8da7767d3781ea41ff",
        "jobStatus": "esriJobSucceeded",
        "results": {
            "out_unassigned_stops": {
                "paramUrl": "results/out_unassigned_stops"
            },
            "out_stops": {
                "paramUrl": "results/out_stops"
            },
            "out_routes": {
                "paramUrl": "results/out_routes"
            },
            "out_directions": {
                "paramUrl": "results/out_directions"
            },
            "solve_succeeded": {
                "paramUrl": "results/solve_succeeded"
            },
            "out_network_analysis_layer": {
                "paramUrl": "results/out_network_analysis_layer"
            },
            "out_route_data": {
                "paramUrl": "results/out_route_data"
            }
        },
        "inputs": {
            "orders": {
                "paramUrl": "inputs/orders"
            },
            "depots": {
                "paramUrl": "inputs/depots"
            },
            "routes": {
                "paramUrl": "inputs/routes"
            },
            "breaks": {
                "paramUrl": "inputs/breaks"
            },
            "time_units": {
                "paramUrl": "inputs/time_units"
            },
            "distance_units": {
                "paramUrl": "inputs/distance_units"
            },
            "analysis_region": {
                "paramUrl": "inputs/analysis_region"
            },
            "default_date": {
                "paramUrl": "inputs/default_date"
            },
            "uturn_policy": {
                "paramUrl": "inputs/uturn_policy"
            },
            "time_window_factor": {
                "paramUrl": "inputs/time_window_factor"
            },
            "spatially_cluster_routes": {
                "paramUrl": "inputs/spatially_cluster_routes"
            },
            "route_zones": {
                "paramUrl": "inputs/route_zones"
            },
            "route_renewals": {
                "paramUrl": "inputs/route_renewals"
            },
            "order_pairs": {
                "paramUrl": "inputs/order_pairs"
            },
            "excess_transit_factor": {
                "paramUrl": "inputs/excess_transit_factor"
            },
            "point_barriers": {
                "paramUrl": "inputs/point_barriers"
            },
            "line_barriers": {
                "paramUrl": "inputs/line_barriers"
            },
            "polygon_barriers": {
                "paramUrl": "inputs/polygon_barriers"
            },
            "use_hierarchy_in_analysis": {
                "paramUrl": "inputs/use_hierarchy_in_analysis"
            },
            "restrictions": {
                "paramUrl": "inputs/restrictions"
            },
            "attribute_parameter_values": {
                "paramUrl": "inputs/attribute_parameter_values"
            },
            "populate_route_lines": {
                "paramUrl": "inputs/populate_route_lines"
            },
            "route_line_simplification_tolerance": {
                "paramUrl": "inputs/route_line_simplification_tolerance"
            },
            "populate_directions": {
                "paramUrl": "inputs/populate_directions"
            },
            "directions_language": {
                "paramUrl": "inputs/directions_language"
            },
            "directions_style_name": {
                "paramUrl": "inputs/directions_style_name"
            },
            "travel_mode": {
                "paramUrl": "inputs/travel_mode"
            },
            "impedance": {
                "paramUrl": "inputs/impedance"
            },
            "time_zone_usage_for_time_fields": {
                "paramUrl": "inputs/time_zone_usage_for_time_fields"
            },
            "save_output_layer": {
                "paramUrl": "inputs/save_output_layer"
            },
            "overrides": {
                "paramUrl": "inputs/overrides"
            },
            "save_route_data": {
                "paramUrl": "inputs/save_route_data"
            }
        },
        "messages": [
            {
                "type": "esriJobMessageTypeWarning",
                "description": "Network elements with avoid-restrictions are traversed in the output (restriction attribute names: \"Through Traffic Prohibited\").\n"
            }
        ]
    }
    
  2. In the JSON, explore the following results:

    • jobStatus: Indicates if the job was successful or not.
    • results: All of the results available.
      • out_xxx: Name of the result that can be retrieved.
      • paramUrl: The location of the result to be retrieved.
    • inputs: All of the input parameters (including the defaults) used to calculate the results.
    • messages: Feedback about the status of the route calculation including error messages.

Get the routes

Create a new request to get the route results. The routes include the geometries of the optimized paths each vehicle should take to complete the delivery route.

  1. In the tab bar, click [+] to create a new request. Set the following to retrieve the route results:

    • HTTP Method: POST
    • Request URL: https://logistics.arcgis.com/arcgis/rest/services/World/VehicleRoutingProblem/GPServer/SolveVehicleRoutingProblem/jobs/<JobId>/results/out_routes (Replace the <JobId> with yours)

    Click on Body and add the following Key/Value pairs:

    • f: json
    • token: <your token>

    Click Send to run the request.

  2. In the JSON response, use the find tool to explore the values of the following results:

    • geometryType: Usually esriGeometryPolyline, this indicates the type of geometry of the route.
    • fields: A list of the field types for each route.
    • features: An array that contains the attribute values and the line geometry for each route.

    How many route features were returned?

Get the stops

Create a new request to get the stops results. The stops include all of the ordered stops each vehicle should visit to complete each delivery route. This includes the start and finish stops from the depot.

  1. In the tab bar, click [+] to create a new request. Set the following to get the stop results:

    • HTTP Method: POST
    • Request URL: https://logistics.arcgis.com/arcgis/rest/services/World/VehicleRoutingProblem/GPServer/SolveVehicleRoutingProblem/jobs/<JobId>/results/out_stops (Replace the <JobId> with yours)

    Click on Body and add the following Key/Value pairs:

    • f: json
    • token: <your token>

    Click Send to run the request.

  2. In the JSON response, use the find tool to explore the following results:

    • RouteName: The name of the route that makes the stop.
    • Sequence: The relative sequence in which the assigned route visits the stop.
    • ArriveTime: The time of day when the route arrives at the stop. The time-of-day value for this attribute is in the time zone in which the stop is located.
    • DepartTime: The time of day when the route departs from the stop. The time-of-day value for this attribute is in the time zone in which the stop is located.

    How many stops were returned?

Get the directions

Create a new request to get the directions results. The directions include all of the line segments and turn-by-turn directions each vehicle should follow to complete each delivery route.

  1. In the tab bar, click [+] to create a new request. Set the following to get the direction results:

    • HTTP Method: POST
    • Request URL: https://logistics.arcgis.com/arcgis/rest/services/World/VehicleRoutingProblem/GPServer/SolveVehicleRoutingProblem/jobs/<JobId>/results/out_directions (Replace the <JobId> with yours)

    Click on Body and add the following Key/Value pairs:

    • f: json
    • token: <your token>

    Click Send to run the request.

  2. In the JSON response, use the find tool to explore the following results:

    • fields: A list of the attribute information available including turn-by-turn text.
    • features: An array of features representing each line segment with directions, time, and distance.

    How many lines with turn-by-turn directions were returned?

Congratulations, you're done!

You have successfully calculated the optimized routes for an appliance delivery company to find routes for two vehicles which start from the warehouse and return to the same at the end of the working day. If you want to see what the results look like on a map, open this web map solution that was created in the ArcGIS Online - Route multiple vehicles tutorial.

Challenge

Reduce the total route time per vehicle

Go back to Step 3 and change the MaxTotalTime to 30 minutes and then run all of the requests again.

How do the routing results differ from the first analysis? Why?

Add time windows to your orders

You can add time windows to orders to restrict the arrival time at a location. Use the TimeWindowStart1, TimeWindowEnd1 and TimeWindowStart2, TimeWindowEnd2 attributes to set these values.

If you use time windows in date time values, you have to specify default_date. The parameter value should be specified as a numeric value representing the milliseconds since midnight January 1, 1970. The time_zone_usage_for_time_fields parameter indicates if the time fields are in the local time zone for that location or if all the times are in UTC for the time fields. This parameter can be specified as GEO_LOCAL or UTC.

For example, let's say the first order named "Pono Burger" is located in Santa Monica and needs to be serviced on April 14, 2018, between 1:00 pm and 3:00 pm Pacific Time. Since other orders could potentially be in other time zones, it is better to specify the times in UTC for all the orders instead of local time. By default, the service assumes that the time constraints are local times, so this would have to be changed to UTC time. So the TimeWindowStart1 - April 14, 2018, 1:00 pm PDT would be April 14, 2018, 8:00 pm UTC and the TimeWindowEnd1 - April 14, 2018, 3:00 pm PDT would be April 14, 2018, 10:00 pm UTC. You will find many converters online to convert local/UTC time to unix time and vice versa. Using one of these converters you will find the epoch time for TimeWindowStart1 is 1523563200000 UTC and that for TimeWIndowEnd1 is 1523570400000 UTC.

The JSON for this request would look like:

{
    "attributes": {
        "Name": "Pono Burger",
        "ServiceTime": 10,
        "TimeWindowStart1": 1523563200000,
        "TimeWindowEnd1": 1523570400000,
    },
        "geometry": {
        "x": -118.489469,
        "y": 34.019000
    }
}

To learn more, visit the Vehicle Routing Problem service documentation.