00001 #! /usr/bin/perl -w
00002
00003 # TODO handle xsi:nil="true" handling
00004 # TODO can soap data be received as stream, so we don't have to store giant string?
00005 # TODO cache SOAP response with all data, and only update that occasionally, to
00006 # prevent hammering the NWS servers, also, to speed up the scripts.
00007 # TODO is visibility handling right? Have never seen it not nil, so I don't know
00008 # what its going to look like. Update: I think nil means unlimited from looking
00009 # at table 7 in http://www.weather.gov/mdl/XML/Design/MDL_XML_Design.htm, still
00010 # waiting to get something other than nil though.
00011
00012 package NDFDParser;
00013 require Exporter;
00014
00015 use base qw(XML::SAX::Base);
00016 use ndfdXML;
00017 use Date::Manip;
00018
00019 our @ISA = qw(Exporter);
00020 our @EXPORT = qw(doParse);
00021 our $VERSION = 0.1;
00022
00023 my %timelayout;
00024 my %weatherdata;
00025 my $currKey;
00026 my $currtimearray;
00027 my $creationdate;
00028
00029 ########### Parsing Methods ##################
00030
00031 sub StartTag {
00032 my ($expat, $name, %atts) = @_;
00033
00034 if ($name eq 'time-layout') {
00035 $expat->setHandlers( Start => \&timeStart, Char => \&timeChar );
00036 return;
00037 }
00038
00039 if ($name eq 'parameters') {
00040 $expat->setHandlers( Start => \&weatherStart, End => \&weatherEnd, Char => \&weatherChar );
00041 }
00042 }
00043
00044 sub EndTag {
00045 my ($expat, $name, %atts) = @_;
00046 if ($name eq 'time-layout') {
00047 $timelayout{$currKey} = $currtimearray;
00048 $expat->{CurrDay} = 0;
00049 $expat->{CurrNight} = 0;
00050 undef $expat->{TimeDate};
00051 $expat->setHandlers( Char => \&Text, Start => \&StartTag );
00052 return;
00053 }
00054
00055 }
00056
00057 sub Text {
00058 my $expat = shift;
00059 if ($expat->in_element('creation-date')) {
00060 $creationdate = $expat->{Text};
00061 }
00062 }
00063
00064 sub EndDocument {
00065 my ($expat, $name, %atts) = @_;
00066 }
00067
00068
00069 sub conditionsStart {
00070 my ($expat, $name, %atts) = @_;
00071
00072 if ($name eq 'value') {
00073 $timestr = $currtimearray->[$expat->{timeindex}];
00074 my $condhash = {};
00075 foreach $attr (keys(%atts)) {
00076 $condhash->{$attr} = $atts{$attr};
00077 }
00078 push @{$weatherdata{$timestr}->{$currLbl}}, $condhash;
00079 }
00080
00081 if ($name eq 'visibility') {
00082 $timestr = $currtimearray->[$expat->{timeindex}];
00083 $index = $#{$weatherdata{$timestr}->{$currLbl}};
00084 if ($atts{'xsi:nil'}) {
00085 $weatherdata{$timestr}->{$currLbl}->[$index]->{visibility} =
00086 'unlimited';
00087 }
00088 }
00089 }
00090
00091 sub conditionsChar {
00092 my ($expat, $text) = @_;
00093 if ($expat->in_element('visibility')) {
00094 $timestr = $currtimearray->[$expat->{timeindex}];
00095 $weatherdata{$timestr}->{$currLbl}->{visibility} = $text;
00096 }
00097 }
00098
00099 sub conditionsEnd {
00100 my ($expat, $name) = @_;
00101
00102 if ($name eq 'weather') {
00103 $expat->setHandlers( Start => \&weatherStart,
00104 End => \&weatherEnd, Char => \&weatherChar );
00105 }
00106 if ($name eq 'weather-conditions') {
00107 $expat->{timeindex}++;
00108 }
00109 }
00110
00111 sub weatherStart {
00112 my ($expat, $name, %atts) = @_;
00113
00114 if ($expat->in_element('parameters')) {
00115 $currLbl = $name;
00116 if ($atts{type}) {
00117 $currLbl .= "_$atts{type}";
00118 }
00119 $currtimearray = $timelayout{$atts{'time-layout'}};
00120 $expat->{timeindex} = 0;
00121
00122 if ($name eq 'weather') {
00123 $expat->setHandlers( Start => \&conditionsStart,
00124 End =>\&conditionsEnd, Char => \&conditionsChar);
00125 }
00126 }
00127
00128 }
00129
00130 sub weatherEnd {
00131 my ($expat, $name, %atts) = @_;
00132 if ($name eq 'parameters') {
00133 $expat->setHandlers( Char=> \&Text, Start => \&StartTag );
00134 return;
00135 }
00136 }
00137
00138 sub weatherChar {
00139 my ($expat, $text) = @_;
00140 if ($expat->in_element('value') || $expat->in_element('icon-link')) {
00141 $timestr = $currtimearray->[$expat->{timeindex}++];
00142 $weatherdata{$timestr}->{$currLbl} = $text;
00143 }
00144
00145 }
00146
00147 sub timeStart {
00148 my $expat = shift;
00149 }
00150
00151 sub timeChar {
00152 my ($expat, $text) = @_;
00153 if ($expat->in_element('layout-key')) {
00154 $currKey = $text;
00155 $currtimearray = [];
00156 }
00157
00158 if ($expat->in_element('start-valid-time')) {
00159 # yea, putting it into almost the same format, the reason for this that
00160 # you can't get a date out of UnixDate that has tz in 0:00 format,
00161 # annoying.
00162 $text = UnixDate($text, "%O%z");
00163 if ($currKey =~ /p3h/) {
00164 push (@$currtimearray, $text);
00165 }
00166 $expat->{CurrStartTime} = $text;
00167 }
00168 if ($expat->in_element('end-valid-time')) {
00169 $text = UnixDate($text, "%O%z");
00170 if ($currKey !~ /p3h/) {
00171 push (@$currtimearray, "$expat->{CurrStartTime},$text");
00172 }
00173
00174 }
00175 }
00176
00177 ########## Exported method to do parsing #################
00178
00179 sub doParse {
00180 my ($lat, $lon, $start, $end, $params) = @_;
00181 my $product = "time-series";
00182
00183 my $NDFD_XML = ndfdXML->NDFDgen(SOAP::Data->name("latitude" => $lat),
00184 SOAP::Data->name("longitude" => $lon),
00185 SOAP::Data->name("product" => $product),
00186 SOAP::Data->name("startTime" => $start),
00187 SOAP::Data->name("endTime" => $end),
00188 SOAP::Data->name("weatherParameters" =>
00189 \SOAP::Data->value(
00190 SOAP::Data->type('boolean')->name("maxt" => $params->{maxt}),
00191 SOAP::Data->type('boolean')->name("mint" => $params->{mint}),
00192 SOAP::Data->type('boolean')->name("temp" => $params->{temp}),
00193 SOAP::Data->type('boolean')->name("dew" => $params->{dew}),
00194 SOAP::Data->type('boolean')->name("pop12" => $params->{pop12}),
00195 SOAP::Data->type('boolean')->name("qpf" => $params->{qpf}),
00196 SOAP::Data->type('boolean')->name("sky" => $params->{sky}),
00197 SOAP::Data->type('boolean')->name("snow" => $params->{snow}),
00198 SOAP::Data->type('boolean')->name("wspd" => $params->{wspd}),
00199 SOAP::Data->type('boolean')->name("wdir" => $params->{wdir}),
00200 SOAP::Data->type('boolean')->name("wx" => $params->{wx}),
00201 SOAP::Data->type('boolean')->name("waveh" => $params->{waveh}),
00202 SOAP::Data->type('boolean')->name("icons" => $params->{icons}),
00203 SOAP::Data->type('boolean')->name("rh" => $params->{rh}),
00204 SOAP::Data->type('boolean')->name("appt" => $params->{appt}))));
00205
00206 my $parser = new XML::Parser(Style => 'Stream');
00207 $parser->parse("$NDFD_XML");
00208 return (\%weatherdata, $creationdate);
00209 }
00210
00211 1;