Skip to content

Draw an Isochrone Polygon in MapLibre GL JS

This example fetches a 10-minute walking isochrone area for a point in Vienna and draws it as a filled polygon on a MapLibre GL map.

let map = new maplibregl.Map({
      container: "map",
      style: "https://styles.maptoolkit.net/maptoolkit/maptoolkit.summer.json?api_key=YOUR_API_KEY",
      center: [16.372182, 48.208266],
      zoom: 14,
    });
    map.on("load", () => {
      let point = [16.372493, 48.20883];
      let url = new URL("https://routing.maptoolkit.net/isochrone");
      url.searchParams.append("time", 10);
      url.searchParams.append("point", `${point[1]},${point[0]}`);
      url.searchParams.append("routeType", "foot");
      url.searchParams.append("api_key", "YOUR_API_KEY");
      fetch(url)
        .then((r) => r.json())
        .then((polygon) => {
          map.addSource("isochrone", { type: "geojson", data: { type: "Feature", geometry: { type: "Polygon", coordinates: [polygon.map((latLng) => latLng.reverse())] } } });
          map.addLayer({ id: "isochrone-fill", type: "fill", source: "isochrone", paint: { "fill-color": "#2a3561", "fill-opacity": 0.2 } });
          map.addLayer({ id: "isochrone-line", type: "line", source: "isochrone", layout: { "line-join": "round", "line-cap": "round" }, paint: { "line-color": "#2a3561", "line-width": 2 } });
          let $img = document.createElement("img");
          $img.src = "https://static.maptoolkit.net/sprites/toursprung/marker.svg";
          $img.width = 29; $img.height = 30;
          new maplibregl.Marker({ element: $img, anchor: "bottom" })
            .setLngLat(point).addTo(map)
            .setPopup(new maplibregl.Popup({ offset: 30 }).setHTML("<p>Within a 10 minute walking distance</p>"))
            .togglePopup();
        });
    });
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <script src="https://cdn.jsdelivr.net/npm/maplibre-gl@5.6.1/dist/maplibre-gl.js"></script>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/maplibre-gl@5.6.1/dist/maplibre-gl.css" />
  <style>
    html, body { width: 100%; height: 100%; margin: 0; padding: 0; }
    #map { width: 100%; height: 100%; }
  </style>
</head>
<body>
<div id="map"></div>
  <script>
    let map = new maplibregl.Map({
      container: "map",
      style: "https://styles.maptoolkit.net/maptoolkit/maptoolkit.summer.json?api_key=YOUR_API_KEY",
      center: [16.372182, 48.208266],
      zoom: 14,
    });
    map.on("load", () => {
      let point = [16.372493, 48.20883];
      let url = new URL("https://routing.maptoolkit.net/isochrone");
      url.searchParams.append("time", 10);
      url.searchParams.append("point", `${point[1]},${point[0]}`);
      url.searchParams.append("routeType", "foot");
      url.searchParams.append("api_key", "YOUR_API_KEY");
      fetch(url)
        .then((r) => r.json())
        .then((polygon) => {
          map.addSource("isochrone", { type: "geojson", data: { type: "Feature", geometry: { type: "Polygon", coordinates: [polygon.map((latLng) => latLng.reverse())] } } });
          map.addLayer({ id: "isochrone-fill", type: "fill", source: "isochrone", paint: { "fill-color": "#2a3561", "fill-opacity": 0.2 } });
          map.addLayer({ id: "isochrone-line", type: "line", source: "isochrone", layout: { "line-join": "round", "line-cap": "round" }, paint: { "line-color": "#2a3561", "line-width": 2 } });
          let $img = document.createElement("img");
          $img.src = "https://static.maptoolkit.net/sprites/toursprung/marker.svg";
          $img.width = 29; $img.height = 30;
          new maplibregl.Marker({ element: $img, anchor: "bottom" })
            .setLngLat(point).addTo(map)
            .setPopup(new maplibregl.Popup({ offset: 30 }).setHTML("<p>Within a 10 minute walking distance</p>"))
            .togglePopup();
        });
    });
  </script>
</body>
</html>

Use the prompt below with any LLM to get the same result. Make sure the Maptoolkit MCP server is connected first — check out AI Integration & MCP to get started.

Use the Maptoolkit Connector. Create an interactive map centered around [16.372493, 48.20883] with an Isochrone for 10 minutes walking time around the center, using MapLibre GL.

How it works

time is in minutes, and 60 is the maximum: above that the service returns 400 with Parameter 'time' must not exceed 60 minutes.

routeType decides the shape as much as the time does. A 10 minute foot isochrone is a small blob; the same 10 minutes as car follows the road network outward and is far less round.

The response is a bare array of [lat, lng] pairs, not GeoJSON. It has to be reversed into [lng, lat] and wrapped in a Feature before MapLibre will take it, which is what the polygon.map(latLng => latLng.reverse()) and the surrounding Polygon geometry are doing. Note that reverse() mutates the array in place, so the response cannot be reused afterwards without re-fetching.

The fill and the outline are two layers over one source. A fill layer cannot draw its own border, so an accompanying line layer gives the edge.

Next steps

time and routeType are the two dials worth pulling. Ten minutes on foot and ten by car are different questions, and stacking bands at several times, shaded dark to light, reads better than a single outline.

The step that makes an isochrone useful is usually testing your own data against it: which branches, stops or listings fall inside the shape. That is a point-in-polygon check on the response. For “how do I get there” rather than “how far can I get”, the Routing API uses the same key.