Chapter 3. Geolocation API in Code

At this point, I am sure there are a lot of you reading this that are thinking to yourself, “Thank you, Mr. Author, for all of that lovely background information on what geolocation is, but can we see how to do this in code, already?” If that is you, then you are in luck, because this chapter is all about coding with the W3C Geolocation API.

The background information in the previous chapters is definitely relevant to our discussion on the Geolocation API itself. Understanding, for example, that the latitude and longitude that we retrieve from the user’s browser is in the WGS 84 datum will come in handy. If you have no idea what I am talking about, go back and read (or re-read) Chapter 2 so that you have a good grasp on the information we are going to be working with.

W3C Geolocation API

The W3C Geolocation API is a specification that provides scripted access to geographical location information associated with the hosting device.[8] It is meant to be a “high-level interface” so that the developer using it does not need to worry about details such as how the location information is being gathered. It does not matter whether the device is using GPS, IP Address, or Cell ID; only the geolocation information itself is important. The one caveat that the specification makes, however, is that there is no guarantee that the location returned from the API is the actual location of the device. This should come as no surprise, given that GPS may not have enough visible satellites to determine an accurate position, there may not be enough cell towers to get a proper triangulation from a Cell ID, or an IP Address could be spoofed to give a completely false location. Because of these, and other possible reasons, the developer may feel reasonably confident in the results returned by the API, but should never rely on any information blindly.

Note

The latest version of the W3C Geolocation API Specification is W3C Candidate Recommendation 07 September 2010.

Current API Support

Currently, the W3C Geolocation API is supported by most modern browsers on both the desktop and on mobile phones. Table 3-1 shows the current browser support for the API. The biggest issue developers will face is that older browsers have obviously not adopted this technology since it was created after those browsers were released. This is particularly difficult for developers because of the widespread use of Internet Explorer 8 (and perhaps even Internet Explorer 7), and also for all users of mobile phones who have not upgraded to current versions of software or hardware. The good news is that the API should be available in all future browser and phone releases.

Table 3-1. Browser Support for the W3C Geolocation API
Web browserSupported in versions
Firefox3.5+
Chrome5.0+
Internet Explorer9.0+
Safari5.0+[a]
Opera10.6+
iPhone3.1+
Android2.0+
BlackBerry6+[a]

[a] Has correct implementation, but not completely implemented

Other Browser Solutions

As I noted, not all browsers support the W3C Geolocation API, and these legacy browsers never will natively. Fortunately, other programmers have taken it upon themselves to do something about it, and wrote wrapper libraries that give these browsers most of the functionality found in the Geolocation API. However, there are differences between these libraries and the W3C Geolocation API which make it a bit more challenging for the developer to write code that will work in all browsers. First, let us take a look at some of these other APIs, and then we will see how we can resolve our differences.

Gears

Gears, formally known as Google Gears, is (as the name obviously suggests) a code library written by the good folks over at Google, Inc. Gears is designed to be an open source project that enables more powerful web applications by adding new features to the web browser. The component of Gears that most interests us is the Geolocation module, which acts very much like the W3C Geolocation API. In fact, parts of the W3C’s Geolocation API were probably modeled on Gears.

Warning

In the Gears API Blog on March 11, 2011, Google announced that no further new releases to Gears would be coming, nor would Gears support newer browsers such as Firefox 4 and Internet Explorer 9, and it will be removed from Chrome 12. Older browsers, however, can continue to use Gears to gain the functionality they lack.

Gears can be added to a web page using the following line of code:

<script type="text/javascript" 
    src="http://code.google.com/apis/gears/gears_init.js"></script>

We do not need to worry about the specifics of using Gears in our code because, as you will see in geo-location-javascript we will let another library take care of reconciling differences between Gears, other APIs, and the W3C Geolocation API. Additional examples can be found in Chapter 4, but for now, just remember that this library is out there and available.

Other Mobile APIs

Of course, hardware vendors have their own specifications and APIs for using geolocation on their devices, especially for older mobile device platforms before geolocation became mainstream. The following lists some of the other Geolocation APIs that may be encountered or required depending on the devices connecting to your site:

  • iOS

  • BlackBerry

  • Nokia

  • webOS (Palm)

All iPhones with OS less than 3.0 relied on Apple’s own Geolocation API for any location-based application development. The same holds true for BlackBerry developers—BlackBerry devices with OS less than 6 relied on BlackBerry’s own implementation.

Nokia has its own version of a Geolocation API that works in the browser shipped with its phones, as does Palm with its webOS 2.1 SDK’s HTML5 Enhancements. It is hoped that further development by these vendors will release fully W3C Geolocation API compliant browsers or support, which would eliminate the mess that developers currently face.

