File Coverage

blib/lib/Astro/DSS.pm
Criterion Covered Total %
statement 114 195 58.4
branch 14 48 29.1
condition 1 3 33.3
subroutine 12 23 52.1
pod 7 15 46.6
total 148 284 52.1


line stmt bran cond sub pod time code
1             package Astro::DSS;
2              
3             # ---------------------------------------------------------------------------
4              
5             #+
6             # Name:
7             # Astro::DSS
8              
9             # Purposes:
10             # Perl wrapper for the Digital Sky Survey (DSS)
11              
12             # Language:
13             # Perl module
14              
15             # Description:
16             # This module wraps the DSS online database.
17              
18             # Authors:
19             # Alasdair Allan (aa@astro.ex.ac.uk)
20              
21             # Revision:
22             # $Id: DSS.pm,v 1.7 2003/02/21 18:52:15 aa Exp $
23              
24             # Copyright:
25             # Copyright (C) 2001 University of Exeter. All Rights Reserved.
26              
27             #-
28              
29             # ---------------------------------------------------------------------------
30              
31             =head1 NAME
32              
33             Astro::DSS - An Object Orientated interface to the Digital Sky Survey
34              
35             =head1 SYNOPSIS
36              
37             $dss = new Astro::DSS( RA => $ra,
38             Dec => $dec,
39             Target => $object_name,
40             Equinox => $equinox,
41             Xsize => $x_arcmin,
42             Ysize => $y_arcmin,
43             Survey => $dss_survey,
44             Format => $type );
45              
46             my $file_name = $dss->querydb();
47              
48             =head1 DESCRIPTION
49              
50             Stores information about an prospective DSS query and allows the query to
51             be made, returning a filename pointing to the file returned.
52              
53             The object will by default pick up the proxy information from the HTTP_PROXY
54             and NO_PROXY environment variables, see the LWP::UserAgent documentation for
55             details.
56              
57             It will save returned files into the ESTAR_DATA directory or to TMP if
58             the ESTAR_DATA environment variable is not defined.
59              
60             =cut
61              
62             # L O A D M O D U L E S --------------------------------------------------
63              
64 1     1   9640 use strict;
  1         3  
  1         113  
65 1     1   5 use vars qw/ $VERSION /;
  1         2  
  1         313  
66              
67 1     1   2284 use LWP::UserAgent;
  1         118108  
  1         49  
68 1     1   3930 use Net::Domain qw(hostname hostdomain);
  1         30039  
  1         346  
69 1     1   14 use File::Spec;
  1         3  
  1         32  
70 1     1   7 use Carp;
  1         2  
  1         3139  
