Location Scoring - Reachable POI Count

Comparing multiple locations based on how many categorical POIs they can access.
<!DOCTYPE html>
<html>
<head>
    <!-- use maki-based iconfont to symbolize POIs by type -->
    <link rel="stylesheet" href="https://releases.targomo.com/tgm-icons/webfont/tgm-icon.css">

    <!--  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 turf for view fitting -->
    <script src="https://npmcdn.com/@turf/turf/turf.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%;
        }

        .score_result {
            position: absolute; padding: 8px;
            top: 10px; left: 10px;
            font-family: 'Open Sans', sans-serif;
            background: white; border-radius: 4px;
            box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.1);
            z-index: 1;
        }      
        .mapboxgl-marker {
            cursor: pointer;
        }
        #markerScores1, #markerScores2 {
            display: inline;
        }
        #markerLabel1 {
            color: #FF8319;
        }
        #markerLabel2 {
            color: #4BB5C5;
        }
    </style>
</head>
<body>
    <div class="score_result">
        <b>Number of reachable POIs:</b>
        <br><br>
        <b id="markerLabel1">Marker 1: </b><div id="markerScores1"></div><br>
        <b id="markerLabel2">Marker 2: </b><div id="markerScores2"></div>
    </div>

    <!--  where the map will live  -->
    <div id="map"></div>

    <script>// create targomo client
        const client = new tgm.TargomoClient('westcentraleurope', '__targomo_key_here__')
        
        // Travel options
        const EDGE_WEIGHT = 'time' // Can be 'time' or 'distance'
        const MAX_TRAVEL = 400 // Integer that represents meters or seconds, depending on EDGE_WEIGHT's value
        const TRAVEL_MODE = 'walk' // Can be 'walk', 'car', 'bike' or 'transit'
        
        const attributionText = `<a href='//localhost:1313/resources/attribution/' target='_blank'>&copy; Targomo</a>`;
        
        // Specifying which POI groups we want to show on the map... See the POI hierarchy for more info.
        const POI_TYPES = ['supermarket', 'convenience']
        const POI_URL = `https://api.targomo.com/pointofinterest/{z}/{x}/{y}.mvt` +
            `?apiKey=${client.serviceKey}&group=${POI_TYPES[0]}&group=${POI_TYPES[1]}&loadAllTags=false&layerType=node`
        
        // Add the map and set the initial center
        const map = new mapboxgl.Map({
            container: 'map',
            style: client.basemaps.getGLStyleURL("Light"),
            zoom: 14,
            minZoom: 9,
            center: [2.378, 48.8451],
            attributionControl: false
        })
            .addControl(new mapboxgl.NavigationControl())
            .addControl(new mapboxgl.AttributionControl({ compact: true, customAttribution: attributionText }))
        
        
        // disable scroll zoom
        map.scrollZoom.disable()
        
        const emptyData = { 'type': 'FeatureCollection', 'features': [] }
        
        const pBar = new mipb({ fg: '#FF8319' });
        
        // Initialize markers
        let marker1 = new mapboxgl.Marker({
            draggable: true,
            color: '#FF8319'
        })
        let marker2 = new mapboxgl.Marker({
            draggable: true,
            color: '#4BB5C5'
        })
        
        marker1.setLngLat([2.3722, 48.8458]).addTo(map)
        marker1.on('dragend', updateMap)
        
        marker2.setLngLat([2.3893, 48.8443]).addTo(map)
        marker2.on('dragend', updateMap)
        
        
        map.on('load', async () => {
            preLoadIconsForMap()
            addSources()
            addLayers()
        
            updateMap()
        })
        function addSources() {
            map.addSource('reachable-poi-sources', {
                'type': 'geojson',
                'data': emptyData
            })
        }
        function preLoadIconsForMap() {
            for (let icon of ['tgm-convenience', 'tgm-grocery', 'tgm-circle']) {
                map.loadImage(`https://releases.targomo.com/tgm-icons/images/${icon}.png`, (error, image) => {
                    if (error) throw error
                    if (!map.hasImage(icon)) map.addImage(icon, image, { sdf: true })
                })
            }
        }
        function addLayers() {
            addPoisLayer()
            addReachablePoisLayer()
            addAggregatedPoisLayer()
        }
        function addPoisLayer() {
            map.addLayer({
                'id': 'poi',
                'type': 'symbol',
                'source': {
                    'type': 'vector',
                    'tiles': [POI_URL],
                    'minzoom': 9
                },
                'source-layer': 'poi',
                'layout': {
                    'icon-allow-overlap': true,
                    'icon-image': [
                        'match', ['get', 'groupId'],
                        'convenience', 'tgm-convenience',
                        'supermarket', 'tgm-grocery',
                        'tgm-circle'
                    ],
                    'icon-size': 0.25
                },
                'filter': ['==', ['get', 'numOfPois'], 1],
                'paint': {
                    'icon-color': 'gray'
                }
            })
        }
        function addReachablePoisLayer() {
            map.addLayer({
                'id': 'reachable-poi',
                'type': 'symbol',
                'source': 'reachable-poi-sources',
                'layout': {
                    'icon-allow-overlap': true,
                    'icon-image': [
                        'match', ['get', 'groupId'],
                        'convenience', 'tgm-convenience',
                        'supermarket', 'tgm-grocery',
                        'tgm-circle'
                    ],
                    'icon-size': 0.25
                },
                'paint': {
                    'icon-color': [
                        'match', ['get', 'groupId'],
                        'convenience', 'mediumpurple',
                        'supermarket', 'darkblue',
                        'gray'
                    ]
                }
            })
        }
        function addAggregatedPoisLayer() {
            map.addLayer({
                'id': 'poi-many',
                'type': 'symbol',
                'source': {
                    'type': 'vector',
                    'tiles': [POI_URL],
                    'minzoom': 9
                },
                'source-layer': 'poi',
                'layout': {
                    'icon-allow-overlap': true,
                    'icon-image': 'tgm-circle',
                    'icon-size': 0.25,
                    'text-field': '{numOfPois}',
                    'text-size': 8
                },
                'filter': ['>', ['get', 'numOfPois'], 1],
                'paint': {
                    'icon-color': 'gray',
                    'text-color': 'white'
                }
            })
        }
        
        async function updateMap() {
            pBar.show();
            let locations = getMarkersLocations(marker1, marker2)
        
            let scores = await getScores(locations)
            const reachablePois = getReachablePois(scores)
            const poiGeoJSON = poiLocationsToJSON(reachablePois)
            setMapReachablePoiSource(poiGeoJSON)
        
            updateLegend(scores)
        
            pBar.hide();
        }
        
        function getMarkersLocations(marker1, marker2) {
            let locations = []
            locations[0] = { ...marker1.getLngLat(), id: 0 }
            locations[1] = { ...marker2.getLngLat(), id: 1 }
            return locations
        }
        
        // Location Scoring API request.
        async function getScores(locations) {
            const config = POI_TYPES.reduce((acc, cur) => {
                acc[cur] = {
                    type: 'poiCoverageCount',
                    osmTypes: [ { 'key': 'group', 'value': cur } ],
                    maxEdgeWeight: MAX_TRAVEL, 
                    edgeWeight: EDGE_WEIGHT,
                    travelMode: { [TRAVEL_MODE]: {} },
                    coreServiceUrl: 'https://api.targomo.com/westcentraleurope/'
                }
                return acc
            }, {})
            let result = await client.quality.fetch(locations, config, true)
            return result.data
        }
        
        function getReachablePois(scores) {
            let reachablePois = []
            Object.values(scores).map((markerScore) => {
                let reachableConvenience = getReachablePoiByGroupId(markerScore, 'convenience')
                let reachableSupermarkets = getReachablePoiByGroupId(markerScore, 'supermarket')
                reachablePois.push(...reachableConvenience, ...reachableSupermarkets)
            })
            return reachablePois
        }
        function getReachablePoiByGroupId(marker, groupId) {
            return Object.keys(marker.details[groupId]).map((key) => {
                let node = marker.details[groupId][key][0]
                return nodeToLatLngId(node, groupId)
            })
        }
        function nodeToLatLngId(node, groupId) {
            return { id: 0, lat: node.lat, lng: node.lng, groupId: groupId}
        }
        
        function poiLocationsToJSON(latlngid) {
            return turf.featureCollection(latlngid.map(p => {
                let point = turf.point([p.lng, p.lat])
                point.properties = {'groupId':p.groupId}
                return point
            }))
        }
        function setMapReachablePoiSource(data) {
            map.getSource('reachable-poi-sources').setData(data)
        }
        
        function updateLegend(data) {
            let marker1scores = data[0].scores
            let marker2scores = data[1].scores
        
            let supermarketsCountMarker1 = marker1scores[POI_TYPES[0]]
            let convenienceCountMarker1 = marker1scores[POI_TYPES[1]]
            let supermarketsCountMarker2 = marker2scores[POI_TYPES[0]]
            let convenienceCountMarker2 = marker2scores[POI_TYPES[1]]
        
            let poiElem1 = document.querySelector('#markerScores1')
            poiElem1.innerHTML =  supermarketsCountMarker1 + ' supermarkets, ' + convenienceCountMarker1  + ' convenience stores'
            let poiElem2 = document.querySelector('#markerScores2')
            poiElem2.innerHTML = supermarketsCountMarker2 + ' supermarkets, ' + convenienceCountMarker2  + ' convenience stores'
        }</script>
</body>
</html>
content_copy
Copied to clipboard