File Coverage

blib/lib/Astro/Catalog/IO/VOTable.pm
Criterion Covered Total %
statement 19 21 90.4
branch n/a
condition n/a
subroutine 7 7 100.0
pod n/a
total 26 28 92.8


line stmt bran cond sub pod time code
1             package Astro::Catalog::IO::VOTable;
2              
3             =head1 NAME
4              
5             Astro::Catalog::IO::VOTable - VOTable Input/Output format
6              
7             =head1 SYNOPSIS
8              
9             $catalog = Astro::Catalog::IO::VOTable->_read_catalog( \@lines );
10             \@lines = Astro::Catalog::IO::VOTable->_write_catalog( $catalog );
11             Astro::Catalog::IO::VOTable->_default_file();
12              
13             =head1 DESCRIPTION
14              
15             Performs simple IO, reading or writing a VOTable.
16              
17             =cut
18              
19              
20             # L O A D M O D U L E S --------------------------------------------------
21              
22 1     1   5686526 use 5.006;
  1         12  
  1         76  
23 1     1   16 use strict;
  1         2  
  1         104  
24 1     1   53 use warnings;
  1         3  
  1         105  
25 1     1   5 use warnings::register;
  1         2  
  1         415  
26 1     1   7 use vars qw/ $VERSION /;
  1         2  
  1         129  
27 1     1   13 use Carp;
  1         2  
  1         253  
28              
29 1     1   847 use Astro::Catalog;
  0            
  0            
