Marco
Marco

Reputation: 181

How to add variying markers to a map when accessing external JSON data asynchronously

I have the following markercollection.json file with several geographical objects in an array called features.

{
    "features":
    [
        {
            "name": "Point A",
            "address": {
                "street": "228 Park Ave S",
                "zipcode": "10003",
                "city": "New York"
            },
            "description": "Sample description for point A",
            "iconFile": "icon-marker-iconsample-a.png",
            "filterPrimary": "hotel",
            "year": "2014"
        },
        {
            "name": "Point B",
            "address": {
                "street": "542 W. 27th Street",
                "zipcode": "10001",
                "city": "New York"
            },
            "description": "Sample description for point B",
            "iconFile": "icon-marker-iconsample-b.png",
            "filterPrimary": "pub",
            "year": "2015"
        }
    ]
}

I am using the leaflet JavaScript library to put corresponding markers on a map. Since I do not have the longitudes and latitudes I have pass the object's address to a geocoding service. For this I am currently using mapbox, but I also tried with OSM nominatim. Both work as expected.

The mapbox geocoding API expects a callback function to be passed to do something with the resulting data (i.e. longitude and latitude).

So far I have worked out how to pass the geo-objects to the geocoder and have my addMarkers callback function add the corresponding markers to my map. In addition to this I would also like to have my markers have different icons depending on the iconFile value in the original JSON file.

I haven't been able to figure out where to build and subsequently add my customized markers. I tried building the marker icon for every loop but that leaves me with the same icon for all markers (see my screenshot).

Result of console.log variable value when simply assigning the icon file string value within the loop and accessing it within the callback function

I assume this is because the loop has finished before my addMarkers function is triggered, so the value of sIconFile is that of the last loop iteration.

Do I have to chain another callback, and if so, what's the best way to do this?

        // Pass Mapbox access token and set initial map view
        L.mapbox.accessToken = ACCESS_TOKEN;
        var myMap = L.mapbox.map('map','mapbox.streets').setView([40.730610, -73.935242], 13);

        // 
        var promise = $.getJSON('data/markercollection.json')
            .then(function(data) {

                /**
                 * Iterate through fetched data and do something...
                 */
                for (var i = 0; i < data.features.length; i++) {

                    // Build address string for passing to geocoder
                    var sAddress = data.features[i].address.street + ', ' + data.features[i].address.zipcode + ', ' + data.features[i].address.city;

                    // Icon file string variable
                    var sIconFile = data.features[i].iconFile;

                    /**
                     * Query Mapbox geocoder with address string
                     * and pass reply to addMakers function as per
                     * mapbox geocoder API
                     */
                    L.mapbox.geocoder('mapbox.places').query(sAddress, addMarkers);

                    // Add markers to geocoded latitudes and longitudes to map 
                    function addMarkers(err, geocoderCallbackData) {
                        console.log(sIconFile); // accessing the variable here does not work :(
                        L.marker(geocoderCallbackData.latlng).addTo(myMap);
                    }
                }

            // Basic error-handling
            }, function() {
                console.log('An error has occured!');
        });

Upvotes: 0

Views: 344

Answers (1)

Roamer-1888
Roamer-1888

Reputation: 19288

By looping through data.features with forEach(fn) instead of a for(...) loop, you will benefit from the local scope afforded by fn.

Therefore :

data.features.forEach(function(feature) {
    var sAddress = feature.address.street + ', ' + feature.address.zipcode + ', ' + feature.address.city;
    var sIconFile = feature.iconFile;
    L.mapbox.geocoder('mapbox.places').query(sAddress, addMarkers);
    function addMarkers(err, geocoderCallbackData) {
        console.log(sIconFile);
        L.marker(geocoderCallbackData.latlng).addTo(myMap);
    }
});

Upvotes: 3

Related Questions