File Coverage

blib/lib/Astro/Coords/Angle.pm
Criterion Covered Total %
statement 177 196 90.3
branch 83 108 76.8
condition 8 15 53.3
subroutine 30 31 96.7
pod 16 18 88.8
total 314 368 85.3


line stmt bran cond sub pod time code
1             package Astro::Coords::Angle;
2              
3             =head1 NAME
4              
5             Astro::Coords::Angle - Representation of an angle
6              
7             =head1 SYNOPSIS
8              
9             use Astro::Coords::Angle;
10              
11             $ang = new Astro::Coords::Angle( 45.5, units => 'deg' );
12             $ang = new Astro::Coords::Angle( "45:30:00", units => 'sexagesimal' );
13              
14             $rad = $ang->radians;
15             $deg = $ang->degrees;
16             $asec = $ang->arcsec;
17             $amin = $ang->arcmin;
18             $string = $ang->string;
19              
20             =head1 DESCRIPTION
21              
22             Helper class for C<Astro::Coords> to represent an angle. Methods are
23             provided for parsing angles in sexagesimal format and for returning
24             angles in any desired format.
25              
26             =cut
27              
28 23     23   8876758 use 5.006;
  23         90  
29 23     23   157 use strict;
  23         78  
  23         545  
30 23     23   152 use warnings;
  23         82  
  23         838  
31 23     23   214 use warnings::register;
  23         68  
  23         4120  
32 23     23   336 use Carp;
  23         80  
  23         1906  
33              
34 23     23   218 use Scalar::Util qw/ looks_like_number /;
  23         43  
  23         3261  
35 23     23   1269 use Astro::PAL;
  23         7804  
  23         9667  
36              
37             # Overloading
38             use overload
39 23         503 '""' => "stringify",
40             '0+' => "numify",
41 23     23   265 fallback => 1;
  23         45  
42              
43             # Package Global variables
44 23     23   2967 use vars qw/ $VERSION /;
  23         111  
  23         55275  
