Posts Tagged ‘geolocation’

Geolocation Redux and a JS Library

Thursday, May 22nd, 2008

After the excellent feedback I got on my last geolocation blog post, I’ve updated the proposed geolocation API. More importantly, I’ve also created a pure Javascript library which you can use to start playing around with geolocation before we actually get it into the browser. This library provides exactly the same API as would be provided by the browser. Although it can’t offer GPS-quality data, it does provide a decent city-level geolocation based on your IP. You can get the library here, and there’s also a no-frills demo.

For quick hacking you can just include the following in your head tag.

<script src="http://azarask.in/projects/geolocation/MockGeolocation.js"><script>

The best way to get a feel for the API is to see some examples:

var g = new navigator.GeolocationRequest();
g.request( function(geolocation){
  alert( geolocation.longitude + ", " + geolocation.latitude );
})

This is the same example as we saw last time; it alerts the user’s current location. What’s new is that I’ve spec’ed out the object passed to the callback (I’ve borrowed some of the great work done for the GoogleGears Location API):

interface Geolocation {
  readonly double latitude;         // in degrees
  readonly double longitude;        // in degrees
  readonly double altitude;         // in meters, or null if no data.
  readonly double accuracy;         // in meters
  readonly double altitudeAccuracy; // in meters, or null if no data
  int timestamp;                    // time of the location read, in seconds since
                                    // the epoch
}

Everything here should be self-explanatory. As a commenter mentioned last time, it’s important to know when the last location reading occurred. Without it, for example, it would be impossible to reliably calculate the current speed of the browsing device. (Knowing such data gives developers all sorts of fun—we could have an onDrunkDriving DOM event for when the browser detects your moving at 60MPH and weaving!).

Let’s move on to something a bit more complicated.

var g = new navigator.GeolocationRequest();
g.request({
  error: function(e){ alert( e.message ); },
  success: function(location){
    alert( location.altitude + "+/-" + location.altitudeAccuracy );
  },
  desiredAccuracy: g.defaultAccuracy
})

Here’s the new stuff:
error takes a callback whose argument is an object. The only attribute the object must have is message, a human readable explanation of the error. The error object may have other attributes, as dictated by the particulars of the location-giving device.

desiredAccuracy is a string which can take one of three values: “exact”, “neighbourhood”, and “city”. In order, “exact” means to return a geolocation as precisely as possible, “neighbourhood” means to return a geolocation good to 1km, and “city” means to return a geolocation good to 10km. It’s unnecessary to include a “country” option because there are more accurate ways of knowing what country a user is browsing from than with a highly fuzzed lat/long (e.g., IP-based geolocation).

defaultAccuracy is a read only property of the GeolocationRequest object that contains the user’s globally-set preference for yielding location to content providers. If the user has opted to give all websites city-level access to their location, then a website requesting city-level information won’t cause the browser to prompt for permission. In the above example, the request is asking for the highest accuracy location that won’t prompt the user for permission. defaultAccuracy is null if the user has opted out of providing geolocation information.

That about wraps it up for the API. Full interface documentation is available.


Why not on window.navigator?

It’s tempting to attach the GeolocationRequest object to window, but it makes more semantic sense to place it on the navigator object (and, unfortunately, not just for the pun). The navigator object contains meta information about the browsing agent—browser version, os, language, etc—and geolocation information is just that. Looking at it from the other side, the window object should contain information that is tied to the current tab/window. Geolocation is independent of what tab/window you are looking at (unless, of course, you have an extremely large screen), and so should not be attached directly to window.


Geocoding API

I’m somewhat on the fence about whether to include geocoding as part of the fundamental GeolocationRequest API. The information is useful to web developers, and it is a boon to not require an extra Ajax call to perform geocoding. However, it’s going beyond the scope of the fundamental language of geolocation. To quote Arun Ranganathan, our standards hero here at Mozilla:

When coining new browser primitives exposed at the same level as DOM stuff, we essentially go with the simplest thing that developers can do that won’t break the web and that are possibly exposed on the given device. You’ll notice that there’s plenty of scope for more nuanced APIs on top of our primitive stack, and this has contributed for much of the boom on the web for third party encapsulations around basic browser primitives (Dojo, Prototype, jQuery, etc.)… latitude/longitude is the basic thing we should expose. These can be used with other services and providers (example: Google Maps) to obtain address information or proximity information based on latitude and longitude.

That aside, we don’t really have to choose. By designing the API right, we can upgrade to include geocoding at a later date, without breaking any code. Let’s see it in action:

var g = new navigator.GeolocationRequest();
g.request({
  error: function(e){ /* Handle error. */ ); },
  success: function(location){ alert( location.address.city ); },
  desiredAccuracy: "neighborhood",
  requestAddress: true,
  addressLanguage: "en-US"
})

You can get the library for the address-enabled API here. The address object interface is:

interface Address {
  // Any of the below can be null, if not known.

  readonly string streetNumber; // street number
  readonly string street;       // street address
  readonly string premises;     // premises, e.g. building name
  readonly string city;         // city name
  readonly string county;       // county name
  readonly string region;       // region, e.g. a state in the US
  readonly string country;      // country
  readonly string countryCode;  // country code (ISO 3166-1)
  readonly string postalCode;   // postal code
}

The full specification is available, if you are interested in such sundry documentation.


Interface

Based on a number of good suggestions from the last post, this is how I’m thinking the security UI should look:
Geolocation Security Mockup