File Coverage

blib/lib/Astro/Catalog/Query/SuperCOSMOS.pm
Criterion Covered Total %
statement 15 15 100.0
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 20 20 100.0


line stmt bran cond sub pod time code
1             package Astro::Catalog::Query::SuperCOSMOS;
2              
3             # Depressingly the generic reg expression used the SkyCat.pm doesn't
4             # seem to work for SuperCOSMOS URL's, eventually we're going to have
5             # to make the regexp more generic. In the interim, I've cut and pasted
6             # the entire module into this sub-class so I can do queries.
7             #
8             # Yes Tim, I know this sucks.
9              
10             =head1 NAME
11              
12             Astro::Catalog::Query::CMC - A query request to the SuperCOSMOS catalogue
13              
14             =head1 SYNOPSIS
15              
16             $supercos = new Astro::Catalog::Query::SuperCOSMOS( RA => $ra,
17             Dec => $dec,
18             Radius => $radius,
19             Nout => $number_out,
20             Colour => $band );
21              
22             my $catalog = $supercos->querydb();
23              
24             =head1 WARNING
25              
26             This code totally ignores the epoch of the observations and the associated
27             proper motions, this pretty much means that for astrometric work the catalogues
28             you get back from the query are pretty much bogus. This should be sorted in
29             the next distribution.
30              
31             =head1 DESCRIPTION
32              
33             The module is an object orientated interface to the online SuperCOSMOS
34             catalogue using the generic Astro::Catalog::Query::SkyCat class
35              
36             Stores information about an prospective query and allows the query to
37             be made, returning an Astro::Catalog::Query::SuperCOSMOS object.
38              
39             The object will by default pick up the proxy information from the HTTP_PROXY
40             and NO_PROXY environment variables, see the LWP::UserAgent documentation for
41             details.
42              
43             See L for the catalog-independent methods.
44              
45             =cut
46              
47             # L O A D M O D U L E S --------------------------------------------------
48              
49 1     1   9615485 use 5.006;
  1         12  
  1         80  
50 1     1   7 use strict;
  1         1  
  1         131  
51 1     1   67 use warnings;
  1         3  
  1         92  
52 1     1   4 use warnings::register;
  1         2  
  1         457  
53 1     1   7 use base qw/ Astro::Catalog::Transport::REST /;
  1         3  
  1         1018  
