Skip to content
Show Polygon Information on Click

Show Polygon Information on Click

Showing feature data on polygon click requires listening to the map’s click event on a fill layer and reading the properties of the first returned feature. A Popup is then placed at the click coordinates with the feature properties formatted as HTML. This example loads Austria’s federal-state boundaries from a public GeoJSON dataset, groups them into Western and Eastern Austria with one color each, and shows the region name, area, and capital on click. Use this pattern for choropleth maps, administrative boundary explorers, or any map where polygon features carry data that users should be able to inspect by clicking.

const API_KEY = 'YOUR_API_KEY';

    const map = new maptoolkit.Map({
        container: 'map',
        apiKey: API_KEY,
        style: `https://styles.maptoolkit.net/maptoolkit/maptoolkit.summer.json?api_key=${API_KEY}`,
        center: [13.35, 47.6],
        zoom: 6.4,
        attributionControl: { compact: false }
    });

    map.addControl(new maptoolkit.NavigationControl(), 'top-right');

    // Austria's nine federal states, grouped into West (capital Innsbruck) and East (capital Vienna).
    const WEST_STATES = ['Tirol', 'Vorarlberg', 'Salzburg'];

    // Geodesic area (m^2) for Polygon / MultiPolygon rings (spherical excess, like turf.area).
    function ringArea(ring) {
        const R = 6378137;
        let total = 0;
        for (let i = 0; i < ring.length - 1; i++) {
            const [lng1, lat1] = ring[i];
            const [lng2, lat2] = ring[i + 1];
            total += (lng2 - lng1) * Math.PI / 180 *
                (2 + Math.sin(lat1 * Math.PI / 180) + Math.sin(lat2 * Math.PI / 180));
        }
        return Math.abs(total * R * R / 2);
    }
    function polygonArea(rings) {
        let area = ringArea(rings[0]);
        for (let i = 1; i < rings.length; i++) area -= ringArea(rings[i]); // subtract holes
        return area;
    }
    function featureArea(geometry) {
        if (geometry.type === 'MultiPolygon') {
            return geometry.coordinates.reduce((sum, poly) => sum + polygonArea(poly), 0);
        }
        return polygonArea(geometry.coordinates);
    }

    map.on('load', async () => {
        // Administrative boundaries: public dataset (Statistik Austria, via the
        // ginseng666/GeoJSON-TopoJSON-Austria project), served over the jsDelivr CDN.
        const response = await fetch('https://cdn.jsdelivr.net/gh/ginseng666/GeoJSON-TopoJSON-Austria@master/2021/simplified-99.9/laender_999_geo.json');
        const states = await response.json();

        // Tag each state with its group, then total each group's area.
        const groupArea = { West: 0, East: 0 };
        states.features.forEach((feature) => {
            const group = WEST_STATES.includes(feature.properties.name) ? 'West' : 'East';
            feature.properties.group = group;
            feature.properties.region = group === 'West' ? 'Western Austria' : 'Eastern Austria';
            feature.properties.capital = group === 'West' ? 'Innsbruck' : 'Vienna';
            groupArea[group] += featureArea(feature.geometry);
        });
        states.features.forEach((feature) => {
            feature.properties.regionArea = Math.round(groupArea[feature.properties.group] / 1e6).toLocaleString() + ' km²';
        });

        map.addSource('states', { type: 'geojson', data: states });

        // One color per group: West vs East.
        map.addLayer({
            id: 'states-fill',
            type: 'fill',
            source: 'states',
            paint: {
                'fill-color': ['match', ['get', 'group'], 'West', '#2c7fb8', 'East', '#de2d26', '#888888'],
                'fill-opacity': 0.45
            }
        });

        map.addLayer({
            id: 'states-border',
            type: 'line',
            source: 'states',
            paint: { 'line-color': '#ffffff', 'line-width': 1 }
        });

        map.fitBounds([[9.45, 46.35], [17.2, 49.1]], { padding: 30, duration: 0 });

        map.on('click', 'states-fill', (e) => {
            const p = e.features[0].properties;
            new maptoolkit.Popup()
                .setLngLat(e.lngLat)
                .setHTML(`<strong>${p.region}</strong><br>State: ${p.name}<br>Region area: ${p.regionArea}<br>Capital: ${p.capital}`)
                .addTo(map);
        });

        map.on('mouseenter', 'states-fill', () => { map.getCanvas().style.cursor = 'pointer'; });
        map.on('mouseleave', 'states-fill', () => { map.getCanvas().style.cursor = ''; });
    });
