Search code examples
javascriptgoogle-maps-api-3google-geocoder

Javascript Scope & Geocode


I've been searching here all day for help with what is likely, just a basic noob javascript mistake.

I'm trying to get the lat/long from an address using google's geocoder glass. However, I can't seem to assign it to a global or even derive it from the object's property (which I would prefer). Basically all I need at this point to to just get the location from the geocoder and everything else should fall in place. See code:

<script src="https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=true&libraries=places"></script>

<script>
    var geocoder = new google.maps.Geocoder();
    var ListingAddress = '1600 Pennsylvania Ave NW  Washington, DC 20500';
    var map;
    var infowindow;

    //var ListingLoc = new google.maps.LatLng(-33.8665433, 151.1956316);
    var ListingLatLong;
    var ListingLoc;

    function initialize() {



        geocoder.geocode({
                address: ListingAddress
                },
                function(results){
                    ListingLatLong = results[0].geometry.location;
                    ListingLoc = new google.maps.LatLng(ListingLatLong);
                });

        map = new google.maps.Map(document.getElementById('map_canvas'), {
            mapTypeId: google.maps.MapTypeId.ROADMAP,
            center: ListingLoc,
            zoom: 15
        });

        var request = {
            location: ListingLoc,
            radius: 500,
            types: ['school']
        };
        infowindow = new google.maps.InfoWindow();
        var service = new google.maps.places.PlacesService(map);
        service.nearbySearch(request, callback);
    }

    function callback(results, status) {
        if (status == google.maps.places.PlacesServiceStatus.OK) {
            for (var i = 0; i < results.length; i++) {
                createMarker(results[i]);
            }
        }
    }

    function createMarker(place) {
        var placeLoc = place.geometry.location;
        var marker = new google.maps.Marker({
            map: map,
            position: place.geometry.location
        });

        google.maps.event.addListener(marker, 'click', function() {
            infowindow.setContent(place.name);
            infowindow.open(map, this);
        });
    }
    google.maps.event.addDomListener(window, 'load', initialize);
</script>

AND the markup

<div id="map_canvas" style="width:600px;height:400px;border:none;"></div>

Solution

  • The second argument of geocoder.geocode is a callback function that executes asynchronously. That means that the function will run sometime after the rest of your code. Thus,ListingLatLong is not assigned a value until after you've tried to use it.

    Asynchronous execution is the norm for performing network requests in JavaScript. Instead of causing your code to hang by waiting a long time for a network round-trip, you simply dispatch a request and define a listener function to fire when the request completes some time later. That's what is happening here: the function argument to geocoder.geocode is a listener function that fires once the data arrives from Google's geocoding server. geocoder.geocode doesn't run that function, exactly -- it just says, "Okay, JavaScript, here's a function you should run whenever my request completes."

    To solve this problem, simply move any code that needs to use the values of results (and/or ListingLatLong) inside the callback function:

        geocoder.geocode({
            address: ListingAddress
            },
            function(results){
                ListingLatLong = results[0].geometry.location;
                ListingLoc = new google.maps.LatLng(ListingLatLong);
    
                // ** note this is inside the callback now **
                map = new google.maps.Map(document.getElementById('map_canvas'), {
                    mapTypeId: google.maps.MapTypeId.ROADMAP,
                    center: ListingLoc,
                    zoom: 15
                });
    
                // add the rest of your code here, too
                // ...
            });
    

    (Side note: you should use uppercase variable names only for constructors (e.g., var Person = function(name) { this.name = name; }) and lowercase variable names for instances (e.g., someGuy = new Person("Bob");). When I see the name ListingLatLong, I expect it to be a constructor, but it's not, so I'd suggest using listingLatLong instead.)