54             use vars qw/ $VERSION $DEBUG $FOLLOW_DIRS /;
55              
56             use Data::Dumper;
57             use Carp;
58             use File::Spec;
59             use Carp;
60              
61             # generic catalog objects
62             use Astro::Catalog;
63             use Astro::Catalog::Star;
64              
65             use Astro::Flux;
66             use Astro::FluxColor;
67             use Astro::Fluxes;
68             use Number::Uncertainty;
69              
70             $VERSION = '4.31';
71             $DEBUG = 0;
72              
73             # Controls whether we follow 'directory' config entries and recursively
74             # expand those. Default to false at the moment.
75             $FOLLOW_DIRS = 0;
76              
77             # This is the name of the config file that was used to generate
78             # the content in %CONFIG. Can be different to the contents ofg_file
79             # if that
80             my $CFG_FILE;
81              
82             # This is the content of the config file
83             # organized as a hash indexed by remote server shortname
84             # this has the advantage of removing duplicates
85             my %CONFIG;
86              
87             =head1 REVISION
88              
89             $Id: SuperCOSMOS.pm,v 1.11 2005/06/16 03:11:11 aa Exp $
90              
91             =head1 METHODS
92              
93             =head2 Constructor
94              
95             =over 4
96              
97             =item B
98              
99             Simple constructor, handles the 'Colour' option, e.g.
100              
101             long_name: SuperCOSMOS catalog - blue (UKJ) southern survey
102             short_name: SSScat_UKJ@WFAU
103              
104             long_name: SuperCOSMOS catalog - red (UKR) southern survey
105             short_name: SSScat_UKR@WFAU
106              
107             long_name: SuperCOSMOS catalog - near IR (UKI) southern survey
108             short_name: SSScat_UKI@WFAU
109              
110             long_name: SuperCOSMOS catalog - red (ESOR) southern survey
111             short_name: SSScat_ESOR@WFAU
112              
113             $q = new Astro::Catalog::Query::SuperCOSMOS( colour => 'UKJ', %options );
114              
115             Allowed options are 'UKJ', 'UKR', 'UKI', and 'ESOR' for the UK Blue, UK Red,
116             UK near-IR and ESO Red catalogues respectively.
117              
118             All other options are passed on to SUPER::new().
119              
120             =cut
121              
122             sub new {
123             my $proto = shift;
124             my $class = ref($proto) || $proto;
125              
126             # Instantiate via base class
127             my $block = $class->SUPER::new( @_ );
128              
129             return $block;
130             }
131              
132             =back
133              
134             =head2 Accessor methods
135              
136             =over 4
137              
138             =item B<_selected_catalog>
139              
140             Catalog name selected by the user and currently configured for
141             this object. Not to be used outside this class..
142              
143             =cut
144              
145             sub _selected_catalog {
146             my $self = shift;
147             if (@_) {
148             # The class has to be configured as a hash!!!
149             $self->{SKYCAT_CATALOG} = shift;
150             }
151              
152             #print "\nSuperCOSMOS: _selected_catalog() returning " .
153             # $self->{SKYCAT_CATALOG} . "\n" if $DEBUG;
154              
155             return $self->{SKYCAT_CATALOG};
156             }
157              
158             =back
159              
160             =head2 General methods
161              
162             =over 4
163              
164             =item C
165              
166             Configure the object. This calls the base class configure , after it has
167             made sure that a sky cat config file has been read (otherwise we will
168             not be able to vet the incoming arguments.
169              
170             =cut
171              
172             sub configure {
173             my $self = shift;
174              
175             # load a config if we do not have one read yet
176             # Note that this may force a remote URL read via directory
177             # directives even though we do not have a user agent configured...
178             $self->_load_config() unless %CONFIG;
179              
180             # Error if we have no config yet
181             croak "Error instantiating SuperCOSMOS object since no config was located"
182             unless %CONFIG;
183              
184             # Now we need to configure this object based on the
185             # supplied catalog name. This is not really a public interface
186             # let's call it a protected interface available to subclases
187             # even though we are not technically a subclass...
188             my %args = Astro::Catalog::_normalize_hash(@_);
189              
190             #if( $DEBUG ) {
191             # print "Arguements\n\n";
192             # foreach my $key ( sort keys %args ) {
193             # print " $key = $args{$key}\n";
194             # }
195             # print "\n\n";
196             #}
197              
198             croak "A colour must be provided using the 'colour' key"
199             unless exists $args{colour};
200              
201             # case-insensitive
202             my $colour = lc($args{colour});
203              
204             if ( $colour eq 'ukj' ) {
205             $self->_selected_catalog( 'ssscat_ukj@wfau' );
206              
207             } elsif ( $colour eq 'ukr' ) {
208             $self->_selected_catalog( 'ssscat_ukr@wfau' );
209              
210             } elsif ( $colour eq 'uki' ) {
211             $self->_selected_catalog( 'ssscat_uki@wfau' );
212              
213             } elsif ( $colour eq 'esor' ) {
214             $self->_selected_catalog( 'ssscat_esor@wfau' );
215              
216             } else {
217              
218             # default to UKR
219             $self->_selected_catalog( 'SSScat_UKR@WFAU' );
220             }
221              
222             # Configure
223             $self->SUPER::configure( %args );
224              
225             }
226              
227             =item B<_build_query>
228              
229             Construct a query URL based on the options.
230              
231             $url = $q->_build_query();
232              
233             =cut
234              
235             sub _build_query {
236             my $self = shift;
237              
238             my $cat = $self->_selected_catalog();
239              
240             # Get the URL
241             my $url = $CONFIG{$cat}->{url};
242              
243             # Translate all the options to the internal skycat format
244             my %translated = $self->_translate_options();
245              
246             #print "Translated query: ".Dumper(\%translated,$url) if $DEBUG;
247              
248             # Now for each token replace it in the URL
249             for my $key (keys %translated) {
250             my $tok = "%". $key;
251             croak "Token $tok is mandatory but was not specified"
252             unless defined $translated{$key};
253             $url =~ s/$tok/$translated{$key}/;
254             }
255              
256             #print "Final URL: $url\n";
257              
258             return $url;
259             }
260              
261              
262             =item B<_parse_query>
263              
264             All the SkyCat servers return data in TST format.
265             Need to make sure that column information is passed
266             into the TST parser.
267              
268             =cut
269              
270             sub _parse_query {
271             my $self = shift;
272              
273             # Get the catalog info
274             my $cat = $self->_selected_catalog();
275              
276             # and extract formatting information needed by the TST parser
277             my %params;
278             for my $key (keys %{ $CONFIG{$cat} }) {
279             if ($key =~ /_col$/) {
280             #print "FOUND $key in column $CONFIG{$cat}->{$key}\n" if $DEBUG;
281             $params{$key} = $CONFIG{$cat}->{$key};
282             }
283             }
284              
285             # Time to pad the params with known values, this is yet another un-Godly
286             # hack for which I'm duely ashamed. God help us if they ever change the
287             # catalogues. Why is SuperCOSMOS so much bloody trouble?
288              
289             #print $self->{BUFFER} ."\n" if $DEBUG;
290              
291             # Make sure we set origin and field centre if we know it
292             my $query = new Astro::Catalog( Format => 'TST',
293             Data => $self->{BUFFER},
294             ReadOpt => \%params,
295             Origin => $CONFIG{$cat}->{long_name} );
296              
297             # Grab each star in the catalog and add some value to it
298             my $catalog = new Astro::Catalog( );
299             $catalog->origin( $query->origin() );
300             $catalog->set_coords( $query->get_coords() ) if defined $query->get_coords();
301              
302             my @stars = $query->allstars();
303              
304             my ( @mags, @cols );
305             foreach my $i ( 0 ... $#stars ) {
306             my ($cval, $err, $mag, $col );
307             my @mags = undef;
308             my @cols = undef;
309              
310             my $star = $stars[$i];
311             #print Dumper( $star );
312              
313             # if we have a non-zero quality, set the quality to 1 (this sucks!)
314             $star->quality(1) if( $star->quality() != 0 );
315              
316             # calulate the errors
317              
318             $err = 0.04;
319             if ( $star->get_magnitude( "BJ" ) != 99.999 ) {
320             $err = 0.04 if $star->get_magnitude( "BJ" ) > 15.0;
321             $err = 0.05 if $star->get_magnitude( "BJ" ) > 17.0;
322             $err = 0.06 if $star->get_magnitude( "BJ" ) > 19.0;
323             $err = 0.07 if $star->get_magnitude( "BJ" ) > 20.0;
324             $err = 0.12 if $star->get_magnitude( "BJ" ) > 21.0;
325             $err = 0.08 if $star->get_magnitude( "BJ" ) > 22.0;
326             } else {
327             $err = 99.999;
328             }
329             $mag = new Astro::Flux( new Number::Uncertainty(
330             Value => $star->get_magnitude("BJ"), Error => $err ), 'mag', 'BJ' );
331             push @mags, $mag;
332              
333             $err = 0.06;
334             if ( $star->get_magnitude( "R1" ) != 99.999 ) {
335             $err = 0.06 if $star->get_magnitude( "R1" ) > 11.0;
336             $err = 0.03 if $star->get_magnitude( "R1" ) > 12.0;
337             $err = 0.09 if $star->get_magnitude( "R1" ) > 13.0;
338             $err = 0.10 if $star->get_magnitude( "R1" ) > 14.0;
339             $err = 0.12 if $star->get_magnitude( "R1" ) > 18.0;
340             $err = 0.18 if $star->get_magnitude( "R1" ) > 19.0;
341             } else {
342             $err = 99.999;
343             }
344             $mag = new Astro::Flux( new Number::Uncertainty(
345             Value => $star->get_magnitude("R1"), Error => $err ), 'mag', 'R1' );
346             push @mags, $mag;
347              
348             $err = 0.02;
349             if ( $star->get_magnitude( "R2" ) != 99.999 ) {
350             $err = 0.02 if $star->get_magnitude( "R2" ) > 12.0;
351             $err = 0.03 if $star->get_magnitude( "R2" ) > 13.0;
352             $err = 0.04 if $star->get_magnitude( "R2" ) > 15.0;
353             $err = 0.05 if $star->get_magnitude( "R2" ) > 17.0;
354             $err = 0.06 if $star->get_magnitude( "R2" ) > 18.0;
355             $err = 0.11 if $star->get_magnitude( "R2" ) > 19.0;
356             $err = 0.16 if $star->get_magnitude( "R2" ) > 20.0;
357             } else {
358             $err = 99.999;
359             }
360             $mag = new Astro::Flux( new Number::Uncertainty(
361             Value => $star->get_magnitude("R2"), Error => $err ), 'mag', 'R2' );
362             push @mags, $mag;
363              
364             $err = 0.05;
365             if ( $star->get_magnitude( "I" ) != 99.999 ) {
366             $err = 0.05 if $star->get_magnitude( "I" ) > 15.0;
367             $err = 0.06 if $star->get_magnitude( "I" ) > 16.0;
368             $err = 0.09 if $star->get_magnitude( "I" ) > 17.0;
369             $err = 0.16 if $star->get_magnitude( "I" ) > 18.0;
370             } else {
371             $err = 99.999;
372             }
373             $mag = new Astro::Flux( new Number::Uncertainty(
374             Value => $star->get_magnitude("I"), Error => $err ), 'mag', 'I' );
375             push @mags, $mag;
376              
377             # calculate colours UKST Bj - UKST R, UKST Bj - UKST I
378              
379             if ( $star->get_magnitude( "BJ" ) != 99.999 &&
380             $star->get_magnitude( "R2" ) != 99.999 ) {
381              
382             my $bj_minus_r2 = $star->get_magnitude( "BJ" ) -
383             $star->get_magnitude( "R2" );
384             $bj_minus_r2 = sprintf("%.4f", $bj_minus_r2 );
385              
386             my $delta_bjmr = ( ( $star->get_errors( "BJ" ) )**2.0 +
387             ( $star->get_errors( "R2" ) )**2.0 )** (1/2);
388             $delta_bjmr = sprintf("%.4f", $delta_bjmr );
389              
390             $cval = $bj_minus_r2;
391             $err = $delta_bjmr;
392              
393             } else {
394             $cval = 99.999;
395             $err = 99.999;
396             }
397             $col = new Astro::FluxColor( upper => 'BJ', lower => "R2",
398             quantity => new Number::Uncertainty( Value => $cval, Error => $err ) );
399             push @cols, $col;
400              
401             if ( $star->get_magnitude( "BJ" ) != 99.999 &&
402             $star->get_magnitude( "I" ) != 99.999 ) {
403              
404             my $bj_minus_i = $star->get_magnitude( "BJ" ) -
405             $star->get_magnitude( "I" );
406             $bj_minus_i = sprintf("%.4f", $bj_minus_i );
407              
408             my $delta_bjmi = ( ( $star->get_errors( "BJ" ) )**2.0 +
409             ( $star->get_errors( "I" ) )**2.0 )** (1/2);
410             $delta_bjmi = sprintf("%.4f", $delta_bjmi );
411              
412             $cval = $bj_minus_i;
413             $err = $delta_bjmi;
414              
415             } else {
416             $cval = 99.999;
417             $err = 99.999;
418             }
419             $col = new Astro::FluxColor( upper => 'BJ', lower => "I",
420             quantity => new Number::Uncertainty( Value => $cval, Error => $err ) );
421             push @cols, $col;
422              
423             # Push the data back into the star object, overwriting ther previous
424             # values we got from the initial Skycat query. This isn't a great
425             # solution, but it wasn't easy in version 3 syntax either, so I guess
426             # your milage may vary.
427              
428             my $fluxes = new Astro::Fluxes( @mags, @cols );
429             $star->fluxes( $fluxes, 1 ); # the 1 means overwrite the previous values
430              
431              
432              
433             # push it onto the stack
434             $stars[$i] = $star if defined $star;
435              
436              
437             }
438              
439             $catalog->allstars( @stars );
440              
441             # set the field centre
442             my %allow = $self->_get_allowed_options();
443             my %field;
444             for my $key ("ra","dec","radius") {
445             if (exists $allow{$key}) {
446             $field{$key} = $self->query_options($key);
447             }
448             }
449             $catalog->fieldcentre( %field );
450              
451             return $catalog;
452             }
453              
454             =item B<_get_allowed_options>
455              
456             This method declares which options can be configured by the user
457             of this service. Generated automatically by the skycat config
458             file and keyed to the requested catalog.
459              
460             =cut
461              
462             sub _get_allowed_options {
463             my $self = shift;
464             my $cat = $self->_selected_catalog();
465              
466             #print "SuperCOSMOS.pm: \$CONFIG{\$cat} = $CONFIG{$cat}\n" if $DEBUG;
467             return %{ $CONFIG{$cat}->{allow} };
468              
469             }
470              
471             =item B<_get_default_options>
472              
473             Get the default options that are relevant for the selected
474             catalog.
475              
476             %defaults = $q->_get_default_options();
477              
478             =cut
479              
480             sub _get_default_options {
481             my $self = shift;
482              
483             # Global skycat defaults
484             my %defaults = (
485             # Target information
486             ra => undef,
487             dec => undef,
488             id => undef,
489              
490             # Limits
491             radmin => 0,
492             radmax => 5,
493             width => 10,
494             height => 10,
495              
496             magfaint => 100,
497             magbright => 0,
498              
499             nout => 20000,
500             cond => '',
501             );
502              
503             # Get allowed options
504             my %allow = $self->_get_allowed_options();
505              
506             # Trim the defaults (could do with hash slice?)
507             my %trim = map { $_ => $defaults{$_} } keys %allow;
508              
509             return %trim;
510             }
511              
512             =item B<_get_supported_init>
513              
514              
515              
516             =cut
517              
518             sub _get_supported_init {
519             croak "xxx - get supported init";
520             }
521              
522             =back
523              
524             =head2 Class methods
525              
526             These methods are not associated with any particular object.
527              
528             =over 4
529              
530             =item B
531              
532             Location of the skycat config file. Default location is
533             C<$PERLPREFIX/etc/sss.cfg>.
534              
535             =cut
536              
537             # set or get the cfg_file() name
538             sub cfg_file {
539             my $class = shift;
540              
541             my $cfg_file;
542             if (@_) {
543             $cfg_file = shift;
544             $class->_load_config() || ($cfg_file = undef);
545             } else {
546             # generate the default path to the $PERLPRFIX/etc/sss.cfg file,
547             # this is a horrible hack, there is probably an elegant way to do
548             # this but I can't be bothered looking it up right now.
549             my $perlbin = $^X;
550             my ($volume, $dir, $file) = File::Spec->splitpath( $perlbin );
551             my @dirs = File::Spec->splitdir( $dir );
552             my @path;
553             foreach my $i ( 0 .. $#dirs-2 ) {
554             push @path, $dirs[$i];
555             }
556             my $directory = File::Spec->catdir( @path, 'etc' );
557              
558             # reset to the default
559             $cfg_file = File::Spec->catfile( $directory, "sss.cfg" );
560              
561             # debugging and testing purposes
562             unless ( -f $cfg_file ) {
563             # use blib version!
564             $cfg_file = File::Spec->catfile( '.', 'etc', 'sss.cfg' );
565             }
566             }
567              
568             print "SuperCOSMOS.pm: \$cfg_file in cfg_file() is $cfg_file\n" if $DEBUG;
569             return $cfg_file;
570             }
571              
572             =back
573              
574             =begin __PRIVATE_METHODS__
575              
576             =head2 Internal methods
577              
578             =over 4
579              
580             =item B<_load_config>
581              
582             Method to load the skycat config information into
583             the class and configure the modules.
584              
585             $q->_load_config() or die "Error loading config";
586              
587             The config file name is obtained from the C method.
588             Returns true if the file was read successfully and contained at
589             least one catalog server. Otherwise returns false.
590              
591             Requires an object to attach itself to (mainly for the useragent
592             remote directory follow up). The results of this load are
593             visible to all instances of this class.
594              
595             Usually called automatically from the constructor if a config
596             has not previously been read.
597              
598              
599             =cut
600              
601             sub _load_config {
602             my $self = shift;
603             my $cfg = $self->cfg_file;
604              
605             #print "SuperCOSMOS.pm: \$cfg = $cfg\n" if $DEBUG;
606              
607             if (!defined $cfg) {
608             warnings::warnif("Config file not specified (undef)");
609             return;
610             }
611              
612             unless (-e $cfg) {
613             my $xcfg = (defined $cfg ? $cfg : "" );
614             return;
615             }
616              
617             my $fh;
618             unless (open $fh, "<$cfg") {
619             warnings::warnif( "Specified config file, $cfg, could not be opened: $!");
620             return;
621             }
622              
623             # Need to read the contents into an array
624             my @lines = <$fh>;
625              
626             # Process the config file and extract the raw content
627             my @configs = $self->_extract_raw_info( \@lines );
628              
629             #print "Pre-filtering has \@configs " . @configs . " entries\n" if $DEBUG;
630              
631             # Close file
632             close( $fh ) or do {
633             warnings::warnif("Error closing config file, $cfg: $!");
634             return;
635             };
636              
637             # Get the token mapping for validation
638             my %map = $self->_token_mapping;
639              
640             # Currently we are only interested in catalog, namesvr and archive
641             # so throw everything else away
642             @configs = grep { $_->{serv_type} =~ /(namesvr|catalog|archive)/ } @configs;
643              
644             #print "Post-filtering has \@configs " . @configs . " entries\n" if $DEBUG;
645              
646             # Process each entry. Mainly URL processing
647             for my $entry ( @configs ) {
648             # Skip if we have already analysed this server
649             if (exists $CONFIG{lc($entry->{short_name})}) {
650             #print "Already know about " . $entry->{short_name} . "\n" if $DEBUG;
651             next;
652             }
653              
654             #print "Processing " . $entry->{short_name} . "\n\n" if $DEBUG;
655             #print Dumper( $entry ) . "\n" if( $DEBUG );
656              
657             # Extract info from the 'url'. We need to extract the following info:
658             # - Host name and port
659             # - remaining url path
660             # - all the CGI options including the static options
661             # Note that at the moment we do not do token replacement (the
662             # rest of the REST architecture expects to get the above
663             # information separately). This might well prove to be silly
664             # since we can trivially replace the tokens without having to
665             # reconstruct the url. Of course, this does allow us to provide
666             # mandatory keywords. $url =~ s/\%ra/$ra/;
667             if ( $entry->{url} =~ m|^http://www-wfau.roe.ac.uk/~sss/cgi-bin/gaia_obj.cgi?
668             (.*) # CGI options without trailing space
669             |x) {
670             $entry->{remote_host} = "www-wfau.roe.ac.uk";
671             $entry->{url_path} = "~sss/cgi-bin/gaia_obj.cgi?";
672             my $options = $1;
673              
674             # if first character is & we append that to url_path since it
675             # is an empty argument
676             $entry->{url_path} .= "&" if $options =~ s/^\&//;
677              
678             # In general the options from skycat files are a real pain
679             # Most of them have nice blah=%blah format but there are some cases
680             # that do ?%ra%dec or coords=%ra %dec that just cause more trouble
681             # than they are worth given the standard URL constructor that we
682             # are attempting to inherit from REST
683             # Best idea is not to fight against it. Extract the host, path
684             # and options separately but simply use token replacement when it
685             # comes time to build the URL. This will require that the url
686             # is moved into its own method in REST.pm for subclassing.
687             # We still need to extract the tokens themselves so that we
688             # can generate an allowed options list.
689              
690             # tokens have the form %xxx but we have to make sure we allow
691             # %mime-type. Use the /g modifier to get all the matches
692             my @tokens = ( $options =~ /(\%[\w\-]+)/g);
693              
694             # there should always be tokens. No obvious way to reomve the anomaly
695             warnings::warnif( "No tokens found in $options!!!" )
696             unless @tokens;
697              
698             # Just need to make sure that these are acceptable tokens
699             # Get the lookup table and store that as the allowed options
700             my %allow;
701             for my $tok (@tokens) {
702             # only one token. See if we recognize it
703             my $strip = $tok;
704             $strip =~ s/%//;
705              
706             if (exists $map{$strip}) {
707             if (!defined $map{$strip}) {
708             warnings::warnif("Do not know how to process token $tok" );
709             } else {
710             $allow{ $map{$strip} } = $strip;
711             }
712             } else {
713              
714             warnings::warnif("Token $tok not currently recognized")
715             unless exists $map{$strip};
716             }
717              
718             }
719              
720             # Store them
721             $entry->{tokens} = \@tokens;
722             $entry->{allow} = \%allow;
723              
724             #print Dumper( $entry ) if $DEBUG;
725              
726             # And store this in the config. Only store it if we have
727             # tokens
728             $CONFIG{lc($entry->{short_name})} = $entry;
729              
730             } # if entry
731              
732             } # for loop
733              
734             # Debug
735             #print Dumper(\%CONFIG) if $DEBUG;
736              
737             return;
738             }
739              
740              
741             =item B<_extract_raw_info>
742              
743             Go through a skycat.cfg file and extract the raw unprocessed entries
744             into an array of hashes. The actual content of the file is passed
745             in as a reference to an array of lines.
746              
747             @entries = $q->_extract_raw_info( \@lines );
748              
749             This routine is separate from the main load routine to allow recursive
750             calls to remote directory entries.
751              
752             =cut
753              
754             sub _extract_raw_info {
755             my $self = shift;
756             my $lines = shift;
757              
758             # Now read in the contents
759             my $current; # Current server spec
760             my @configs; # Somewhere temporary to store the entries
761              
762             for my $line (@$lines) {
763              
764              
765             # Skip comment lines and blank lines
766             next if $line =~ /^\s*\#/;
767             next if $line =~ /^\s*$/;
768              
769             if ($line =~ /^(\w+):\s*(.*?)\s*$/) {
770             # This is content
771             my $key = $1;
772             my $value = $2;
773             # Assume that serv_type is always first
774             if ($key eq 'serv_type') {
775             # Store previous config if it contains something
776             # If it actually contains information on a serv_type of
777             # directory we can follow the URL and recursively expand
778             # the content
779             push(@configs, $self->_dir_check( $current ));
780              
781             # Clear the config and store the serv_type
782             $current = { $key => $value };
783              
784             } else {
785             # Just store the key value pair
786             $current->{$key} = $value;
787             }
788              
789             } else {
790             # do not know what this line signifies since it is
791             # not a comment and not a content line
792             warnings::warnif("Unexpected line in config file: $line\n");
793             }
794              
795             }
796              
797             # Last entry will still be in %$current so store it if it contains
798             # something.
799             push(@configs, $self->_dir_check( $current ));
800              
801             # Return the entries
802             return @configs;
803             }
804              
805             =item B<_dir_check>
806              
807             If the supplied hash reference has content, look at the content
808             and decide whether you simply want to keep that content or
809             follow up directory specifications by doing a remote URL call
810             and expanding that directory specification to many more remote
811             catalogue server configs.
812              
813             @configs = $q->_dir_check( \%current );
814              
815             Returns the supplied argument, additional configs derived from
816             that argument or nothing at all.
817              
818             Do not follow a 'directory' link if we have already followed a link with
819             the same short name. This prevents infinite recursion when the catalog
820             pointed to by 'catalogs@eso' itself contains a reference to 'catalogs@eso'.
821              
822             =cut
823              
824             my %followed_dirs;
825             sub _dir_check {
826             my $self = shift;
827             my $current = shift;
828              
829             if (defined $current && %$current) {
830             if ($current->{serv_type} eq 'directory') {
831             # Get the content of the URL unless we are not
832             # reading directories
833             if ($FOLLOW_DIRS && defined $current->{url} &&
834             !exists $followed_dirs{$current->{short_name}}) {
835             print "Following directory link to ". $current->{short_name}.
836             "[".$current->{url}."]\n"
837             if $DEBUG;
838              
839             # Indicate that we have followed this link
840             $followed_dirs{$current->{short_name}} = $current->{url};
841              
842             # Retrieve the url, pass that array to the raw parser and then
843             # return any new configs to our caller
844             # Must force scalar context to get array ref
845             # back rather than a simple list.
846             return $self->_extract_raw_info(scalar $self->_get_directory_url( $current->{url} ));
847             }
848             } else {
849             # Not a 'directory' so this is a simple config entry. Simply return it.
850             return ($current);
851             }
852             }
853              
854             # return empty list since we have no value
855             return ();
856             }
857              
858              
859             =item B<_get_directory_url>
860              
861             Returns the content of the remote directory URL supplied as
862             argument. In scalar context returns reference to array of lines. In
863             list context returns the lines in a list.
864              
865             \@lines = $q->_get_directory_url( $url );
866             @lines = $q->_get_directory__url( $url );
867              
868             If we have an error retrieving the file, just return an empty
869             array (ie skip it).
870              
871             =cut
872              
873             sub _get_directory_url {
874             my $self = shift;
875             my $url = shift;
876              
877             # Call the base class to get the actual content
878             my $content = '';
879             eval {
880             $content = $self->_fetch_url( $url );
881             };
882              
883             # Need an array
884             my @lines;
885             @lines = split("\n", $content) if defined $content;
886              
887             if (wantarray) {
888             return @lines;
889             } else {
890             return \@lines;
891             }
892             }
893              
894             =item B<_token_mapping>
895              
896             Provide a mapping of tokens found in SkyCat config files to the
897             internal values used generically by Astro::Catalog::Query classes.
898              
899             %map = $class->_token_mappings;
900              
901             Keys are skycat tokens.
902              
903             =cut
904              
905             sub _token_mapping {
906             return (
907             id => 'id',
908              
909             ra => 'ra',
910             dec => 'dec',
911              
912             # Arcminutes
913             r1 => 'radmin',
914             r2 => 'radmax',
915             w => 'width',
916             h => 'height',
917              
918             n => 'nout',
919              
920             # which filter???
921             m2 => 'magfaint',
922             m1 => 'magbright',
923              
924             # Is this a conditional?
925             cond => 'cond',
926              
927             # Not Yet Supported
928             cols => undef,
929             'mime-type' => undef,
930             ws => undef,
931             );
932             }
933              
934             =back
935              
936             =head2 Translations
937              
938             SkyCat specific translations from the internal format to URL format
939             go here.
940              
941             RA/Dec must match format described in
942             http://vizier.u-strasbg.fr/doc/asu.html
943             (at least for GSC) ie hh:mm:ss.s+/-dd:mm:ss
944             or decimal degrees.
945              
946             =over 4
947              
948             =cut
949              
950             sub _from_dec {
951             my $self = shift;
952             my $dec = $self->query_options("dec");
953             my %allow = $self->_get_allowed_options();
954              
955             # Need colons
956             $dec =~ s/\s+/:/g;
957              
958             # Need a + preprended
959             $dec = "+" . $dec if $dec !~ /^[\+\-]/;
960              
961             return ($allow{dec},$dec);
962             }
963              
964             sub _from_ra {
965             my $self = shift;
966             my $ra = $self->query_options("ra");
967             my %allow = $self->_get_allowed_options();
968              
969             # need colons
970             $ra =~ s/\s+/:/g;
971              
972             return ($allow{ra},$ra);
973             }
974              
975             =item B<_translate_one_to_one>
976              
977             Return a list of internal options (as defined in C<_get_allowed_options>)
978             that are known to support a one-to-one mapping of the internal value
979             to the external value.
980              
981             %one = $q->_translate_one_to_one();
982              
983             Returns a hash with keys and no values (this makes it easy to
984             check for the option).
985              
986             This method also returns, the values from the parent class.
987              
988             =cut
989              
990             sub _translate_one_to_one {
991             my $self = shift;
992             # convert to a hash-list
993             return ($self->SUPER::_translate_one_to_one,
994             map { $_, undef }(qw/
995             cond
996             /)
997             );
998             }
999              
1000             =back
1001              
1002             =end __PRIVATE_METHODS__
1003              
1004             =head1 COPYRIGHT
1005              
1006             Copyright (C) 2001 University of Exeter. All Rights Reserved.
1007             Some modifications copyright (C) 2003 Particle Physics and Astronomy
1008             Research Council. All Rights Reserved.
1009              
1010             This program was written as part of the eSTAR project and is free software;
1011             you can redistribute it and/or modify it under the terms of the GNU Public
1012             License.
1013              
1014             =head1 AUTHORS
1015              
1016             Alasdair Allan Eaa@astro.ex.ac.ukE
1017              
1018             =cut
1019              
1020             # L A S T O R D E R S ------------------------------------------------------
1021              
1022             1;