Geocoding with GeoPy

Posted by on October 14, 2009

There are now may geocoding web services out there, like Google geocoder, Yahoo geocoder, geocoder.us (only for US address), Microsoft MapPoint, GeoNames and MediaWiki.

Normally you may access this web services API directly with HTTP REST request, and get a response in common formats like xml, json, kml.
For example you may geocode with the Google geocoder an address like this one: “1071 5th Avenue, New York, NY” with a request like this one:

http://maps.google.com/maps/geo?q=1071+5th+Avenue,+New+York,+NY&output=json&oe=utf8&sensor=true_or_false=&key=your_apy_key

In this case we are querying the Google geocoder web service to get a response in json via the output parameter in the query string. This is the json response we get back from the web service:

{
  "name": "1071 5th Avenue, New York, NY",
  "Status": {
    "code": 200,
    "request": "geocode"
  },
  "Placemark": [ {
    "id": "p1",
    "address": "1071 5th Ave, New York, NY 10128, USA",
    "AddressDetails": {
   "Accuracy" : 8,
   "Country" : {
      "AdministrativeArea" : {
         "AdministrativeAreaName" : "NY",
         "SubAdministrativeArea" : {
            "Locality" : {
               "DependentLocality" : {
                  "DependentLocalityName" : "Manhattan",
                  "PostalCode" : {
                     "PostalCodeNumber" : "10128"
                  },
                  "Thoroughfare" : {
                     "ThoroughfareName" : "1071 5th Ave"
                  }
               },
               "LocalityName" : "New York"
            },
            "SubAdministrativeAreaName" : "New York"
         }
      },
      "CountryName" : "USA",
      "CountryNameCode" : "US"
   }
},
    "ExtendedData": {
      "LatLonBox": {
        "north": 40.7860701,
        "south": 40.7797749,
        "east": -73.9563496,
        "west": -73.9626448
      }
    },
    "Point": {
      "coordinates": [ -73.9588670, 40.7829790, 0 ]
    }
  } ]
}

Note that the response includes the main result (the coordinates of the geocoded point) and other important attributes, like - in the case of Google - the administrative area name, the sub administrative area name, the country name and so on.

The other web services works in a manner very similiar to Google. For example this is how the same address is geocoded by GeoNames (we will use the name place here - GeoNames is a database of geographic names: “Solomon Guggenheim Museum, NY”)
If we make this request to the web services:
http://ws.geonames.org/search?q=Solomon+Guggenheim+Museum+NY

we get this response:

<geonames style="MEDIUM">
<totalResultsCount>1</totalResultsCount>
<geoname>
<name>Solomon R Guggenheim Museum</name>
<lat>40.782879</lat>
<lng>-73.959027</lng>
<geonameId>5119572</geonameId>
<countryCode>US</countryCode>
<countryName>United States</countryName>
<fcl>S</fcl>
<fcode>BLDG</fcode>
</geoname>
</geonames>

This is the same request made to the Yahoo Geocoding Web Service: http://local.yahooapis.com/MapsService/V1/geocode?appid=your_app_id&street=1071+Fifth+Avenue&city=NY&state=NY

And this is the result (only xml and Serialized PHP are available for Yahoo):

<ResultSet xsi:schemaLocation="urn:yahoo:maps http://api.local.yahoo.com/MapsService/V1/GeocodeResponse.xsd">
<Result precision="address">
<Latitude>40.782976</Latitude>
<Longitude>-73.959358</Longitude>
<Address>1071 5th Ave</Address>
<City>New York</City>
<State>NY</State>
<Zip>10128-0112</Zip>
<Country>US</Country>
</Result>
</ResultSet>

Google and GeoNames have also the possibility to reverse geocoding an address (to find an associated textual location such as a street address from geographic coordinates).
This is how to do the reverse geocoding request with Google (note that now we pass in the querystring the coordinates of the point and not the address):
http://maps.google.com/maps/geo?q=40.7829790,-73.9588670&output=json&oe=utf8&sensor=true_or_false&key=your_api_key

And this is the result (we still have a json output - note that there in this case are many results!):

