Isochrone
This example calculates an isochrone — the area reachable from a point within a given travel time or distance. Click the map to place the origin; switch between travel time and distance, and between speed profiles, to see how the reachable area changes.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script src="https://cdn.smartmaps.cloud/packages/smartmaps/smartmaps-gl/v2/umd/smartmaps-gl.min.js"></script>
<style>
body {
margin: 0;
padding: 0;
}
#map {
height: 100vh;
}
#controls {
position: absolute;
top: 10px;
left: 10px;
z-index: 1;
background: rgba(255, 255, 255, 0.9);
padding: 10px;
border-radius: 4px;
font: 13px sans-serif;
}
#controls label {
display: block;
margin-top: 6px;
}
#status {
margin-top: 8px;
max-width: 220px;
}
</style>
</head>
<body>
<div id="controls">
<strong>Click the map to place the origin</strong>
<label>
Mode:
<select id="mode">
<option value="time">Travel time</option>
<option value="distance">Distance</option>
</select>
</label>
<label id="value-label">
Minutes (max 60):
<input id="value" type="number" value="15" min="1" max="60" />
</label>
<label>
Speed profile:
<select id="profile">
<option value="FAST">Car - fast</option>
<option value="SLOW">Car - slow</option>
<option value="BICYCLE">Bicycle</option>
<option value="PEDESTRIAN">Pedestrian</option>
</select>
</label>
<div id="status"></div>
</div>
<div id="map"></div>
<script>
const map = new smartmapsgl.Map({
apiKey: '[INSERT API-KEY]',
container: 'map',
center: { lat: 49.02164948779226, lng: 8.439330018049352 },
zoom: 12,
style: smartmapsgl.MapStyle.LIGHT
});
const modeSelect = document.getElementById('mode');
const valueLabel = document.getElementById('value-label');
const valueInput = document.getElementById('value');
const profileSelect = document.getElementById('profile');
const statusEl = document.getElementById('status');
let marker = null;
modeSelect.addEventListener('change', () => {
const isTime = modeSelect.value === 'time';
valueLabel.firstChild.textContent = isTime ? 'Minutes (max 60): ' : 'Meters: ';
valueInput.value = isTime ? 15 : 5000;
});
function removeIsochroneLayer() {
if (map.getLayer('isochrone-fill')) map.removeLayer('isochrone-fill');
if (map.getLayer('isochrone-outline')) map.removeLayer('isochrone-outline');
if (map.getSource('isochrone')) map.removeSource('isochrone');
}
map.on('click', async (event) => {
statusEl.textContent = 'Calculating…';
if (marker) marker.remove();
marker = new smartmapsgl.Marker({ color: '#0A5FA0' }).setLngLat(event.lngLat).addTo(map);
const isTime = modeSelect.value === 'time';
const routingparams = {
type: 'ISOCHRONE',
isochroneGrid: '80',
speedProfile: profileSelect.value,
coordFormatOut: 'GEODECIMAL_POINT',
routingTimeMode: 'ARRIVAL',
timeInMinutes: isTime ? Number(valueInput.value) : null,
distanceInMeters: isTime ? null : Number(valueInput.value)
};
try {
const response = await fetch(
'https://www.yellowmap.de/api_rst/v2/geojson/route?apiKey=[INSERT API-KEY]',
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
type: 'FeatureCollection',
routingparams,
authentication: { channel: '' },
crs: { type: 'name', properties: { name: 'urn:ogc:def:crs:OGC:1.3:CRS84' } },
features: [
{
type: 'Feature',
geometry: { type: 'Point', coordinates: [event.lngLat.lng, event.lngLat.lat] },
properties: {}
}
]
})
}
);
if (!response.ok) throw new Error(`Server error: ${response.status}`);
const result = await response.json();
const polygon = result.features.find(
(feature) => feature.properties?.featureType === 'ISOCHRONE_OUTLINE_POLYGON'
);
if (!polygon) throw new Error('No isochrone in response');
removeIsochroneLayer();
map.addSource('isochrone', { type: 'geojson', data: polygon });
map.addLayer({
id: 'isochrone-fill',
type: 'fill',
source: 'isochrone',
paint: { 'fill-color': '#0A5FA0', 'fill-opacity': 0.25 }
});
map.addLayer({
id: 'isochrone-outline',
type: 'line',
source: 'isochrone',
paint: { 'line-color': '#0A5FA0', 'line-width': 2 }
});
const label = isTime ? `${valueInput.value} min` : `${valueInput.value} m`;
statusEl.textContent = `Area reachable within ${label} (${profileSelect.value.toLowerCase()}).`;
} catch (error) {
console.error('Error fetching isochrone:', error);
statusEl.textContent = 'Failed to calculate the isochrone. Please try again.';
}
});
</script>
</body>
</html>
Notes
timeInMinutesanddistanceInMetersare mutually exclusive — set the one you don't use tonull, exactly like the mode switch above does.- The response is a
FeatureCollectionwith two features: the outline polygon (properties.featureType === "ISOCHRONE_OUTLINE_POLYGON") and the origin point. Filter onfeatureTyperather than relying on array order. isochroneGrid(35–150) controls how fine the polygon is — higher values follow the road network more closely but take longer to calculate.
See the Isochrone reference for the full parameter list, or the
library version if you'd rather call it from the
@smartmaps/routing npm package. Route Optimization
shows an isochrone combined with Matrix and Autocomplete in a full logistics scenario.