Code covered by the BSD License  

Highlights from
Yahoo! Weather

from Yahoo! Weather by Skynet
Get location specific weather data from Yahoo! Weather.

yweather.m
function[data]=yweather(location,unit)

%YWEATHER Get location specific weather data from Yahoo! Weather.
%
% YWEATHER(LOCATION) queries the Yahoo! Weather RSS feed for the specified
% location(s), and returns a struct containing the queried information.
%
% The first input argument LOCATION can be one or more (U.S.) ZIP Codes or
% Location IDs. It can be a string, a vertical char array, or a vertical
% cell array of strings. To determine s Location ID, visit
% http://weather.yahoo.com/ and browse or search for the relevant
% location(s).
%
% The second argument UNIT is optional, and can be either 'f' for
% Fahrenheit or 'c' for Celsius. It can be a single char string, a
% vertical char array, or a vertical cell array of strings. Yahoo!
% Weather's default value for UNIT is 'f'.
%
% For more information about the RSS feeds and the data contained within
% them, visit http://developer.yahoo.com/weather/.
%
% Data is acquired in XML format, after which it is converted into a struct
% using the xml_parse function. This function is available in the XML
% Toolbox by Marc Molinari on the MATLAB Central File Exchange (File ID 
% 4278). The URL to it is:
%   http://www.mathworks.com/matlabcentral/fileexchange/loadFile.do?objectId=4278&objectType=file
%
% EXAMPLES:
%
%   Example 1: Using a ZIP Code.
%   yweather('10001') returns a 1x1 struct having fields (among others):
%
%           yweather_location.city: 'New York'
%     yweather_atmosphere.humidity: 50
%       yweather_astronomy.sunrise: '6:06 am'
%     item.yweather_condition.temp: 35
%     item.yweather_condition.date: 'Thu, 16 Mar 2006 2:51 am EST'
%
%   Example 2: Using a Location ID and (optional) unit.
%   yweather('UKXX0085','c') returns a 1x1 struct.
%
%   Example 3: Using a cell array of three locations.
%   yweather({'90028';'14850';'CAXX0301'}) returns a 3x1 struct.
%
%   Example 4: Using a char array of two ZIP Codes.
%   yweather(['56711';'33040']) returns a 2x1 struct.
%
%   Example 5: Using a cell array of three locations, in degrees Celsius.
%   yweather({'98381';'USCA0715';'04652'},'c') returns a 3x1 struct.
%
%   Example 6: Using a cell array of two locations, in variable units.
%   yweather({'27949';'UKXX0061'},{'f';'c'}) returns a 2x1 struct.
%
% REMARKS:
%
%   - This function does not include any caching features, so conservative
%     use of the service, perhaps no more than once per hour
%     per location, is warranted. Refer to the corresponding
%     'ttl' field values and documentation.
%   - As the feed layout is updated by Yahoo! over time, the layout of
%     the output struct should automatically be correspondingly updated.
%     Unexpected changes to the feed may however prevent this function from
%     working predictably.
%   - This function has been tested with version 2005-04-20 of the XML
%     Toolbox.
%
% VERSION: 20070108
% MATLAB VERSION: 7.3.0.267 (R2006b)
%
% See also URLREAD, REGEXP, REGEXPREP, CELLFUN, STRUCTFUN.

%{
VERSION HISTORY:
20070108: - Removed extra line breaks.
          - Added hyperlinks to relevant error messages.
20060318: - Added implicit support for char and cell array inputs.
20060317: - Original version.

KEYWORDS:
atmosphere, climate, meteorology, temperature, weather, yahoo
%}

%**************************************************************************

%% Convert input argument LOCATION to cellstr as relevant

if ischar(location)
    location=cellstr(location);
elseif ~iscellstr(location)
    error('The input argument LOCATION is invalid.')
end

%% Call main subfunction as relevant

switch nargin
    case 1
        data=cellfun(@yweather_main,location);
    case 2
        if ischar(unit)
            unit=cellstr(unit);
        end
        if numel(location)>1 && numel(unit)==1
            unit=repmat(unit,size(location));
        end
        data=cellfun(@yweather_main,location,unit);
    otherwise
        error('An incorrect number of input arguments has been supplied.')