{
  "name": "40.7829790,-73.9588670",
  "Status": {
    "code": 200,
    "request": "geocode"
  },
  "Placemark": [ {
    "id": "p1",
    "address": "1070 5th Ave, New York, NY 10128, USA",
    "AddressDetails": {
   "Accuracy" : 8,
   "Country" : {
      "AdministrativeArea" : {
         "AdministrativeAreaName" : "NY",
         "SubAdministrativeArea" : {
            "Locality" : {
               "DependentLocality" : {
                  "DependentLocalityName" : "Manhattan",
                  "PostalCode" : {
                     "PostalCodeNumber" : "10128"
                  },
                  "Thoroughfare" : {
                     "ThoroughfareName" : "1070 5th Ave"
                  }
               },
               "LocalityName" : "New York"
            },
            "SubAdministrativeAreaName" : "New York"
         }
      },
      "CountryName" : "USA",
      "CountryNameCode" : "US"
   }
},
    "ExtendedData": {
      "LatLonBox": {
        "north": 40.7861576,
        "south": 40.7798624,
        "east": -73.9557594,
        "west": -73.9620546
      }
    },
    "Point": {
      "coordinates": [ -73.9589070, 40.7830100, 0 ]
    }
  }, {
    "id": "p2",
    "address": "Solomon R Guggenheim Museum, New York, NY 10128, USA",
    "AddressDetails": {
   "Accuracy" : 9,
   "Country" : {
      "AdministrativeArea" : {
         "AdministrativeAreaName" : "NY",
         "SubAdministrativeArea" : {
            "Locality" : {
               "DependentLocality" : {
                  "AddressLine" : [ "Solomon R Guggenheim Museum" ],
                  "DependentLocalityName" : "Manhattan",
                  "PostalCode" : {
                     "PostalCodeNumber" : "10128"
                  }
               },
               "LocalityName" : "New York"
            },
            "SubAdministrativeAreaName" : "New York"
         }
      },
      "CountryName" : "USA",
      "CountryNameCode" : "US"
   }
},
    "ExtendedData": {
      "LatLonBox": {
        "north": 40.7860266,
        "south": 40.7797314,
        "east": -73.9558794,
        "west": -73.9621746
      }
    },
    "Point": {
      "coordinates": [ -73.9590270, 40.7828790, 0 ]
    }
  },
.... other results

If you need to invoke these web services in your code and you are using a Python, there is an excellent API that let you query all these web services in the same manner without messing your code with xml/json request/response to them: this API is called GeoPy.
Let’s see how it is easy to work with it. First I create a virtualenv in my Ubuntu 9.04 with Python 2.5 and geopy 0.93. GeoPy at this time doesn’t support Python > 2.5, and I will use the GeoPy from a branch, because that is the only one that at this time supporting reverse geocoding. If you do not need reverse geocoding you may use the stable version.

paolo@paolo-laptop:~/training$ virtualenv --python=python2.5 geopy
Running virtualenv with interpreter /home/paolo/training/reverse_geocoding/bin/python2.5
Using real prefix '/usr'
New python executable in geopy/bin/python2.5
Also creating executable in geopy/bin/python
Installing setuptools............done.
paolo@paolo-laptop:~/training$ cd geopy
paolo@paolo-laptop:~/training/geopy$ source bin/activate
(geopy)paolo@paolo-laptop:~/training/geopy$ easy_install http://geopy.googlecode.com/svn/branches/reverse-geocode
Downloading http://geopy.googlecode.com/svn/branches/reverse-geocode
Doing subversion checkout from http://geopy.googlecode.com/svn/branches/reverse-geocode to /tmp/easy_install-JVmlqN/reverse-geocode
Processing reverse-geocode
Running setup.py -q bdist_egg --dist-dir /tmp/easy_install-JVmlqN/reverse-geocode/egg-dist-tmp-HaqZ4c
zip_safe flag not set; analyzing archive contents...
Adding geopy 0.93dev-r84 to easy-install.pth file
Installed /home/paolo/training/geopy/lib/python2.5/site-packages/geopy-0.93dev_r84-py2.5.egg
Processing dependencies for geopy==0.93dev-r84
Finished processing dependencies for geopy==0.93dev-r84

GeoPy needs SimpleJson and BeautifulSoup, so we install them in the virtualenv:

(geocoding)paolo@paolo-laptop:~/training/geocoding$ easy_install simplejson
(geocoding)paolo@paolo-laptop:~/training/geocoding$ easy_install BeautifulSoup

Now let’s activate the virtualenv againg and time to test this API.
I will geocode the address with Google and after I get the point coordinates I will reverse geocode that point:

>>>from geopy import geocoders
>>>g = geocoders.Google('your_api_key')
>>>(place, point) = g.geocode('1071 5th Avenue, New York, NY')
Fetching http://maps.google.com/maps/geo?q=1071+5th+Avenue%2C+New+York%2C+NY&output=kml&key=your_api_key...
>>>print place, point
1071 5th Ave, New York, NY 10128, USA (40.782978999999997, -73.958866999999998)
>>>(new_place, new_point) = g.reverse(point)
Fetching http://maps.google.com/maps/geo?q=40.782979%2C-73.958867&output=kml&key=your_api_key...
>>>print new_place, new_point
1070 5th Ave, New York, NY 10128, USA (40.783009999999997, -73.958906999999996)

Using the API with different web services is just very similiar, there is a geocoder class for each web service: this is how to interact with Yahoo:

>>>from geopy import geocoders
>>>y = geocoders.Yahoo('your_app_id')
>>>(place, point) = y.geocode('1071 5th Avenue, New York, NY')
Fetching http://api.local.yahoo.com/MapsService/V1/geocode?output=xml&location=1071+5th+Avenue%2C+New+York%2C+NY&appid=your_app_id...
>>>print place, point
1071 5th Ave, New York, NY 10128-0112, US (40.782975999999998, -73.959357999999995)

You will find more samples at the GeoPy website.

One thing that is useful with these Geocoding Web Services is the possibility to get the accuracy of the geocoded address: Google calls this “Accuracy”, Yahoo calls this “Precision”. You have seen the value of this parameter in the answers from the web services. Unluckily this value is still not accessible from GeoPy, but looks like it will be soon implemented.

Share and Enjoy:
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google
  • Furl
  • LinkedIn
  • Reddit
  • StumbleUpon
10 Comments on Geocoding with GeoPy

Closed

  1. Sean Gillies says:

    Well, HTTP *GET*, not REST, but yes, geopy is neat. I also like seeing virtualenv in the mix.

  2. Paolo Corti says:

    Hi Sean, just to be sure: you mean it is not REST because it is not implement a hierarchy in the URL or because the otput format is specified in the query string? Because the data is read here, and the GET request make senses here in the REST point of view.

  3. Sean Gillies says:

    Paolo, REST is an architectural style and involves constraints that don’t crop up at all in the process of querying a network geocoder. One request does not an architecture make. GeoPy makes a HTTP GET request. That’s all.

  4. Paolo says:

    Hey Sean, I am going to remove the “REST” word between “HTTP” and “request” but: to my understanding isn’t a read only access to a URL resource made by an HTTP GET, like in this case? Okthere is not PUT-DELETE etc in this case, but it is still a REST resource accessed by a GET.
    And why, ie Yahoo is talking about REST here: http://developer.yahoo.com/search/rest.html
    Thanks for your (eventual) explanation :D

  5. GIS-Lab Blog says:

    ??????? ??????…

    SASGIS.ru сообÑ?аеÑ? об обновлении сеÑ?виса Ð?аÑ?Ñ?Ñ?@Mail.Ru (каÑ?Ñ?Ñ? СамаÑ?Ñ?, Челябинска, Ð?Ð…

  6. G says:

    All of your sample URLs have sensor=true_or_false in them. This breaks as of October 15, 2009… you’ll need to set that to either “sensor=true” or “sensor=false”

  7. Paolo Corti says:

    thanks, but I have left it there because it depends from what device the user is making the request. Just like I left out my original key values, every user needs to provide its values.

  8. Greg Corradini says:

    I love GeoPy. Thanks Paolo

  9. Cool, I see that you are getting into a Pinax a bit too. Ever tried GeoDjango?

  10. Paolo Corti says:

    Skylar, look at the 3th post before this one ;-)