30             use Astro::Catalog::Star;
31             use Astro::Coords;
32             use Astro::Flux;
33             use Astro::FluxColor;
34             use Astro::Fluxes;
35              
36             use Astro::VO::VOTable::Document;
37              
38             use base qw/ Astro::Catalog::IO::ASCII /;
39              
40             use Data::Dumper;
41              
42             $VERSION = "4.31";
43              
44              
45             # C O N S T R U C T O R ----------------------------------------------------
46              
47             =head1 REVISION
48              
49             $Id: VOTable.pm,v 1.10 2005/06/15 19:03:42 aa Exp $
50              
51             =begin __PRIVATE_METHODS__
52              
53             =head1 Private methods
54              
55             These methods are for internal use only and are called from the
56             Astro::Catalog module. It is not expected that anyone would want to
57             call them from outside that module.
58              
59             =over 4
60              
61             =item B<_read_catalog>
62              
63             Parses a reference to an array containing a simply formatted catalogue
64              
65             $catalog = Astro::Catalog::IO::VOTable->_read_catalog( \@lines );
66              
67             =cut
68              
69             sub _read_catalog {
70             #croak( 'Astro::IO::VOTable, _read_catalog() - Function not implemented' );
71             croak( 'Usage: _read_catalog( \@lines )' ) unless scalar(@_) >= 1;
72             my $class = shift;
73             my $arg = shift;
74             my @lines = @{$arg};
75              
76             # create an Astro::Catalog object;
77             my $catalog = new Astro::Catalog();
78              
79             # make the array a string
80             my $string = "";
81             foreach my $i ( 0 ... $#lines ) {
82             $string = $string . $lines[$i] . "\n";
83             }
84              
85             #print Dumper( @lines );
86             #print Dumper( $string );
87             #return;
88              
89             # create a VOTable object from the string.
90             my $doc = Astro::VO::VOTable::Document->new_from_string($string);
91              
92             # Get the VOTABLE element.
93             my $votable = ($doc->get_VOTABLE())[0];
94              
95             # Get the RESOURCE element.
96             my $resource = ($votable->get_RESOURCE())[0];
97              
98             # Get the DESCRIPTION element and its contents.
99             my $description = ($resource->get_DESCRIPTION())[0];
100              
101             # Get the DEFINITIONS element and its contents.
102             my $definitions = ( $votable->get_DEFINITIONS())[0];
103              
104             # Get the coordinate system (COOSYS) and its contents.
105             my $coosys = ( $definitions->get_COOSYS())[0];
106              
107             # ...and the equinox and epoch and system. I LOVE VOTABLE.
108             my $equinox = $coosys->get_equinox();
109             my $epoch = $coosys->get_epoch();
110             my $system = $coosys->get_system();
111             if( $system =~ /fk4/i ) {
112             $equinox = "B" . $equinox;
113             } else {
114             $equinox = "J" . $equinox;
115             }
116              
117             # Get the TABLE element.
118             my $table = ($resource->get_TABLE())[0];
119              
120             # Get the FIELD elements.
121             my (@field_names, @field_ucds, @field_datatypes, @field_units, @field_sizes);
122              
123             foreach my $field ( $table->get_FIELD()) {
124             push @field_names, $field->get_name();
125             push @field_ucds, $field->get_ucd();
126             push @field_datatypes, $field->get_datatype();
127             push @field_units, $field->get_unit();
128             push @field_sizes, $field->get_arraysize();
129             }
130              
131             # Get the DATA element.
132             my $data = ($table->get_DATA())[0];
133              
134             # Get the TABLEDATA element.
135             my $tabledata = ($data->get_TABLEDATA())[0];
136              
137             # loop round UCDs and try and figure out what everthing is so
138             # we can stuff the table contents into the relevant places
139             my %contents;
140             foreach my $i ( 0 ... $#field_ucds ) {
141             $contents{"id"} = $i if $field_ucds[$i] =~ "ID_MAIN";
142             $contents{"ra"} = $i if $field_ucds[$i] =~ "POS_EQ_RA_MAIN";
143             $contents{"dec"} = $i if $field_ucds[$i] =~ "POS_EQ_DEC_MAIN";
144             $contents{"quality"} = $i if $field_ucds[$i] =~ "CODE_QUALITY";
145             if( $field_ucds[$i] =~ "PHOT_" ) {
146             $contents{ $field_ucds[$i] } = $i;
147             }
148             $contents{"parallax"} = $i if $field_ucds[$i] =~ "POS_EQ_PLX_FACTOR";
149             $contents{"pm_dec"} = $i if $field_ucds[$i] =~ "POS_EQ_PMDEC";
150             $contents{"pm_ra"} = $i if $field_ucds[$i] =~ "POS_EQ_PMRA";
151             }
152              
153             # loop over each row in the TABLEDATA (ie each star)
154             foreach my $j ( 0 ... $tabledata->get_num_rows()-1 ) {
155              
156             # grab a row
157             my @row = $tabledata->get_row($j);
158             #print "# ROW $j\n";
159             #print Dumper( @row ) . "\n";
160              
161             # loop around the contents and grab the magnitudes and colours
162             my ( @fluxes, @colours );
163             foreach my $key ( keys %contents ) {
164              
165             # drop through unless we have a magntiude
166             next unless $key =~ "PHOT";
167              
168             my $identifier = $key;
169             $identifier =~ s/^PHOT_[A-Z]+_//;
170              
171             # okay we either have a magnitude or a colour, why did I ever
172             # make these two different things? Maybe I should re-engineer
173             # the Astro::Catalog::Star so that it hides the difference in
174             # some sort of meta API for both? Oh God this is so yuck...
175              
176             # colours
177             if ( $identifier =~ /^(\w+)-(\w+)$/ ) { # non-greedy
178              
179             # we might have a colour, who knows?
180             #print "COLOUR IN COLUMN $contents{$key}\n";
181             #$colours{$identifier} = $row[$contents{$key}];
182              
183             my $color = new Astro::FluxColor(
184             upper => new Astro::WaveBand( Filter => $1 ),
185             lower => new Astro::WaveBand( Filter => $2 ),
186             quantity => $row[$contents{$key}] );
187             unshift @colours, $color; # I don't understand why I have
188             # to unshift here rather than
189             # push, this is oddly disturbing
190             } else {
191              
192             # we might have a magnitude, who knows?
193             #print "MAGNITUDE IN COLUMN $contents{$key}\n";
194             #$mags{$identifier} = $row[$contents{$key}];
195             my $flux = new Astro::Flux( $row[$contents{$key}],
196             'mag', $identifier );
197             push @fluxes, $flux;
198             }
199              
200             }
201             my $fluxes = new Astro::Fluxes( @fluxes, @colours );
202              
203             # Set defaults for the proper motions and parallax.
204             my $pm_dec = ( exists( $contents{"pm_dec"} ) && defined( $contents{"pm_dec"} ) ? $row[$contents{"pm_dec"}] : undef );
205             my $pm_ra = ( exists( $contents{"pm_ra"} ) && defined( $contents{"pm_ra"} ) ? $row[$contents{"pm_ra"}] : undef );
206             my @pm;
207             if( ! defined( $pm_dec ) && ! defined( $pm_ra ) ) {
208             @pm = ();
209             } else {
210             @pm = ( $pm_ra, $pm_dec );
211             }
212             my $parallax = ( exists( $contents{"parallax"} ) && defined( $contents{"parallax"} ) ? $row[$contents{"parallax"}] : undef );
213              
214             # Create an Astro::Coords object for the star.
215             my $coords = new Astro::Coords( ra => $row[$contents{"ra"}],
216             dec => $row[$contents{"dec"}],
217             type => $equinox,
218             epoch => $epoch,
219             pm => \@pm,
220             parallax => $parallax,
221             units => 's',
222             );
223              
224             # create a star
225             my $star = new Astro::Catalog::Star(
226             id => $row[$contents{"id"}],
227             coords => $coords,
228             #magnitudes => \%mags,
229             #colours => \%colours,
230             fluxes => $fluxes,
231             quality => $row[$contents{"quality"}] );
232              
233             # push the star onto the catalog
234             $catalog->pushstar( $star );
235             }
236              
237             # return the catalogue
238             $catalog->origin( 'IO::VOTable' );
239             return $catalog;
240              
241             }
242              
243             =item B<_write_catalog>
244              
245             Will write the catalogue object to an simple output format
246              
247             \@lines = Astro::Catalog::IO::VOTable->_write_catalog( $catalog );
248              
249             where $catalog is an Astro::Catalog object.
250              
251             =cut
252              
253             sub _write_catalog {
254             croak ( 'Usage: _write_catalog( $catalog )') unless scalar(@_) >= 1;
255             my $class = shift;
256             my $catalog = shift;
257              
258             # debugging, drop the catalogue to disk as it flys right by...
259             #use Data::Dumper;
260             #print "Dumping Catalogue to disk 'catalog_dump.cat'\n";
261             #my $status = open my $fh, ">catalog_dump.cat";
262             #if (!$status) {
263             # print "Error: cannot open dump file catalog_dump.cat\n";
264             # return;
265             #}
266             #print $fh Dumper($catalog);
267             #close( $fh );
268              
269             # real list of filters and colours in the catalogue
270             my @mags = $catalog->starbyindex(0)->what_filters();
271             my @cols = $catalog->starbyindex(0)->what_colours();
272              
273             # number of stars in catalogue
274             my $number = $catalog->sizeof();
275              
276             # number of filters & colours
277             my $num_mags = $catalog->starbyindex(0)->what_filters();
278             my $num_cols = $catalog->starbyindex(0)->what_colours();
279              
280             # reference to the $self->{STARS} array in Astro::Catalog
281             my $stars = $catalog->stars();
282              
283             # generate the field headers
284             # --------------------------
285             my (@field_names, @field_ucds, @field_datatypes, @field_units, @field_sizes);
286              
287             # field names
288             push @field_names, "Identifier";
289             push @field_names, "RA";
290             push @field_names, "Dec";
291             foreach my $i ( 0 .. $#mags ) {
292             push @field_names, $mags[$i] . " Magnitude";
293             push @field_names, $mags[$i] . " Error";
294             }
295             foreach my $i ( 0 .. $#cols ) {
296             push @field_names, $cols[$i] . " Colour";
297             push @field_names, $cols[$i] . " Error";
298             }
299             push @field_names, "Quality";
300              
301              
302             # field ucds
303             push @field_ucds, "ID_MAIN";
304             push @field_ucds, "POS_EQ_RA_MAIN";
305             push @field_ucds, "POS_EQ_DEC_MAIN";
306             foreach my $i ( 0 .. $#mags ) {
307             push @field_ucds, "PHOT_MAG_" . $mags[$i];
308             #push @field_ucds, "PHOT_MAG_" . $mags[$i] . "_ERROR";
309             push @field_ucds, "CODE_ERROR";
310             }
311             foreach my $i ( 0 .. $#cols ) {
312             push @field_ucds, "PHOT_CI_" . $cols[$i];
313             #push @field_ucds, "PHOT_CI_" . $cols[$i] . "_ERROR";
314             push @field_ucds, "CODE_ERROR";
315             }
316             push @field_ucds, "CODE_QUALITY";
317              
318              
319             # field datatypes
320             push @field_datatypes, "char";
321             push @field_datatypes, "char";
322             push @field_datatypes, "char";
323             foreach my $i ( 0 .. $#mags ) {
324             push @field_datatypes, "double";
325             push @field_datatypes, "double";
326             }
327             foreach my $i ( 0 .. $#cols ) {
328             push @field_datatypes, "double";
329             push @field_datatypes, "double";
330             }
331             push @field_datatypes, "int";
332              
333              
334             # field units
335             push @field_units, "";
336             push @field_units, '"h:m:s.ss"';
337             push @field_units, '"d:m:s.ss"';
338             foreach my $i ( 0 .. $#mags ) {
339             push @field_units, "mag";
340             push @field_units, "mag";
341             }
342             foreach my $i ( 0 .. $#cols ) {
343             push @field_units, "mag";
344             push @field_units, "mag";
345             }
346             push @field_units, "";
347              
348              
349             # array size
350             push @field_sizes, "*";
351             push @field_sizes, "*";
352             push @field_sizes, "*";
353              
354             # generate the data table
355             # -----------------------
356             my @data;
357              
358             foreach my $star ( 0 .. $#$stars ) {
359             my @row;
360              
361             # Check to see if we should be writing out the proper motions
362             # and parallax.
363             my $coords = ${$stars}[$star]->coords;
364              
365             if( scalar( $coords->pm ) ) {
366             push @field_names, "RA Proper Motion";
367             push @field_names, "Dec Proper Motion";
368             push @field_ucds, "POS_EQ_PMRA";
369             push @field_ucds, "POS_EQ_PMDEC";
370             push @field_datatypes, "double";
371             push @field_datatypes, "double";
372             push @field_units, "arcsec/yr";
373             push @field_units, "arcsec/yr";
374             }
375             if( defined( $coords->parallax ) ) {
376             push @field_names, "Parallax";
377             push @field_ucds, "POS_EQ_PLX_FACTOR";
378             push @field_datatypes, "double";
379             push @field_units, "arcsec";
380             }
381              
382             # id
383             if ( defined ${$stars}[$star]->id() ) {
384             push @row, ${$stars}[$star]->id();
385             } else {
386             push @row, $star;
387             }
388              
389             # ra & dec -- we want these in J2000.
390             push @row, $coords->ra2000(format => 's');
391             push @row, $coords->dec2000(format => 's');
392              
393             # magnitudes
394             foreach my $i ( 0 .. $#mags ) {
395              
396             if ( defined ${$stars}[$star]->get_magnitude($mags[$i]) ) {
397             push @row, ${$stars}[$star]->get_magnitude($mags[$i]);
398             } else {
399             push @row, "0.000";
400             }
401             if ( defined ${$stars}[$star]->get_errors($mags[$i]) ) {
402             push @row, ${$stars}[$star]->get_errors($mags[$i]);
403             } else {
404             push @row, "0.000";
405             }
406              
407             }
408              
409             # colours
410             foreach my $i ( 0 .. $#cols ) {
411              
412             if ( defined ${$stars}[$star]->get_colour($cols[$i]) ) {
413             push @row, ${$stars}[$star]->get_colour($cols[$i]);
414             } else {
415             push @row, "0.000";
416             }
417             if ( defined ${$stars}[$star]->get_colourerr($cols[$i]) ) {
418             push @row, ${$stars}[$star]->get_colourerr($cols[$i]);
419             } else {
420             push @row, "0.000";
421             }
422              
423             }
424              
425             # quality
426             if ( defined ${$stars}[$star]->quality() ) {
427             push @row, ${$stars}[$star]->quality();
428             } else {
429             push @row, "0";
430             }
431              
432             # Proper motions and parallax
433             if( defined ${$stars}[$star]->coords ) {
434             my $coords = ${$stars}[$star]->coords;
435             my @pm = $coords->pm;
436             push @row, $pm[0];
437             push @row, $pm[1];
438             push @row, $coords->parallax;
439             }
440              
441             # push a reference to the row into the data
442             push @data, \@row;
443              
444             } # end of loop around the stars array
445              
446              
447             # Create the VOTABLE document.
448             my $doc = new Astro::VO::VOTable::Document();
449              
450             # Get the VOTABLE element.
451             my $votable = ($doc->get_VOTABLE)[0];
452              
453             # Create the DESCRIPTION element and its contents, and add it to the VOTABLE
454             my $description = new Astro::VO::VOTable::DESCRIPTION();
455             $description->set('Created using Astro::Catalog::IO::VOTable');
456             $votable->set_DESCRIPTION($description);
457              
458             # Create a DEFINITION element and its contents and add it to the VOTABLE
459             my $definitions = new Astro::VO::VOTable::DEFINITIONS();
460             my $coosys = new Astro::VO::VOTable::COOSYS();
461             $coosys->set_ID( "J2000" );
462             $coosys->set_equinox( 2000.0 );
463             $coosys->set_epoch( 2000.0 );
464             $coosys->set_system( 'eq_FK5' );
465             $definitions->set_COOSYS( $coosys );
466             $votable->set_DEFINITIONS( $definitions );
467              
468             # Create the RESOURCE element and add it to the VOTABLE.
469             my $resource = new Astro::VO::VOTable::RESOURCE();
470             $votable->set_RESOURCE($resource);
471              
472             #create the LINK element and its contents, and add it to the VOTABLE
473             my $link = new Astro::VO::VOTable::LINK();
474             $link->set_title('eSTAR Project');
475             $link->set_href('http://www.estar.org.uk/');
476             $link->set_content_role('doc');
477             $resource->set_LINK($link);
478              
479             # Create the TABLE element and add it to the RESOURCE.
480             my $table = new Astro::VO::VOTable::TABLE();
481             $resource->set_TABLE($table);
482              
483             # Create and add the FIELD elements to the TABLE.
484             my($i);
485             my($field);
486             for ($i = 0; $i < @field_names; $i++) {
487             $field = new Astro::VO::VOTable::FIELD();
488             $field->set_name($field_names[$i]);
489             $field->set_ucd($field_ucds[$i]);
490             $field->set_datatype($field_datatypes[$i]);
491             $field->set_unit($field_units[$i]);
492             $field->set_arraysize($field_sizes[$i]) if defined $field_sizes[$i];
493             $table->append_FIELD($field);
494             }
495              
496             # Create and append the DATA element.
497             my $data = new Astro::VO::VOTable::DATA();
498             $table->set_DATA($data);
499              
500             # Create and append the TABLEDATA element.
501             my $tabledata = new Astro::VO::VOTable::TABLEDATA();
502             $data->set_TABLEDATA($tabledata);
503              
504             # Create and append each TR element, and each TD element.
505             my($tr, $td);
506             my($j);
507             for ($i = 0; $i < @data; $i++) {
508             $tr = new Astro::VO::VOTable::TR();
509             for ($j = 0; $j < @field_names; $j++) {
510             $td = new Astro::VO::VOTable::TD();
511             $td->set($data[$i][$j]);
512             $tr->append_TD($td);
513             }
514             $tabledata->append_TR($tr);
515             }
516              
517             # Print the finished document.
518             my $output_string = $doc->toString(1);
519             my @output = split("\n", $output_string );
520              
521             #print $output_string;
522             #use Data::Dumper; print Dumper(@output);
523              
524             # return a reference to an array
525             return \@output;
526              
527             }
528              
529             =item B<_default_file>
530              
531             If Astro::Catalog is created with a Format but no Filename or other data
532             source it checked this routine to see whether there is a default file
533             that should be read. This is mainly for Astro::Catalo::IO::JCMT and the
534             JAC, but might prive useful elsewhere.
535              
536             =cut
537              
538             sub _default_file {
539              
540             # returns an empty list
541             return;
542             }
543              
544             =back
545              
546             =end __PRIVATE_METHODS__
547              
548             =head1 FORMAT
549              
550             This class implements an interface to VOTable documents. This uses the
551             GSFC VOTable classes which inherits from XML::LibXML::Document class.
552              
553             =head1 COPYRIGHT
554              
555             Copyright (C) 2001-2003 University of Exeter. All Rights Reserved.
556             Some modificiations Copyright (C) 2003 Particle Physics and Astronomy
557             Research Council. All Rights Reserved.
558              
559             This module was written as part of the eSTAR project in collaboration
560             with the Joint Astronomy Centre (JAC) in Hawaii and is free software;
561             you can redistribute it and/or modify it under the terms of the GNU
562             Public License.
563              
564             =head1 AUTHORS
565              
566             Alasdair Allan Eaa@astro.ex.ac.ukE
567              
568             =cut
569              
570             # L A S T O R D E R S ------------------------------------------------------
571              
572             1;