45              
46             $VERSION = '0.21';
47              
48             =head1 METHODS
49              
50             =head2 Constructor
51              
52             =over 4
53              
54             =item B<new>
55              
56             Construct a new C<Angle> object. Must be called with an angle as first
57             argument. Optional hash arguments can be supplied to specify, for example,
58             the units of the supplied angle.
59              
60             $ang = new Astro::Coords::Angle( $angle,
61             units => "degrees" );
62              
63             Supported options are:
64              
65             units - units of the supplied string or number
66             range - restricted range of the angle
67              
68             Supported units are:
69              
70             sexagesimal - A string of format either dd:mm:ss or "dd mm ss"
71             "dms" separators are also supported.
72             degrees - decimal degrees
73             radians - radians
74             arcsec - arc seconds (abbreviated form is 'as')
75             arcmin - arc minutes (abbreviated form is 'am')
76              
77             The units can be abbreviated to the first 3 characters.
78              
79             If the units are not supplied the default is to assume "sexagesimal"
80             if the supplied string contains spaces or colons or the characters
81             "d", "m" or "s", "degrees" if the supplied number is greater than 2*PI
82             (6.28), and "radians" for all other values. Negative angles are supported.
83              
84             The options for range are documented in the C<range> method.
85              
86             If the angle can not be decoded (if a string), the constructor will fail.
87              
88             =cut
89              
90             sub new {
91 70302     70302 1 113404 my $proto = shift;
92 70302   66     198916 my $class = ref($proto) || $proto;
93              
94 70302 50       141243 croak "Constructor for object of class $class must be called with an argument" unless @_;
95              
96             # first argument is the angle
97 70302         102392 my $input_ang = shift;
98              
99             # optional hash, only read it if we have an even number of
100             # remaining arguments
101 70302         96561 my %args;
102 70302 100       124113 if (@_) {
103 70295 50       141090 if (scalar(@_) % 2 == 0) {
104 70295         180630 %args = @_;
105             } else {
106 0         0 warnings::warnif("An odd number of Optional arguments were supplied to constructor");
107             }
108             }
109              
110             # Now need to convert this to radians (the internal representation)
111             # Allow for inheritance
112 70302         181396 my $rad = $class->_cvt_torad($input_ang, $args{units});
113              
114 70302 0       131989 croak "Unable to decode supplied angle (".
    50          
115             (defined $input_ang ? "'$input_ang'" : "<undef>").")"
116             unless defined $rad;
117              
118             # Create the object
119 70302         240233 my $ang = bless {
120             ANGLE => $rad,
121             RANGE => 'NONE',
122             NDP => undef, # number of decimal places
123             DELIM => undef, # string delimiter
124             }, $class;
125              
126             # If a range was specified, normalise the angle
127 70302 100       203886 $ang->range( $args{range} ) if exists $args{range};
128              
129             # And return the object
130 70302         202292 return $ang;
131             }
132              
133             =back
134              
135             =head2 Accessor Methods
136              
137             =over 4
138              
139             =item B<radians>
140              
141             Return the angle in radians.
142              
143             $rad = $ang->radians;
144              
145             =cut
146              
147             sub radians {
148 106634     106634 1 141870 my $self = shift;
149 106634         1299299 return $self->{ANGLE};
150             }
151              
152             # undocumented since we do not want a public way of changing the
153             # angle
154             sub _setRadians {
155 38388     38388   54483 my $self = shift;
156 38388         49216 my $rad = shift;
157 38388 50       73559 croak "Angle must be defined" unless defined $rad;
158 38388         64293 $self->{ANGLE} = $rad;
159             }
160              
161             =item B<degrees>
162              
163             Return the angle in decimal degrees.
164              
165             $deg = $ang->degrees;
166              
167             =cut
168              
169             sub degrees {
170 227     227 1 449 my $self = shift;
171 227         414 my $rad = $self->radians;
172 227         4844 return $rad * Astro::PAL::DR2D;
173             }
174              
175             =item B<str_ndp>
176              
177             Number of decimal places to use when stringifying the object.
178             Default is to use the global class value (see the C<NDP> class method).
179             Set to C<undef> to revert to the class setting.
180              
181             $ang->str_ndp( 4 );
182             $ndp = $ang->str_ndp;
183              
184             =cut
185              
186             sub str_ndp {
187 96     96 1 645 my $self = shift;
188 96 100       212 if (@_) {
189 12         25 $self->{NDP} = shift;
190             }
191             # A value has been requested. Do we have a local value
192             # or should we return the default.
193 96 100       223 if (defined $self->{NDP} ) {
194 26         52 return $self->{NDP};
195             } else {
196 70         192 return $self->NDP;
197             }
198             }
199              
200             =item B<str_delim>
201              
202             Delimiter to use between components when stringifying.
203             Default is to use the global class value (see the C<DELIM> class method).
204             Set to C<undef> to revert to the class setting.
205              
206             $ang->str_delim( ":" );
207             $delim = $ang->str_delim;
208              
209             =cut
210              
211             sub str_delim {
212 87     87 1 165 my $self = shift;
213 87 100       179 if (@_) {
214 6         13 $self->{DELIM} = shift;
215             }
216             # A value has been requested. Do we have a local value
217             # or should we return the default.
218 87 100       194 if (defined $self->{DELIM} ) {
219 13         26 return $self->{DELIM};
220             } else {
221 74         238 return $self->DELIM;
222             }
223             }
224              
225             =item B<components>
226              
227             Return an array of components that correspond to the sign, degrees,
228             arcminutes and arcseconds of the angle. The sign will be either a '+'
229             or '-' and is required to distinguish '+0' from '-0'.
230              
231             @comp = $ang->components;
232              
233             The number of decimal places in the seconds will not be constrained by the
234             setting of C<str_ndp>, but is constrained by an optional argument:
235              
236             @comp = $ang->components( $ndp );
237              
238             Default resolution is 5 decimal places. The limit is 9 to avoid
239             overflowing the results from palDr2af or palDr2tf.
240              
241             In scalar context, returns a reference to an array.
242              
243             =cut
244              
245             sub components {
246 84     84 1 130 my $self = shift;
247 84         126 my $res = shift; # internal api
248              
249             # Get the angle in radians
250 84         172 my $rad = $self->radians;
251              
252             # Convert to components using PAL. COCO uses 4 dp for high
253             # resolution.
254 84 50       198 $res = 5 unless defined $res;
255              
256             # Limit $res to avoid overflowing results from palDr2af or palDr2tf.
257 84 50       195 if ($res > 9) {
258 0         0 warnings::warnif("Excess dp ($res) requested, limiting to 9");
259 0         0 $res = 9;
260             }
261              
262 84         255 my @dmsf = $self->_r2f( $res );
263              
264             # Combine the fraction with the seconds unless no decimal places
265 84         162 my $frac = pop(@dmsf);
266 84 100       461 $dmsf[-1] .= sprintf( ".%0$res"."d",$frac) unless $res == 0;
267              
268             #use Data::Dumper;
269             #print Dumper(\@dmsf);
270              
271 84 100       193 if (wantarray) {
272 81         263 return @dmsf;
273             } else {
274 3         17 return \@dmsf;
275             }
276              
277             }
278              
279             =item B<string>
280              
281             Return the angle as a string in sexagesimal format (e.g. 12:30:52.4).
282              
283             $string = $ang->string();
284              
285             The form of this string depends on the C<str_delim> and C<str_ndp>
286             settings and on whether the angular range allows negative values (the
287             sign will be dropped if the range is known to be positive).
288              
289             =cut
290              
291             sub string {
292 81     81 1 195 my $self = shift;
293              
294             # Get the components
295 81         173 my $ndp = $self->str_ndp;
296 81         243 my @dms = $self->components( $ndp );
297              
298             # Play it safe, and split the fractional part into two strings.
299             # if ndp > 0
300 81 100       217 if ( $ndp > 0 ) {
301 80         256 my ($sec, $frac) = split(/\./,$dms[-1]);
302 80         151 $dms[-1] = $sec;
303 80         170 push(@dms, $frac);
304             }
305              
306             # Now build the string.
307              
308             # Clear the + sign, setting it to empty string if the angle can never
309             # go negative.
310 81         152 my $sign = shift(@dms);
311 81 100       204 if ($sign eq '+') {
312 50 100       120 if ($self->range eq '2PI') {
313 41         90 $sign = '';
314             } else {
315 9         26 $sign = ' ';
316             }
317             }
318              
319             # Get the delimiter
320 81         221 my $delim = $self->str_delim;
321              
322             # Build the format
323              
324             # fractional part will not require a decimal place
325             # if ndp is 0. If ndp>0 the fraction is formatted
326 81 100       216 my $fracfmt = ( $ndp == 0 ? '' : '.%s' );
327              
328             # starting with the numeric part. Gal longitude will want %03d and no sign.
329             # RA will want no sign and %02d. Dec wants sign with %02d.
330              
331 81         219 my @fmts = ( '%02d', '%02d', '%02d'.$fracfmt);
332 81         129 my $fmt;
333 81 100       171 if (length($delim) == 1) {
334 78         193 $fmt = join($delim, @fmts );
335             } else {
336 3         9 my @chars = split (//, $delim );
337 3         16 for my $f (@fmts) {
338 9         71 $fmt .= $f . shift(@chars);
339             }
340             }
341              
342 81         1045 return $sign . sprintf( $fmt, @dms);
343              
344             }
345              
346             =item B<arcsec>
347              
348             Return the angle in arcseconds.
349              
350             $asec = $ang->arcsec;
351              
352             =cut
353              
354             sub arcsec {
355 11     11 1 21 my $self = shift;
356 11         22 my $rad = $self->radians;
357 11         36 return $rad * Astro::PAL::DR2AS;
358             }
359              
360             =item B<arcmin>
361              
362             Return the angle in arcminutes.
363              
364             $amin = $ang->arcmin;
365              
366             =cut
367              
368             sub arcmin {
369 0     0 1 0 my $self = shift;
370 0         0 my $asec = $self->arcsec;
371 0         0 return $asec / 60.0;
372             }
373              
374             =item B<range>
375              
376             String describing the allowed range of the angle. Allowed values
377             are
378              
379             NONE - no pre-determined range
380             2PI - 0 to 2*PI radians (0 to 360 degrees)
381             PI - -PI to +PI radians (-180 to 180 degrees)
382              
383             Any other strings will be ignored (and a warning issued if appropriate).
384              
385             When a new value is provided, the angle is normalised to this range.
386             Note that this is not always reversible (especially if reverting to
387             "NONE"). The range can also be specified to the constructor.
388              
389             Default is not to normalize the angle.
390              
391             =cut
392              
393             sub range {
394 38447     38447 1 56909 my $self = shift;
395 38447 100       78899 if (@_) {
396 38391         58103 my $rng = shift;
397 38391 50       65224 if (defined $rng) {
398             # upper case
399 38391         63321 $rng = uc($rng);
400              
401             # get the current value for the angle
402 38391         65707 my $rad = $self->radians;
403              
404             # Now check validity of string and normalise
405 38391 100       92562 if ($rng eq 'NONE') {
    100          
    50          
406             # do nothing apart from store it
407             } elsif ($rng eq '2PI') {
408 29672         88921 $self->_setRadians( Astro::PAL::palDranrm( $rad ));
409             } elsif ($rng eq 'PI') {
410 8716         27054 $self->_setRadians( Astro::PAL::palDrange( $rad ));
411             } else {
412 0         0 warnings::warnif("Supplied range '$rng' not recognized");
413 0         0 return;
414             }
415             # store it
416 38391         75358 $self->{RANGE} = $rng;
417             } else {
418 0         0 warnings::warnif("Supplied range was not defined");
419             }
420             }
421 38447         55691 return $self->{RANGE};
422             }
423              
424             =item B<in_format>
425              
426             Simple wrapper method to support the backwards compatibility interface
427             in C<Astro::Coords> when requesting an angle by using a string format rather
428             than an explicit method.
429              
430             $angle = $ang->in_format( 'sexagesimal' );
431              
432             Supported formats are:
433              
434             radians calls 'radians' method
435             degrees calls 'degrees' method
436             sexagesimal calls 'string' method
437             array calls 'components' method (returns 2 dp resolution)
438             arcsec calls 'arcsec' method
439             arcmin calls 'arcmin' method
440              
441             The format can be abbreviated to the first 3 letters, or 'am' or 'as'
442             for arcmin and arcsec respectively. If no format is specified explicitly, the
443             object itself will be returned.
444              
445             =cut
446              
447             sub in_format {
448 41441     41441 1 65689 my $self = shift;
449 41441         64353 my $format = shift;
450              
451             # No format (including empty string), return the object
452 41441 100       133193 return $self unless $format;
453 2749         5935 $format = lc($format);
454              
455 2749 100 33     12933 if ($format =~ /^d/) {
    100 33        
    100          
    50          
    50          
    50          
456 184         417 return $self->degrees;
457             } elsif ($format =~ /^s/) {
458 16         46 return $self->string();
459             } elsif ($format =~ /^r/) {
460 2546         5084 return $self->radians();
461             } elsif ($format =~ /^arcm/ || $format eq 'am') {
462 0         0 return $self->arcmin;
463             } elsif ($format =~ /^arcs/ || $format eq 'as') {
464 0         0 return $self->arcsec;
465             } elsif ($format =~ /^a/) {
466 3         9 return $self->components($self->str_ndp);
467             } else {
468 0         0 warnings::warnif("Unsupported format '$format'. Returning radians.");
469 0         0 return $self->radians;
470             }
471             }
472              
473             =item B<clone>
474              
475             Create new cloned copy of this object.
476              
477             $clone = $ang->clone;
478              
479             =cut
480              
481             sub clone {
482 1     1 1 3 my $self = shift;
483 1         7 return bless { %$self }, ref $self;
484             }
485              
486             =item B<negate>
487              
488             Negate the sense of the angle, returning a new angle object.
489              
490             $neg = $ang->negate;
491              
492             Not allowed if the range is defined as 0 to 2PI.
493              
494             =cut
495              
496             sub negate {
497 3     3 1 8 my $self = shift;
498 3 50       66 croak "Angle can not be negated since its range is 0 to 2PI"
499             if $self->range eq '2PI';
500 3         10 my $rad = $self->radians;
501 3         42 return $self->new( $rad * -1.0, units => 'radians', range => $self->range );
502             }
503              
504             =back
505              
506             =head2 Overloading
507              
508             The object is overloaded such that it stringifies via the C<string>
509             method, and returns the angle in radians in numify context.
510              
511             =cut
512              
513             sub stringify {
514 22     22 0 534 my $self = shift;
515 22         68 return $self->string();
516             }
517              
518             sub numify {
519 64943     64943 0 123465 my $self = shift;
520 64943         107334 return $self->radians();
521             }
522              
523             =head2 Class Methods
524              
525             The following methods control the default behaviour of the class.
526              
527             =over 4
528              
529             =item B<NDP>
530              
531             The number of decimal places to use in the fractional part of
532             the number when stringifying (from either the C<string> method
533             or the C<components> method).
534              
535             Astro::Coords::Angle->NDP( 4 );
536              
537             Default value is 2. If this is changed then
538             all instances will be affected on stringification unless the
539             C<str_ndp> attribute has been set explicitly for an instance.
540              
541             If an undefined argument is supplied, the class will revert to its
542             initial state.
543              
544             Astro::Coords::Angle->NDP( undef );
545              
546             =cut
547              
548             {
549             my $DEFAULT_NDP = 2;
550             my $NDP = $DEFAULT_NDP;
551             sub NDP {
552 41     41 1 71 my $class = shift;
553 41 100       115 if (@_) {
554 1         2 my $arg = shift;
555 1 50       3 if (defined $arg) {
556 1         3 $NDP = $arg;
557             } else {
558 0         0 $NDP = $DEFAULT_NDP;
559             }
560             }
561 41         96 return $NDP;
562             }
563             }
564              
565             =item B<DELIM>
566              
567             Delimiter to use to separate components of a sexagesimal triplet when
568             the object is stringified. If this is changed then all instances will
569             be affected on stringification unless the C<str_delim> attribute has
570             been set explicitly for an instance.
571              
572             Common values are a colon (12:52:45.4) or a space (12 52 45.4). If
573             more than one character is present in the string, each character will
574             be used in turn as a delimiter in the string until either no more gaps
575             are present (or characters have been exhausted. In the former, if
576             there are more characters than gaps, the first character remaining in
577             the string will be appended, in the latter case, no more characters
578             will be printed. For example, "dms" would result in '12d52m45.4s',
579             whereas 'dm' would result in '12d52m45.4'
580              
581             Astro::Coords::Angle->DELIM( ':' );
582              
583             Default is ":". An undefined argument will result in the class reverting
584             to the default state.
585              
586             =cut
587              
588             {
589             my $DEFAULT_DELIM = ":";
590             my $DELIM = $DEFAULT_DELIM;
591             sub DELIM {
592 43     43 1 13966 my $class = shift;
593 43 100       126 if (@_) {
594 2         7 my $arg = shift;
595 2 50       10 if (defined $arg) {
596 2         6 $DELIM = $arg;
597             } else {
598 0         0 $DELIM = $DEFAULT_DELIM;
599             }
600             }
601 43         98 return $DELIM;
602             }
603             }
604              
605             =item B<to_radians>
606              
607             Low level utility routine to convert an input value in specified format
608             to radians. This method uses the same code as the object constructor to parse
609             the supplied input argument but does not require the overhead of object
610             construction if the result is only to be used transiently.
611              
612             $rad = Astro::Coords::Angle->to_radians( $string, $format );
613              
614             See the constructor documentation for the supported format strings.
615              
616             =cut
617              
618             sub to_radians {
619 2064     2064 1 3473 my $class = shift;
620             # simply delegate to the internal routine. Could use it directly but it feels
621             # better to leave options open for the moment
622 2064         5082 $class->_cvt_torad( @_ );
623             }
624              
625             =back
626              
627             =begin __PRIVATE_METHODS__
628              
629             =head2 Private Methods
630              
631             These methods are not part of the API and should not be called directly.
632             They are documented for completeness.
633              
634             =over 4
635              
636             =item B<_cvt_torad>
637              
638             Internal class method to convert an input string to the equivalent value in
639             radians. The following units are supported:
640              
641             sexagesimal - A string of format "dd:mm:ss.ss", "dd mm ss.ss"
642             or even "-ddxmmyss.ss" (ie -5x53y28.5z)
643             degrees - decimal degrees
644             radians - radians
645             arcsec - arc seconds (abbreviated form is 'as')
646             arcmin - arc minutes (abbreviated form is 'am')
647              
648             If units are not supplied, default is to call the C<_guess_units>
649             method.
650              
651             $radians = $angle->_cvt_torad( $angle, $units );
652              
653             Warnings are issued if the string can not be parsed or the values are
654             out of range.
655              
656             If the supplied angle is an Angle object itself, units are ignored and
657             the value is extracted directly from the object.
658              
659             Returns C<undef> on error. Does not modify the internal state of the object.
660              
661             =cut
662              
663             sub _cvt_torad {
664 72366     72366   107674 my $self = shift;
665 72366         97175 my $input = shift;
666 72366         94491 my $units = shift;
667              
668 72366 50       129988 return undef unless defined $input;
669              
670             # do we have an object?
671             # and can it implement the radians() method?
672 72366 100       607744 if (UNIVERSAL::can( $input, 'radians')) {
673 214         788 return $input->radians;
674             }
675              
676             # Clean up the string
677 72152         252619 $input =~ s/^\s+//g;
678 72152         190420 $input =~ s/\s+$//g;
679              
680             # guess the units
681 72152 100       136177 unless (defined $units) {
682 28         73 $units = $self->_guess_units( $input );
683 28 50       74 croak "No units supplied, and unable to guess any units either"
684             unless defined $units;
685             }
686              
687             # Now process the input - starting with strings
688 72152         102263 my $output = 0;
689 72152 100 66     378396 if ($units =~ /^s/) {
    100 66        
    100          
    100          
690              
691             # Since we can support aritrary delimiters on write,
692             # we should be flexible on read. Slalib is very flexible
693             # once the numbers are space separated, so remove all
694             # non-numeric characters except + and - and replace with space
695             # For now, remove all alphabetic characters and colon only
696              
697             # Need to clean up the string for PAL
698 2047         10611 $input =~ s/[:[:alpha:]]/ /g;
699              
700 2047         3825 my $nstrt = 1;
701 2047         11947 ($nstrt, $output, my $j) = Astro::PAL::palDafin( $input, $nstrt );
702 2047 50       5492 $output = undef unless $j == 0;
703              
704 2047 50       7172 if ($j == -1) {
    50          
    50          
    50          
705 0         0 warnings::warnif "In coordinate '$input' the degrees do not look right";
706             } elsif ($j == -2) {
707 0         0 warnings::warnif "In coordinate '$input' the minutes field is out of range";
708             } elsif ($j == -3) {
709 0         0 warnings::warnif "In coordinate '$input' the seconds field is out of range (0-59.9)";
710             } elsif ($j == 1) {
711 0         0 warnings::warnif "Unable to find plausible coordinate in string '$input'";
712             }
713              
714             } elsif ($units =~ /^d/) {
715             # Degrees decimal
716 32         108 $output = $input * Astro::PAL::DD2R;
717              
718             } elsif ($units =~ /^arcs/ || $units eq 'as') {
719             # Arcsec
720 24         43 $output = $input * Astro::PAL::DAS2R;
721              
722             } elsif ($units =~ /^arcm/ || $units eq 'am') {
723             # Arcmin
724 2         5 $output = $input * Astro::PAL::DAS2R * 60 ;
725              
726             } else {
727             # Already in radians
728 70047         114548 $output = $input;
729             }
730              
731 72152         151109 return $output;
732             }
733              
734             =item B<_guess_units>
735              
736             Given a string or number, tries to guess the units. Default is to
737             assume "sexagesimal" if the supplied string does not look like a
738             number to perl, "degrees" if the supplied number is greater than 2*PI
739             (6.28), and "radians" for all other values.
740              
741             $units = $class->_guess_units( $input );
742              
743             Returns undef if the input does not look at all plausible or is undef
744             itself.
745              
746             Arcsec or arcmin can not be determined with this routine.
747              
748             =cut
749              
750             sub _guess_units {
751 47     47   83 my $self = shift;
752 47         80 my $input = shift;
753 47 50       103 return undef if !defined $input;
754              
755             # Now if we have a space, colon or alphabetic character
756             # then we have a real string and assume sexagesimal.
757             # Use pre-defined character classes
758 47         67 my $units;
759             # if it does not look like a number choose sexagesimal
760 47 100       199 if (!looks_like_number($input)) {
    100          
761 34         57 $units = "sexagesimal";
762             } elsif ($input > Astro::PAL::D2PI) {
763 4         7 $units = "degrees";
764             } else {
765 9         20 $units = "radians";
766             }
767              
768 47         101 return $units;
769             }
770              
771             =item B<_r2f>
772              
773             Routine to convert angle in radians to a formatted array
774             of numbers in order of sign, deg, min, sec, frac.
775              
776             @retval = $ang->_r2f( $ndp );
777              
778             Note that the number of decimal places is an argument.
779              
780             =cut
781              
782             sub _r2f {
783 48     48   93 my $self = shift;
784 48         85 my $res = shift;
785              
786 48 50       144 warnings::warnif("More than 9 dp requested ($res), result from palDr2af likely to overflow in fractional part") if $res > 9;
787              
788 48         132 my ($sign, @dmsf) = Astro::PAL::palDr2af($res, $self->radians);
789 48         180 return ($sign, @dmsf);
790             }
791              
792             =back
793              
794             =end __PRIVATE_METHODS__
795              
796             =head1 AUTHOR
797              
798             Tim Jenness E<lt>t.jenness@cpan.orgE<gt>
799              
800             =head1 COPYRIGHT
801              
802             Copyright (C) 2004-2005 Tim Jenness. All Rights Reserved.
803              
804             This program is free software; you can redistribute it and/or modify it under
805             the terms of the GNU General Public License as published by the Free Software
806             Foundation; either version 3 of the License, or (at your option) any later
807             version.
808              
809             This program is distributed in the hope that it will be useful,but WITHOUT ANY
810             WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
811             PARTICULAR PURPOSE. See the GNU General Public License for more details.
812              
813             You should have received a copy of the GNU General Public License along with
814             this program; if not, write to the Free Software Foundation, Inc., 59 Temple
815             Place,Suite 330, Boston, MA 02111-1307, USA
816              
817             =cut
818              
819             1;
820