Yahoo weather component

This article is also available in the following languages:
By 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:

<?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

  • Posted 12/07/10 08:49:28 AM
    This component should be placed in [cakephp root]/app/controller/components/yahoo_Weather.php

    In your controller you will need to add:
    var $components = 'yahooWeather';
    .....
    $this->set('yahooWeather', $this->yahooWeather->get_weather( $locationCode ,'c'));

    Where the $locationCode can be any of the codes in this link:
    http://www.edg3.co.uk/snippets/weather-location-codes/
    And do whatever you need to with $yahooWeather in your view

    Hope it helps!
    Rafa
  • Posted 11/16/09 06:52:55 PM
    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 11/17/09 03:14:13 AM
      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 11/16/09 12:35:36 PM
    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 10/13/09 12:28:33 PM
    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 01/07/08 09:04:14 AM
    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 01/13/08 11:36:20 AM
      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.

Comments are closed for articles over a year old