Friday, January 19, 2007

Rflickr and Google maps mashup

So you've started with rails and now your looking for something else to do after all you didn't think that the book was to hard easy right? Gems are an awesome part of ruby they can add a good deal of functionality to your project. In this tutorials we are going to be using the rflickr gem not to be confused with the flickr gem; These two are complete polar opposites. Getting started you can use Maxx Dunn's tutorial for installing and activating the rflickr gem (if you don't have it installed already). Now that you have all that done lets begin to write some code. Since the rflickr gem doesn't have any built in geo functions we'll have to construct them ourselves. First we need create a new rails project so that we can start off with a nice clean canvas.

rails mynewproject


Now change directories to the mynewproject and create a new controller with an index page.


cd mynewproject

script/generate controller View index


Alright now that we have that completed, start writing some code after all that's what we came to do. Open up the view.rb file found in app/controller directory. We'll get the peliminaries out of the way first. Declare you variables which are API_KEY and SHARED_SECRET these are just so we don't have to write them over and over again. Then we set the flickr gem to required.


require 'flickr'

API_KEY = "your_api_key"

SHARED_SECRET = "your_secret"


Now we need to create the index actions. For the index we need to load in a flickr instance and get the proper authorizations.


flickr = Flickr.new("/tmp/flickr.cache", API_KEY, SHARED_SECRET)

flickr.auth.token


Now we get to write our own custom function using the xml and rflickr libraries not really too complicated. I'll try to explain what each line does. First lets define a new function that way everything is separated.


def GetLocation(photo)

end


Then we start adding more code in this function.


flickr = Flickr.new("/tmp/flickr.cache", API_KEY, SHARED_SECRET)

photo = photo.id if photo.class == Flickr::Photo


Now what we've done is made another instance to use just for this function then we added the photo.class to make sure we get a photo after all that's what we wanted right?



res = flickr.call_method('flickr.photos.geo.getLocation',

'photo_id'=>photo)


What we just did is added the method that we want to call and believe it or not it is that easy. Now that method will return an xml response which we can use. Which would look something like this.


<rsp stat="ok">

<photo id="269619243">

<location latitude="36.675303" longitude="-82.816829" accuracy="16"/>

</photo>

</rsp>


Now add the last bit of code.


lat = [ ]

res.elements['/photo'].each_element do |location|

att = location.attributes

lat << att['latitude'] + ", " + att['longitude']

end

return lat


And now that's the last bit of code we have to add. First we made a variable to contain the data then we have to put data into or it is useless. The att variable just makes our program look at the attributes instead of the content because with these xml files we don't have any content that's useful. So after we pick off the lat and lng we end it and return the lat variable which has now got our geo data in it. Lets add that function we just created into our index function. For now we'll just hard code the id in. To make this really functional we would load the id# from a text box on the page or something like that but I'm sure that you can figure out how to do that.


@loc = GetLocation('269619243')


Now go to the app/views/view/ directory and open up the index.rhtml. Delete all of the content inside of it. I prefer starting with a clean slate. I don't really need to explain all of this code just the ruby parts the google maps documentation does a pretty good job of explaining everything.


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"

"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">

<head>

<meta http-equiv="content-type" content="text/html; charset=utf-8"/>

<title>View</title>

<script src="http://maps.google.com/maps?file=api&amp;v=2&amp;key=ABQIAAAAUzR0Te-MXqdOV0HHbLuZzxTJQa0g3IQ9GZqIMmInSLzwtGDKaBQCm9xJHrKAuzvUhpgFvwlPeNxPnw"

type="text/javascript"></script>

<script type="text/javascript">

//<![CDATA[


function load() {

if (GBrowserIsCompatible()) {

var map = new GMap2(document.getElementById("map"));



<% for location in @loc %>

map.setCenter(new GLatLng(<%= location %>), 13);

map.addOverlay(new GMarker(<%= location %>));

<% end %>

}

}


//]]>

</script>

</head>

<body onload="load()" onunload="GUnload()">

<div id="map" style="width: 500px; height: 300px"></div>

<br />

<% for location in @loc %>

<%= location %> <br />

<% end %>

</body>

</html>


Ok I know if we were loading multiple locations we would want something fancier than what I've got but this tutorial is just to get you started by no means is it all you can do with it. So basically all our ruby does is set the map center to the location we gave it and put a marker there (I'm trying to keep this simple so you can follow that's why I just did one image at a time.). We'll boot up web brick and watch this thing run.


When your done your source for the view.rb file should look like this:


class ActivateController < ApplicationController


require 'flickr'


API_KEY = "7483873a88574d1ec1cdb94ee3ccf0b1"

SHARED_SECRET = "b08a4090e6b235a3"


def index

flickr = Flickr.new("/tmp/flickr.cache", API_KEY, SHARED_SECRET)

flickr.auth.token

@loc = GetLocation('269619243')

end


def GetLocation(photo)

flickr = Flickr.new("/tmp/flickr.cache", API_KEY, SHARED_SECRET)

photo = photo.id if photo.class == Flickr::Photo

res = flickr.call_method('flickr.photos.geo.getLocation',

'photo_id'=>photo)

lat = []

res.elements['/photo'].each_element do |location|

att = location.attributes

lat << att['latitude'] + ", " + att['longitude']

end

return lat

end


and the index.rhtml should look like this:


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">

<head>

<meta http-equiv="content-type" content="text/html; charset=utf-8"/>

<title>View</title>

<script src="http://maps.google.com/maps?file=api&amp;v=2&amp;key=your_api_key_here" type="text/javascript"></script>

<script type="text/javascript">


function load() {

if (GBrowserIsCompatible()) {

var map = new GMap2(document.getElementById("map"));



<% for location in @loc %>

map.setCenter(new GLatLng(<%= location %>), 13);

map.addOverlay(new GMarker(<%= location %>));

<% end %>

}

}


</script>

</head>

<body onload="load()" onunload="GUnload()">

<div id="map" style="width: 500px; height: 300px"></div>

<br />

<% for location in @loc %>

<%= location %> <br />

<% end %>

</body>

</html>


I hope you've enjoyed this tutorial and I look forward to writing more. Please send all of your feedback to chrisprayingmantis@yahoo.com.