File Coverage

blib/lib/Geo/Weather.pm
Criterion Covered Total %
statement 40 280 14.2
branch 1 174 0.5
condition 2 49 4.0
subroutine 6 15 40.0
pod 11 11 100.0
total 60 529 11.3


line stmt bran cond sub pod time code
1             ## Geo::Weather
2             ## Written by Mike Machado 2000-11-01
3             ##
4              
5             # Modified by Kevin L. Papendick
6             # E-mail: kevinp@polarlava.com
7             # Website: www.polarlava.com
8              
9             # V0.9b
10             # - Added report_raw() function
11             # - Modified report() format
12              
13             # V0.9c
14             # - URL & RegEx Changes due to weather.com changes
15              
16             # V1.1_PL
17             # - Incorporated Mike's V1.1 $ERROR_BUSY changes
18              
19             # V1.2
20             # Parse new weather.com as of 2002-12-05 -klp
21             # - New image locator comment
22             # - New current temperature locator
23             # - New dew point locator
24             # - New relative humidity locator
25             # - New visability locator
26             # - New barometric locator
27             # - New UV locator
28             # - New wind locator
29              
30             # V1.21
31             # Parse new weather.com as of 2003-01-08 -klp
32              
33             # V1.22 - 1/27/03
34             # Bug Fix for negative dew points -klp
35              
36             # V1.23 - 02/24/03
37             # Change to picture parsing for new HTML code -klp
38              
39             # V1.3 - 05/27/03
40             # Change request URL -klp
41              
42             # V1.31 - 05/28/03
43             # Added data_check() function in an effort to detect and catch bad/missing data. -klp
44             # Removed unnecessary UserAgent cookie jar left behind from V1.3 development -klp
45              
46             # V1.32 - 06/12/03 -klp
47             # Changed $self->{server_zip} value
48             # Cleared $self->{ext} value
49              
50             # V1.4 - 08/12/03 -klp
51             # Bug Fix for City, State request. Added recursive lookup call against redirect URL.
52             # Added get_city(), get_state() functions.
53             # Added set_report_colors() functions.
54             # Minor reformatting of report() function.
55             # Added lookup_forecast() and report_forecast() function.
56             # Removed $self->{ext} variable.
57             # Additional debugging messages added.
58              
59             # V1.41 - 08/27/03 -klp
60             # Changed City, State URL extraction due to weather.com change
61             # Changed $self->{forecast_flag} to $self->{location_code} as it is now needed by
62             # both the current and forecast weather retrievals
63              
64             package Geo::Weather;
65              
66 1     1   752 use strict;
  1         1  
  1         37  
67 1     1   6 use Carp;
  1         2  
  1         112  
68 1     1   26681 use LWP::UserAgent;
  1         130995  
  1         46  
69              
70 1         12831 use vars qw( $VERSION @ISA @EXPORT @EXPORT_OK
71 1     1   12 $OK $ERROR_UNKNOWN $ERROR_QUERY $ERROR_PAGE_INVALID $ERROR_CONNECT $ERROR_NOT_FOUND $ERROR_TIMEOUT $ERROR_BUSY);
  1         2  
