How to reverse geocode serverside with python, json and google maps?
Asked Answered
C

1

6

I'm trying serverside reverse geocoding that can get me a json response and now I want to get 2 or 3 variables from the json response:

I'd like to parse for instance this data and end with eg. administrative_area_level_1 = 'Stockholm'

jsondata = json.load(urllib2.urlopen('http://maps.googleapis.com/maps/api/geocode/json?latlng=59.3,18.1&sensor=false'))

Here is my python code that fetches the json, now I wonder how to parse it to get just the

  • administrative_area_level_1 long_name (ie state or region name)
  • locality long name (ie city name)
  • an understanding how to parse my json

I can parse it but it is not always is comes out as administrative_area_1:

jsondata["results"][0]["address_components"][5]["long_name"]

The line above correctly outputs "New York" for a point in New York but for Stockholm it outputs a postal city ie Johanneshow which is not the administraive_area_1 (region/state). So how guarantee that the function always returns the administrative_area_1, preferably without looping?

I want it to work something like the following with direct access to country, region and city:

logging.info("country:"+str(jsondata["results"][9]["formatted_address"]))
logging.info("administrative_area_level_1:"+str(jsondata["results"][8]["formatted_address"]))
logging.info("locality:"+str(jsondata["results"][8]["formatted_address"]))

Thanks in advance

Update

It's a good answer here with the results I expected. While waiting for the answer I also tried implement a solution myself that seems to do it:

jsondata = json.load(urllib2.urlopen('http://maps.googleapis.com/maps/api/geocode/json?latlng='+str(ad.geopt.lat)+','+str(ad.geopt.lon)+'&sensor=false'))
logging.info("geography:"+str(jsondata["results"][1]["formatted_address"]))
region = None
city = None
for result in jsondata["results"]:
  #logging.info("result:"+str(result))
  for component in result["address_components"]:
    logging.info("components:"+str(component))
    logging.info("components type:"+str(component["types"]))
    if 'administrative_area_level_1' in component["types"]:
      #logging.info(unicode('found admin area:%s' % component["long_name"]))
      region = component["long_name"]
    if 'locality' in component["types"]:
      logging.info("found locality:"+str(component["long_name"]))
      city = component["long_name"]

Coupling answered 6/12, 2011 at 4:11 Comment(0)
O
14

Processing the response

There is no need to parse the JSON - it is already parsed by json.load() and returned as Python's data structure. Use it like simple dictionary with lists or different dictionaries in it.

Accessing the needed part of the response

To access data you should be working with you can use the following:

jsondata['results'][0]['address_components']

which is where all information on geographical names is included:

[{u'long_name': u'S\xf6dra L\xe4nken', u'types': [u'route'], u'short_name': u'S\xf6dra L\xe4nken'}, {u'long_name': u'Stockholm', u'types': [u'locality', u'political'], u'short_name': u'Stockholm'}, {u'long_name': u'Stockholm', u'types': [u'administrative_area_level_1', u'political'], u'short_name': u'Stockholm'}, {u'long_name': u'Sweden', u'types': [u'country', u'political'], u'short_name': u'SE'}, {u'long_name': u'12146', u'types': [u'postal_code'], u'short_name': u'12146'}, {u'long_name': u'Johanneshov', u'types': [u'postal_town'], u'short_name': u'Johanneshov'}]

Filtering data you need

As you can see, there is plenty of data you do not need, but you want only locality and administrative_area_level_1 information. You can filter the data using filter() Python function like that:

>>> mydata = jsondata['results'][0]['address_components']
>>> types = ['locality', 'administrative_area_level_1']
>>> geonames = filter(lambda x: len(set(x['types']).intersection(types)), mydata)

Basically you are getting only elements that have 'locality' or 'administrative_area_level_1' in their "types" lists. After the above, geonames will be list containing dictionaries you need:

[{u'long_name': u'Stockholm', u'types': [u'locality', u'political'], u'short_name': u'Stockholm'}, {u'long_name': u'Stockholm', u'types': [u'administrative_area_level_1', u'political'], u'short_name': u'Stockholm'}]

Displaying the data

To display their names you can eg. iterate through them, displaying long_names and respective types values:

>>> for geoname in geonames:
    common_types = set(geoname['types']).intersection(set(types))
    print '{} ({})'.format(geoname['long_name'], str(', '.join(common_types)))


Stockholm (locality)
Stockholm (administrative_area_level_1)

Is this what you expected?

Whole code

The code could look like this:

import json
import urllib2

def get_geonames(lat, lng, types):
    url = 'http://maps.googleapis.com/maps/api/geocode/json' + \
            '?latlng={},{}&sensor=false'.format(lat, lng)
    jsondata = json.load(urllib2.urlopen(url))
    address_comps = jsondata['results'][0]['address_components']
    filter_method = lambda x: len(set(x['types']).intersection(types))
    return filter(filter_method, address_comps)

lat, lng = 59.3, 18.1
types = ['locality', 'administrative_area_level_1']

# Display all geographical names along with their types
for geoname in get_geonames(lat, lng, types):
    common_types = set(geoname['types']).intersection(set(types))
    print '{} ({})'.format(geoname['long_name'], ', '.join(common_types))
Operation answered 6/12, 2011 at 4:55 Comment(2)
Thank you very much! In the meantime I also developed a solution that filters the locality and the admin area level 1 so we can compare the 2 solutions. I'm updating the question with my "solution" and I will also try the one from this answer. What I basically did was looping through results and looping through types to find matches and then assign those matches to variables region and city. Thank you!Coupling
@Nickle: Your solution should work also! :) The thing is I focused on readability, length and reusability of my code - it uses filter() instead of big loop, lambda instead of conditional checks, formatting instead of concatenation. The last one (string formatting) could improve the readability of your code a lot. But as I said, it should work :)Operation

© 2022 - 2024 — McMap. All rights reserved.