package Weather::Com::Location; use 5.006; use strict; use warnings; use Carp; use Time::Local; use Weather::Com::Cached; use Weather::Com::Units; use Weather::Com::CurrentConditions; use Weather::Com::Forecast; use base "Weather::Com::Cached"; our $VERSION = sprintf "%d.%03d", q$Revision: 1.3 $ =~ /(\d+)/g; #------------------------------------------------------------------------ # Constructor #------------------------------------------------------------------------ sub new { my $proto = shift; my $class = ref($proto) || $proto; my %parameters = (); # parameters provided by new method if ( ref( $_[0] ) eq "HASH" ) { %parameters = %{ $_[0] }; } else { %parameters = @_; } unless ( $parameters{location_id} ) { die "You need to provide a location id!\n"; } # set some parameters to sensible values for a pure location # object $parameters{current} = 0; $parameters{forecast} = 0; # creating the SUPER instance my $self = $class->SUPER::new( \%parameters ); $self->{ID} = $parameters{location_id}; $self->{NAME} = $parameters{location_name}; $self->{DEBUG} = $parameters{debug}; # the weather data will be initialized when the first call on # data is performed $self->{HEAD} = undef; $self->{WEATHER} = undef; $self->{CONDITIONS} = undef; $self->{FORECAST} = undef; $self->{LOCALTIME} = undef; $self->{SUNRISE} = undef; $self->{SUNSET} = undef; # last update will be used to trigger automatic refresh of # location data $self->{LSUP} = time(); bless( $self, $class ); # init object, add timezone to ARGS $self->{ARGS}->{lang} = $parameters{language} || 'en'; return $self; } # end new() #------------------------------------------------------------------------ # refresh weather data #------------------------------------------------------------------------ # this calls refresh if weather data is not initialized yet sub refresh { my $self = shift; if ( !$self->{WEATHER} || $self->_update ) { $self->{WEATHER} = $self->get_weather( $self->{ID} ); $self->_debug("Weather data refreshed!"); } return 1; } #------------------------------------------------------------------------ # access location data #------------------------------------------------------------------------ sub id { my $self = shift; return $self->{ID}; } sub name { my $self = shift; return $self->{NAME}; } sub units { my $self = shift; $self->refresh(); unless ( $self->{HEAD} ) { $self->{HEAD} = Weather::Com::Units->new(); } $self->{HEAD}->update( $self->{WEATHER}->{head} ); return $self->{HEAD}; } sub timezone { my $self = shift; unless ( $self->{WEATHER} ) { $self->{WEATHER} = $self->get_weather( $self->{ID} ); } $self->{ARGS}->{zone} = $self->{WEATHER}->{loc}->{zone}; return $self->{ARGS}->{zone}; } sub latitude { my $self = shift; $self->refresh(); return $self->{WEATHER}->{loc}->{lat}; } sub longitude { my $self = shift; $self->refresh(); return $self->{WEATHER}->{loc}->{lon}; } # localtime will be calculated because it does not make # any sense to used a cached time as current local time of # some location sub localtime { my $self = shift; $self->refresh(); unless ( $self->{LOCALTIME} ) { $self->{LOCALTIME} = Weather::Com::DateTime->new( $self->timezone() ); } return $self->{LOCALTIME}; } sub localtime_ampm { carp("Use of deprecated method 'localtime_ampm()'!"); carp("Please use 'localtime()->time_ampm()' instead."); my $self = shift; $self->refresh(); return $self->localtime()->time_ampm(); } sub sunrise { my $self = shift; $self->refresh(); unless ( $self->{SUNRISE} ) { $self->{SUNRISE} = Weather::Com::DateTime->new( $self->timezone() ); } $self->{SUNRISE}->set_time( $self->{WEATHER}->{loc}->{sunr} ); return $self->{SUNRISE}; } sub sunrise_ampm { carp("Use of deprecated method 'sunrise_ampm()'!"); carp("Please use 'sunrise()->time_ampm()' instead."); my $self = shift; $self->refresh(); return $self->sunrise()->time_ampm(); } sub sunset { my $self = shift; $self->refresh(); unless ( $self->{SUNSET} ) { $self->{SUNSET} = Weather::Com::DateTime->new( $self->timezone() ); } $self->{SUNSET}->set_time( $self->{WEATHER}->{loc}->{suns} ); return $self->{SUNSET}; } sub sunset_ampm { carp("Use of deprecated method 'sunset_ampm()'!"); carp("Please use 'sunset()->time_ampm()' instead."); my $self = shift; $self->refresh(); return $self->sunset()->time_ampm(); } sub current_conditions { my $self = shift; $self->refresh(); unless ( $self->{CONDITIONS} ) { $self->{CONDITIONS} = Weather::Com::CurrentConditions->new( $self->{ARGS} ); } return $self->{CONDITIONS}; } sub forecast { my $self = shift; $self->refresh(); unless ( $self->{FORECAST} ) { $self->{FORECAST} = Weather::Com::Forecast->new( $self->{ARGS} ); } return $self->{FORECAST}; } #------------------------------------------------------------------------ # internal methods go here #------------------------------------------------------------------------ sub _update { my $self = shift; # idea for check if now is one or more days after last update: # 1. transform last update to 00:00:00 of last updated date in # local time of location # 2. get 00:00:00 of today in local time of location # If both in epoc are equal, no update is needed, else we'll get # the new location information. my @lsup = gmtime( $self->timezone() * 3600 + $self->{LSUP} ); $lsup[0] = 0; $lsup[1] = 0; $lsup[2] = 0; my $local_epoc_lsup = timegm(@lsup); $self->{LSUP} = time(); my @now = gmtime( time() + ( $self->timezone() * 3600 ) ); $now[0] = 0; $now[1] = 0; $now[2] = 0; my $local_epoc_now = timegm(@now); if ( $local_epoc_now > $local_epoc_lsup ) { $self->_debug("should refresh location cache...\n"); return 1; } return 0; } 1; __END__ =pod =head1 NAME Weather::Com::Location - class representing one location and its weather =head1 SYNOPSIS #!/usr/bin/perl -w use Weather::Com::Finder; # you have to fill in your ids from weather.com here my $PartnerId = 'somepartnerid'; my $LicenseKey = 'mylicense'; my %weatherargs = ( 'partner_id' => $PartnerId, 'license' => $LicenseKey, ); my $finder = Weather::Com::Finder->new(%weatherargs); # if you want an array of locations: my @locations = $finder->find('Heidelberg'); # or if you prefer an arrayref: my $locations = $finder->find('Heidelberg'); foreach my $location (@locations) { print "Found weather for city: ", $location->name(), "\n"; print "The city is located at: ", $location->latitude(), "deg N, ", $location->longitude(), "deg E\n"; print "Local time is ", $location->localtime()->time(), "\n"; print "Sunrise will be/has been at ", $location->sunrise()->time(), "\n"; } =head1 DESCRIPTION Using I objects is the way to access weather (and some location) information for one specific location (city). You get I objects by using a finder object (see L). I is a subclass of I. An instance of this class will update itself corresponding to the caching rules any time one of its methods is called. =head1 CONSTRUCTOR =head2 new(hash or hashref) The constructor will usually not be used directly because you get ready to use location objects by using a finder. If you ever want to instantiate location objects on your own, you have to provide the same configuration hash or hashref to the constructor you usually would provide to the C method of I. In addition it is necessary to add a hash element C to this config hash. The C has to be a valid I location id. =head1 METHODS =head2 id() Returns the location id used to instantiate this location. =head2 name() Returns the name of the location as provided by I. =head2 current_conditions() Returns a I object containing the current conditions of the location. The I object is instantiated with the first call of the C method. Please refer to L for further details. =head2 forecast() Returns a I object. Please refer to L for further details. =head2 latitude() Returns the latitude of the location. =head2 longitude() Returns the longitude of the location. =head2 localtime() Returns a Weather::Com::DateTime object containing the local time of the location. This value is evaluated each time you call this method. We do not use the value returned from I here because it does not make any sence to use a cached value to show the current time. =head2 localtime_ampm() B Returns the local time of the location. The time is returned in the format C. To get a 24 hour format use C instead. Sample: 10:30 PM =head2 sunrise() Returns a Weather::Com::DateTime object containing the time of sunrise. =head2 sunrise_ampm() B Returns the time of sunrise in 12 hour format (see C for details). =head2 sunset() Returns a Weather::Com::DateTime object containing the time of sunset. =head2 sunset_ampm() B Returns the time of sunset in 12 hour format (see C for details). =head2 timezone() Returns the timezone offset to GMT (without respecting daylight savings time). =head2 units() Returns a I object. Please refer to L for further details. =head1 SEE ALSO See also documentation of L, L, L. =head1 AUTHOR Thomas Schnuecker, Ethomas@schnuecker.deE =head1 COPYRIGHT AND LICENSE Copyright (C) 2004-2009 by Thomas Schnuecker This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. The data provided by I and made accessible by this OO interface can be used for free under special terms. Please have a look at the application programming guide of I (http://www.weather.com/services/xmloap.html) =cut