Easy Yahoo! Maps and GeoRSS with symfony

by Dave Dash 26Apr06

GeoRSS is an extension of RSS that incorporates geographic data (i.e. latitude/longitude coordinates). This is useful for plotting any data that might need to be placed on a map.

While building out the reviewsby.us map, I decided to use the Yahoo! Maps API versus the Google Maps API because I wanted to gain some familiarity with another API.

It was worth trying Yahoo!'s API. First of all, reviewsby.us has addresses for restaurants and Yahoo! provides a simple Geocoding REST service. This made it easy for me to convert street addresses to latitude and longitude pairs (even though this wasn't required as we'll soon see).[1] The real selling point of Yahoo! was the [GeoRSS] functionality. I can extend an RSS feed (which [symfony] generates quite easily) to add latitude or longitude points (or even the street address), direct my Yahoo! map to the feed and voila, all the locations in that feed are now on the map, and when I click on them, the RSS item is displayed. That cut down on a lot of development time.

Yahoo's GeoRSS

The GeoRSS format that Yahoo uses is fairly simple to grasp if you know what an RSS feed looks like. Here's a typical RSS feed:

<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
    <channel>
        <title>Latest restaurants</title>
        <link>http://reviewsby.us/</link>
        <description>A list of the latest restaurants posted to my reviewsby.us</description>
        <language>en</language>
        <item>
            <title>Bubba Gump Shrimp Co. Restaurant and Market</title>
            <description>A *Forest Gump* themed restaurant.  Featuring a large selection of seafood items.</description>
            <link>http://reviewsby.us/restaurant/bubba-gump-shrimp-co-restaurant-and-market</link>
            <guid>25</guid>
            <pubDate>Sun, 23 Apr 2006 08:04:00 -0700</pubDate>
        </item>
        <item>
            <title>Famous Dave's Barbeque</title>
            <link>http://reviewsby.us/restaurant/famous-daves-barbeque</link>
            <guid>24</guid>
            <pubDate>Wed, 19 Apr 2006 19:58:08 -0700</pubDate>
        </item>
    </channel>
</rss>  

The <item /> in this example is a restaurant. To turn this into a GeoRSS feed, we only need to change a few things:

<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#">
    <channel>
        <title>Latest restaurants' locations</title>
        <link>http://reviewsby.us/</link>
        <description>A geocoded list of the latest restaurants' locations posted to my reviewsby.us</description>
        <language>en</language>
        <item>
            <title>Bubba Gump Shrimp Co. Restaurant and Market (Mall of America (Bloomington, MN))</title>
            <link>http://reviewsby.us/restaurant/bubba-gump-shrimp-co-restaurant-and-market/location/mall-of-america</link>
            <guid>18</guid>
            <pubDate>Sun, 23 Apr 2006 08:08:19 -0700</pubDate>
            <geo:lat>44.85380173</geo:lat>
            <geo:long>-93.24040222</geo:long>
        </item>
    </channel>
</rss>

We just added xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" as an attribute to the <rss/> tag and added <geo:lat /> and <geo:long /> tags to any given item. If you'd rather use address, city, state and zip fields instead of latitude and longitude coordinates, the Yahoo! Georss page will tell you how.

[symfony] and Geodata

I knew full well that Yahoo! Maps did not require me to have everything in latitude longitude coordinates, but I felt that from an efficiency standpoint, it made more sense for me to convert them once using a geocoder and then Yahoo! wouldn't have to translate them later. Also, I'm trying to think ahead when more than just Minneapolis restaurants become a part of the reviewsby.us site, I now have an easy way of determining distance between a user's home and a given restaurant. Also, if Yahoo! Maps doesn't work out, I can use these coordinates in other mapping systems.

Extending the model

Models in [symfony] lend themselves to being easily extended. So we can easily take a model for a location:

<table name="location" phpName="Location">
    <column name="id" type="integer" primaryKey="true" autoIncrement="true"/>
    <column name="restaurant_id" type="integer" />
    <foreign-key foreignTable="restaurant">
        <reference local="restaurant_id" foreign="id"/>
    </foreign-key>
    <column name="stripped_title" type="varchar" size="255" />
    <column name="name" type="varchar" size="255" />
    <column name="address" type="varchar" size="255" />
    <column name="city" type="varchar" size="128" />
    <column name="state" type="varchar" size="16" />
    <column name="zip" type="varchar" size="9" />
    <column name="phone" type="varchar" size="16" />
    <column name="approved" type="boolean" />
    <column name="updated_at" type="TIMESTAMP" />
    <column name="created_at" type="TIMESTAMP" />
</table>

and simply add two columns inside the <table/>:

<column name="latitude" type="float" size="10" scale="8"/>
<column name="longitude" type="float" size="10" scale="8"/>

Easy! However, we don't want to have to set the latitude and longitude by hand each time we update a Location. So first we write a function that takes an address and converts it to latitude/longitude. I placed mine in a myTools.class.php in my lib folder:

class myTools
{
    public static function getLatLng($address, $city = null, $state = null, $zip = null)
    {
        $url = sfConfig::get('app_yahoo_geocode');
        $query['appid'] = sfConfig::get('app_yahoo_app_id');
        $query['street'] = $address;
        $query['city'] = $city;
        $query['state'] = $state;
        $query['zip'] = $zip;
        $query['output'] = 'php'; 
        $url .= '?' . http_build_query($query); 
        $response = @file_get_contents($url);
        if ($response) {
            $response = unserialize($response);
            return $response['ResultSet']['Result'];
        }
        return null;
    }
}

Defined in my app.yml is app_yahoo_geocode and my app_yahoo_app_id. myTools::getLatLng() queries the Yahoo! REST service and returns the coordinates that Yahoo! delivers. Note that the generated query string includes output=php. Yahoo! supports serializing output as PHP instead of XML. This can save a bundle of time over decoding XML.

So now let's look at our Location.php and override its inherited save function:

public function save($con = null)
{
    // save latitude and longitude
    $locdata = myTools::getLatLng($this->getAddress(), $this->getCity(), $this->getState(), $this->getZip());
    if ($locdata) 
    {
        $this->setLatitude($locdata['Latitude']);
        $this->setLongitude($locdata['Longitude']);
    }
    parent::save($con);
}

If you stop here, you'll at least now know how to add latitude and longitude coordinates to an object automagically.

Producing a GeoRSS feed

[symfony] very easily will allow you to generate an RSS feed. How do we create a [GeoRSS] feed? Just extend the sfFeed class. Rather than instantiating a feed like this:

$feed = sfFeed::newInstance('rss201rev2');

We do this:

$feed = sfFeed::newInstance('geoRSS');  

And then create an sfGeoRssFeed.class.php and we're done. We've created a GeoRSS feed fairly easily. Comb through sfGeoRssFeed.class.php and compare it to the sfRss201rev2Feed.class.php, you'll notice it's not that different and that it's fairly easy to extend the sfFeed plugin for [symfony].

Adding your feed to a Yahoo! Map.

Adding a GeoRSS feed to Yahoo! maps is simple. Before I embedded the RSS feed into my Yahoo! Map I was prepared to write an algorithm to cluster only on the points in my RSS feed, lucky for me (and you), Yahoo! Maps does this automatically. One pitfall you might reach during development is that Yahoo! Maps must be able to reach your GeoRSS feed. My development machine is my personal laptop, so this didn't work so well until I uploaded to a publically accessible staging server. The maps worked like a charm as you can see.

Conclusion

Yahoo! Maps are very powerful, and [symfony] is up to the task. I hope you found this tutorial useful. If you have any trouble, let me know. I hope your next meal is a good one.

  1. Using Yahoo! Maps wasn't a requisite of using the Yahoo! Geocoder.
    The datasets that were returned could have been used to populate a Google Map just as easily.


Where am I?

This is a single entry in the weblog.

"Easy Yahoo! Maps and GeoRSS with symfony" is filed under programming and symfony. It was published in April 2006.

April 2006
M T W T F S S
    May »
 12
3456789
10111213141516
17181920212223
24252627282930

Tags

&& && && && && && &&

need more help

If you found our tutorials and articles to be useful, but are still looking for more hands on help, consider hiring us. Find out more about how Spindrop can help you.

 

5 Responses to “Easy Yahoo! Maps and GeoRSS with symfony”


  1. 1 Hugo Posted August 26th, 2008 - 11:05 am

    Absolutly awesome ! That’s why I’m looking after for a while to realize my studies project. So, thanks for this nice tutorial, which will help me a lot and will save me a lot of time :)

    Hugo.

Who's linking?

  1. 1 Spindrop » Blog Archive » Google Analytics Pingback on May 26th, 2006
    "[...] Other than that, I like the potential that this will offer me. This combined with Google Sitemaps will provide ... "
  2. 2 Spindrop » Blog Archive » Spindrop objectives Pingback on Jun 5th, 2006
    "[...] With all my efforts, at the very least, I know I’ll have something to show for. For one, I ... "
  3. 3 Spindrop » Blog Archive » More maps, better presentation and prices Pingback on Jul 4th, 2006
    "[...] The real exciting thing is maps. Applying the same principles from the Yahoo! Map tutorial, I added maps to ... "
  4. 4 Spindrop » Blog Archive » Dynamic Linking to Syndication Feeds with symfony Pingback on Jul 4th, 2006
    "[...] Since this is in the reviewsby.us layout.php, the latest feed and the latest GeoRSS feed (which we developed in ... "

Further Help

If you require more hands on assistance, we do offer affordable hands on support.

Leave a Reply


Comment guidelines: No spamming, no profanity, and no flaming. Inappropriate comments will be deleted outright.