Mapbox-GL Statistics-based Reachability
Quantify the reachability - see how many people are within 30 minutes by bike. Drag the marker to see how the reachable population changes
local_activityThe statistics service is a custom offering, and you need to contact us to get set-up. Check our plans for details.
<!DOCTYPE html>
<html>
<head>
<!--  Include mapboxgl javascript and css -->
    <script src="https://api.tiles.mapbox.com/mapbox-gl-js/v1.6.0/mapbox-gl.js"></script>
    <link href="https://api.tiles.mapbox.com/mapbox-gl-js/v1.6.0/mapbox-gl.css" rel="stylesheet">
    <!--  Include targomo core -->
    <script src="https://releases.targomo.com/core/latest.min.js"></script>
    <!--  Include micro progress bar  -->
    <script src="https://targomo.com/developers/scripts/mipb.min.js"></script>
    <style>
        body, html {
            margin: 0;
            width: 100%;
            height: 100%;
        }
        #map {
            width: 100%;
            height: 100%;
            position: relative;
        }
        .population {
            position: absolute; padding: 8px;
            top: 10px; left: 10px;
            font-family: Helvetica, Arial, Sans-Serif;
            background: white; border-radius: 4px;
            box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.1);
        }
    </style>
</head>
<body>
    <!--  where the map will live  -->
    <div id="map"></div>
    <div class="population"><span>Reachable Population:</span>&nbsp;<span id="total-pop"></span></div>

    <script>
        // create targomo client
        const client = new tgm.TargomoClient('westcentraleurope', '__targomo_key_here__');
        // Coordinates to center the map
        const lnglat = [2.374513, 48.844334];
        const attributionText = `<a href='https://targomo.com/developers/resources/attribution/' target='_blank'>&copy; Targomo</a>`;

        // set the progress bar
        const pBar = new mipb({ fg: "#FF8319" });

        // add the map and set the initial center to paris
        const map = new mapboxgl.Map({
            container: 'map',
            style: 'https://api.maptiler.com/maps/positron/style.json?key=__your_maptiler_api_key__',
            minZoom: 9, zoom: 11, center: lnglat, attributionControl: false
        }).addControl(new mapboxgl.NavigationControl()).addControl(new mapboxgl.AttributionControl(
            { compact: true, customAttribution: attributionText }
        ));

        // disable scroll zoom
        map.scrollZoom.disable();

        const marker = new mapboxgl.Marker({
            draggable: true
        }).setLngLat(lnglat).addTo(map);

        marker.on('dragend', getStats);

        const stats = {
            group: 26,
            individual: 8
        }

        async function getStats(){
            // show progress bar
            pBar.show();
            const sources = [{...marker.getLngLat(), id: 1}];
            // reachable stats - returns an object of
            // key = statistic cell id, value = travel time to cell centroid
            const statistics = await client.statistics.travelTimes(sources, {
                maxEdgeWeight: 1800, travelType: "Bike", // 30 minutes on foot
                statisticsGroup: stats.group, statistics: [{ id: stats.individual }]
            });
            // tally full reachable population
            const reachablePop = Object.values(statistics).reduce((acc, cur) => acc + cur);
            var popElem = document.querySelector('#total-pop');
            popElem.innerHTML = reachablePop.toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,');

            // only show the cell which are reachable in 30 minutes on foot
            map.setFilter("statistics", [
                "match", ["get", "id"], Object.keys(statistics).map(k => +k), true, false
            ]);

            // hide progress bar
            pBar.hide()

            map.on("click", "statistics", (e) => {
                const description = `Population: ${Math.round(e.features[0].properties["8"])}
                    <br>Bike time: ${Math.round(statistics[e.features[0].properties.id] / 60)}`;

                new mapboxgl.Popup()
                    .setLngLat(e.lngLat)
                    .setHTML(description).addTo(map);
            });
        }

        map.on('load', async () => {
            // get metadata for statistics group 26 (France INSEE)
            // to see the available statistics, as well as details of the dataset
            const meta = await client.statistics.metadata(stats.group);
            // get details of the population statistics, statistic id 8, of group 26
            const populationMeta = await client.statistics.metadataKey(stats.group, { id: stats.individual });
            // http://colorbrewer2.org/?type=sequential&scheme=RdPu&n=9
            const colors = ['#fff7f3','#fde0dd','#fcc5c0','#fa9fb5','#f768a1','#dd3497','#ae017e','#7a0177','#49006a','#380052'];
            const breakpoints = populationMeta.breakpoints.kmeans.c9.map((b,i) => [b, colors[i]]);
            const colorDef = ["interpolate", ["linear"], ["get", `${stats.individual}`], ...breakpoints.flat()];
            // get the vectortiles url
            const tileRoute = await client.statistics.tileRoute(stats.group, [{ id: stats.individual }]);
            // add the tiles to the map
            map.addLayer({
                "id": "statistics",
                "type": "fill",
                "source": {
                    "type": "vector",
                    "tiles": [tileRoute],
                    "minzoom": meta.min_zoom
                },
                "source-layer": `${stats.group}`, // source-layer is the statistic group id
                "layout": {},
                "paint": {
                    "fill-opacity": 0.6,
                    "fill-color": colorDef,
                    "fill-outline-color": "rgba(0,0,0,0)"
                }
            }, "place_other");

            // Change the cursor to a pointer when the mouse is over the stats layer
            map.on("mouseenter", "statistics", () => {
                map.getCanvas().style.cursor = "pointer";
            });

            // Change it back to a pointer when it leaves.
            map.on("mouseleave", "statistics", () => {
                map.getCanvas().style.cursor = "";
            });

            getStats();
        })
    </script>
</body>
</html>
content_copy
Copied to clipboard