File Coverage

blib/lib/Weather/YR/LocationForecast.pm
Criterion Covered Total %
statement 54 102 52.9
branch 0 12 0.0
condition 0 2 0.0
subroutine 18 25 72.0
pod n/a
total 72 141 51.0


line stmt bran cond sub pod time code
1             package Weather::YR::LocationForecast;
2 3     3   19 use Moose;
  3         4  
  3         13  
3 3     3   15089 use namespace::autoclean;
  3         9  
  3         20  
4              
5             extends 'Weather::YR::Base';
6              
7 3     3   2944 use DateTime;
  3         256263  
  3         114  
8 3     3   1822 use DateTime::Format::ISO8601;
  3         98912  
  3         188  
9              
10 3     3   1520 use Weather::YR::LocationForecast::DataPoint;
  3         12  
  3         133  
11 3     3   1699 use Weather::YR::LocationForecast::Day;
  3         10  
  3         164  
12              
13 3     3   1842 use Weather::YR::Model::Cloudiness;
  3         11  
  3         127  
14 3     3   1749 use Weather::YR::Model::DewPointTemperature;
  3         11  
  3         133  
15 3     3   1522 use Weather::YR::Model::Fog;
  3         13  
  3         157  
16 3     3   1904 use Weather::YR::Model::Humidity;
  3         9  
  3         129  
17 3     3   1870 use Weather::YR::Model::Precipitation::Symbol;
  3         8  
  3         140  
18 3     3   1622 use Weather::YR::Model::Precipitation;
  3         10  
  3         122  
19 3     3   1387 use Weather::YR::Model::Pressure;
  3         9  
  3         124  
20 3     3   1558 use Weather::YR::Model::Probability::Temperature;
  3         11  
  3         137  
21 3     3   1804 use Weather::YR::Model::Probability::Wind;
  3         11  
  3         141  
22 3     3   27 use Weather::YR::Model::Temperature;
  3         4  
  3         78  
23 3     3   1613 use Weather::YR::Model::WindDirection;
  3         11  
  3         145  
24 3     3   1772 use Weather::YR::Model::WindSpeed;
  3         12  
  3         2987  