71              
72             '$Revision: 1.7 $ ' =~ /.*:\s(.*)\s\$/ && ($VERSION = $1);
73              
74             # C O N S T R U C T O R ----------------------------------------------------
75              
76             =head1 REVISION
77              
78             $Id: DSS.pm,v 1.7 2003/02/21 18:52:15 aa Exp $
79              
80             =head1 METHODS
81              
82             =head2 Constructor
83              
84             =over 4
85              
86             =item B
87              
88             Create a new instance from a hash of options
89              
90             $dss = new Astro::DSS( RA => $ra,
91             Dec => $dec,
92             Target => $object_name,
93             Equinox => $equinox,
94             Xsize => $x_arcmin,
95             Ysize => $y_arcmin,
96             Survey => $dss_survey,
97             Format => $image_type );
98              
99             returns a reference to an DSS query object.
100              
101             =cut
102              
103             sub new {
104 2     2 1 1179 my $proto = shift;
105 2   33     15 my $class = ref($proto) || $proto;
106              
107             # bless the query hash into the class
108 2         19 my $block = bless { OPTIONS => {},
109             URL => undef,
110             QUERY => undef,
111             USERAGENT => undef,
112             DATADIR => undef }, $class;
113              
114             # Configure the object
115 2         10 $block->configure( @_ );
116              
117 2         25 return $block;
118              
119             }
120              
121             # Q U E R Y M E T H O D S ------------------------------------------------
122              
123             =back
124              
125             =head2 Accessor Methods
126              
127             =over 4
128              
129             =item B
130              
131             Returns a filename of the image returned from a DSS query.
132              
133             $filename = $dss->querydb();
134              
135             =cut
136              
137             sub querydb {
138 2     2 1 563 my $self = shift;
139              
140             # call the private method to make the actual ADS query
141 2         8 my $file_name = $self->_make_query();
142              
143             # check for failed connect
144 2 50       11 return undef unless defined $file_name;
145              
146             # return the file name
147 2         12 return $file_name;
148              
149             }
150              
151             =item B
152              
153             Return (or set) the current proxy for the ADS request.
154              
155             $query->proxy( 'http://wwwcache.ex.ac.uk:8080/' );
156             $proxy_url = $query->proxy();
157              
158             =cut
159              
160             sub proxy {
161 0     0 1 0 my $self = shift;
162              
163             # grab local reference to user agent
164 0         0 my $ua = $self->{USERAGENT};
165              
166 0 0       0 if (@_) {
167 0         0 my $proxy_url = shift;
168 0         0 $ua->proxy('http', $proxy_url );
169             }
170              
171             # return the current proxy
172 0         0 return $ua->proxy('http');
173              
174             }
175              
176             =item B
177              
178             Return (or set) the current timeout in seconds for the DSS request.
179              
180             $dss->timeout( 30 );
181             $proxy_timeout = $dss->timeout();
182              
183             =cut
184              
185             sub timeout {
186 0     0 1 0 my $self = shift;
187              
188             # grab local reference to user agent
189 0         0 my $ua = $self->{USERAGENT};
190              
191 0 0       0 if (@_) {
192 0         0 my $time = shift;
193 0         0 $ua->timeout( $time );
194             }
195              
196             # return the current timeout
197 0         0 return $ua->timeout();
198              
199             }
200              
201             =item B
202              
203             Return (or set) the current base URL for the DSS query.
204              
205             $url = $dss->url();
206             $query->url( "archive.eso.org" );
207              
208             if not defined the default URL is archive.eso.org
209              
210             =cut
211              
212             sub url {
213 0     0 1 0 my $self = shift;
214              
215             # SETTING URL
216 0 0       0 if (@_) {
217              
218             # set the url option
219 0         0 my $base_url = shift;
220 0         0 $self->{URL} = $base_url;
221 0 0       0 if( defined $base_url ) {
222 0         0 $self->{QUERY} = "http://$base_url/dss/dss/image?";
223             }
224             }
225              
226             # RETURNING URL
227 0         0 return $self->{URL};
228             }
229              
230             =item B
231              
232             Returns the user agent tag sent by the module to the ADS server.
233              
234             $agent_tag = $dss->agent();
235              
236             =cut
237              
238             sub agent {
239 0     0 1 0 my $self = shift;
240 0         0 return $self->{USERAGENT}->agent();
241             }
242              
243             # O T H E R M E T H O D S ------------------------------------------------
244              
245              
246             =item B
247              
248             Return (or set) the current target R.A. defined for the DSS query
249              
250             $ra = $dss->ra();
251             $dss->ra( $ra );
252              
253             where $ra should be a string of the form "HH MM SS.SS", e.g. 21 42 42.66
254              
255             =cut
256              
257             sub ra {
258 0     0 0 0 my $self = shift;
259              
260             # SETTING R.A.
261 0 0       0 if (@_) {
262            
263             # grab the new R.A.
264 0         0 my $ra = shift;
265            
266             # mutilate it and stuff it and the current $self->{RA}
267 0         0 $ra =~ s/\s/\+/g;
268 0         0 ${$self->{OPTIONS}}{"ra"} = $ra;
  0         0  
269             }
270            
271             # un-mutilate and return a nicely formated string to the user
272 0         0 my $ra = ${$self->{OPTIONS}}{"ra"};
  0         0  
273 0         0 $ra =~ s/\+/ /g;
274 0         0 return $ra;
275             }
276              
277             =item B
278              
279             Return (or set) the current target Declination defined for the DSS query
280              
281             $dec = $dss->dec();
282             $dss->dec( $dec );
283              
284             where $dec should be a string of the form "+-HH MM SS.SS", e.g. +43 35 09.5
285             or -40 25 67.89
286              
287             =cut
288              
289             sub dec {
290 0     0 0 0 my $self = shift;
291              
292             # SETTING DEC
293 0 0       0 if (@_) {
294              
295             # grab the new Dec
296 0         0 my $dec = shift;
297            
298             # mutilate it and stuff it and the current $self->{DEC}
299 0         0 $dec =~ s/\+/%2B/g;
300 0         0 $dec =~ s/\s/\+/g;
301 0         0 ${$self->{OPTIONS}}{"dec"} = $dec;
  0         0  
302             }
303            
304             # un-mutilate and return a nicely formated string to the user
305 0         0 my $dec = ${$self->{OPTIONS}}{"dec"};
  0         0  
306 0         0 $dec =~ s/\+/ /g;
307 0         0 $dec =~ s/%2B/\+/g;
308 0         0 return $dec;
309              
310             }
311              
312              
313             =item B
314              
315             The equinox for the R.A. and Dec co-ordinates
316              
317             $equinox = $dss->equinox();
318             $dss->equinox( "2000" );
319              
320             defaults to 2000.
321              
322             =cut
323              
324             sub equinox {
325 0     0 0 0 my $self = shift;
326              
327 0 0       0 if (@_) {
328 0         0 ${$self->{OPTIONS}}{"equinox"} = shift;
  0         0  
329             }
330            
331 0         0 return ${$self->{OPTIONS}}{"equinox"};
  0         0  
332              
333             }
334              
335             =item B
336              
337             Instead of querying DSS by R.A. and Dec., you may also query it by object
338             name. Return (or set) the current target object defined for the DSS query,
339             will query SIMBAD for object name resolution.
340              
341             $ident = $dss->target();
342             $dss->target( "HT Cas" );
343              
344             using an object name will override the current R.A. and Dec settings for the
345             Query object (if currently set) and the next querydb() method call will query
346             DSS using this identifier rather than any currently set co-ordinates.
347              
348             =cut
349              
350             sub target {
351 2     2 0 2 my $self = shift;
352              
353             # SETTING IDENTIFIER
354 2 50       6 if (@_) {
355              
356             # grab the new object name
357 2         3 my $ident = shift;
358            
359             # mutilate it and stuff it into $self->{TARGET}
360 2         13 $ident =~ s/\s/\+/g;
361 2         5 ${$self->{OPTIONS}}{"name"} = $ident;
  2         5  
362 2         4 ${$self->{OPTIONS}}{"ra"} = undef;
  2         3  
363 2         2 ${$self->{OPTIONS}}{"dec"} = undef;
  2         6  
364             }
365            
366 2         3 return ${$self->{OPTIONS}}{"name"};
  2         7  
367              
368             }
369              
370             =item B
371              
372             The x extent of the DSS image to be retrieved in arcmin.
373              
374             $xsize = $dss->xsize();
375             $dss->xsize( 20 );
376              
377             Image sizes for FITS, gzipped FITS and GIF are 260kB,
378             110kB and 70 kB respectively for a field of 10*10 arc minutes.
379             There's a limit of around 4 MB for the largest image to be delivered.
380             Images from the DSS2 are bigger, because the pixel size is smaller.
381              
382             =cut
383              
384             sub xsize {
385 0     0 0 0 my $self = shift;
386              
387 0 0       0 if (@_) {
388 0         0 ${$self->{OPTIONS}}{"x"} = shift;
  0         0  
389             }
390            
391 0         0 return ${$self->{OPTIONS}}{"x"};
  0         0  
392              
393             }
394              
395             =item B
396              
397             The y extent of the DSS image to be retrieved in arcmin.
398              
399             $xsize = $dss->ysize();
400             $dss->ysize( 20 );
401              
402             Image sizes for FITS, gzipped FITS and GIF are 260kB, 110kB and 70 kB respectively for a field of 10*10 arc minutes. There's a limit of around 4 MB for the largest image to be delivered. Images from the DSS2 are bigger, because the pixel size is smaller.
403              
404             =cut
405              
406             sub ysize {
407 0     0 0 0 my $self = shift;
408              
409 0 0       0 if (@_) {
410 0         0 ${$self->{OPTIONS}}{"y"} = shift;
  0         0  
411             }
412            
413 0         0 return ${$self->{OPTIONS}}{"y"};
  0         0  
414              
415             }
416              
417             =item B
418              
419             The survey to return
420              
421             $survey = $dss->survey();
422             $dss->survey( "DSS1" );
423              
424             valid choices are DSS1, DSS2-red, DSS2-blue, DSS2-infrared. The entire DSS1 data is stored on magnetic disks at the ESO-ECF Archive. DSS2 is stored on DVD-ROM in a juke box. Retrieval time takes about less than 5 seconds for a DSS1 field and less than 20 seconds for a random DSS2 field in the juke box.
425              
426             The DSS1 survey is 100% complete, while the DSS2-red now covers 98% of the sky; DSS2-blue 45% of the sky and DSS2-infrared 27% of the sky.
427              
428             =cut
429              
430             sub survey {
431 0     0 0 0 my $self = shift;
432              
433 0 0       0 if (@_) {
434 0         0 ${$self->{OPTIONS}}{"Sky-Survey"} = shift;
  0         0  
435             }
436            
437 0         0 return ${$self->{OPTIONS}}{"Sky-Survey"};
  0         0  
438              
439             }
440              
441             =item B
442              
443             The image format required
444              
445             $format = $dss->format();
446             $dss->format( "FITS" );
447              
448             valid format types are FITS and GIF and FITS.gz. The default is to return
449             a GIF Image.
450              
451             =cut
452              
453             sub format {
454 1     1 0 2 my $self = shift;
455              
456 1 50       5 if (@_) {
457 1         1 my $format = shift;
458 1 50       4 if( $format eq "FITS" ) {
    0          
459 1         2 ${$self->{OPTIONS}}{"mime-type"} = "download-fits";
  1         4  
460             } elsif ( $format eq "FITS.gz" ) {
461 0         0 ${$self->{OPTIONS}}{"mime-type"} = "download-gz-fits";
  0         0  
462             } else {
463 0         0 ${$self->{OPTIONS}}{"mime-type"} = "download-gif";
  0         0  
464             }
465             }
466            
467 1         2 return $self->{FORMAT};
468              
469             }
470              
471             # C O N F I G U R E -------------------------------------------------------
472              
473             =back
474              
475             =head2 General Methods
476              
477             =over 4
478              
479             =item B
480              
481             Configures the object, takes an options hash as an argument
482              
483             $dss->configure( %options );
484              
485             Does nothing if the array is not supplied.
486              
487             =cut
488              
489             sub configure {
490 2     2 1 4 my $self = shift;
491              
492             # CONFIGURE DEFAULTS
493             # ------------------
494              
495             # define the default base URL
496 2         9 $self->{URL} = "archive.eso.org";
497            
498             # define the query URLs
499 2         6 my $default_url = $self->{URL};
500 2         7 $self->{QUERY} = "http://$default_url/dss/dss/image?";
501            
502             # Setup the LWP::UserAgent
503 2         11 my $HOST = hostname();
504 2         9612 my $DOMAIN = hostdomain();
505 2         28 $self->{USERAGENT} = new LWP::UserAgent( timeout => 30 );
506 2         3539 $self->{USERAGENT}->agent("Astro::DDS/$VERSION ($HOST.$DOMAIN)");
507              
508             # Grab Proxy details from local environment
509 2         110 $self->{USERAGENT}->env_proxy();
510            
511             # Grab something for DATA directory
512 2 50       17488 if ( defined $ENV{"ESTAR_DATA"} ) {
    0          
513 2 50       140 if ( opendir (DIR, File::Spec->catdir($ENV{"ESTAR_DATA"}) ) ) {
514             # default to the ESTAR_DATA directory
515 2         16 $self->{DATADIR} = File::Spec->catdir($ENV{"ESTAR_DATA"});
516 2         37 closedir DIR;
517             } else {
518             # Shouldn't happen?
519 0         0 croak("Cannot open $ENV{ESTAR_DATA} for incoming files.");
520             }
521             } elsif ( opendir(TMP, File::Spec->tmpdir() ) ) {
522             # fall back on the /tmp directory
523 0         0 $self->{DATADIR} = File::Spec->tmpdir();
524 0         0 closedir TMP;
525             } else {
526             # Shouldn't happen?
527 0         0 croak("Cannot open any directory for incoming files.");
528             }
529            
530             # configure the default options
531 2         7 ${$self->{OPTIONS}}{"ra"} = undef;
  2         8  
532 2         5 ${$self->{OPTIONS}}{"dec"} = undef;
  2         6  
533 2         4 ${$self->{OPTIONS}}{"name"} = undef;
  2         5  
534            
535 2         4 ${$self->{OPTIONS}}{"equinox"} = 2000;
  2         6  
536 2         2 ${$self->{OPTIONS}}{"x"} = 10;
  2         6  
537 2         3 ${$self->{OPTIONS}}{"y"} = 10;
  2         6  
538 2         3 ${$self->{OPTIONS}}{"Sky-Survey"} = "DSS1";
  2         4  
539 2         4 ${$self->{OPTIONS}}{"mime-type"} = "download-gif";
  2         7  
540              
541             # CONFIGURE FROM ARGUEMENTS
542             # -------------------------
543              
544             # return unless we have arguments
545 2 50       9 return undef unless @_;
546              
547             # grab the argument list
548 2         9 my %args = @_;
549              
550             # Loop over the allowed keys and modify the default query options
551 2         5 for my $key (qw / RA Dec Target Equinox Xsize Ysize Survey Format
552             URL Timeout Proxy / ) {
553 22         27 my $method = lc($key);
554 22 100       60 $self->$method( $args{$key} ) if exists $args{$key};
555             }
556              
557             }
558              
559             # T I M E A T T H E B A R --------------------------------------------
560              
561             =back
562              
563             =begin __PRIVATE_METHODS__
564              
565             =head2 Private methods
566              
567             These methods are for internal use only.
568              
569             =over 4
570              
571             =item B<_make_query>
572              
573             Private function used to make an DSS query. Should not be called directly,
574             since it does not parse the results. Instead use the querydb() assessor method.
575              
576             =cut
577              
578             sub _make_query {
579 2     2   4 my $self = shift;
580              
581             # grab the user agent
582 2         4 my $ua = $self->{USERAGENT};
583              
584             # clean out the buffer
585 2         5 $self->{BUFFER} = "";
586              
587             # grab the base URL
588 2         23 my $URL = $self->{QUERY};
589 2         4 my $options = "";
590              
591             # loop round all the options keys and build the query
592 2         4 foreach my $key ( keys %{$self->{OPTIONS}} ) {
  2         11  
593 16         45 $options = $options .
594 16 100       16 "&$key=${$self->{OPTIONS}}{$key}" if defined ${$self->{OPTIONS}}{$key};
  12         29  
595             }
596              
597             # build final query URL
598 2         5 $URL = $URL . $options;
599              
600             # build request
601 2         19 my $request = new HTTP::Request('GET', $URL);
602              
603             # grab page from web
604 2         18711 my $reply = $ua->request($request);
605              
606             # declare file name
607 2         1926605 my $file_name;
608            
609 2 50       6 if ( ${$reply}{"_rc"} eq 200 ) {
  2         88  
610 2 50       6 if ( ${${$reply}{"_headers"}}{"content-type"}
  2         5  
  2         16  
611             eq "application/octet-stream" ) {
612            
613             # mangle filename from $ENV and returned unique(?) filename
614 2         3 $file_name = ${${$reply}{"_headers"}}{"content-disposition"};
  2         5  
  2         8  
615 2         7 my $start_index = index( $file_name, q/"/ );
616 2         5 my $last_index = rindex( $file_name, q/"/ );
617 2         12 $file_name = substr( $file_name, $start_index+1,
618             $last_index-$start_index-1);
619            
620 2         59 $file_name = File::Spec->catfile( $self->{DATADIR}, $file_name);
621             # Open output file
622 2 50       402 unless ( open ( FH, ">$file_name" )) {
623 0         0 croak("Error: Cannont open output file $file_name");
624             }
625              
626             # Needed for Windows (yuck!)
627 2         11 binmode FH;
628            
629             # Write to output file
630 2         5 my $length = length(${$reply}{"_content"});
  2         7  
631 2         4 syswrite( FH, ${$reply}{"_content"}, $length );
  2         1315  
632 2         41 close(FH);
633            
634             }
635             } else {
636 0         0 croak("Error ${$reply}{_rc}: Failed to establish network connection");
  0         0  
637             }
638            
639 2         151 return $file_name;
640             }
641              
642              
643             =item B<_dump_options>
644              
645             Private function for debugging and other testing purposes. It will return
646             the current query options as a hash.
647              
648             =cut
649              
650             sub _dump_options {
651 0     0     my $self = shift;
652              
653 0           return %{$self->{OPTIONS}};
  0            
654             }
655              
656             =back
657              
658             =end __PRIVATE_METHODS__
659              
660             =head1 COPYRIGHT
661              
662             Copyright (C) 2001 University of Exeter. All Rights Reserved.
663              
664             This program was written as part of the eSTAR project and is free software;
665             you can redistribute it and/or modify it under the terms of the GNU Public
666             License.
667              
668             =head1 AUTHORS
669              
670             Alasdair Allan Eaa@astro.ex.ac.ukE,
671              
672             =cut
673              
674             # L A S T O R D E R S ------------------------------------------------------
675              
676             1;