Sponsored by

  • Intel
  • HP

HTML5How to

Getting started with HTML5 geolocation

Getting started with HTML5 geolocation

In an exclusive excerpt from The HTML5 Cookbook Christopher Deutsch and Marc Grabanski explain how to access the W3C Geolocation API and what you can do with this data using Google Maps and other third-party libraries like SimpleGeo.

  • Knowledge needed: Intermedia JavaScript, basic working web development knowledge
  • Requires: JavaScript, jQuery, HTML, CSS, signing up for some APIs
  • Project Time: 6-8 hours to put together examples; longer to dissect, learn

This excerpt is Chapter 8 of the HTML5 Cookbook by Christopher Schmitt and Kyle Simpson.

The W3C Geolocation API allows for scripted access to geographical location information associated with a device’s browser. In this article we’ll first cover how to access this API and then dive into some examples of what you can do with this data using Google Maps and other third-party libraries like SimpleGeo.

All of the examples in this tutorial use jQuery, which can easily be included using a script tag in one of two ways. There is no need to download the jquery.js file; your web page can use the version hosted on Google’s Content Delivery Network (CDN):

t src="//ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.js"></script>

And, just in case a call to the file across the internet isn’t available, a fallback to a local copy of the jQuery file can come right afterward in a second script element that checks for the previous copy’s successful inclusion:

