Geolocation in the Browser

Andrew Markham
Share

Geolocation is undoubtably one of the most useful features of smartphones, and much attention has given to native apps that take advantage of it. Less appreciated is the fact that it’s now feasible to build web apps and sites with geolocation capabilities. The W3C Geolocation API Specification proposes a standard for accessing location services in the browser via Javascript. The spec is recent but is already widely supported by modern browsers. Now is the ideal time to consider how a location aware mobile site could benefit your users.

Current browser support for the W3C Geolocation API

  • iPhone 3.0 +
  • Android 2.0 +
  • Firefox 3.5
  • Safari 5.0
  • Chrome 5.0
  • Internet Explorer 9.0+

How the Browser Finds You

There are a number of methods employed in geolocation. Your browser may simply lookup your IP address, or ask Google for the location a nearby wifi hotspot. A desktop browser, with no access to GPS, will resort to one of these methods and they are not particularly accurate. Chrome misses my current location by about 15km, but it’s good enough to tell I’m in Melbourne, Australia.

Your phone has access to better information. It can access the position of the cell towers it’s talking to and take a reasonable stab at your actual location. Last but not least, the phone will activate its GPS hardware which can pin you down to within a few meters. The GPS chip draws significant power from a phones battery, so be considerate: if this level of accuracy isn’t necessary, don’t use it.

As with native apps, permission for the browser to use your location is always opt-in. Users will be receive a request to access their location, and can reset permissions at any time via their browser settings. The challenge is ensure users trust you to use their location responsibly, and for your site to offer value with—or without—access to the location data.

The Code

If geolocation is supported, a global navigator.geolocation object will be available:

if ( navigator.geolocation )
{
	navigator.geolocation.getCurrentPosition(handlePosition);
}

getCurrentPosition will request location from the browser and pass a position object to the specified callback:

function handlePosition(pos)
{
	alert("Your location is: " + pos.coords.latitude + ', ' + pos.coords.longitude);
}


Try it:


The position object should have the following properties:

  • coords.latitude in degrees.
  • coords.longitude in degrees.
  • coords.accuracy approximate, in meters.
  • coords.heading degrees from north (0…360).
  • coords.speed in meters per second.
  • coords.altitude in meters above sea level (approximate – I wouldn’t rely on it!).
  • coords.altitudeAccuracy in meters.
  • timestamp a DOMTimeStamp object.

There’s a good chance many of these will be null, you can only rely on coords.latitude and coords.longitude.

There are also a number of reasons getCurrentPosition may fail: location data may simply be unavailable, or the user may have denied access. A second callback can be specified to handle errors:

if ( navigator.geolocation )
{
	navigator.geolocation.getCurrentPosition(handlePosition, handleError);
}

function handleError(error)
{
	switch (error.code)
	{
		case error.PERMISSION_DENIED:
			// User denied access to location. Perhaps redirect to alternate content?
			alert('Permission was denied');
			break;
		case error.POSITION_UNAVAILABLE:
			alert('Position is currently unavailable.');
			break;
		case error.PERMISSION_DENIED_TIMEOUT:
			alert('User took to long to grant/deny permission.');
			break;
		case error.UNKNOWN_ERROR:
			alert('An unknown error occurred.')
			break;
	}
}

Your web app should handle all cases gracefully. If a user declines to share their location, consider offering alternative content or features that do not rely on geolocation. Further consideration can be given to your users with options passed to getCurrentPosition:

options = {
	enableHighAccuracy: false,
	timeout:            30000,  // milliseconds (30 seconds)
	maximumAge:         600000 // milliseconds (10 minutes)
}
navigator.geolocation.getCurrentPosition(handlePosition, handleError, options);

enableHighAccuracy is essentially a hint that you want GPS data (rather than less accurate methods such a cell tower triangulation). As mentioned above, GPS drains the phone battery and isn’t always required – set this to false if you only need a general area.

timeout specifies how long you’re willing to wait for location results.

maximumAge tells the browser you’re willing to accept a result from within, for example, the last 10 minutes. If the phone has done a location lookup in that time (possibly for another site/app) it will return a cached result. Again, this is a way of being kind to the phone’s battery life.

What if you need up-to-the-minute location data that updates when the user moves? You can use watchPosition:

if ( navigator.geolocation )
{
	navigator.geolocation.watchPosition(handlePosition, handleError, {
		enabledHighAccuracy: true,
		maximumAge: 60000
		});
}

function handlePosition(pos)
{
	console.log("Heading: " + pos.coords.heading);
	console.log("Speed:   " + pos.coords.speed);
}

In most instances this level of accuracy won’t be required. With valid privacy concerns around the use of location data, it’s important to only access information your need and use it responsibly. Some simple guidelines:

  1. Consider the value you’re providing to your users in return for their location – don’t simply collect data for the sake of it!
  2. Be kind to phone batteries – use enableHighAccuracy: false if you only need a broad location.
  3. If you store location data, make this clear through on your website (and in your site’s terms & conditions).

Google Maps

Now we’ve covered the basics of geolocation let’s see how it fits in with Google maps. Maps support can be added to your web app with:

<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=true"></script>

The sensor parameter indicates whether you are using a sensor – i.e. GPS – to detect location. For mobile applications this should be set to true. We can further tailor our map with some user agent detection – this snippet will give Android and iPhone users a full screen map:

function detectBrowser()
{
	var userAgent = navigator.userAgent;
	var mapDiv = document.getElementById("map");

	if (userAgent.indexOf('iPhone') != -1 || userAgent.indexOf('Android') != -1 )
	{
		mapDiv.style.width = '100%';
		mapDiv.style.height = '100%';
	}
	else
	{
		mapDiv.style.width = '600px';
		mapDiv.style.height = '800px';
	}
}

iPhones also pay attention the tag. This disables zooming the page so that pinch-to-zoom will be applied only to the map.

<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />

Now to setup the map itself:

var map = new google.maps.Map(document.getElementById("map"), {
	zoom: 10,
	mapTypeId: google.maps.MapTypeId.ROADMAP
});

That’s all there is to it. See the Google Maps Developers Guide for the full set of map configuration options. We can now update our handlePosition function to use the map:

function handlePosition(pos)
{
	// Google maps use a different location object (LatLng) which we can convert to easily:
	latlng = new google.maps.LatLng(pos.coords.latitude, pos.coords.longitude);

	// Center the map on the current location.
	map.setCenter(latlng);

	// Drop a marker on the current location
	marker = new google.maps.Marker
	({
		map: map,
		draggable: false,
		animation: google.maps.Animation.DROP,
		position: latlng
	});
}

Maps Example

Showing the user their current location isn’t the most useful application, but with the Geolocation API and Google maps at your disposal, you have an excellent set of tools for building location-based web apps. The challenge is to do something interesting! Happy geolocating.

Some further reading:

CSS Master, 3rd Edition