Real-time traffic with the Mapbox Matrix API
In my previous post, I ran through a worked example that used the Google Maps Directions API to augment a route optimization system with traffic-aware ETAs (Expected Arrival Times).
In this one, I'll show how to feed real-time traffic data directly into a routing engine by using the Mapbox Matrix API so that the solution produces optimal, traffic-aware routes out of the box.
Part 1: Real-time traffic route optimization with Google Maps
Part 2: Real-time traffic with the Mapbox Matrix API (this article)
Part 3: The best route optimization API for ETA accuracy with real-time traffic data
What is a travel time matrix?
A travel time matrix (also commonly known as a cost matrix) is a table that shows the travel times between pairs of objects. For example, if you are a delivery driver with a set of stops to visit, a travel time matrix would show the time taken to drive between each pair of stops. Travel time matrices are commonly used in transportation and logistics to help plan the most efficient routes between multiple stops. Here's a simple example of a travel time matrix for 3 stops - A*, B and C (we marked A with an asterisk to indicate that it's our driver's start location).
A* | B | C | |
---|---|---|---|
A* | A* → A* (0 min) | A* → B (6 min) | A* → C (2 min) |
B | B → A* (6 min) | B → B (0 min) | B → C (8 min) |
C | C → A* (2 min) | C → B (8 min) | C → C (0 min) |
We can also represent this travel time matrix pictorially using a graph. Here, nodes represent the physical location of a stop and the edges represent the travel times between stops.
Routing engines use the information in the travel time matrix to retrieve the driving time between the points and determine the optimal route. For instance, a quick visual inspection of the graph above shows that the optimal route starting from stop A* is A* → C → B for a total travel time of 2 + 8 = 10 minutes.
Routing engines work by efficiently coming up with different driver-stop combinations to produce a set of routes that minimize the total overall travel time for the vehicle fleet.
The types of algorithms used to solve this so-called vehicle routing problem vary depending on which engine you use. For example, Google OR-Tools, a popular open source solver, uses local search while Routific, a commercial route optimization API provider uses an algorithm inspired by bees. Regardless of the method, these routing engines all use a cost matrix as a starting point.
If you want your travel time matrix to consider real time traffic, my recommendation is to use the Mapbox Matrix API. Here's how it works.
Mapbox Matrix API
The first thing you need to do is sign up for a free Mapbox account and create an access token. Mapbox provides a generous free tier (up to 100,000 matrix elements a month) after which they charge $2.00 per 1000 elements thereafter.
Next, petition for beta access to the Matrix API's depart_at
feature, which gives you access to real time traffic based on anticipated road conditions at the time of travel. A friendly Mapbox sales representative will review your request and get back to you within three business days. Once that's been set up on your account, you're in business and ready to start using the Matrix API (docs).
Method: GET
https://api.mapbox.com/directions-matrix/v1/mapbox/
{profile}/
{coordinates}?
&depart_at={depart_at}
&access_token=MAPBOX_ACCESS_TOKEN
We'll be using the driving-traffic
{profile} which, when paired with the {depart_at} parameter, lets us take advantage of real time traffic.
{coordinates} is a semicolon-separated list of longitude latitude pairs e.g. -122.42,37.78;-122.45,37.91;-122.48,37.73.
{depart_at} is a timestamp in ISO-8601 format in the local time at the route origin e.g. 2022-12-31T17:00 would be used to represent 31 December 2022 at 5 pm.
{access_token} is the Mapbox API key found on your account page. It should look something like this: pk.ey123joidGhlcHJvZiIsImEiOianY1In0.7v5Itqbe2hcug
Putting everything together, let's call the Matrix API on the three pickup/dropoff location pairs we used in Using Route Optimization to Build a Ride Share Dispatch Algorithm :
booking_id | pickup_location | dropoff_location |
---|---|---|
booking_001 | Brassneck Brewery (49.2657714, -123.1017201) |
3450 E Hastings St (49.2814623, -123.0292998) |
booking_002 | Zakkushi on Denman (49.2912, -123.1384817) |
2083 Alma St (49.2688767, -123.1857463) |
booking_003 | Granville Island Brewing (49.2709294, -123.13623) |
5251 Oak St (49.2385564, -123.1309102) |
Method: GET
https://api.mapbox.com/directions-matrix/v1/mapbox/driving-traffic/-123.1017201,49.2657714;-123.0292998,49.2814623;-123.1384817,49.2912;-123.1857463,49.2688767;-123.13623,49.2709294;-123.1309102,49.2385564?&depart_at=2022-12-31T17:00&access_token=MAPBOX_ACCESS_TOKEN
The resulting travel time matrix (in seconds) for 31 December 2022 at 5 pm (2022-12-31T17:00) can be found in the durations
object of the response (below). durations
is an array of arrays that represents the matrix in row-major order e.g. durations[i][j] gives the travel time from the ith source to the jth destination so the travel time from Brassneck Brewery (1st element) to 3450 E Hastings St (2nd element) is 1013 seconds, which can be retrieved from durations[0][1].
{
"durations": [
[
0,1013, 924,1079, 711, 754
],
[
984, 0,1465,1828,1579,1467
],
[
930,1474, 0,1202, 982,1131
],
[
964,1786,1136, 0, 899, 851
],
[
524,1394, 870, 683, 0, 710
],
[
833,1495,1106, 848, 859, 0
]
]
}
If you change the value of the depart_at
parameter slightly, you'll get a different set of results. This is not only because of different levels of traffic on the shortest route, but also because the shortest route from one point to another can change due to different traffic conditions, leading to different travel times over the course of the day.
Integrating the Mapbox Matrix API with your routing engine
So what's the best way to feed travel times from the Matrix API into your routing engine? Using the open source Jsprit optimization toolkit as an example, here's what you need to do.
First, write a subroutine to pull the travel time matrix from Mapbox.
MapboxService.java
package io.afilabs.vrp.service;
import io.afilabs.vrp.api.v5.utils.JsonMapper;
import io.afilabs.vrp.api.v5.utils.Request;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Optional;
@Component
public class MapboxService {
@Autowired
Request request;
@Autowired
JsonMapper jsonMapper;
public Optional getMatrix(String pointsListAsString) {
try {
SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm");
String departAt = f.format(new Date());
// {longitude},{latitude};
String serviceUrl = "https://api.mapbox.com/directions-matrix/v1/mapbox/driving-traffic/";
HttpResponse response = request.get(
serviceUrl.concat(pointsListAsString)
.concat("?")
.concat("depart_at=" + departAt)
.concat("&")
.concat(System.getenv("MAPBOX_API_KEY")),
5000);
if (response.getStatusLine().getStatusCode() == 200) {
return convert(response.getEntity());
}
return Optional.empty();
} catch (IOException e) {
return Optional.empty();
}
}
private Optional convert(HttpEntity entity) {
try {
return jsonMapper.toObjIgnoreUnknownFields(entity.getContent(), MapboxMatrixResponse.class);
} catch (UnsupportedOperationException | IOException e) {
return Optional.empty();
}
}
}
This returns a JSON representation of Mapbox's matrix result.
Next, write a helper class to extract the durations
field from MapboxService.java's output.
MapboxMatrixResponse.java
package io.afilabs.vrp.service;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.List;
public class MapboxMatrixResponse {
private List durations = new ArrayList<>();
public void setDurations(List durations) {
this.durations = durations;
}
public List getDurations() {
return durations;
}
}
Lastly, add this code snippet to RouteOptimizationService.java.
MapboxService.java (code extract)
ArrayList locations = new ArrayList();
for (VehicleDTO vehicle : vehicles) {
AddressDTO address = vehicle.getStartAddress();
locations.add(address.getLongitude().toString() + "," + address.getLatitude().toString());
}
for (ShipmentDTO shipment : shipments) {
AddressDTO address = shipment.getAddress();
locations.add(address.getLongitude().toString() + "," + address.getLatitude().toString());
}
String points = StringUtils.join(locations, ";");
Optional matrixResponse = this.mapboxService.getMatrix(points);
List matrixDurations = new ArrayList<>();
if (matrixResponse.isPresent()) {
MapboxMatrixResponse matrix = matrixResponse.get();
matrixDurations = matrix.getDurations();
}
This segment of code runs through all driver and stop locations, extracts the corresponding longitude/latitude pair, concatenates it into a ;
delimited string and sends it to MapboxService.java to retrieve the resulting travel time matrix.
Limitations of the Mapbox Matrix API
The main downside of using the Matrix API is that Mapbox limits you to 10 input coordinates when using the driving-traffic
profile.
If you put in a request, Mapbox will increase this to 25 coordinates. Still, this limits the size of your matrix to 625 elements, which makes this approach only practical for solving relatively small optimization problems.
In my next and final post, I'll show you how to overcome these limitations by using a route optimization API with built in real time traffic support.
And what about other travel time matrices?
The best data source for your travel time matrix is the one that is fast, meets your needs, and is affordable. For example, you might decide to use straight line (great-circle) distance as a substitute for actual drive time because it's free and fast to compute.
However, this means that you'll get routes that look weird because it does not take into account barriers such as bodies of water, mountains, or other physical features that may obstruct or lengthen the actual route. For example, it might sequence deliveries to two addresses that are on opposite sides of a highway one after another just because the distance away is short as the crow flies, when in actual fact, it might take a very long time to reach one from the other.
Alternatively, you could also use OSRM - the Open Street Maps Routing Machine, It's open source and free to use, but you'd need to hire an engineer familiar with C++ and setting up a map server. You'd also be limited to travel times that don't change based on traffic conditions or time of day (OSRM uses static road network data compiled by thousands of volunteers).
👋 As always, if you have any questions or suggestions for me, please reach out or say hello on LinkedIn.
Next: Part 3: The best route optimization API for ETA accuracy with real-time traffic data