Dominikus BaurData Visualization

HTML5 Geolocation and User Experience

Sometimes less callbacks aren't necessarily better
Published: 2014/08/02

▶ Check out OECD Regional Well-Being

While detecting a website visitor's location has been around for a while (server-sided), the new (client-sided) HTML5 approach has the big advantage of respecting a person's privacy: before actually detecting anything, the browser asks the user if their location should be shared at all. Server-sided approaches just assume a user's consent (another example of forced opt-in in UI design), sometimes leading to unexpected consequences.

Ad showing girl asking if you lived in Anonymous Proxy too

Wow, small world! Server-sided location detection sometimes leads to surprising consequences.

Working with the HTML5 API is very convenient and simple, but it comes with some caveats when you're going for an enjoyable user experience as we discovered. While it's usually a good idea to keep the number of required callback handlers in an API to a minimum (reducing the amount of required code as well), sometimes more is more in that regard.

Regional Well-Being's location detection

We used the new API for the OECD Regional Well-Being project which shows well-being in various regions across the globe. The website starts out with the visitor's current region, providing them with a direct understanding of the data and its dimensions. From there, people can explore neighboring regions or other similar places across the globe (today's trivia: Which global regions is New York most similar to?)

OECD Regional Well-Being starts out with the visitor's current location

OECD Regional Well-Being starts out with the visitor's current location for their exploration.

To explain the potential and problems with HTML5 geolocation detection, I'll move along our own experiences with it in the Regional Well-Being project.

We wanted to keep the process as accessible as possible, so we decided to guide our visitors using a series of dialogs that provide feedback about what they're supposed to do or what the system is currently doing:

OECD Regional Well-Being dialogs that guide through the location process

In a perfect world, this would be the series of dialogs a visitor sees: (1) we're asking for their consent to location detection (clicking the corresponding button in the browser), (2) the system does its detection magic, (3) the region has been detected and the fun can start by clicking the big, bold Go! button.

At least, that's the theory.

Detection step 1: User consent

In contrast to server-sided detection, the HTML5 version comes with one type of user interaction, namely: asking for their consent to be tracked. These settings can be adjusted globally (Firefox, for example, lets you opt out of location-aware browsing altogether), but usually you're asked for every new website that you visit. These settings are stored, so if you re-visit the page you won't be asked again.

This behavior is implicitly called for in the W3C specification and should result in a way to either 'Allow' or 'Decline' a website's call for location data. In practice, of course, this dialog looks (and even acts!) different in every browser:

Geolocation detection across various browsers

If you look closely, you can see that there are two main ways to ask for a user's consent: non-blocking and blocking. Chrome, Internet Explorer and Firefox show a dialog while keeping the website still interactive. Safari and Opera (and their mobile versions) completely block interaction with the full browser (!) until a decision has been made. Additionally, some browsers provide more options when it comes to storing their decision: Firefox lets you decide whether to decline but ask again later and Safari can store a decision for only a day.

Providing a good user experience in all these scenarios is tough. We saw the dreaded browser detection creeping up at the horizon, but first tried to stick with the plan: our first dialog, asking people politely to click 'Allow' on whatever thing popped up on their browser, would still work in either the blocking or non-blocking case.

Step 2 of the process, however, ruined our good intentions.

Detection step 2: Browser magic

Once the user has given their consent, the actual location detection starts. Depending on the browser, this can be either getting a GPS-signal (on mobile devices) or sending a list of currently visible Wifi networks in the environment to a database and deriving the location from the result. As different browsers use different databases, sitting at your desk and launching a website on two different browsers can lead to two completely different detected locations.

But apart from that, we already ran into problems with just trying to set up our planned dialog flow: The geolocation API is very cautious when it comes to informing a website what's currently happening. There are only two types of callbacks available: that a location has been detected or that something has gone wrong (for example, missing browser support or a detection time-out).

This made it difficult to react to a user's first decision: while clicking on 'Deny' leads to an immediate error-callback, clicking on 'Allow' triggers the internal location detection without informing the requesting website. Once the process has finished or failed there's the corresponding callback.