<!DOCTYPE html>
<html lang="en">
<head>
    <title>Show Polygon Information on Click - Maptoolkit Maps JS</title>
    <meta property="og:description" content="Show a popup with feature properties when clicking on a polygon." />
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <script src="https://unpkg.com/@maptoolkit/[email protected]/dist/maptoolkit.js"></script>
    <link rel="stylesheet" href="https://unpkg.com/@maptoolkit/[email protected]/dist/maptoolkit.css" />
    <style>
        html, body { width: 100%; height: 100%; margin: 0; padding: 0; }
        #map { width: 100%; height: 100%; }
    </style>
</head>
<body>
<div id="map"></div>
<script>
    const API_KEY = 'YOUR_API_KEY';

    const map = new maptoolkit.Map({
        container: 'map',
        apiKey: API_KEY,
        style: `https://styles.maptoolkit.net/maptoolkit/maptoolkit.summer.json?api_key=${API_KEY}`,
        center: [13.35, 47.6],
        zoom: 6.4,
        attributionControl: { compact: false }
    });

    map.addControl(new maptoolkit.NavigationControl(), 'top-right');

    // Austria's nine federal states, grouped into West (capital Innsbruck) and East (capital Vienna).
    const WEST_STATES = ['Tirol', 'Vorarlberg', 'Salzburg'];

    // Geodesic area (m^2) for Polygon / MultiPolygon rings (spherical excess, like turf.area).
    function ringArea(ring) {
        const R = 6378137;
        let total = 0;
        for (let i = 0; i < ring.length - 1; i++) {
            const [lng1, lat1] = ring[i];
            const [lng2, lat2] = ring[i + 1];
            total += (lng2 - lng1) * Math.PI / 180 *
                (2 + Math.sin(lat1 * Math.PI / 180) + Math.sin(lat2 * Math.PI / 180));
        }
        return Math.abs(total * R * R / 2);
    }
    function polygonArea(rings) {
        let area = ringArea(rings[0]);
        for (let i = 1; i < rings.length; i++) area -= ringArea(rings[i]); // subtract holes
        return area;
    }
    function featureArea(geometry) {
        if (geometry.type === 'MultiPolygon') {
            return geometry.coordinates.reduce((sum, poly) => sum + polygonArea(poly), 0);
        }
        return polygonArea(geometry.coordinates);
    }

    map.on('load', async () => {
        // Administrative boundaries: public dataset (Statistik Austria, via the
        // ginseng666/GeoJSON-TopoJSON-Austria project), served over the jsDelivr CDN.
        const response = await fetch('https://cdn.jsdelivr.net/gh/ginseng666/GeoJSON-TopoJSON-Austria@master/2021/simplified-99.9/laender_999_geo.json');
        const states = await response.json();

        // Tag each state with its group, then total each group's area.
        const groupArea = { West: 0, East: 0 };
        states.features.forEach((feature) => {
            const group = WEST_STATES.includes(feature.properties.name) ? 'West' : 'East';
            feature.properties.group = group;
            feature.properties.region = group === 'West' ? 'Western Austria' : 'Eastern Austria';
            feature.properties.capital = group === 'West' ? 'Innsbruck' : 'Vienna';
            groupArea[group] += featureArea(feature.geometry);
        });
        states.features.forEach((feature) => {
            feature.properties.regionArea = Math.round(groupArea[feature.properties.group] / 1e6).toLocaleString() + ' km²';
        });

        map.addSource('states', { type: 'geojson', data: states });

        // One color per group: West vs East.
        map.addLayer({
            id: 'states-fill',
            type: 'fill',
            source: 'states',
            paint: {
                'fill-color': ['match', ['get', 'group'], 'West', '#2c7fb8', 'East', '#de2d26', '#888888'],
                'fill-opacity': 0.45
            }
        });

        map.addLayer({
            id: 'states-border',
            type: 'line',
            source: 'states',
            paint: { 'line-color': '#ffffff', 'line-width': 1 }
        });

        map.fitBounds([[9.45, 46.35], [17.2, 49.1]], { padding: 30, duration: 0 });

        map.on('click', 'states-fill', (e) => {
            const p = e.features[0].properties;
            new maptoolkit.Popup()
                .setLngLat(e.lngLat)
                .setHTML(`<strong>${p.region}</strong><br>State: ${p.name}<br>Region area: ${p.regionArea}<br>Capital: ${p.capital}`)
                .addTo(map);
        });

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

Use the prompt below with any LLM to get the same result. Make sure the Maptoolkit MCP server is connected first — check out AI Integration & MCP to get started.

Use the Maptoolkit Connector. Create an interactive map centered on Austria. Load the Austrian federal-state boundaries from a public GeoJSON dataset, group the states into Western Austria (Tirol, Vorarlberg, Salzburg) and Eastern Austria with one fill color each, and show a popup with the region name, area, and capital on click.