Isochrone
This example calculates an isochrone using the @smartmaps/routing npm package instead of calling
the REST endpoint directly. 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/routing/umd/routing.min.js"></script>
<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</option>
<option value="BICYCLE">Bicycle</option>
<option value="PEDESTRIAN">Pedestrian</option>
</select>
</label>
<div id="status"></div>
</div>
<div id="map"></div>
<script>
const apiKey = '[INSERT API-KEY]';
const map = new smartmapsgl.Map({
apiKey,
container: 'map',
center: { lat: 49.02164948779226, lng: 8.439330018049352 },
zoom: 12,
style: smartmapsgl.MapStyle.LIGHT
});
const isochroneService = new smartmaps.routingService.RoutingIsochrone(apiKey);
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';
try {
const result = await isochroneService.calcRoute(
[{ longitude: event.lngLat.lng, latitude: event.lngLat.lat }],
{
speedProfile: profileSelect.value,
isochroneGrid: 80,
timeInMinutes: isTime ? Number(valueInput.value) : undefined,
distanceInMeters: isTime ? undefined : Number(valueInput.value)
}
);
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
calcRoute()takes an array of points and an options object; here the array holds a single origin. Points can be{ longitude, latitude },{ lat, lng },[lng, lat], a GeoJSONPoint, or a browserGeolocationCoordinatesobject.- The library's
SpeedProfiletype is"FAST" | "BICYCLE" | "PEDESTRIAN"— narrower than the REST endpoint'sspeedProfile, which also accepts"SLOW". timeInMinutesanddistanceInMetersare mutually exclusive — leave the one you don't use asundefinedrather than0, or it overrides the default.- The response has the same shape as the REST endpoint: two features, the outline polygon
(
properties.featureType === "ISOCHRONE_OUTLINE_POLYGON") and the origin point.
See RoutingIsochrone for the full class reference, or the
REST version if you'd rather call the endpoint directly.