Using Geocoders with GeoDjango
January 24th, 2010 | Published in Mapping, Python, django | 4 Comments
Update: Simon has updated his library to make it easy to reverse the order of coordinates. Thanks!
For a “15-minute project“, Simon Willison’s geocoders library is pretty handy if you’re doing geocoding with Python. It offers a common interface to the geocoding services provided by Google, Yahoo and other sources. When we were looking at replacing the home-grown geocoding system that Andrei Scheinkman built for Represent, Simon’s project seemed a natural choice.
It was an easy drop-in, but there was one thing about it that was just slightly off. A successful geocoding result looks like this:
(u'New York, NY, USA', (40.756053999999999, -73.986951000000005))
Notice the coordinate pair is latitude, longitude. For folks using GeoDjango alongside Simon’s library, the way you build a Point object from coordinates is to pass the longitude first, like so:
>>> from django.contrib.gis.geos import Point
>>> p = Point((5, 23)) # 2D point, passed in as a tuple
So on Friday I forked Simon’s project and reversed the ordering of the coordinates in a successful result. That way you can pass that portion of the result directly to a Point constructor:
>>> from django.contrib.gis.geos import *
>>> from geocoders.google import geocoder
>>> geocode = geocoder('GOOGLE-API-KEY')
>>> results = geocode('new york')
(u'New York, NY, USA', (-73.986951000000005, 40.756053999999999))
>>> pnt = Point(results[1])
Not a huge deal, but in keeping with the spirit of library, I think.
January 25th, 2010 at 8:36 am (#)
I’m clearly not a real geo-nerd, because I’ve always found longitude/latitude ordering unintuitive. You’re right though, it’s silly not to support the format used by GeoDjango. I’ve just committed a fix for this, which changes the API to look like this:
>>> from geocoders.google import geocoder
>>> geocode = geocoder(‘GOOGLE-API-KEY’, lonlat=True)
>>> results = geocode(‘new york’)
(u’New York, NY, USA’, (-73.986951000000005, 40.756053999999999))
>>> pnt = Point(results[1])
As you can see, the default order remains the same (partly to ensure backwards compatibility for existing library users, but mainly because I prefer it) but you can now specify the order you want when you instantiate the geocoder – which you hopefully only do in one place in your code.
Here’s the commit: http://github.com/simonw/geocoders/commit/46fc00bda4cff0fe222d38fe498aa9ba861e8dc1
January 25th, 2010 at 8:47 am (#)
As you say, it’s not a huge deal — and I’m not opposed to forking for minor changes — but would it be easier to just do:
pnt = Point(results[1][::-1])
January 25th, 2010 at 11:02 am (#)
Thanks, Simon! I think yours is the most flexible solution for everybody. And I’m not a real geo-nerd either; just someone who gets to look good thanks to GeoDjango.
Aaron – yeah, you could, sure. I guess it just looked cleaner to me the other way.
January 25th, 2010 at 11:49 am (#)
Here’s how I’ve mostly done it (using Geopy, which has the same issue):
def geocode(q):
place, (lat, lng) = g.geocode(‘somewhere’)
return Point(lng, lat)
I do like Simon’s fix, though.