72              
73             require Exporter;
74              
75             @ISA = qw(Exporter);
76             @EXPORT_OK = qw();
77             @EXPORT = qw( $OK $ERROR_UNKNOWN $ERROR_QUERY $ERROR_PAGE_INVALID $ERROR_CONNECT $ERROR_NOT_FOUND $ERROR_TIMEOUT $ERROR_BUSY);
78             $VERSION = '1.41';
79              
80             $OK = 1;
81             $ERROR_UNKNOWN = 0;
82             $ERROR_QUERY = -1;
83             $ERROR_PAGE_INVALID = -2;
84             $ERROR_CONNECT = -3;
85             $ERROR_NOT_FOUND = -4;
86             $ERROR_TIMEOUT = -5;
87             $ERROR_BUSY = -6;
88              
89              
90             sub new {
91 1     1 1 49 my $class = shift;
92 1         3 my $self = {};
93 1         5 $self->{debug} = 0;
94 1         4 $self->{version} = $VERSION;
95 1         2 $self->{server_zip} = 'www.w3.weather.com';
96 1         3 $self->{server_cst} = 'www.weather.com';
97 1         2 $self->{port} = 80;
98 1         3 $self->{timeout} = 10;
99 1         3 $self->{proxy} = '';
100 1         3 $self->{proxy_username} = '';
101 1         4 $self->{proxy_password} = '';
102 1         3 $self->{agent_string} = "Geo::Weather/$VERSION";
103 1         3 $self->{base_zip} = '/weather/local/';
104 1         3 $self->{base_cst} = '/search/search?where=';
105 1         3 $self->{location_code} = '';
106              
107             #--- Forecast
108 1         3 $self->{forecast_server} = $self->{server_zip};
109 1         4 $self->{forecast_base} = '/weather/print/';
110 1         3 $self->{forecast_table_size} = '80';
111              
112             #--- Report Colors
113 1         9 $self->{report_hdr_color} = "#000000";
114 1         3 $self->{report_cond_color} = "#000080";
115 1         3 $self->{report_result_color} = "#0000a0";
116              
117 1         3 bless $self, $class;
118 1         5 return $self;
119             }
120              
121             sub get_weather {
122 1     1 1 127 my $self = shift;
123 1   50     9 my $city = shift || '';
124 1   50     8 my $state = shift || '';
125 1         2 my $mode;
126              
127 1 50       7 return $ERROR_QUERY unless $city;
128              
129 0           my $page = '';
130 0 0         if ($city =~ /^\d+$/) {
131             # Use zip code
132 0           $page = $self->{base_zip}.$city;
133 0           $self->{location_code} = $city;
134 0           $mode = 'zip';
135             } else {
136             # Use City, State
137 0           $state = lc($state);
138 0           $city = lc($city);
139 0           $city =~ s/ /+/g;
140 0           $page = $self->{base_cst}.$city.','.$state;
141             #forecast flag set in lookup for City, State
142 0           $mode = 'cst';
143             }
144              
145 0           $self->{results} = $self->lookup($page, $mode);
146              
147 0           return $self->{results};
148             }
149              
150             sub get_city {
151 0     0 1   my $self = shift;
152              
153 0 0         return $ERROR_UNKNOWN unless $self->{results};
154 0           my $results = $self->{results};
155              
156 0           return $results->{city};
157             }
158              
159             sub get_state {
160 0     0 1   my $self = shift;
161              
162 0 0         return $ERROR_UNKNOWN unless $self->{results};
163 0           my $results = $self->{results};
164              
165 0           return $results->{state};
166             }
167              
168             sub set_report_colors {
169 0     0 1   my $self = shift;
170 0           my $report_hdr_color = shift;
171 0           my $report_cond_color = shift;
172 0           my $report_result_color = shift;
173              
174 0 0         return 0 if (length($report_hdr_color) < 7);
175 0 0         return 0 if (length($report_cond_color) < 7);
176 0 0         return 0 if (length($report_result_color) < 7);
177              
178 0           $self->{report_hdr_color} = $report_hdr_color;
179 0           $self->{report_cond_color} = $report_cond_color;
180 0           $self->{report_result_color} = $report_result_color;
181              
182 0           return 1;
183             }
184              
185             sub data_check {
186 0     0 1   my $self = shift;
187 0           my $data = $self->report_raw();
188 0           my $data_integrity = 1;
189              
190 0 0         $data_integrity = 0 if ($data =~ /^\|{4}/);
191              
192 0           return $data_integrity;
193             }
194              
195             sub report_raw {
196 0     0 1   my $self = shift;
197 0           my $results = $self->{results};
198 0           my $output;
199              
200 0 0         return $ERROR_UNKNOWN unless $self->{results};
201              
202 0           $output .= $results->{city} . '|';
203 0           $output .= $results->{state} . '|';
204 0           $output .= $results->{pic} . '|';
205 0           $output .= $results->{cond} . '|';
206 0           $output .= $results->{temp} . '|';
207 0           $output .= $results->{wind} . '|';
208 0           $output .= $results->{dewp} . '|';
209 0           $output .= $results->{humi} . '|';
210 0           $output .= $results->{visb} . '|';
211 0           $output .= $results->{baro} . '|';
212 0           $output .= $results->{uv};
213              
214 0           return $output;
215             }
216              
217             sub report {
218 0     0 1   my $self = shift;
219              
220 0 0         return $ERROR_UNKNOWN unless $self->{results};
221              
222 0           my $output = '';
223 0           my $heat_c = 0;
224 0           my $feels_like = '';
225 0           my $results = $self->{results};
226              
227 0 0         if ($results->{heat} ne 'N/A') {
228 0           $heat_c = sprintf("%0.0f", 5/9 * ($results->{heat} - 32));
229 0           $feels_like = "(Feels Like: $results->{heat}° F/$heat_c° C)";
230             }
231              
232              
233 0           $output = <
234             {report_hdr_color}\">
235             $results->{city}, $results->{state}
236            
237            
238             {url}\">{pic}\" border=0>
239            
240             $results->{cond}
241            
242            
243            
244            
245            
246             Temperature:
247            
248             $results->{temp}° F/$results->{temp_c}° C   $feels_like
249            
250            
251              
252             REPORT_START
253              
254 0 0         if ($results->{wind}) {
255 0           $output .= <
256            
257             Wind:
258            
259             $results->{wind}
260            
261            
262              
263             REPORT_WIND
264             }
265              
266 0           $output .= <
267            
268             Dew Point:
269            
270             $results->{dewp}° F/$results->{dewp_c}° C
271            
272            
273            
274             Rel. Humidity:
275            
276             $results->{humi} %
277            
278            
279            
280             Visibility:
281            
282             $results->{visb}
283            
284            
285              
286             REPORT_MID
287              
288 0 0         if ($results->{baro}) {
289 0           $output .= <
290            
291             Barometer:
292            
293             $results->{baro}
294            
295            
296              
297             REPORT_BARO
298             }
299              
300 0 0         if ($results->{baro}) {
301 0           $output .= <
302            
303             UV Index:
304            
305             $results->{uv}
306            
307            
308              
309             REPORT_UV
310             }
311              
312 0           $output .= "
\n";
313              
314 0           return $output;
315             }
316              
317             sub report_forecast {
318 0     0 1   my $self = shift;
319 0           my $table_size = shift;
320 0           my $url = 'http://' . $self->{forecast_server} . $self->{forecast_base} . $self->{location_code};
321 0           my $output;
322              
323 0 0         return $ERROR_QUERY unless $self->{results};
324              
325 0 0 0       $self->{forecast_table_size} = $table_size if (defined($table_size) && length($table_size) > 0);
326              
327 0           my @forecast = $self->lookup_forecast($url);
328 0 0         print STDERR __LINE__, ": Geo::Weather: Forecast size " . $#forecast . "\n" if $self->{debug} > 3;
329              
330 0           $output = <
331            
332             $self->{city}, $self->{state}
333            
334            
335             Ten Day Forecast
336            
337              
338             REPORT_START
339              
340             #--- Reformat Data
341 0           my $strip = 0;
342 0           my $weather_href = "href=\"http://www.weather.com";
343 0           foreach (@forecast) {
344 0           s/HREF="/$weather_href/ig; #convert relative links
345 0           s/bgcolor=\"#ffffff\"\s+//ig; #remove white background from table cell
346 0 0         s/>/ target=\"_blank\">/ if (/href/ig); #open links in new window
347 0 0         if (/
    0          
348 0           s/BGCOLOR="#\w*">/>/i;
349             } elsif (/<\/TABLE>/i) {
350 0           $strip = 0;
351             }
352 0 0         if (//) {
    0          
353 0           $strip = 1;
354 0           $output .= <
355            
356            
357              
358             High /
Low (°F)
359             Precip. %
360            
361              
362             FORECAST
363              
364             } elsif ($strip) {
365             # forecast content
366 0           $output .= "$_\n";
367             } else {
368             # unwanted content
369             }
370             }
371 0           $output .= "
\n\n";
372              
373 0           return $output;
374             }
375              
376             sub lookup {
377 0     0 1   my $self = shift;
378 0   0       my $page = shift || '';
379 0   0       my $mode = shift || 'raw';
380              
381 0           my $rh_cnt = 0;
382 0           my $dew_cnt = 0;
383 0           my $vis_cnt = 0;
384 0           my $baro_cnt = 0;
385 0           my $uv_cnt = 0;
386 0           my $wind_cnt = 0;
387              
388 0 0         return $ERROR_PAGE_INVALID unless $page;
389              
390 0           my %results = ();
391              
392 0 0         $results{url} = "http://$self->{server_zip}" if ($mode eq 'zip');
393 0 0         $results{url} = "http://$self->{server_cst}" if ($mode eq 'cst');
394 0 0         $results{url} .= ":$self->{port}" unless $self->{port} eq '80';
395 0           $results{url} .= $page;
396 0           $results{page} = $page;
397              
398 0           my $not_found_marker = 'not found';
399 0           my $end_report_marker = '';
400 0           my $line = '';
401              
402 0 0         print STDERR __LINE__, ": Geo::Weather: Attempting to GET current weather at $results{url}\n" if $self->{debug};
403 0           my $ua = new LWP::UserAgent;
404 0           my $request = new HTTP::Request('GET',$results{url});
405 0   0       my $proxy_user = $self->{proxy_user} || $ENV{HTTP_PROXY_USER} || '';
406 0   0       my $proxy_pass = $self->{proxy_pass} || $ENV{HTTP_PROXY_PASS} || '';
407 0 0 0       $request->proxy_authorization_basic($proxy_user, $proxy_pass) if $self->{proxy} && $proxy_user;
408              
409 0 0         $ua->timeout($self->{timeout}) if $self->{timeout};
410              
411 0           $ua->agent($self->{agent_string});
412 0 0         $ua->proxy(['http'], $self->{proxy}) if $self->{proxy};
413              
414              
415 0           my $response = $ua->request($request);
416 0 0         unless ($response->is_success) {
417 0 0         print STDERR __LINE__, ": Geo::Weather: GET Failed for current weather " . $response->status_line . "\n" if $self->{debug};
418 0           return $ERROR_TIMEOUT;
419             }
420 0           my $content = $response->content();
421 0           my @lines = split(/\n/, $content);
422              
423             #--- Parse out City, State URL
424 0 0         if ($mode eq 'cst') {
425 0           for (my $i = 0; $i < @lines; $i++) {
426 0           my $line = $lines[$i];
427 0 0         next if ($line eq '');
428              
429             #--- Recursive look up of weather page
430 0 0         if ($line =~ s/.+URL=.+\/(.+)">/$1/) {
431 0           $self->{location_code} = $line;
432 0 0         print STDERR __LINE__, ": CST Location Code: $self->{location_code}\n" if $self->{debug} > 2;
433 0           my $url = 'http://' . $self->{server_zip} . $self->{base_zip} . $self->{location_code};
434 0           $self->{results} = $self->lookup($url);
435 0           return $self->{results};
436             }
437             }
438             }
439              
440 0           for (my $i = 0; $i < @lines; $i++) {
441 0           my $line = $lines[$i];
442 0 0         next if ($line eq '');
443 0 0 0       print STDERR "tagline: $line\n" if ($line =~ /