| 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; |