Which brings us to...

geo-location-javascript

geo-location-javascript is an attempt to build a JavaScript framework that will wrap all of the underlying platform implementations into a single API that works similarly to the W3C Geolocation API Specification. The following platforms are currently supported by the API:

  • iOS

  • Android

  • BlackBerry OS

  • Gears

  • Nokia Web Run-Time (WRT)

  • webOS Application Platform (Palm)

  • Torch Mobile Iris Browser

  • Mozilla Geode

The API has two key functions: checking to see if the connecting device has geolocation capabilities and getting the location of the device. As you will see in The W3C Geolocation API Does More, this is just a small subset of functionality that the full W3C Geolocation API has, and there is additional coding work needed if you want that additional functionality.

Consider the following code snippet:

<script type="text/javascript" 
    src="http://code.google.com/apis/gears/gears_init.js"></script> 
<script type="text/javascript" src="geo.js"></script>
<script type="text/javascript">
  function initialize() {
    if (geo_position_js.init())
      geo_position_js.getCurrentPosition(show_position, error_handler, 
        { enableHighAccuracy: true }
      );
    else
      alert('Geolocation functionality is not available.');
</script>

The first line of code loads Gears into the application, and the following line loads the geo-location-javascript API. In a real application scenario, the initialize() function would be called on an onload event fired by the document.

The initialize() function itself is straightforward. It first checks to see if the device supports geolocation, and if it does it acquires the current position of the device. Should the device not support geolocation, then a message to that effect will be displayed to the user. The geo-location-javascript API, like all other Geolocation APIs, including the W3C Geolocation API, requires the user to opt in and allow the location to be collected before proceeding. Learn more about that in Privacy.

There are two callback functions in the call to geo_position_js.getCurrentPosition(): show_position and error_handler. If anything were to go wrong with the attempt to locate the device, or the user opted out of the location search, the error_handler function would be called. Otherwise, the show_position function would be called, and the latitude and longitude of the device would be available to the application.

Warning

As previously mentioned, the geo-location-javascript API does not have support for a position polling method like the W3C Geolocation API does (discussed further in the next section). You will need to use a JavaScript setInterval() and poll the getCurrentPosition() yourself.

The W3C Geolocation API Does More

In this chapter, and the book in general, the focus is on a JavaScript implementation of the W3C Geolocation API to work within HTML5 applications. The interface’s implementation is being included in all modern browsers for both desktop and mobile devices. Because it is a newer API, however, there has been a need for other APIs to step in and create the cross-browser implementations needed to include older devices and browsers. The geo-location-javascript API, described in geo-location-javascript, is one such API.

The downside to using these APIs is they do not share all the functionality that the W3C Geolocation API has, leaving gaps in what can be developed. As you will see in the rest of this chapter, the W3C Geolocation API is a robust and thorough interface that allows for the development of web applications that rival applications created natively on devices.

The Geolocation Object

The W3C Geolocation API specifies a general implementation for the objects, properties, and methods associated with the Geolocation interface. One object holds the whole implementation of the W3C Geolocation API—the Geolocation object. This object can be used within JavaScript to gather geolocation information about the device on which the browser resides. The Geolocation object is a new property of the global window.navigator Browser Object, and can be accessed from the window.navigator.geolocation instantiation.

As with all JavaScript objects, it is a best practice to first test for the existence of an object’s implementation within a browser before using it, as the following code illustrates:

if (window.navigator.geolocation) {
  // do some geolocation stuff
} else {
  // the browser does not natively support geolocation
}

In the preceding code, the existence of an implementation of the geolocation property is tested, and if it exists, the code will do geolocation processing, otherwise something else will need to be tried.

The Geolocation object contains three public methods, as described in Table 3-2. All geolocation functionality stems from these methods and the callback functions they take as parameters.

Table 3-2. Geolocation methods
MethodDescription
clearWatch(watchId)Stops the watch process associated with the passed watchId.
getCurrentPosition(successCallback, [errorCallback, [options]])Attempt to gather geolocation information, calling successCallback when it succeeds or the optional errorCallback when it fails.
watchPosition(successCallback, [errorCallback, [options]])Attempts to gather geolocation information at regular intervals, calling successCallback when it succeeds or the optional errorCallback when it fails.

Get the User’s Position

Once it has been verified that the browser supports the W3C Geolocation API, requests can be made to get the current position of the device in question. This is done using the getCurrentPosition() method. The getCurrentPosition() method requires at least one parameter, a position callback function. Optionally, it can also take an error callback function, and an options parameter. It is called like this:

navigator.geolocation.getCurrentPosition(successCallback, errorCallback, 
    options);

The first parameter, successCallback, will be called when a successful position has been discovered by the inner workings of the API. The second parameter, errorCallback, is optional and will be called when there is an error in gathering a position. The final parameter, options, is a PositionOptions object that is also optional.

Warning

The successCallback parameter for the getCurrentPosition() callback is required—if it is omitted, the getCurrentPosition() call automatically fails and aborts any location fetching.

The following code snippet shows the getCurrentPosition() more fully:

if (window.navigator.geolocation) {
  navigator.geolocation.getCurrentPosition(successCallback, errorCallback, 
      options);
} else {
  alert('Your browser does not natively support geolocation.');
}

function successCallback(position) {
  // Do something with a location here
}

function errorCallback(error) {
  // There was a problem getting the location
}

PositionOptions

The PositionOptions object is an optional parameter that can be passed to the getCurrentPosition() method, and as you will see in Update the User’s Position, is also an optional parameter to the watchPosition() method. All of the properties available in the PositionOptions object, shown in Table 3-3, are optional as well.

Table 3-3. PositionOptions object properties
PropertyJavaScript typeDescription
enableHighAccuraryBooleanFlags the API to attempt to get as close to the exact location of the device as possible. The default value is false. Slower response times and/or increased power consumption may result from setting this property to true.
maximumAgeIntegerSignals to the application that it will accept a cached position with an age no greater than the time specified in milliseconds. The default value is 0.
timeoutIntegerIndicates the maximum length of time, in milliseconds, that the application will wait from the beginning of a call to the evocation of the successCallback function. The default value is 0.

The following is an example of calling getCurrentPosition() with the PositionOptions object set:

var options = {
  enableHighAccuracy: true,
  maximumAge: 60000,
  timeout: 45000
};

navigator.geolocation.getCurrentPosition(successCallback, errorCallback, 
    options);

This code calls getCurrentPosition(), requesting high accuracy, and a cached position no older than 60 seconds, with a timeout period of 45 seconds before returning an error.

Cached Positions

A cached position is a position that was gathered by the application sometime in the past that may be used again instead of requiring a new position to be fetched. When it is not necessary for the application to display changes in a position very frequently, a cached position is a good alternative. This will save processing overhead since a new call to the API will not be needed in order to get a cached position. To specify the acceptable age of a position, the PositionOptions object is passed to the getCurrentPosition() or watchPosition() method with the optional maximumAge property set to the desired time in milliseconds.

For example:

var options = {
  maximumAge: 600000
};

navigator.geolocation.getCurrentPosition(successCallback, errorCallback, 
    options);

The preceding code would accept a cached position that no older than 60 minutes. If you wish to get a fresh position each time, do not pass a maximumAge property (it defaults to 0), or pass the value 0 yourself. Should you wish to always get a cached position, regardless of its age, pass the value Infinity to the calling method, like this:

var options = {
  maximumAge: Infinity
};

navigator.geolocation.getCurrentPosition(successCallback, errorCallback, 
    options);

Update the User’s Position

There are times when an application requires an updated position every time the device changes location. In these cases, a call to the watchPosition() method is warranted in place of calling the getCurrentPosition() method. The watchPosition() method has the same basic structure as the getCurrentPosition() method. It also takes a successCallback parameter that is required, as well as two optional parameters: errorCallback and options.

The major difference between the two methods is that the watchPosition() method will return a value immediately upon being called which uniquely identifies that watch operation. The watch operation itself is an asynchronous operation. It is called like this:

var watcher = navigator.geolocation.watchPosition(successCallback, 
                errorCallback, options);

The first parameter, successCallback, will be called when a successful position has been gathered by the API. The second parameter, errorCallback, is optional and will be called when there is an error in gathering a position. The final parameter, options, is a PositionOptions object that is also optional. The variable, watcher, is the unique identifier for this particular watch operation.

The following code snippet shows how to use watchPosition():

var watcher = null;
var options = {
  enableHighAccuracy: true,
  timeout: 45000
};

if (window.navigator.geolocation) {
  watcher = navigator.geolocation.watchPosition(successCallback, 
              errorCallback, options);
} else {
  alert('Your browser does not natively support geolocation.');
}

function successCallback(position) {
  // Do something with a location here
}

function errorCallback(error) {
  // There was a problem getting the location
}

No Need for Polling

The watchPosition() method has built-in functionality to automatically poll the device for a change in position and will call the successCallback function every time there is a new position for the device. This eliminates the need for the developer to roll her own code to poll the device every x number of seconds. Because the watchPosition() method has automatic polling, this also provides for true real-time geolocation applications. Creating custom polling functionality will give you pseudo-real-time position updates at best, and will cause additional processing overhead that will slow the application down in the long run.

Whenever possible, the watchPosition() method’s polling capabilities should be used for position updating. Do not create custom position polling functionality unless your application has a unique need for it.

Clearing a Watch Operation

Like the JavaScript clearTimeout() and clearInterval() methods, the W3C Geolocation API provides a method for clearing a watch operation by passing the desired watchId to the clearWatch() method. The syntax is:

navigator.geolocation.clearWatch(watcher);

The following code demonstrates creating a new watch operation, and then removing that watch upon the successful fetching of a position:

var watcher = null;
var options = {
  enableHighAccuracy: true,
  timeout: 45000
};

if (window.navigator.geolocation) {
  watcher = navigator.geolocation.watchPosition(successCallback, 
              errorCallback, options);
} else {
  alert('Your browser does not natively support geolocation.');
}

function successCallback(position) {
  navigator.geolocation.clearWatch(watcher);
  // Do something with a location here
}

Handling a Successful Request

Once a location request has been gathered by the API, the successCallback function is called. This works the same using either the getCurrentPosition() or updatePosition() methods. The successCallback function is passed one parameter from the API, a Position object.

Position Object

The Position object holds all of the geolocation information that is returned from the W3C Geolocation API call and is passed to a successCallback function. Table 3-4 contains a list of the current properties this object has. There is room in this object for additional information, and, in particular, geocoding information in possible future versions of the API.

Table 3-4. Position object properties
PropertyDescription
coordsA Coordinates object containing geographic coordinates and other properties.
timestampA DOMTimeStamp holding the time when the Position object was obtained.

Coordinates Object

The main geographic information gathered by the API is held in a Coordinates object which is a property of the Position object (see Position Object). This information is in the World Geodetic System reference system WGS 84. More information on this reference system can be found in WGS 84. Currently, no other reference system is supported by the W3C Geolocation API. A list of the properties found in the Coordinates object are in Table 3-5.

Table 3-5. Coordinates object properties
PropertyDescription
latitudeThe geographic coordinate of latitude for the device, measured in decimal degrees.
longitudeThe geographic coordinate of longitude for the device, measured in decimal degrees.
altitudeThe geographic height of the device, measured in meters above the WGS 84 ellipsoid.
accuracyThe accuracy of the latitude and longitude, specified in meters.
altitudeAccuracyThe accruacy of the height, specified in meters. When not supported, this value is null.
headingThe direction of travel of the device, measured in degrees from 0° clockwise to 360°. This value will be NaN when the device is not moving. When not supported, this value is null.
speedThe current ground speed of the device, measured in meters per second. When not supported, this value is null.

In Example 3-1, all of the components of the Geolocation object covered so far are shown in use.

Example 3-1. A first geolocation example
<!DOCTYPE html>
<html lang="en">
  <head>
    <title>A First Geolocation Example</title>
    <meta name="viewport" content="initial-scale=1.0, user-scalable=no"/>
    <meta charset="utf-8"/>'
    <script type="text/javascript">
      var options = {
        enableHighAccuracy: true,
        maximumAge: 1000,
        timeout: 45000
      };

      if (window.navigator.geolocation) {
        navigator.geolocation.getCurrentPosition(successCallback, 
            errorCallback, options);
      } else {
        alert('Your browser does not natively support geolocation.');
      }

      function successCallback(position) {
        var output = '';

        output += "Your position has been located.\n\n";
        output += 'Latitude: ' + position.coords.latitude + "°\n";
        output += 'Longitude: ' + position.coords.longitude + "°\n";
        output += 'Accuracy: ' + position.coords.accuracy + " meters\n";
        if (position.coords.altitude)
          output += 'Altitude: ' + position.coords.altitude + " meters\n";
        if (position.coords.altitudeAccuracy)
          output += 'Altitude Accuracy: ' + position.coords.altitudeAccuracy + 
              " meters\n";
        if (position.coords.heading)
          output += 'Heading: ' + position.coords.Heading + "°\n";
        if (position.coords.speed)
          output += 'Speed: ' + position.coords.Speed + " m/s\n";
        output += 'Time of Position: ' + position.timestamp;

        alert(output);'
      }

      function errorCallback(error) {
        // There was a problem getting the location
      }
    </script>
  </head>
  <body>
    <div>A First Geolocation Example</div>
  </body>
</html>

This example shows how to get all of the geographic information from the Position object, though it does not do anything more than alert the results to the user. In a real application, the coordinates would be used to plot a point on a map, or they could be saved to a database.

Handling an Error from the Request

There are several reasons why a location request could fail within the API, and with any of these the errorCallback function is called when it is provided with either the getCurrentPosition() or updatePosition() methods. When provided, the errorCallback function is passed one parameter from the API, a PositionError object.

PositionError Object

The PositionError object holds all of the error information returned from the W3C Geolocation API call and is passed to an errorCallback function. Table 3-6 contains a list of this object’s current properties.

Table 3-6. PositionError object properties
PropertyDescription
codeThe code is a numeric value alerting the developer to what the error is. The code will be one of the following values:
PERMISSION_DENIED (1)

The location call failed because the application does not have the necessary permissions to use the Geolocation API.

POSITION_UNAVAILALE (2)

The location call failed because the device could not determine a position.

TIMEOUT (2)

The location call failed because the length of time elapsed making attempting to find a position exceeded the value of the timeout property.

messageA detailed message containing the error that was encountered for the purposes of developer debugging. This message is not meant to be displayed to the end-user of the application.

In Example 3-3, a final version of the Geolocation object using all of the API is shown.

Example 3-3. A second geolocation example
<!DOCTYPE html>
<html lang="en">
  <head>
    <title>A First Geolocation Example</title>
    <meta name="viewport" content="initial-scale=1.0, user-scalable=no"/>
    <meta charset="utf-8"/>
    <script type="text/javascript">
      var options = {
        enableHighAccuracy: true,
        maximumAge: 1000,
        timeout: 45000
      };

      if (window.navigator.geolocation) {
        navigator.geolocation.getCurrentPosition(successCallback, 
            errorCallback, options);
      } else {
        alert('Your browser does not natively support geolocation.');
      }

      function successCallback(position) {
        var output = '';

        output += "Your position has been located.\n\n";
        output += 'Latitude: ' + position.coords.latitude + "°\n";
        output += 'Longitude: ' + position.coords.longitude + "°\n";
        output += 'Accuracy: ' + position.coords.accuracy + " meters\n";
        if (position.coords.altitude)
          output += 'Altitude: ' + position.coords.altitude + " meters\n";
        if (position.coords.altitudeAccuracy)
          output += 'Altitude Accuracy: ' + position.coords.altitudeAccuracy + 
              " meters\n";
        if (position.coords.heading)
          output += 'Heading: ' + position.coords.Heading + "°\n";
        if (position.coords.speed)
          output += 'Speed: ' + position.coords.Speed + " m/s\n";
        output += 'Time of Position: ' + position.timestamp;

        alert(output);
      }

      function errorCallback(error) {
        switch (error.code) {
          case error.PERMISSION_DENIED:
            alert('You have denied access to your position.');
            break;
          case error.POSITION_UNAVAILABLE:
            alert('There was a problem getting your position.');
            break;
          case error.TIMEOUT:
            alert('The application has timed out attempting to get your ' +
			    location.');
              break;
        }
      }
    </script>
  </head>
  <body>
    <div>A First Geolocation Example</div>
  </body>
</html>

In this example, the errors are captured, and a more user-friendly alert is sent to the user. In a real application, it would be good to log the error, and possibly do more with the message for the user, but this should give you some idea of what to do with the error code being passed with the PositionError object.

Privacy

Privacy is an important matter, not only with the W3C Geolocation API, but with all geolocation applications available today. Companies know how dearly individuals hold their privacy, and take many precautions to safeguard any data collected from the user. If a user is ever in any doubt as to a company’s policies and procedures, he should check with the company’s Privacy Policy regarding user data.

To address security and privacy considerations with the W3C Geolocation API, the specification outlines that no locations may be sent to web applications without the express permission of the user. The standard way this is being implemented across browsers is to provide an information bar that, as per the W3C specification, lists the URI of the document requesting a location (see Figure 3-1). The user can opt to always allow access for the site with a checkbox indicating this site-level access, which can be undone at any time within the settings of the browser itself.

Opt-in policy in Chrome
Figure 3-1. Opt-in policy in Chrome

Note

From a development standpoint, should the user decide not to grant your application access to his location information, you should make sure that your application can handle this gracefully.

Unless the user has granted the application a site-level access retained within the specific browser, the permissions given by the user will expire at the end of the current browsing session. With such opt-in policies, any implementation of the W3C Geolocation API should achieve adequate privacy functionality and alleviate a user’s anxiety towards their location data. The best a developer can do is guarantee proper handling of a user’s data, while users would be wise to follow a policy of only granting access to their personal data to trusted individuals and sites.



[8] Geolocation API Specification: W3C Candidate Recommendation 07 September 2010. Editor, Andrei Popescu, Google, Inc. http://www.w3.org/TR/geolocation-API/.