Geotag Everywhere with Fire Eagle

A note from the editors: Since this article was written Yahoo! has retired the Fire Eagle service.

Location, they say, is everywhere. Everyone has one, all of the time. But on the web, it’s taken until this year to see the emergence of location in the applications we use and build.

The possibilities are broad. Increasingly, mobile phones provide SDKs to approximate your location wherever you are, browser extensions such as Loki and Mozilla’s Geode provide browser-level APIs to establish your location from the proximity of wireless networks to your laptop. Yahoo’s Brickhouse group launched Fire Eagle, an ambitious location broker enabling people to take their location from any of these devices or sources, and provide it to a plethora of web services. It enables you to take the location information that only your iPhone knows about and use it anywhere on the web.

That said, this is still a time of location as an emerging technology. Fire Eagle stores your location on the web (protected by application-specific access controls), but to try and give an idea of how useful and powerful your location can be — regardless of the services you use now — today’s 24ways is going to build a bookmarklet to call up your location on demand, in any web application.

Location Support on the Web

Over the past year, the number of applications implementing location features has increased dramatically. Plazes and Brightkite are both full featured social networks based around where you are, whilst Pownce rolled in Fire Eagle support to allow geotagging of all the content you post to their microblogging service. Dipity’s beautiful timeline shows for you moving from place to place and Six Apart’s activity stream for Movable Type started exposing your movements.

The number of services that hook into Fire Eagle will increase as location awareness spreads through the developer community, but you can use your location on other sites indirectly too.

Consider Flickr. Now world renowned for their incredible mapping and places features, geotagging on Flickr started out as a grassroots extension of regular tagging. That same technique can be used to start rolling geotagging in any publishing platform you come across, for any kind of content. Machine-tags (geo:lat= and geo:lon=) and the adr and geo microformats can be used to enhance anything you write with location information.

A crash course in avian inflammability

Fire Eagle is a location store. A broker between services and devices which provide location and those which consume it. It’s a switchboard that controls which pieces of your location different applications can see and use, and keeps hidden anything you want kept private. A blog widget that displays your current location in public can be restricted to display just your current city, whilst a service that provides you with a list of the nearest ATMs will operate better with a precise street address.

Even if your iPhone tells Fire Eagle exactly where you are, consuming applications only see what you want them to see. That’s important for users to realise that they’re in control, but also important for application developers to remember that you cannot rely on having super-accurate information available all the time. You need to build location aware applications which degrade gracefully, because users will provide fuzzier information — either through choice, or through less accurate sources.

Application specific permissions are controlled through an OAuth API. Each application has a unique key, used to request a second, user-specific key that permits access to that user’s information. You store that user key and it remains valid until such a time as the user revokes your application’s access. Unlike with passwords, these keys are unique per application, so revoking the access rights of one application doesn’t break all the others.

Building your first Fire Eagle app; Geomarklet

Fire Eagle’s developer documentation can take you through examples of writing simple applications using server side technologies (PHP, Python). Here, we’re going to write a client-side bookmarklet to make your location available in every site you use. It’s designed to fast-track the experience of having location available everywhere on web, and show you how that can be really handy. Hopefully, this will set you thinking about how location can enhance the new applications you build in 2009.

An oddity of bookmarklets

Bookmarklets (or ‘favlets’, for those of an MSIE persuasion) are a strange environment to program in. Critically, you have no persistent storage available. As such, using token-auth APIs in a static environment requires you to build you application in a slightly strange way; authing yourself in advance and then hardcoding the keys into your script.

Get started

Before you do anything else, go to http://fireeagle.com and log in, get set up if you need to and by all means take a look around. Take a look at the mobile updaters section of the application gallery and perhaps pick out an app that will update Fire Eagle from your phone or laptop.

Once that’s done, you need to register for an application key in the developer section. Head straight to /developer/create and complete the form. Since you’re building a standalone application, choose ‘Auth for desktop applications’ (rather than web applications), and select that you’ll be ‘accessing location’, not updating.

At the end of this process, you’ll have two application keys, a ‘Consumer Key’ and a ‘Consumer Secret’, which look like these:

