Introduction
For many years now, paper maps have become an antique. They've been replaced by dedicated GPS navigation devices and mobile applications, which have become ubiquitous. You find them in cars and, more importantly, on tablets and smartphones.
One of the main features of a navigation device is detecting the device's current position and updating it as it changes. This helps us to get from one location to another by giving us directions.
Today, we're lucky enough to have geolocation natively supported by browsers. In this article, we'll discuss the Geolocation API, allowing applications to detect and track the device's location.
The possibility to detect the device's location has a wide range of applications. On the web, for example, Google, Microsoft, and Yahoo use the user's location to personalize the SERPs (Search Engine Results Page) based on the user's location. Localization is another great fit for geolocation.
1. What is it?
The Geolocation API defines a high-level interface to location information, such as latitude and longitude, which is linked to the device hosting the implementation. The API itself is agnostic of the underlying location information sources.
Common sources of location information include the Global Positioning System (GPS) and location information inferred from network signals such as the device's IP address, RFID, Wi-Fi, Bluetooth, MAC addresses, GSM/CDMA cell IDs, and user input. No guarantee is given that the API returns the device's actual location.
The Geolocation API is a W3C Recommendation meaning that the specification is stable. We can assume that it won't change in the future unless a new version is being worked on. It's worth noting that the Geolocation API isn't officially part of the HTML5 specification, because it's been developed separately.
Now that we know what the Geolocation API is and what it can do, it's time to see what methods it exposes to developers.
2. Implementation
Accuracy
The API uses several sources to detect the device's position. For example, on a notebook or a desktop computer without a GPS chip, it's likely that the position is inferred from the device's IP address, which means that the location returned by the API isn't very accurate.
On a mobile device, however, the API can use information from multiple and more accurate sources, such as the device's GPS chip, the network connection (Wi-Fi, 3G, HSPA+), and the GSM/CDMA cell. You can expect more accurate location information on a mobile device, especially if GPS is enabled.
Privacy
The specification of the Geolocation API also discusses privacy and permissions. In fact, the specification clearly states that the user's permission needs to be explicitly obtained before enabling the API.
What this means is that the browser is required to display a notification to the user asking for their permission. An example of the message shown to the user is shown below (Google Maps).
API
The API exposes three methods that belong to the window.navigator.geolocation
object. The methods provided are:
getCurrentPosition
watchPosition
clearWatch
Detecting Support
As many other APIs, detecting whether the device's browser supports the Geolocation API is very easy. Take a look at the following snippet in which we detect support for the Geolocation API.if (window.navigator && window.navigator.geolocation) { // I can watch you wherever you go... } else { // Not supported }
Locating the Device
To detect the device's location we call getCurrentPosition
or watchPosition
, depending on your needs. Both methods perform the same task with only a few minor differences.
To obtain the device's location, getCurrentPosition
and watchPosition
make an asynchronous request. The difference between these methods is that getCurrentPosition
performs a one-time request, while watchPosition
monitors the device's location for changes and notifies the application when a location changes takes place.
The Geolocation API is smart enough to only invoke the success callback of watchPosition
—invoked when the position is obtained—if the user's location changes.
Another important difference between getCurrentPosition
and watchPosition
is the return value of each method. The getCurrentPosition
method returns nothing, while watchPosition
returns an identifier that can be used to stop the API from monitoring the device's location through the clearWatch
function.
The signatures of getCurrentPosition
and watchPosition
look like this:
// Get Current Position getCurrentPosition(successCallback [, errorCallback [, options]]) // Watch Position watchPosition(successCallback [, errorCallback [, options]])
As the signatures indicate, each function accepts three parameters. Only the first argument, the success callback function, is required. Let's take a closer look at each argument.
successCallback
: This callback function is executed after successfully obtaining the user's location. The callback accepts aposition
object that contains the device's location information.errorCallback
: The error callback is executed when an error is encountered. The error callback accepts anerror
object, containing information about the type of error that occurred.options
: Theoptions
object gives the developer the ability to configure the asynchronous request.
Take a look at the following snippets to see how you can use getCurrentPosition
and watchPosition
to obtain the device's location.
var geolocation = null; if (window.navigator && window.navigator.geolocation) { geolocation = window.navigator.geolocation; } if (geolocation) { geolocation.getCurrentPosition(function(position) { console.log(position); }); var identifier = geolocation.watchPosition(function(position) { console.log(position); }); console.log(identifier); }
Stop Monitoring Location
In the previous section, I mentioned the clearWatch
function. This function lets you stop monitoring the device's location, initiated by invoking watchPosition
.
The clearWatch
function accepts one required argument, the identifier returned to us after invoking watchPosition
.
Now that we've covered the technical details of the Geolocation API, it's time to explore the position
, error
, and options
objects returned by the Geolocation API.
Location Information
Position
The methods exposed by the Geolocation API accept or return three types of objects. The first object that's of interest to us is the position
object, which contains the location information that we're interested in. Take a look at the following table to get an idea of the information it contains.
The position object that's returned from the success callbacks of getCurrentPosition
and watchPosition
contains a timestamp
and coords
property. The coords
property is an object containing the location's latitude
, longitude
, altitude
, accuracy
, altitudeAccuracy
, heading
, and speed
.
Most desktop browsers will not return a value for the altitude
, altitudeAccuracy
, heading
, and speed
properties. Mobile devices, however, such as smartphones and tablets, will provide more accurate information thanks to the presence of a GPS chip or other hardware that helps detecting the location of the device.
The timestamp
property holds the time the location was detected, which can be useful if you need to know how fresh the data is that was returned.
PositionError
The error
object of the error callback, the optional second argument of getCurrentPosition
and watchPosition
, has a code
and a message
property.
The message
property briefly describes the type of error. The code
property can have one of four values:
0
: The request failed, but the reason is not known.1
: The request failed because the user didn't give permission to use the device's location.2
: The request failed as a result of a network error.3
: The request failed because it took too long to resolve the device's position.
PositionOptions
The optional third argument of getCurrentPosition
and watchPosition
is a PositionOptions
object, enabling the developer to customize the asynchronous request.
The PositionOptions object currently supports three options:
enableHighAccuracy
: If the value is set totrue
, the web page or application indicates that it wants the best possible—most accurate—result. This may result in a slower response time or increased power consumption. The default value isfalse
.timeout
: This property specifies the maximum number of milliseconds after which the request should be considered out of time. The default value isInfinity
.maximumAge
: When a location request is successful, the browser caches the result for later use. ThemaximumAge
property specifies the time after which the cache must be invalidated. The default value is0
, meaning that request should not be cached.
Browser Support
Support for the Geolocation API is really good. This is true for desktop and mobile browsers. Take a look at this summary to get an idea of which desktop browsers support the Geolocation API:
- Firefox 3.5+
- Chrome 5.0+
- Safari 5.0+
- Opera 10.60+
- Internet Explorer 9.0+
Support in mobile browsers is even better as you can see in this summary:
- Android 2.0+
- iPhone 3.0+
- Opera Mobile 10.1+
- Symbian (S60 third and fifth generation)
- Blackberry OS 6
The Geolocation API is widely supported. You may be wondering what you can do if you encounter a browser that doesn't support the Geolocation API is. Believe it or not, several polyfills and shims exist to remedy that issue. The most notable solutions are the one created by Manuel Bieh and a lightweight shim created by Paul Irish.
Demo
Now that we know the ins and outs of the Geolocation API, it's time to see it in action. This demo is fully functional and uses all the methods and objects described in this article. The purpose is simple, every time a request to detect the device's position is performed, the location data is shown to the user in list format.
The demo contains three buttons, allowing you to select the operation you want to perform. The demo also detects if the browser supports the Geolocation API or not. If it doesn't, you'll see the message "API not supported" and the buttons are disabled.
The source code of the demo is shown below, but you can also play with a live demo of the Geolocation API.
<!DOCTYPE html><html><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"/><meta name="author" content="Aurelio De Rosa"><title>Geolocation API Demo by Aurelio De Rosa</title><style> * { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } body { max-width: 500px; margin: 2em auto; padding: 0 0.5em; font-size: 20px; } h1 { text-align: center; } .hidden { display: none; } .buttons-wrapper { text-align: center; } .button-demo { padding: 0.5em; margin: 1em auto; } .g-info { font-weight: bold; } #log { height: 200px; width: 100%; overflow-y: scroll; border: 1px solid #333333; line-height: 1.3em; } .author { display: block; margin-top: 1em; } </style></head><body><h1>Geolocation API</h1><div class="buttons-wrapper"><button id="button-get-position" class="button-demo">Get current position</button><button id="button-watch-position" class="button-demo">Watch position</button><button id="button-stop-watching" class="button-demo">Stop watching position</button></div><span id="g-unsupported" class="hidden">API not supported</span><h2>Information</h2><div id="g-information"><ul><li> Your position is <span id="latitude" class="g-info">unavailable</span>° latitude, <span id="longitude" class="g-info">unavailable</span>° longitude (with an accuracy of <span id="position-accuracy" class="g-info">unavailable</span> meters)</li><li> Your altitude is <span id="altitude" class="g-info">unavailable</span> meters (with an accuracy of <span id="altitude-accuracy" class="g-info">unavailable</span> meters)</li><li>You're <span id="heading" class="g-info">unavailable</span>° from the True north</li><li>You're moving at a speed of <span id="speed" class="g-info">unavailable</span>° meters/second</li><li>Data updated at <span id="timestamp" class="g-info">unavailable</span></li></ul></div><h3>Log</h3><div id="log"></div><button id="clear-log" class="button-demo">Clear log</button><small class="author"> Demo created by <a href="http://www.audero.it">Aurelio De Rosa</a> (<a href="https://twitter.com/AurelioDeRosa">@AurelioDeRosa</a>)</small><script> window.navigator = window.navigator || {}; window.navigator.geolocation = window.navigator.geolocation || undefined; if (navigator.geolocation === undefined) { document.getElementById('g-unsupported').classList.remove('hidden'); ['button-get-position', 'button-watch-position', 'button-stop-watching'].forEach(function(elementId) { document.getElementById(elementId).setAttribute('disabled', 'disabled'); }); } else { var log = document.getElementById('log'); var watchId = null; var positionOptions = { enableHighAccuracy: true, timeout: 10 * 1000, // 10 seconds maximumAge: 30 * 1000 // 30 seconds }; function success(position) { document.getElementById('latitude').innerHTML = position.coords.latitude; document.getElementById('longitude').innerHTML = position.coords.longitude; document.getElementById('position-accuracy').innerHTML = position.coords.accuracy; document.getElementById('altitude').innerHTML = position.coords.altitude ? position.coords.altitude : 'unavailable'; document.getElementById('altitude-accuracy').innerHTML = position.coords.altitudeAccuracy ? position.coords.altitudeAccuracy : 'unavailable'; document.getElementById('heading').innerHTML = position.coords.heading ? position.coords.heading : 'unavailable'; document.getElementById('speed').innerHTML = position.coords.speed ? position.coords.speed : 'unavailable'; document.getElementById('timestamp').innerHTML = (new Date(position.timestamp)).toString(); log.innerHTML = 'Position succesfully retrieved<br />' + log.innerHTML; } function error(positionError) { log.innerHTML = 'Error: ' + positionError.message + '<br />' + log.innerHTML; } document.getElementById('button-get-position').addEventListener('click', function() { navigator.geolocation.getCurrentPosition(success, error, positionOptions); }); document.getElementById('button-watch-position').addEventListener('click', function() { watchId = navigator.geolocation.watchPosition(success, error, positionOptions); }); document.getElementById('button-stop-watching').addEventListener('click', function() { if (watchId !== null) { navigator.geolocation.clearWatch(watchId); log.innerHTML = 'Stopped watching position<br />' + log.innerHTML; } }); document.getElementById('clear-log').addEventListener('click', function() { log.innerHTML = ''; }); } </script></body></html>
Conclusion
In this article, we've learned about the Geolocation API. We've seen what it is, how to use it, and when to use it.
The Geolocation API is a useful tool to improve the user experience and can serve many purposes. Support is broad, but don't forget that older versions of Internet Explorer don't support it.
As we discussed in this article, you should be aware that some location data, such as speed
, altitude
, and heading
, aren't always available. Also remember to use the Geolocation API with with care, because it does require significant battery power, especially on mobile devices equipped with a GPS chip.