Astro-Coords

 view release on metacpan or  search on metacpan

lib/Astro/Coords/Angle.pm  view on Meta::CPAN

{
  my $DEFAULT_DELIM = ":";
  my $DELIM = $DEFAULT_DELIM;
  sub DELIM {
    my $class = shift;
    if (@_) {
      my $arg = shift;
      if (defined $arg) {
        $DELIM = $arg;
      } else {
        $DELIM = $DEFAULT_DELIM;
      }
    }
    return $DELIM;
  }
}

=item B<to_radians>

Low level utility routine to convert an input value in specified format
to radians. This method uses the same code as the object constructor to parse
the supplied input argument but does not require the overhead of object
construction if the result is only to be used transiently.

  $rad = Astro::Coords::Angle->to_radians( $string, $format );

See the constructor documentation for the supported format strings.

=cut

sub to_radians {
  my $class = shift;
  # simply delegate to the internal routine. Could use it directly but it feels
  # better to leave options open for the moment
  $class->_cvt_torad( @_ );
}

=back

=begin __PRIVATE_METHODS__

=head2 Private Methods

These methods are not part of the API and should not be called directly.
They are documented for completeness.

=over 4

=item B<_cvt_torad>

Internal class method to convert an input string to the equivalent value in
radians. The following units are supported:

 sexagesimal - A string of format "dd:mm:ss.ss", "dd mm ss.ss"
               or even "-ddxmmyss.ss" (ie -5x53y28.5z)
 degrees     - decimal degrees
 radians     - radians
 arcsec      - arc seconds (abbreviated form is 'as')
 arcmin      - arc minutes (abbreviated form is 'am')

If units are not supplied, default is to call the C<_guess_units>
method.

  $radians = $angle->_cvt_torad( $angle, $units );

Warnings are issued if the string can not be parsed or the values are
out of range.

If the supplied angle is an Angle object itself, units are ignored and
the value is extracted directly from the object.

Returns C<undef> on error. Does not modify the internal state of the object.

=cut

sub _cvt_torad {
  my $self = shift;
  my $input = shift;
  my $units = shift;

  return undef unless defined $input;

  # do we have an object?
  # and can it implement the radians() method?
  if (UNIVERSAL::can( $input, 'radians')) {
    return $input->radians;
  }

  # Clean up the string
  $input =~ s/^\s+//g;
  $input =~ s/\s+$//g;

  # guess the units
  unless (defined $units) {
    $units = $self->_guess_units( $input );
    croak "No units supplied, and unable to guess any units either"
      unless defined $units;
  }

  # Now process the input - starting with strings
  my $output = 0;
  if ($units =~ /^s/) {

    # Since we can support aritrary delimiters on write,
    # we should be flexible on read. Slalib is very flexible
    # once the numbers are space separated, so remove all
    # non-numeric characters except + and - and replace with space
    # For now, remove all alphabetic characters and colon only

    # Need to clean up the string for PAL
    $input =~ s/[:[:alpha:]]/ /g;

    my $nstrt = 1;
    ($nstrt, $output, my $j) = Astro::PAL::palDafin( $input, $nstrt );
    $output = undef unless $j == 0;

    if ($j == -1) {
      warnings::warnif "In coordinate '$input' the degrees do not look right";
    } elsif ($j == -2) {
      warnings::warnif "In coordinate '$input' the minutes field is out of range";
    } elsif ($j == -3) {
      warnings::warnif "In coordinate '$input' the seconds field is out of range (0-59.9)";
    } elsif ($j == 1) {
      warnings::warnif "Unable to find plausible coordinate in string '$input'";
    }

  } elsif ($units =~ /^d/) {
    # Degrees decimal
    $output = $input * Astro::PAL::DD2R;

  } elsif ($units =~ /^arcs/ || $units eq 'as') {
    # Arcsec
    $output = $input * Astro::PAL::DAS2R;

  } elsif ($units =~ /^arcm/ || $units eq 'am') {
    # Arcmin
    $output = $input * Astro::PAL::DAS2R * 60 ;

  } else {
    # Already in radians
    $output = $input;
  }

  return $output;
}

=item B<_guess_units>

Given a string or number, tries to guess the units.  Default is to
assume "sexagesimal" if the supplied string does not look like a
number to perl, "degrees" if the supplied number is greater than 2*PI
(6.28), and "radians" for all other values.

  $units = $class->_guess_units( $input );

Returns undef if the input does not look at all plausible or is undef
itself.

Arcsec or arcmin can not be determined with this routine.

=cut

sub _guess_units {
  my $self = shift;
  my $input = shift;
  return undef if !defined $input;

  # Now if we have a space, colon or alphabetic character
  # then we have a real string and assume sexagesimal.
  # Use pre-defined character classes
  my $units;
  # if it does not look like a number choose sexagesimal
  if (!looks_like_number($input)) {
    $units = "sexagesimal";
  } elsif ($input > Astro::PAL::D2PI) {
    $units = "degrees";
  } else {
    $units = "radians";
  }

  return $units;
}

=item B<_r2f>

Routine to convert angle in radians to a formatted array
of numbers in order of sign, deg, min, sec, frac.

  @retval = $ang->_r2f( $ndp );

Note that the number of decimal places is an argument.

=cut

sub _r2f {
  my $self = shift;
  my $res = shift;

  warnings::warnif("More than 9 dp requested ($res), result from palDr2af likely to overflow in fractional part") if $res > 9;

  my ($sign, @dmsf) = Astro::PAL::palDr2af($res, $self->radians);
  return ($sign, @dmsf);
}

=back

=end __PRIVATE_METHODS__

=head1 AUTHOR

Tim Jenness E<lt>t.jenness@cpan.orgE<gt>

=head1 COPYRIGHT

Copyright (C) 2004-2005 Tim Jenness. All Rights Reserved.

This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation; either version 3 of the License, or (at your option) any later
version.

This program is distributed in the hope that it will be useful,but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A



( run in 1.221 second using v1.01-cache-2.11-cpan-5b529ec07f3 )