MythTV  master
NDFDParser.pm
Go to the documentation of this file.
1 #! /usr/bin/perl -w
2 
3 # TODO handle xsi:nil="true" handling
4 # TODO can soap data be received as stream, so we don't have to store giant string?
5 # TODO cache SOAP response with all data, and only update that occasionally, to
6 # prevent hammering the NWS servers, also, to speed up the scripts.
7 # TODO is visibility handling right? Have never seen it not nil, so I don't know
8 # what its going to look like. Update: I think nil means unlimited from looking
9 # at table 7 in http://www.weather.gov/mdl/XML/Design/MDL_XML_Design.htm, still
10 # waiting to get something other than nil though.
11 
12 package NDFDParser;
13 require Exporter;
14 
15 use base qw(XML::SAX::Base);
16 use ndfdXML;
17 use Date::Manip;
18 
19 our @ISA = qw(Exporter);
20 our @EXPORT = qw(doParse);
21 our $VERSION = 0.1;
22 
23 my %timelayout;
24 my %weatherdata;
25 my $currKey;
26 my $currtimearray;
27 my $creationdate;
28 
29 ########### Parsing Methods ##################
30 
31 sub StartTag {
32  my ($expat, $name, %atts) = @_;
33 
34  if ($name eq 'time-layout') {
35  $expat->setHandlers( Start => \&timeStart, Char => \&timeChar );
36  return;
37  }
38 
39  if ($name eq 'parameters') {
40  $expat->setHandlers( Start => \&weatherStart, End => \&weatherEnd, Char => \&weatherChar );
41  }
42 }
43 
44 sub EndTag {
45  my ($expat, $name, %atts) = @_;
46  if ($name eq 'time-layout') {
47  $timelayout{$currKey} = $currtimearray;
48  $expat->{CurrDay} = 0;
49  $expat->{CurrNight} = 0;
50  undef $expat->{TimeDate};
51  $expat->setHandlers( Char => \&Text, Start => \&StartTag );
52  return;
53  }
54 
55 }
56 
57 sub Text {
58  my $expat = shift;
59  if ($expat->in_element('creation-date')) {
60  $creationdate = $expat->{Text};
61  }
62 }
63 
64 sub EndDocument {
65  my ($expat, $name, %atts) = @_;
66 }
67 
68 
69 sub conditionsStart {
70  my ($expat, $name, %atts) = @_;
71 
72  if ($name eq 'value') {
73  $timestr = $currtimearray->[$expat->{timeindex}];
74  my $condhash = {};
75  foreach $attr (keys(%atts)) {
76  $condhash->{$attr} = $atts{$attr};
77  }
78  push @{$weatherdata{$timestr}->{$currLbl}}, $condhash;
79  }
80 
81  if ($name eq 'visibility') {
82  $timestr = $currtimearray->[$expat->{timeindex}];
83  $index = $#{$weatherdata{$timestr}->{$currLbl}};
84  if ($atts{'xsi:nil'}) {
85  $weatherdata{$timestr}->{$currLbl}->[$index]->{visibility} =
86  'unlimited';
87  }
88  }
89 }
90 
91 sub conditionsChar {
92  my ($expat, $text) = @_;
93  if ($expat->in_element('visibility')) {
94  $timestr = $currtimearray->[$expat->{timeindex}];
95  $weatherdata{$timestr}->{$currLbl}->{visibility} = $text;
96  }
97 }
98 
99 sub conditionsEnd {
100  my ($expat, $name) = @_;
101 
102  if ($name eq 'weather') {
103  $expat->setHandlers( Start => \&weatherStart,
104  End => \&weatherEnd, Char => \&weatherChar );
105  }
106  if ($name eq 'weather-conditions') {
107  $expat->{timeindex}++;
108  }
109 }
110 
111 sub weatherStart {
112  my ($expat, $name, %atts) = @_;
113 
114  if ($expat->in_element('parameters')) {
115  $currLbl = $name;
116  if ($atts{type}) {
117  $currLbl .= "_$atts{type}";
118  }
119  $currtimearray = $timelayout{$atts{'time-layout'}};
120  $expat->{timeindex} = 0;
121 
122  if ($name eq 'weather') {
123  $expat->setHandlers( Start => \&conditionsStart,
124  End =>\&conditionsEnd, Char => \&conditionsChar);
125  }
126  }
127 
128 }
129 
130 sub weatherEnd {
131  my ($expat, $name, %atts) = @_;
132  if ($name eq 'parameters') {
133  $expat->setHandlers( Char=> \&Text, Start => \&StartTag );
134  return;
135  }
136 }
137 
138 sub weatherChar {
139  my ($expat, $text) = @_;
140  if ($expat->in_element('value') || $expat->in_element('icon-link')) {
141  $timestr = $currtimearray->[$expat->{timeindex}++];
142  $weatherdata{$timestr}->{$currLbl} = $text;
143  }
144 
145 }
146 
147 sub timeStart {
148  my $expat = shift;
149 }
150 
151 sub timeChar {
152  my ($expat, $text) = @_;
153  if ($expat->in_element('layout-key')) {
154  $currKey = $text;
155  $currtimearray = [];
156  }
157 
158  if ($expat->in_element('start-valid-time')) {
159  # yea, putting it into almost the same format, the reason for this that
160  # you can't get a date out of UnixDate that has tz in 0:00 format,
161  # annoying.
162  $text = UnixDate($text, "%O%z");
163  if ($currKey =~ /p3h/) {
164  push (@$currtimearray, $text);
165  }
166  $expat->{CurrStartTime} = $text;
167  }
168  if ($expat->in_element('end-valid-time')) {
169  $text = UnixDate($text, "%O%z");
170  if ($currKey !~ /p3h/) {
171  push (@$currtimearray, "$expat->{CurrStartTime},$text");
172  }
173 
174  }
175 }
176 
177 ########## Exported method to do parsing #################
178 
179 sub doParse {
180  my ($lat, $lon, $start, $end, $units, $params) = @_;
181  my $product = "time-series";
182 
183  my $NDFD_XML = ndfdXML->NDFDgen(SOAP::Data->name("latitude" => $lat),
184  SOAP::Data->name("longitude" => $lon),
185  SOAP::Data->name("product" => $product),
186  SOAP::Data->name("startTime" => $start),
187  SOAP::Data->name("endTime" => $end),
188  SOAP::Data->name("Unit" => $units),
189  SOAP::Data->name("weatherParameters" =>
190  \SOAP::Data->value(
191  SOAP::Data->type('boolean')->name("maxt" => $params->{maxt}),
192  SOAP::Data->type('boolean')->name("mint" => $params->{mint}),
193  SOAP::Data->type('boolean')->name("temp" => $params->{temp}),
194  SOAP::Data->type('boolean')->name("dew" => $params->{dew}),
195  SOAP::Data->type('boolean')->name("pop12" => $params->{pop12}),
196  SOAP::Data->type('boolean')->name("qpf" => $params->{qpf}),
197  SOAP::Data->type('boolean')->name("sky" => $params->{sky}),
198  SOAP::Data->type('boolean')->name("snow" => $params->{snow}),
199  SOAP::Data->type('boolean')->name("wspd" => $params->{wspd}),
200  SOAP::Data->type('boolean')->name("wdir" => $params->{wdir}),
201  SOAP::Data->type('boolean')->name("wx" => $params->{wx}),
202  SOAP::Data->type('boolean')->name("waveh" => $params->{waveh}),
203  SOAP::Data->type('boolean')->name("icons" => $params->{icons}),
204  SOAP::Data->type('boolean')->name("rh" => $params->{rh}),
205  SOAP::Data->type('boolean')->name("appt" => $params->{appt}))));
206 
207  my $parser = new XML::Parser(Style => 'Stream');
208  $parser->parse("$NDFD_XML");
209  return (\%weatherdata, $creationdate);
210 }
211 
212 1;