The W3C Browser Geolocation API

The World Wide Web Consortium (W3C) have published a Geolocation API specification that allows a web page to query the user’s location using JavaScript to access objects exposed by the browser. Last Tuesday’s release of Firefox 3.5 was the first mainstream implementation of this API.

This article demonstrates how to use the new geolocation functionality, and implements it in an existing Google Maps application.

The new Geolocation API allows a web page to asynchronously determine the user’s geographic location. The API supports single queries, altitude, error handling, cached results, and periodic updates (ie. for a mobile application).
Although the API is clearly intended to work with coordinates derived from a GPS device, the current Firefox implementation uses Google Location Services to derive the user’s location from their IP address and/or WiFi access point data.

The API obviously has some security and privacy concerns. Firefox mainly deals with these by making the entire functionality opt-in only. Ie. the user has to explicitly agree to share their location with a specific website.

Developers who are considering the Geolocation API should also read the W3C’s discussion of Security and Privacy Considerations, repeated here:

Security and privacy considerations

The API defined in this specification can be used to retrieve the geographic location of a hosting device. In almost all cases, this information also discloses the location of the user of the device, thereby potentially compromising the user’s privacy. A conforming implementation of this specification must provide a mechanism that protects the user’s privacy and this mechanism should ensure that no location information is made available without the user’s express permission.

Privacy considerations for implementors of the Geolocation API

User Agents must not send location information to Web sites without the express permission of the user. User Agents must acquire permission through a user interface, unless they have prearranged trust relationships with users, as described below. The user interface must include the URI of the document origin. Those permissions that are acquired through the user interface and that are preserved beyond the current browsing session (i.e. beyond the time when the browsing context is navigated to another URL) must be revocable and User Agents must respect revoked permissions.

Some User Agents will have prearranged trust relationships that do not require such user interfaces. For example, while a Web browser will present a user interface when a Web site performs a geolocation request, a VOIP telephone may not present any user interface when using location information to perform an E911 function.

Privacy considerations for recipients of location information

Recipients must only request location information when necessary. Recipients must only use the location information for the task for which it was provided to them. Recipients must dispose of location information once that task is completed, unless expressly permitted to retain it by the user. Recipients must also take measures to protect this information against unauthorized access. If location information is stored, users should be allowed to update and delete this information.

The recipient of location information must not retransmit the location information without the user’s express permission. Care should be taken when retransmitting and use of encryption is encouraged.

Recipients must clearly and conspicuously disclose the fact that they are collecting location data, the purpose for the collection, how long the data is retained, how the data is secured, how the data is shared if it is shared, how users may access, update and delete the data, and any other choices that users have with respect to the data. This disclosure must include an explanation of any exceptions to the guidelines listed above.

Additional implementation considerations

Further to the requirements listed in the previous section, implementors of the Geolocation API are also advised to consider the following aspects that may negatively affect the privacy of their users: in certain cases, users may inadvertently grant permission to the User Agent to disclose their location to Web sites. In other cases, the content hosted at a certain URL changes in such a way that the previously granted location permissions no longer apply as far as the user is concerned. Or the users might simply change their minds.

Predicting or preventing these situations is inherently difficult. Mitigation and in-depth defensive measures are an implementation responsibility and not prescribed by this specification. However, in designing these measures, implementers are advised to enable user awareness of location sharing, and to provide easy access to interfaces that enable revocation of permissions.

 

Using the Geolocation API

The new geolocation functionality is implemented through a new geolocation object in the parent navigator object. This object will exist only if the browser supports geolocation. It will be set to null (0) if the browser does not support geolocation. The location is requested by an asynchonous call to getCurrentPosition. This has one parameter: the function that will receive the location. A second method, watchPosition, works in a similar manner but results in repeated callbacks at a periodic interval.

Both methods can also be passed a second function that handles errors. Further parameters support advanced features such as setting the age of cached location results. See the specification at http://dev.w3.org/geo/api/spec-source.html for further details.

Here are some examples of simple Geolocation usage:

// Geolocation API Samples


///////////////
// Test that the browser supports the GeoLocation object

if (navigator.geolocation)
{
   // Call your geolocation code, HERE
}
else
{
   alert("Geolocation services are not supported by your browser.");
}


////////////////
// Simple asynchronous request of the user's location


function processLocation(position)
{
   alert("Your location is: Lng "+position.coords.longitude+", Lat "+position.coords.latitude);
}

// Request the location
navigator.geolocation.getCurrentPosition( processLocation );



///////////////
// Request multiple updates, and handle any errors


function trackMap(position)
{
   // Add code here to position the map and a pushpin at the new location
   // at position.coords
}


function errorHandler(error)
{
   alert("Location Error: " + error.message);
}


// Request multiple location updates
// watchID is a handler identifier for the request
var watchID = navigator.geolocation.watchPosition( trackMap, errorHandler );


// Stop the updates
navigator.geolocation.clearWatch( watchID );

