Visualize Multigraph with Deck.gl
Multigraph’s power comes from it’s ability to return multiple geometry types. The base-level is nodes - here we’re using the raw nodes to manually aggregate into dynamic hexagons
<!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 deck.gl -->
<script src="https://unpkg.com/deck.gl@latest/dist.min.js"></script>
<script type="text/javascript">
const { MapboxLayer, HexagonLayer } = deck;
</script>
<!-- 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%;
}
#control-panel {
position: absolute;
margin: 10px;
font-family: 'Open Sans', sans-serif;
top: 0; z-index: 100;
display: flex;
flex-direction: column;
padding: 5px 8px;
background: #fff;
}
.panel-item {
display: flex;
align-items: center;
justify-content: space-between;
}
#map {
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<!-- where the map will live -->
<div id="map"></div>
<div id="control-panel">
<div class="panel-item">
<label>Hex size</label>
<input id="radius" type="range" min="100" max="500" step="50" value="200"></input>
<span id="radius-value"></span>
</div>
<div class="panel-item">
<label>Hex pct.</label>
<input id="coverage" type="range" min="0" max="1" step="0.1" value=".9"></input>
<span id="coverage-value"></span>
</div>
</div>
<script>
// create targomo client
const client = new tgm.TargomoClient('westcentraleurope', '__targomo_key_here__')
// Coordinates to center the map
const source = { id: 'src', lng: 2.375, lat: 48.844 }
const attributionText = '<a href="https://www.targomo.com/developers/resources/attribution/" target="_blank">© Targomo</a>'
// set the progress bar
const pBar = new mipb({ fg: '#FF8319' })
// add the map and set the initial center to berlin
const map = new maplibregl.Map({
container: 'map',
style: client.basemaps.getGLStyleURL('Light'),
zoom: 11, pitch: 40,
center: source,
attributionControl: false
})
.addControl(new maplibregl.NavigationControl())
.addControl(new maplibregl.AttributionControl({ customAttribution: attributionText }))
// disable scroll zoom
map.scrollZoom.disable()
const marker = new maplibregl.Marker({
//draggable: true
}).setLngLat(source).addTo(map)
// container for housing the multigraph data
let data = []
const OPTIONS = ['radius', 'coverage']
const COLOR_RANGE = [
[26, 152, 80], [145, 207, 96],
[217, 239, 139], [254, 224, 139],
[252, 141, 89], [215, 48, 39]
]
function renderLayer() {
const options = {}
OPTIONS.forEach((key) => {
const value = +document.getElementById(key).value
document.getElementById(key + '-value').innerHTML = value
options[key] = value
})
const myDeckLayer = new MapboxLayer({
id: 'heatmap',
type: HexagonLayer,
colorRange: COLOR_RANGE,
data,
getPosition: (d) => [Number(d.lng), Number(d.lat)],
getColorWeight: (d) => d.w,
colorAggregation: 'MEAN',
opacity: 0.3,
...options
})
if (map.getLayer('heatmap')) {
map.removeLayer('heatmap')
}
map.addLayer(myDeckLayer, 'place_other')
}
async function setUp() {
// show progress bar
pBar.show()
// here we're deciding that we want transit coverage, 30 minutes (1800s), nodes (as we are aggregating with deckGL)
const multigraphOptions = {
edgeWeight: 'time',
travelType: 'transit',
maxEdgeWeight: 1800,
multigraph: {
layer: { type: 'identity' },
domain: { type: 'node' },
serialization: { format: 'geojson' },
aggregation: { type: 'routing_union' }
}
}
const mg = await client.multigraph.fetch([source], multigraphOptions)
// hide progress bar
pBar.hide()
data = mg.data.features.map((f) => {
return {
lng: f.geometry.coordinates[0],
lat: f.geometry.coordinates[1],
w: f.properties.w
}
})
// initial layer rendering
renderLayer()
OPTIONS.forEach((key) => {
// re-render when hex settings change
document.getElementById(key).oninput = renderLayer
})
}
map.on('load', () => {
setUp()
})
</script>
</body>
</html>
Copied to clipboard