Where is my user? Part 2, Browser Geolocation

As we saw last week GeoIP can be pretty inaccurate for mobile users – the exact audience we may be trying hardest to serve with a geographically aware website. But the W3C saw, or was made to see, the writing on the wall and built a set of standard APIs into HTML5 for just this case and most modern browsers have picked it up.

The draft for the spec is http://dev.w3.org/geo/api/spec-source.html if you want to read it through or need further info.

The API is pretty marvelously simple. This implementation changes the URL to return latitude and longitude when they are available, which we can use in our Django view. Plus, the same code works on mobile devices (at least the iOS ones I carry) with no changes.

So let’s dive right in, and make the campgrounds dataset grab the nearest results to the user.

The view:

from django.contrib.gis.geos import Point
from django.shortcuts import render_to_response
from django.template import RequestContext

from uscampgrounds.models import *

def nearby_campgrounds(request):
    if request.GET.get('lat') and request.GET.get('lon'):
        origin = Point(float(request.GET.get('lon')), float(request.GET.get('lat')))
        camps = Campground.objects.all().distance(origin).order_by('distance')
    else:
        camps = Campground.objects.all().order_by('name')

    return render_to_response('uscampgrounds/nearby.html', {
        'object_list': camps
    }, context_instance=RequestContext(request))

uscampgrounds/nearby.html template:

<html>
<head>
  <script src='http://media.adamfast.com/QueryData.compressed.js'></script>
  <script language='JavaScript'>

    function checkLocation() {
      var getData = new QueryData();
      if ('lat' in getData) { }
      else {
        if (navigator.geolocation) {
          navigator.geolocation.getCurrentPosition(
            function (ppos) {
              window.location.href = window.location.href + '?lat=' + ppos.coords.latitude + '&lon=' + ppos.coords.longitude;
            },
            function (err) {
              switch(err.code) {

                case err.TIMEOUT:
                  alert('Attempts to retrieve location timed out.')
                  break;

                case err.POSITION_UNAVAILABLE:
                  alert("Your browser doesn't know where you are.")
                  break;

                case err.PERMISSION_DENIED:
                  alert('You have to give us permission!')
                  break;

                case err.UNKNOWN_ERROR:
                  alert('Unknown error returned.')
                  break;

                default:
                  alert(err + ' ' + err.code)

              }
            }
          );
        }
      }
    }
  </script>
</head>
<body{% block body_override %} onLoad="javascript:checkLocation();"{% endblock body_override %}>
{% block content %}

{% endblock content %}
</body>
</html>

It’s using QueryData.js, a small library to make getting data out of the current page’s querystring easier.

Other than that we check to see if the querystring has location info available – if not we request it from the browser and register a callback to bring the user back to the page with the right querystring args. The second function passed into getCurrentPosition() is the error-handling callback. In this case I’ve just set it to alert in the various cases for simplicity’s sake.

Leave a Reply

Your email address will not be published. Required fields are marked *