Finding country name from geolocation

I recently held a presentation on Storm at work and I wanted to create a little demo to show what Storm could do. One of the things I did was to subscribe to tweets from Twitter and some of these tweets had geospatial coordinates (latitude and longitude) attached to them. By combing Storm and Esper I wanted to produce a list of tweets per country over some small time frame, say 10 seconds or so. In order to achieve this I had to find the name of country corresponding to the latitude and longitude attached to each tweet. I used two different services for this, Geonames and Google Maps API. Let’s examine both!

Geonames

Using the webservice provided Geonames was very easy. For example if latitude is 55.704093 and longitude is 13.193582 a GET request to http://ws.geonames.org/findNearbyPlaceName?lat=55.704093&lng=13.193582 will then return a XML document as such:


  
    Lund
    Lund
    55.70584
    13.19321
    2693678
    SE
    Sweden
    P
    PPLA2
    0.19536
  

In Java we can use the REST Assured to easily retrieve the country name:

String countryName = get("http://ws.geonames.org/findNearbyPlaceName?lat={lat}&lng={long}", latitude, longitude).
                      xmlPath().getString("geonames.geoname.countryName");

As you can probably see we can do a lot of other interesting stuff with the data we received from Geonames but let’s ignore that for now.

Google Maps API

Google also provide a webservice that can be used to get the country name from a geolocation. Assuming we’re using the same coordinates as in the previous example we can make a GET request to this resource: http://maps.googleapis.com/maps/api/geocode/json?latlng=55.704093,13.193582&sensor=false. It will return a pretty large JSON document that looks something similar to this:

{
   "results":[
      {
         "address_components":[
            {
               "long_name":"6",
               "short_name":"6",
               "types":[
                  "street_number"
               ]
            },
            {
               "long_name":"Kyrkogatan",
               "short_name":"Kyrkogatan",
               "types":[
                  "route"
               ]
            },
            {
               "long_name":"Lund",
               "short_name":"Lund",
               "types":[
                  "locality",
                  "political"
               ]
            },
            {
               "long_name":"Lund",
               "short_name":"Lund",
               "types":[
                  "administrative_area_level_2",
                  "political"
               ]
            },
            {
               "long_name":"Skåne County",
               "short_name":"Skåne County",
               "types":[
                  "administrative_area_level_1",
                  "political"
               ]
            },
            {
               "long_name":"Sweden",
               "short_name":"SE",
               "types":[
                  "country",
                  "political"
               ]
            },
            {
               "long_name":"22222",
               "short_name":"22222",
               "types":[
                  "postal_code"
               ]
            },
            {
               "long_name":"Lund",
               "short_name":"Lund",
               "types":[
                  "postal_town"
               ]
            }
         ],
         "formatted_address":"Kyrkogatan 6, 222 22 Lund, Sweden",
         "geometry":{
            "location":{
               "lat":55.7040564,
               "lng":13.1936853
            },
            "location_type":"ROOFTOP",
            "viewport":{
               "northeast":{
                  "lat":55.7054053802915,
                  "lng":13.1950342802915
               },
               "southwest":{
                  "lat":55.7027074197085,
                  "lng":13.1923363197085
               }
            }
         },
         "types":[
            "street_address"
         ]
      },
      {
         "address_components":[
            {
               "long_name":"Lund Domkyrkan",
               "short_name":"Lund Domkyrkan",
               "types":[
                  "bus_station",
                  "transit_station",
                  "establishment"
               ]
            },
          }
	...
}

Again we can use REST Assured to find the country name, although this time it’ll be a little more complex:

String countryName = get("http://maps.googleapis.com/maps/api/geocode/json?latlng={lat},{long}&sensor=false", latitude, longitude).
                      jsonPath().getString("results.address_components.flatten().find { it.types.flatten().contains('country') }?.long_name");

Since the JSON document contains several address_components elements we start by flattening them all into a single list (using results.address_components.flatten()). Next we need to find the first types element of address_components that contains the word country. REST Assured uses the Groovy GPath syntax which means we can do find { it.types.flatten().contains('country') }. Again we make use of the flatten function to transform the nested types elements into a flat list. Next we use the safe navigation operator (?.) from Groovy to finally retrieve the name of the country (long_name).

Conclusion

Both Geonames and the Google Maps API makes it easy to find the country name for a given geolocation. Both services have a limit on how many requests you can make per time unit and that’s the reason why I had to use (and alter between) two services to begin with.

This Post Has 5 Comments

  1. Hello!
    Nice work. Do you have github for the project? I have a similar case where I have to integrate Storm and Esper.
    It would be nice if you can share your work.
    Thanks.
    Florin

      1. Hello!
        Many thanks for sharing this project. Regarding the twitter, is fine for me. I’m interested in the Esper-Storm integration.

  2. not working for me, how to use it.

Leave a Reply

Close Menu