package Weather::NWS::NDFDgen; use warnings; use strict; use LWP::Simple; use SOAP::DateTime; use Readonly; use Class::Std; =pod =head1 NAME Weather::NWS::NDFDgen - Object interface to the NWS NDFDgen Web Service. =head1 VERSION Version 0.02 =cut our $VERSION = '0.02'; =pod =head1 SYNOPSIS use Weather::NWS::NDFDgen; my $NDFDgen = Weather::NWS::NDFDgen->new(); my $NDFDgen = Weather::NWS::NDFDgen->new( 'Product' => 'Time-Series', 'Latitude' => 42, 'Longitude' => -88, 'Weather Parameters' => { 'Maximum Temperature' => 1, 'Minimum Temperature' => 0, }, ); my $latitude = 42; $NDFDgen->set_latitude($latitude); $latitude = $NDFDgen->get_latitude(); my $longitude = -88; $NDFDgen->set_longitude($longitude); $longitude = $NDFDgen->get_longitude(); my $product = 'Time-Series'; $NDFDgen->set_product($product); $product = $NDFDgen->get_product(); my $start_time = scalar localtime; $NDFDgen->set_start_time($start_time); $start_time = $NDFDgen->get_start_time(); my $end_time = scalar localtime; $NDFDgen->set_end_time($end_time); $end_time = $NDFDgen->get_end_time(); $NDFDgen->set_weather_parameters( 'Maximum Temperature' => 1, 'Minimum Temperature' => 0, ); my @weather_parameters = $NDFDgen->get_weather_parameters( 'Maximum Temperature', 'Minimum Temperature', ); my $xml = $NDFDgen->get_forecast_xml(); my $xml = $NDFDgen->get_forecast_xml( 'Product' => 'Time-Series', 'Latitude' => 42, 'Longitude' => -88, 'Weather Parameters' => { 'Maximum Temperature' => 1, 'Minimum Temperature' => 0, }, ); my @products = $NDFDgen->get_available_products(); my @weather_parameters = $NDFDgen->get_available_weather_parameters(); =cut =pod =head1 NDFDgen The NDFDgen web service is provided by the National Weather Service as part of their National Digital Forecast Database. Official NWS documentation for the XML web service can be found at L. The service allows for users to request custom weather forecasts based on the following parameters. =head2 Latitude The latitude of the point for which you want NDFD data. For Northern latituded, this will be a positive decimal value, such as 42.011. =head2 Longitude The longitude of the point for which you want NDFD data. For Western longitudes, this will be a negative decimal value, such as -88.81. =head2 Product There are two products presented by NDFDgen. These are the time-series and glance products. The time-series product returns all data between the start and end times for the selected weather parameters. The glance product returns all data between the start and end times for maximum temperature, minimum temperature, cloud cover, weather and weather icons elements. For this object, 'Time-Series' and 'Glance' are used to identify the products. =head2 Start Time The beginning time for which you want NDFD data. The format for this date is and XSD DateTime, which is represented like '2004-01-01T00:00:00'. You can really pass almost any date format in as the start time though because the date is passed through L before use. =head2 End Time The ending time for which you want NDFD data. The format for this date is and XSD DateTime, which is represented like '2004-01-01T00:00:00'. You can really pass almost any date format in as the end time though because the date is passed through L before use. =head2 Weather Parameters You can request any set of multiple weather parameters. These parameters are listed below. The definitions are taken from the NWS at L. =head3 Maximum Temperature Maximum temperature is the daytime max or the overnight min temperature. Verifying observations are deduced via a comprehensive algorithm that examines reported max/min and hourly temperatures. Daytime is defined as 0700-1900 Local Standard Time, and overnight as 1900-0800 Local Standard Time. =head3 Minimum Temperature Minimum temperature is the daytime max or the overnight min temperature. Verifying observations are deduced via a comprehensive algorithm that examines reported max/min and hourly temperatures. Daytime is defined as 0700-1900 Local Standard Time, and overnight as 1900-0800 Local Standard Time. =head3 3 Hourly Temperature 3 Hourly temperature is the expected temperature valid for the indicated hour. =head3 Dewpoint Temperature Dewpoint temperature is the expected dew point temperature for the indicated hour. =head3 Apparent Temperature Apparent temperature is the perceived temperature derived from either a combination of temperature and wind (Wind Chill), or temperature and humidity (Heat Index) for the indicated hour. Apparent temperature grids will signify the Wind Chill when temperatures fall to 50 °F or less, and the Heat Index when temperatures rise above 80 °F. Between 51 and 80 °F the Apparent Temperature grids will be populated by the ambient air temperature. =head3 12 Hour Probability of Precipitation 12 hour probability of precipitation is defined as the likelihood, expressed as a percent, of a measurable precipitation event (1/100th of an inch or more ) during the 12-hour valid period. The 12-hour periods begin and end at 0000 and 1200 UTC. =head3 Liquid Precipitation Amount Liquid precipitation amount is the total amount of expected liquid precipitation during a 6-hour period. The 6-hour periods begin and end at 0600, 1200, 1800, and 0000 UTC. =head3 Cloud Cover Amount Cloud cover amount is the expected amount of all opaque clouds (in percent) covering the sky for the indicated hour. =head3 Snowfall Amount Snowfall amount is the expected total accumulation of new snow during a 6-hour period. The 6-hour periods begin and end at 0600, 1200, 1800, and 0000 UTC. =head3 Wind Speed Wind speed is the expected sustained 10 meter wind speed for the indicated hour. =head3 Wind Direction Wind direction is the expected sustained 10 meter wind direction for the indicated hour, using 36 points of a compass. =head3 Weather Weather is the expected weather (precipitating or non-precipitating) valid at the indicated hour. Precipitating weather includes type, probability, and intensity information. In cases of convective weather, coverage may be substituted for probability. =head3 Wave Height Wave height (significant) is the average height (trough to crest) of the one-third highest waves for the indicated 12-hour period. The 12-hour periods begin and end at 0000 and 1200 UTC. =head3 Weather Icons Weather icons are links to images from the NWS that illustrate weather conditions at specific time points. =head3 Relative Humidity Relative humidity is the expected Relative Humidity (RH) for the indicated hour. RH is derived from the associated Temperature and Dew Point grids for the indicated hour. =cut Readonly my $SERVICE => 'http://www.weather.gov/forecasts/xml/DWMLgen/wsdl/ndfdXML.wsdl'; Readonly my %NAME_TO_ARGUMENT => ( 'Latitude' => 'latitude', 'Longitude' => 'longitude', 'Product' => 'product', 'Start Time' => 'startTime', 'End Time' => 'endTime', 'Weather Parameters' => 'weatherParameters', ); Readonly my @ARGUMENTS => keys %NAME_TO_ARGUMENT; Readonly my %NAME_TO_PRODUCT => ( 'Time-Series' => 'time-series', 'Glance' => 'glance', ); Readonly my @PRODUCTS => keys %NAME_TO_PRODUCT; Readonly my %NAME_TO_WEATHER_PARAMETER => ( 'Maximum Temperature' => 'maxt', 'Minimum Temperature' => 'mint', '3 Hourly Temperature' => 'temp', 'Dewpoint Temperature' => 'dew', 'Apparent Temperature' => 'appt', '12 Hour Probability of Precipitation' => 'pop12', 'Liquid Precipitation Amount' => 'qpf', 'Cloud Cover Amount' => 'sky', 'Snowfall Amount' => 'snow', 'Wind Speed' => 'wspd', 'Wind Direction' => 'wdir', 'Weather' => 'wx', 'Wave Height' => 'waveh', 'Weather Icons' => 'icons', 'Relative Humidity' => 'rh', ); Readonly my @WEATHER_PARAMETERS => keys %NAME_TO_WEATHER_PARAMETER; Readonly my $DEFAULT_PRODUCT => 'Time-Series'; Readonly my $DEFAULT_START_TIME => ConvertDate( scalar localtime ); =pod =head1 METHODS =cut { my %forecaster : ATTR; my %forecast_xml : ATTR; my %default_latitude : ATTR; my %default_longitude : ATTR; my %default_product : ATTR; my %default_start_time : ATTR; my %default_end_time : ATTR; my %default_weather_parms : ATTR; =pod =head2 BUILD (new) Constructor for new NDFDgen objects. If called with no parameters, it will return a new object initialized with the 'Time-Series' product and the current time as the start time. All other parameters are left unintialized. Values can be provided for 'Latitude', 'Longitude', 'Product', 'Start Time', 'End Time', and 'Weather Parameters'. =cut sub BUILD { my ( $self, $ident, $arg_ref ) = @_; my %args = %{$arg_ref}; map { $default_weather_parms{$ident}{$_} = 0 } @WEATHER_PARAMETERS; $self->set_latitude( $args{'Latitude'} || undef ); $self->set_longitude( $args{'Longitude'} || undef ); $self->set_product( $args{'Product'} || $DEFAULT_PRODUCT ); $self->set_start_time( $args{'Start Time'} || $DEFAULT_START_TIME ); $self->set_end_time( $args{'End Time'} || undef ); if ( $args{'Weather Parameters'} and ref $args{'Weather Parameters'} ) { $self->set_weather_parameters( %{ $args{'Weather Parameters'} } ); } } =pod =head2 set_latitude Sets the latitude for the object. This is a decimal value. =cut sub set_latitude { my ( $self, $new_latitude ) = @_; return $default_latitude{ ident $self} = $new_latitude; } =pod =head2 get_latitude Returns the latitude stored in the object. =cut sub get_latitude { my ($self) = @_; return $default_latitude{ ident $self}; } =pod =head2 set_longitude Sets the longitude for the object. This is a decimal value. =cut sub set_longitude { my ( $self, $new_longitude ) = @_; return $default_longitude{ ident $self} = $new_longitude; } =pod =head2 get_longitude Returns the longitude stored in the object. =cut sub get_longitude { my ($self) = @_; return $default_longitude{ ident $self}; } =pod =head2 set_product Sets the product for the object. This is either 'Time-Series' or 'Glance'. =cut sub set_product { my ( $self, $new_product ) = @_; die("Invalid product ($new_product)") unless ( grep { /^${new_product}$/ } @PRODUCTS ); return $default_product{ ident $self} = $new_product; } =pod =head2 get_product Returns the product stored in the object. =cut sub get_product { my ($self) = @_; return $default_product{ ident $self}; } =pod =head2 set_start_time Sets the start time for the object. =cut sub set_start_time { my ( $self, $new_start_time ) = @_; return unless $new_start_time; return $default_start_time{ ident $self} = ConvertDate($new_start_time); } =pod =head2 get_start_time Gets the start time stored in the object. =cut sub get_start_time { my ($self) = @_; return $default_start_time{ ident $self}; } =pod =head2 set_end_time Sets the end time for the object. =cut sub set_end_time { my ( $self, $new_end_time ) = @_; return unless $new_end_time; return $default_end_time{ ident $self} = ConvertDate($new_end_time); } =pod =head2 get_end_time Gets the end time stored in the object. =cut sub get_end_time { my ($self) = @_; return $default_end_time{ ident $self}; } =pod =head2 set_weather_parameters Sets the weather parameters for the object. These parameters are passed in as a list of key-value pairs where the key is the weather parameter and the value is a 1 or 0 indicating wether or not the parameter is going to be requested or not. =cut sub set_weather_parameters { my ( $self, @params ) = @_; return unless @params; return unless $#params % 2; my %params = @params; while ( my ( $parameter, $value ) = each %params ) { die("Invalid weather parameter ($parameter)") unless ( grep { /^$parameter$/ } @WEATHER_PARAMETERS ); die("Invalid value ($value) for weather parameter ($parameter)") unless ( $value =~ /^[01]$/ ); } my $stored_params = $default_weather_parms{ ident $self}; while ( my ( $parameter, $value ) = each %params ) { $stored_params->{$parameter} = $value; } return @params; } =pod =head2 get_weather_parameters Returns the requested weather parameters stored in the object as key-value pairs where the key is the weather parameter and the value is a 1 or 0 indicating wether or not the parameter is going to be requested or not. A list of parameter names can be passed to this method so that only those parameters are returned. If no arguments are passed to this method, all parameters will be returned. =cut sub get_weather_parameters { my ( $self, @params ) = @_; my $stored_params = $default_weather_parms{ ident $self}; return %{$stored_params} unless @params; my @results; for my $parameter (@params) { die("Invalid weather parameter ($parameter)") unless ( grep { /^$parameter$/ } @WEATHER_PARAMETERS ); push @results, $parameter, $stored_params->{$parameter}; } return @results; } =pod =head2 get_available_products Return a list of all products available through this service. =cut sub get_available_products { my ($self) = @_; return @PRODUCTS; } =pod =head2 get_available_weather_parameters Return a list of all weather parameters that can be requested through this service. =cut sub get_available_weather_parameters { my ($self) = @_; return @WEATHER_PARAMETERS; } =pod =head2 get_forecast_xml Return the NWS NDFD XML as described in L. The data returned depends on the state of the NDFDgen object at the time of the call to this method. Any parameters can be overridden by being passed in as arguments to this method. =cut sub get_forecast_xml { my ( $self, %args ) = @_; my ($ident) = ident $self; my ( $latitude, $longitude, $product, $start_time, $end_time, %weather_params ); die("Latitude required") unless $latitude = $args{'Latitude'} || $default_latitude{$ident}; die("Longitude required") unless $longitude = $args{'Longitude'} || $default_longitude{$ident}; die("Product required") unless $product = $args{'Product'} || $default_product{$ident}; die("Start time required") unless $start_time = $args{'Start Time'} || $default_start_time{$ident}; die("End time required") unless $end_time = $args{'End Time'} || $default_end_time{$ident}; %weather_params = %{ $default_weather_parms{$ident} }; if ( exists $args{'Weather Parameters'} ) { while ( my ( $param, $value ) = each %{ $args{'Weather Parameters'} } ) { die("Invalid weather parameter ($param) found") unless exists $weather_params{$param}; $weather_params{$param} = $value; } } my $url = 'http://www.weather.gov/forecasts/xml/sample_products/browser_interface/ndfdXMLclient.php?'; $url .= '&lat=' . $latitude; $url .= '&lon=' . $longitude; $url .= '&product=' . $NAME_TO_PRODUCT{$product}; $url .= '&begin=' . $start_time if $start_time; $url .= '&end=' . $end_time if $end_time; for my $param ( keys %weather_params ) { if ( $weather_params{$param} ) { $url .= '&' . $NAME_TO_WEATHER_PARAMETER{$param} . '=' . $NAME_TO_WEATHER_PARAMETER{$param}; } } $forecast_xml{$ident} = get $url; return $forecast_xml{$ident}; } } =pod =head1 AUTHOR Josh McAdams, C<< >> =head1 BUGS Please report any bugs or feature requests to C, or through the web interface at L. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes. =head1 SUPPORT You can find documentation for this module with the perldoc command. perldoc Weather::NWS::NDFDgen You can also look for information at: =over 4 =item * AnnoCPAN: Annotated CPAN documentation L =item * CPAN Ratings L =item * RT: CPAN's request tracker L =item * Search CPAN L =back =head1 ACKNOWLEDGEMENTS =head1 COPYRIGHT & LICENSE Copyright 2006 Josh McAdams, all rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut 1; # End of Weather::NWS::NDFDgen