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>
content_copy
Copied to clipboard