Search code examples
javascriptleafletgis

Leaflet map doesn't fire any of the events I've created?


For the site I'm making, I've added a Leaflet.js map of South Africa with provinces following the example choropleth map in the Leaflet docs. I've tried my best to follow the docs but to no avail.

The provinces are showing up nicely but my events aren't firing and I'm not sure why. The idea is to make the province that's hovered over a different colour from the others and when clicked on the province returns its name and three political parties to vote for in that province.

I'll do some AJAX requests to my PHP pages for that later, I just want to solve this problem first of my events not firing.

The code I'm using and the image of the map I'm using.

// This object is used to create the interactive provinces on the map
// The IDs for every feature are taken from the results of every province from 
// https://mapit.code4sa.org/#api-by_area_id
var provinceData = {
    "type": "FeatureCollection",
    "features":[
        {
            "type": "Feature",
            "id": "4288",
            "properties": {"name": "Eastern Cape", "abbr": "EC"},
            "geometry": null
        },
        {
            "type": "Feature",
            "id": "4289",
            "properties": {"name": "Free State", "abbr": "FS"},
            "geometry": null
        },
        {
            "type": "Feature",
            "id": "4290",
            "properties": {"name": "Gauteng", "abbr": "GP"},
            "geometry": null
        },
        {
            "type": "Feature",
            "id": "4291",
            "properties": {"name": "KwaZulu-Natal", "abbr": "KZN"},
            "geometry": null
        },
        {
            "type": "Feature",
            "id": "4292",
            "properties": {"name": "Limpopo", "abbr": "LM"},
            "geometry": null
        },
        {
            "type": "Feature",
            "id": "4293",
            "properties": {"name": "Mpumalanga", "abbr": "MP"},
            "geometry": null
        },
        {
            "type": "Feature",
            "id": "4294",
            "properties": {"name": "Northern Cape", "abbr": "NC"},
            "geometry": null
        },
        {
            "type": "Feature",
            "id": "4295",
            "properties": {"name": "North West", "abbr": "NW"},
            "geometry": null
        },
        {
            "type": "Feature",
            "id": "4296",
            "properties": {"name": "Western Cape", "abbr": "WC"},
            "geometry": null
        }
    ]
};

// Get the province title h1 and province result div
let provinceTitle = document.getElementById("province_name");
let provinceResult = document.getElementById("province_result");

// initialize the map on the "map" div with a given center and zoom
let map = L.map('map').setView([-29.459, 24.785], 5);

var tiles = L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
    maxZoom: 19,
    attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> | <a href="https://mapit.code4sa.org/#api-by_area_id">Powered by MapIt</a>'
}).addTo(map);

// Fetch province geometries and assign them to the features
async function fetchProvinceGeometry(url) {
    try {
        // Do async await to retrieve GeoJSON of every province
        const response = await fetch(url);
        const data = await response.json();
        return data;
    } catch (error) {
        console.error("Error fetching province geometry:", error);
        return null;
    }
}

// Update provinceData with fetched geometries
async function updateProvinceGeometries() {
    // Loop through the provinceData object and get the ids from every feature to
    // get the GeoJSON of every province and put it on the map
    for (let feature of provinceData.features) {
        const url = `https://mapit.code4sa.org/area/${feature.id}.geojson`;
        const geometry = await fetchProvinceGeometry(url);
        if (geometry) {
            feature.geometry = geometry;
        }
    }

    // Once all geometries are fetched, add GeoJSON data to the map
    L.geoJson(provinceData).addTo(map);
}

// Call the function to update province geometries
updateProvinceGeometries();

// PROBLEM STARTS HERE ///////////////////////////////////////////

// Create the mouseout, onclick and mouseover event
// Also create the function that takes a feature's province abbr and returns the parties to be displayed for
// the province.
var geojson;
geojson = L.geoJson(provinceData).addTo(map);

function highlightProvince(e)
{
    e.target.setStyle(
        {
            weight: 5,
            color: '#689',
            dashArray: '',
            fillOpacity: 0.7
        }
    );

    e.target.bringToFront();
}

function resetProvince(e)
{
    geoJson.resetStyle(e.target);
}

function returnProvinceResults(abbr)
{
    switch (abbr)
    {
        case "WC":
            console.log(69);
            break;
        case "EC":
            console.log(70);
            break;
        case "NC":
            console.log(71);
            break;
        case "KZN":
            console.log(72);
            break;
        case "FS":
            console.log(73);
            break;
        case "NW":
            console.log(74);
            break;
        case "GP":
            console.log(75);
            break;
        case "MP":
            console.log(76);
            break;
        case "LM":
            console.log(77);
            break;
    }
}

function onClickProvince(e)
{
    console.log("ONLINE");
    provinceTitle.textContent = e.target.properties.name;
}

// Bring all the events together in one function that is then fired in the geojson variable.

function eventsWrapper(feature, layer) {
    layer.on({
        mouseover: highlightProvince,
        mouseout: resetProvince,
        click: onClickProvince
    });
}

geojson = L.geoJson(provinceData, {
    style: style,
    onEachFeature: eventsWrapper
}).addTo(map);

Image of the map


