Astro-Catalog
view release on metacpan or search on metacpan
lib/Astro/Catalog/Query/SkyCat.pm view on Meta::CPAN
our $VERSION = '4.38';
our $DEBUG = 0;
# Controls whether we follow 'directory' config entries and recursively
# expand those. Default to false at the moment.
our $FOLLOW_DIRS = 0;
# This is the name of the config file that was used to generate
# the content in %CONFIG. Can be different to the contents ofg_file
# if that
my $CFG_FILE;
# This is the content of the config file
# organized as a hash indexed by remote server shortname
# this has the advantage of removing duplicates
my %CONFIG;
=head1 METHODS
=head2 Constructor
=over 4
=item B<new>
Simple constructor. Forces read of config file if one can be found and
the config has not been read previously. If no config file can be located
the query object can not be instantiated since it will not know the
location of any servers.
$q = new Astro::Catalog::Query::SkyCat(catalog => 'gsc', %options);
$q = new Astro::Catalog::Query::SkyCat(catalog => 'gsc@eso', %options);
The C<catalog> field must be present, otherwise the new object will
not know which remote server to use and which options are mandatory
in the query. Note that the remote catalog can not be changed after
the object is instantiated. In general it is probably not wise to
try to change the remote host via either the C<query_url> or
C<url> methods unless you know what you are doing. Modifying your
C<skycat.cfg> file is safer.
Currently only one config file is supported at any given time.
If a config file is changed (see the C<cfg_file> class method)
the current config is overwritten automatically.
It is not possible to override the catalog file in the
constructor. Use the C<cfg_file> class method instead.
Obviously a config per object can be supported but this is
probably not that helpful. This will be reconsidered if demand
is high.
=cut
sub new {
my $proto = shift;
my $class = ref($proto) || $proto;
# Instantiate via base class
my $block = $class->SUPER::new(@_);
return $block;
}
=back
=head2 Accessor methods
=over 4
=item B<_selected_catalog>
Catalog name selected by the user and currently configured for
this object. Not to be used outside this class..
=cut
sub _selected_catalog {
my $self = shift;
if (@_) {
# The class has to be configured as a hash!!!
$self->{SKYCAT_CATALOG} = shift;
}
return $self->{SKYCAT_CATALOG};
}
=back
=head2 General methods
=over 4
=item C<configure>
Configure the object. This calls the base class configure , after it has
made sure that a sky cat config file has been read (otherwise we will
not be able to vet the incoming arguments.
=cut
sub configure {
my $self = shift;
# load a config if we do not have one read yet
# Note that this may force a remote URL read via directory
# directives even though we do not have a user agent configured...
$self->_load_config() unless %CONFIG;
# Error if we have no config yet
croak "Error instantiating SkyCat object since no config was located"
unless %CONFIG;
# Now we need to configure this object based on the
# supplied catalog name. This is not really a public interface
# let's call it a protected interface available to subclases
# even though we are not technically a subclass...
my %args = Astro::Catalog::_normalize_hash(@_);
croak "A remote service catalog name must be provided using a 'catalog' key"
unless exists $args{catalog};
# case-insensitive
my $cat = lc($args{catalog});
# if we have an entry in %CONFIG then we can use it directly
# else we may have a root name without a remote server
if (! exists $CONFIG{$cat}) {
my $name = $cat;
# clear it and look for another
$cat = undef;
# if name does not include an @ we probably have a generic catalog
# and just need to choose a random specific version
if ($name !~ /\@/) {
# look through the catalog
for my $rmt (keys %CONFIG) {
if ($rmt =~ /^$name\@/) {
# a match
$cat = $rmt;
}
}
}
# No luck finding catalog name
croak "unable to find a remote service named $name"
unless defined $cat;
}
# Now we know the details we need to store this somewhere in
# the object so that it won't get clobbered. Otherwise the
# super class configure will not be able to get the information
# it needs. We can not simply store this in options since configure
# does not know it is an allowed option...
$self->_selected_catalog( $cat );
# delete catalog from list
delete $args{catalog};
# Configure
$self->SUPER::configure(%args);
}
=item B<_build_query>
Construct a query URL based on the options.
$url = $q->_build_query();
=cut
sub _build_query {
my $self = shift;
my $cat = $self->_selected_catalog();
# Get the URL
my $url = $CONFIG{$cat}->{url};
# Translate all the options to the internal skycat format
my %translated = $self->_translate_options();
print "Translated query: ".Dumper(\%translated,$url) if $DEBUG;
# Now for each token replace it in the URL
for my $key (keys %translated) {
my $tok = "%". $key;
croak "Token $tok is mandatory but was not specified"
unless defined $translated{$key};
$url =~ s/$tok/$translated{$key}/;
}
print "Final URL: $url\n" if $DEBUG;
return $url;
}
=item B<_parse_query>
All the SkyCat servers return data in TST format.
Need to make sure that column information is passed
into the TST parser.
=cut
sub _parse_query {
my $self = shift;
# Get the catalog info
my $cat = $self->_selected_catalog();
# and extract formatting information needed by the TST parser
my %params;
for my $key (keys %{ $CONFIG{$cat} }) {
if ($key =~ /_col$/) {
print "FOUND column specified $key\n" if $DEBUG;
$params{$key} = $CONFIG{$cat}->{$key};
}
}
lib/Astro/Catalog/Query/SkyCat.pm view on Meta::CPAN
=back
=head2 Translations
SkyCat specific translations from the internal format to URL format
go here.
RA/Dec must match format described in
http://vizier.u-strasbg.fr/doc/asu.html
(at least for GSC) ie hh:mm:ss.s+/-dd:mm:ss
or decimal degrees.
=over 4
=cut
sub _from_dec {
my $self = shift;
my $dec = $self->query_options("dec");
my %allow = $self->_get_allowed_options();
# Need colons
$dec =~ s/\s+/:/g;
# Need a + preprended
$dec = "+" . $dec if $dec !~ /^[\+\-]/;
return ($allow{dec}, $dec);
}
sub _from_ra {
my $self = shift;
my $ra = $self->query_options("ra");
my %allow = $self->_get_allowed_options();
# need colons
$ra =~ s/\s+/:/g;
return ($allow{ra}, $ra);
}
=item B<_translate_one_to_one>
Return a list of internal options (as defined in C<_get_allowed_options>)
that are known to support a one-to-one mapping of the internal value
to the external value.
%one = $q->_translate_one_to_one();
Returns a hash with keys and no values (this makes it easy to
check for the option).
This method also returns, the values from the parent class.
=cut
sub _translate_one_to_one {
my $self = shift;
# convert to a hash-list
return ($self->SUPER::_translate_one_to_one,
map { $_, undef }(qw/
cond
/)
);
}
1;
__END__
=back
=end __PRIVATE_METHODS__
=head1 NOTES
'directory' entries are not followed by default although the class
can be configured to do so by setting
$Astro::Catalog::Query::SkyCat::FOLLOW_DIRS = 1;
to true.
This class could simply read the catalog config file and allow queries
on explicit servers directly rather than going to the trouble of
auto-generating a class per server. This has the advantage of allowing
a user to request USNO data from different servers rather than generating
a single USNO class. ie
my $q = new Astro::Catalog::Query::SkyCat(
catalog => 'usnoa@eso',
target => 'HL Tau',
radius => 5);
as opposed to
my $q = new Astro::Catalog::Query::USNOA(
target => 'HL Tau',
radius => 5 );
What to do with catalog mirrors is an open question. Of course,
convenience wrapper classes could be made available that simply delegate
the calls to the SkyCat class.
=head1 SEE ALSO
SkyCat FTP server. [URL goes here]
SSN75 [http://www.starlink.rl.ac.uk/star/docs/ssn75.htx//ssn75.html]
by Clive Davenhall.
=head1 BUGS
At the very least for testing, an up-to-date skycat.cfg file
should be distributed with this module. Whether it should be
used by this module once installed is an open question (since
many people will not have a version in the standard location).
=head1 COPYRIGHT
( run in 0.737 second using v1.01-cache-2.11-cpan-8f98c5d2c55 )