00001 #! /usr/bin/perl -w
00002
00003 #TODO the icons aren't very meaningful, the server gives them to us for 3 or 6
00004 # hr intervals, but since we're parsing for 12 hour, that seem a little useless
00005
00006 package main;
00007 use strict;
00008 use Data::Dumper;
00009 use NDFDParser;
00010 use NWSLocation;
00011 use Date::Manip;
00012 use Getopt::Std;
00013
00014 our ($opt_v, $opt_t, $opt_T, $opt_l, $opt_u, $opt_d);
00015
00016 my $name = 'NDFD-6_day';
00017 my $version = 0.1;
00018 my $author = 'Lucien Dunning';
00019 my $email = 'ldunning@gmail.com';
00020 my $updateTimeout = 15*60;
00021 my $retrieveTimeout = 30;
00022 my @types = ('3dlocation', '6dlocation', 'updatetime',
00023 'high-0', 'high-1', 'high-2', 'high-3', 'high-4', 'high-5',
00024 'low-0', 'low-1', 'low-2', 'low-3', 'low-4', 'low-5',
00025 'icon-0', 'icon-1', 'icon-2', 'icon-3', 'icon-4', 'icon-5',
00026 'date-0', 'date-1', 'date-2', 'date-3', 'date-4', 'date-5');
00027 my $dir = './';
00028 getopts('Tvtlu:d:');
00029
00030 if (defined $opt_v) {
00031 print "$name,$version,$author,$email\n";
00032 exit 0;
00033 }
00034
00035 if (defined $opt_T) {
00036 print "$updateTimeout,$retrieveTimeout\n";
00037 exit 0;
00038 }
00039 if (defined $opt_l) {
00040 my $search = shift;
00041 NWSLocation::AddLocSearch($search);
00042 NWSLocation::AddStateSearch($search);
00043 NWSLocation::AddStationIdSearch($search);
00044 my $results = doSearch();
00045 my $result;
00046 while($result = shift @$results) {
00047 if ($result->{latitude} ne "NA" && $result->{longitude} ne "NA") {
00048 print "$result->{latitude},$result->{longitude}::";
00049 print "$result->{station_name}, $result->{state}\n";
00050 }
00051 }
00052 exit 0;
00053 }
00054
00055 if (defined $opt_t) {
00056 foreach (@types) {print; print "\n";}
00057 exit 0;
00058 }
00059
00060 if (defined $opt_d) {
00061 $dir = $opt_d;
00062 }
00063
00064 my $locstr = shift;
00065 my $units = $opt_u;
00066 my ($latitude, $longitude) = getLocation($locstr);
00067 if (!(defined $opt_u && defined $latitude && defined $longitude
00068 && $latitude ne "" && $longitude ne "")) {
00069 die "Invalid Usage";
00070 }
00071
00072 my $param = { maxt => 1,
00073 mint =>1,
00074 temp =>0,
00075 dew=>0,
00076 pop12=>0,
00077 qpf=>0,
00078 sky=>0,
00079 snow=>0,
00080 wspd=>0,
00081 wdir=>0,
00082 wx=>0,
00083 waveh=>0,
00084 icons=>1,
00085 rh=>0,
00086 appt=>0 };
00087
00088 my $d1 = UnixDate("8am tomorrow", "%O");
00089 my $d2 = UnixDate(DateCalc($d1, "+ 168 hours"), "%O");
00090 my $result;
00091 my $creationdate;
00092 my $nextupdate;
00093 my $getData = 1;
00094 if (open (CACHE, "$dir/ndfd_cache_${latitude}_${longitude}")) {
00095 ($nextupdate, $creationdate) = split / /, <CACHE>;
00096 # We don't have to check the start/end dates, since we get the same chunk
00097 # every time, and we update the cache atleast every hour, which is how often the
00098 # data is updated by the NWS.
00099 if (Date_Cmp($nextupdate, "today") > 0) { # use cache
00100 no strict "vars"; # because eval doesn't scope var correctly
00101 $result = eval <CACHE>;
00102 if ($result) {
00103 $getData = 0;
00104 } else {
00105 print STDERR "Error parsing cache $@\n";
00106 };
00107 }
00108
00109 }
00110
00111 if ($getData) {
00112 ($result, $creationdate) = NDFDParser::doParse($latitude, $longitude, $d1, $d2, $param);
00113 # output cache
00114 open(CACHE, ">$dir/ndfd_cache_${latitude}_${longitude}") or
00115 die "cannot open cache ($dir/ndfd_cache_${latitude}_${longitude}) for writing";
00116 $Data::Dumper::Purity = 1;
00117 $Data::Dumper::Sortkeys = 1;
00118 $Data::Dumper::Indent = 0;
00119 # NDFD is updated by 45 minutes after the hour, we'll give them until 50 to
00120 # make sure
00121 my $min = UnixDate("today", "%M");
00122 my $newmin;
00123 if ($min < 50) {
00124 $newmin = 50-$min;
00125 } else {
00126 $newmin = 60-($min-50);
00127 }
00128 $nextupdate = DateCalc("today", "+ $newmin minutes");
00129 print CACHE UnixDate($nextupdate, "%O ") . UnixDate("today", "%O\n");
00130 print CACHE Dumper($result);
00131 }
00132
00133 my $lowindex = 0;
00134 my $hiindex = 0;
00135 my $dateindex = 0;
00136 my $iconindex = 0;
00137 my @dates;
00138 my $time;
00139 my $date;
00140
00141 printf "updatetime::Last Updated on %s\n",
00142 UnixDate($creationdate, "%b %d, %I:%M %p %Z");
00143
00144 foreach $time (sort(keys(%$result))) {
00145 my $date;
00146 if ($time =~ m/,/) {
00147 ($date) = split /,/, $time;
00148 } else {
00149 $date = $time;
00150 }
00151
00152 if (Date_Cmp($date, $d1) < 0) {
00153 next;
00154 }
00155
00156 my $numdate = UnixDate($date, "%Q");
00157 if (!grep /$numdate/, @dates) {
00158 push @dates, $numdate;
00159 }
00160 my $geticon = 0;
00161 if ($lowindex <= 5 && $result->{$time}->{temperature_minimum}) {
00162 if ($units eq 'SI') {
00163 $result->{$time}->{temperature_minimum} =
00164 int ( (5/9) * ($result->{$time}->{temperature_minimum}-32));
00165 }
00166 print "low-${lowindex}::$result->{$time}->{temperature_minimum}\n";
00167 $lowindex++;
00168 } elsif ($hiindex <= 5 && $result->{$time}->{temperature_maximum}) {
00169 if ($units eq 'SI') {
00170 $result->{$time}->{temperature_maximum} =
00171 int ( (5/9) * ($result->{$time}->{temperature_maximum}-32));
00172 }
00173 print "high-${hiindex}::$result->{$time}->{temperature_maximum}\n";
00174 $hiindex++;
00175 $geticon = 1;
00176 }
00177 if ($geticon) {
00178 my $iconkey = $date;
00179 my $i = 0;
00180 my $icon;
00181 until ($result->{$iconkey}->{'conditions-icon_forecast-NWS'}
00182 || $i++ > 8) {
00183 $iconkey = UnixDate(DateCalc($iconkey, "+ 1 hour"), "%O%z");
00184 }
00185 if ($i >= 8) {
00186 $icon = "unknown.png";
00187 } else {
00188 $icon = $result->{$iconkey}->{'conditions-icon_forecast-NWS'};
00189 $icon =~ s/.*\/([a-z0-9_]+[.][j][p][g])/$1/;
00190 local *FH;
00191 open(FH, "icons") or die "Cannot open icons";
00192 while(my $line = <FH>) {
00193 if ($line =~ /${icon}::/) {
00194 $line =~ s/.*::
00195 print "icon-${iconindex}::$line";
00196 $iconindex++;
00197 last;
00198 }
00199 }
00200 }
00201 }
00202 }
00203 print "high-${hiindex}::NA\n" and $hiindex++ while ($hiindex <= 5);
00204 print "low-${lowindex}::NA\n" and $lowindex++ while ($lowindex <= 5);
00205 print "low-${iconindex}::NA\n" and $iconindex++ while ($iconindex<= 5);
00206
00207 foreach $date (sort(@dates)) {
00208 print "date-${dateindex}::" . UnixDate($date, "%A") . "\n"
00209 if ($dateindex <= 5);
00210 $dateindex++;
00211 }
00212
00213
00214
00215 # This script will accept locations that are either station ids, or latitude
00216 # longitude. This is because I haven't decided which to use yet :)
00217 sub getLocation {
00218 my $str = shift;
00219
00220 $str =~ tr/[a-z]/[A-Z]/;
00221 my $lat;
00222 my $lon;
00223
00224 if ($str =~ m/[A-Z]{4,4}/) { # station id form
00225 NWSLocation::AddStationIdSearch($str);
00226
00227 } else { # hopefully lat/lon
00228 ($lat, $lon) = split /,/, $str;
00229 $lat =~ s/(\d{1,3}([.]\d{1,3})?)([.]\d{1,3})?[N]/+$1/ or
00230 $lat =~ s/(\d{1,3}([.]\d{1,3})?)([.]\d{1,3})?[S]/-$1/;
00231 $lon =~ s/(\d{1,3}[.](\d{1,3})?)([.]\d{1,3})?[E]/+$1/ or
00232 $lon =~ s/(\d{1,3}([.]\d{1,3})?)([.]\d{1,3})?[W]/-$1/;
00233 NWSLocation::AddLatLonSearch($lat, $lon);
00234 }
00235
00236 my $results = NWSLocation::doSearch($str);
00237 if ($lat && $lon && !$results) {
00238 # didn't find a matching station
00239 print "location::$lat,$lon\n";
00240 return ($lat, $lon);
00241 }
00242
00243 # Should be one result in array
00244 my $location = $results->[0];
00245 $lat = $location->{latitude};
00246 $lon = $location->{longitude};
00247 if ($lat eq 'NA' || $lon eq 'NA') {
00248 # maybe scrape them from website, since they are there, annoying that
00249 # they aren't all in the XML file, gotta love the U.S. Gov :)
00250 die "Latitude and Longitude do not exist for $str";
00251 }
00252 print "3dlocation::$location->{station_name}, $location->{state}\n";
00253 print "6dlocation::$location->{station_name}, $location->{state}\n";
00254
00255 return ($lat, $lon);
00256 }