LogisticsOS: The best route optimization API for ETA accuracy with real-time traffic data

Using the LogisticsOS API to add real-time traffic to your route optimization system.

LogisticsOS: the ultimate route optimization API for ETA accuracy with real-time traffic data

In my last two blog posts, I showed how to improve the accuracy of your ETAs (estimated arrival times) by incorporating real-time traffic from a third-party data provider.

Part 1: Real-time traffic route optimization with Google Maps
Part 2: Real-time traffic with the Mapbox Matrix API
Part 3: The best route optimization API for ETA accuracy with real-time traffic data (this article)

Even though both these methods are simple and fast to use, they have notable disadvantages:

  • With Google Maps' route optimization API, real-time traffic information could only be incorporated after the route optimization algorithm was completed. This means that there is a risk that the updated route might not take important factors, such as stop time windows, into account.
  • Mapbox's API has a restriction of 625 elements per distance matrix, which makes it unsuitable for larger optimization problems with more than 25 stops and drivers.

In this blog post, I'll introduce a third option, the LogisticsOS route optimization API. It allows you to enable real-time traffic with just three lines of code like so:

{
  "solver_parameters": {
    "map_provider": "here",
    "traffic_type": "predictive",
    "traffic_time": "2022-12-01T14:00:00-05:00"
  }
}

Why the LogisticsOS route optimization API works so well

LogisticsOS works as an intermediary of sorts. When you send the LogisticsOS route optimization API a list of stops and drivers to optimize, the heavy lifting is done by their proprietary routing engine.

But instead of using a static distance matrix, LogisticsOS integrates live traffic information from HERE Maps to provide their customers with a route optimization API that takes into account real-time traffic (updated every 15 minutes).

Here's a worked example with one driver and three stops:

vehicle_id depot_id coordinates
driver_01 yvr-airport 49.1966913,-123.18370
order_id order_name coordinates
ABC-123 Brassneck Brewery 49.265, -123.101
DEF-456 Zakkushi on Denman 49.291, -123.138
GHI-789 Granville Island Brewing 49.270, -123.136

Using HERE Maps as our traffic source, here's what the API call looks like:

Endpoint POST https://api.logisticsos.com/v2/vrp

Headers
Content-Type: application/json
x-api-key: YOUR_API_KEY (email sales@logisticsos.com to get access)

Body

{
  "orders": [
    {
      "id": "ABC-123",
      "geometry": {
        "lat": 49.265,
        "lon": -123.101
      },
      "service": {
        "dropoff_quantities": 1,
        "duration": 5
      },
      "time_window": {
        "start": 900,
        "end": 1440
      }
    },
    {
      "id": "DEF-456",
      "geometry": {
        "lat": 49.291,
        "lon": -123.138
      },
      "service": {
        "dropoff_quantities": 1,
        "duration": 5
      },
      "time_window": {
        "start": 900,
        "end": 1440
      }
    },
    {
      "id": "GHI-789",
      "geometry": {
        "lat": 49.27,
        "lon": -123.136
      },
      "service": {
        "dropoff_quantities": 1,
        "duration": 5
      },
      "time_window": {
        "start": 900,
        "end": 1440
      }
    }
  ],
  "start_depots": [
    {
      "id": "yvr-airport",
      "geometry": {
        "lat": 49.1966913,
        "lon": -123.1837
      }
    }
  ],
  "vehicle_types": [
    {
      "id": "0",
      "profile": "car",
      "count": 1,
      "capacity": 10,
      "dispatch_after": 900,
      "dismiss_before": 1440,
      "depots": {
        "start_depot": "yvr-airport",
        "end_depot": "none"
      }
    }
  ],
  "units": {
    "distance": "meter",
    "duration": "minute"
  },
  "solver_parameters": {
    "map_provider": "here",
    "traffic_type": "predictive",
    "traffic_time": "2023-03-06T15:00:00-08:00"
  }
}

LogisticOS's real-time traffic parameters are stored in the solver_parameters object (docs).

map_provider (string) indicates what data source you are using to calculate the origin destination matrix. The default map provider is Open Street Maps ("osm") but if you want your optimization to consider real-time traffic, you must change this to HERE Maps ("here").

traffic_type (string) lets you specify what kind of traffic information you want to use. For dates in the future (a common scenario is last mile route optimization is planning routes several days in advance), you should use "predictive". HERE Maps will then run machine learning algorithms on historical traffic data to predict how traffic will look like on the date you've chosen.

traffic_time (date-time string) is used to retrieve real-time traffic information for a specific date and time. For example, "2021-01-01T14:00:00-08:00" means 2021 1 Jan, 14:00 hrs, UTC-08:00 (Pacific Standard Time). To improve accuracy, traffic_time should be as close as possible to the vehicle's dispatch_after field e.g. if dispatch_after is at minute 900 (or 15:00 hrs) on 12 Dec 2022, you should use "2022-12-01T15:00:00-05:00" for traffic_time.