Consumer Key
luKrM9U1pMnu
Consumer Secret
ZZl9YXXoJX5KLiKyVrMZffNEaBnxnd6M

These keys combined allow your application to make requests to Fire Eagle.

Next up, you need to auth yourself; granting your new application permission to use your location. Because bookmarklets don’t have local storage, you can’t integrate the auth process into the bookmarklet itself — it would have no way of storing the returned key. Instead, I’ve put together a simple web frontend through which you can auth with your application.

Head to Auth me, Amadeus!, enter the application keys you just generated and hit ‘Authorize with Fire Eagle’. You’ll be taken to the Fire Eagle website, just as in regular Fire Eagle applications, and after granting access to your app, be redirected back to Amadeus which will provide you your user tokens. These tokens are used in subsequent requests to read your location.

And, skip to the end…

The process of building the bookmarklet, making requests to Fire Eagle, rendering it to the page and so forth follows, but if you’re the impatient type, you might like to try this out right now. Take your four API keys from above, and drag the following link to your Bookmarks Toolbar; it contains all the code described below. Before you can use it, you need to edit in your own API keys. Open your browser’s bookmark editor and where you find text like ‘YOUR_CONSUMER_KEY_HERE’, swap in the corresponding key you just generated.

Get Location

Bookmarklet Basics

To start on the bookmarklet code, set out a basic JavaScript module-pattern structure:

var Geomarklet = function() {
	return ({
		callback: function(json) {},
		run: function() {}
	});
};
Geomarklet.run();

Next we’ll add the keys obtained in the setup step, and also some basic Fire Eagle support objects:

var Geomarklet = function() {
	var Keys = {
			consumer_key: 'IuKrJUHU1pMnu',
			consumer_secret: 'ZZl9YXXoJX5KLiKyVEERTfNEaBnxnd6M',
			user_token: 'xxxxxxxxxxxx',
			user_secret: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
	};
	var LocationDetail = {
			EXACT: 0,
			POSTAL: 1,
			NEIGHBORHOOD: 2,
			CITY: 3,
			REGION: 4,
			STATE: 5,
			COUNTRY: 6
	};
	var index_offset;
	return ({
		callback: function(json) {},
		run: function() {}
	});
};
Geomarklet.run();

The Location Hierarchy

A successful Fire Eagle query returns an object called the ‘location hierarchy’. Depending on the level of detail shared, the index of a particular piece of information in the array will vary. The LocationDetail object maps the array indices of each level in the hierarchy to something comprehensible, whilst the index_offset variable is an adjustment based on the detail of the result returned.

The location hierarchy object looks like this, providing a granular breakdown of a location, in human consumable and machine-friendly forms.

