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
%**************************************************************************