3 # fetch a metar, taf or short taf from http://weather.noaa.gov
5 # This is a module which shows me doing my own thing using the
6 # normalised input. It does essentially the same job as
7 # cgi_weather.pl, it's just a lot more complicated but returns
8 # a much shorter string that is a bit more cryptic.
10 # It also is designed really to just get the forecast and
13 # This is designed to be used in a IFRAME and returns HTML.
14 # It will only query the website once every 30 minutes, the rest
15 # of the time it will cache the result in an 'easily guessable'
16 # place in /tmp (consider that as a warning).
18 # Call it from a web page like this:-
20 # <iframe src="cgi-bin/fetch_weather.pl?icao=EGSH&metar=1"
21 # name="METAR for EGSH" frameborder="1" width="90%" height="50">
22 # [Your user agent does not support frames or is currently configured
23 # not to display frames. However, you may visit
24 # <A href="cgi-bin/fetch_weather.pl?icao=EGSH&metar=1">METAR for EGSH</A>]
27 # You can set as many of these as you like:-
29 # break=1 insert a "<br /><br />" between each result
30 # onediv=1 make a multiple one div (not one div per thing)
31 # raw=1 will display the raw weather string
32 # debug=1 will display the objects
33 # force=1 always fetch the data (don't use any cached stuff)
37 # Copyright (c) 2003 Dirk Koopman G1TLH
48 my $site_code = uc $q->param('icao');
49 my @sort = qw(metar staf);
50 my $debug = $q->param('debug');
51 my $raw = $q->param('raw');
52 my $force = $q->param('force');
53 my $dobrk = "<br /><br />" if $q->param('break') && @sort > 1;
54 my $onediv = $q->param('onediv') && @sort > 1;
60 CLR => "no cloud no sig wthr",
65 CAVOK => "CAVOK(no cloud >10Km viz no sig wthr)",
68 NSC => 'no sig cloud',
69 BLU => '3okt 2500ft 8Km viz',
70 WHT => '3okt 1500ft 5Km viz',
71 GRN => '3okt 700ft 3700m viz',
72 YLO => '3okt 300ft 1600m viz',
73 AMB => '3okt 200ft 800m viz',
74 RED => '3okt <200ft <800m viz',
82 'VC' => 'in the vicinity',
90 TS => 'thunderstorms containing',
101 GS => 'small hail/snow pellets',
102 UP => 'unknown precip',
107 VA => 'volcanic ash',
113 PO => 'dust/sand whirls',
118 '+FC' => 'water spouts',
122 'NOSIG' => 'no significant weather',
128 error("No ICAO (valid) site code ($site_code) specified") unless $site_code && $site_code =~ /^[A-Z]{4}$/;
131 my ($sort, $fn, $started);
133 print "<div class=\"weather\">$site_code ";
135 while ($sort = shift @sort) {
136 $fn = "$base/${sort}_$site_code";
138 if (!$force && -e $fn) {
139 my ($mt, $size) = (stat $fn)[9,7] ;
142 if ($mt + 30*60 < time || $size == 0) {
143 my $s = fetch_icao($sort);
147 my $s = retrieve($fn);
151 my $s = fetch_icao($sort);
157 print $onediv ? ' ' : '</div>';
158 print $dobrk if $dobrk;
159 print '<div class="weather">' unless $onediv;
169 open IN, "$fn" or die "cannot open $fn $!\n";
179 my $ua = new LWP::UserAgent;
180 my $req = new HTTP::Request GET =>
181 "http://weather.noaa.gov/cgi-bin/mget$sort.pl?cccc=$site_code";
183 my $response = $ua->request($req);
186 if (!$response->is_success) {
187 error("METAR Fetch $site_code Error", $response->error_as_HTML);
190 my $data = $response->as_string;
191 ($metar) = $data =~ /($site_code\s+\d+Z?[^<]*)/; # find the METAR string
194 if (length $metar < 10) {
195 error("METAR ($metar) is too short");
204 my $metar = fetch_thing($sort);
206 # pass the data to the METAR module.
207 my $m = new Geo::TAF;
208 if ($sort =~ /taf$/) {
216 $s .= join "<br />", $m->raw, "<br />" if $raw;
217 $s .= join "<br />", $m->as_chunk_strings, "<br />" if $debug;
218 foreach my $c ($m->chunks) {
219 my ($sub) = (ref $c) =~ /::([A-Z]+)$/;
221 if ($sub eq 'HEAD') {
222 $sub = $sort =~ /taf$/ ? "taf$sub" : "metar$sub";
233 print $q->header(-type=>'text/html', -expires=>'+60m');
234 print $q->start_html(-title=>"Weather for $site_code", -style=>{'src'=>'/style.css'},);
241 print $q->end_html, "\n";
247 open OUT, ">$fn" or die "cannot open $fn $!\n";
257 print $more if $more;
258 print "</div>", $q->end_html;
267 return "FORECAST Issued $in[3] on " . Geo::TAF::EN::day($in[2]);
273 return "CURRENT Issued $in[3] on " . Geo::TAF::EN::day($in[2]);
279 return "Valid $in[1]-\>$in[2] on " . Geo::TAF::EN::day($in[0]);
286 $out .= $in[0] eq 'VRB' ? " variable" : " $in[0]";
287 $out .= " varying $in[4]-\>$in[5]" if defined $in[4];
288 $out .= ($in[0] eq 'VRB' ? '' : "deg") . " $in[1]";
289 $out .= " gust $in[2]" if defined $in[2];
303 my $out = "Temp $in[0]C";
304 $out .= " Dewp $in[1]C" if defined $in[1];
313 return $st{$in[0]} if @in == 1;
314 return "Cloud $st{$in[0]} \@ $in[1]ft" if $in[0] eq 'VV';
315 my $out = "Cloud $st{$in[0]} \@ $in[1]ft";
316 $out .= " $st{$in[2]}" if defined $in[2];
322 # goto &Geo::TAF::EN::WEATHER::as_string;
339 } elsif ($t eq 'VC') {
342 } elsif ($t eq 'SH') {
345 } elsif ($t eq '+' && $one eq 'FC') {
346 push @out, $wt{'+FC'};
353 if (@out && $shower) {
355 push @out, $wt{'SH'};
358 push @out, $wt{'VC'} if $vic;
360 return join ' ', @out;
366 my $out = "RVR R$in[0] $in[1]$in[3]";
367 $out .= " vary $in[2]$in[3]" if defined $in[2];
368 if (defined $in[4]) {
369 $out .= " decr" if $in[4] eq 'D';
370 $out .= " incr" if $in[4] eq 'U';
384 my $out = "Prob $in[0]%";
385 $out .= " $in[1]-\>$in[2]" if defined $in[1];
392 my $out = "Temporary";
393 $out .= " $in[0]-\>$in[1]" if defined $in[0];
401 my $out = "Becoming";
402 $out .= " $in[0]-\>$in[1]" if defined $in[0];
411 return "Viz $in[0]$in[1]";
418 return "From $in[0]";
425 return "Until $in[0]";