"user": {
		"location_hierarchy": [{
			"level": 0,
			"level_name": "exact",
			"name": "707 19th St, San Francisco, CA",
			"normal_name": "94123",
			"geometry": {
					"type": "Point",
					"coordinates": [ - 0.2347530752, 67.232323]
			},
			"label": null,
			"best_guess": true,
			"id": ,
			"located_at": "2008-12-18T00:49:58-08:00",
			"query": "q=707%2019th%20Street,%20Sf"
		},
		{
				"level": 1,
				"level_name": "postal",
				"name": "San Francisco, CA 94114",
				"normal_name": "12345",
				"woeid": ,
				"place_id": "",
				"geometry": {
						"type": "Polygon",
						"coordinates": [],
						"bbox": []
				},
				"label": null,
				"best_guess": false,
				"id": 59358791,
				"located_at": "2008-12-18T00:49:58-08:00"
		},
		{
				"level": 2,
				"level_name": "neighborhood",
				"name": "The Mission, San Francisco, CA",
				"normal_name": "The Mission",
				"woeid": 23512048,
				"place_id": "Y12JWsKbApmnSQpbQg",
				"geometry": {
						"type": "Polygon",
						"coordinates": [],
						"bbox": []
				},
				"label": null,
				"best_guess": false,
				"id": 59358801,
				"located_at": "2008-12-18T00:49:58-08:00"
			},
		}

In this case the first object has a level of 0, so the index_offset is also 0.

Prerequisites

To query Fire Eagle we call in some existing libraries to handle the OAuth layer and the Fire Eagle API call. Your bookmarklet will need to add the following scripts into the page:

When the bookmarklet is first run, we’ll insert these scripts into the document. We’re also inserting a stylesheet to dress up the UI that will be generated.

If you want to follow along any of the more mundane parts of the bookmarklet, you can download the full source code.

Rendering

This bookmarklet can be extended to support any formatting of your location you like, but for sake of example I’m going to build three common formatters that you’ll find useful for common location scenarios: Sites which already ask for your location; and in publishing systems that accept tags or HTML mark-up.

All the rendering functions are items in a renderers object, so they can be iterated through easily, making it trivial to add new formatting functions as your find new use cases (just add another function to the object).

var renderers = {
geotag: function(user) {
	if(LocationDetail.EXACT !== index_offset) {
			return false;
	}
	else {
		var coords =
			user.location_hierarchy[LocationDetail.EXACT].geometry.coordinates;
		return "geo:lat=" + coords[0] + ", geo:lon=" + coords[1];
	}
},
city: function(user) {
	if(LocationDetail.CITY < index_offset) {
		return false;
	}
	else {
		return user.location_hierarchy[LocationDetail.CITY - index_offset].name; 
	}						 
}

You should always fail gracefully, and in line with catering to users who choose not to share their location precisely, always check that the location has been returned at the level you require. Geotags are expected to be precise, so if an exact location is unavailable, returning false will tell the rendering aspect of the bookmarklet to ignore the function altogether.

These first two are quite simple, geotag returns geo:lat=-0.2347530752, geo:lon=67.232323 and city returns San Francisco, CA.

This final renderer creates a chunk of HTML using the adr and geo microformats, using all available aspects of the location hierarchy, and can be used to geotag any content you write on your blog or in comments:

html: function(user) {
	var geostring = '';
	var adrstring = '';
	var adr = [];		 
	adr.push('<p class="adr">');
	// city
	if(LocationDetail.CITY >= index_offset) {
		adr.push(
			'\n		 <span class="locality">'
		+ user.location_hierarchy[LocationDetail.CITY-index_offset].normal_name
		+ '</span>,'
		);
	}
	// county
	if(LocationDetail.REGION >= index_offset) {
		adr.push(
			'\n		 <span class="region">' 
		+ user.location_hierarchy[LocationDetail.REGION-index_offset].normal_name
		+ '</span>,'
			);
	}
	// locality
	if(LocationDetail.STATE >= index_offset) {
		adr.push(
			'\n		 <span class="region">'
		+ user.location_hierarchy[LocationDetail.STATE-index_offset].normal_name
		+ '</span>,'
		);
	}
	// country
	if(LocationDetail.COUNTRY >= index_offset) {
		adr.push(
			'\n		 <span class="country-name">'
		+ user.location_hierarchy[LocationDetail.COUNTRY-index_offset].normal_name
		+ '</span>'
		);
	}
	// postal
	if(LocationDetail.POSTAL >= index_offset) {
		adr.push(
			'\n		 <span class="postal-code">'
		+ user.location_hierarchy[LocationDetail.POSTAL-index_offset].normal_name
		+ '</span>,'
		);
	}
	adr.push('\n</p>\n');
	adrstring = adr.join('');
	if(LocationDetail.EXACT === index_offset) {
		var coords = 
			user.location_hierarchy[LocationDetail.EXACT].geometry.coordinates;
		geostring = '<p class="geo">'
			+'\n		<span class="latitude">'
			+ coords[0]
			+ '</span>;'
			+ '\n		 <span class="longitude">'
			+ coords[1]
			+ '</span>\n</p>\n';
	}
	return (adrstring + geostring);
}

Here we check the availability of every level of location and build it into the adr and geo patterns as appropriate. Just as for the geotag function, if there’s no exact location the geo markup won’t be returned.

Finally, there’s a rendering method which creates a container for all this data, renders all the applicable location formats and then displays them in the page for a user to copy and paste. You can throw this together with DOM methods and some simple styling, or roll in some components from YUI or JQuery to handle drawing full featured overlays.

You can see this simple implementation for rendering in the full source code.

Make the call

With a framework in place to render Fire Eagle’s location hierarchy, the only thing that remains is to actually request your location. Having already authed through Amadeus earlier, that’s as simple as instantiating the Fire Eagle JavaScript wrapper and making a single function call. It’s a big deal that whilst a lot of new technologies like OAuth add some complexity and require new knowledge to work with, APIs like Fire Eagle are really very simple indeed.

return {
	run: function() {
		insert_prerequisites();
		setTimeout(
			function() {
				var fe = new FireEagle(
					Keys.consumer_key,
					Keys.consumer_secret,
					Keys.user_token,
					Keys.user_secret
				);
				var script = document.createElement('script');
				script.type = 'text/javascript';
				script.src = fe.getUserUrl(
					FireEagle.RESPONSE_FORMAT.json,
					'Geomarklet.callback'
				);
				document.body.appendChild(script);
			},
			2000
		);
	},
	callback: function(json) {
		if(json.rsp && 'fail' == json.rsp.stat) {
			alert('Error ' + json.rsp.code + ": " + json.rsp.message);
		}
		else {
			index_offset = json.user.location_hierarchy[0].level;						 
			draw_selector(json);
		}
	}
};

We first insert the prerequisite scripts required for the Fire Eagle request to function, and to prevent trying to instantiate the FireEagle object before it’s been loaded over the wire, the remaining instantiation and request is wrapped inside a setTimeout delay.

We then create the request URL, referencing the Geomarklet.callback callback function and then append the script to the document body — allowing a cross-domain request.

The callback itself is quite simple. Check for the presence and value of rsp.status to test for errors, and display them as required. If the request is successful set the index_offset — to adjust for the granularity of the location hierarchy — and then pass the object to the renderer.

The result? When Geomarklet.run() is called, your location from Fire Eagle is read, and each renderer displayed on the page in an easily copy and pasteable form, ready to be used however you need.

Deploy

The final step is to convert this code into a long string for use as a bookmarklet. Easiest for Mac users is the JavaScript bundle in TextMate — choose Bundles: JavaScript: Copy as Bookmarklet to Clipboard. Then create a new ‘Get Location’ bookmark in your browser of choice and paste in.

Those without TextMate can shrink their code down into a single line by first running their code through the JSLint tool (to ensure the code is free from errors and has all the required semi-colons) and then use a find-and-replace tool to remove line breaks from your code (or even run your code through JSMin to shrink it down).

With the bookmarklet created and added to your bookmarks bar, you can now call up your location on any page at all. Get a feel for a web where your location is just another reliable part of the browsing experience.

Where next?

So, the Geomarklet you’ve been guided through is a pretty simple premise and pretty simple output. But from this base you can start to extend: Add code that will insert each of the location renderings directly into form fields, perhaps, or how about site-specific handlers to add your location tags into the correct form field in Wordpress or Tumblr? Paste in your current location to Google Maps? Or Flickr?

Geomarklet gives you a base to start experimenting with location on your own pages and the sites you browse daily.

The introduction of consumer accessible geo to the web is an adventure of discovery; not so much discovering new locations, but discovering location itself.

About the author

Ben Ward is a Front End Engineer at YDN — the Yahoo Developer Network. Until recently, he had worked in Yahoo’s Brickhouse group, where he wrangled HTML, CSS and JavaScript for fun things like Fire Eagle.

By night he put his efforts into the microformats.org community, working as an admininstrator and getting pedantic about semantics. He regularly speaks at conferences on microformats, is a credited specification author and active editor.

When not trying to make the internet better he’s slowly but surely settling into his new life in San Francisco, embracing an eclectic cultural combination of imported cheese, fine wines and Rock Band 2.

His blog is at http://ben-ward.co.uk.

Photo: Nathan Ward

More articles by Ben

Comments