Search code examples
javascriptweb-servicesgoogle-mapsgoogle-geocoder

List a group of addresses obtained with the Google Maps Geocoder API


I can't find a way to properly list a group of addresses obtained from the Google Maps Geocoder API.

What I want to achieve:

  1. Read an object containing longitude and latitude info for a bunch of stores (in production this will come from a WS, right now I'm using a hardcoded object named responseObj)
  2. Use the Geocoder API to obtain full addresses for each store (the WS only provides lat & lon values)
  3. Display a list of stores, using information from the responseObj object (the name of the store, distance to current location; both already provided by the WS) as well as the full addresses provided by the Geocoder

My code so far (JS):



    /*
    I'm simulating the response I would obtain from a WS inside this object:
    */
    var responseObj = [{
        "storeName": "Store Number One",
        "lat": "XX.XXXX",
        "lon": "-XX.XXXX",
        "kilometers": 0.10
    }, {
        "storeName": "Two: The Store",
        "lat": "XX.XXXX",
        "lon": "-XX.XXXX",
        "kilometers": 0.50
    }, {
        "storeName": "Three Market",
        "lat": "XX.XXXX",
        "lon": "-XX.XXXX",
        "kilometers": 0.60
    }, {
        "storeName": "Four's",
        "lat": "XX.XXXX",
        "lon": "-XX.XXXX",
        "kilometers": 0.85
    }]

    /*
    /* Getting a list of stores and displaying addresses
    */
    function listStores(stores) {

        for (var key in stores) {
            if (stores.hasOwnProperty(key)) {
                var storeLat = stores[key].lat;
                var storeLon = stores[key].lon;

                var geocoder = new google.maps.Geocoder();
                var storeLatLong = new google.maps.LatLng(storeLat, storeLon);
                var formattedAddress;

                geocoder.geocode({
                    "latLng": storeLatLong
                }, function(results, status) {
                    if (status == google.maps.GeocoderStatus.OK) {
                        if (results) {
                            formattedAddress = results[0].formatted_address;
                            // For debugging:
                            //console.log(results[0].formatted_address);
                        }
                    } else {
                        formattedAddress = "Address unavailable.";
                        // For debugging:
                        // console.log("Address unavailable.");
                    }
                });

                var dataListItem = document.createElement("li");
                dataListItem.innerHTML = "" + stores[key].tienda + "" + " , " + stores[key].kilometers.toFixed(2) + " km away. 
Address: " + formattedAddress; document.getElementById('stores_list').appendChild(dataListItem); }; } } listStores(responseObj);

Consider the most basic markup structure:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>Stores List</title>
</head>

<body>    
    <h1>Stores:</h1>
    <ul id="stores_list">
    </ul>
</body>

</html>

My problem

I can't manage to properly append the address returned by the geocoder into the list items with the rest of the store info. I'm pretty sure I'm missing something about the asynchronous behaviour of the geocoder, but I'm quite new to JS and can't figure out what.

Please help me understand how to properly display everything as mentioned above.


Solution

  • Well, is far as i can see, the problem is that the google geocoder function is asynchronous, to solve this i usually build a recursive function just like this:

    //I'm simulating the response I would obtain from a WS inside this object:
        var responseObj = [{
            "storeName": "Store Number One",
            "lat": "XX.XXXX",
            "lon": "-XX.XXXX",
            "kilometers": 0.10
        }, {
            "storeName": "Two: The Store",
            "lat": "XX.XXXX",
            "lon": "-XX.XXXX",
            "kilometers": 0.50
        }, {
            "storeName": "Three Market",
            "lat": "XX.XXXX",
            "lon": "-XX.XXXX",
            "kilometers": 0.60
        }, {
            "storeName": "Four's",
            "lat": "XX.XXXX",
            "lon": "-XX.XXXX",
            "kilometers": 0.85
        }]
    
        /*
    /* Getting a list of stores and displaying addresses
    */
        function listStores(stores, index) {
    
            // Test for not going on infinite loop
            if (index == stores.length) {
                return;
            }
    
            var storeLat = stores[index].lat;
            var storeLon = stores[index].lon;
            var geocoder = new google.maps.Geocoder();
            var storeLatLong = new google.maps.LatLng(storeLat, storeLon);
            var formattedAddress;
    
            geocoder.geocode({
                "latLng": storeLatLong
            }, function(results, status) {
                if (status == google.maps.GeocoderStatus.OK) {
                    if (results) {
                        formattedAddress = results[0].formatted_address;
                        // For debugging:
                        //console.log(results[0].formatted_address);
    
                        var dataListItem = document.createElement("li");
                        dataListItem.innerHTML = "" + stores[index].tienda + "" + " , " + stores[index].kilometers.toFixed(2) + " km away. Address: " + formattedAddress;
                        document.getElementById('stores_list').appendChild(dataListItem);
    
                        // after a success response call it gain with new intex value
                        listStores(stores, (index + 1));
                    }
    
    
                } else {
                    formattedAddress = "Address unavailable.";
                    // For debugging:
                    // console.log("Address unavailable.");
                }
            });
    
        }
    
    
        listStores(responseObj);
    

    I didn't test id, so give it a try.

    Best regards.