Mapbox-GL Tiled, Reachable POIs
Showing the tiled POIs is great, but lets add reachability into the mix. Drag the pin to re-calculate.
<!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://www.targomo.com/developers/scripts/mipb.min.js"></script>
    <style>
        body, html {
            margin: 0;
            width: 100%;
            height: 100%;
        }
        #map {
            width: 100%;
            height: 100%;   
        }
    </style>
</head>
<body>
    <!--  where the map will live  -->
    <div id="map"></div>
    <script>
        // create targomo client
        const client = new tgm.TargomoClient('westcentraleurope', '__targomo_key_here__');
        // Coordinates to center the map
        const lnglat = [2.38208, 48.8697];
        
        const attributionText = `<a href="https://www.targomo.com/developers/resources/attribution/" target="_blank">&copy; Targomo</a>`;;
        
        // set the progress bar
        const pBar = new mipb({ fg: "#FF8319" });
        
        let poiLookup = {};
        
        // add the map and set the initial center to berlin
        const map = new mapboxgl.Map({
            container: 'map',
            style: 'https://api.maptiler.com/maps/positron/style.json?key=__your_maptiler_api_key__',
            zoom: 12,
            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);
        
        // definition for OSM subway entrances
        const osmQuery = {
            key: 'railway',
            value: 'subway_entrance'
        }
        
        marker.on('dragend', measurePOIs);
        
        // layerGeometryDetailPerTile, layerMaxGeometryDetailLevel and maxGeometryCount
        // used to ensure that POIs are not aggregated by proximity, so we can match them to reachability results
        const POI_URL = [`https://api.targomo.com/pointofinterest/{z}/{x}/{y}.mvt`,
            `?apiKey=${client.serviceKey}&${osmQuery.key}=${osmQuery.value}`,
            `&loadAllTags=true&layerType=node&layerGeometryDetailPerTile=20`,
            `&layerMaxGeometryDetailLevel=40&maxGeometryCount=10000000`].join('');
        
        map.on('load', () => {
            map.addLayer({
                'id': 'poi',
                'type': 'circle',
                'source': {
                    'type': 'vector',
                    'tiles': [POI_URL],
                    'minzoom': 9
                },
                'source-layer': 'poi',
                'paint': {
                    'circle-radius': 4,
                    'circle-color': '#666'
                },
                'filter': ['==', 'numOfPois', 1]
            });
        
            // get the initial reachability measurement
            measurePOIs();
        
            // Change the cursor to a pointer when the mouse is over the poi layer
            map.on("mouseenter", "poi", () => {
                map.getCanvas().style.cursor = "pointer";
            });
        
            // Change it back to a pointer when it leaves.
            map.on("mouseleave", "poi", () => {
                map.getCanvas().style.cursor = "";
            });
        
            // popup with type, name and walk time, if relevant
            map.on("click", "poi", (e) => {
                const props = e.features[0].properties;
                let description = '';
                description += `<strong>POI: ${props.groupId.replace(/^\w/, c => c.toUpperCase())}</strong><br>`
                const tags = JSON.parse(e.features[0].properties.tags);
                description += tags.name ? tags.name : '<em>Name not listed...</em>';
                const walkTime = poiLookup[props.osmId] ? Math.round((poiLookup[props.osmId].edgeWeight / 60) * 10) / 10 : null;
                description += walkTime ? `<br><strong>Walk time: </strong>${walkTime} minutes` : '';
        
                // append description to popup
                new mapboxgl.Popup()
                    .setLngLat(e.lngLat)
                    .setHTML(description).addTo(map);
            });
        });
        
        async function measurePOIs() {
            // show the progress bar
            pBar.show();
            const curLatLng = marker.getLngLat();
            // 30 minutes
            const maxEdgeWeight = 1800;
            const options = {
                maxEdgeWeight: maxEdgeWeight,
                travelType: 'walk',
                edgeWeight: 'time',
                osmTypes: [osmQuery]
            }
            // measure walk time up to 30 minutes from the marker
            poiLookup = await client.pois.reachable({ id: 0, lat: curLatLng.lat, lng: curLatLng.lng }, options);
        
            // build the circle color expression based on the POI ids
            let expression = ['match', ['get', 'osmId']];
            for (let [key, value] of Object.entries(poiLookup)) {
                if (value.edgeWeight) {
                    const hue = Math.round((1 - (value.edgeWeight / maxEdgeWeight)) * 120);
                    const color = `hsl(${hue},70%,50%)`;
                    expression.push(key, color);
                }
            }
            expression.push('#666');
            map.setPaintProperty('poi', 'circle-color', expression);
            // hide the progress bar
            pBar.hide();
        }
    </script></body>
</html>
content_copy
Copied to clipboard