Skip to content

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

  • timeInMinutes and distanceInMeters are mutually exclusive — set the one you don't use to null, exactly like the mode switch above does.
  • The response is a FeatureCollection with two features: the outline polygon (properties.featureType === "ISOCHRONE_OUTLINE_POLYGON") and the origin point. Filter on featureType rather 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.