OdeToCode IC Logo

Geolocation, Geocoding, and jQuery Promises

Monday, June 18, 2012

If you want to use the customer’s hardware to find their exact address, one approach is to combine the HTML 5 Geolocation APIs with a Geocoding web service (like Google).

For Google, you can still get in without an API key (for a limited number of calls) using the Google Maps JavaScript library (just reference http://maps.google.com/maps/api/js in a script tag).

With the library in place, the code is straightforward (particularly the following code, which doesn’t have any error handling, but is a good skeleton of the calls you’ll need to make).

(function () {

    var getPosition = function (options) {
        navigator.geolocation.getCurrentPosition(
            lookupCountry,
            null,
            options);
    };

    var lookupCountry = function (position) {
        console.log(position);
        var latlng = new google.maps.LatLng(
                            position.coords.latitude,
                            position.coords.longitude);
        
        var geoCoder = new google.maps.Geocoder();
        geoCoder.geocode({ location: latlng }, displayResults);
    };

    var displayResults = function (results, status) {
        // here you can look through results ...
        $("body").append("<div>").text(results[0].formatted_address);      
    };

    $(function () {
        getPosition();
    });

} ());

 

Making Promises

Adding some jQuery deferred objects makes the code a little longer, but also a little more robust, as the individual pieces of work are no longer responsible for knowing what to do next and we can invert control of the execution flow. In other words, if you return promises from getPosition and lookupCountry:

var getPosition = function (options) {
    var deferred = $.Deferred();

    navigator.geolocation.getCurrentPosition(
        deferred.resolve,
        deferred.reject,
        options);

    return deferred.promise();
};

var lookupCountry = function (position) {
    var deferred = $.Deferred();

    var latlng = new google.maps.LatLng(
                        position.coords.latitude,
                        position.coords.longitude);
    var geoCoder = new google.maps.Geocoder();
    geoCoder.geocode({ location: latlng }, deferred.resolve);

    return deferred.promise();
};

Then the control logic reads pretty well:

$(function () {
    $.when(getPosition())
     .pipe(lookupCountry)
     .then(displayResults);
});

Note that pipe is different than then because pipe gives back a new promise.

Try it out on jsFiddle.