# The other side of the moon

/bb|[^b]{2}/
Never stop Grokking

## Tuesday, January 31, 2006

### Of microformats and geocoding

I'd been toying with the idea of adding geo data to my restaurant reviews. I thought it would be nice to have a map pointer right below the instructions for getting there. I started looking around for well estabilished methods to markup geo data in a blog post.

I came upon an article in linux journal that spoke about geotagging and geocoding for websites. It talks about ICBM and meta tags, which are great except for one thing. It goes into the page header, so can't be different for different sections of the page. It also talks about embedding the information in comments - which means that users and javascript can't easily read it, and about RDF/RSS feeds - which would be useful for my blog feed, but not for my blog itself.

I decided to try my own minimalistic markup, and came up with this:
   <address class="gmap" lat="yy" lon="xx" zoom="z">Some text</address>

Of course, I went through a couple of iterations to settle on this, and it was based largely on what the google maps api accepts.

A side note before I go on. The reason I chose google maps was because they provided aerial photos of a few major Indian cities, which is where most of my reviews are based. This turned out to be of no use though, because the aerial photos are not provided via the API. One has to visit Google Local to see them.

Ok, so this gave me the ability to easily add geo information to a post - just a single line. If I wanted to be really cool, I'd need to translate that to a map, so I started studying the google maps API, and after several iterations, came up with this:
   //! Add a marker with a callout containing the specified html   function addMarkerToMap(map, point, html)   {      var marker = new GMarker(point);      map.addOverlay(marker);      if(html)      {         marker.openInfoWindowHtml(html)         GEvent.addListener(marker, 'click', function() {marker.openInfoWindowHtml(html);});      }   }   window.onload=function()   {      var adr = document.getElementsByTagName("address");      for(var i=0; i<adr.length; i++)      {         if(adr[i].className == 'gmap')         {            // Grab HTML to put into callout            var html=adr[i].innerHTML;            var lat, lon, zoom;            lat=1*adr[i].getAttribute('lat');            lon=1*adr[i].getAttribute('lon');            zoom=parseInt(adr[i].getAttribute('zoom'));            adr[i].innerHTML = "";            // Build map and center on lat/lon            var map = new GMap(adr[i]);            map.addControl(new GSmallMapControl());            map.addControl(new GMapTypeControl());            var point = new GPoint(lon,lat);            map.centerAndZoom(point, zoom);            addMarkerToMap(map, point, html);         }      }   }

What it does is quite simple. After page load, it iterates through all the divs in the page, looking for divs with class 'gmap'. When it finds such a div, it looks at the lat, lon and zoom attributes (which are not standard HTML btw) of the div and uses that to draw the map.

I soon realised that I was only using zoom level 0, so dropped that attribute and hard coded it in.

I showed it to Nate a little later, and he mentioned that there was a microformat for geocoding. Had a look at it, and while it was slightly more verbose than my format, it achieved a little more, and wouldn't be much harder to parse.

Changed the div to this:
<address class="geo">Some text<br><abbr class="latitude" title="37.801324">N 37° 48.079</abbr> <br><abbr class="longitude" title="-122.424903">W 122° 25.494</abbr></address>

This shows up neatly, and I could change my javascript to accept both classes, 'gmap' as well as 'geo', and change the parsing to this:
             if(adr[i].className == 'geo')             {                var ab = adr[i].getElementsByTagName('abbr');                for(var j=0; j<ab.length; j++)                {                   if(ab[j].className == 'latitude')                      lat = 1*ab[j].title;                   else if(ab[j].className == 'longitude')                      lon = 1*ab[j].title;                }             }             else             {                lat=1*adr[i].getAttribute('lat');                lon=1*adr[i].getAttribute('lon');             }

The rest of the code remains the same.

It isn't too hard to replace Google maps with Yahoo! maps in this implementation. The parsing of the microformat is the heart of it all, after that it's just a matter of using your API of choice.

You can see both formats in action on my reviews of US restaurants. Note that one of the restaurants uses the old format that I've described above, and the other two use the geo microformat. I'll add more in time, and a few from the UK as well.

Update: Calvin Yu has written a Yahoo! Maps implementation of geo.

Update: Switched to use the <address> tag instead of a <div>. Just seems more semantic.

Update: I have a Yahoo! Maps version as well.

Anonymous

Kewl! I will use it on my http://nagpurbirds.org site

Anonymous

Well done Philip! This is a very cool script. I've added it to the "Implementations" section of the geo microformats specification.

Just a couple of quick comments/suggestions:

1. The <address> element doesn't actually mean "address" unfortunately - it is the most misnamed element in HTML4. It actually means "contact information for this specific page or portion thereof" - see the hCard FAQ (the first question and answer) for more explanation. In short: better to just use span or div tags for your geos, not address.

2. I took a look at your restaurant reviews and them seem quite well structured.

Consider marking them up in the hReview microformat as well. You'll note that when reviewing a business (like a restaurant), that you'll be able to markup the restaurant and its address as a complete hCard which then you can add a link using the Technorati contacts feed service to turn it into a vCard that users can click and automatically add to their address books.

Once again, great work, and I hope you find these suggestions helpful.

Thanks,

Tantek

Philip

Thanks Tantek. Went through a little debate in my head before choosing the <address> tag, and I wasn't very clear on it.

For my particular case, it meant that I'd have fewer elements returned by getElementsByTagName, and nothing else. I didn't think it was semantically incorrect, but looks like I didn't think of the exact definition of address.

I have a few doubts about hReview, which I'll probably bring up on irc sometime soon. It'll be a headache to go back and markup all of my entries, since the files aren't autogenerated.

Nate Koechley

Philip, this is slick. Thanks for the mention, and for sharing your experience (and code). I wish I could fast-forward 18 months and see what the web's gonna look like when all these little pieces and improvements start being commonplace. Oh well, 18 months isn't soooo long.

@Tantek: You're right, it's gotta be the most poorly named element around. A shame. I love the compactness of HTML4, but a /few/ more elements wouldn't have hurt ;)