end

%**************************************************************************

%% Main subfunction

function[data]=yweather_main(location,unit)

%% Declare URL and parameters

%Declare url
url='http://xml.weather.yahoo.com/forecastrss';

%Conditionally declare parameters
switch nargin
    case 1
        params={'p',location};
    case 2
        params={'p',location,'u',unit};
end
clear unit

%% Resiliently read URL

tries_max=3;
wait_time_base=.75;

for tries=1:tries_max;
    [data,status]=urlread(url,'get',params);
    if status==1
        break
    end
    if tries==tries_max
        error(['The URL ''<a href="',url,'">',url,'</a>'' could not be',...
               ' accessed (with the relevant parameters). Ensure that ',...
               'the Internet connection is active.'])
    end
    pause(tries*wait_time_base)
end
clear tries* status wait_time_base ...
      url params

%% Check for invalid returned data

if ~isempty(strfind(data,'<title>City not found</title>'))
    error(['''',location,''' is not a valid Location ID.',...
           ' Ensure that a valid ZIP Code or Location ID is used,',...
           ' and retry.'])
end
clear location

%% Convert yweather code to XML (for convenient parsing to struct)

expr='<yweather:(?<category>\w+) (?<keys_values>[^>]+)/>';
data=regexprep(data,expr,'<yweather:$1>$2</yweather:$1>');

expr='<yweather:\w+>(?<keys_values>[^>]+)</yweather:\w+>';
dc1=regexp(data,expr,'match');

expr='(?<key>\w+)="(?<value>[^"]+)" ';
dc2=regexprep(dc1,expr,'<$1>$2</$1>');

data=regexprep(data,dc1,dc2);
clear expr dc*

%% Parse and store <description> CDATA value as HTML

expr='<description><!\[CDATA\[(?<description>.+)]]></description>';
description=regexp(data,expr,'names','once');
description=description.description;
clear expr

%% Preprocess XML for parsing

%Rename <item> tag to something else (<itemdata>) to prevent parsing error
data=regexprep(data,'<(?<slash>[/]?)item>','<$1itemdata>');

%Replace colon in relevant (yweather* and geo*) tags to underscore for
%compatibility with struct field naming conventions
expr='<(?<slash>[/]?)(?<tag_left_part>\w+):(?<tag_right_part>\w+)>';
data=regexprep(data,expr,'<$1$2_$3>');
clear expr

%Convert multiple <yweather_forecast> sets into a single set
data=regexprep(data,'</yweather_forecast>\n{1}<yweather_forecast>','');

%% Confirm presence of xml_parse function

% This cell can optionally be commented

if isempty(which('xml_parse'))
    fe.url='http://www.mathworks.com/matlabcentral/fileexchange/';
    fe.xt.url=[fe.url,'loadFile.do?objectId=4278&objectType=file'];
    error(['The function xml_parse is not available. Ensure that the <',...
           'a href="',fe.xt.url,'">XML Toolbox</a> by Marc Molinari, a',...
           'vailable on the <a href="',fe.url,'">MATLAB Central File E',...
           'xchange</a>, is installed. The URL to it is <a href="',...
           fe.xt.url,'">',fe.xt.url,'</a>.'])
end

%% Parse XML to struct

%Disable warning, and parse
wstate=warning('off','all');
data=xml_parse(data);
warning(wstate);
clear wstate

%% Postprocess struct

%Simplify struct
data=data.channel;

%Rename <itemdata> back to <item> (in struct)
data.item=data.itemdata;
data=rmfield(data,'itemdata');

%Store previously parsed <description> CDATA HTML value in struct
data.item.description=description;
clear description

%% Convert numbers from strings to doubles

% This cell can optionally be commented

data=structfun(@cnsd,data,'UniformOutput',false);

%**************************************************************************

%% Subfunction to convert numbers from strings to doubles

function[field]=cnsd(field)

if ischar(field) && ~isnan(str2double(field))
    field=str2double(field);
elseif isstruct(field)
    for i=1:numel(field)
        field(i)=structfun(@cnsd,field(i),'UniformOutput',false);
    end
end

%**************************************************************************

Contact us at files@mathworks.com