25              
26             =head1 NAME
27              
28             Weather::YR::LocationForecast - Object-oriented interface to Yr.no's "location
29             forecast" API.
30              
31             =head1 DESCRIPTION
32              
33             Don't use this class directly. Instead, access it from the L<Weather::YR> class.
34              
35             =cut
36              
37             has 'status_code' => ( isa => 'Num', is => 'rw', required => 0 );
38              
39             has 'url' => ( isa => 'Str', is => 'ro', lazy_build => 1 );
40             has 'schema_url' => ( isa => 'Str', is => 'ro', lazy_build => 1 );
41             has 'datapoints' => ( isa => 'ArrayRef[Weather::YR::LocationForecast::DataPoint]', is => 'ro', lazy_build => 1 );
42             has 'days' => ( isa => 'ArrayRef[Weather::YR::LocationForecast::Day]', is => 'ro', lazy_build => 1 );
43              
44             has 'now' => ( isa => 'Weather::YR::LocationForecast::Day', is => 'ro', lazy_build => 1 );
45             has 'today' => ( isa => 'Weather::YR::LocationForecast::Day', is => 'ro', lazy_build => 1 );
46             has 'tomorrow' => ( isa => 'Weather::YR::LocationForecast::Day', is => 'ro', lazy_build => 1 );
47              
48             =head1 METHODS
49              
50             =head2 url
51              
52             Returns the URL to YR.no's location forecast service. This is handy if you
53             want to retrieve the XML from YR.no yourself;
54              
55             my $yr = Weather::YR->new(
56             lat => 63.590833,
57             lon => 10.741389,
58             );
59              
60             my $url = $yr->location_forecast->url;
61              
62             my $xml = My FancyHttpClient->new->get( $url );
63              
64             my $yr = Weather::YR->new(
65             xml => $xml,
66             tz => DateTime::TimeZone->new( name => 'Europe/Oslo' ),
67             );
68              
69             my $forecast = $yr->location_forecast;
70              
71             =cut
72              
73             sub _build_url {
74 0     0     my $self = shift;
75              
76 0           return 'http://api.yr.no/weatherapi/locationforecast/1.9/?lat=' . $self->lat . ';lon=' . $self->lon . ';msl=' . $self->msl;
77             }
78              
79             =head2 schema_url
80              
81             Returns the URL to YR.no' location forecast service XML schema. This is used
82             internally for validating the XML output from YR.no itself.
83              
84             =cut
85              
86             sub _build_schema_url {
87 0     0     return 'http://api.met.no/weatherapi/locationforecast/1.9/schema';
88             }
89              
90             =head2 datapoints
91              
92             Returns an array reference of L<Weather::YR::LocationForecast::DataPoint> instances.
93              
94             =cut
95              
96             sub _build_datapoints {
97 0     0     my $self = shift;
98              
99 0           my @datapoints = ();
100              
101 0 0         if ( my $xml_ref = $self->xml_ref ) {
102 0   0       my $times = $xml_ref->{weatherdata}->{product}->{time} || [];
103 0           my $datapoint = undef;
104              
105 0           foreach my $t ( @{$times} ) {
  0            
106              
107 0           my $from = $self->date_to_datetime( $t->{from}->{value} );
108 0           my $to = $self->date_to_datetime( $t->{to }->{value} );
109              
110 0 0         if ( $t->{location}->{temperature} ) {
    0          
111 0           my $loc = $t->{location};
112              
113 0 0         if ( defined $datapoint ) {
114 0           push( @datapoints, $datapoint );
115 0           $datapoint = undef;
116             }
117              
118 0           $datapoint = Weather::YR::LocationForecast::DataPoint->new(
119             from => $from,
120             to => $to,
121             lang => $self->lang,
122             type => $t->{datatype}->{value},
123              
124             temperature => Weather::YR::Model::Temperature->new(
125             from => $from,
126             to => $to,
127             lang => $self->lang,
128             celsius => $loc->{temperature}->{value}->{value},
129             ),
130              
131             wind_direction => Weather::YR::Model::WindDirection->new(
132             from => $from,
133             to => $to,
134             lang => $self->lang,
135             degrees => $loc->{windDirection}->{deg}->{value},
136             name => $loc->{windDirection}->{name}->{value},
137             ),
138              
139             wind_speed => Weather::YR::Model::WindSpeed->new(
140             from => $from,
141             to => $to,
142             lang => $self->lang,
143             mps => $loc->{windSpeed}->{mps}->{value},
144             beaufort => $loc->{windSpeed}->{beaufort}->{value},
145             name => $loc->{windSpeed}->{name}->{value},
146             ),
147              
148             humidity => Weather::YR::Model::Humidity->new(
149             from => $from,
150             to => $to,
151             lang => $self->lang,
152             percent => $loc->{humidity}->{value}->{value},
153             ),
154              
155             pressure => Weather::YR::Model::Pressure->new(
156             from => $from,
157             to => $to,
158             lang => $self->lang,
159             hPa => $loc->{pressure}->{value}->{value},
160             ),
161              
162             cloudiness => Weather::YR::Model::Cloudiness->new(
163             from => $from,
164             to => $to,
165             lang => $self->lang,
166             percent => $loc->{cloudiness}->{percent}->{value},
167             ),
168              
169             fog => Weather::YR::Model::Fog->new(
170             from => $from,
171             to => $to,
172             lang => $self->lang,
173             percent => $loc->{fog}->{percent}->{value},
174             ),
175              
176             dew_point_temperature => Weather::YR::Model::DewPointTemperature->new(
177             from => $from,
178             to => $to,
179             lang => $self->lang,
180             celsius => $loc->{dewpointTemperature}->{value}->{value},
181             ),
182              
183             temperature_probability => Weather::YR::Model::Probability::Temperature->new(
184             from => $from,
185             to => $to,
186             lang => $self->lang,
187             value => $loc->{temperatureProbability}->{value}->{value},
188             ),
189              
190             wind_probability => Weather::YR::Model::Probability::Wind->new(
191             from => $from,
192             to => $to,
193             lang => $self->lang,
194             value => $loc->{windProbability}->{value}->{value},
195             ),
196             );
197             }
198             elsif ( my $p = $t->{location}->{precipitation} ) {
199 0           my $precipitation = Weather::YR::Model::Precipitation->new(
200             from => $from,
201             to => $to,
202             lang => $self->lang,
203             value => $p->{value}->{value},
204             min => $p->{minvalue}->{value},
205             max => $p->{maxvalue}->{value},
206             symbol => Weather::YR::Model::Precipitation::Symbol->new(
207             from => $from,
208             to => $to,
209             lang => $self->lang,
210             id => $t->{location}->{symbol}->{id}->{value},
211             number => $t->{location}->{symbol}->{number}->{value},
212             ),
213             );
214              
215 0           $datapoint->add_precipitation( $precipitation );
216             }
217             }
218             }
219              
220 0           return \@datapoints;
221             }
222              
223             =head2 days
224              
225             Returns an array reference of L<Weather::YR::LocationForecast::Day> instances.
226              
227             =cut
228              
229             sub _build_days {
230 0     0     my $self = shift;
231              
232 0           my %day_datapoints = ();
233              
234 0           foreach my $datapoint ( @{$self->datapoints} ) {
  0            
235 0           push( @{$day_datapoints{$datapoint->{from}->ymd}}, $datapoint );
  0            
236             }
237              
238 0           my @days = ();
239              
240 0           foreach my $date ( sort keys %day_datapoints ) {
241 0           my $day = Weather::YR::LocationForecast::Day->new(
242             date => DateTime::Format::ISO8601->parse_datetime( $date ),
243             datapoints => $day_datapoints{ $date },
244             );
245              
246 0           push( @days, $day );
247             }
248              
249 0           return \@days;
250             }
251              
252             =head2 now
253              
254             Returns a L<Weather::YR::LocationForecast::Day> instance, representing the
255             closest forecast in time.
256              
257             =cut
258              
259             sub _build_now {
260 0     0     my $self = shift;
261              
262 0           my $now = DateTime->now( time_zone => 'UTC' );
263 0           my $closest_dp = undef;
264              
265 0           foreach my $dp ( @{$self->today->datapoints} ) {
  0            
266 0 0         unless ( defined $closest_dp ) {
267 0           $closest_dp = $dp;
268 0           next;
269             }
270              
271 0           my $diff_from_now = abs( $dp->from->epoch - $now->epoch );
272              
273 0 0         if ( $diff_from_now < ( abs($closest_dp->from->epoch - $now->epoch) ) ) {
274 0           $closest_dp = $dp;
275             }
276             }
277              
278 0           return Weather::YR::LocationForecast::Day->new(
279             date => $closest_dp->from,
280             datapoints => [ $closest_dp ],
281             );
282             }
283              
284             =head2 today
285              
286             Returns a L<Weather::YR::LocationForecast::Day> instance, representing today's
287             weather.
288              
289             =cut
290              
291             sub _build_today {
292 0     0     my $self = shift;
293              
294 0           return $self->days->[0];
295             }
296              
297             =head2 tomorrow
298              
299             Returns a L<Weather::YR::LocationForecast::Day> instance, representing
300             tomorrow's weather.
301              
302             =cut
303              
304             sub _build_tomorrow {
305 0     0     my $self = shift;
306              
307 0           return $self->days->[1];
308             }
309              
310             __PACKAGE__->meta->make_immutable;
311              
312             1;