Munich Octoberfest Footfall Comparison
Compare pedestrian traffic during Octoberfest vs rest of year using side-by-side maps
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Munich Octoberfest Footfall Comparison</title>
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
<!-- MapLibre GL -->
<link href="https://unpkg.com/maplibre-gl@5.14.0/dist/maplibre-gl.css" rel="stylesheet" />
<script src="https://unpkg.com/maplibre-gl@5.14.0/dist/maplibre-gl.js"></script>
<!-- MapLibre GL Compare -->
<link href="__maplibre_compare_css__" rel="stylesheet" />
<script src="__maplibre_compare_js__"></script>
<style>
body {
margin: 0;
padding: 0;
}
#comparison-container {
position: absolute;
top: 0;
bottom: 0;
width: 100%;
}
.map {
position: absolute;
top: 0;
bottom: 0;
width: 100%;
}
.label {
position: absolute;
top: 40%;
transform: translateY(-50%);
/* Center vertically */
padding: 10px 16px;
/* Larger padding */
background: rgba(255, 255, 255, 0.8);
font-family: sans-serif;
font-size: 38px;
/* Larger font */
font-weight: bold;
border-radius: 6px;
z-index: 10;
}
.label-left {
left: 30px;
}
.label-right {
right: 30px;
}
</style>
</head>
<body>
<div id="comparison-container">
<div id="Octoberfest" class="map"></div>
<div id="Rest of the year" class="map"></div>
<div class="label label-left">Octoberfest</div>
<div class="label label-right">Rest of the year</div>
</div>
<div id="opacity-control"
style="position:absolute;top:10px;left:10px;z-index:1000;background:rgba(255,255,255,0.9);padding:10px;border-radius:6px;">
<label for="opacity-slider">Heatmap opacity: </label>
<input id="opacity-slider" type="range" min="0" max="1" step="0.01" value="0.8">
<span id="opacity-value">0.8</span>
</div>
<script>// Munich center
const center = [11.5621, 48.1371];
const zoom = 14.3;
// Jenks breaks from QGIS
const breaks = [
0.028165277056355,
1.0,
2.0,
3.0,
5.0,
6.869,
8.317,
11.365,
14.413,
17.461,
21.509,
25.0,
30.605,
39.653542697736498
];
// A blue color ramp (repeats if breaks > colors)
const colors = [
"#000004",
"#110a30",
"#320a5e",
"#57106e",
"#781c6d",
"#9a2865",
"#bc3754",
"#d84c3e",
"#ed6925",
"#f98e09",
"#fbb61a",
"#f4df53",
"#fcffa4",
];
function makeStepExpression(property, breaks) {
const expr = ['step', ['get', property], colors[0]];
for (let i = 1; i < breaks.length && i < colors.length; i++) {
expr.push(breaks[i]);
expr.push(colors[i % colors.length]);
}
return expr;
}
// Create a minimal style with OSM raster tiles
function createBaseStyle() {
return {
version: 8,
sources: {
'osm-tiles': {
type: 'raster',
tiles: ['https://tile.openstreetmap.org/{z}/{x}/{y}.png'],
tileSize: 256,
attribution: '© OpenStreetMap contributors'
}
},
layers: [
{ id: 'osm-tiles', type: 'raster', source: 'osm-tiles' }
]
};
}
// Octoberfest map
const mapOctoberfest = new maplibregl.Map({
container: 'Octoberfest',
style: createBaseStyle(),
center: center,
zoom: zoom
});
mapOctoberfest.on('load', () => {
mapOctoberfest.addSource('footfall', {
type: 'vector',
tiles: [
"https://api.targomo.com/basemaps/data/muenchen-tourists-locals/{z}/{x}/{y}.pbf?key=__targomo_key_here__"
],
});
mapOctoberfest.addLayer({
id: 'octoberfest-layer',
type: 'fill',
source: 'footfall',
'source-layer': 'footfall',
paint: {
'fill-color': makeStepExpression('octoberfest', breaks),
'fill-opacity': 0.8
}
});
});
// NonOctoberfest map
const mapNonOctoberfest = new maplibregl.Map({
container: 'Rest of the year',
style: createBaseStyle(),
center: center,
zoom: zoom
});
mapNonOctoberfest.on('load', () => {
mapNonOctoberfest.addSource('footfall', {
type: 'vector',
tiles: [
"https://api.targomo.com/basemaps/data/muenchen-tourists-locals/{z}/{x}/{y}.pbf?key=__targomo_key_here__"
],
});
mapNonOctoberfest.addLayer({
id: 'non-octoberfest-layer',
type: 'fill',
source: 'footfall',
'source-layer': 'footfall',
paint: {
'fill-color': makeStepExpression('non-octoberfest', breaks),
'fill-opacity': 0.8
}
});
});
// Add comparison slider
new maplibregl.Compare(mapOctoberfest, mapNonOctoberfest, '#comparison-container', {
orientation: 'vertical'
});
const slider = document.getElementById("opacity-slider");
const opacityValue = document.getElementById("opacity-value");
slider.addEventListener("input", () => {
const opacity = parseFloat(slider.value);
opacityValue.innerText = opacity.toFixed(2);
if (mapOctoberfest.getLayer("octoberfest-layer")) {
mapOctoberfest.setPaintProperty("octoberfest-layer", "fill-opacity", opacity);
}
if (mapNonOctoberfest.getLayer("non-octoberfest-layer")) {
mapNonOctoberfest.setPaintProperty("non-octoberfest-layer", "fill-opacity", opacity);
}
});
</script>
</body>
</html>
Copied to clipboard