<scrip<script src="//ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.js"></script>
<script>window.jQuery || document.write("<script
    src='js/libs/jquery-1.6.4.min.js'>\x3C/script>")</script>

Advertisement

Getting basic geolocation data

Problem

You want to find the location of the user’s internet device.

Solution

Use the new HTML5 Geolocation API to get the user’s location when she clicks a button and output the values to the web page, as shown below.

The latitude and longitude coordinates determined natively through the browser
The latitude and longitude coordinates determined natively through the browser

Start by adding an input button to the page:

<input type="button" id="go" value="Click Me To View Your Location" />

Next, add the following JavaScript to handle the button’s click event, access the Geolocation API, and output the results:

<script>
$(document).ready(function () {
    // wire up button click
    $('#go').click(function () {
        // test for presence of geolocation
        if (navigator && navigator.geolocation) {
            navigator.geolocation.getCurrentPosition(geo_success, geo_error);
        } else {
            error('Geolocation is not supported.');
        }
    });
});

function geo_success(position) {
    printLatLong(position.coords.latitude, position.coords.longitude);
}

// The PositionError object returned contains the following attributes:
// code: a numeric response code
// PERMISSION_DENIED = 1
// POSITION_UNAVAILABLE = 2
// TIMEOUT = 3
// message: Primarily for debugging. It's recommended not to show this error
// to users.
function geo_error(err) {
    if (err.code == 1) {
        error('The user denied the request for location information.')
    } else if (err.code == 2) {
        error('Your location information is unavailable.')
    } else if (err.code == 3) {
        error('The request to get your location timed out.')
    } else {
        error('An unknown error occurred while requesting your location.')
    }
}

// output lat and long
function printLatLong(lat, long) {
    $('body').append('<p>Lat: ' + lat + '</p>');
    $('body').append('<p>Long: ' + long + '</p>');
}

function error(msg) {
    alert(msg);
}
</script>

Discussion

The navigator object gives us access to the new geolocation object. The geolocation object has the following methods:

  • getCurrentPosition() returns the user’s current position.
  • watchPosition() returns the user’s current position, but also continues to monitor the position and invoke the appropriate callback every time the position changes.
  • clearWatch() ends the watchPosition() method’s monitoring of the current position.

When determining the location of the internet device, first check that the user’s browser supports the Geolocation feature natively. If it does, call the getCurrentPosition() method:

if (navigator && navigator.geolocation) {
    navigator.geolocation.getCurrentPosition(geo_success, geo_error);
} else {
    error('Geolocation is not supported.');
}

Since this method executes asynchronously, pass it two callback functions: geo_success and geo_error.The error callback is passed a position error object that contains a code and a message property. The code can be one of the following:

  1. Unknown
  2. Permission Denied
  3. Position Unavailable
  4. Timeout

The success callback is passed a position object that contains a coordinates object and a timestamp. The coordinates object contains the following:

  • latitude, which is specified in decimal degrees
  • longitude, which is specified in decimal degrees
  • altitude, which is specified in meters above the ellipsoid
  • accuracy, which is specified in meters
  • altitudeAccuracy, which is specified in meters
  • heading, which is the direction of travel specified in degrees
  • speed, which is specified in meters per second

Of those seven, only three are guaranteed to be there: latitude, longitude, and accuracy.
For the solution, take the latitude and longitude and append them to the body of the web page using jQuery:

function printLatLong(lat, long) {
    $('body').append('<p>Lat: ' + lat + '</p>');
    $('body').append('<p>Long: ' + long + '</p>');
}

See also

The W3C Geolocation specification at dev.w3.org/geo/api/spec-source.html.

Getting basic geolocation data with a fallback

Problem

You want to determine a user’s internet location when that user’s browser does not support the HTML5 Geolocation API natively.

Solution

Perform an IP-to-location lookup as a fallback. It’s certainly not as accurate as latitude and longitude coordinates, but it’s far better than not having any location data at all.

Google versus MaxMind

Google offers the google.loader.ClientLocation object in its Google Maps API v3 library, but it does not work for many US IP addresses.

The MaxMind GeoIP JavaScript Web Service seems more accurate and up-to-date. Also, it’s free as long as you link back to the www.maxmind.com website. Alternatively, MaxMind offers a JavaScript attribution-free license that can be purchased for $250/year.

Coding the solution

Modify our previous example to use MaxMind as a fallback. Start by adding the JavaScript library to the page:

<script src="http://j.maxmind.com/app/geoip.js"></script>

Then add the MaxMind fallback:

$(document).ready(function () {
    // wire up button click
    $('#go').click(function () {
        // test for presence of geolocation
        if (navigator && navigator.geolocation) {
            // make the request for the user's position
            navigator.geolocation.getCurrentPosition(geo_success, geo_error);
        } else {
            // use MaxMind IP to location API fallback
            printLatLong(geoip_latitude(), geoip_longitude(), true);
        }
    });
});

// output lat and long
function printLatLong(latitude, longitude, isMaxMind) {
    $('body').append('<p>Lat: ' + latitude + '</p>');
    $('body').append('<p>Long: ' + longitude + '</p>');
    // if we used MaxMind for location, add attribution link
    if (isMaxMind) {
        $('body').append('<p><a href="http://www.maxmind.com" target="_blank">IP
            to Location Service Provided by MaxMind</a></p>');
    }
}

function geo_error(err) {
    // instead of displaying an error, fall back to MaxMind IP to location library
    printLatLong(geoip_latitude(), geoip_longitude(), true);
}

When calling printLatLong() using MaxMind, pass in an extra true parameter.

Discussion

Instead of showing an error if navigator or navigator.geolocation is undefined, use the geoip_latitude() and geoip_longitude() functions that the MaxMind JavaScript library provides to retrieve the user’s latitude and longitude.

If you look at the source of the MaxMind geoip.js file, you’ll see that it has already translated your IP address into location data. MaxMind creates a dynamic JavaScript file by reading the IP address that made the HTTP request, doing the IP-to-location translation on the server side, and then outputting the results.

In addition to latitude and longitude, the location data shown in this table is available.

Location data examples from MaxMind geoip.js
Location data examples from MaxMind geoip.js

The free version of MaxMind requires attribution in the form of a link back to the website, so the isMaxMind parameter has been added to the printLatLong() function to indicate that MaxMind was used to get the location:

function printLatLong(latitude, longitude, isMaxMind) {
         $('body').append('<p>Lat: ' + latitude + '</p>');
         $('body').append('<p>Long: ' + longitude + '</p>');
         // if we used MaxMind for location, add attribution link
         if (isMaxMind) {
             $('body').append('<p><a href="http://www.maxmind.com" target="_blank">IP to Location Service Provided by MaxMind</a></p>');
         }
}

Another scenario to be mindful of is if the user denies your request for location information or something else goes wrong. To handle this eventuality, set up the geo_error handler to also fall back to using IP-to-location translation, as shown in the next recipe.

Because we’ve added MaxMind as a fallback, this solution is able to handle a larger percentage of browsers and devices without having to rely on native geolocation support in the browser.

See also

MaxMind provides free/open source geolocation solutions for city, country, and IP lookups at www.maxmind.com/app/ip-location.

Reverse geocoding an address with latitude and longitude

Problem

You want to convert latitude and longitude coordinates into a human-friendly address.

Solution

Use the Google Maps JavaScript API to turn latitude and longitude into an address, as shown here:

Determining a user’s address based on latitude and longitude
Determining a user’s address based on latitude and longitude

The process of turning geographic data like a street address and zip code into geographic coordinates such as latitude and longitude is called geocoding (we’ll get to this in the next recipe). Doing the opposite, turning coordinates into an address, is called reverse geocoding.

Begin by adding the needed scripts to your web page:

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.js">
</script>
<script src="http://j.maxmind.com/app/geoip.js"></script>
<script src="http://maps.google.com/maps/api/js?sensor=false"></script>

Add a button to trigger getting the user’s coordinates and looking up the address:

<input type="button" id="go" value="Click Me To Find Your Address" />

and then add the JavaScript to handle the button click and getCurrentPosition() success callback:

$(document).ready(function () {

    // wire up button click
    $('#go').click(function () {
        // test for presence of geolocation
        if (navigator && navigator.geolocation) {
            // make the request for the user's position
            navigator.geolocation.getCurrentPosition(geo_success, geo_error);
        } else {
            // use MaxMind IP to location API fallback
            printAddress(geoip_latitude(), geoip_longitude(), true);
        }
    });


function geo_success(position) {
    printAddress(position.coords.latitude, position.coords.longitude);
}

function geo_error(err) {
    // instead of displaying an error, fall back to MaxMind IP to location library
    printAddress(geoip_latitude(), geoip_longitude(), true);
}

// use Google Maps API to reverse geocode our location
function printAddress(latitude, longitude, isMaxMind) {
    // set up the Geocoder object
    var geocoder = new google.maps.Geocoder();

    // turn coordinates into an object
    var yourLocation = new google.maps.LatLng(latitude, longitude);


    // find out info about our location
    geocoder.geocode({ 'latLng': yourLocation }, function (results, status) {
        if (status == google.maps.GeocoderStatus.OK) {
            if (results[0]) {
                $('body').append('<p>Your Address:<br />' +
                    results[0].formatted_address + '</p>');
            } else {
                error('Google did not return any results.');
            }
        } else {
            error("Reverse Geocoding failed due to: " + status);
        }
    });

    // if we used MaxMind for location, add attribution link
    if (isMaxMind) {
        $('body').append('<p><a href="http://www.maxmind.com" target="_blank">IP
            to Location Service Provided by MaxMind</a></p>');
    }
}

function error(msg) {
    alert(msg);
}

Discussion

Get the coordinates from getCurrentPosition() and pass them to a printAddress() function, which uses the Google Maps API to do the reverse geocoding.

The printAddress() function begins by creating a new Google Geocoder object. The Geocoder object gives us access to the geocode() method, which can take in a variety of options and return information based on them.

In our case, we’re using the google.maps.LatLng() method to create a new Google LatLng object that is passed into geocode() in order to get the address. The geocode() method is asynchronous, just like getCurrentPosition(), so we define an inline Java- Script function to handle the callback.

The callback’s response contains two parameters, one for the results and the other for the status code. If the status is OK, then it’s safe to parse the array of GeocoderResults objects stored in the results variable. The results variable is an array since Geocoder may return more than one entry.

Next, check for a GeocoderResults object in the first position of the array and, if it exists, append the formatted_address property to the web page’s body.

See also

For more information about reverse geocoding, see code.google.com/apis/maps/documentation/javascript/services.html#ReverseGeocoding.

Converting an address into latitude and longitude

Problem

You want to turn an address into latitude and longitude coordinates.

Solution

Use the Google Maps JavaScript API V3 to turn an address into latitude and longitude, as shown below. This is called geocoding.

To begin, include 1.6.4 and the Google Maps JavaScript API V3 in the web page:

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.js">
</script>
<script src="http://maps.google.com/maps/api/js?sensor=false"></script>

Determining latitude and longitude based on a human-friendly address
Determining latitude and longitude based on a human-friendly address

The HTML5 Geolocation API can only return the user’s location in coordinates, so let the user enter his address via an input text field:

<div>
    <label for="address">Enter an Address:</label>
    <input type="text" id="address" />
</div>
<div>
    <input type="button" id="go" value="Click Me To Find Latitude And Longitude"/>
</div>

The JavaScript below handles the button click, reading the input from the user, and calls the Google API to geocode the address:

$(document).ready(function () {

    // wire up button click
    $('#go').click(function () {
        // get the address the user entered
        var address = $('#address').val();
        if (address) {
            // use Google Maps API to geocode the address
            // set up the Geocoder object
            var geocoder = new google.maps.Geocoder();
            // return the coordinates
            geocoder.geocode({ 'address': address }, function (results, status) {
                if (status == google.maps.GeocoderStatus.OK) {
                    if (results[0]) {
                        // print results
                        printLatLong(results[0].geometry.location.lat(),
                            results[0].geometry.location.lng());
                    } else {
                        error('Google did not return any results.');
                    }

                } else {
                    error("Reverse Geocoding failed due to: " + status);
                }
            });
        }
        else {
            error('Please enter an address');
        }
    });

});

// output lat and long
function printLatLong(lat, long) {
    $('body').append('<p>Lat: ' + lat + '</p>');
    $('body').append('<p>Long: ' + long + '</p>');
}

function error(msg) {
    alert(msg);
}

Discussion

When the user clicks the button, use jQuery to read the value and validate that it’s not blank. Next, create an instance of the Geocoder object. In order to do so, call the geocode() method, but pass an address option instead of latitude and longitude:

// set up the Geocoder object
var geocoder = new google.maps.Geocoder();
// return the coordinates
geocoder.geocode({ 'address': address }, function (results, status) {
...

Then access the geometry property of the GeocoderResults object. The geometry property contains a location property that can then be used to call the lat and lng methods to get our address’s coordinates, which are then appended to the web page body in our printLatLong() function:

// print results
printLatLong(results[0].geometry.location.lat(), results[0].geometry.location.lng());

See also

For more information about geocoding, see code.google.com/apis/maps/documen tation/geocoding/.

Getting directions from the current location

Problem

You want to get directions from the user’s current location to a specific address.

Solution

Use the Google Maps API to display the route the same way the Google Maps website would, and give the user the option to output the distance in miles or kilometers, as shown below.

Determining the directions from a location
Determining the directions from a location

Use the jQuery and Google Maps JavaScript API V3 libraries:

<script src="http://maps.google.com/maps/api/js?sensor=false"></script>
<script src="http://j.maxmind.com/app/geoip.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.js">
</script>

The HTML is similar to the geocoding example in the previous recipe, with the addition of a drop-down box to select miles or kilometers. Also, instead of appending our results to the page body, we’ll add a div to hold the calculated distance and a div to hold the Google Map:

<div class="field">
    <label for="address">Enter a Destination Address:</label>
    <input type="text" id="address" />
</div>

<div class="field">
    <label for="units">Units:</label>
    <select id="units">
        <option value="IMPERIAL">Miles</option>
        <option value="METRIC">Kilometers</option>
    </select>
</div>

<div>
    <input type="button" id="go" value="Get Directions" />
</div>

<div id="distance"></div>

<div id="map"></div>

We now need some JavaScript to do the following:

  • Display a map of the United States on page load
  • Handle the button click
  • Get the user’s current location
  • Read the address input
  • Pass the current location and address to the Google API to get the driving distance between the two locations
  • Update the Google Map with the suggested driving route

The code looks like this:

var directionRenderer;
var directionsService = new google.maps.DirectionsService();
var map;

$(document).ready(function () {

    // Set up map starting point for Google Maps.
    // Set initial coords to latitude −92 and longitude 32, which is somewhere
    // around Kansas City in the center of the US, and then set the zoom to 4
    // so the entire US is visible and centered.
    var kansas = new google.maps.LatLng(32, −92);
    var myOptions = {
        zoom: 4,
        mapTypeId: google.maps.MapTypeId.ROADMAP,
        center: kansas
    }
    map = new google.maps.Map(document.getElementById("map"), myOptions);
    directionsRenderer = new google.maps.DirectionsRenderer();
    directionsRenderer.setMap(map);

    // wire up button click
    $('#go').click(function () {
        // Use our new getLatLng with fallback and define an inline function to
        // handle the callback.
        getLatLng(function (latitude, longitude, isMaxMind) {
            // set the starting point
            var start = new google.maps.LatLng(latitude, longitude);

            // get the address the user entered
            var address = $('#address').val();
            if (address) {
                // set end point
                var end = $('#address').val();

                // set the request options
                var request = {
                    origin: start,
                    destination: end,
                    travelMode: google.maps.DirectionsTravelMode.DRIVING
                };

                // make the directions request
                directionsService.route(request, function (result, status) {
                    if (status == google.maps.DirectionsStatus.OK) {

                        // Display the directions using Google's Directions
                        // Renderer.
                        directionsRenderer.setDirections(result);

                        // output total distance separately
                        var distance = getTotalDistance(result);
                        // output either miles or km
                        var units = $('#units').val();
                        if (units == 'IMPERIAL') {
                            $('#distance').html('Total Distance: <strong>' +
                                metersToMiles(distance) + '</strong> miles');
                        } else {
                            $('#distance').html('Total Distance: <strong>' +
                                metersToKilometers(distance) + '</strong> km');
                        }

                    } else {
                        error("Directions failed due to: " + status);
                    }
                });

            }
            else {
                error('Please enter an address');
            }

            // if we used MaxMind for location, add attribution link
            if (isMaxMind) {
                $('body').append('<p><a href="http://www.maxmind.com"
                    target="_blank">IP to Location Service Provided by
                    MaxMind</a></p>');
            }
        });
    });

});

function getLatLng(callback) {
    // test for presence of geolocation
    if (navigator && navigator.geolocation) {
        // make the request for the user's position
        navigator.geolocation.getCurrentPosition(function (position) {
            // success handler
            callback(position.coords.latitude, position.coords.longitude);
        },
        function (err) {
            // handle the error by passing the callback the location from MaxMind
            callback(geoip_latitude(), geoip_longitude(), true);
        });
    } else {
        // geolocation not available, so pass the callback the location from
        // MaxMind
        callback(geoip_latitude(), geoip_longitude(), true);
    }
}

// return total distance in meters
function getTotalDistance(result) {
    var meters = 0;
    var route = result.routes[0];
    for (ii = 0; ii < route.legs.length; ii++) {
        // Google stores distance value in meters
        meters += route.legs[ii].distance.value;
    }
    return meters;
}

function metersToKilometers(meters) {
    return Math.round(meters / 1000);
}

function metersToMiles(meters) {
    // 1 mile = 1609.344 meters
    return Math.round(meters / 1609.344);
}

function error(msg) {
    alert(msg);
}

Discussion

To build out the solution, start by defining three global variables that are used to communicate with the Google API and to update our map div.

When the document loads, set the map of the US to be displayed. The Google Map object represents a map on your web page (you can have more than one).

Create a Map object by calling new google.maps.Map(document.getElementById("map"), myOptions), passing in the HTML element where you want to display the map and a Map options object.

There are many options that can be set, but the three used for this solution are zoom, mapTypeId, and center. The options are fairly descriptive as to their purpose. Set zoom to 4 to allow the user to see the entire US. For the mapTypeId, use ROADMAP, which displays the normal, default 2D tiles of Google Maps. The other options are SATELLITE, HYBRID, and TERRAIN. The center option indicates the location that is displayed in the center of the map.

The latitude and longitude of Kansas, which is a central location in the US, are hard- coded to create a LatLng object that can be used to set the center parameter. When the Map object is created using the new keyword it automatically updates our map div.

The next line, directionsRenderer = new google.maps.DirectionsRenderer();, creates a new DirectionsRenderer object that can automatically update Maps for us. The line directionsRenderer.setMap(map); doesn’t do anything yet, but it tells the user to enter an address and click the button.

In this example, refactored logic does a geolocation fallback in order to be a little more compact and reusable:

// Google Maps globals:
function getLatLng(callback) {
    // test for presence of geolocation
    if (navigator && navigator.geolocation) {
        // make the request for the user's position
        navigator.geolocation.getCurrentPosition(function (position) {
            // success handler
            callback(position.coords.latitude, position.coords.longitude);
        },
        function (err) {
            // handle the error by passing the callback the location from MaxMind
            callback(geoip_latitude(), geoip_longitude(), true);
        });
    } else {
        // geolocation not available, so pass the callback the location from
        // MaxMind
        callback(geoip_latitude(), geoip_longitude(), true);
    }
}

The getLatLng() function takes a single callback parameter that returns the latitude, longitude, and isMaxMind variables.

We check for the existence of navigator.geolocation just like we did before, but this time we define the navigator.geolocation callback handlers inline to call our common callback function. That returns either the results of getCurrentPosition() or the Max-Mind latitude and longitude.

For the button-click handler in the main example, we start by using the new get LatLng() function to collect the user’s current location, which then is used to create a new LatLng object that we store in the start variable.

Next, we collect the address and store the text as a string in the end variable. To get the directions, we use the DirectionsService object that was created and stored into the global variable directionsService. The route() method of the DirectionsService object takes a DirectionsRequest object parameter and a callback method. The DirectionsRequest object supports many options, but for this solution we only need to set the origin, destination, and travelMode options.

We could make an API request to geocode the address and get its coordinates, but the Google API handles that automatically in the next step.

The origin and destination options can be either strings like the end variable, or the LatLng values. We set the travelMode option to DRIVING (the other options are WALKING or BICYCLING).

The route() method executes asynchronously, so we define a callback function that is passed a DirectionsResult object and a status code. We check the status variable to make sure the route() method finished successfully and then pass the result object to the DirectionsRenderer object, which updates the map with a highlighted driving route between our start and end locations.

To give you an idea of what is contained in the result variable, we pass it to the getTotalDistance() function, which is responsible for totaling the distance of the driving route. The result object contains a routes property, which is an array of DirectionsRoute objects. Each route indicates a way to get from the start to the end location. Usually only one route is returned, unless you set the provideRouteAlternatives option to true.

Our getTotalDistance() function only looks at the first route. Each DirectionsRoute object contains multiple properties, but the property needed is legs, which is an array of DirectionsLeg objects that defines a single leg of the journey between the start and end locations.

If the route does not contain any waypoints, it only has a single leg. Since waypoints were not defined here, the results should have a single leg, but for good measure we loop through each leg anyway.

Like the route object, the leg object also contains multiple properties, but the only one we need to access is the distance property, which contains a DirectionsDistance object. The value property of the DirectionsDistance object gives the total distance of the leg in meters. The loop adds up the distance of each leg and returns the total in meters.

Finally, we check the value of the units drop-down to find out if the user wanted the total distance in miles or kilometers. Then we call one of our helper functions metersToKilometers() or metersToMiles() to convert meters into kilometers or miles, re- spectively, and output the value to the distance div element.

See also

For more about getting directions from the Google Maps API, see code.google .com/apis/maps/documentation/javascript/services.html#Directions.

Example: Starbucks to Starbucks

Problem

You want to get directions from the nearest Starbucks to the next closest Starbucks.

Solution

Use SimpleGeo’s Places API to find the closest Starbucks to the user’s current location and then, once that location is set, make a second API call to SimpleGeo to find the next closest Starbucks location. Then use the Google Maps API to give directions from the first Starbucks to the second Starbucks.

To begin, add the SimpleGeo API to the collection of JavaScript libraries:

<script src="http://maps.google.com/maps/api/js?sensor=false"></script>
<script src="http://j.maxmind.com/app/geoip.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.js">
</script>
<script src="http://cdn.simplegeo.com/js/1.2/simplegeo.places.jq.min.js">
</script>

SimpleGeo is free, but it does require you to sign up to get an API key (see simplegeo.com). Once you’ve signed up, you can find the API key by clicking the Tokens menu and then the JSONP Tokens submenu, as shown below.

SimpleGeo API key
SimpleGeo API key

Add your website domain(s) to the allowed domains list. This prevents other people from using your API key. Now copy the key and replace the placeholder at the top of our sample’s JavaScript:

// SimpleGeo globals:
var geoclient = new simplegeo.PlacesClient('REPLACE WITH YOUR API KEY');

// Google Maps globals:
var directionRenderer;
var directionsService = new google.maps.DirectionsService();
var map;

$(document).ready(function () {

    // Set up map starting point for Google Maps.
    // Set initial coords to latitude −92 and longitude 32, which is somewhere
    // around Kansas City in the center of the US, and then set the zoom to 4
    // so the entire US is visible and centered.
    var kansas = new google.maps.LatLng(32, −92);
    var myOptions = {
        zoom: 4,
        mapTypeId: google.maps.MapTypeId.ROADMAP,
        center: kansas
    }
    map = new google.maps.Map(document.getElementById("map"), myOptions);
    directionsRenderer = new google.maps.DirectionsRenderer();
    directionsRenderer.setMap(map);

    // wire up button click
    $('#go').click(function () {
        // Use our new getLatLng with fallback and define an inline function
        // to handle the callback.
        getLatLng(function (latitude, longitude, isMaxMind) {
            // use SimpleGeo to get closest Starbucks
            var query = "Starbucks";
            geoclient.search(latitude, longitude, { q: query, radius: 20,
                    num: 1 }, function (err, dataStart) {
                if (err) {
                    error(err);
                } else {
                    // We only asked for one result and SimpleGeo returns results
                    // based on distance so the closest is first, so make sure we
                    // got a result.
                    if (dataStart.features.length == 1) {
                        // save start coordinates and address
                        var startLat =
                            dataStart.features[0].geometry.coordinates[1];
                        var startLng =
                            dataStart.features[0].geometry.coordinates[0];
                        var startAddress =
                            dataStart.features[0].properties['address'];
                        // save in Google LatLng as well
                        var start = new google.maps.LatLng(startLat, startLng);

                        // look up the closest Starbucks to the one we just found
                        geoclient.search(startLat, startLng, { q: query, radius:
                                20, num: 2 }, function (err, dataEnd) {
                            if (err) {
                                error(err);
                            } else {
                                // This time we asked for two results; the first
                                // result should be the starting Starbucks,
                                // so this time access the second result.
                                if (dataEnd.features.length == 2) {
                                    // save end coordinates and address
                                    var endLat =
                                      dataEnd.features[1].geometry.coordinates[1];
                                    var endLng =
                                      dataEnd.features[1].geometry.coordinates[0];
                                    var endAddress =
                                      dataEnd.features[1].properties['address'];
                                    // save in Google LatLng as well
                                    var end = new google.maps.LatLng(endLat,
                                      endLng);

                                    // Now add directions from starting Starbucks
                                    // to ending one.
                                    // Set the request options:
                                    var request = {
                                        origin: start,
                                        destination: end,
                                        travelMode:
                                          google.maps.DirectionsTravelMode.DRIVING
                                    };

                                    // make the directions request
                                    directionsService.route(request, function
                                            (result, status) {
                                        if (status ==
                                            google.maps.DirectionsStatus.OK) {

                                            // Display the directions using
                                            // Google's Directions Renderer.
                                            directionsRenderer.
                                                setDirections(result);

                                            // output info separately
                                            $('#info').html('Closest Starbucks:
                                                <strong>' + startAddress
                                                + '</strong><br />' + 'Next
                                                Starbucks: <strong>' + endAddress
                                                + '</strong>');


                                        } else {
                                            error("Directions failed due to: " +
                                                status);
                                        }
                                    });
                                }
                                else {
                                    error('Could not find a Starbucks near ' +
                                        startAddress);
                                }
                            }
                        });

                    }
                    else {
                        error('Could not find a Starbucks near you.');
                    }
                }
            });

            // if we used MaxMind for location, add attribution link
            if (isMaxMind) {
                $('body').append('<p><a href="http://www.maxmind.com"
                    target="_blank">IP to Location Service Provided by
                    MaxMind</a></p>');
            }
        });
    });

});

function getLatLng(callback) {
    // test for presence of geolocation
    if (navigator && navigator.geolocation) {
        // make the request for the user's position
        navigator.geolocation.getCurrentPosition(function (position) {
            // success handler
            callback(position.coords.latitude, position.coords.longitude);
        },
        function (err) {
            // handle the error by passing the callback the location from MaxMind
            callback(geoip_latitude(), geoip_longitude(), true);
        });
    } else {
        // geolocation not available, so pass the callback the location from
        // MaxMind
        callback(geoip_latitude(), geoip_longitude(), true);
    }
}

function error(msg) {
    alert(msg);
}

Discussion

Other than the call to simplegeo.PlacesClient() to set up SimpleGeo, the code starts off the same as the previous example.

In the click handler, we get the user’s current location using the getLatLng() function and we use the resulting latitude and longitude to call the SimpleGeo geoclient.search() function to find the closest Starbucks to us. The geoclient.search() function takes in latitude and longitude parameters, our query options, and a callback function.

We set the query options to a search term (q param) of Starbucks within a 20-kilometer radius (radius param), and indicate that only one result (num param) is required.

The callback returns a features collection, which contains various information about each search result (including latitude and longitude). In the callback, we save the latitude, longitude, and address in variables as our starting location.

Then we make a second geoclient.search() call using the starting location as the reference point. This time it returns two results, since the first result is still the starting location. We store the second feature item’s latitude, longitude, and address in the ending location variables.

Now that the start and end locations are set, we can use the same Google Maps API call as in the last example to display the driving directions between the two locations. To finish off the example, we display the starting and ending addresses above the map.

See also

Create dynamic maps with datasets with a free JavaScript library at polymaps.org.

Log in to Creative Bloq with your preferred social network to comment

OR

Log in with your Creative Bloq account

site stat collection