Yahoo weather component

By Arash Hemmat (arash.hemmat)
Yahoo! developer network provides a RSS based API to access weather information for locations all around the world. The component has a method named get_weather($locationCode,$degreeUnit) that retrieves the weather information for the given location code and outputs the data in an easy to use array. Please read "http://developer.yahoo.com/weather/" for more information about the location code and degree unit. I will provide a tutorial as soon as possible.

Component Class:

Download code <?php 
/* Yahoo! Weather API component for Cakephp
 * @author Arash Hemmat www.hemmat.biz
 * @version 1.2  some bugs has been fixed.
 * @license http://www.opensource.org/licenses/mit-license.php
 * Special thanks to "Ed Eliot" (http://www.ejeliot.com/blog/38) ,I used his code for this component.
 */

/*
 * Yahoo weather component class
 */
class yahooWeatherComponent extends Object
{
    
/*
     * @var bool
     */
    
var $controller true;

    
/*
     * @var array
     */
    
var $errors=array();

    
/*
     * @var string
     */
    
var $content;

    
/*
     * The url of Yahoo! weather API
     * @var string
     */
    
var $WEATHER_API_URL='http://weather.yahooapis.com/forecastrss?p=';

    
/*
     * THe address for images, you need to change this if you want to use your own images.
     * @var string
     */
    
var $WEATHER_IMAGE_BASE='http://l.yimg.com/us.yimg.com/i/us/we/52/';
    
    
/*
     * initialization
     * @param bool $controller
     */
    
function startup(&$controller)
    {
        
$this->controller =& $controller;
    }

    
/*
     * Get the weather information for given location code
     * @param string $locationCode a code that represent a location in Yahoo weather system
     * @param string $degreeUnit it could be f: Fahrenheit or c: Celsius
     * @param bool $translate this parametr indicates if the result should be translated of not
     * @return array|bool $result
     */
    
function get_weather($locationCode,$degreeUnit='c')
    {
        if(
$this->content file_get_contents($this->WEATHER_API_URL.$location_code.'&u='.$degreeUnit))
        {
            
$result['location']=$this->GetTagAttributes('yweather:location');
            
$result['condition']=$this->GetTagAttributes('yweather:condition');
            
$result['units']=$this->GetTagAttributes('yweather:units');
            
$result['wind']=$this->GetTagAttributes('yweather:wind');
            
$result['atmosphere']=$this->GetTagAttributes('yweather:atmosphere');
            
$result['astronomy']=$this->GetTagAttributes('yweather:astronomy');
            
$result['geo:lat']=$this->GetTagValue('geo:lat');
            
$result['geo:long']=$this->GetTagValue('geo:long');
            
$result['link']=explode('*',$this->GetTagValue('link'));
            
$result['pubDate']=$this->GetTagValue('pubDate');
            
$result['description']=$this->GetTagValue('description');
            
$result['image_url']=$this->WEATHER_IMAGE_BASE.$result['condition']['code'].'.gif';
            
$forecasts=$this->GetTagAttributes('yweather:forecast');
            
$result['tonight_forecast']=$forecasts[0];
            
$result['tonight_forecast']['image_url']=$this->WEATHER_IMAGE_BASE.$result['tonight_forecast']['code'].'.gif';        
            
$result['tomorrow_forecast']=$forecasts[1];
            
$result['tomorrow_forecast']['image_url']=$this->WEATHER_IMAGE_BASE.$result['tomorrow_forecast']['code'].'.gif';
            return 
$result;
        }
        else
        {
            
$this->errors[]='Unable to connect to yahoo api.';
            return 
false;
        }
    }

    
/*
     * @param string $sTag
     * @return bool
     */
    
function GetTagValue($sTag)
    {
        
$aMatches = array();
            
        if (
preg_match("/<$sTag>([^<]*)<\/$sTag>/i"$this->content$aMatches))
        {
            
$aResult = array();
            
$aResult['value'] = $aMatches[1];
            return 
trim($aMatches[1]);
        }
        return 
false;
    }

    
/*
     * @param string $sTag
     * @return string $aResult
     */
    
function GetTagAttributes($sTag)
    {
        
$aMatches = array();
            
        if (
preg_match_all("/<$sTag([^\/]*)\/>/i"$this->content$aMatches))
        {
            
$aResult = array();

            for (
$i 0$i count($aMatches[1]); $i++)
            {
                
$aSubMatches = array();

                if (
preg_match_all("/([^=]+)=\"([^\"]*)\"/i"$aMatches[1][$i], $aSubMatches))
                {
                    for (
$j 0$j count($aSubMatches[1]); $j++)
                    {
                        
$aResult[$i][trim($aSubMatches[1][$j])] = trim($aSubMatches[2][$j]);
                    }
                }
            }
            
$iNumResults count($aResult);
            if (
$iNumResults 1)
            {
                return 
$aResult;
            } elseif (
$iNumResults == 1)
            {
                return 
$aResult[0];
            }
        }
        return 
false;
    }
}
?>

 

