Skip to content

Add Contour Lines

Add contour lines to your map from a raster-dem source.

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Add Contour Lines</title>
    <meta property="og:description" content="Add contour lines to your map from a raster-dem source." />
    <meta charset='utf-8'>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel='stylesheet' href='https://unpkg.com/maplibre-gl@4.4.0/dist/maplibre-gl.css' />
    <script src='https://unpkg.com/maplibre-gl@4.4.0/dist/maplibre-gl.js'></script>
    <style>
        body { margin: 0; padding: 0; }
        html, body, #map { height: 100%; }
    </style>
</head>
<body>
<div id="map"></div>
<script src="https://unpkg.com/maplibre-contour@0.0.5/dist/index.min.js"></script>
<script>
    const demSource = new mlcontour.DemSource({
        url: 'https://demotiles.maplibre.org/terrain-tiles/{z}/{x}/{y}.png',
        encoding: 'mapbox',
        maxzoom: 12,
        // offload contour line computation to a web worker
        worker: true
    });

    // calls maplibregl.addProtocol to register a dynamic vector tile provider that
    // downloads raster-dem tiles, computes contour lines, and encodes as a vector
    // tile for each tile request from maplibre
    demSource.setupMaplibre(maplibregl);

    const map = (window.map = new maplibregl.Map({
        container: 'map',
        zoom: 13,
        center: [11.3229, 47.2738],
        hash: true,
        style: {
            version: 8,
            glyphs: 'https://demotiles.maplibre.org/font/{fontstack}/{range}.pbf',
            sources: {
                hillshadeSource: {
                    type: 'raster-dem',
                    // share cached raster-dem tiles with the contour source
                    tiles: [demSource.sharedDemProtocolUrl],
                    tileSize: 512,
                    maxzoom: 12
                },
                contourSourceFeet: {
                    type: 'vector',
                    tiles: [
                        demSource.contourProtocolUrl({
                        // meters to feet
                            multiplier: 3.28084,
                            overzoom: 1,
                            thresholds: {
                            // zoom: [minor, major]
                                11: [200, 1000],
                                12: [100, 500],
                                13: [100, 500],
                                14: [50, 200],
                                15: [20, 100]
                            },
                            elevationKey: 'ele',
                            levelKey: 'level',
                            contourLayer: 'contours'
                        })
                    ],
                    maxzoom: 15
                }
            },
            layers: [
                {
                    id: 'hills',
                    type: 'hillshade',
                    source: 'hillshadeSource',
                    layout: {visibility: 'visible'},
                    paint: {'hillshade-exaggeration': 0.25}
                },
                {
                    id: 'contours',
                    type: 'line',
                    source: 'contourSourceFeet',
                    'source-layer': 'contours',
                    paint: {
                        'line-opacity': 0.5,
                        // "major" contours have level=1, "minor" have level=0
                        'line-width': ['match', ['get', 'level'], 1, 1, 0.5]
                    }
                },
                {
                    id: 'contour-text',
                    type: 'symbol',
                    source: 'contourSourceFeet',
                    'source-layer': 'contours',
                    filter: ['>', ['get', 'level'], 0],
                    paint: {
                        'text-halo-color': 'white',
                        'text-halo-width': 1
                    },
                    layout: {
                        'symbol-placement': 'line',
                        'text-size': 10,
                        'text-field': [
                            'concat',
                            ['number-format', ['get', 'ele'], {}],
                            '\''
                        ],
                        'text-font': ['Noto Sans Bold']
                    }
                }
            ]
        }
    }));
</script>
</body>
</html>