▶ 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.
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?)
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:
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:
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:
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