LearnGetting Started with Yahoo! GeoPlanet Explorer

Treehouse
writes on March 8, 2010

Where are you and what is around you?

Geolocation is a hot topic. Google just got the patent on geolocated advertising, mobile phones allow us to pinpoint ourselves on the planet and find things nearby and with augmented reality applications we can even find our way by filming our surrounding and finding hidden treasures by moving our mobile around. Using geolocation as a developer is quite easy, you can do a few things:

  • If you are building something in a social network, you can get the geographical location from the user’s profile.
  • If you work on a certain mobile platform (Android, WebOS, iPhone) you get APIs to detect the current location.
  • If that is not an option you can use the W3C geo location API in browsers that support it.
  • If everything else fails you can guess the visitor’s location from their IP number.

That is the where, but how to know what is around me?

Knowing the location is one thing, but what if you want to know more about the area? What about the geographical hierarchy? What part of the city/country are you in and which other geographical and administrative areas are nearby?

All of this has been available for you for quite a while. The GeoPlanet API and dataset released by Yahoo! has been out for a while but did not quite get the love from the mainstream developer crowd it deserves. The geo hackers, on the other hand already love it to bits and helped make it more accurate by providing feedback.

To make it a bit easier for you to understand what the GeoPlanet API allows you to do, I’ve put together the GeoPlanet Explorer – a tool that lets you explore all the GeoPlanet data in an interactive way:

The Geoplanet Explorer uses three tools: YQL for accessing and filtering data, YUI3 for the rich interaction and CSS layout, Yahoo Maps for display and PHP to glue all of that together. You can get the full source code on GitHub.

Simplifying matters using woeid

One big problem of geo location is ambiguity. If you have a location with latitude and longitude you can pinpoint it on the globe. You don’t know what it is though. The centre of a certain county, city and a point of interest could share the same latitude and longitude, but they are actually totally different things.

To fix this problem, Yahoo! a long time ago introduced the Where on Earth ID (or short woeid) which is a number that defines a place with its geographical location and describes what it is. For example my neighbourhood in London has the woeid of 36239 and that means I can get the following information from it when querying the GeoPlanet API:

  • Stoke Newington (Suburb):
    • Country: United Kingdom
    • WOEID: 36239
    • Administrative:
      • England (Country)
      • Greater London (County)
    • Localities:
      • London (Town)
      • Stoke Newington (Suburb)
    • Postal N16 (Postal Code)
    • Location (lat/lon): 51.561199, -0.082980
    • Bounding Box: NE 51.577190, -0.058070 SW 51.546692, -0.092380

The bounding box information is very useful for displaying this information on a map as it shows you which points need to be visible in order to see the whole place.

There is just something pretty about having a number instead of a lat/lon pair which is why woeid is rapidly becoming a standard. Flickr supported it for quite a while, Dopplr uses it under the hood, the Yahoo! Weather API understands it and soon Twitter will also use it for geolocating content in an unambiguous way (right now there is only support in the Twitter Trends API).

So how could you use this for your own products?

There are a lot of ways you can use the geo tools by Yahoo! in your own solutions – by far the easiest is using YQL to access the data. This, for example allowed me to write the addmap.js solution:

Using this you can can easily take the content of a page element with a certain ID and add a map of the locations it “talks about” to the document:

<script src="http://github.com/codepo8/geotoys/raw/master/addmap.js"></script>
<script>
addmap.config.mapkey = 'YOUR_API_KEY';
addmap.analyse('content');
</script>

The API key is needed for the Google maps, not for YQL. YQL in this case under the hood uses Placemaker which is an API that finds geographical content in URLs or texts.

YQL and the geo tools offer you some other interesting options:

Geographical locations in texts/urls/RSS feeds

By using the Placemaker table in YQL you can extract geographical locations from a texts or a source on the web:

select * from geo.placemaker where documentContent =
"Hi, I am Chris, I live in London but actually I am from Germany"
and documentType="text/plain" and appid=""

This finds both “London” and “Germany” as geographical locations. You will find places and references. Places are what has been found and references where it was found. The start and end elements describe the character location in the whole string.

Placemaker also takes feed and web site URIs and finds the locations in them for you. For example to see the locations mentioned in my portfolio page you can do the following:

select * from geo.placemaker where documentURL =
"http://icant.co.uk" and documentType="text/html" and appid=""

Again, you can check the result as an XML file. As this is an HTML document there is no start and end but the references are reported as XPATHs.

Feeds are also supported, so if you want to get the locations in the BBC News feed you can use the following:

select * from geo.placemaker where documentURL =
"http://newsrss.bbc.co.uk/rss/newsonline_uk_edition/front_page/rss.xml"
and documentType="text/rss" and appid=""

Verify the result as an XML at your leisure – this time the XPATHs point to the RSS items.

The easiest way to see the power of Placemaker is using GeoMaker which gives you a simple interface to do turn a text or URL into a map or microformats to embed into your documents:

Location by IP

Getting the geographical location of an IP is possible with the following YQL statement:

select * from geo.places where woeid in (
  select place.woeid from flickr.places where (lat,lon) in (
    select Latitude,Longitude from ip.location where ip = "123.23.23.33"
  )
)What we're doing here is using the IP2location API ,get the woeid from the latitude and longitude using the Flickr findByLatLon method and run the result through the geo API to get all the information - quite some work off your hands, isn't it? Without YQL you'd have to sign up for all these APIs and understand their inner workings to get the same information.

Getting location by IP is an old trick in the book of advertisers – you probably will have encountered ads like “meet singles in your city now” and see them changing when you went to another place – this is what they use to get this information. The accuracy of IP location is not very high though. It also can feel a bit creepy.

Location by Latitude and Longitude

Therefore it is much better to use a system that allows people to opt-in to tell their geographical location. There are services for that but we also have a W3C API built into browsers. Right now Firefox and Mobile Safari support it but it also cropped up in the latest developer build of Chrome.

Using the W3C location API is easy:

<script>
if(navigator.geolocation){
  navigator.geolocation.getCurrentPosition(function(position){
    var lat = position.coords.latitude;
    var lon = position.coords.longitude;
    alert('you are at' + lat +','+lon);
  },function(error){
    alert('Couldn't get your location :(');
  });
}
</script>

You test if the browser supports navigator.geolocation and if it does you use the getCurrentPosition() method to retrieve the current position. This will cause the browser to ask the visitor if they want to share their information – normally in an information bar on top of the viewport as shown in this screenshot:

The getCurrentPosition() method takes two parameters which are function to call in the success and the failure case – meaning that the visitor allowed you to get their location or not – or that there was some other error.

You can then use YQL once more to get the location from latitude and longitude – this time using the Flickr API method as the first one:

select * from geo.places where woeid in (
  select place.woeid from flickr.places where lat=51.5142271 and lon=-0.1289602
)

See the XML output here.

Putting it all together

Let’s end with an example script how you can use this. We use the W3C geolocation API to get a visitor’s location and if it isn’t available we fall back to using the IP. In any case, we use YQL to get the location information and the areas around the current location. The solution uses JavaScript except for retrieving the IP which is done in this case by PHP. You can see the full demo in action here. See the comments // for information about what is going on.

<script type="text/javascript" charset="utf-8">
<?php
  if ($_SERVER['HTTP_X_FORWARD_FOR']) {
   $ip = $_SERVER['HTTP_X_FORWARD_FOR'];
  } else {
   $ip = $_SERVER['REMOTE_ADDR'];
  }
  if(preg_match('/^[d+.?]+$/',$ip)){
    echo 'var IP = "' . $ip . '"';
  }
?>

// ^^
// This PHP block will get the current IP and assign it to a JavaScript
// variable - if it is the right format.

if(navigator.geolocation){
  navigator.geolocation.getCurrentPosition(
    function(position){
      getDataForLatLon(position.coords.latitude,
                       position.coords.longitude);
    },
    function(error){
      if(IP){
        getFromIP(IP);
      }
    });
} else{
  if(IP){
    getFromIP(IP);
  };
}

// ^^
// we test if the W3C location API is supported and if it is, we
// get the current lat and lon and call the appropriate method.
// if there was an error or the API is not available at all we call
// getFromIP.

function load(yql,cb){
  var src = 'http://query.yahooapis.com/v1/public/yql?q='+
            encodeURIComponent(yql) + '&format=json&callback=' + cb + '&'+
            'env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys';
  var head = document.getElementsByTagName('head')[0];
  var s = document.createElement('script');
  s.setAttribute('src',src);
  head.appendChild(s);
}

// ^^
// YQL is a web service that returns JSON. This allows us to create
// script nodes dynamically to get information from it. This function
// does that for us.

function getDataForLatLon(lat,lon){
  var yql = 'select * from geo.places where woeid in ('+
            'select place.woeid from flickr.places where lat='+
            lat + ' and  lon=' + lon + ')';
  load(yql,'foundLocation');
}
function foundLocation(o){
  if(o.query.results){
    var place = o.query.results.place
    var out = '<p>You are in ' + place.name + ' which is a '+
               place.placeTypeName.content + ' located at ('+
               place.centroid.latitude + ',' + place.centroid.longitude+
               ')</p>';
    document.getElementById('location').innerHTML = out;
    getBelongTos(place.woeid);
  }
}

// ^^
// If we can get a lat and lon from the API, we put together the right
// YQL statement. If we are successful we call the foundLocation() method
// which writes out the right HTML to the document and calls the
// getBelongTos() method

function getFromIP(ip){
  var yql = 'select * from geo.places where woeid in ('+
            'select place.woeid from flickr.places where (lat,lon) in('+
            'select Latitude,Longitude from ip.location'+
            ' where ip="'+ip+'"))';
  load(yql,'foundLocation');
}

// ^^
// If there was no luck with the W3C geo API we call this function using
// the IP we got from PHP

function getBelongTos(woeid){
  var yql = 'select * from geo.places.belongtos(0) where '+
            'member_woeid='+woeid;
  load(yql,'foundBelongs');
}
function foundBelongs(o){
  if(o.query.results){
    var out = '<p>Your location belongs to: ';
    var place = o.query.results.place;
    var info = [];
    for(var i=0;i<place.length;i++){
      var cur = place[i];
      info.push(cur.name + ' (' + cur.placeTypeName.content + ')');
    }
    var out = '<p>Your location belongs to: ' + info.join(', ') + '</p>';
    document.getElementById('location').innerHTML += out;
  }
}

// ^^
// If we found a location, then we call the belongtos method of the
// GeoPlanet API to get all the areas the current location belongs to.

</script>

In my case the result from the W3C geo location is:

You are in St. Giles’s which is a Suburb located at (51.516151,-0.125460)

Your location belongs to: WC1V 6 (Postal Code), London Borough of Camden (Local Administrative Area), 0207 London (Zone), Middlesex (Historical County), London (Town), Greater London (County), MMA London (MMA), South East England (Colloquial), DMA South East England (DMA), England (Country), England and Wales (Colloquial), Great Britain (Colloquial), United Kingdom (Country), Market United Kingdom and Ireland (Market), Europe/London (Time Zone), British Isles (Supername), Northern Europe (Supername), European Union (Supername), Western Europe (Supername), Europe (Continent), Eurasia (Supername), Earth (Supername)

Only using IP I get:

You are in Whitehall which is a Suburb located at (51.501831,-0.125760)

Your location belongs to: SW1A 2 (Postal Code), Westminster (Suburb), City of Westminster (Local Administrative Area), 0207 London (Zone), Middlesex (Historical County), London (Town), Greater London (County), MMA London (MMA), South East England (Colloquial), DMA South East England (DMA), England (Country), England and Wales (Colloquial), Great Britain (Colloquial), United Kingdom (Country), Market United Kingdom and Ireland (Market), Europe/London (Time Zone), British Isles (Supername), Northern Europe (Supername), European Union (Supername), Western Europe (Supername), Europe (Continent), Eurasia (Supername), Earth (Supername)

Quite a lot of information using a few lines of code, isn’t it? Geographical location is a very interesting topic and allows us to show much more useful content to our visitors. Using YQL and GeoPlanet it is pretty easy for you to do so – give it a go!

3 Responses to “Getting Started with Yahoo! GeoPlanet Explorer”

  1. mega_tux on October 2, 2013 at 10:57 am said:

    I think ip.location table is not available anymore 🙁

  2. One thing that bothers me about the Yahoo APIs is that the license says they are limited to non-commercial use. So if you want to add some features to your commercial application and take advantage of the Yahoo Maps API (or GeoPlanet for that matter) you can’t. At least not easily or affordably. Why? I’m sure this has been discussed elsewhere but I will ask again, feel free to ignore me. Why not make the service a commercially viable one? Have clear pricing points etc and have a free service along with a commercial one that allows people to buy a bundle of requests? Right now according to the license on the website it’s as if Yahoo only wants to support non-commercial applications?

  3. Really in depth & informative article Christian, I’ve been following you for a while, always with good comments & tips about stuff.

    I’m going to have a proper read through this later, I want to utilise “geo-ness” somehow but not sure yet, got a few idea up my sleeve though 🙂

Leave a Reply

You must be logged in to post a comment.

Want to learn more about Javascript?

Learn how to use JavaScript to add interactivity to websites.

Learn more