Astro::Catalog::IO::SExtractor - SExtractor output catalogue I/O for


Astro-Catalog documentation Contained in the Astro-Catalog distribution.

Index


Code Index:

NAME

Top

Astro::Catalog::IO::SExtractor - SExtractor output catalogue I/O for Astro::Catalog.

SYNOPSIS

Top

$cat = Astro::Catalog::IO::SExtractor->_read_catalog( \@lines );

DESCRIPTION

Top

This class provides read and write methods for catalogues written by SExtractor, as long as they were written in ASCII_HEAD format. The methods are not public and should, in general, only be called from the Astro::Catalog read_catalog and write_catalog methods.


Astro-Catalog documentation Contained in the Astro-Catalog distribution.
package Astro::Catalog::IO::SExtractor;

use 5.006;
use warnings;
use warnings::register;
use Carp;
use strict;

# Bring in the Astro:: modules.
use Astro::Catalog;
use Astro::Catalog::Item;
use Astro::Catalog::Item::Morphology;
use Astro::Coords;
use Astro::SLA;

use Number::Uncertainty;
use Astro::Flux;
use Astro::FluxColor;
use Astro::Fluxes;

use base qw/ Astro::Catalog::IO::ASCII /;

use vars qw/ $VERSION $DEBUG /;

$VERSION = '0.01';
$DEBUG = 0;