The above API call results in the following route solution:

{
    "job_id": "ba23a4ed-6039-4d46-a47f-ed8b7681c888",
    "status": "SUCCEED",
    "solver_time": "1ms",
    "plan_summary": {
        "distance": 21707.0,
        "total_time": 58.8,
        "travel_time": 43.8,
        "wait_time": 0.0,
        "late_time": 0.0,
        "service_time": 15.0,
        "num_routes": 1,
        "unassigned": 0,
        "assigned": 3,
        "assigned_pairs": 0,
        "average_speed": "29.73 km/h",
        "total_cost": 0.0
    },
    "routes": [
        {
            "summary": {
                "distance": 21707.0,
                "total_time": 58.8,
                "travel_time": 43.8,
                "service_time": 15.0,
                "begin_time": 900.0,
                "end_time": 958.8,
                "wait_time": 0.0,
                "late_time": 0.0,
                "pickup_quantity": 0.0,
                "dropoff_quantity": 3.0,
                "paired_pickup_quantity": 0.0,
                "paired_delivery_quantity": 0.0,
                "capacity_utilization": 0.75,
                "num_breaks": 0,
                "num_orders": 3,
                "num_waypoints": 4,
                "vehicle_id": "0",
                "route_id": 0,
                "profile": "car"
            },
            "stops": [
                {
                    "id": "yvr-airport",
                    "position": 0,
                    "arrival_time": 900.0,
                    "wait_time": 0.0,
                    "service_time": 0.0,
                    "late_time": 0.0,
                    "depart_time": 900.0,
                    "current_load": 3.0,
                    "type": "start_depot",
                    "geometry": {
                        "lon": -123.1837,
                        "lat": 49.1966913
                    }
                },
                {
                    "id": "ABC-123",
                    "position": 1,
                    "arrival_time": 923.5833333333334,
                    "wait_time": 0.0,
                    "service_time": 5.0,
                    "late_time": 0.0,
                    "depart_time": 928.5833333333334,
                    "current_load": 2.0,
                    "type": "order",
                    "duration_from_previous": 23.583333333333332,
                    "distance_from_previous": 12908.0,
                    "paired_stop": "null",
                    "geometry": {
                        "lon": -123.101,
                        "lat": 49.265
                    }
                },
                {
                    "id": "GHI-789",
                    "position": 2,
                    "arrival_time": 936.95,
                    "wait_time": 0.0,
                    "service_time": 5.0,
                    "late_time": 0.0,
                    "depart_time": 941.95,
                    "current_load": 1.0,
                    "type": "order",
                    "duration_from_previous": 8.366666666666667,
                    "distance_from_previous": 3851.0,
                    "paired_stop": "null",
                    "geometry": {
                        "lon": -123.136,
                        "lat": 49.27
                    }
                },
                {
                    "id": "DEF-456",
                    "position": 3,
                    "arrival_time": 953.8,
                    "wait_time": 0.0,
                    "service_time": 5.0,
                    "late_time": 0.0,
                    "depart_time": 958.8,
                    "current_load": 0.0,
                    "type": "order",
                    "duration_from_previous": 11.85,
                    "distance_from_previous": 4948.0,
                    "paired_stop": "null",
                    "geometry": {
                        "lon": -123.138,
                        "lat": 49.291
                    }
                }
            ]
        }
    ],
    "unassigned": [],
    "units": {
        "distance": "meter",
        "duration": "minute"
    },
    "user_tags": [],
    "warnings": []
}

Testing the route optimization API with a different date

To test that real-time traffic is working, let's try a different date.

Our original API call used real-time traffic on 6 March 2023 at 15:00 hrs PST. This was a Monday, so let's try the following Sunday, 5 March 2023 (simply swap in 2023-03-06T15:00:00-08:00 in the traffic_time field) and see if there is any difference in the arrival times of our three stops.

order_id order_name arrival (mon 6 mar, 2023) arrival (sun 5 mar, 2023)
ABC-123 Brassneck Brewery 15:28 15:21
GHI-789 Granville Island Brewing 15:36 15:34
DEF-456 Zakkushi on Denman 15:53 15:50

As expected, the driver arrives earlier and the travel times between each stop are shorter on Sundays than on Mondays.

But wouldn't a single traffic_time field imply that the optimization only considers real-time traffic conditions at the time the driver starts his route?

Fortunately, LogisticsOS handles this by using multiple distance matrices that correspond to different times of the day.

For example, if a stop is scheduled for 12 pm, the travel time to get to that stop would reflect real-time traffic from lunchtime rush hour. If that same stop was scheduled at 6 pm, evening rush hour traffic would be used instead.

So there you have it: three ways (Google Maps, Mapbox and LogisticOS) to add real-time traffic to your route optimization system.

👋 As always, if you have any questions or suggestions for me, please reach out or say hello on LinkedIn.