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