sub _read_catalog {
  my $class = shift;
  my $lines = shift;
  my %args = @_;

  if( ref( $lines ) ne 'ARRAY' ) {
    croak "Must supply catalogue contents as a reference to an array";
  }

  if( defined( $args{'Filter'} ) &&
      ! UNIVERSAL::isa( $args{'Filter'}, "Astro::WaveBand" ) ) {
    croak "Filter as passed to SExtractor->_read_catalog must be an Astro::WaveBand object";
  }

  my $filter;
  if( defined( $args{'Filter'} ) ) {
    $filter = $args{'Filter'}->natural;
  } else {
    $filter = 'unknown';
  }

  my $quality = $args{'Quality'};
  if( ! defined( $quality ) ) {
    $quality = -1;
  }

  my @lines = @$lines; # Dereference, make own copy.

  # Create an Astro::Catalog object;
  my $catalog = new Astro::Catalog();

  # Set up columns.
  my $id_column = -1;
  my $x_column = -1;
  my $x_pixel_column = -1;
  my $xerr_column = -1;
  my $xwin_column = -1;
  my $xwinerr_column = -1;
  my $y_column = -1;
  my $y_pixel_column = -1;
  my $yerr_column = -1;
  my $ywin_column = -1;
  my $ywinerr_column = -1;
  my $ra_column = -1;
  my $dec_column = -1;
  my $mag_iso_column = -1;
  my $magerr_iso_column = -1;
  my $flux_iso_column = -1;
  my $fluxerr_iso_column = -1;
  my $flux_isocor_column = -1;
  my $fluxerr_isocor_column = -1;
  my $mag_isocor_column = -1;
  my $magerr_isocor_column = -1;
  my $flux_aper1_column = -1;
  my $fluxerr_aper1_column = -1;
  my $mag_aper1_column = -1;
  my $magerr_aper1_column = -1;
  my $flux_aper2_column = -1;
  my $fluxerr_aper2_column = -1;
  my $mag_aper2_column = -1;
  my $magerr_aper2_column = -1;
  my $flux_auto_column = -1;
  my $fluxerr_auto_column = -1;
  my $mag_auto_column = -1;
  my $magerr_auto_column = -1;
  my $flux_best_column = -1;
  my $fluxerr_best_column = -1;
  my $mag_best_column = -1;
  my $magerr_best_column = -1;
  my $ell_column = -1;
  my $posang_pixel_column = -1;
  my $posangerr_pixel_column = -1;
  my $posang_world_column = -1;
  my $posangerr_world_column = -1;
  my $minor_pixel_column = -1;
  my $minorerr_pixel_column = -1;
  my $major_pixel_column = -1;
  my $majorerr_pixel_column = -1;
  my $minor_world_column = -1;
  my $minorerr_world_column = -1;
  my $major_world_column = -1;
  my $majorerr_world_column = -1;
  my $area_column = -1;
  my $flag_column = -1;

  # Loop through the lines.
  for ( @lines ) {
    my $line = $_;

    # If we're on a column line that starts with a #, check to see
    # if it's describing where the X, Y, RA, or Dec position is in
    # the table, or the object number, or the flux, or the error in
    # flux.
    if( $line =~ /^#/ ) {
      my @column = split( /\s+/, $line );
      if( $column[2] =~ /^NUMBER/ ) {
        $id_column = $column[1] - 1;
        print "ID column is $id_column\n" if $DEBUG;

      } elsif( $column[2] =~ /^X_IMAGE/ ) {
        $x_column = $column[1] - 1;
        print "X_IMAGE column is $x_column\n" if $DEBUG;

      } elsif( $column[2] =~ /^Y_IMAGE/ ) {
        $y_column = $column[1] - 1;
        print "Y_IMAGE column is $y_column\n" if $DEBUG;

      } elsif( $column[2] =~ /^X_PIXEL/ ) {
        $x_pixel_column = $column[1] - 1;
        print "X_PIXEL column is $x_pixel_column\n" if $DEBUG;

      } elsif( $column[2] =~ /^Y_PIXEL/ ) {
        $y_pixel_column = $column[1] - 1;
        print "Y_PIXEL column is $y_pixel_column\n" if $DEBUG;

      } elsif( $column[2] =~ /^ERRX2_IMAGE/ ) {
        $xerr_column = $column[1] - 1;
        print "X ERROR column is $xerr_column\n" if $DEBUG;

      } elsif( $column[2] =~ /^ERRY2_IMAGE/ ) {
        $yerr_column = $column[1] - 1;
        print "Y ERROR column is $yerr_column\n" if $DEBUG;

      } elsif( $column[2] =~ /^XWIN_IMAGE/ ) {
        $xwin_column = $column[1] - 1;
        print "XWIN_IMAGE column is $xwin_column\n" if $DEBUG;

      } elsif( $column[2] =~ /^ERRX2WIN_IMAGE/ ) {
        $xwinerr_column = $column[1] - 1;
        print "ERRX2WIN_IMAGE column is $xwinerr_column\n" if $DEBUG;

      } elsif( $column[2] =~ /^YWIN_IMAGE/ ) {
        $ywin_column = $column[1] - 1;
        print "YWIN_IMAGE column is $ywin_column\n" if $DEBUG;

      } elsif( $column[2] =~ /^ERRY2WIN_IMAGE/ ) {
        $ywinerr_column = $column[1] - 1;
        print "ERRY2WIN_IMAGE column is $ywinerr_column\n" if $DEBUG;

      } elsif( $column[2] =~ /^ALPHA_J2000/ ) {
        $ra_column = $column[1] - 1;
        print "RA column is $ra_column\n" if $DEBUG;

      } elsif( $column[2] =~ /^DELTA_J2000/ ) {
        $dec_column = $column[1] - 1;
        print "DEC column is $dec_column\n" if $DEBUG;

      } elsif( $column[2] =~ /^MAG_ISO$/ ) {
        $mag_iso_column = $column[1] - 1;
        print "MAG_ISO column is $mag_iso_column\n" if $DEBUG;

      } elsif( $column[2] =~ /^MAGERR_ISO$/ ) {
        $magerr_iso_column = $column[1] - 1;
        print "MAGERR_ISO column is $magerr_iso_column\n" if $DEBUG;

      } elsif( $column[2] =~ /^FLUX_ISO$/ ) {
        $flux_iso_column = $column[1] - 1;
        print "FLUX_ISO column is $flux_iso_column\n" if $DEBUG;

      } elsif( $column[2] =~ /^FLUXERR_ISO$/ ) {
        $fluxerr_iso_column = $column[1] - 1;
        print "FLUXERR_ISO column is $fluxerr_iso_column\n" if $DEBUG;

      } elsif( $column[2] =~ /^FLUX_ISOCOR/ ) {
        $flux_isocor_column = $column[1] - 1;
        print "FLUX_ISOCOR column is $flux_isocor_column\n" if $DEBUG;

      } elsif( $column[2] =~ /^FLUXERR_ISOCOR/ ) {
        $fluxerr_isocor_column = $column[1] - 1;
        print "FLUXERR_ISOCOR column is $fluxerr_isocor_column\n" if $DEBUG;

      } elsif( $column[2] =~ /^MAG_ISOCOR/ ) {
        $mag_isocor_column = $column[1] - 1;
        print "MAG_ISOCOR column is $mag_isocor_column\n" if $DEBUG;

      } elsif( $column[2] =~ /^MAGERR_ISOCOR/ ) {
        $magerr_isocor_column = $column[1] - 1;
        print "MAGERR_ISOCOR column is $magerr_isocor_column\n" if $DEBUG;

      } elsif( $column[2] =~ /^FLUX_APER/ ) {
        $flux_aper1_column = $column[1] - 1;
        print "FLUX_APER column is $flux_aper1_column\n" if $DEBUG;

      } elsif( $column[2] =~ /^FLUXERR_APER/ ) {
        $fluxerr_aper1_column = $column[1] - 1;
        print "FLUXERR_APER column is $fluxerr_aper1_column\n" if $DEBUG;

      } elsif( $column[2] =~ /^MAG_APER/ ) {
        $mag_aper1_column = $column[1] - 1;
        print "MAG_APER column is $mag_aper1_column\n" if $DEBUG;

      } elsif( $column[2] =~ /^MAGERR_APER/ ) {
        $magerr_aper1_column = $column[1] - 1;
        print "MAGERR_APER column is $magerr_aper1_column\n" if $DEBUG;

      } elsif( $column[2] =~ /^FLUX_AUTO/ ) {
        $flux_auto_column = $column[1] - 1;
        print "FLUX_AUTO column is $flux_auto_column\n" if $DEBUG;

      } elsif( $column[2] =~ /^FLUXERR_AUTO/ ) {
        $fluxerr_auto_column = $column[1] - 1;
        print "FLUXERR_AUTO column is $fluxerr_auto_column\n" if $DEBUG;

      } elsif( $column[2] =~ /^MAG_AUTO/ ) {
        $mag_auto_column = $column[1] - 1;
        print "MAG_AUTO column is $mag_auto_column\n" if $DEBUG;

      } elsif( $column[2] =~ /^MAGERR_AUTO/ ) {
        $magerr_auto_column = $column[1] - 1;
        print "MAGERR_AUTO column is $magerr_auto_column\n" if $DEBUG;

      } elsif( $column[2] =~ /^FLUX_BEST/ ) {
        $flux_best_column = $column[1] - 1;
        print "FLUX_BEST column is $flux_best_column\n" if $DEBUG;

      } elsif( $column[2] =~ /^FLUXERR_BEST/ ) {
        $fluxerr_best_column = $column[1] - 1;
        print "FLUXERR_BEST column is $fluxerr_best_column\n" if $DEBUG;

      } elsif( $column[2] =~ /^MAG_BEST/ ) {
        $mag_best_column = $column[1] - 1;
        print "MAG_BEST column is $mag_best_column\n" if $DEBUG;

      } elsif( $column[2] =~ /^MAGERR_BEST/ ) {
        $magerr_best_column = $column[1] - 1;
        print "MAGERR_BEST_COLUMN is $magerr_best_column\n" if $DEBUG;

      } elsif( $column[2] =~ /^ELLIPTICITY/ ) {
        $ell_column = $column[1] - 1;
        print "ELLIPTICITY column is $ell_column\n" if $DEBUG;

      } elsif( $column[2] =~ /^THETA_IMAGE/ ) {
        $posang_pixel_column = $column[1] - 1;
        print "THETA_IMAGE column is $posang_pixel_column\n" if $DEBUG;

      } elsif( $column[2] =~ /^ERRTHETA_IMAGE/ ) {
        $posangerr_pixel_column = $column[1] - 1;
        print "ERRTHETA_IMAGE column is $posangerr_pixel_column\n" if $DEBUG;

      } elsif( $column[2] =~ /^THETA_SKY/ ) {
        $posang_world_column = $column[1] - 1;
        print "THETA_SKY column is $posang_world_column\n" if $DEBUG;

      } elsif( $column[2] =~ /^ERRTHETA_SKY/ ) {
        $posangerr_world_column = $column[1] - 1;
        print "ERRTHETA_SKY column is $posangerr_world_column\n" if $DEBUG;

      } elsif( $column[2] =~ /^B_IMAGE/ ) {
        $minor_pixel_column = $column[1] - 1;
        print "B_IMAGE column is $minor_pixel_column\n" if $DEBUG;

      } elsif( $column[2] =~ /^ERRB_IMAGE/ ) {
        $minorerr_pixel_column = $column[1] - 1;
        print "ERRB_IMAGE column is $minorerr_pixel_column\n" if $DEBUG;

      } elsif( $column[2] =~ /^A_IMAGE/ ) {
        $major_pixel_column = $column[1] - 1;
        print "A_IMAGE column is $major_pixel_column\n" if $DEBUG;

      } elsif( $column[2] =~ /^ERRA_IMAGE/ ) {
        $majorerr_pixel_column = $column[1] - 1;
        print "ERRA_IMAGE column is $majorerr_pixel_column\n" if $DEBUG;

      } elsif( $column[2] =~ /^B_WORLD/ ) {
        $minor_world_column = $column[1] - 1;
        print "B_WORLD column is $minor_world_column\n" if $DEBUG;

      } elsif( $column[2] =~ /^ERRB_WORLD/ ) {
        $minorerr_world_column = $column[1] - 1;
        print "ERRB_WORLD column is $minorerr_world_column\n" if $DEBUG;

      } elsif( $column[2] =~ /^A_WORLD/ ) {
        $major_world_column = $column[1] - 1;
        print "A_WORLD column is $major_world_column\n" if $DEBUG;

      } elsif( $column[2] =~ /^ERRA_WORLD/ ) {
        $majorerr_world_column = $column[1] - 1;
        print "ERR_AWORLD column is $majorerr_world_column\n" if $DEBUG;

      } elsif( $column[2] =~ /^ISOAREA_IMAGE/ ) {
        $area_column = $column[1] - 1;
        print "AREA column is $area_column\n" if $DEBUG;

      } elsif( $column[2] =~ /^FLAGS/ ) {
        $flag_column = $column[1] - 1;
        print "FLAGS column is $flag_column\n" if $DEBUG;

      }
      next;
    }

    # Remove leading whitespace and go to the next line if the
    # current one is blank.
    $line =~ s/^\s+//;
    next if length( $line ) == 0;

    # Form an array of the fields in the catalogue.
    my @fields = split( /\s+/, $line );

    # Don't deal with this object if our requested quality is not -1
    # and the quality of the object is not equal to the requested
    # quality and we have a quality flag for this object.
    if( ( $quality != -1 ) &&
        ( $flag_column != -1 ) &&
        ( $fields[$flag_column] != $quality ) ) {
      next;
    }

    # Create a temporary Astro::Catalog::Item object.
    my $star = new Astro::Catalog::Item();

    # Grab the coordinates, forming an Astro::Coords object., but only
    # if the RA and Dec columns are defined.
    if( $ra_column != -1 &&
        $dec_column != -1 ) {
      my $coords = new Astro::Coords( type => 'J2000',
                                      ra => $fields[$ra_column],
                                      dec => $fields[$dec_column],
                                      name => ( $id_column != -1 ? $fields[$id_column] : undef ),
                                      units => 'degrees',
                                    );
      $star->coords( $coords );
    }

    if( $flag_column != -1 ) {
      $star->quality( $fields[$flag_column] );
    } else {
      $star->quality( 0 );
    }

    if( $id_column != -1 ) {
      $star->id( $fields[$id_column] );
    }

    # Set up the various flux and magnitude measurements.
    if( $mag_iso_column != -1 ) {
      my $num;
      if( $magerr_iso_column != -1 ) {
        $num = new Number::Uncertainty( Value => $fields[$mag_iso_column],
                                        Error => $fields[$magerr_iso_column] );
      } else {
        $num = new Number::Uncertainty( Value => $fields[$mag_iso_column] );
      }
      my $mag_iso = new Astro::Flux( $num, 'MAG_ISO', $filter );
      $star->fluxes( new Astro::Fluxes( $mag_iso ) );
    }
    if( $flux_iso_column != -1 ) {
      my $num;
      if( $fluxerr_iso_column != -1 ) {
        $num = new Number::Uncertainty( Value => $fields[$flux_iso_column],
                                        Error => $fields[$fluxerr_iso_column] );
      } else {
        $num = new Number::Uncertainty( Value => $fields[$flux_iso_column] );
      }
      my $flux_iso = new Astro::Flux( $num, 'FLUX_ISO', $filter );
      $star->fluxes( new Astro::Fluxes( $flux_iso ) );
    }

    if( $mag_isocor_column != -1 ) {
      my $num;
      if( $magerr_isocor_column != -1 ) {
        $num = new Number::Uncertainty( Value => $fields[$mag_isocor_column],
                                        Error => $fields[$magerr_isocor_column] );
      } else {
        $num = new Number::Uncertainty( Value => $fields[$mag_isocor_column] );
      }
      my $mag_isocor = new Astro::Flux( $num, 'MAG_ISOCOR', $filter );
      $star->fluxes( new Astro::Fluxes( $mag_isocor ) );
    }
    if( $flux_isocor_column != -1 ) {
      my $num;
      if( $fluxerr_isocor_column != -1 ) {
        $num = new Number::Uncertainty( Value => $fields[$flux_isocor_column],
                                        Error => $fields[$fluxerr_isocor_column] );
      } else {
        $num = new Number::Uncertainty( Value => $fields[$flux_isocor_column] );
      }
      my $flux_isocor = new Astro::Flux( $num, 'FLUX_ISOCOR', $filter );
      $star->fluxes( new Astro::Fluxes( $flux_isocor ) );
    }

    if( $mag_aper1_column != -1 ) {
      my $num;
      if( $magerr_aper1_column != -1 ) {
        $num = new Number::Uncertainty( Value => $fields[$mag_aper1_column],
                                        Error => $fields[$magerr_aper1_column] );
      } else {
        $num = new Number::Uncertainty( Value => $fields[$mag_aper1_column] );
      }
      my $mag_aper1 = new Astro::Flux( $num, 'MAG_APER1', $filter );
      $star->fluxes( new Astro::Fluxes( $mag_aper1 ) );
    }
    if( $flux_aper1_column != -1 ) {
      my $num;
      if( $fluxerr_aper1_column != -1 ) {
        $num = new Number::Uncertainty( Value => $fields[$flux_aper1_column],
                                        Error => $fields[$fluxerr_aper1_column] );
      } else {
        $num = new Number::Uncertainty( Value => $fields[$flux_aper1_column] );
      }
      my $flux_aper1 = new Astro::Flux( $num, 'FLUX_APER1', $filter );
      $star->fluxes( new Astro::Fluxes( $flux_aper1 ) );
    }

    if( $mag_auto_column != -1 ) {
      my $num;
      if( $magerr_auto_column != -1 ) {
        $num = new Number::Uncertainty( Value => $fields[$mag_auto_column],
                                        Error => $fields[$magerr_auto_column] );
      } else {
        $num = new Number::Uncertainty( Value => $fields[$mag_auto_column] );
      }
      my $mag_auto = new Astro::Flux( $num, 'MAG_AUTO', $filter );
      $star->fluxes( new Astro::Fluxes( $mag_auto ) );
    }
    if( $flux_auto_column != -1 ) {
      my $num;
      if( $fluxerr_auto_column != -1 ) {
        $num = new Number::Uncertainty( Value => $fields[$flux_auto_column],
                                        Error => $fields[$fluxerr_auto_column] );
      } else {
        $num = new Number::Uncertainty( Value => $fields[$flux_auto_column] );
      }
      my $flux_auto = new Astro::Flux( $num, 'FLUX_AUTO', $filter );
      $star->fluxes( new Astro::Fluxes( $flux_auto ) );
    }

    if( $mag_best_column != -1 ) {
      my $num;
      if( $magerr_best_column != -1 ) {
        $num = new Number::Uncertainty( Value => $fields[$mag_best_column],
                                        Error => $fields[$magerr_best_column] );
      } else {
        $num = new Number::Uncertainty( Value => $fields[$mag_best_column] );
      }
      my $mag_best = new Astro::Flux( $num, 'MAG_BEST', $filter );
      $star->fluxes( new Astro::Fluxes( $mag_best ) );
    }
    if( $flux_best_column != -1 ) {
      my $num;
      if( $fluxerr_best_column != -1 ) {
        $num = new Number::Uncertainty( Value => $fields[$flux_best_column],
                                        Error => $fields[$fluxerr_best_column] );
      } else {
        $num = new Number::Uncertainty( Value => $fields[$flux_best_column] );
      }
      my $flux_best = new Astro::Flux( $num, 'FLUX_BEST', $filter );
      $star->fluxes( new Astro::Fluxes( $flux_best ) );
    }

    # Set the x and y coordinates. Preferentially use the NDF pixel
    # coordinates, then the windowed coordinates, then the standard
    # coordinates.
    if( $x_pixel_column != -1 ) {
      $star->x( $fields[$x_pixel_column] );
    } elsif( $xwin_column != -1 ) {
      $star->x( $fields[$xwin_column] );
    } elsif( $x_column != -1 ) {
      $star->x( $fields[$x_column] );
    }
    if( $y_pixel_column != -1 ) {
      $star->y( $fields[$y_pixel_column] );
    } elsif( $ywin_column != -1 ) {
      $star->y( $fields[$ywin_column] );
    } elsif( $x_column != -1 ) {
      $star->y( $fields[$y_column] );
    }

    # Set up the star's morphology.
    my $ellipticity;
    my $position_angle_pixel;
    my $position_angle_world;
    my $major_axis_pixel;
    my $minor_axis_pixel;
    my $major_axis_world;
    my $minor_axis_world;
    my $area;
    if( $ell_column != -1 ) {
      $ellipticity = new Number::Uncertainty( Value => $fields[$ell_column] );
    }
    if( $posang_pixel_column != -1 ) {
      if( $posangerr_pixel_column != -1 ) {
        $position_angle_pixel = new Number::Uncertainty( Value => $fields[$posang_pixel_column],
                                                         Error => $fields[$posangerr_pixel_column] );
      } else {
        $position_angle_pixel = new Number::Uncertainty( Value => $fields[$posang_pixel_column] );
      }
    }
    if( $posang_world_column != -1 ) {
      if( $posangerr_world_column != -1 ) {
        $position_angle_world = new Number::Uncertainty( Value => $fields[$posang_world_column],
                                                         Error => $fields[$posangerr_world_column] );
      } else {
        $position_angle_world = new Number::Uncertainty( Value => $fields[$posang_world_column] );
      }
    }
    if( $major_pixel_column != -1 ) {
      if( $majorerr_pixel_column != -1 ) {
        $major_axis_pixel = new Number::Uncertainty( Value => $fields[$major_pixel_column],
                                                     Error => $fields[$majorerr_pixel_column] );
      } else {
        $major_axis_pixel = new Number::Uncertainty( Value => $fields[$major_pixel_column] );
      }
    }
    if( $major_world_column != -1 ) {
      if( $majorerr_world_column != -1 ) {
        $major_axis_world = new Number::Uncertainty( Value => $fields[$major_world_column],
                                                     Error => $fields[$majorerr_world_column] );
      } else {
        $major_axis_world = new Number::Uncertainty( Value => $fields[$major_world_column] );
      }
    }
    if( $minor_pixel_column != -1 ) {
      if( $minorerr_pixel_column != -1 ) {
        $minor_axis_pixel = new Number::Uncertainty( Value => $fields[$minor_pixel_column],
                                                     Error => $fields[$minorerr_pixel_column] );
      } else {
        $minor_axis_pixel = new Number::Uncertainty( Value => $fields[$minor_pixel_column] );
      }
    }
    if( $minor_world_column != -1 ) {
      if( $minorerr_world_column != -1 ) {
        $minor_axis_world = new Number::Uncertainty( Value => $fields[$minor_world_column],
                                                     Error => $fields[$minorerr_world_column] );
      } else {
        $minor_axis_world = new Number::Uncertainty( Value => $fields[$minor_world_column] );
      }
    }
    if( $area_column != -1 ) {
      $area = new Number::Uncertainty( Value => $fields[$area_column] );
    }
    my $morphology = new Astro::Catalog::Item::Morphology( ellipticity => $ellipticity,
                                                           position_angle_pixel => $position_angle_pixel,
                                                           position_angle_world => $position_angle_world,
                                                           major_axis_pixel => $major_axis_pixel,
                                                           minor_axis_pixel => $minor_axis_pixel,
                                                           major_axis_world => $major_axis_world,
                                                           minor_axis_world => $minor_axis_world,
                                                           area => $area,
                                                         );
    $star->morphology( $morphology );

    # Push the star onto the catalog.
    $catalog->pushstar( $star );
  }

  $catalog->origin( 'IO::SExtractor' );
  return $catalog;
}

