File Coverage

blib/lib/Weather/Meteo.pm
Criterion Covered Total %
statement 27 66 40.9
branch 4 26 15.3
condition 1 8 12.5
subroutine 7 9 77.7
pod 3 3 100.0
total 42 112 37.5


line stmt bran cond sub pod time code
1             package Weather::Meteo;
2              
3 3     3   480143 use strict;
  3         26  
  3         86  
4 3     3   18 use warnings;
  3         6  
  3         77  
5              
6 3     3   31 use Carp;
  3         6  
  3         173  
7 3     3   1506 use JSON::MaybeXS;
  3         17884  
  3         228  
8 3     3   2210 use LWP::UserAgent;
  3         153557  
  3         120  
9 3     3   33 use URI;
  3         7  
  3         1782  
10              
11             =head1 NAME
12              
13             Weather::Meteo - Interface to L for historical weather data
14              
15             =head1 VERSION
16              
17             Version 0.04
18              
19             =cut
20              
21             our $VERSION = '0.04';
22              
23             =head1 SYNOPSIS
24              
25             use Weather::Meteo;
26              
27             my $meteo = Weather::Meteo->new();
28             my $weather = $meteo->weather({ latitude => 0.1, longitude => 0.2, date => '2022-12-25' });
29              
30             =head1 DESCRIPTION
31              
32             Weather::Meteo provides an interface to open-meteo.com
33             for historical weather data
34              
35             =head1 METHODS
36              
37             =head2 new
38              
39             my $meteo = Weather::Meteo->new();
40             my $ua = LWP::UserAgent->new();
41             $ua->env_proxy(1);
42             $meteo = Weather::Meteo->new(ua => $ua);
43              
44             my $weather = $meteo->weather({ latitude => 51.34, longitude => 1.42, date => '2022-12-25' });
45             my @snowfall = @{$weather->{'hourly'}->{'snowfall'}};
46              
47             print 'Number of cms of snow: ', $snowfall[1], "\n";
48              
49             =cut
50              
51             sub new {
52 2     2 1 143 my($class, %args) = @_;
53              
54 2 100       8 if(!defined($class)) {
    50          
55             # Weather::Meteo::new() used rather than Weather::Meteo->new()
56 1         2 $class = __PACKAGE__;
57             } elsif(ref($class)) {
58             # clone the given object
59 0         0 return bless { %{$class}, %args }, ref($class);
  0         0  
60             }
61              
62 2         4 my $ua = $args{ua};
63 2 50       6 if(!defined($ua)) {
64 2         13 $ua = LWP::UserAgent->new(agent => __PACKAGE__ . "/$VERSION");
65 2         3580 $ua->default_header(accept_encoding => 'gzip,deflate');
66             }
67 2   50     121 my $host = $args{host} || 'archive-api.open-meteo.com';
68              
69 2         17 return bless { ua => $ua, host => $host }, $class;
70             }
71              
72             =head2 weather
73              
74             use Geo::Location::Point;
75              
76             my $ramsgate = Geo::Location::Point->new({ latitude => 51.34, longitude => 1.42 });
77             # Print snowfall at 1AM on Christmas morning in Ramsgate
78             $weather = $meteo->weather($ramsgate, '2022-12-25');
79             @snowfall = @{$weather->{'hourly'}->{'snowfall'}};
80              
81             print 'Number of cms of snow: ', $snowfall[1], "\n";
82              
83             =cut
84              
85             sub weather {
86 0     0 1   my $self = shift;
87 0           my %param;
88              
89 0 0 0       if(ref($_[0]) eq 'HASH') {
    0 0        
    0          
    0          
90 0           %param = %{$_[0]};
  0            
91             } elsif((@_ == 2) && (ref($_[0]) =~ /::/) && ($_[0]->can('latitude'))) {
92 0           my $location = $_[0];
93 0           $param{latitude} = $location->latitude();
94 0           $param{longitude} = $location->longitude();
95 0           $param{'date'} = $_[1];
96             } elsif(ref($_[0])) {
97 0           Carp::croak('Usage: weather(latitude => $latitude, longitude => $logitude, date => "YYYY-MM-DD")');
98 0           return;
99             } elsif(@_ % 2 == 0) {
100 0           %param = @_;
101             }
102              
103 0           my $latitude = $param{latitude};
104 0           my $longitude = $param{longitude};
105 0           my $date = $param{'date'};
106              
107 0 0         if(!defined($latitude)) {
108 0           Carp::croak('Usage: weather(latitude => $latitude, longitude => $logitude, date => "YYYY-MM-DD")');
109 0           return;
110             }
111              
112 0           my $uri = URI->new("https://$self->{host}/v1/archive");
113 0           my %query_parameters = (
114             'latitude' => $latitude,
115             'longitude' => $longitude,
116             'start_date' => $date,
117             'end_date' => $date,
118             'hourly' => 'temperature_2m,rain,snowfall,weathercode',
119             'timezone' => 'Europe/London',
120             'windspeed_unit' => 'mph',
121             'precipitation_unit' => 'inch'
122             );
123 0           $uri->query_form(%query_parameters);
124 0           my $url = $uri->as_string();
125              
126 0           $url =~ s/%2C/,/g;
127              
128 0           my $res = $self->{ua}->get($url);
129              
130 0 0         if($res->is_error()) {
131 0           Carp::croak("$url API returned error: ", $res->status_line());
132 0           return;
133             }
134             # $res->content_type('text/plain'); # May be needed to decode correctly
135              
136 0           my $json = JSON::MaybeXS->new()->utf8();
137 0 0         if(my $rc = $json->decode($res->decoded_content())) {
138 0 0         if($rc->{'error'}) {
139             # TODO: print error code
140 0           return;
141             }
142 0 0         if(defined($rc->{'hourly'})) {
143 0           return $rc; # No support for list context, yet
144             }
145             }
146              
147             # my @results = @{ $data || [] };
148             # wantarray ? @results : $results[0];
149             }
150              
151             =head2 ua
152              
153             Accessor method to get and set UserAgent object used internally. You
154             can call I for example, to get the proxy information from
155             environment variables:
156              
157             $meteo->ua()->env_proxy(1);
158              
159             You can also set your own User-Agent object:
160              
161             use LWP::UserAgent::Throttled;
162              
163             my $ua = LWP::UserAgent::Throttled->new();
164             $ua->throttle('open-meteo.com' => 1);
165             $meteo->ua($ua);
166              
167             =cut
168              
169             sub ua {
170 0     0 1   my $self = shift;
171 0 0         if (@_) {
172 0           $self->{ua} = shift;
173             }
174 0           $self->{ua};
175             }
176              
177             =head1 AUTHOR
178              
179             Nigel Horne, C<< >>
180              
181             This library is free software; you can redistribute it and/or modify
182             it under the same terms as Perl itself.
183              
184             Lots of thanks to the folks at L.
185              
186             =head1 BUGS
187              
188             =head1 SEE ALSO
189              
190             =head1 LICENSE AND COPYRIGHT
191              
192             Copyright 2023 Nigel Horne.
193              
194             This program is released under the following licence: GPL2
195              
196             =cut
197              
198             1;