1package Astro::Catalog::IO::LCOGTFITSTable;
2
3=head1 NAME
4
5Astro::Catalog::IO::LCOGTFITSTable - Binary LCOGT FITS table I/O for Astro::Catalog
6
7=head1 SYNOPSIS
8
9    $cat = Astro::Catalog::IO::LCOGTFITSTable->_read_catalog($whatever);
10
11=cut
12
13use warnings;
14use warnings::register;
15use Carp;
16use strict;
17
18use Astro::Catalog;
19use Astro::Catalog::Item;
20use Astro::Catalog::Item::Morphology;
21use Astro::Coords;
22use Astro::FITS::CFITSIO qw/:longnames :constants/;
23use File::Temp qw/tempfile/;
24
25use Astro::Flux;
26use Astro::FluxColor;
27use Astro::Fluxes;
28
29use DateTime;
30use DateTime::Format::ISO8601;
31
32use POSIX qw/log10/;
33use base qw/Astro::Catalog::IO::Binary/;
34
35our $VERSION = '4.36';
36our $DEBUG = 0;
37
38=begin __PUBLIC_METHODS__
39
40=head1 PUBLIC METHODS
41
42These methods are usually called automatically from the C<Astro::Catalog>
43constructor, but are available for public use.
44
45=over 4
46
47=item B<input_format>
48
49Returns the requested input format for the FITSTable class, which is
50'name', meaning the name of the file to be turned into an C<Astro::Catalog>
51object.
52
53    $input_format = Astro::Catalog::IO::LCOGTFITSTable->input_format;
54
55=cut
56
57sub input_format {
58    return "name";
59}
60
61=back
62
63=begin __PRIVATE_METHODS__
64
65=head1 PRIVATE METHODS
66
67These methods are usually called automatically from the C<Astro::Catalog>
68constructor.
69
70=item B<_read_catalog>
71
72Parses the binary FITS table and returns a new C<Astro::Catalog> object
73containing the catalogue entries.
74
75    $cat = Astro::Catalog::IO::LCOGTFITSTable->_read_catalog($whatever);
76
77The current translations from FITS table column names to
78C<Astro::Catalog::Item> properties are:
79
80=over 4
81
82=item No. - ID
83
84=item X_coordinate - X
85
86=item Y_coordinate - Y
87
88=item RA & DEC - Coords
89
90=item Isophotal_flux, Total_flux, Total_flux_err, Core_flux, Core1_flux, Core2_flux,
91 Core3_flux, Core4_flux - C<Astro::Flux> objects pushed into
92 the C<Astro::Catalog::Item> fluxes accessor.
93
94=item Isoarea, Ellipticity & Position_angle - Morphology
95
96=item Flags - Quality
97
98=back
99
100RA and Dec are assumed to be in J2000 coordinates, and are in units
101of degrees. The total flux is assumed to be in units of counts,
102and is converted into a magnitude through the formula -2.5 * log10( flux ).
103The position angle is assumed to be the angle measured counter-
104clockwise from the positive x axis, in degrees.
105
106An attempt to read in the DATE-OBS header is made so that flux measurements
107can be timestamped. If the DATE-OBS header does not exist, then the current
108date and time will be used for the flux timestamps.
109
110There are optional named parameters. These are case-sensitive, and are:
111
112=item Filter - An Astro::WaveBand object denoting the waveband that
113the catalogue values were measured in.
114
115=cut
116
117sub _read_catalog {
118    my $class = shift;
119    my %args = @_;
120
121    unless (defined $args{'filename'}) {
122        croak "Must supply a filename to read";
123    }
124    my $filename = $args{'filename'};
125
126    my $obsid;
127    if (defined $args{'obsid'}) {
128        $obsid = $args{'obsid'};
129    }
130    else {
131        $obsid = [];
132    }
133
134    if ((defined $args{'Filter'}) &&
135            ! UNIVERSAL::isa($args{'Filter'}, "Astro::WaveBand")) {
136        croak "Filter as passed to LCOGTFITSTable->_read_catalog must be an Astro::WaveBand object";
137    }
138
139    my $filter;
140    if (defined $args{'Filter'}) {
141        print "Filter defined\n" if $DEBUG;
142        $filter = $args{'Filter'}->natural;
143    }
144    else {
145        $filter = 'unknown';
146    }
147    print "Input Filter=$filter\n" if $DEBUG;
148    # A lookup table for column name mappings.
149    my %column_name = (
150        'ID' => 'NUMBER',
151        'X' => 'X_IMAGE',
152        'Y' => 'Y_IMAGE',
153        'RA' => 'ALPHA_J2000',
154        'Dec' => 'DELTA_J2000',
155        'isophotal_flux' => 'FLUX_ISO',
156        'total_flux' => 'FLUX_AUTO',
157        'total_flux_err' => 'FLUXERR_AUTO',
158        'core_flux' => 'FLUX_APER',
159        'core1_flux' => 'FLUX_APER1',
160        'core2_flux' => 'FLUX_APER2',
161        'core3_flux' => 'FLUX_APER3',
162        'core4_flux' => 'FLUX_APER4',
163        'isoarea' => 'ISOAREA_IMAGE',
164        'ellipticity' => 'ELLIPTICITY',
165        'position_angle' => 'THETA_IMAGE',
166        'flags' => 'FLAGS',
167    );
168
169    # The new Astro::Catalog object.
170    my $catalog = new Astro::Catalog;
171
172    # CFITSIO status variable.
173    my $status = 0;
174
175    # Open the file using CFITSIO.
176    my $fptr = Astro::FITS::CFITSIO::open_file($filename,
177            Astro::FITS::CFITSIO::READONLY(),
178            $status);
179    if ($status != 0) {
180        Astro::FITS::CFITSIO::fits_get_errstatus($status, my $text);
181        croak "Error opening FITS file: $status $text";
182    }
183
184    # Get the number of HDUs in the FITS file.
185    $fptr->get_num_hdus(my $num_hdus, $status);
186    if ($status != 0) {
187        Astro::FITS::CFITSIO::fits_get_errstatus($status, my $text);
188        croak "Error retrieving number of HDUs from FITS file: $status $text";
189    }
190
191    $fptr->get_hdu_num(my $hdu_pos);
192
193    my $datetime;
194    my $waveband;
195    while ($hdu_pos <= $num_hdus) {
196        # Get the type of HDU for the one we're at.
197        $fptr->get_hdu_type(my $hdutype, $status);
198        if ($status != 0) {
199            Astro::FITS::CFITSIO::fits_get_errstatus($status, my $text);
200            croak "Error retrieving HDU type from FITS file: $status $text";
201        }
202        print "hdutype=$hdutype\n" if $DEBUG;
203        if ($hdutype == IMAGE_HDU) {
204            # Try to retrieve the DATE-OBS header. This will be used
205            # to give each flux measurement a datetime stamp. If DATE-OBS
206            # cannot be determined, then set the datetime to the current
207            # time.
208
209            $fptr->read_keyword('DATE-OBS', my $dateobs, my $comment, $status);
210            if ($status != 0) {
211                if ($status == KEY_NO_EXIST) {
212                    # We can deal with this, just take the current time and set
213                    # the status back to 0 (good).
214                    $datetime = DateTime->now;
215                    $status = 0;
216                }
217                else {
218                    Astro::FITS::CFITSIO::fits_get_errstatus($status, my $text);
219                    croak "Error retrieving DATE-OBS header from FITS file: $status $text";
220                }
221            }
222            else {
223                # Strip out any characters that aren't meant to be there.
224                # read_keyword() puts single quotes around strings, so we need
225                # to get rid of those, along with any trailing Zs.
226                $dateobs =~ s/['Z]//g;
227                $datetime = DateTime::Format::ISO8601->parse_datetime($dateobs);
228            }
229            print "DATE-OBS=$datetime\n" if $DEBUG;
230
231            unless (defined $filter) {
232                $fptr->read_keyword('FILTER', my $filter, my $filtercomment, $status);
233                if ($status != 0) {
234                    if ($status == KEY_NO_EXIST) {
235                        # We can deal with this, just set the filter to be 'unknown'.
236                        $filter = 'unknown';
237                        $status = 0;
238                    }
239                    else {
240                        Astro::FITS::CFITSIO::fits_get_errstatus($status, my $text);
241                        croak "Error retrieving FILTER header from FITS file: $status $text";
242                    }
243                }
244                else {
245                    # Strip out any characters that aren't meant to be there.
246                    $filter =~ s/'//g;
247                    $filter =~ s/^\s+//;
248                    $filter =~ s/\s+$//;
249                }
250                print "Filter from header=$filter\n" if $DEBUG;
251            }
252            $waveband = new Astro::WaveBand(Filter => $filter);
253            print "FILTER, waveband=$filter\n" if $DEBUG;
254
255        }
256        elsif ($hdutype == BINARY_TBL) {
257            print "2 Wavelength= " . $waveband->wavelength . ", Frequency=" . $waveband->frequency . "\n" if $DEBUG;
258            # Get the number of rows in this table.
259            $fptr->get_num_rows(my $nrows, $status);
260            if ($status != 0) {
261                Astro::FITS::CFITSIO::fits_get_errstatus($status, my $text);
262                croak "Error retrieving number of rows from HDU $hdu_pos from FITS file: $status $text";
263            }
264
265            # Grab all the information we can from this HDU.
266            # First, get the column numbers for the ID, RA, Dec, flux,
267            # ellipticity, position angle, and x and y position.
268            $fptr->get_colnum(CASEINSEN, $column_name{'ID'}, my $id_column, $status);
269            if ($status == COL_NOT_FOUND) {
270                $status = 0;
271                $id_column = -1;
272            }
273            elsif ($status != 0) {
274                Astro::FITS::CFITSIO::fits_get_errstatus($status, my $text);
275                croak "Error in finding ID column: $status $text";
276            }
277            if ($id_column == 0) {
278                $id_column = -1;
279            }
280            print "ID column: $id_column\n" if $DEBUG;
281
282            $fptr->get_colnum(CASEINSEN, $column_name{'RA'}, my $ra_column, $status);
283            if ($status == COL_NOT_FOUND) {
284                $status = 0;
285                $ra_column = -1;
286            }
287            elsif ($status != 0) {
288                Astro::FITS::CFITSIO::fits_get_errstatus($status, my $text);
289                croak "Error in finding RA column: $status $text";
290            }
291            if ($ra_column == 0) {
292                $ra_column = -1;
293            }
294            print "RA column: $ra_column\n" if $DEBUG;
295
296            $fptr->get_colnum(CASEINSEN, $column_name{'Dec'}, my $dec_column, $status);
297            if ($status == COL_NOT_FOUND) {
298                $status = 0;
299                $dec_column = -1;
300            }
301            elsif ($status != 0) {
302                Astro::FITS::CFITSIO::fits_get_errstatus($status, my $text);
303                croak "Error in finding Dec column: $status $text";
304            }
305            if ($dec_column == 0) {
306                $dec_column = -1;
307            }
308            print "Dec column: $dec_column\n" if $DEBUG;
309
310            $fptr->get_colnum(CASEINSEN, $column_name{'isophotal_flux'}, my $iso_flux_column, $status);
311            if ($status == COL_NOT_FOUND) {
312                $status = 0;
313                $iso_flux_column = -1;
314            }
315            elsif ($status != 0) {
316                Astro::FITS::CFITSIO::fits_get_errstatus($status, my $text);
317                croak "Error in finding isophotal flux column: $status $text";
318            }
319            if ($iso_flux_column == 0) {
320                $iso_flux_column = -1;
321            }
322            print "Isophotal flux column: $iso_flux_column\n" if $DEBUG;
323
324            $fptr->get_colnum(CASEINSEN, $column_name{'total_flux'}, my $total_flux_column, $status);
325            if ($status == COL_NOT_FOUND) {
326                $status = 0;
327                $total_flux_column = -1;
328            }
329            elsif ($status != 0) {
330                Astro::FITS::CFITSIO::fits_get_errstatus($status, my $text);
331                croak "Error in finding total flux column: $status $text";
332            }
333            if ($total_flux_column == 0) {
334                $total_flux_column = -1;
335            }
336            print "Total flux column: $total_flux_column\n" if $DEBUG;
337
338            $fptr->get_colnum(CASEINSEN, $column_name{'total_flux_err'}, my $total_flux_err_column, $status);
339            if ($status == COL_NOT_FOUND) {
340                $status = 0;
341                $total_flux_err_column = -1;
342            }
343            elsif ($status != 0) {
344                Astro::FITS::CFITSIO::fits_get_errstatus($status, my $text);
345                croak "Error in finding total flux err column: $status $text";
346            }
347            if ($total_flux_err_column == 0) {
348                $total_flux_err_column = -1;
349            }
350            print "Total flux err column: $total_flux_err_column\n" if $DEBUG;
351
352            $fptr->get_colnum(CASEINSEN, $column_name{'core_flux'}, my $core_flux_column, $status);
353            if ($status == COL_NOT_FOUND) {
354                $status = 0;
355                $core_flux_column = -1;
356            }
357            elsif ($status != 0) {
358                Astro::FITS::CFITSIO::fits_get_errstatus($status, my $text);
359                croak "Error in finding core flux column: $status $text";
360            }
361            if ($core_flux_column == 0) {
362                $core_flux_column = -1;
363            }
364            print "Core flux column: $core_flux_column\n" if $DEBUG;
365
366            $fptr->get_colnum(CASEINSEN, $column_name{'core1_flux'}, my $core1_flux_column, $status);
367            if ($status == COL_NOT_FOUND) {
368                $status = 0;
369                $core1_flux_column = -1;
370            }
371            elsif ($status != 0) {
372                Astro::FITS::CFITSIO::fits_get_errstatus($status, my $text);
373                croak "Error in finding core1 flux column: $status $text";
374            }
375            if ($core1_flux_column == 0) {
376                $core1_flux_column = -1;
377            }
378            print "Core1 flux column: $core1_flux_column\n" if $DEBUG;
379
380            $fptr->get_colnum(CASEINSEN, $column_name{'core2_flux'}, my $core2_flux_column, $status);
381            if ($status == COL_NOT_FOUND) {
382                $status = 0;
383                $core2_flux_column = -1;
384            }
385            elsif ($status != 0) {
386                Astro::FITS::CFITSIO::fits_get_errstatus($status, my $text);
387                croak "Error in finding core2 flux column: $status $text";
388            }
389            if ($core2_flux_column == 0) {
390                $core2_flux_column = -1;
391            }
392            print "Core2 flux column: $core2_flux_column\n" if $DEBUG;
393
394            $fptr->get_colnum(CASEINSEN, $column_name{'core3_flux'}, my $core3_flux_column, $status);
395            if ($status == COL_NOT_FOUND) {
396                $status = 0;
397                $core3_flux_column = -1;
398            }
399            elsif ($status != 0) {
400                Astro::FITS::CFITSIO::fits_get_errstatus($status, my $text);
401                croak "Error in finding core3 flux column: $status $text";
402            }
403            if ($core3_flux_column == 0) {
404                $core3_flux_column = -1;
405            }
406            print "Core3 flux column: $core3_flux_column\n" if $DEBUG;
407
408            $fptr->get_colnum(CASEINSEN, $column_name{'core4_flux'}, my $core4_flux_column, $status);
409            if ($status == COL_NOT_FOUND) {
410                $status = 0;
411                $core4_flux_column = -1;
412            }
413            elsif ($status != 0) {
414                Astro::FITS::CFITSIO::fits_get_errstatus($status, my $text);
415                croak "Error in finding core4 flux column: $status $text";
416            }
417            if ($core4_flux_column == 0) {
418                $core4_flux_column = -1;
419            }
420            print "Core4 flux column: $core4_flux_column\n" if $DEBUG;
421
422            $fptr->get_colnum(CASEINSEN, $column_name{'isoarea'}, my $isoarea_column, $status);
423            if ($status == COL_NOT_FOUND) {
424                $status = 0;
425                $isoarea_column = -1;
426            }
427            elsif ($status != 0) {
428                Astro::FITS::CFITSIO::fits_get_errstatus($status, my $text);
429                croak "Error in finding isoarea column: $status $text";
430            }
431            if ($isoarea_column == 0) {
432                $isoarea_column = -1;
433            }
434            print "Isoarea column: $isoarea_column\n" if $DEBUG;
435
436            $fptr->get_colnum(CASEINSEN, $column_name{'ellipticity'}, my $ell_column, $status);
437            if ($status == COL_NOT_FOUND) {
438                $status = 0;
439                $ell_column = -1;
440            }
441            elsif ($status != 0) {
442                Astro::FITS::CFITSIO::fits_get_errstatus($status, my $text);
443                croak "Error in finding ellipticity column: $status $text";
444            }
445            if ($ell_column == 0) {
446                $ell_column = -1;
447            }
448            print "Ellipticity column: $ell_column\n" if $DEBUG;
449
450            $fptr->get_colnum(CASEINSEN, $column_name{'position_angle'}, my $posang_column, $status);
451            if ($status == COL_NOT_FOUND) {
452                $status = 0;
453                $posang_column = -1;
454            }
455            elsif ($status != 0) {
456                Astro::FITS::CFITSIO::fits_get_errstatus($status, my $text);
457                croak "Error in finding position angle column: $status $text";
458            }
459            if ($posang_column == 0) {
460                $posang_column = -1;
461            }
462            print "Position angle column: $posang_column\n" if $DEBUG;
463
464            $fptr->get_colnum(CASEINSEN, $column_name{'X'}, my $x_column, $status);
465            if ($status == COL_NOT_FOUND) {
466                $status = 0;
467                $x_column = -1;
468            }
469            elsif ($status != 0) {
470                Astro::FITS::CFITSIO::fits_get_errstatus($status, my $text);
471                croak "Error in finding x-coordinate column: $status $text";
472            }
473            if ($x_column == 0) {
474                $x_column = -1;
475            }
476            print "X-coordinate column: $x_column\n" if $DEBUG;
477
478            $fptr->get_colnum(CASEINSEN, $column_name{'Y'}, my $y_column, $status);
479            if ($status == COL_NOT_FOUND) {
480                $status = 0;
481                $y_column = -1;
482            }
483            elsif ($status != 0) {
484                Astro::FITS::CFITSIO::fits_get_errstatus($status, my $text);
485                croak "Error in finding y-coordinate column: $status $text";
486            }
487            if ($y_column == 0) {
488                $y_column = -1;
489            }
490            print "Y-coordinate column: $y_column\n" if $DEBUG;
491
492            $fptr->get_colnum(CASEINSEN, $column_name{'flags'}, my $flag_column, $status);
493            if ($status == COL_NOT_FOUND) {
494                $status = 0;
495                $flag_column = -1;
496            }
497            elsif ($status != 0) {
498                Astro::FITS::CFITSIO::fits_get_errstatus($status, my $text);
499                croak "Error in finding flags column: $status $text";
500            }
501            if ($flag_column == 0) {
502                $flag_column = -1;
503            }
504            print "Flags column: $flag_column\n" if $DEBUG;
505
506            # Now that we've got all the columns defined, we need to grab each column
507            # in one big array, then take those arrays and stuff the information into
508            # Astro::Catalog::Item objects
509            my $id;
510            my $ra;
511            my $dec;
512            my ($iso_flux, $total_flux, $total_flux_err, $core_flux, $core1_flux, $core2_flux);
513            my ($core3_flux, $core4_flux);
514            my $isoarea;
515            my $ell;
516            my $posang;
517            my $x_pos;
518            my $y_pos;
519            my $flags;
520            if ($id_column != -1) {
521                $fptr->read_col(TFLOAT, $id_column, 1, 1, $nrows, undef, $id, undef, $status);
522                if ($status != 0) {
523                    Astro::FITS::CFITSIO::fits_get_errstatus($status, my $text);
524                    croak "Error in retrieving data for ID column: $status $text";
525                }
526            }
527            if ($ra_column != -1) {
528                $fptr->read_col(TFLOAT, $ra_column, 1, 1, $nrows, undef, $ra, undef, $status);
529                if ($status != 0) {
530                    Astro::FITS::CFITSIO::fits_get_errstatus($status, my $text);
531                    croak "Error in retrieving data for RA column: $status $text";
532                }
533            }
534            if ($dec_column != -1) {
535                $fptr->read_col(TFLOAT, $dec_column, 1, 1, $nrows, undef, $dec, undef, $status);
536                if ($status != 0) {
537                    Astro::FITS::CFITSIO::fits_get_errstatus($status, my $text);
538                    croak "Error in retrieving data for Dec column: $status $text";
539                }
540            }
541            if ($iso_flux_column != -1) {
542                $fptr->read_col(TFLOAT, $iso_flux_column, 1, 1, $nrows, undef, $iso_flux, undef, $status);
543                if ($status != 0) {
544                    Astro::FITS::CFITSIO::fits_get_errstatus($status, my $text);
545                    croak "Error in retrieving data for isophotal flux column: $status $text";
546                }
547            }
548            if ($total_flux_column != -1) {
549                $fptr->read_col(TFLOAT, $total_flux_column, 1, 1, $nrows, undef, $total_flux, undef, $status);
550                if ($status != 0) {
551                    Astro::FITS::CFITSIO::fits_get_errstatus($status, my $text);
552                    croak "Error in retrieving data for total flux column: $status $text";
553                }
554            }
555            if ($total_flux_err_column != -1) {
556                $fptr->read_col(TFLOAT, $total_flux_err_column, 1, 1, $nrows, undef, $total_flux_err, undef, $status);
557                if ($status != 0) {
558                    Astro::FITS::CFITSIO::fits_get_errstatus($status, my $text);
559                    croak "Error in retrieving data for total flux err column: $status $text";
560                }
561            }
562            if ($core_flux_column != -1) {
563                $fptr->read_col(TFLOAT, $core_flux_column, 1, 1, $nrows, undef, $core_flux, undef, $status);
564                if ($status != 0) {
565                    Astro::FITS::CFITSIO::fits_get_errstatus($status, my $text);
566                    croak "Error in retrieving data for core flux column: $status $text";
567                }
568            }
569            if ($core1_flux_column != -1) {
570                $fptr->read_col(TFLOAT, $core1_flux_column, 1, 1, $nrows, undef, $core1_flux, undef, $status);
571                if ($status != 0) {
572                    Astro::FITS::CFITSIO::fits_get_errstatus($status, my $text);
573                    croak "Error in retrieving data for core1 flux column: $status $text";
574                }
575            }
576            if ($core2_flux_column != -1) {
577                $fptr->read_col(TFLOAT, $core2_flux_column, 1, 1, $nrows, undef, $core2_flux, undef, $status);
578                if ($status != 0) {
579                    Astro::FITS::CFITSIO::fits_get_errstatus($status, my $text);
580                    croak "Error in retrieving data for core2 flux column: $status $text";
581                }
582            }
583            if ($core3_flux_column != -1) {
584                $fptr->read_col(TFLOAT, $core3_flux_column, 1, 1, $nrows, undef, $core3_flux, undef, $status);
585                if ($status != 0) {
586                    Astro::FITS::CFITSIO::fits_get_errstatus($status, my $text);
587                    croak "Error in retrieving data for core3 flux column: $status $text";
588                }
589            }
590            if ($core4_flux_column != -1) {
591                $fptr->read_col(TFLOAT, $core4_flux_column, 1, 1, $nrows, undef, $core4_flux, undef, $status);
592                if ($status != 0) {
593                    Astro::FITS::CFITSIO::fits_get_errstatus($status, my $text);
594                    croak "Error in retrieving data for core4 flux column: $status $text";
595                }
596            }
597            if ($isoarea_column != -1) {
598                $fptr->read_col(TINT, $isoarea_column, 1, 1, $nrows, undef, $isoarea, undef, $status);
599                if ($status != 0) {
600                    Astro::FITS::CFITSIO::fits_get_errstatus($status, my $text);
601                    croak "Error in retrieving data for isoarea column: $status $text";
602                }
603            }
604            if ($ell_column != -1) {
605                $fptr->read_col(TFLOAT, $ell_column, 1, 1, $nrows, undef, $ell, undef, $status);
606                if ($status != 0) {
607                    Astro::FITS::CFITSIO::fits_get_errstatus($status, my $text);
608                    croak "Error in retrieving data for ellipticity column: $status $text";
609                }
610            }
611            if ($posang_column != -1) {
612                $fptr->read_col(TFLOAT, $posang_column, 1, 1, $nrows, undef, $posang, undef, $status);
613                if ($status != 0) {
614                    Astro::FITS::CFITSIO::fits_get_errstatus($status, my $text);
615                    croak "Error in retrieving data for position angle column: $status $text";
616                }
617            }
618            if ($x_column != -1) {
619                $fptr->read_col(TFLOAT, $x_column, 1, 1, $nrows, undef, $x_pos, undef, $status);
620                if ($status != 0) {
621                    Astro::FITS::CFITSIO::fits_get_errstatus($status, my $text);
622                    croak "Error in retrieving data for x-coordinate column: $status $text";
623                }
624            }
625            if ($y_column != -1) {
626                $fptr->read_col(TFLOAT, $y_column, 1, 1, $nrows, undef, $y_pos, undef, $status);
627                if ($status != 0) {
628                    Astro::FITS::CFITSIO::fits_get_errstatus($status, my $text);
629                    croak "Error in retrieving data for y-coordinate column: $status $text";
630                }
631            }
632            if ($flag_column != -1) {
633                $fptr->read_col(TINT, $flag_column, 1, 1, $nrows, undef, $flags, undef, $status);
634                if ($status != 0) {
635                    Astro::FITS::CFITSIO::fits_get_errstatus($status, my $text);
636                    croak "Error in retrieving data for flags column: $status $text";
637                }
638            }
639
640            # Go through each array, grabbing the information and creating a
641            # new Astro::Catalog::Item object each time through.
642            for (my $i = 0; $i < $nrows; $i++) {
643                my $id_value;
644                if ($id_column != -1) {
645                    $id_value = $id->[$i];
646                }
647                my $ra_value;
648                if ($ra_column != -1) {
649                    $ra_value = $ra->[$i];
650                }
651                my $dec_value;
652                if ($dec_column != -1) {
653                    $dec_value = $dec->[$i];
654                }
655                my $iso_flux_value;
656                if ($iso_flux_column != -1) {
657                    $iso_flux_value = $iso_flux->[$i];
658                }
659                my $total_flux_value;
660                if ($total_flux_column != -1) {
661                    $total_flux_value = $total_flux->[$i];
662                }
663                my $total_flux_err_value;
664                if ($total_flux_err_column != -1) {
665                    $total_flux_err_value = $total_flux_err->[$i];
666                }
667                my $core_flux_value;
668                if ($core_flux_column != -1) {
669                    $core_flux_value = $core_flux->[$i];
670                }
671                my $core1_flux_value;
672                if ($core1_flux_column != -1) {
673                    $core1_flux_value = $core1_flux->[$i];
674                }
675                my $core2_flux_value;
676                if ($core2_flux_column != -1) {
677                    $core2_flux_value = $core2_flux->[$i];
678                }
679                my $core3_flux_value;
680                if ($core3_flux_column != -1) {
681                    $core3_flux_value = $core3_flux->[$i];
682                }
683                my $core4_flux_value;
684                if ($core4_flux_column != -1) {
685                    $core4_flux_value = $core4_flux->[$i];
686                }
687                my $isoarea_value;
688                if ($isoarea_column != -1) {
689                    $isoarea_value = $isoarea->[$i];
690                }
691                my $ell_value;
692                if ($ell_column != -1) {
693                    $ell_value = $ell->[$i];
694                }
695                my $posang_value;
696                if ($posang_column != -1) {
697                    $posang_value = $posang->[$i];
698                }
699                my $x_pos_value;
700                if ($x_column != -1) {
701                    $x_pos_value = $x_pos->[$i];
702                }
703                my $y_pos_value;
704                if ($y_column != -1) {
705                    $y_pos_value = $y_pos->[$i];
706                }
707                my $flags_value = 0;
708                if ($flag_column != -1) {
709                    $flags_value = $flags->[$i];
710                }
711
712                # Create a temporary Astro::Catalog::Item object.
713                my $star = new Astro::Catalog::Item();
714
715                # Set up the Astro::Coords object, assuming our RA and Dec are in units
716                # of degrees.
717                my $coords;
718                if (defined($ra_value) && defined($dec_value)) {
719                    $coords = new Astro::Coords(
720                        ra => $ra_value,
721                        dec => $dec_value,
722                        units => 'degrees',
723                        type => 'J2000',
724                    );
725                    $star->coords($coords);
726                }
727
728                if ($flag_column != -1) {
729                    $star->quality($flags_value);
730                }
731                else {
732                    $star->quality(0);
733                }
734
735                if ($id_column != -1) {
736                    $star->id($id_value);
737                }
738
739                if ($x_column != -1) {
740                    $star->x($x_pos_value);
741                }
742                if ($y_column != -1) {
743                    $star->y($y_pos_value);
744                }
745
746                # Set up the Astro::Flux objects.
747                if ($iso_flux_column != -1) {
748                    my $num;
749                    $num = new Number::Uncertainty(Value => $iso_flux_value);
750                    my $flux_iso = new Astro::Flux($num, 'isophotal_flux', $waveband,
751                            datetime => $datetime, obsid => $obsid);
752                    $star->fluxes(new Astro::Fluxes($flux_iso));
753                }
754
755                if ($total_flux_column != -1) {
756                    my $num;
757                    $num = new Number::Uncertainty(Value => $total_flux_value);
758                    my $flux_total = new Astro::Flux($num, 'total_flux', $waveband,
759                            datetime => $datetime, obsid => $obsid );
760                    $star->fluxes(new Astro::Fluxes($flux_total));
761                }
762
763                if ($core_flux_column != -1) {
764                    my $num;
765                    $num = new Number::Uncertainty(Value => $core_flux_value);
766                    my $core_flux_obj = new Astro::Flux($num, 'core_flux', $waveband,
767                            datetime => $datetime, obsid => $obsid );
768                    $star->fluxes(new Astro::Fluxes($core_flux_obj));
769                }
770
771                if ($core1_flux_column != -1) {
772                    my $num;
773                    $num = new Number::Uncertainty(Value => $core1_flux_value);
774                    my $core1_flux_obj = new Astro::Flux($num, 'core1_flux', $waveband,
775                            datetime => $datetime, obsid => $obsid );
776                    $star->fluxes(new Astro::Fluxes($core1_flux_obj));
777                }
778
779                if ($core2_flux_column != -1) {
780                    my $num;
781                    $num = new Number::Uncertainty(Value => $core2_flux_value);
782                    my $core2_flux_obj = new Astro::Flux($num, 'core2_flux', $waveband,
783                            datetime => $datetime, obsid => $obsid );
784                    $star->fluxes(new Astro::Fluxes($core2_flux_obj));
785                }
786
787                if ($core3_flux_column != -1) {
788                    my $num;
789                    $num = new Number::Uncertainty(Value => $core3_flux_value);
790                    my $core3_flux_obj = new Astro::Flux($num, 'core3_flux', $waveband,
791                            datetime => $datetime, obsid => $obsid );
792                    $star->fluxes(new Astro::Fluxes($core3_flux_obj));
793                }
794
795                if ($core4_flux_column != -1) {
796                    my $num;
797                    $num = new Number::Uncertainty(Value => $core4_flux_value);
798                    my $core4_flux_obj = new Astro::Flux($num, 'core4_flux', $waveband,
799                        datetime => $datetime, obsid => $obsid);
800                    $star->fluxes(new Astro::Fluxes($core4_flux_obj));
801                }
802
803                # Compute a magnitude and mag. error from the total flux value and total
804                # flux error (if available).
805                if ($total_flux_value > 0.0 and $total_flux_err_value > 0.0) {
806                    my $mag = -2.5 * log10($total_flux_value);
807                    my $num;
808                    if ($total_flux_err_column != -1) {
809                        my $magerr = 2.5 / log(10) * $total_flux_err_value / $total_flux_value;
810                        $num = new Number::Uncertainty(
811                            Value => $mag,
812                            Error => 2.0 * $magerr);
813                    }
814                    else {
815                        $num = new Number::Uncertainty(Value => $mag);
816                    }
817                    my $mag_obj = new Astro::Flux($num, 'MAG',  $waveband,
818                        datetime => $datetime, obsid => $obsid);
819                    $star->fluxes(new Astro::Fluxes($mag_obj));
820                }
821                # And set up the Astro::Catalog::Item::Morphology object.
822                my $morphology = new Astro::Catalog::Item::Morphology(
823                    area => $isoarea_value,
824                    ellipticity => $ell_value,
825                    position_angle_pixel => $posang_value,
826                );
827                $star->morphology($morphology);
828
829
830                # Push it onto the Astro::Catalog object.
831                $catalog->pushstar($star);
832            }
833
834        }
835        $status = 0;
836
837        # Move to the next one.
838        $fptr->movrel_hdu(1, $hdutype, $status);
839        last if ($status == END_OF_FILE);
840
841        # And set $hdu_pos.
842        $fptr->get_hdu_num($hdu_pos);
843    }
844
845    # Set the origin.
846    $catalog->origin('IO::LCOGTFITSTable');
847
848    # And return.
849    return $catalog;
850}
851
852=item B<_write_catalog>
853
854Create an output catalog as a binary FITS table.
855
856    $ref = Astro::Catalog::IO::LCOGTFITSTable->_write_catalog($catalog);
857
858Argument is an C<Astro::Catalog> object.
859
860This method is not yet implemented.
861
862=cut
863
864sub _write_catalog {
865    croak "Not yet implemented.";
866}
867
8681;
869
870__END__
871
872=back
873
874=head1 SEE ALSO
875
876L<Astro::Catalog>
877
878=head1 COPYRIGHT
879
880Copyright (C) 2012 Las Cumbres Observatory Global Telescope Network.
881All Rights Reserved.
882
883This module is free software;
884you can redistribute it and/or modify it under the terms of the GNU
885Public License.
886
887=head1 AUTHORS
888
889Tim Lister E<lt>tlister@lcogt.netE<gt>
890
891=cut
892