1package Astro::Catalog::Item; 2 3=head1 NAME 4 5Astro::Catalog::Item - A generic star object in a stellar catalogue. 6 7=head1 SYNOPSIS 8 9 $star = new Astro::Catalog::Item( 10 ID => $id, 11 Coords => new Astro::Coords(), 12 Morphology => new Astro::Catalog::Item::Morphology(), 13 Fluxes => new Astro::Fluxes(), 14 Quality => $quality_flag, 15 Field => $field, 16 GSC => $in_gsc, 17 Distance => $distance_to_centre, 18 PosAngle => $position_angle, 19 X => $x_pixel_coord, 20 Y => $y_pixel_coord, 21 WCS => new Starlink::AST(), 22 Comment => $comment_string 23 SpecType => $spectral_type, 24 StarType => $star_type, 25 LongStarType => $long_star_type, 26 MoreInfo => $url, 27 InsertDate => new Time::Piece(), 28 Misc => $hash_ref, 29 ); 30 31=head1 DESCRIPTION 32 33Stores generic meta-data about an individual stellar object from a catalogue. 34 35If the catalogue has a field center the Distance and Position Angle properties 36should be used to store the direction to the field center, e.g. a star from the 37USNO-A2 catalogue retrieived from the ESO/ST-ECF Archive will have these 38properties. 39 40=cut 41 42 43use strict; 44use warnings; 45use Carp; 46use Astro::Coords 0.12; 47use Astro::Catalog::Item::Morphology; 48use Astro::Fluxes; 49use Astro::Flux; 50use Astro::FluxColor; 51 52# Register an Astro::Catalog::Item warning category 53use warnings::register; 54 55our $VERSION = '4.36'; 56 57# Internal lookup table for Simbad star types 58my %STAR_TYPE_LOOKUP = ( 59 'vid' => 'Underdense region of the Universe', 60 'Er*' => 'Eruptive variable Star', 61 'Rad' => 'Radio-source', 62 'Q?' => 'Possible Quasar', 63 'IR' => 'Infra-Red source', 64 'SB*' => 'Spectrocopic binary', 65 'C*' => 'Carbon Star', 66 'Gl?' => 'Possible Globular Cluster', 67 'DNe' => 'Dark Nebula', 68 'GlC' => 'Globular Cluster', 69 'No*' => 'Nova', 70 'V*?' => 'Star suspected of Variability', 71 'LeG' => 'Gravitationnaly Lensed Image of a Galaxy', 72 'mAL' => 'metallic Absorption Line system', 73 'LeI' => 'Gravitationnaly Lensed Image', 74 'WU*' => 'Eclipsing binary of W UMa type', 75 'Be*' => 'Be Star', 76 'PaG' => 'Pair of Galaxies', 77 'Mas' => 'Maser', 78 'LeQ' => 'Gravitationnaly Lensed Image of a Quasar', 79 'mul' => 'Composite object', 80 'SBG' => 'Starburst Galaxy', 81 '*' => 'Star', 82 'gam' => 'gamma-ray source', 83 'bL*' => 'Eclipsing binary of beta Lyr type', 84 'S*' => 'S Star', 85 'El*' => 'Elliptical variable Star', 86 'GNe' => 'Galactic Nebula', 87 'DQ*' => 'Cataclysmic Var. DQ Her type', 88 '?' => 'Object of unknown nature', 89 'WV*' => 'Variable Star of W Vir type', 90 'SR?' => 'SuperNova Remnant Candidate', 91 'Bla' => 'Blazar', 92 'G' => 'Galaxy', 93 'SCG' => 'Supercluster of Galaxies', 94 'OH*' => 'Star with envelope of OH/IR type', 95 'Lev' => '(Micro)Lensing Event', 96 'BNe' => 'Bright Nebula', 97 'RV*' => 'Variable Star of RV Tau type', 98 'IR0' => 'IR source at lambda < 10 microns', 99 'OVV' => 'Optically Violently Variable object', 100 'a2*' => 'Variable Star of alpha2 CVn type', 101 'IR1' => 'IR source at lambda > 10 microns', 102 'Em*' => 'Emission-line Star', 103 'PM*' => 'High proper-motion Star', 104 'X' => 'X-ray source', 105 'HzG' => 'Galaxy with high redshift', 106 'Sy*' => 'Symbiotic Star', 107 'LXB' => 'Low Mass X-ray Binary', 108 '*i*' => 'Star in double system', 109 'Sy1' => 'Seyfert 1 Galaxy', 110 'Sy2' => 'Seyfert 2 Galaxy', 111 'LIN' => 'LINER-type Active Galaxy Nucleus', 112 'rG' => 'Radio Galaxy', 113 'Cl*' => 'Cluster of Stars', 114 'NL*' => 'Nova-like Star', 115 'HV*' => 'High-velocity Star', 116 'EmG' => 'Emission-line galaxy', 117 '*iA' => 'Star in Association', 118 'grv' => 'Gravitational Source', 119 '*iC' => 'Star in Cluster', 120 'SyG' => 'Seyfert Galaxy', 121 'RNe' => 'Reflection Nebula', 122 'EmO' => 'Emission Object', 123 'Ce*' => 'Classical Cepheid variable Star', 124 'CV*' => 'Cataclysmic Variable Star', 125 '*iN' => 'Star in Nebula', 126 'BY*' => 'Variable of BY Dra type', 127 'Pe*' => 'Peculiar Star', 128 'AM*' => 'Cataclysmic Var. AM Her type', 129 'FU*' => 'Variable Star of FU Ori type', 130 'HVC' => 'High-velocity Cloud', 131 'ClG' => 'Cluster of Galaxies', 132 'Ir*' => 'Variable Star of irregular type', 133 'PN?' => 'Possible Planetary Nebula', 134 'ALS' => 'Absorption Line system', 135 'cm' => 'centimetric Radio-source', 136 'As*' => 'Association of Stars', 137 'V*' => 'Variable Star', 138 'Fl*' => 'Flare Star', 139 'EB*' => 'Eclipsing binary', 140 'CGG' => 'Compact Group of Galaxies', 141 'UV' => 'UV-emission source', 142 'Ro*' => 'Rotationally variable Star', 143 'SN*' => 'SuperNova', 144 'pr*' => 'Pre-main sequence Star', 145 'CH*' => 'Star with envelope of CH type', 146 'Al*' => 'Eclipsing binary of Algol type', 147 'Pu*' => 'Pulsating variable Star', 148 'Cld' => 'Cloud of unknown nature', 149 'QSO' => 'Quasar', 150 'Psr' => 'Pulsars', 151 'GiC' => 'Galaxy in Cluster of Galaxies', 152 'V* RI*' => 'Variable Star with rapid variations', 153 'sh' => 'HI shell', 154 'GiG' => 'Galaxy in Group of Galaxies', 155 'OpC' => 'Open (galactic) Cluster', 156 'WR*' => 'Wolf-Rayet Star', 157 'BCG' => 'Blue compact Galaxy', 158 'blu' => 'Blue object', 159 'GiP' => 'Galaxy in Pair of Galaxies', 160 'LyA' => 'Ly alpha Absorption Line system', 161 'CGb' => 'Cometary Globule', 162 '**' => 'Double or multiple star', 163 'H2G' => 'HII Galaxy', 164 'RR*' => 'Variable Star of RR Lyr type', 165 'HB*' => 'Horizontal Branch Star', 166 'RC*' => 'Variable Star of R CrB type', 167 'SNR' => 'SuperNova Remnant', 168 'MoC' => 'Molecular Cloud', 169 'HXB' => 'High Mass X-ray Binary', 170 'mR' => 'metric Radio-source', 171 'TT*' => 'T Tau-type Star', 172 'DN*' => 'Dwarf Nova', 173 'eg sr*' => 'Semi-regular pulsating Star', 174 'HII' => 'HII (ionized) region', 175 'HH' => 'Herbig-Haro Object', 176 'HI' => 'HI (neutral) region', 177 'WD*' => 'White Dwarf', 178 'Or*' => 'Variable Star in Orion Nebula', 179 'dS*' => 'Variable Star of delta Sct type', 180 'DLy' => 'Dumped Ly alpha Absorption Line system', 181 'AGN' => 'Active Galaxy Nucleus', 182 'GrG' => 'Group of Galaxies', 183 'Mi*' => 'Variable Star of Mira Cet type', 184 'RS*' => 'Variable of RS CVn type', 185 'mm' => 'millimetric Radio-source', 186 'red' => 'Very red source', 187 'BLL' => 'BL Lac - type object', 188 'reg' => 'Region defined in the sky', 189 'PN' => 'Planetary Nebula', 190 'ZZ*' => 'Variable White Dwarf of ZZ Cet type', 191 'gB' => 'gamma-ray Burster', 192 'PoC' => 'Part of Cloud', 193 'XB*' => 'X-ray Binary', 194 'PoG' => 'Part of a Galaxy', 195 'Neb' => 'Nebula of unknown nature' 196); 197 198=head1 METHODS 199 200=head2 Constructor 201 202=over 4 203 204=item B<new> 205 206Create a new instance from a hash of options 207 208 $star = new Astro::Catalog::Item( 209 ID => $id, 210 Coords => new Astro::Coords(), 211 Morphology => new Astro::Catalog::Item::Morphology(), 212 Fluxes => new Astro::Fluxes(), 213 Quality => $quality_flag, 214 Field => $field, 215 GSC => $in_gsc, 216 Distance => $distance_to_centre, 217 PosAngle => $position_angle, 218 X => $x_pixel_coord, 219 Y => $y_pixel_coord, 220 Comment => $comment_string 221 SpecType => $spectral_type, 222 StarType => $star_type, 223 LongStarType => $long_star_type, 224 MoreInfo => $url, 225 InsertDate => new Time::Piece(), 226 Misc => $misc, 227 ); 228 229returns a reference to an Astro::Catalog::Item object. 230 231The coordinates can also be specified as individual RA and Dec values 232(sexagesimal format) if they are known to be J2000. 233 234=cut 235 236sub new { 237 my $proto = shift; 238 my $class = ref($proto) || $proto; 239 240 # bless the query hash into the class 241 my $block = bless { 242 ID => undef, 243 FLUXES => undef, 244 MORPHOLOGY => undef, 245 QUALITY => undef, 246 FIELD => undef, 247 GSC => undef, 248 DISTANCE => undef, 249 POSANGLE => undef, 250 COORDS => undef, 251 X => undef, 252 Y => undef, 253 WCS => undef, 254 COMMENT => undef, 255 SPECTYPE => undef, 256 STARTYPE => undef, 257 LONGTYPE => undef, 258 MOREINFO => undef, 259 INSERTDATE => undef, 260 PREFERRED_MAG_TYPE => undef, 261 MISC => undef, 262 }, $class; 263 264 # If we have arguments configure the object 265 $block->configure( @_ ) if @_; 266 267 return $block; 268} 269 270=back 271 272=head2 Accessor Methods 273 274=over 4 275 276=item B<id> 277 278Return (or set) the ID of the star 279 280 $id = $star->id(); 281 $star->id( $id ); 282 283If an Astro::Coords object is associated with the Star, the name 284field is set in the underlying Astro::Coords object as well as in 285the current Star object. 286 287=cut 288 289sub id { 290 my $self = shift; 291 if (@_) { 292 $self->{ID} = shift; 293 294 my $c = $self->coords; 295 $c->name($self->{ID}) if defined $c; 296 } 297 return $self->{ID}; 298} 299 300=item B<coords> 301 302Return or set the coordinates of the star as an C<Astro::Coords> 303object. 304 305 $c = $star->coords(); 306 $star->coords($c); 307 308The object returned by this method is the actual object stored 309inside this Star object and not a clone. If the coordinates 310are changed through this object the coordinate of the star is 311also changed. 312 313Currently, if you modify the RA or Dec through the ra() 314or dec() methods of Star, the internal object associated with 315the Star will change. 316 317Returns undef if the coordinates have never been specified. 318 319If the name() field is defined in the Astro::Coords object 320the id() field is set in the current Star object. Similarly for 321the comment field. 322 323=cut 324 325sub coords { 326 my $self = shift; 327 if (@_) { 328 my $c = shift; 329 croak "Coordinates must be an Astro::Coords object" 330 unless UNIVERSAL::isa($c, "Astro::Coords"); 331 332 # force the ID and comment to match 333 $self->id($c->name) if defined $c->name; 334 $self->comment($c->comment) if $c->comment; 335 336 # Store the new coordinate object 337 # Storing it late stops looping from the id and comment methods 338 $self->{COORDS} = $c; 339 } 340 return $self->{COORDS}; 341} 342 343=item B<ra> 344 345Return (or set) the current object R.A. (J2000). 346 347 $ra = $star->ra(); 348 349If the Star is associated with a moving object such as a planet, 350comet or asteroid this method will return the J2000 RA associated 351with the time and observer position associated with the coordinate 352object itself (by default current time, longitude of 0 degrees). 353Returns undef if no coordinate has been associated with this star. 354 355 $star->ra($ra); 356 357The RA can be changed using this method but only if the coordinate 358object is associated with a fixed position. Attempting to change the 359J2000 RA of a moving object will fail. If an attempt is made to 360change the RA when no coordinate is associated with this object then 361a new Astro::Coords object will be created (with a 362Dec of 0.0). 363 364RA accepted by this method must be in sexagesimal format, space or 365colon-separated. Returns a space-separated sexagesimal number. 366 367 368=cut 369 370sub ra { 371 my $self = shift; 372 if (@_) { 373 my $ra = shift; 374 375 # Issue a warning specifically for this call 376 my @info = caller(); 377 warnings::warnif("deprecated","Use of ra() method for setting RA now deprecated. Please use the coords() method instead, at $info[1] line $info[2]"); 378 379 380 # Get the coordinate object 381 my $c = $self->coords; 382 if (defined $c) { 383 # Need to tweak RA? 384 croak "Can only adjust RA with Astro::Coords::Equatorial coordinates" 385 unless $c->isa("Astro::Coords::Equatorial"); 386 387 # For now need to kluge since Astro::Coords does not allow 388 # you to change the position (it is an immutable object) 389 $c = $c->new( 390 type => 'J2000', 391 dec => $c->dec(format => 's'), 392 ra => $ra, 393 ); 394 395 } 396 else { 397 $c = new Astro::Coords( 398 type => 'J2000', 399 ra => $ra, 400 dec => '0', 401 ); 402 } 403 404 # Update the object 405 $self->coords($c); 406 } 407 408 my $outc = $self->coords; 409 return unless defined $outc; 410 411 # Astro::Coords inserts colons by default. Grab the old delimiter 412 # and number of decimal places if we're using a recent enough 413 # version of Astro::Coords. 414 my $ra = $outc->ra; 415 if (UNIVERSAL::isa($ra, "Astro::Coords::Angle")) { 416 $ra->str_delim(' '); 417 $ra->str_ndp(2); 418 return "$ra"; 419 } 420 else { 421 my $outra = $outc->ra(format => 's'); 422 $outra =~ s/:/ /g; 423 $outra =~ s/^\s*//; 424 425 return $outra; 426 } 427} 428 429=item B<dec> 430 431Return (or set) the current object Dec (J2000). 432 433 $dec = $star->dec(); 434 435If the Star is associated with a moving object such as a planet, 436comet or asteroid this method will return the J2000 Dec associated 437with the time and observer position associated with the coordinate 438object itself (by default current time, longitude of 0 degrees). 439Returns undef if no coordinate has been associated with this star. 440 441 $star->dec( $dec ); 442 443The Dec can be changed using this method but only if the coordinate 444object is associated with a fixed position. Attempting to change the 445J2000 Dec of a moving object will fail. If an attempt is made to 446change the Dec when no coordinate is associated with this object then 447a new Astro::Coords object will be created (with a 448Dec of 0.0). 449 450Dec accepted by this method must be in sexagesimal format, space or 451colon-separated. Returns a space-separated sexagesimal number 452with a leading sign. 453 454=cut 455 456sub dec { 457 my $self = shift; 458 if (@_) { 459 my $dec = shift; 460 461 # Issue a warning specifically for this call 462 my @info = caller(); 463 warnings::warnif("deprecated","Use of ra() method for setting RA now deprecated. Please use the coords() method instead, at $info[1] line $info[2]"); 464 465 # Get the coordinate object 466 my $c = $self->coords; 467 if (defined $c) { 468 # Need to tweak RA? 469 croak "Can only adjust Dec with Astro::Coords::Equatorial coordinates" 470 unless $c->isa("Astro::Coords::Equatorial"); 471 472 # For now need to kluge since Astro::Coords does not allow 473 # you to change the position (it is an immutable object) 474 $c = $c->new( 475 type => 'J2000', 476 ra => $c->ra(format => 's'), 477 dec => $dec, 478 ); 479 480 } 481 else { 482 $c = new Astro::Coords( 483 type => 'J2000', 484 dec => $dec, 485 ra => 0, 486 ); 487 } 488 489 # Update the object 490 $self->coords($c); 491 } 492 493 my $outc = $self->coords; 494 return unless defined $outc; 495 496 # Astro::Coords inserts colons by default. Grab the old delimiter 497 # and number of decimal places if we're using a recent enough 498 # version of Astro::Coords. 499 my $dec = $outc->dec; 500 if (UNIVERSAL::isa($dec, "Astro::Catalog::Angle")) { 501 $dec->str_delim(' '); 502 $dec->str_ndp(2); 503 $dec = "$dec"; 504 $dec = (substr($dec, 0, 1) eq '-' ? '' : '+') . $dec; 505 return $dec; 506 } 507 else { 508 my $outdec = $outc->dec(format => 's'); 509 $outdec =~ s/:/ /g; 510 $outdec =~ s/^\s*//; 511 512 # require leading sign for backwards compatibility 513 # Sign will be there for negative 514 $outdec = (substr($outdec, 0, 1) eq '-' ? '' : '+') . $outdec; 515 516 return $outdec; 517 } 518} 519 520=item B<fluxes> 521 522Return or set the flux measurements of the star as an C<Astro::Fluxes> 523object. 524 525 $f = $star->fluxes(); 526 $star->fluxes($f); 527 528 $star->fluxes($f, 1); # will replace instead of appending 529 530 531The object returned by this method is the actual object stored 532inside this Item object and not a clone. If the flux values 533are changed through this object the flu values of the star is 534also changed. 535 536If an optional flag is passed as set to the routine it will replace 537instead of appending (default action) to an existing fluxes object 538in the catalogue. 539 540Returns undef if the fluxes have never been specified. 541 542=cut 543 544sub fluxes { 545 my $self = shift; 546 if (@_) { 547 my $flux = shift; 548 my $flag = shift; 549 croak "Flux must be an Astro::Fluxes object" 550 unless UNIVERSAL::isa($flux, "Astro::Fluxes"); 551 552 if (defined $self->{FLUXES}) { 553 if (defined $flag) { 554 $self->{FLUXES} = $flux; 555 } 556 else { 557 $self->{FLUXES}->merge( $flux ); 558 } 559 } 560 else { 561 $self->{FLUXES} = $flux; 562 } 563 } 564 return $self->{FLUXES}; 565} 566 567=item B<what_filters> 568 569Returns a list of the wavebands for which the object has defined values. 570 571 @filters = $star->what_filters(); 572 $num = $star->what_filters(); 573 574if called in a scalar context it will return the number of filters which 575have defined magnitudes in the object. It will included 'derived' values, 576see C<Astro::Flux> for details. 577 578=cut 579 580sub what_filters { 581 my $self = shift; 582 583 my $fluxes = $self->{FLUXES}; 584 585 my @mags = $fluxes->original_wavebands('filters') if defined $fluxes; 586 587 # return array of filters or number if called in scalar context 588 return wantarray ? @mags : scalar(@mags); 589} 590 591=item B<what_colours> 592 593Returns a list of the colours for which the object has defined values. 594 595 @colours = $star->what_colours(); 596 $num = $star->what_colours(); 597 598if called in a scalar context it will return the number of colours which 599have defined values in the object. 600 601=cut 602 603sub what_colours { 604 my $self = shift; 605 606 my $fluxes = $self->{FLUXES}; 607 my @cols = $fluxes->original_colors() if defined $fluxes; 608 609 # return array of colours or number if called in scalar context 610 return wantarray ? @cols : scalar(@cols); 611} 612 613=item B<get_magnitude> 614 615Returns the magnitude for the supplied filter if available 616 617 $magnitude = $star->get_magnitude('B'); 618 619=cut 620 621sub get_magnitude { 622 my $self = shift; 623 624 my $magnitude; 625 if (@_) { 626 # grab passed filter 627 my $filter = shift; 628 my $fluxes = $self->{FLUXES}; 629 $magnitude = $fluxes->flux( 630 waveband => $filter, 631 type => $self->preferred_magnitude_type); 632 633 if (defined($magnitude)) { 634 return $magnitude->quantity($self->preferred_magnitude_type); 635 } 636 else { 637 return undef; 638 } 639 } 640} 641 642=item B<get_flux_quantity> 643 644Returns the flux quantity for the given waveband and flux type. 645 646 my $flux = $star->get_flux_quantity( 647 waveband => 'B', 648 type => 'mag'); 649 650The arguments are passed as a hash. The value for the waveband 651argument can be either a string describing a filter or an 652Astro::WaveBand object. The value for the flux type is 653case-insensitive. 654 655Returns a scalar. 656 657=cut 658 659sub get_flux_quantity { 660 my $self = shift; 661 my %args = @_; 662 663 unless (defined $args{'waveband'}) { 664 croak "Must supply waveband to Astro::Catalog::Item->get_flux_quantity()"; 665 } 666 unless (defined $args{'type'}) { 667 croak "Must supply flux type to Astro::Catalog::Item->get_flux_quantity()"; 668 } 669 670 my $waveband; 671 unless (UNIVERSAL::isa($args{'waveband'}, "Astro::WaveBand")) { 672 $waveband = new Astro::WaveBand(Filter => $args{'waveband'}); 673 } 674 else { 675 $waveband = $args{'waveband'}; 676 } 677 678 my $fluxes = $self->fluxes; 679 if (defined $fluxes) { 680 my $flux = $fluxes->flux(waveband => $waveband, type => $args{'type'}); 681 if (defined $flux) { 682 return $flux->quantity($args{'type'} ); 683 } 684 } 685 return undef; 686} 687 688=item B<get_errors> 689 690Returns the error in the magnitude value for the supplied filter if available 691 692 $mag_errors = $star->get_errors('B'); 693 694=cut 695 696sub get_errors { 697 my $self = shift; 698 699 my $mag_error; 700 if (@_) { 701 # grab passed filter 702 my $filter = shift; 703 my $fluxes = $self->{FLUXES}; 704 my $magnitude = $fluxes->flux( 705 waveband => $filter, 706 type => $self->preferred_magnitude_type); 707 if (defined $magnitude) { 708 return $magnitude->error( $self->preferred_magnitude_type ); 709 } 710 else { 711 return undef; 712 } 713 } 714 return $mag_error; 715} 716 717=item B<get_flux_error> 718 719Returns the flux error for the given waveband and flux type. 720 721 my $flux = $star->get_flux_error( 722 waveband => 'B', 723 type => 'mag'); 724 725The arguments are passed as a hash. The value for the waveband 726argument can be either a string describing a filter or an 727Astro::WaveBand object. The value for the flux type is 728case-insensitive. 729 730Returns a scalar. 731 732=cut 733 734sub get_flux_error { 735 my $self = shift; 736 my %args = @_; 737 738 unless (defined $args{'waveband'}) { 739 croak "Must supply waveband to Astro::Catalog::Item->get_flux_error()"; 740 } 741 unless(defined $args{'type'}) { 742 croak "Must supply flux type to Astro::Catalog::Item->get_flux_error()"; 743 } 744 745 my $waveband; 746 unless (UNIVERSAL::isa($args{'waveband'}, "Astro::WaveBand")) { 747 $waveband = new Astro::WaveBand(Filter => $args{'waveband'}); 748 } 749 else { 750 $waveband = $args{'waveband'}; 751 } 752 my $fluxes = $self->fluxes; 753 if (defined $fluxes) { 754 my $flux = $fluxes->flux(waveband => $waveband, type => $args{'type'}); 755 if (defined $flux) { 756 return $flux->error($args{'type'}); 757 } 758 } 759 return undef; 760} 761 762=item B<get_colour> 763 764Returns the value of the supplied colour if available 765 766 $colour = $star->get_colour('B-V'); 767 768=cut 769 770sub get_colour { 771 my $self = shift; 772 773 my $value; 774 if (@_) { 775 # grab passed colour 776 my $colour = shift; 777 my @filters = split "-", $colour; 778 my $fluxes = $self->{FLUXES}; 779 my $color = $fluxes->color( 780 upper => new Astro::WaveBand(Filter => $filters[0]), 781 lower => new Astro::WaveBand(Filter => $filters[1])); 782 $value = $color->quantity('mag'); 783 } 784 return $value; 785} 786 787=item B<get_colourerror> 788 789Returns the error in the colour value for the supplied colour if available 790 791 $col_errors = $star->get_colourerr('B-V'); 792 793=cut 794 795sub get_colourerr { 796 my $self = shift; 797 798 my $col_error; 799 if (@_) { 800 # grab passed colour 801 my $colour = shift; 802 my @filters = split "-", $colour; 803 my $fluxes = $self->{FLUXES}; 804 my $color = $fluxes->color( 805 upper => new Astro::WaveBand(Filter => $filters[0]), 806 lower => new Astro::WaveBand(Filter => $filters[1])); 807 808 $col_error = $color->error('mag'); 809 } 810 return $col_error; 811} 812 813=item B<preferred_magnitude_type> 814 815Get or set the preferred magnitude type to be returned from the get_magnitude method. 816 817 my $type = $item->preferred_magnitude_type; 818 $item->preferred_magnitude_type('MAG_ISO'); 819 820Defaults to 'MAG'. 821 822=cut 823 824sub preferred_magnitude_type { 825 my $self = shift; 826 if (@_) { 827 my $type = shift; 828 $self->{PREFERRED_MAG_TYPE} = $type; 829 } 830 831 unless (defined $self->{PREFERRED_MAG_TYPE}) { 832 $self->{PREFERRED_MAG_TYPE} = 'MAG'; 833 } 834 835 return $self->{PREFERRED_MAG_TYPE}; 836} 837 838=item B<morphology> 839 840Get or set the morphology of the star as an C<Astro::Catalog::Item::Morphology> 841object. 842 843 $star->morphology($morphology); 844 845The object returned by this method is the actual object stored 846inside this Star object and not a clone. If the morphology 847is changed through this object the morphology of the star is 848also changed. 849 850=cut 851 852sub morphology { 853 my $self = shift; 854 if (@_) { 855 my $m = shift; 856 croak "Morphology must be an Astro::Catalog::Item::Morphology object" 857 unless UNIVERSAL::isa($m, "Astro::Catalog::Item::Morphology"); 858 859 # Store the new coordinate object 860 # Storing it late stops looping from the id and comment methods 861 $self->{MORPHOLOGY} = $m; 862 } 863 return $self->{MORPHOLOGY}; 864} 865 866=item B<quality> 867 868Return (or set) the quality flag of the star 869 870 $quality = $star->quailty(); 871 $star->quality(0); 872 873for example for the USNO-A2 catalogue, 0 denotes good quality, and 1 874denotes a possible problem object. In the generic case any flag value, 875including a boolean, could be used. 876 877These quality flags are standardised sybolically across catalogues and 878have the following definitions: 879 880 STARGOOD 881 STARBAD 882 883TBD. Need to provide quality constants and mapping to and from these 884constants on catalog I/O. 885 886=cut 887 888sub quality { 889 my $self = shift; 890 if (@_) { 891 892 # 2MASS hack 893 # ---------- 894 # quick, dirty and ultimately icky hack. The entire quality flag 895 # code is going to have to be rewritten so it works like mag errors, 896 # and gets assocaited with a magnitude. For now, if the JHK QFlag 897 # for 2MASS is A,B or C then the internal quality flag is 0 (good), 898 # otherwise it gets set to 1 (bad). This pretty much sucks. 899 900 # Yes Tim, I know I'm doing this in the wrong place. I'm panicing 901 # I'll fix it later. I've moved the Cluster specific hack about the 902 # star ID's out of Astro::Catalog::query::USNOA2 and into the Cluster 903 # IO module and used Scalar::Util to figure out whether I've got a 904 # number (neat solution) before blowing it away. 905 906 # Anyway... 907 my $quality = shift; 908 909 # Shouldn't happen? 910 unless (defined $quality) { 911 $self->{QUALITY} = undef; 912 return undef; 913 } 914 915 if ($quality =~ /^[A-Z][A-Z][A-Z]$/) { 916 $_ = $quality; 917 m/^([A-Z])([A-Z])([A-Z])$/; 918 919 my $j_quality = $1; 920 my $h_quality = $2; 921 my $k_quality = $3; 922 923 if (($j_quality eq 'A' || $j_quality eq 'B' || $j_quality eq 'C') && 924 ($h_quality eq 'A' || $h_quality eq 'B' || $h_quality eq 'C')) { 925 # good quality 926 $self->{QUALITY} = 0; 927 } 928 else { 929 # bad quality 930 $self->{QUALITY} = 1; 931 } 932 } 933 else { 934 $self->{QUALITY} = $quality; 935 } 936 937 } 938 return $self->{QUALITY}; 939} 940 941=item B<field> 942 943Return (or set) the field parameter for the star 944 945 $field = $star->field(); 946 $star->field('0080'); 947 948=cut 949 950sub field { 951 my $self = shift; 952 if (@_) { 953 $self->{FIELD} = shift; 954 } 955 return $self->{FIELD}; 956} 957 958=item B<gsc> 959 960Return (or set) the GSC flag for the object 961 962 $gsc = $star->gsc(); 963 $star->gsc( 'TRUE' ); 964 965the flag is TRUE if the object is known to be in the Guide Star Catalogue, 966and FALSE otherwise. 967 968=cut 969 970sub gsc { 971 my $self = shift; 972 if (@_) { 973 $self->{GSC} = shift; 974 } 975 return $self->{GSC}; 976} 977 978=item B<distance> 979 980Return (or set) the distance from the field centre 981 982 $distance = $star->distance(); 983 $star->distance('0.009'); 984 985e.g. for the USNO-A2 catalogue. 986 987=cut 988 989sub distance { 990 my $self = shift; 991 if (@_) { 992 $self->{DISTANCE} = shift; 993 } 994 return $self->{DISTANCE}; 995} 996 997=item B<posangle> 998 999Return (or set) the position angle from the field centre 1000 1001 $position_angle = $star->posangle(); 1002 $star->posangle('50.761'); 1003 1004e.g. for the USNO-A2 catalogue. 1005 1006=cut 1007 1008sub posangle { 1009 my $self = shift; 1010 if (@_) { 1011 $self->{POSANGLE} = shift; 1012 } 1013 return $self->{POSANGLE}; 1014} 1015 1016=item B<x> 1017 1018Return (or set) the X pixel co-ordinate of the star 1019 1020 $x = $star->x(); 1021 $star->id($x); 1022 1023=cut 1024 1025sub x { 1026 my $self = shift; 1027 if (@_) { 1028 $self->{X} = shift; 1029 } 1030 1031 if (! defined($self->{X}) && 1032 defined($self->wcs) && 1033 defined($self->coords)) { 1034 1035 # We need to get a template FK5 SkyFrame to be able to convert 1036 # properly between RA/Dec and X/Y, but we can only do this if 1037 # we load Starlink::AST. So that we don't have a major dependency 1038 # on that module, load it here at runtime. 1039 eval {require Starlink::AST;}; 1040 if ($@) { 1041 croak "Attempted to convert from RA/Dec to X position and cannot load Starlink::AST. Error: $@"; 1042 } 1043 my $template = new Starlink::AST::SkyFrame("System=FK5"); 1044 my $wcs = $self->wcs; 1045 my $frameset = $wcs->FindFrame($template, ""); 1046 unless (defined $frameset) { 1047 croak "Could not find FK5 SkyFrame to do RA/Dec to X position translation"; 1048 } 1049 my ($ra, $dec) = $self->coords->radec(); 1050 my ($x, $y) = $frameset->Tran2( 1051 [$ra->radians], 1052 [$dec->radians], 1053 0); 1054 $self->{X} = $x->[0]; 1055 } 1056 return $self->{X}; 1057} 1058 1059=item B<y> 1060 1061Return (or set) the Y pixel co-ordinate of the star 1062 1063 $y = $star->y(); 1064 $star->id($y); 1065 1066=cut 1067 1068sub y { 1069 my $self = shift; 1070 if (@_) { 1071 $self->{Y} = shift; 1072 } 1073 1074 if (! defined($self->{Y}) && 1075 defined($self->wcs) && 1076 defined($self->coords)) { 1077 1078 # We need to get a template FK5 SkyFrame to be able to convert 1079 # properly between RA/Dec and X/Y, but we can only do this if 1080 # we load Starlink::AST. So that we don't have a major dependency 1081 # on that module, load it here at runtime. 1082 eval {require Starlink::AST;}; 1083 if ($@) { 1084 croak "Attempted to convert from RA/Dec to Y position and cannot load Starlink::AST. Error: $@"; 1085 } 1086 my $template = new Starlink::AST::SkyFrame("System=FK5"); 1087 my $wcs = $self->wcs; 1088 my $frameset = $wcs->FindFrame($template, ""); 1089 unless (defined $frameset) { 1090 croak "Could not find FK5 SkyFrame to do RA/Dec to Y position translation"; 1091 } 1092 my ($ra, $dec) = $self->coords->radec(); 1093 my ($x, $y) = $frameset->Tran2( 1094 [$ra->radians], 1095 [$dec->radians], 1096 0); 1097 $self->{Y} = $y->[0]; 1098 } 1099 1100 return $self->{Y}; 1101} 1102 1103=item B<wcs> 1104 1105Return (or set) the WCS associated with the star. 1106 1107 $wcs = $star->wcs; 1108 $star->wcs($wcs); 1109 1110The WCS is a C<Starlink::AST> object. 1111 1112=cut 1113 1114sub wcs { 1115 my $self = shift; 1116 if (@_) { 1117 my $wcs = shift; 1118 unless (defined $wcs) { 1119 $self->{WCS} = undef; 1120 } 1121 elsif (UNIVERSAL::isa($wcs, "Starlink::AST")) { 1122 $self->{WCS} = $wcs; 1123 } 1124 } 1125 return $self->{WCS}; 1126} 1127 1128=item B<comment> 1129 1130Return (or set) a comment associated with the star 1131 1132 $comment = $star->comment(); 1133 $star->comment($comment_string); 1134 1135The comment is propogated to the underlying coordinate 1136object (if one is present) if the comment is updated. 1137 1138=cut 1139 1140sub comment { 1141 my $self = shift; 1142 if (@_) { 1143 $self->{COMMENT} = shift; 1144 1145 my $c = $self->coords; 1146 $c->comment($self->{COMMENT}) if defined $c; 1147 } 1148 return $self->{COMMENT}; 1149} 1150 1151=item B<spectype> 1152 1153The spectral type of the Star. 1154 1155 $spec = $star->spectype; 1156 1157=cut 1158 1159sub spectype { 1160 my $self = shift; 1161 if (@_) { 1162 $self->{SPECTYPE} = shift; 1163 } 1164 return $self->{SPECTYPE}; 1165} 1166 1167=item B<startype> 1168 1169The type of star. Usually uses the Simbad abbreviation. 1170eg. '*' for a star, 'rG' for a Radio Galaxy. 1171 1172 $type = $star->startype; 1173 1174See also C<longstartype> for the expanded version of this type. 1175 1176=cut 1177 1178sub startype { 1179 my $self = shift; 1180 if (@_) { 1181 $self->{STARTYPE} = shift; 1182 } 1183 return $self->{STARTYPE}; 1184} 1185 1186=item B<longstartype> 1187 1188The full description of the type of star. Usually uses the Simbad text. 1189If no text has been provided, a lookup will be performed using the 1190abbreviated C<startype>. 1191 1192 $long = $star->longstartype; 1193 $star->longstartype("A variable star"); 1194 1195See also C<longstartype> for the expanded version of this type. 1196 1197=cut 1198 1199sub longstartype { 1200 my $self = shift; 1201 if (@_) { 1202 $self->{LONGTYPE} = shift; 1203 } 1204 # if we have nothing, attempt a look up 1205 if (! defined $self->{LONGTYPE} && defined $self->startype 1206 && exists $STAR_TYPE_LOOKUP{$self->startype}) { 1207 return $STAR_TYPE_LOOKUP{$self->startype}; 1208 } 1209 else { 1210 return $self->{STARTYPE}; 1211 } 1212} 1213 1214=item B<moreinfo> 1215 1216A link (URL) to more information on the star in question. For example 1217this might provide a direct link to the full Simbad description. 1218 1219 $url = $star->moreinfo; 1220 1221=cut 1222 1223sub moreinfo { 1224 my $self = shift; 1225 if (@_) { 1226 $self->{MOREINFO} = shift; 1227 } 1228 return $self->{MOREINFO}; 1229} 1230 1231=item B<insertdate> 1232 1233The time the information for the star in question was gathered. This 1234is different from the time of observation of the star. 1235 1236 $insertdate = $star->insertdate; 1237 1238This is a C<Time::Piece> object. 1239 1240=cut 1241 1242sub insertdate { 1243 my $self = shift; 1244 if (@_) { 1245 $self->{INSERTDATE} = shift; 1246 } 1247 return $self->{INSERTDATE}; 1248} 1249 1250 1251=item B<fluxdatestamp> 1252 1253Apply a datestamp to all the C<Astro::Flux> objects inside the 1254C<Astro::Fluxes> object contained within this object 1255 1256 $star->fluxdatestamp(new DateTime()); 1257 1258this is different from the time for which the inormation about the 1259star was gathered, see the insertdate() method call, and is the 1260time of observation of the object. 1261 1262=cut 1263 1264sub fluxdatestamp { 1265 my $self = shift; 1266 if (@_) { 1267 my $datetime = shift; 1268 croak "Astro::Catalog::Item::fluxdatestamp()\n". 1269 "Error: Not a DateTime object\n" 1270 unless UNIVERSAL::isa($datetime, "DateTime"); 1271 $self->{FLUXES}->datestamp($datetime); 1272 } 1273 return $self->{FLUXES}; 1274} 1275 1276 1277=item B<distancetostar> 1278 1279The distance from another Item, 1280 1281 my $distance1 = $star->distancetostar($star2); 1282 1283returns a tangent plane separation value in arcsec. Returns undef if 1284the star is too far away. 1285 1286=cut 1287 1288sub distancetostar { 1289 my $self = shift; 1290 my $other = shift; 1291 1292 croak "Astro::Catalog::Item::distancetostar()\n". 1293 "Error: Not an Astro::Catalog::Item object\n" 1294 unless UNIVERSAL::isa($other, "Astro::Catalog::Item"); 1295 1296 my $sep = $self->coords->distance($other->coords); 1297 return (defined $sep ? $sep->arcsec : $sep); 1298} 1299 1300 1301=item B<within> 1302 1303Check if the passed star is within $distance_in_arcsec of the object. 1304 1305 my $status = $star->within($star2, $distance_in_arcsec); 1306 1307returns true if this is the case. 1308 1309=cut 1310 1311sub within { 1312 my $self = shift; 1313 my $other = shift; 1314 my $max = shift; 1315 1316 croak "Astro::Catalog::Item::within()\n". 1317 "Error: Not an Astro::Catalog::Item object\n" 1318 unless UNIVERSAL::isa( $other, "Astro::Catalog::Item" ); 1319 1320 my $distance = $self->distancetostar($other); 1321 return 1 if $distance < $max; 1322 return 0; 1323} 1324 1325 1326=item B<misc> 1327 1328A hold-all method to contain information not covered by other methods. 1329 1330 my $misc = $item->misc; 1331 $item->misc($misc); 1332 1333This accessor can hold any type of variable, although it is 1334recommended that a hash reference is used for easier lookups: 1335 1336 my $misc = $item->misc; 1337 my $vrad = $misc->{'vrad'}; 1338 my $vopt = $misc->{'vopt'} 1339 1340=cut 1341 1342sub misc { 1343 my $self = shift; 1344 if (@_) { 1345 $self->{'MISC'} = shift; 1346 } 1347 return $self->{'MISC'}; 1348} 1349 1350=back 1351 1352=head2 Obsolete Methods 1353 1354Several methods were made obsolete with the introduction of V4 of the 1355Astro::Catalog class. These were magnitudes(), magerr(), colours() and 1356colerr(). The functionality these supported is now part of the addfluxes() 1357method. 1358 1359=cut 1360 1361sub magnitudes { 1362 my $self = shift; 1363 croak "Astro::Catalog::Item::magnitudes()\n" . 1364 "This method is no longer supported, use fluxes() instead.\n"; 1365} 1366 1367sub magerr { 1368 my $self = shift; 1369 croak "Astro::Catalog::Item::magerr()\n" . 1370 "This method is no longer supported, use fluxes() instead.\n"; 1371} 1372 1373 1374sub colours { 1375 my $self = shift; 1376 croak "Astro::Catalog::Item::colours()\n" . 1377 "This method is no longer supported, use fluxes() instead.\n"; 1378 1379} 1380 1381sub colerr { 1382 my $self = shift; 1383 croak "Astro::Catalog::Item::colerr()\n" . 1384 "This method is no longer supported, use fluxes() instead.\n"; 1385 1386} 1387 1388=head2 General Methods 1389 1390=over 4 1391 1392=item B<configure> 1393 1394Configures the object from multiple pieces of information. 1395 1396 $star->configure(%options); 1397 1398Takes a hash as argument with the list of keywords. 1399The keys are not case-sensitive and map to accessor methods. 1400 1401Note that RA and Dec keys are allowed. The values can be supplied in either sexagesimal or decimal degrees. 1402 1403=cut 1404 1405sub configure { 1406 my $self = shift; 1407 1408 # return unless we have arguments 1409 return unless @_; 1410 1411 # grab the argument list 1412 my %args = @_; 1413 1414 # First check for duplicate keys (case insensitive) with different 1415 # values and store the unique lower-cased keys 1416 my %check; 1417 for my $key (keys %args) { 1418 my $lckey = lc($key); 1419 if (exists $check{$lckey} && $check{$lckey} ne $args{$key}) { 1420 warnings::warnif("Duplicated key in constructor [$lckey] with differing values ". 1421 " '$check{$lckey}' and '$args{$key}'\n"); 1422 } 1423 $check{$lckey} = $args{$key}; 1424 } 1425 1426 # Now that we have lower cased keys we can look to see if we have 1427 # ra & dec as well as coords and also verify that they are actually 1428 # the same if we have them 1429 if (exists $check{coords} && (exists $check{ra} || exists $check{dec})) { 1430 # coords + one of ra or dec is a mistake 1431 if (exists $check{ra} && exists $check{dec}) { 1432 # Create a new coords object - assume J2000 1433 my $c = new Astro::Coords( 1434 type => 'J2000', 1435 ra => $check{ra}, 1436 dec => $check{dec}, 1437 # units => 'sex', 1438 ); 1439 1440 # Make sure we have the same reference place and time 1441 $c->datetime($check{coords}->datetime) 1442 if $check{coords}->has_datetime; 1443 $c->telescope($check{coords}->telescope) 1444 if defined $check{coords}->telescope; 1445 1446 1447 # Check the distance 1448 my $d = $c->distance($check{coords}); 1449 1450 # Raise warn if the error is more than 1 arcsecond 1451 warnings::warnif( "Coords and RA/Dec were specified and they differ by more than 1 arcsec [". 1452 (defined $d ? $d->arcsec : "<undef>") 1453 ." sec]. Ignoring RA/Dec keys.\n") 1454 if (!defined $d || $d->arcsec > 1.0); 1455 1456 } 1457 elsif (! exists $check{ra}) { 1458 warnings::warnif("Dec specified in addition to Coords but without RA. Ignoring it."); 1459 } 1460 elsif (! exists $check{dec}) { 1461 warnings::warnif("RA specified in addition to Coords but without Dec. Ignoring it."); 1462 } 1463 1464 # Whatever happens we do not want ra and dec here 1465 delete $check{dec}; 1466 delete $check{ra}; 1467 } 1468 elsif (exists $check{ra} || $check{dec}) { 1469 # Generate a Astro::Coords object here in one go rather than 1470 # relying on the old ra() dec() methods individually 1471 my $ra = $check{ra} || 0.0; 1472 my $dec = $check{dec} || 0.0; 1473 $check{coords} = new Astro::Coords( 1474 type => 'J2000', 1475 ra => $ra, 1476 dec => $dec); 1477 delete $check{ra}; 1478 delete $check{dec}; 1479 } 1480 1481 # Loop over the allowed keys storing the values 1482 # in the object if they exist. Case insensitive. 1483 for my $key (keys %check) { 1484 my $method = lc($key); 1485 $self->$method($check{$key}) if $self->can($method); 1486 } 1487 return; 1488} 1489 14901; 1491 1492__END__ 1493 1494=back 1495 1496=head1 COPYRIGHT 1497 1498Copyright (C) 2001-2002 University of Exeter. All Rights Reserved. 1499Some modification are Copyright (C) 2003 Particle Physics and 1500Astronomy Research Council. All Rights Reserved. 1501 1502This program was written as part of the eSTAR project and is free software; 1503you can redistribute it and/or modify it under the terms of the GNU Public 1504License. 1505 1506=head1 AUTHORS 1507 1508Alasdair Allan E<lt>aa@astro.ex.ac.ukE<gt>, 1509Tim Jenness E<lt>tjenness@cpan.orgE<gt>, 1510 1511=cut 1512