This tutorial demonstrates how you can use the Mapbox Isochrone API, which uses Mapbox routing profiles and travel times, to create a web app that allows users to estimate how far they could travel by foot, bicycle, or car within a set amount of time.
To complete this tutorial, you will need:
An Isochrone API request requires three parameters:
profile
: The Mapbox routing profile
that the query should use. This can be walking
for pedestrian and hiking travel times, cycling
for travel times by bicycle, or driving
for travel times by car.coordinates
: A {longitude,latitude}
coordinate pair around which to center the isochrone lines.contours_minutes
: Times that describe the duration in minutes of the trip. This can be a comma-separated list of up to four times. The maximum duration is 60 minutes.https://api.mapbox.com/isochrone/v1/mapbox/{profile}/{coordinates}.json?{contours_minutes}&access_token=YOUR_MAPBOX_ACCESS_TOKEN
The Isochrone API also accepts several optional parameters that can be used to customize the query. For this app, you will be using one optional parameter:
polygons
: This parameter specifies whether to return the contours as GeoJSON polygons or linestrings. When polygons=true
, any contour that forms a ring is returned as a polygon.To learn more about the Isochrone API and its other optional parameters, explore the Isochrone API documentation.
This example query uses the driving
routing profile, has a contours_minutes
of 15, and has the polygons
parameter set to true
:
https://api.mapbox.com/isochrone/v1/mapbox/driving/-117.17282,32.71204?contours_minutes=15&polygons=true&access_token=YOUR_MAPBOX_ACCESS_TOKEN
To see the JSON response to this query, you can paste it into your browser. The response's geometry
object contains an array of coordinates
that describe the outlines of the isochrone contour. The response also contains a features
object that describes styling rules for drawing the polygon, including its fill color and fill opacity. You will use the information returned by the Isochrone API to draw and style the contours of the returned isochrone to the app's map.
To start the app, you will create a map using Mapbox GL JS.
This code creates the structure of the page and imports Mapbox GL JS and Assembly in the <head>
of the page. The Mapbox GL JS JavaScript and CSS files allow you to use Mapbox GL JS functionality and map style, and the Assembly CSS framework allows you to further refine the style of the non-map elements on the page.
There is a <div>
element with the ID map
in the <body>
of the page. This <div>
is the container in which the map will be displayed on the page.
Follow these steps to add the map:
index.html
.<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Get started with the Isochrone API</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
{/* Import Mapbox GL JS */}
<script src="https://api.tiles.mapbox.com/mapbox-gl-js/v3.3.0/mapbox-gl.js"></script>
<link
href="https://api.tiles.mapbox.com/mapbox-gl-js/v3.3.0/mapbox-gl.css"
rel="stylesheet"
/>
{/* Import Assembly */}
<link
href="https://api.mapbox.com/mapbox-assembly/v1.3.0/assembly.min.css"
rel="stylesheet"
/>
<script src="https://api.mapbox.com/mapbox-assembly/v1.3.0/assembly.js"></script>
<style>
body {
margin: 0;
padding: 0;
}
#map {
position: absolute;
top: 0;
bottom: 0;
width: 100%;
}
</style>
</head>
<body>
{/* Create a container for the map */}
<div id="map"></div>
<script>
// Add your Mapbox access token
mapboxgl.accessToken = 'YOUR_MAPBOX_ACCESS_TOKEN ';
const map = new mapboxgl.Map({
container: 'map', // Specify the container ID
style:
'mapbox://styles/mapbox/streets-v12', // Specify which map style to use
center: [-77.0369, 38.895], // Specify the starting position
zoom: 11.5 // Specify the starting zoom
});
</script>
</body>
</html>
Next, add a sidebar to the web app that will allow users to select a transportation profile and time duration:
<div>
by copying and pasting the code below into the <body>
of your HTML.
<div>
holds the app's options and uses Assembly classes for styling.The sidebar will display on the left side of the page. While it looks good, clicking the buttons doesn't do anything — yet. In the next step, you will add a call to the Isochrone API, which you will be able to hook up to the user interface to create an interactive app.
<div class="absolute fl my24 mx24 py24 px24 bg-gray-faint round">
<form id="params">
<h4 class="txt-m txt-bold mb6">Choose a travel mode:</h4>
<div class="mb12 mr12 toggle-group align-center">
<label class="toggle-container">
<input name="profile" type="radio" value="walking" />
<div class="toggle toggle--active-null toggle--null">Walking</div>
</label>
<label class="toggle-container">
<input name="profile" type="radio" value="cycling" checked />
<div class="toggle toggle--active-null toggle--null">Cycling</div>
</label>
<label class="toggle-container">
<input name="profile" type="radio" value="driving" />
<div class="toggle toggle--active-null toggle--null">Driving</div>
</label>
</div>
<h4 class="txt-m txt-bold mb6">Choose a maximum duration:</h4>
<div class="mb12 mr12 toggle-group align-center">
<label class="toggle-container">
<input name="duration" type="radio" value="10" checked />
<div class="toggle toggle--active-null toggle--null">10 min</div>
</label>
<label class="toggle-container">
<input name="duration" type="radio" value="20" />
<div class="toggle toggle--active-null toggle--null">20 min</div>
</label>
<label class="toggle-container">
<input name="duration" type="radio" value="30" />
<div class="toggle toggle--active-null toggle--null">30 min</div>
</label>
</div>
</form>
</div>
The code for the form uses Assembly
classes. These classes provide CSS styling
for the specified elements. For example, applying the classes absolute fl my24 mx24 py24 px24 bg-gray-faint round
to the parent div
sets its position
to absolute
and floats it to the left, gives it margin and padding of 24
pixels, sets the background to a light gray, and adds a border radius to round
the corners. In your own app, you could also use plain CSS or the CSS
framework of your choice to style the sidebar and the buttons instead of using
Assembly.
To integrate the Isochrone API into your app, you will write a new function, getIso
, that will construct the query string and will use fetch to make the Isochrone API call.
map
constant that you used to initialize the map.Since you have not set up the layers yet this code will draw the isochrone contour described in the response to the map, this code prints the results of the query to the console.
// Create constants to use in getIso()
const urlBase = 'https://api.mapbox.com/isochrone/v1/mapbox/';
const lon = -77.034;
const lat = 38.899;
const profile = 'cycling'; // Set the default routing profile
const minutes = 10; // Set the default duration
// Create a function that sets up the Isochrone API query then makes an fetch call
async function getIso() {
const query = await fetch(
`${urlBase}${profile}/${lon},${lat}?contours_minutes=${minutes}&polygons=true&access_token=${mapboxgl.accessToken}`,
{ method: 'GET' }
);
const data = await query.json();
console.log(data);
}
// Call the getIso function
// You will remove this later - it's just here so you can see the console.log results in this step
getIso();
In the last step, you created a console.log
statement to view the API response. But for this app, you want to show the isochrone contours on the map!
getIso();
call from the bottom of your JavaScript and replace it with the code from below.
map.on('load', () => {
// When the map loads, add the source and layer
map.addSource('iso', {
type: 'geojson',
data: {
type: 'FeatureCollection',
features: []
}
});
map.addLayer(
{
id: 'isoLayer',
type: 'fill',
// Use "iso" as the data source for this layer
source: 'iso',
layout: {},
paint: {
// The fill color for the layer is set to a light purple
'fill-color': '#5a3fc0',
'fill-opacity': 0.3
}
},
'poi-label'
);
// Make the API call
getIso();
});
console.log(data)
statement in the getIso
function with the following code, which will set the iso
source to the data returned by the API call:// Set the 'iso' source's data to what's returned by the API query
map.getSource('iso').setData(data);
cycling
routing profile and a trip duration of 10 minutes) is added to the map.Now, you need to hook the buttons that you created earlier up to your JavaScript so that users can change the routing profile and the trip duration and see the results displayed on the map. Add the following code to the bottom of your JavaScript, before the closing </script>
tag:
// Target the "params" form in the HTML portion of your code
const params = document.getElementById('params');
// When a user changes the value of profile or duration by clicking a button, change the parameter's value and make the API query again
params.addEventListener('change', (event) => {
if (event.target.name === 'profile') {
profile = event.target.value;
} else if (event.target.name === 'duration') {
minutes = event.target.value;
}
getIso();
});
Now, when you click the buttons to change the routing profile or the trip duration, the event listener sets the parameter in the query to the new value and runs the getIso
function again. This in turn redraws the new isochrone contours to the map.
Save your changes and refresh the page in your browser. Click on different combinations of routing profiles and trip durations to see the isochrone contour change.
Now you will add a marker to your map at the coordinates of the query to make the center of the isochrone contour more distinct. In this case, the coordinates used represent the location of the Mapbox office in Washington D.C.
To add the marker follow these steps:
map.on('load')
function:const marker = new mapboxgl.Marker({
color: '#314ccd'
});
// Create a LngLat object to use in the marker initialization
// https://docs.mapbox.com/mapbox-gl-js/api/#lnglat
const lngLat = {
lon: lon,
lat: lat
};
map.on('load')
function:// Initialize the marker at the query coordinates
marker.setLngLat(lngLat).addTo(map);
You will now see a blue marker at the specified coordinates.
You have created an app that uses the Mapbox Isochrone API to visualize how far a person could walk, bike, or drive within a given amount of time.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Demo: Get started with the Isochrone API</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<!-- Mapbox GL JS -->
<script src="https://api.tiles.mapbox.com/mapbox-gl-js/v3.3.0/mapbox-gl.js"></script>
<link
href="https://api.tiles.mapbox.com/mapbox-gl-js/v3.3.0/mapbox-gl.css"
rel="stylesheet"
/>
<!-- Mapbox Assembly -->
<link
href="https://api.mapbox.com/mapbox-assembly/v1.3.0/assembly.min.css"
rel="stylesheet"
/>
<script src="https://api.mapbox.com/mapbox-assembly/v1.3.0/assembly.js"></script>
<style>
body {
margin: 0;
padding: 0;
}
#map {
position: absolute;
top: 0;
bottom: 0;
width: 100%;
}
</style>
</head>
<body>
<div id="map"></div>
<div class="absolute fl my24 mx24 py24 px24 bg-gray-faint round">
<form id="params">
<h4 class="txt-m txt-bold mb6">Choose a travel mode:</h4>
<div class="mb12 mr12 toggle-group align-center">
<label class="toggle-container">
<input name="profile" type="radio" value="walking" />
<div class="toggle toggle--active-null toggle--null">Walking</div>
</label>
<label class="toggle-container">
<input name="profile" type="radio" value="cycling" checked />
<div class="toggle toggle--active-null toggle--null">Cycling</div>
</label>
<label class="toggle-container">
<input name="profile" type="radio" value="driving" />
<div class="toggle toggle--active-null toggle--null">Driving</div>
</label>
</div>
<h4 class="txt-m txt-bold mb6">Choose a maximum duration:</h4>
<div class="mb12 mr12 toggle-group align-center">
<label class="toggle-container">
<input name="duration" type="radio" value="10" checked />
<div class="toggle toggle--active-null toggle--null">10 min</div>
</label>
<label class="toggle-container">
<input name="duration" type="radio" value="20" />
<div class="toggle toggle--active-null toggle--null">20 min</div>
</label>
<label class="toggle-container">
<input name="duration" type="radio" value="30" />
<div class="toggle toggle--active-null toggle--null">30 min</div>
</label>
</div>
</form>
</div>
<script>
mapboxgl.accessToken = '{{MAPBOX_ACCESS_TOKEN}}';
const map = new mapboxgl.Map({
container: 'map', // container id
style: 'mapbox://styles/mapbox/streets-v12', // stylesheet
center: [-77.0369, 38.895], // starting position [lng, lat]
zoom: 11.5 // starting zoom
});
// Target the params form in the HTML
const params = document.getElementById('params');
// Create variables to use in getIso()
const urlBase = 'https://api.mapbox.com/isochrone/v1/mapbox/';
const lon = -77.034;
const lat = 38.899;
let profile = 'cycling';
let minutes = 10;
// Set up a marker that you can use to show the query's coordinates
const marker = new mapboxgl.Marker({
'color': '#314ccd'
});
// Create a LngLat object to use in the marker initialization
// https://docs.mapbox.com/mapbox-gl-js/api/#lnglat
const lngLat = {
lon: lon,
lat: lat
};
// Create a function that sets up the Isochrone API query then makes a fetch call
async function getIso() {
const query = await fetch(
`${urlBase}${profile}/${lon},${lat}?contours_minutes=${minutes}&polygons=true&access_token=${mapboxgl.accessToken}`,
{ method: 'GET' }
);
const data = await query.json();
// Set the 'iso' source's data to what's returned by the API query
map.getSource('iso').setData(data);
}
// When a user changes the value of profile or duration by clicking a button, change the parameter's value and make the API query again
params.addEventListener('change', (event) => {
if (event.target.name === 'profile') {
profile = event.target.value;
} else if (event.target.name === 'duration') {
minutes = event.target.value;
}
getIso();
});
map.on('load', () => {
// When the map loads, add the source and layer
map.addSource('iso', {
type: 'geojson',
data: {
'type': 'FeatureCollection',
'features': []
}
});
map.addLayer(
{
'id': 'isoLayer',
'type': 'fill',
'source': 'iso',
'layout': {},
'paint': {
'fill-color': '#5a3fc0',
'fill-opacity': 0.3
}
},
'poi-label'
);
// Initialize the marker at the query coordinates
marker.setLngLat(lngLat).addTo(map);
// Make the API call
getIso();
});
</script>
</body>
</html>
To build on top of the tools and techniques you used in this tutorial, explore the following resources:
addLayer
documentation.