Maplibre-GL Statistics Context
Quantify the reachability - see how many people are within 45 minutes on foot. Drag the marker to see how the reachable population changes
<!DOCTYPE html>
<html>
<head>
<!-- Include maplibregl javascript and css -->
<script src="https://unpkg.com/maplibre-gl@2.4.0/dist/maplibre-gl.js"></script>
<link href="https://unpkg.com/maplibre-gl@2.4.0/dist/maplibre-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%;
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> <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 = [-3.699543, 40.415822]
// set the progress bar
const pBar = new mipb({ fg: '#FF8319' })
// add the map and set the initial center to paris
const map = new maplibregl.Map({
container: 'map',
style: client.basemaps.getGLStyleURL('Light'),
minZoom: 9, zoom: 12.5, center: lnglat
}).addControl(new maplibregl.NavigationControl())
// disable scroll zoom
map.scrollZoom.disable()
const marker = new maplibregl.Marker({
draggable: true
}).setLngLat(lnglat).addTo(map)
marker.on('dragend', getStats)
const statsConfig = {
maxEdgeWeight: 1800, travelType: 'Walk', // 30 minutes on foot
statisticsGroup: 94, statistics: [{ id: 1 }] // Spain statistics on H3, total population
}
async function getStats() {
// show progress bar
pBar.show()
const sources = [{ ...marker.getLngLat(), id: 'Marker1' }]
// get the results as well as an id:travelTime map
const [statisticsResults, statisticsTimes] = await Promise.all([
client.statistics.dependent(sources, statsConfig),
client.statistics.travelTimes(sources, statsConfig)
])
const colors = [
'rgb(26,152,80)', 'rgb(145,207,96)', 'rgb(217,239,139)',
'rgb(254,224,139)', 'rgb(252,141,89)', 'rgb(215,48,39)'
]
const colorExpression = ['match', ['get', 'id']]
const opacityExpression = ['match', ['get', 'id']]
// Calculate color values for each country based on 'id' value
for (const [id, time] of Object.entries(statisticsTimes)) {
// Determine which color bucket the cell's value lies within
const colorIndex = Math.ceil((time/(statsConfig.maxEdgeWeight/colors.length)))-1
const color = colors[colorIndex]
// update the expressions
colorExpression.push(+id, color)
opacityExpression.push(+id, .5)
}
// Last value is the default, used where there is no data
colorExpression.push('#666')
opacityExpression.push(0.2)
// update the layer's style
map.setPaintProperty('statistics', 'fill-color', colorExpression)
map.setPaintProperty('statistics', 'fill-opacity', opacityExpression)
// tally full reachable population
const reachablePop = Object.values(
statisticsResults.raw.statistics[statsConfig.statistics[0].id]
).reduce((acc, cur) => { return acc+cur }, 0)
// update the
var popElem = document.querySelector('#total-pop')
popElem.innerHTML = Math.round(reachablePop).toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,')
// hide progress bar
pBar.hide()
map.on('click', 'statistics', (e) => {
const travelTime = Math.round(statisticsTimes[e.features[0].properties.id] / 60)
const description = `Population: ${Math.round(e.features[0].properties[statsConfig.statistics[0].id])}
<br>Walk time: ${travelTime ? travelTime : 'not reachable'}`
new maplibregl.Popup()
.setLngLat(e.lngLat)
.setHTML(description).addTo(map)
})
}
map.on('load', async () => {
// get metadata for statistics group 123 (France INSEE on hexagons)
// to see the available statistics, as well as details of the dataset
const meta = await client.statistics.metadata(statsConfig.statisticsGroup)
// get the vectortiles url
const tileRoute = await client.statistics.tileRoute(statsConfig.statisticsGroup, [statsConfig.statistics[0]])
// add the tiles to the map
map.addLayer({
id: 'statistics',
type: 'fill',
source: {
type: 'vector',
tiles: [tileRoute],
minzoom: meta.min_zoom,
attribution: '<a href="https://www.targomo.com/developers/resources/attribution/" target="_blank">© Targomo</a>'
},
'source-layer': `${statsConfig.statisticsGroup}`, // source-layer is the statistic group id
layout: {},
paint: {
'fill-opacity': 0.2,
'fill-color': '#666',
'fill-outline-color': 'rgba(50, 50, 50, 0.1)'
}
}, '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>
Copied to clipboard