Advertisement

An Introduction to the Geolocation API

by

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.

  1. successCallback: This callback function is executed after successfully obtaining the user's location. The callback accepts a position object that contains the device's location information.
  2. errorCallback: The error callback is executed when an error is encountered. The error callback accepts an error object, containing information about the type of error that occurred.
  3. options: The options 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, accuracyaltitudeAccuracy, heading, and speed.

Most desktop browsers will not return a value for the altitudealtitudeAccuracyheading, 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 to true, 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 is false.
  • timeout: This property specifies the maximum number of milliseconds after which the request should be considered out of time. The default value is Infinity.
  • maximumAge: When a location request is successful, the browser caches the result for later use. The maximumAge property specifies the time after which the cache must be invalidated. The default value is 0, 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 speedaltitude, 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.

Advertisement