December 2011
Volume 26 Number 12
Building HTML5 Applications - Integrating Geolocation into Web Applications
By Brandon Satrom | December 2011
If you own a smartphone, you’ve probably used an application or built-in functionality to get directions from where you are to where you want to go, or to find a nearby restaurant, hotel or gas station in some unfamiliar town. The technical term for this facility is geolocation, and it describes a capability whereby a device can use external location data (global positioning system [GPS], cell tower triangulation and Wi-Fi data) to find out where you are and provide that information to the application or service requesting this data. Your location is typically expressed using data such as latitude and longitude, heading, speed, direction, and the like. With that information, a device can provide features such as turn-by-turn navigation or the locations of nearby taco trucks that happen to be open at 2 a.m.
Geolocation is something we take for granted on our mobile devices, but its use is spreading quickly beyond native mobile applications. More and more laptops and mobile PCs are now fitted with GPS chips, and the explosion of the Web as a viable platform for mobile application development has stirred demand for geolocation services right within the browser.
Luckily for us, the W3C picked up on this trend early and created a specification that describes a standard way to use geolocation services from within Web applications. The spec, which you can find at bit.ly/3r737c, isn’t “officially” part of HTML5, but because I’m using that term in this series to describe a set of open Web technologies—as the W3C does—I couldn’t miss the opportunity to give you a brief tour of this exciting technology.
This month’s article will give you an overview of the Geolocation API and show you how you can get started using it, as well as how to handle the most common types of errors that can occur during a geolocation request. I’ll discuss how geolocation is implemented by desktop and mobile browsers, and wrap up with a brief look at how you can use a polyfilling library to provide basic geolocation support to users with older browsers.
Getting Started with Geolocation
According to the W3C specification, geolocation is an “… API that provides scripted access to geographical location information associated with the hosting device.” Because geolocation is a built-in API, all you need to get started is a browser that supports it. At the time of this writing, Internet Explorer 9 (including Internet Explorer 9 on Windows Phone 7.5, code-named “Mango”),as well as the current versions of Chrome, Firefox, Safari and Opera, all support geolocation, so the specification is not only broadly supported, but probably available to a large segment of your user base.
The Geolocation API lives in the global “navigator” object in JavaScript (window.navigator.geolocation). It provides two ways to work with location services: one-shot position requests and repeated position updates. For one-shot requests, use the getCurrentPosition method, like so:
navigator.geolocation.getCurrentPosition(function(position) {
var coordinates = position.coords;
console.log("Your latitude: " + coordinates.latitude + " and longitude:
" + coordinates.longitude + " (Accuracy of:
" + coordinates.accuracy + " meters)");
}, errorHandler, { maximumAge: 100, timeout: 6000, enableHighAccuracy: true});
The method getCurrentPosition accepts three parameters. The first is a callback function that fires when the browser successfully determines the current location of a device. The second is a callback function that fires if an error occurs (more on that in a moment). The third is an object literal for specifying geolocation options. The available options and their defaults are listed in Figure 1.
Figure 1 Geolocation Options
Option | Description | Values |
maximumAge | Length of time (in milliseconds) to cache the reported location. | 0 (default) to Infinity |
Timeout | Length of time (in milliseconds) to wait for the location service to respond. | 0 to Infinity (default) |
enableHighAccuracy | Whether the browser should attempt to use high-accuracy location services (for example, GPS), where supported by the browser and device. | true, false (default) |
The success handler provides a position object that holds important properties related to the user’s current location. The Geolocation spec defines several properties, but only three are implemented consistently across all browsers: latitude, longitude and accuracy. Other properties such as heading, direction and altitude are available for browsers to implement, and could be quite useful for mobile browsing scenarios in the future.
For repeated position requests, the geolocation object offers two public methods, watchPosition and clearWatch; watchPosition can be used like this:
var watchId = navigator.geolocation.watchPosition(function(position) {
var coordinates = position.coords;
console.log("Your latitude: " + coordinates.latitude + " and longitude:
" + coordinates.longitude + " (Accuracy of:
" + coordinates.accuracy + " meters)");
}, errorHandler, {maximumAge: 100, timeout: 6000, enableHighAccuracy: true});
The watchPostion method takes the same parameters as getCurrentPosition, with a success handler, error handler and options. The key difference is that watchPosition immediately returns an ID that can be used to identify the function in question. Because watchPosition is a repetitive function, the browser will call it each time it detects a change in the user’s location until the window is closed or the script explicitly cancels the watch. To cancel watchPosition, you simply call clearWatch with the ID returned by watchPosition, like so:
clearWatch(watchId);
Let’s look at using getCurrentPosition in a sample application. For this example, I’ll continue using the WebMatrix (https://aka.ms/webm) Bakery sample I used in my last article. The site is a place where users can order baked goods and have them delivered, but I want to extend this functionality to allow the user to specify a nearby brick-and-mortar location where they’ll pick up the order. What Best Buy has done for in-store pickup of electronics, I want to do for the bustling baked goods industry.
I started by using the modified Bakery sample application I decked out with HTML5 Forms features in my November article (msdn.microsoft.com/magazine/hh547102), and then added a feature that allows the user to select a nearby bakery for pickup by clicking a link on the form. I’m using the Bing Maps AJAX control (which you can download at msdn.microsoft.com/library/gg427610), and after adding the proper references and creating an HTML element to contain my map, I added a few divs to the page to represent the Pickup and Shipping sections of the form. Each div contains a link that allows the user to toggle between the two options. When the “Interested in picking your order up at one of our stores?” link is clicked, I execute a function in a JavaScript file called locateBakery.js. The key methods of that function are shown in Figure 2.
Figure 2 locateBakery.js
var mapDiv = document.getElementById("map");
var _map = new Microsoft.Maps.Map(mapDiv, { credentials: myCredentials });
function hideMap() {
$('#pickup').hide();
$('#map').hide();
$('#ship').show();
}
function displayError(msg) {
$('#error').val(msg);
}
function placeLocationOnMap(latitude, longitude) {
var location = new Microsoft.Maps.Location(latitude, longitude);
var bakeries = lookupNearbyBakeries(latitude, longitude);
_map.setView({ zoom: 12, center: location });
// Add a pushpin to the map representing the current location
var pin = new Microsoft.Maps.Pushpin(location);
_map.entities.push(pin);
for (var i=0;i<bakeries.length;i++) {
_map.entities.push(bakeries[i]);
}
}
function errorHandler(e) {
if (e === 1) { // PERMISSION_DENIED
hideMap();
} else if (e === 2) { //POSITION_UNAVAILABLE
displayError('Cannot find your location. Make sure your network
connection is active and click the link to try again.');
hideMap();
} else if (e === 3) { //TIMEOUT
displayError('Cannot find your location. Click the link to try again.');
hideMap();
}
}
function locate() {
navigator.geolocation.getCurrentPosition(function (position) {
var coordinates = position.coords;
placeLocationOnMap(coordinates.latitude, coordinates.longitude);
}, errorHandler);
}
The locate function calls navigator.geolocation.getCurrentPosition and if that call succeeds, provides the coordinates to the placeLocationOnMap function, which places the user’s location on a map along with a list of nearby bakeries (the bakery lookup logic was omitted for space). This allows the user to find a nearby bakery from which to pick up her order. The result is depicted in Figure 3.
Figure 3 Using Geolocation with the Bakery Application
Of course, geolocation is a network-dependent service, so what do you do when geolocation fails? Perhaps you noticed that for both the getCurrentPosition and watchPosition functions I specified a second parameter called errorHandler. According to the Geolocation specification, calls to getCurrentPosition and watchPosition should call both a success handler and an error handler. When calling the geolocation service, there are three possible errors:
- The user could reject the browser’s request to track location information
- The location request could timeout
- The location request could fail before the timeout expires
When adding geolocation support to your application, you should be sure to handle all three cases. An example of this can be found in the source in Figure 2, where I display an error message and redisplay the shipping information section when the geolocation call fails for any reason.
How Geolocation Works
Even though the Geolocation specification is quite prescriptive in defining the public API that each browser must provide to developers, it has nothing to say regarding exactly how geolocation should be implemented in a browser. The general guidance is that each browser should attempt to balance accuracy with power-consumption considerations, with the goal of providing applications with a reasonably accurate set of coordinates.
In a desktop or laptop scenario, most browsers use the same mechanism to provide geolocation data, though the background services they depend on differ. In the case of Internet Explorer 9 and later, the browser will collect your IP address or a list of nearby Wi-Fi hotspots and then pass that information to the Microsoft Location Service (the same service built-in location facilities on Windows Phone devices use) for a set of coordinates. Google Chrome and the Mozilla Firefox browser both also use IP and Wi-Fi hotspot data, but rely on the Google Location Service for the actual request. The location service method for finding a user’s location sacrifices some accuracy for speed (and low power consumption), and is quite useful as a backup, or when the device lacks a built-in GPS chip.
For mobile browsers, the situation is a bit more complex, and again is up to the browser in question. Because most modern smartphones have GPS chips, the browser is free to use the GPS location services provided by the mobile OS in an attempt to obtain more accurate coordinates. In the case of Internet Explorer 9 on Windows Phone 7.5 and Safari on iOS, that is exactly what those browsers do. The result is a more accurate location at the expense of additional power consumption, which is usually an acceptable trade-off in most mobile geolocation scenarios, such as turn-by-turn navigation. It’s important to note, however, that a mobile device browser isn’t guaranteed to use GPS-based geolocation simply because a chip is available. Because the specification leaves implementation up to the browser, the browser or underlying mobile OS service may choose to fall back to cell tower triangulation or IP lookup, or use a combination of cell triangulation and GPS. (Many mobile OSes already do this for native applications as a part of their built-in location services.)
For a Web application developer, this is great news, because it means that with geolocation, you can build cross-platform mobile applications that can rely on location facilities native to a device, just as native-application developers do.
Detecting and Polyfilling Geolocation Support
When adding geolocation support to your application, you need to consider what your approach will be for those users visiting your site with an unsupported browser. As I’ve discussed in the series already, the way you determine if geolocation is supported in a given browser is by performing manual feature detection (“if (!!navigator.geolocation)”) or using a library like Modernizr (modernizr.com). For users without geolocation support, there are two courses of action: gracefully degrade the application or polyfill support via an external library. For the first option, you can degrade your application by showing a form that lets users give you their location, which you then can use (via a service call) to determine the latitude and longitude of that user. I covered this scenario in my September article of this series, so I won’t repeat it here. You can view both that scenario and the online code sample at (msdn.microsoft.com/magazine/hh394148).
I’ll cover the second option in a bit more depth this time. As I’ve mentioned in previous articles, when you find yourself in the market for a cross-browser polyfill for an HTML5 technology, the first place to go is Modernizr’s home on GitHub, where they’ve posted a pretty comprehensive list of usable polyfills (bit.ly/eBMoLW). At the time of this writing, there were three geolocation polyfills listed. I chose to use the one provided by Paul Irish (bit.ly/q20TOl) purely as an example for this article.
You’ll recall from my September article that a polyfill is a library that provides a script-based implementation for an HTML5 feature, and it often adheres to the documented API for that specification in the process. The latter point is key, because it means that once I’ve added such a library to my application, I can continue to interact with the capabilities of that feature as if it were fully supported in the browser, and I don’t have to fork the flow of my program with conditional logic to support an older browser.
As I’ve done before in this series, I’m using Modernizr to help me detect support for the geolocation feature and, if no such support exists, asynchronously load the geolocationShim.js script file:
Modernizr.load({
test: Modernizr.geolocation,
nope: '../js/geolocationShim.js',
complete: function() {
locate();
}
});
If geolocation is supported, execution will continue to the locate function. If not, the Geolocation polyfill will be loaded and, when complete, execution will then continue to the locate function. When I run the sample with this logic in Internet Explorer 9, everything works as it should and the native geolocation functionality is called.
To test what this looks like in an older browser, I can open up the F12 Developer Tools for Internet Explorer 9 and switch the Browser Mode to Internet Explorer 8. When I do this and execute the page again, Modernizr will detect that geolocation is not supported and load my shim. Then, when the getCurrentPosition method in my shim is called, the code in Figure 4 will be executed.
Figure 4 The getCurrentPosition Method Implemented via a Shim
geolocation.getCurrentPosition = function(callback){
if (cache) callback(cache);
$.getScript('//www.google.com/jsapi',function(){
cache = {
coords : {
"latitude": google.loader.ClientLocation.latitude,
"longitude": google.loader.ClientLocation.longitude
}
};
callback(cache);
});
};
Because this polyfilling library adheres to the Geolocation API specification and provides the getCurrentPosition and watchPosition methods, I’m not required to implement any additional checks or conditional logic in my main getLocation function.
Whether you choose to gracefully degrade your application and allow users to manually provide their location or try out implementing a shim, you have options when it comes to adopting geolocation in your applications, while making sure that your site continues to provide a great experience to those who don’t yet use a modern browser.
Much of what the world is calling HTML5, or the Open Web Technologies, is a set of technologies geared toward making real application development possible on the Web. The gap between the Web and the desktop is narrowing, and geolocation is a great example of what’s possible with Web applications today. In this article, I covered the basics of the Geolocation spec. I showed how you can get started, how geolocation is implemented across desktop and mobile browsers, and how to polyfill geolocation support in older browsers. Now it’s time for you to add this exciting feature to your Web applications, so go make some awesome apps today!
If you’re looking for more information on geolocation support in Internet Explorer 9, check out the Internet Explorer 9 Guide for Developers at msdn.microsoft.com/ie/ff468705. For other cross-browser polyfills for geolocation, check out the complete list at bit.ly/qNE92g.
Finally, all of the demos for this article—which are available online—were built using WebMatrix, a free, lightweight Web development tool from Microsoft. You can try WebMatrix out for yourself at aka.ms/webm.
Brandon Satrom works as a developer evangelist for Microsoft outside of Austin. He blogs at userinexperience.com and can be found on Twitter at twitter.com/BrandonSatrom.
Thanks to the following technical experts for reviewing this article: Jon Box, John Hrvatin, Clark Sell and Andy Zeigler