Comments 586

CakePHP Team Comments Author Comments
 

Comment

1 Just what I needed

Thanks for this, a few things to note;

There is a typo in the get_weather function;

if($this->content =file_get_contents( $this->WEATHER_API_URL.$location_code.'&u='.$degreeUnit))
$location_code should be $locationCode.

Plus the first regex for the GetTagAttributes function has a slight issue ...

preg_match_all("/<$sTag([^\/]*)\/>/i", $this->content,$aMatches)
This will ignore any yweather:forecast with a '/' in the them such as text="Rain/Snow".

Looking at the XML that the Yahoo RSS feed dishes out, there doesn't seem to be any need to strip '/' from the code for the GetTagAttributes function so I have this working ok using a simpler regex as follows;

"/<$sTag(.*)\/>/i"
Thanks again.
Posted Jan 7, 2008 by Andy Christie
 

Comment

2 BUGS FIXED

Thanks for this, a few things to note;

There is a typo in the get_weather function;

if($this->content =file_get_contents( $this->WEATHER_API_URL.$location_code.'&u='.$degreeUnit))
$location_code should be $locationCode.

Plus the first regex for the GetTagAttributes function has a slight issue ...

preg_match_all("/<$sTag([^\/]*)\/>/i", $this->content,$aMatches)
This will ignore any yweather:forecast with a '/' in the them such as text="Rain/Snow".

Looking at the XML that the Yahoo RSS feed dishes out, there doesn't seem to be any need to strip '/' from the code for the GetTagAttributes function so I have this working ok using a simpler regex as follows;

"/<$sTag(.*)\/>/i"
Thanks again.
Thanks for your tips, I have fixed the bug in locationCode.
Currently I prefer not to change that regex.
Thanks for your tips, I appreciate your help.
Posted Jan 13, 2008 by Arash Hemmat
 

Comment

3 regex

Thanks for this component. I had to change the regex too, but thanks to Andy Christie I simply could copy/paste his regex.

Also, I added a function to the component that creates a small table with today's, tonight's and tomorrows weather: Regrettably I cannot add that code here without screwing up the layout of that code.
Posted Oct 13, 2009 by Michiel Boertje
 

Comment

4 Caching

I have made some changes to the get_weather function to include caching.

function get_weather() {
        // Set our cache for 1 hour
        Cache::set(array('duration' => '+1 hour'));
        // Check if we have anything already cached
        $weather = Cache::read('weather-' . Configure::read('weather.city'));
        // If we do, then read it into $this->data
        if($weather !== false) {
            $this->content = unserialize(Cache::read('weather-' . Configure::read('weather.city')));
        }
        // If we don't, fetch it from Yahoo, put it into $this->data, and then cache it
        else {
            $this->content = file_get_contents($this->WEATHER_API_URL.Configure::read('weather.city').'&u='.Configure::read('weather.units'));
            Cache::write('weather-' . Configure::read('weather.city'), serialize($this->content));
        }
        
        // Take all the info from Yahoo and put into a usable array
        if($this->content) {
            $result['location']=$this->GetTagAttributes('yweather:location');
            $result['condition']=$this->GetTagAttributes('yweather:condition');
            $result['units']=$this->GetTagAttributes('yweather:units');
            $result['wind']=$this->GetTagAttributes('yweather:wind');
            $result['atmosphere']=$this->GetTagAttributes('yweather:atmosphere');
            $result['astronomy']=$this->GetTagAttributes('yweather:astronomy');
            $result['geo:lat']=$this->GetTagValue('geo:lat');
            $result['geo:long']=$this->GetTagValue('geo:long');
            $result['link']=explode('*',$this->GetTagValue('link'));
            $result['pubDate']=$this->GetTagValue('pubDate');
            $result['description']=$this->GetTagValue('description');
            $forecasts=$this->GetTagAttributes('yweather:forecast');
            $result['tonight_forecast']=$forecasts[0];
            $result['tomorrow_forecast']=$forecasts[1];
            // code 3200 is Yahoo's not available code, so if we get it, clear the cache
            if($result['condition'] == '3200') {
                Cache::delete('weather-' . Configure::read('weather.city'));
            }
            return $result;
        } else {
            $this->errors[]='Unable to connect to yahoo api.';
            return false;
        }
    }
Posted Nov 16, 2009 by Eric Friesen
 

Comment

5 Some changes I forgot

I also realized that I have set my city and units via cakes Configure::write() method.

To go back to the default, change Configure::read('weather.city') to $locationCode and Configure::read('weather.units') to $degreeUnit
Posted Nov 16, 2009 by Eric Friesen
 

Comment

6 Caching

Eric thank you for sharing, you are right having some sort of caching is necessary here however I think caching should be optional so I'll update the code to have caching feature just like you showed us but it will be optional. Thanks again for your contribution.
Posted Nov 17, 2009 by Arash Hemmat