Search code examples
ajaxleafletgeojson

AJAX and Leaflet: Inspect feature properties before styling/adding to map


I'm using leaflet-ajax to load geoJSON on demand. I want to find the maximum theProperty value so I can use that to scale the feature's fill colors before I add them to the map.

Here's my general approach:

    function maxBinPropertyValue(theProperty) {
        var theGeoJson = null;
        var maxPropertyValue = 0;
        var propertyValue = null;
        var theGeoJson = new L.GeoJSON.AJAX(binsFileName());
        theGeoJson.on('data:loaded', function() {
            console.log('The data is loaded');
            theGeoJson.eachLayer(function(layer) {
                console.log('Looping through the layers');
                propertyValue = feature.properties[theProperty];
                if (propertyValue > maxPropertyValue) {
                    maxPropertyValue = propertyValue;
                    console.log('Max so far: ' + maxPropertyValue);
                };
            });
        });
        theGeoJson = null;
        console.log('The final maximum value: ' + maxPropertyValue);
        return maxPropertyValue;
    };

I'm trying to wait for the data:loaded event, then loop through all the features to find the maximum value of theProperty, which is returned to the calling routine.

Except it doesn't work. The first console.log says 'The data is loaded'. The second and third console.logs are never reached, and the fourth and final one reports a value of 0 for maxPropertyValue.

How can I examine all the features in a featureset before styling them, in a way guaranteed to not have asynchronous problems?

PS: I'm pretty sure I can't use onEachFeature: instead of the above approach, because I need to examine every feature's property to determine the maximum value in the set before I can style any of the features.


Solution

  • As for your issue about inspecting your data and retrieving the maximum value, you are indeed facing the classic asynchronous concept of JavaScript.

    See How do I return the response from an asynchronous call?

    Asynchronism is a problem if not dealt with properly, but an advantage if correctly handled.

    To put the concept shortly, you do not manage asynchronism in a "standard" sequential way, but you should rather consider parts of code (callbacks) that are executed at a later time based on events.

    Whenever you provide a function as an argument, it is certainly a callback that will be executed at a later time, but very probably much later than the next instructions.

    So in your case, your 2nd and 3rd console.log are within a callback, and will be executed once your data is loaded, which will happen much later than your 4th console.log.

    As for your next step (styling and adding to map), you actually do not need to perform an extra AJAX call, since you already have all data available in theGeoJson variable. You simply need to refactor / restyle it properly.

    It is a good approach to break your problem in small steps indeed.

    Good luck!

    PS: that being said, ES7 provides async and await functionalities that will emulate a sequential execution for asynchronous functions. But to be able to use those, you need latest browser versions or transpilation, which is probably more work to learn and configure as of today for a beginner than understanding how to work with async JS.