summaryrefslogtreecommitdiff
path: root/inst/degrees2dms.m
blob: 31f4fedca1848a784f4257345564aa2d5ec74d2a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
## Copyright (C) 2014-2022 Carnë Draug <carandraug@octave.org>
##
## 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 PARTICULAR PURPOSE. See the GNU
## General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; see the file COPYING.  If not, see
## <http://www.gnu.org/licenses/>.

## -*- texinfo -*-
## @deftypefn {Function File} {} degrees2dms (@var{deg})
## Convert decimal degrees to its degrees, minutes, and seconds components.
##
## Separates the fractional part of an angle in decimal degrees and converts
## it into minutes and seconds.  Each row of the output corresponds to one
## angle the first and second column to the degree and minute component
## (both integers), and the third to the seconds (which may have a
## fractional part).
##
## @example
## @group
## degrees2dms (10 + 20/60 + 20/3600)
## @result{}  [ 10   20   20 ]
##
## degrees2dms (10 + 20/60 + pi)
## @result{}  [ 10   28   29.734 ]
## @end group
## @end example
##
## The sign of the first non-zero component indicates the sign of
## the angle, i.e., if the degree and minute components are zero, the sign
## of the seconds indicates the sign of the angle, but if the degree component
## is non-zero, both the minute and second components will be positive
## independently of the sign of the angle.
##
## @example
## @group
## angles = [  10.5
##            -10.5
##             -0.5
##              0.5 ];
## degrees2dms (angles)
##   @result{}
##          10   30   0
##         -10   30   0
##          -0  -30   0
##           0   30   0
## @end group
## @end example
##
## @seealso{degrees2dm, dm2degrees, dms2degrees}
## @end deftypefn

## Author: Carnë Draug <carandraug@octave.org>

function dms = degrees2dms (deg)

  if (nargin != 1)
    print_usage ();
  endif

  if (! iscolumn (deg))
    warning ("Octave:array-to-vector",
             "degrees2dms: DEG reshaped into column vector");
  endif

  if (isinteger (deg))
    ## If the angles are integers, there is no decimal part
    ## so this is easy. Also, class must be conserved.
    dms = [deg(:) zeros(numel (deg), 2, class (deg))];
  else
    ## Everything else is valid and will be converted to double (including
    ## text and logical). Only class single will be conserved.
    d = fix (deg(:));
    m = fix ((deg(:) - d) * 60);
    s = rem (deg(:) * 3600, 60);
    dms = [d m s];

    ## We could have also calculated the seconds part with
    ##    (m - fm) * 60
    ## where m and fm are the minutes before and after fix(). While
    ## that performs almost twice as fast, it also leads to some
    ## precision errors.

    ## remove sign from any minute and second where its degree part is non-zero
    nnzd = logical (dms(:,1));
    dms(nnzd, 2:3) = abs (dms(nnzd, 2:3));
    ## remove sign from any second where its minute is non-zero
    nnzm = logical (dms(:,2));
    dms(nnzm, 3) = abs (dms(nnzm, 3));
  endif

endfunction

%!test
%! hs  = 0.5/60;
%! deg = [ 10  10.5  -10.5  -10  -0.5  0.5  hs  0  -1/60 ]' + hs;
%! dms = [ 10    0   30
%!         10   30   30
%!        -10   29   30
%!         -9   59   30
%!          0  -29   30
%!          0   30   30
%!          0    1    0
%!          0    0   30
%!          0    0  -30];
%! for i = 1:rows (deg)
%!   assert (degrees2dms (deg(i)), dms(i,:));
%! endfor
%! assert (degrees2dms (deg), dms);
%! assert (degrees2dms (single (deg)), single (dms), 4*10^-6);
%!
%! warning ("error", "Octave:array-to-vector", "local")
%! got_warn = false;
%! try
%!   degrees2dms (deg');
%! catch
%!   got_warn = true;
%! end_try_catch
%! assert (got_warn, true)

%!assert (degrees2dms ("f"), [102 0 0])
%!assert (degrees2dms ("fm"), [102 0 0; 109 0 0])
%!assert (degrees2dms (true), [1 0 0])
%!assert (degrees2dms ([true false]), [1 0 0; 0 0 0])
%!assert (degrees2dms (uint8 ([5 48 9]')), uint8 ([5 0 0; 48 0 0; 9 0 0]))