Creating isochrone catchments from a (distance) matrix.

In this article I want to outline a simple javascript algorithm for the creation of drivetime catchments from a distance matrix API.

A catchment is defined by an origin (given as a latitude and longitude coordinate) and a distance in seconds which can be reached from the origin. The catchment itself is an isoband polygon which is generated with Mapbox’ TurfJS library.

The algorithm was initially developed for the Mapbox Matrix API (formerly the Distance API). The algorithm has successfully been tested with the Google Distance Matrix API and should work with TomTom Matrix Routing and similar APIs.

I am aware of Mapbox own drivetime isochrones demo which has been posted by Peter Liu here. While there are some similarities, the algorithm outlined in this article was developed independently.

Please note that the aim of a catchment is to find the region which can be accessed from an origin within a certain time limit. The aim is not to create a surface displaying the time which it takes to reach any point within the catchment from the origin. The catchment is to be used to either select grid cells or distinct geometries which intersect with the catchment area. The catchment is defined by the outer isoband. Additional isobands are only displayed for explanatory purposes.

Creating Sample Points

The first step is to create sample points for requests to the Matrix API. Sample points should be evenly distributed with points nearer the origin being closer together. The Mapbox Matrix API provides a maximum of 24 origin > destination results per request. The Google Distance Matrix may provide 25 origin > destination results. 24 sample points can be evenly spaced with 8 points on 3 rings, or 12 points on 2 rings each.

Having more points on a single ring allows for a better sector analysis. A detail parameter describes how many rings should be drawn for the analysis. The number of rings used is 2 times the detail. The ring size increases exponentially with the outermost ring being distance / 30 kilometer in size. The divisor (reach) can be altered to acknowledge for different countries and/or modes of transport. Every second ring should be rotated by 15 degree around the origin.

Making API requests

The destination coordinates from the sample points are written into an array which is passed to the API request. We loop through these destinations, slicing the array into chunks of 24 destinations per request.

The Mapbox Distance matrix returns the latitude and longitude of the snapped the location along with the duration of travel in seconds for each destination in the request. We assign the snap location to the samplePoints leaving the original location on the circlePoints feature group.

Identifying outliers

The distance between the original location and the snapped location can be measured by creating a line object using the TurfJS library.

We exclude points which are displaced for more than a kilometer and calculate the standard deviation for travel time on each sample ring. Sample points whose travel time is above the standard deviation will be excluded from the isochrone calculation. This reduces the effect of destinations snapped to a forest trail or other network locations which are more difficult to reach than expected.

Calculating catchments

The TurfJS library is used to create a pointgrid over the extent of the sample locations. The resolution of the pointgrid is the maximum distance in seconds divided by 450 in kilometers.

A tin is generated from the sample point locations and travel times. This tin is used to calculate the planepoint values for the pointgrid.

Isobands are then generated for the distance, 1/3, and 2/3 of the distance. The latter are shown for explanatory reasons only.

I have created a jsFiddle to show the complete algorithm in action. Circles represent the original location and dots show the snapped location. Please note that I use ES6 features such as template literals and arrow functions which will not run in an older browser. Having read this far, it’s probably safe to assume that you use a modern browser.

Please change my Mapbox key if you fork the fiddle. I use a free public key and hope to help as many people as possible with this fiddle. This won’t be possible if a few people use up the complete allowance on this key.

I forked this fiddle for the Google Distance Matrix API here. However, this fiddle wants a Google API key and you will need to overcome the CORS problem in accessing the Google API through jsFiddle. Here is the result for the same drivetime as shown above but calculated with results from the Google Distance Matrix.

Note that the Google Distance Matrix does not return the latitude and longitude of the snap location.

As always, if you use any of the code bits shown in the example please credit GEOLYTIX. Don’t hesitate to get in touch with us if you want to implement drivetime catchments in a commercial product.

Happy mappings and codings.