File Coverage

blib/lib/WebService/MODIS.pm
Criterion Covered Total %
statement 39 420 9.2
branch 0 182 0.0
condition 0 12 0.0
subroutine 13 47 27.6
pod 18 24 75.0
total 70 685 10.2


line stmt bran cond sub pod time code
1             package WebService::MODIS;
2              
3 1     1   581 use strict;
  1         1  
  1         22  
4 1     1   4 use warnings;
  1         1  
  1         17  
5 1     1   4 use Carp;
  1         1  
  1         39  
6 1     1   4 use URI;
  1         1  
  1         27  
7 1     1   5 use LWP::UserAgent;
  1         1  
  1         17  
8 1     1   437 use HTTP::Cookies;
  1         5135  
  1         22  
9 1     1   375 use Net::Netrc;
  1         9321  
  1         32  
10 1     1   6 use File::Basename;
  1         2  
  1         50  
11 1     1   391 use File::HomeDir;
  1         4139  
  1         44  
12 1     1   5 use File::Path qw(make_path);
  1         2  
  1         35  
13 1     1   379 use Date::Simple;
  1         3572  
  1         32  
14 1     1   5 use List::Util qw(any max min none);
  1         2  
  1         135  
15              
16             require Exporter;
17             our @ISA = qw(Exporter);
18              
19             our %EXPORT_TAGS = ( 'all' => [ qw(initCache readCache writeCache getCacheState getVersions getModisProducts getModisDates getModisGlobal) ] );
20             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
21             our @EXPORT = qw(initCache readCache writeCache getCacheState getVersions isGlobal);
22              
23             our $VERSION = '2.0';
24              
25             ## Redefine the authentication function
26             {
27 1     1   7 no warnings 'redefine';
  1         1  
  1         3772  
28             sub LWP::UserAgent::get_basic_credentials {
29 0     0 1   my ($self, $realm, $url, $isproxy) = @_;
30 0           my $uri = URI->new($url);
31 0 0         if (my $tuple = Net::Netrc->lookup($uri->host)) {
32 0           return ($tuple->login, $tuple->password);
33             } else {
34 0           croak "Did not find username/password for machine '".$uri->host."' in ~/.netrc!\n";
35             }
36             }
37             }
38              
39             my %modisProducts = ();
40             my %modisDates = ();
41             my %modisGlobal = ();
42             my $modisProductsFile = "ModisProducts.pl";
43             my $modisDatesFile = "ModisDates.pl";
44             my $modisGlobalFile = "ModisGlobal.pl";
45             my $cacheDir;
46              
47             my $cacheState = '';
48              
49             my $BASE_URL = "https://e4ftl01.cr.usgs.gov";
50             my @DATA_DIR = ("MOLA", "MOLT", "MOTA");
51              
52             if (lc($^O) =~ /mswin/) {
53             $cacheDir = File::HomeDir->my_home()."/AppData/Local/WebService-MODIS";
54             } else {
55             $cacheDir = File::HomeDir->my_home()."/.cache/WebService-MODIS";
56             }
57              
58             sub new {
59 0     0 1   my $class = shift;
60 0           my %options = @_;
61 0           my $self = {
62             product => '',
63             version => '',
64             dates => [],
65             h => [],
66             v => [],
67             ifExactDates => 0,
68             ifExactHV => 0,
69             targetDir => ".",
70             forceReload => 0,
71             %options,
72             url => [],
73             };
74 0           bless($self, $class);
75              
76             # User input error checks
77 0 0         $self->product($self->{product}) if ($self->{product} ne '');
78 0 0         $self->version($self->{version}) if ($self->{version} ne '');
79 0 0         $self->dates($self->{dates}) if (@{$self->{dates}} != 0);
  0            
80 0 0         $self->h($self->{h}) if (@{$self->{h}} != 0);
  0            
81 0 0         $self->v($self->{v}) if (@{$self->{v}} != 0);
  0            
82 0 0         $self->ifExactDates($self->{ifExactDates}) if ($self->{ifExactDates} != 0);
83 0 0         $self->ifExactHV($self->{ifExactHV}) if ($self->{ifExactHV} != 0);
84              
85 0           return($self);
86             }
87              
88             #######################################################################
89             ### For debuging and informations
90              
91             # returns the cached products data hash
92             sub getModisProducts() {
93 0     0 0   return(%modisProducts);
94             }
95              
96             #returns the reference to the dates array
97             sub getModisDates($) {
98 0     0 0   my $product = shift;
99 0           return($modisDates{$product});
100             }
101              
102             # returns the cached global grid check hash
103             sub getModisGlobal() {
104 0     0 0   return(%modisGlobal);
105             }
106              
107             ########################################################################
108             ### Cache not related to object. Need to create own object, which gets
109             ### part of WebService::MODIS
110              
111             ### retrieve the directory structure from the server
112             sub initCache {
113             # my $self = shift;
114 0     0 1   %modisProducts = getAvailProducts();
115 0           %modisDates = getAvailDates(\%modisProducts);
116 0           for (keys %modisProducts) {
117 0           my $check = isGlobal($_);
118 0           $modisGlobal{$_} = $check;
119             }
120 0           $cacheState = "mem";
121             }
122              
123             ### load the previously saved server side directory structure from
124             ### local files
125             sub readCache {
126             # my $self = shift;
127 0     0 1   my $arg = shift;
128 0 0         $cacheDir = $arg if ($arg);
129 0 0         croak "Cache directory '$cacheDir' does not exist. Use 'writeCache' create it first or check your parameter!" if (!-d $cacheDir);
130              
131 0 0         if (-s "$cacheDir/$modisProductsFile") {
132 0           %modisProducts = do "$cacheDir/$modisProductsFile";
133 0 0         if (-s "$cacheDir/$modisDatesFile") {
134 0           %modisDates = do "$cacheDir/$modisDatesFile";
135 0 0         if (-s "$cacheDir/$modisGlobalFile") {
136 0           %modisGlobal = do "$cacheDir/$modisGlobalFile";
137 0           $cacheState = "file";
138 0           return;
139             } else {
140 0           croak "Cache file '$cacheDir/$modisGlobalFile' is empty. Use 'writeCache' to recreate it.";
141             }
142             } else {
143 0           croak "Cache file '$cacheDir/$modisDatesFile' is empty. Use 'writeCache' to recreate it.";
144             }
145             } else {
146 0           croak "Cache file '$cacheDir/$modisProductsFile' is empty. Use 'writeCache' to recreate it.";
147             }
148 0           return(99);
149             }
150              
151             ### write the server side directory structure to local files
152             sub writeCache {
153             # my $self = shift;
154 0     0 1   my $arg = shift;
155 0 0         $cacheDir = $arg if ($arg);
156              
157 0 0         croak "Nothing cached yet!" if ($cacheState eq '');
158 0 0         carp "Rewriting old cache to file" if ($cacheState eq 'file');
159              
160 0 0         if ( ! -d $cacheDir ) {
161 0 0         make_path($cacheDir) or croak "Could not create cache dir ('$cacheDir'): $!";
162             }
163              
164 0           my $fhd;
165              
166 0 0         open($fhd, ">", "$cacheDir/$modisProductsFile") or
167             croak "cannot open '$cacheDir/$modisProductsFile' for writing: $!\n";
168 0           for (keys %modisProducts) {
169 0           print $fhd "'$_' => '$modisProducts{$_}',\n";
170             }
171 0 0         close($fhd) or carp "close '$cacheDir/$modisProductsFile' failed: $!";
172              
173 0 0         open($fhd, ">", "$cacheDir/$modisDatesFile") or
174             croak "cannot open '$cacheDir/$modisDatesFile' for writing: $!\n";
175 0           for (keys %modisDates) {
176 0           print $fhd "'$_' => ['".join("', '", @{$modisDates{$_}})."'],\n";
  0            
177             }
178 0 0         close($fhd) or carp "close '$cacheDir/$modisDatesFile' failed: $!";
179              
180 0 0         open($fhd, ">", "$cacheDir/$modisGlobalFile") or
181             croak "cannot open '$cacheDir/$modisGlobalFile' for writing: $!\n";
182 0           for (keys %modisGlobal) {
183 0           print $fhd "'$_' => $modisGlobal{$_},\n";
184             }
185              
186 0           $cacheState = "file";
187             }
188              
189             sub getCacheState {
190             # my $self = shift;
191 0     0 1   return $cacheState;
192             }
193              
194             ### return a list of available version for a given product
195             sub getVersions($) {
196 0     0 1   my $product = shift;
197 0           my @versions = ();
198 0           foreach (keys %modisProducts) {
199 0 0         next if (! /$product/);
200 0           s/$product.//;
201 0           push(@versions, $_);
202             }
203 0           return(@versions);
204             }
205              
206             ### check is Product is global or on sinusoidal grid (with h??v?? in name)
207             sub isGlobal($) {
208 0     0 1   my $product = shift;
209 0           my $global = 1;
210              
211 0 0   0     croak "'$product' is not in the MODIS product list: Check name or refresh the cache." if (! any { /$product/ } keys %modisProducts);
  0            
212              
213 0           my @flist = getDateFullURLs($product, ${$modisDates{$product}}[0]);
  0            
214 0           my $teststr = $flist[0];
215 0           $teststr =~ s/.*\///;
216 0 0         $global = 0 if ($teststr =~ /h[0-9]{2}v[0-9]{2}/);
217 0           return($global);
218             }
219              
220             ##################################################
221             ### methods for returning object informations
222             ### or setting them
223              
224             sub product {
225 0     0 1   my $self = shift;
226              
227 0 0         if (@_) {
228 0 0         if ($cacheState eq '') {
229 0           carp "Cache not initialized or loaded, cannot check availability of '$_[0]'.";
230             } else {
231 0           my $failed = 1;
232 0 0   0     $failed = 0 if any { /$_[0]\.[0-9]{3}/ } (keys %modisProducts);
  0            
233 0 0         croak "Product '$_[0]' not available!" if $failed;
234             }
235 0           $self->{product} = shift;
236 0           $self->{version} = '';
237 0           $self->{url} =[];
238 0           return;
239             }
240 0           return $self->{product};
241             }
242              
243             sub version {
244 0     0 1   my $self = shift;
245 0 0         if (@_) {
246 0           $self->{version} = shift;
247 0 0         if ($self->{product} eq '') {
248 0           carp "No product specified yet, so specifying the version does not make sense.";
249             } else {
250 0           my @vers = getVersions($self->{product});
251 0 0   0     if (none {/$self->{version}/} @vers) {
  0            
252 0           carp "Version ".$self->{version}." does not exist! Resetting it to ''.";
253 0           $self->{version} = ''
254             }
255             }
256 0           $self->{url} = [];
257 0           return;
258             }
259 0           return $self->{version};
260             }
261              
262             sub dates {
263 0     0 1   my $self = shift;
264 0 0         if (@_) {
265 0           my $refDates = shift;
266 0 0         if ($self->{product} eq '') {
267 0           carp "No product specified yet, No availability check possible.";
268             } else {
269             # check availability
270             }
271 0           $self->{dates} = $refDates;
272 0           $self->{url} = [];
273 0           return;
274             }
275 0           return @{$self->{dates}};
  0            
276             }
277              
278             sub h {
279 0     0 1   my $self = shift;
280 0 0         if (@_) {
281 0           my $refH = shift;
282 0 0   0     if (any {$_ < 0 or $_ > 35} @$refH) {
  0 0          
283 0           croak "Invalid h values supplied. Valid range: 0-35.";
284             }
285 0           $self->{h} = $refH;
286 0           $self->{url} = [];
287 0           return;
288             }
289 0           return @{$self->{h}};
  0            
290             }
291              
292             sub v {
293 0     0 1   my $self = shift;
294 0 0         if (@_) {
295 0           my $refV = shift;
296 0 0   0     if (any {$_ < 0 or $_ > 17} @$refV) {
  0 0          
297 0           croak "Invalid v values supplied. Valid range: 0-17.";
298             }
299 0           $self->{v} = $refV;
300 0           $self->{url} = [];
301 0           return;
302             }
303 0           return @{$self->{v}};
  0            
304             }
305              
306             sub ifExactDates {
307 0     0 1   my $self = shift;
308 0 0         if (@_) {
309 0           my $nDates = @{$self->{dates}};
  0            
310 0 0         carp "Sure you want to set this before setting the dates!" if ($nDates==0);
311 0           $self->{ifExactDates} = shift;
312 0           $self->{url} = [];
313 0           return;
314             }
315 0           return $self->{ifExactDates};
316             }
317              
318             sub ifExactHV {
319 0     0 1   my $self = shift;
320 0 0         if (@_) {
321             # include error checks
322 0           my $nH = @{$self->{h}};
  0            
323 0           my $nV = @{$self->{v}};
  0            
324 0 0 0       carp "You are setting 'ifExactHV' before setting 'h' and/or 'v'!" if ($nH==0 or $nV==0);
325 0           $self->{ifExactHV} = shift;
326 0           $self->{url} = [];
327 0           return;
328             }
329 0 0         if ($self->{ifExactHV}) {
330 0           my $nH = @{$self->{h}};
  0            
331 0           my $nV = @{$self->{v}};
  0            
332 0 0 0       carp "You set 'ifExactHV' before setting 'h' and/or 'v'!" if ($nH==0 or $nV==0);
333 0 0         carp "If ifExactHV is set, 'h' and 'v' should have equal length!" if ($nH != $nV);
334             }
335 0           return $self->{ifExactHV};
336             }
337              
338             sub url {
339 0     0 1   my $self = shift;
340 0           return @{$self->{url}};
  0            
341             }
342              
343             ########################################################################
344             ### method initializing the URL list
345             ### does not need to be done by hand, is called from within download method.
346             ### This method also checks for all invalid combinations.
347              
348             sub createUrl {
349 0     0 1   my $self = shift;
350 0           my $product = $self->{product};
351              
352 0 0   0     croak "Product '$product' unknown!" if (! any { /$product/ } (keys %modisProducts));
  0            
353              
354 0           my $version = $self->{version};
355 0           my @availVersions = getVersions($product);
356              
357             ### check the MODIS product version
358 0 0         if ($version ne '') {
359 0 0   0     if (any { /$version/ } @availVersions) {
  0            
360 0           $product = "${product}.${version}";
361             } else {
362 0           croak "Version $version not available for $product (available: ".join(" ,", @availVersions).").\n";
363             }
364             } else {
365 0           $version='000';
366 0           foreach (@availVersions) {
367 0 0         $version = $_ if (int($_) > int($version));
368             }
369 0           $product = "${product}.${version}";
370             }
371 0           $self->{version} = $version;
372              
373             ### check the product date availability and reset the $self->{dates} array
374 0 0         if ($self->{ifExactDates}) {
375 0           my @dates = @{$self->{dates}};
  0            
376 0           my @cleanedDates = @dates;
377 0           foreach (@dates) {
378 0           my $failed = 0;
379 0 0   0     $failed = 1 if none { /$_/ } @{$modisDates{$product}};
  0            
  0            
380 0 0         if ($failed) {
381 0           @cleanedDates = grep { $_ != $_ } @cleanedDates;
  0            
382 0           carp "Date '$_' not available! Removing it from list";
383             }
384             }
385 0           @dates = ();
386 0           foreach (@cleanedDates) {
387 0           s/\./\-/g;
388 0           push(@dates, Date::Simple->new($_));
389             }
390 0           $self->{dates} = \@dates;
391             } else {
392 0           my @dates = @{$self->{dates}};
  0            
393 0           my @newDates = ();
394 0           foreach (@dates) {
395 0           s/\./\-/g;
396 0           push(@newDates, Date::Simple->new($_));
397             }
398 0           my @cleanedDates = ();
399 0           foreach (@{$modisDates{$product}}) {
  0            
400 0           s/\./\-/g;
401 0           my $modisDate = Date::Simple->new($_);
402 0 0         next if ($modisDate - min(@newDates) < 0);
403 0 0         next if ($modisDate - max(@newDates) > 0);
404 0           push(@cleanedDates, $modisDate);
405             }
406 0           $self->{dates} = \@cleanedDates;
407             }
408              
409             ### check the h and v availability, but only if necessary
410 0 0         if (!$modisGlobal{$product}) {
411 0           my @h = @{$self->{h}};
  0            
412 0           my @v = @{$self->{v}};
  0            
413 0 0         if ($self->{ifExactHV}) {
414 0           my $nH = @{$self->{h}};
  0            
415 0           my $nV = @{$self->{v}};
  0            
416 0 0         if ($nH != $nV) {
417 0 0         carp "If ifExactHV is set, 'h' and 'v' should have equal length!" if ($nH != $nV);
418 0           @h = splice(@h, 0, min($nH, $nV));
419 0           @v = splice(@v, 0, min($nH, $nV));
420             }
421             } else {
422 0           my @newH = ();
423 0           my @newV = ();
424 0           foreach my $h (min(@h)..max(@h)) {
425 0           foreach my $v (min(@v)..max(@v)) {
426 0           push(@newH, $h);
427 0           push(@newV, $v);
428             }
429             }
430 0           $self->{h} = \@newH;
431 0           $self->{v} = \@newV;
432             }
433             }
434              
435 0           my @url = ();
436 0           foreach (@{$self->{dates}}) {
  0            
437 0           my @fullUrl = getDateFullURLs($product, $_->format("%Y.%m.%d"));
438 0 0         if (!$modisGlobal{$product}) {
439 0           my $nHV = @{$self->{h}};
  0            
440 0           for (my $i=0; $i < $nHV; $i++) {
441 0           my $pat = sprintf("h%02iv%02i", ${$self->{h}}[$i], ${$self->{v}}[$i]);
  0            
  0            
442 0           my @newUrl;
443 0           foreach (@fullUrl) {
444 0 0         if (/$pat/) {
445 0           push(@newUrl, $_);
446             }
447             }
448 0           my $nNewUrl = @newUrl;
449 0 0         if ($nNewUrl == 1) {
    0          
450 0           push(@url, $newUrl[0]);
451             } elsif ($nNewUrl < 1) {
452 0           carp(sprintf("$product: Missing file for %s @ %s.\n", $pat, $_->format("%Y.%m.%d")));
453             } else {
454             # check for duplicate files here and choose the latest one
455 0           carp(sprintf("$product: %i files for %s @ %s, choosing the newest.\n", $nNewUrl, $pat, $_->format("%Y.%m.%d")));
456 0           my $createDate = $newUrl[0];
457 0           $createDate =~ s/\.hdf$//;
458 0           $createDate =~ s/^.*\.//g;
459 0           $createDate = int($createDate);
460 0           my $newest = 0;
461 0           for (my $k=0; $k < $nNewUrl; $k++) {
462 0           s/\.hdf$//;
463 0           s/^.*\.//g;
464 0 0         if (int($_) > $createDate) {
465 0           $newest = $k;
466 0           $createDate = int($_);
467             }
468             }
469 0           push(@url, $newUrl[$newest]);
470             }
471             }
472             } else {
473 0           my $nUrl = @fullUrl;
474 0 0         if ($nUrl == 1) {
    0          
475 0           push(@url, $fullUrl[0]);
476             } elsif ($nUrl < 1) {
477 0           carp(sprintf("$product: Missing file @ %s.\n", $_->format("%Y.%m.%d")));
478             } else {
479             # check for duplicate files here and choose the latest one
480 0           warn(sprintf("$product: %i files @ %s, choosing the newest.\n", $nUrl, $_->format("%Y.%m.%d")));
481 0           my $createDate = $fullUrl[0];
482 0           $createDate =~ s/\.hdf$//;
483 0           $createDate =~ s/^.*\.//g;
484 0           $createDate = int($createDate);
485 0           my $newest = 0;
486 0           for (my $k=0; $k < $nUrl; $k++) {
487 0           s/\.hdf$//;
488 0           s/^.*\.//g;
489 0 0         if (int($_) > $createDate) {
490 0           $newest = $k;
491 0           $createDate = int($_);
492             }
493             }
494 0           push(@url, $fullUrl[$newest]);
495             }
496             }
497             }
498 0           $self->{url} = \@url;
499             }
500              
501             ########################################################################
502             ### method for download
503              
504             sub download {
505 0     0 1   my $self = shift;
506 0           my $arg = shift;
507 0 0         $self->{targetDir} = $arg if ($arg);
508 0           $arg = shift;
509 0 0         $self->{forceReload} = $arg if ($arg);
510              
511 0           my $nUrl = @{$self->{url}};
  0            
512              
513 0 0         $self->createUrl if ($nUrl == 0);
514 0           $nUrl = @{$self->{url}};
  0            
515              
516 0 0         if (! -d $self->{targetDir}) {
517 0 0         make_path($self->targetDir) or croak "Cannot create directory '$self->{targetDir}': $!\n";
518             }
519              
520             # adjusted from http://stackoverflow.com/questions/6813726/continue-getting-a-partially-downloaded-file
521 0           my $ua = LWP::UserAgent->new(cookie_jar => HTTP::Cookies->new());
522              
523 0           for (my $i=0; $i < $nUrl; $i++) {
524 0           my $file = $self->{targetDir}."/".basename(@{$self->{url}}[$i]);
  0            
525 0 0 0       unlink($file) if ($self->{forceReload} && -f $file);
526 0 0         open(my $fh, '>>:raw', $file) or croak "Cannot open '$file': $!\n";
527 0           my $bytes = -s $file;
528 0           my $res;
529 0 0 0       if ( $bytes && ! $self->{forceReload}) {
530             #print "resume ${$self->{url}}[$i] -> $file ($bytes) " if ($verbose);
531             $res = $ua->get(
532 0           ${$self->{url}}[$i],
533             'Range' => "bytes=$bytes-",
534 0     0     ':content_cb' => sub { my ( $chunk ) = @_; print $fh $chunk; }
  0            
535 0           );
536             } else {
537             #print "$URL[$i] -> $destination[$i] " if ($verbose);
538             $res = $ua->get(
539 0           ${$self->{url}}[$i],
540 0     0     ':content_cb' => sub { my ( $chunk ) = @_; print $fh $chunk; }
  0            
541 0           );
542             }
543 0           close $fh;
544              
545 0           my $status = $res->status_line;
546 0 0         if ( $status =~ /^(200|206|416)/ ) {
547             #print "OK\n" if ($verbose && $status =~ /^20[06]/);
548             #print "already complete\n" if ($verbose && $status =~ /^416/);
549             } else {
550 0           print "Unknown STATUS: '$status'!\n";
551             }
552             }
553             }
554              
555             ###################################################
556             ###################################################
557             ### Internal functions
558              
559             ### retrieve a list of available MODIS Products
560             ### and return a hash with the name of the first subdirectory
561             sub getAvailProducts () {
562 0     0 0   my $caller = (caller)[0];
563 0 0         carp "This is an internal WebService::MODIS function. You should know what you are doing." if ($caller ne "WebService::MODIS");
564              
565 0           my %lookupTable = ();
566 0           my $ua = new LWP::UserAgent;
567 0           foreach my $subdir (@DATA_DIR) {
568 0           my $response = $ua->get("${BASE_URL}/${subdir}");
569              
570 0 0         unless ($response->is_success) {
571 0           die $response->status_line;
572             }
573              
574 0           my $content = $response->decoded_content();
575 0           my @content = split("\n", $content);
576 0           foreach (@content) {
577 0 0         next if (!/href="M/);
578 0           s/.*href="//;
579 0           s/\/.*//;
580              
581 0 0         print "Key already exists\n" if exists $lookupTable{$_};
582 0 0         print "Key already defined\n" if defined $lookupTable{$_};
583 0 0         print "True\n" if $lookupTable{$_};
584              
585 0           $lookupTable{$_} = $subdir;
586             }
587             }
588 0           return %lookupTable;
589             }
590              
591             ### get the available second level directories, named by date
592             ### (YYYY.MM.DD) under which the hdf files reside. This does
593             ### not ensure that the files are really there.
594             sub getAvailDates() {
595 0     0 0   my $caller = (caller)[0];
596 0 0         carp "This is an internal WebService::MODIS function. You should know what you are doing." if ($caller ne "WebService::MODIS");
597              
598 0           my %lookupTable = ();
599              
600 0           my $ua = new LWP::UserAgent;
601 0           foreach my $key (keys %modisProducts) {
602 0           my @dates=();
603 0           my $response = $ua->get("${BASE_URL}/$modisProducts{$key}/$key");
604              
605 0 0         unless ($response->is_success) {
606 0           die $response->status_line;
607             }
608              
609 0           my $content = $response->decoded_content();
610 0           my @content = split("\n", $content);
611 0           foreach (@content) {
612 0 0         next if (!/href="20[0-9]{2}\.[0-9]{2}\.[0-9]{2}/);
613 0           s/.*href="//;
614 0           s/\/.*//;
615 0           push(@dates, $_);
616             }
617 0           my $datesString = "['".join("', '", @dates)."']";
618 0           $lookupTable{$key} = eval $datesString;
619             }
620 0           return %lookupTable;
621             }
622              
623             ### return a file list for one product and date on the server
624             sub getDateFullURLs($$) {
625 0     0 0   my $caller = (caller)[0];
626 0 0         carp "This is an internal WebService::MODIS function. You should know what you are doing." if ($caller ne "WebService::MODIS");
627              
628 0           my $product = shift;
629 0           my $date = shift;
630              
631 0           my @flist = ();
632              
633 0           my $ua = new LWP::UserAgent;
634              
635 0           my $response = $ua->get("${BASE_URL}/$modisProducts{$product}/$product/$date");
636            
637 0 0         unless ($response->is_success) {
638 0           die $response->status_line;
639             }
640              
641 0           my $content = $response->decoded_content();
642 0           my @content = split("\n", $content);
643 0           foreach (@content) {
644 0 0         next if (!/href="M/);
645 0 0         next if (/hdf.xml/);
646 0           s/.*href="//;
647 0           s/".*//;
648 0           push(@flist, "${BASE_URL}/$modisProducts{$product}/$product/$date/$_");
649             }
650 0           return(@flist);
651             }
652              
653             1;
654              
655             __END__