Skip to content

Choropleth Map

Visualize data on a map by coloring polygons based on property values using data-driven fill-color expressions.

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <script src="https://cdn.smartmaps.cloud/packages/smartmaps/smartmaps-gl/v2/umd/smartmaps-gl.min.js"></script>
        <style>
            body { margin: 0; padding: 0; }
            html, body, #map { height: 100%; }
            #legend {
                position: absolute;
                bottom: 30px;
                right: 20px;
                background: white;
                padding: 12px 16px;
                border-radius: 8px;
                box-shadow: 0 2px 10px rgba(0,0,0,0.15);
                font-family: sans-serif;
                font-size: 13px;
                z-index: 1;
            }
            #legend h4 { margin: 0 0 8px 0; color: #18345c; }
            .legend-item { display: flex; align-items: center; gap: 8px; margin: 4px 0; }
            .legend-color { width: 16px; height: 16px; border-radius: 3px; border: 1px solid rgba(0,0,0,0.2); }
        </style>
    </head>
    <body>
        <div id="map"></div>
        <div id="legend">
            <h4>Density (per km²)</h4>
            <div class="legend-item"><span class="legend-color" style="background:#ffffcc"></span> &lt; 200</div>
            <div class="legend-item"><span class="legend-color" style="background:#a1dab4"></span> 200 – 500</div>
            <div class="legend-item"><span class="legend-color" style="background:#41b6c4"></span> 500 – 1000</div>
            <div class="legend-item"><span class="legend-color" style="background:#225ea8"></span> &gt; 1000</div>
        </div>
        <script>
            const map = new smartmapsgl.Map({
                apiKey: "[INSERT API-KEY]",
                container: "map",
                center: { lat: 48.78, lng: 9.18 },
                zoom: 10,
                style: smartmapsgl.MapStyle.LIGHT
            });

            // Stuttgart districts as GeoJSON polygons with population density
            const districts = {
                type: "FeatureCollection",
                features: [
                    {
                        type: "Feature",
                        properties: { name: "Stuttgart-Mitte", density: 5200 },
                        geometry: {
                            type: "Polygon",
                            coordinates: [[
                                [9.165, 48.785], [9.190, 48.785], [9.195, 48.775],
                                [9.190, 48.765], [9.170, 48.765], [9.160, 48.775], [9.165, 48.785]
                            ]]
                        }
                    },
                    {
                        type: "Feature",
                        properties: { name: "Bad Cannstatt", density: 3800 },
                        geometry: {
                            type: "Polygon",
                            coordinates: [[
                                [9.195, 48.810], [9.230, 48.810], [9.235, 48.795],
                                [9.220, 48.780], [9.195, 48.785], [9.190, 48.800], [9.195, 48.810]
                            ]]
                        }
                    },
                    {
                        type: "Feature",
                        properties: { name: "Vaihingen", density: 1500 },
                        geometry: {
                            type: "Polygon",
                            coordinates: [[
                                [9.080, 48.745], [9.120, 48.750], [9.130, 48.740],
                                [9.125, 48.720], [9.095, 48.715], [9.075, 48.730], [9.080, 48.745]
                            ]]
                        }
                    },
                    {
                        type: "Feature",
                        properties: { name: "Degerloch", density: 2200 },
                        geometry: {
                            type: "Polygon",
                            coordinates: [[
                                [9.160, 48.760], [9.190, 48.760], [9.195, 48.745],
                                [9.180, 48.735], [9.155, 48.738], [9.150, 48.750], [9.160, 48.760]
                            ]]
                        }
                    },
                    {
                        type: "Feature",
                        properties: { name: "Feuerbach", density: 3200 },
                        geometry: {
                            type: "Polygon",
                            coordinates: [[
                                [9.140, 48.815], [9.170, 48.815], [9.175, 48.800],
                                [9.165, 48.790], [9.145, 48.790], [9.135, 48.800], [9.140, 48.815]
                            ]]
                        }
                    },
                    {
                        type: "Feature",
                        properties: { name: "Plieningen", density: 650 },
                        geometry: {
                            type: "Polygon",
                            coordinates: [[
                                [9.190, 48.725], [9.225, 48.730], [9.230, 48.715],
                                [9.215, 48.700], [9.185, 48.700], [9.180, 48.715], [9.190, 48.725]
                            ]]
                        }
                    },
                    {
                        type: "Feature",
                        properties: { name: "Möhringen", density: 180 },
                        geometry: {
                            type: "Polygon",
                            coordinates: [[
                                [9.130, 48.735], [9.155, 48.738], [9.165, 48.725],
                                [9.155, 48.710], [9.130, 48.710], [9.120, 48.720], [9.130, 48.735]
                            ]]
                        }
                    }
                ]
            };

            map.on("load", () => {
                map.addSource("districts", { type: "geojson", data: districts });

                // Filled polygons with data-driven color
                map.addLayer({
                    id: "district-fill",
                    type: "fill",
                    source: "districts",
                    paint: {
                        "fill-color": [
                            "interpolate", ["linear"], ["get", "density"],
                            0, "#ffffcc",
                            200, "#ffffcc",
                            500, "#a1dab4",
                            1000, "#41b6c4",
                            3000, "#225ea8"
                        ],
                        "fill-opacity": 0.6
                    }
                });

                // Outline
                map.addLayer({
                    id: "district-outline",
                    type: "line",
                    source: "districts",
                    paint: {
                        "line-color": "#18345c",
                        "line-width": 2
                    }
                });

                // Labels
                map.addLayer({
                    id: "district-labels",
                    type: "symbol",
                    source: "districts",
                    layout: {
                        "text-field": ["get", "name"],
                        "text-size": 12,
                        "text-anchor": "center"
                    },
                    paint: {
                        "text-color": "#18345c",
                        "text-halo-color": "#ffffff",
                        "text-halo-width": 1.5
                    }
                });

                // Click popup with details
                map.on("click", "district-fill", (e) => {
                    const props = e.features[0].properties;
                    new smartmapsgl.Popup()
                        .setLngLat(e.lngLat)
                        .setHTML(`<strong>${props.name}</strong><br>Density: ${props.density.toLocaleString()} / km²`)
                        .addTo(map);
                });

                map.on("mouseenter", "district-fill", () => {
                    map.getCanvas().style.cursor = "pointer";
                });
                map.on("mouseleave", "district-fill", () => {
                    map.getCanvas().style.cursor = "";
                });
            });
        </script>
    </body>
</html>