As it would have been confusing for people to still be presented with the 'Please allow detection' dialog even though they already had clicked a button, we had to find a way to circumvent this missing callback. And we had to finally resort to that big ugly solution of old, the (trigger warning) browser detection.

Browser detect like it's 1999

To be honest, initially we thought we could do without, after I had discovered a hack for getting what we wanted without the proper callback.

The geo-detection is launched with the following Javascript-call:

    navigator.geolocation.getCurrentPosition(
        success,    /* callback handler for success */
        failure,    /* callback handler for errors */
        options
    );

Options contains parameters for setting high accuracy detection, whether to use cached location data and also for how long to try the detection. This last timeout parameter proved to be very useful. Using a call such as

    navigator.geolocation.getCurrentPosition(
        success,
        failure,
        {
            "timeout": 250
        }
    );

we can set the timeout to only 250 milliseconds. What this does in practice is that the geolocation fails very quickly (because of the timeout), but the error-handler is only called once the user had clicked on 'Allow'. Having received the error-handler call (with an error-code of 3 for timeout), the website can then show the 'Detecting your location' dialog and restart the location detection with a more reasonable timeout. Thus, we could hijack the error-handler to act as a de-facto consent-handler.

And our plan would have worked, if it wasn't for those meddling Microsoft browsers.

Initially, Internet Explorer's behavior is similar to its kin: it doesn't fire the error-callback for a very short timeout until the user has clicked on either 'Allow' or 'Deny'. Unfortunately, once that has happened there's no stopping the callback madness: when presented with a very short timeout setting, IE internally just repeats the detection over and over again and once the user consent has been given, fires all the resulting callbacks at once. With a timeout of 250 msecs and a time of, say, four seconds to click on the 'Allow' button, this means 16 calls of the error-handler all happening in quick succession. And as the original error-handler's reaction to this call was to launch another geolocation process, we had found a great way to quickly crash a browser.

So there was no way around the browser detection, at least to find out whether we were dealing with IE>9. In the Internet Explorer case, the timeout is set to our regular 5 seconds while skipping the 'Detecting your location' dialog altogether, with a slightly less nice user experience.

Detection step 3: from locations to regions

Finally, we had to derive the actual region from the received location data. There are various reverse geocoding services available (e.g., Google Maps) that can turn latitude and longitude into addresses, zip codes or regions. Unfortunately, this always leads to both technical and financial overhead: communicating with such a service introduces another wait after the location detection and it's pricey (here's an overview). Also it felt like overkill, as we only wanted to have regions not specific postal addresses.

Doing the region conversion in the browser is somewhat tricky, as regions tend not to come in neat circles around a center but have various shapes and sizes. And as transmitting polygon data is costly and checking whether a point lies in a polygon is too (for 362 regions no less), we resorted to a neat trick that Moritz and the guys from Studio NAND had come up with for previous projects: we would just use an image map giving each region their own color, project our current location on it and see which color we get.

Here's the result:

This 34kB image is used to map a user's latitude and longitude to a (color-coded) region.

Using this map, we can quickly detect the current region and do it with only transmitting roughly 50kB of data (34kB for the PNG-image and 16kB for a minified JSON-mapping between colors and region IDs).

To be fair, we have a short list of hard-coded mini-regions that we try beforehand. When the detected location has a certain distance to the centroid of regions such as Berlin, Paris or Jerusalem, we assume the person is currently there. Those regions are too small to be encoded in the image.

And then, finally, we can send our visitor their way to explore their region's well-being.

Conclusion

The new HTML5 geolocation API is a useful tool for detecting a person's location while preserving their privacy, but is challenging to combine with a good user experience. Having blocking/non-blocking, completely different looking versions of the consent-dialog across browsers is already a problem. From a developer perspective, the missing callback from a user's consent is more aching and prevents nicer ways of guiding a person through the (somewhat complex) process. I hope that future versions of the API will introduce something along those lines.

Try the OECD Regional Well-Being site!

More about OECD Regional Well-Being:

My blogpost about OECD Regional Well-Being
Moritz about OECD RWB