Solution

  • You'd want to use the onEachFeature for the events on the L.geoJSON() where the geometries are actually drawn, which would be in the updateProvinceGeometries() function.

    // This object is used to create the interactive provinces on the map
    // The IDs for every feature are taken from the results of every province from 
    // https://mapit.code4sa.org/#api-by_area_id
    var provinceData = {
        "type": "FeatureCollection",
        "features":[
            {
                "type": "Feature",
                "id": "4288",
                "properties": {"name": "Eastern Cape", "abbr": "EC"},
                "geometry": null
            },
            {
                "type": "Feature",
                "id": "4289",
                "properties": {"name": "Free State", "abbr": "FS"},
                "geometry": null
            },
            {
                "type": "Feature",
                "id": "4290",
                "properties": {"name": "Gauteng", "abbr": "GP"},
                "geometry": null
            },
            {
                "type": "Feature",
                "id": "4291",
                "properties": {"name": "KwaZulu-Natal", "abbr": "KZN"},
                "geometry": null
            },
            {
                "type": "Feature",
                "id": "4292",
                "properties": {"name": "Limpopo", "abbr": "LM"},
                "geometry": null
            },
            {
                "type": "Feature",
                "id": "4293",
                "properties": {"name": "Mpumalanga", "abbr": "MP"},
                "geometry": null
            },
            {
                "type": "Feature",
                "id": "4294",
                "properties": {"name": "Northern Cape", "abbr": "NC"},
                "geometry": null
            },
            {
                "type": "Feature",
                "id": "4295",
                "properties": {"name": "North West", "abbr": "NW"},
                "geometry": null
            },
            {
                "type": "Feature",
                "id": "4296",
                "properties": {"name": "Western Cape", "abbr": "WC"},
                "geometry": null
            }
        ]
    };
    
    // Get the province title h1 and province result div
    let provinceTitle = document.getElementById("province_name");
    let provinceResult = document.getElementById("province_result");
    
    // initialize the map on the "map" div with a given center and zoom
    let map = L.map('map').setView([-29.459, 24.785], 5);
    
    var tiles = L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
        maxZoom: 19,
        attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> | <a href="https://mapit.code4sa.org/#api-by_area_id">Powered by MapIt</a>'
    }).addTo(map);
    
    // Fetch province geometries and assign them to the features
    async function fetchProvinceGeometry(url) {
        try {
            // Do async await to retrieve GeoJSON of every province
            const response = await fetch(url);
            const data = await response.json();
            return data;
        } catch (error) {
            console.error("Error fetching province geometry:", error);
            return null;
        }
    }
    
    // Update provinceData with fetched geometries
    async function updateProvinceGeometries() {
        // Loop through the provinceData object and get the ids from every feature to
        // get the GeoJSON of every province and put it on the map
        for (let feature of provinceData.features) {
            const url = `https://mapit.code4sa.org/area/${feature.id}.geojson`;
            const geometry = await fetchProvinceGeometry(url);
            if (geometry) {
                feature.geometry = geometry;
            }
        }
    
        // Once all geometries are fetched, add GeoJSON data to the map
        L.geoJson(provinceData, {
          onEachFeature: eventsWrapper
        }).addTo(map);
    }
    
    // Call the function to update province geometries
    updateProvinceGeometries();
    
    // PROBLEM STARTS HERE ///////////////////////////////////////////
    
    // Create the mouseout, onclick and mouseover event
    // Also create the function that takes a feature's province abbr and returns the parties to be displayed for
    // the province.
    var geojson;
    geojson = L.geoJson(provinceData).addTo(map);
    
    function highlightProvince(e)
    {
        e.target.setStyle(
            {
                weight: 5,
                color: '#689',
                dashArray: '',
                fillOpacity: 0.7
            }
        );
    
        e.target.bringToFront();
    }
    
    function resetProvince(e)
    {
        geoJson.resetStyle(e.target);
    }
    
    function returnProvinceResults(abbr)
    {
        switch (abbr)
        {
            case "WC":
                console.log(69);
                break;
            case "EC":
                console.log(70);
                break;
            case "NC":
                console.log(71);
                break;
            case "KZN":
                console.log(72);
                break;
            case "FS":
                console.log(73);
                break;
            case "NW":
                console.log(74);
                break;
            case "GP":
                console.log(75);
                break;
            case "MP":
                console.log(76);
                break;
            case "LM":
                console.log(77);
                break;
        }
    }
    
    function onClickProvince(e)
    {
        console.log("ONLINE");
        provinceTitle.textContent = e.target.properties.name;
    }
    
    // Bring all the events together in one function that is then fired in the geojson variable.
    
    function eventsWrapper(feature, layer) {
        layer.on({
            mouseover: highlightProvince,
            mouseout: resetProvince,
            click: onClickProvince
        });
    }
    
    geojson = L.geoJson(provinceData, {
        onEachFeature: eventsWrapper
    }).addTo(map);
    #map {
      width: 90vw;
      height: 90vh;
    }
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet.min.css" integrity="sha512-h9FcoyWjHcOcmEVkxOfTLnmZFWIH0iZhZT1H2TbOq55xssQGEJHEaIm+PgoUaZbRvQTNTluNOEfb1ZRy6D3BOw==" crossorigin="anonymous" referrerpolicy="no-referrer" />
    <script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet.js" integrity="sha512-BwHfrr4c9kmRkLw6iXFdzcdWV/PGkVgiIyIWLLlTSXzWQzxuSg4DiQUCpauz/EWjgk5TYQqX/kvn9pG1NpYfqg==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
    
    <div id="map"></div>
    <div id="province_name"></div>
    <div id="province_result"></div>