sub _write_catalog {
  croak ( 'Usage: _write_catalog( $catalog, [%opts] ') unless scalar(@_) >= 1;
  my $class = shift;
  my $catalog = shift;

  my @output;

# First, the header. What we write to the header depends on what
# values we have for our objects, so check for ID, X, Y, RA, and Dec
# values.
  my $write_id  = 0;
  my $write_x   = 0;
  my $write_y   = 0;
  my $write_ra  = 0;
  my $write_dec = 0;

  my @stars = $catalog->stars();

  if( defined( $stars[0]->id ) ) {
    $write_id = 1;
  }
  if( defined( $stars[0]->x ) ) {
    $write_x = 1;
  }
  if( defined( $stars[0]->y ) ) {
    $write_y = 1;
  }
  if( defined( $stars[0]->coords->ra ) ) {
    $write_ra = 1;
  }
  if( defined( $stars[0]->coords->dec ) ) {
    $write_dec = 1;
  }

# Now for the header.
  my $pos = 1;
  if( $write_id ) {
    push @output, "#   $pos NUMBER         Running object number";
    $pos++;
  }
  if( $write_x ) {
    push @output, "#   $pos X_IMAGE        Object position along x                     [pixel]";
    $pos++;
  }
  if( $write_y ) {
    push @output, "#   $pos Y_IMAGE        Object position along y                     [pixel]";
    $pos++;
  }
  if( $write_ra ) {
    push @output, "#   $pos ALPHA_J2000    Right ascension of barycenter (J2000)       [deg]";
    $pos++;
  }
  if( $write_dec ) {
    push @output, "#   $pos DELTA_J2000    Declination of barycenter (J2000)           [deg]";
    $pos++;
  }

# Now go through the objects.
  foreach my $star ( @stars ) {

    my $output_string = "";

    if( $write_id ) {
      $output_string .= $star->id . " ";
    }
    if( $write_x ) {
      $output_string .= $star->x . " ";
    }
    if( $write_y ) {
      $output_string .= $star->y . " ";
    }
    if( $write_ra ) {
      $output_string .= $star->coords->ra->degrees . " ";
    }
    if( $write_dec ) {
      $output_string .= $star->coords->dec->degrees . " ";
    }

    push @output, $output_string;
  }

# And return!
  return \@output;

}

1;