Note that the two location callbacks are passed the location result as a position (COURIER) object. This has two members: the Coordinates object coords, and the DOMTimeStamp object timestamp. The timestamp will be useful for future tracking applications. The Coordinates object has the following properties: latitude, longitude, altitude, accuracy, altitudeAccuracy, heading, and speed. All are double precision numbers. The latitude and longitude are geographic coordinates in decimal degrees. These and the altitude are relative to the WGS84 datum. The altitude and accuracy methods are specified in metres. speed is in metres per second, and heading is a conventional bearing specified in degrees. Any unsupported attributes (e.g. altitude, speed, bearing) are reported as null.

Using the Geolocation API in an existing application

That is an overview of the functionality. Let’s see it in practice. Here we implement the GeoLocation API in the Calculator at Mileage-Charts.com. This is a simple route calculator based on Google Maps, that allows a user to measure the route distance and travel time between two points. The points are entered as text.

We shall modify the application to support the Geolocation API. The user’s location will be marked on the map, and the option will be given to use the current location as the route’s start point.

Here is the modified Javascript (see code comments for GeoLocation explanation):

<script type="text/javascript">

// Global variables
// Of note is "sLoc" which is the user's location coordinate formatted as 
// a string "lng,lat" that can be passed directly to Google as a text location

    var directions, adsManager;
    var map = null;
    var publisher_id;
    var adsManagerOptions;
    var sLoc="";	// GeoLocation as a string

    function handleErrors() {

        // Handles Google Routing Errors
        // (deleted for brevity)

	}
    
    
// Main map initialize. This is called when the HTML body object is loaded
// Also attempts to get the current location
// (contextual advertising parameters have been removed for brevity)

    function initialize() {
    
       // Create the map
       map = new GMap2(document.getElementById("map_canvas"));
       map.addControl(new GLargeMapControl());

       // Set a default view of the US
       map.setCenter( new GLatLng( 40.0,-95.0), 4 );
	
       // Mark the directions as null
       // This makes it easier to check if they are valid
       // We do not want to change the map view to the user's location if 
       // we are already display a route
       directions = 0; 
	
       // Attempt to find the user's position
       if (navigator.geolocation)
       {
           // Send the geolocation request
           navigator.geolocation.getCurrentPosition( position_found );
       }
       else
       {
           // GeoLocation not supported: quiet ignore
       }
	
    }


    // Here is the location callback
    // It is called by geolocation.getCurrentPosition, when we have 
    // the user's location

    function position_found( position )
    {
        // Create a Google location coordinate from the user's location
        var user_loc = new GLatLng( position.coords.latitude,
                                    position.coords.longitude );

        // Centre the map, but only if we do not have any directions
        // already loaded
        if (! directions)
        {
             map.setCenter( user_loc, 15 );
        }

        // Display a map marker at the user's location
        var marker = new GMarker( user_loc );
        map.addOverlay(marker);

        // Update the web page's HTML
        // Add a new "Your Location" section into an existing span entity
        // This displays the user's location as text including the error
        // estimate.
        // The HTML also includes a button which copies the location to the 
        // Start text box. This button has a callback to setFromLocation()
		
        var s = "<h4>Your location...</h4>Longitude:" + 
                position.coords.longitude + "; Latitude: " + 
                position.coords.latitude + " (Error ~" + 
                position.coords.accuracy + "m)";
        s = s + "<form><INPUT TYPE=\"button\" NAME=\"useLoc\" VALUE=\"Start the route here\" onClick=\"setFromLocation()\" /></form>";
	document.all.user_loc.innerHTML = s;			    

        // Keep a string copy of the location
        // This is copied to the start text box by setFromLocation()
	sLoc = position.coords.latitude + "," + position.coords.longitude;		
    }
    

    // This is the call back for the "Start the route here" button
    // This button is only displayed when the user has supplied a valid location
    // The button copies the location (stored as a string in sLoc)
    // to the start location text box
    
    function setFromLocation()
    {
        document.dform.mc_from.value = sLoc;
    }


    // This is the "find directions call back
    // It creates a GDirections object and 
    // calculates the route distance and time

    function find_directions()
    {

       directions = new GDirections(map); 

       GEvent.addListener(directions, "error", handleErrors);
       GEvent.addListener(directions, "load", function() {
			var dist = directions.getDistance();
			var tm = directions.getDuration();
			document.all.dresult.innerHTML = dist.html; 
			document.all.tresult.innerHTML = tm.html;
       });

       var sdir = document.dform.mc_from.value + " to " +
                  document.dform.mc_to.value;	
       directions.load(sdir);
    }
    

// Clean Google Maps up when the page exits

window.onunload = GUnload;
   
    
</script>

The HTML, error handling, and contextual advertising have been removed for clarity; but it should be easy to see how the geolocation api is used. The completed application is here.

Conclusions

Although there may be some privacy concerns with the new Geolocation API, it offers to open a whole new set of web applications and functionality. It will enable further content customization, and when it is tied to GPS devices, it will enable sophisticated tracking applications. Mozilla appear to have taken the security concerns on board and users have to explicitly opt-in to share their location.

Leave a Reply