GeographicLib  2.6
Ellipsoid3.cpp
Go to the documentation of this file.
1 /**
2  * \file Ellipsoid3.cpp
3  * \brief Implementation for GeographicLib::Triaxial::Ellipsoid3 class
4  *
5  * Copyright (c) Charles Karney (2024-2025) <karney@alum.mit.edu> and licensed
6  * under the MIT/X11 License. For more information, see
7  * https://geographiclib.sourceforge.io/
8  **********************************************************************/
9 
11 
12 namespace GeographicLib {
13  namespace Triaxial {
14 
15  using namespace std;
16 
18  : Ellipsoid3(1, 0, 1, 0)
19  {}
20 
21  Ellipsoid3::Ellipsoid3(real a, real b, real c)
22  : _a(a)
23  , _b(b)
24  , _c(c)
25  {
26  real s = (_a - _c) * (_a + _c);
27  _e2 = s / Math::sq(_b);
28  if (s == 0) {
29  // The sphere is a nonuniform limit, we can pick any values in [0,1]
30  // s.t. k2 + kp2 = 1. Here we choose to treat the sphere as an
31  // oblate ellipsoid.
32  _kp2 = 0; _k2 = 1 - _kp2;
33  } else {
34  _kp2 = (_a - _b) * (_a + _b) / s;
35  _k2 = (_b - _c) * (_b + _c) / s;
36  }
37  _k = sqrt(_k2); _kp = sqrt(_kp2);
38  if (! (isfinite(_a) && isfinite(_b) && isfinite(_c) &&
39  _a >= _b && _b >= _c && _c >= 0 && _b > 0) )
40  throw GeographicErr("Bad semiaxes for triaxial ellipsoid");
41  _oblate = _kp2 == 0;
42  _prolate = _k2 == 0;
43  _biaxial = _oblate || _prolate;
44  }
45 
46  Ellipsoid3::Ellipsoid3(real b, real e2, real k2, real kp2)
47  : _b(b)
48  , _e2(e2)
49  , _k2(k2)
50  , _kp2(kp2)
51  {
52  real ksum = _k2 + _kp2;
53  _k2 /= ksum;
54  _kp2 /= ksum;
55  _k = sqrt(_k2);
56  _kp = sqrt(_kp2);
57  _a = _b * sqrt(1 + _e2 * _kp2);
58  _c = _b * sqrt(1 - _e2 * _k2);
59  if (! (isfinite(_a) && isfinite(_b) && isfinite(_c) &&
60  _a >= _b && _b >= _c && _c >= 0 && _b > 0) )
61  throw GeographicErr("Bad semiaxes for triaxial ellipsoid");
62  _oblate = _kp2 == 0;
63  _prolate = _k2 == 0;
64  _biaxial = _oblate || _prolate;
65  }
66 
67  void Ellipsoid3::Norm(vec3& R) const {
68  vec3 rn{R[0] / _a, R[1] / _b, R[2] / _c};
69  real ra = Math::hypot3(rn[0], rn[1], rn[2]);
70  R = {R[0] / ra, R[1] / ra, R[2] / ra};
71  }
72 
73  void Ellipsoid3::Norm(vec3& R, vec3& V) const {
74  Norm(R);
75  vec3 up = {R[0] / Math::sq(_a), R[1] / Math::sq(_b), R[2] / Math::sq(_c)};
76  real u2 = Math::sq(up[0]) + Math::sq(up[1]) + Math::sq(up[2]),
77  uv = up[0] * V[0] + up[1] * V[1] + up[2] * V[2],
78  f = uv/u2;
79  V = {V[0] - f * up[0], V[1] - f * up[1], V[2] - f * up[2]};
80  normvec(V);
81  }
82 
83  void Ellipsoid3::cart2toellipint(vec3 R, Angle& bet, Angle& omg, vec3 axes)
84  const {
85  real a = axes[0], b = axes[1], c = axes[2];
86  real xi = R[0]/a, eta = R[1]/b, zeta = R[2]/c,
87  g = _k2 * Math::sq(xi)
88  + (_k2 - _kp2) * Math::sq(eta)
89  - _kp2 * Math::sq(zeta);
90  if (fabs(R[0]) == a * _kp2 && R[1] == 0 && fabs(R[2]) == c * _k2)
91  g = 0;
92  real h = hypot(g, 2 * _k * _kp * eta),
93  so, co, sb, cb;
94  if (h == 0) {
95  so = 0;
96  cb = 0;
97  } else if (g < 0) {
98  so = copysign(sqrt( (h - g)/2 ) / _kp, eta);
99  cb = fabs(eta / so);
100  } else {
101  cb = sqrt( (h + g)/2 ) / _k;
102  so = eta / cb;
103  }
104  real tz = hypot(_k, _kp * so),
105  tx = hypot(_k * cb, _kp);
106  sb = tz == 0 ? -1 : zeta / tz;
107  co = tx == 0 ? 1 : xi / tx;
108  bet = ang(sb, cb, 0, true);
109  omg = ang(so, co, 0, true);
110  }
111 
112  void Ellipsoid3::cart2toellip(vec3 R, Angle& bet, Angle& omg) const {
113  cart2toellipint(R, bet, omg, {_a, _b, _c});
114  }
115 
117  vec3 V, Angle& alp) const {
118  real tz = hypot(_k, _kp * omg.s()), tx = hypot(_k * bet.c(), _kp);
119  // At oblate pole tx = 0; at prolate pole, tz = 0
120  if (tx == 0 || tz == 0 || !(bet.c() == 0 && omg.s() == 0)) {
121  // Not a triaxial umbilical point
122  vec3
123  N = tx == 0 ?
124  vec3{-omg.c() * bet.s(), -omg.s() * bet.s(), tx * bet.s()} :
125  (tz == 0 ?
126  vec3{tz, -bet.s(), bet.c()} :
127  vec3{-_a * _k2 * bet.c() * bet.s() * omg.c() / tx,
128  -_b * bet.s() * omg.s(),
129  _c * bet.c() * tz}),
130  E = tx == 0 ?
131  vec3{-omg.s(), omg.c(), tx} :
132  (tz == 0 ?
133  vec3{tz * omg.c(), bet.c() * omg.c(), bet.s() * omg.c()} :
134  vec3{-_a * tx * omg.s(),
135  _b * bet.c() * omg.c(),
136  _c * _kp2 * bet.s() * omg.c() * omg.s() / tz});
137  normvec(N); normvec(E);
138  alp = ang(V[0] * E[0] + V[1] * E[1] + V[2] * E[2],
139  V[0] * N[0] + V[1] * N[1] + V[2] * N[2], 0, true);
140  } else { // bet.c() == 0 && omg.s() == 0
141  // Special treatment at umbilical points
142  real w = bet.s() * omg.c(),
143  upx = omg.c() * tx/_a, upz = bet.s() * tz/_c;
144  Math::norm(upx, upz);
145  // compute direction cosines of V wrt the plane y = 0; angle = 2*alp
146  real s2a = -V[1] * w, c2a = (upz*V[0] - upx*V[2]) * w;
147  // Unnecessary: Math::norm(s2a, c2a)
148  // We have
149  // 2*alp = atan2(s2a, c2a), h2 = hypot(s2a, c2a)
150  // alp = atan2(sa, ca)
151  // tan(2*alp) = 2*tan(alp)/(1-tan(alp)^2)
152  // for alp in [-pi/2, pi/2]
153  // c2a>0
154  // [sa, ca] = [s2a / sqrt(2*(1+c2a)), sqrt((1+c2a)/2)]
155  // -> [s2a, h2+c2a]
156  // c2a<0
157  // [sa, ca] = sign(s2a)*[sqrt((1-c2a)/2), s2a / sqrt(2*(1-c2a))]
158  // -> [sign(s2a) * (h2-c2a), abs(s2a)]
159  // for northern umbilical points, we want to flip alp to alp + pi; so
160  // multiply [sa, ca] by -bet.s().
161  real flip = -bet.s();
162  if (c2a >= 0)
163  alp = ang(flip * s2a, flip * (1 + c2a));
164  else
165  alp = ang(flip * copysign(1 - c2a, s2a), flip * fabs(s2a));
166  }
167  }
168 
170  Angle& bet, Angle& omg, Angle& alp)
171  const {
172  cart2toellip(R, bet, omg);
173  cart2toellip(bet, omg, V, alp);
174  }
175 
176  void Ellipsoid3::elliptocart2(Angle bet, Angle omg, vec3& R) const {
177  real tx = hypot(_k * bet.c(), _kp), tz = hypot(_k, _kp * omg.s());
178  R = { _a * omg.c() * tx,
179  _b * bet.c() * omg.s(),
180  _c * bet.s() * tz };
181  }
182 
184  vec3& R, vec3& V) const {
185  elliptocart2(bet, omg, R);
186  real tx = hypot(_k * bet.c(), _kp), tz = hypot(_k, _kp * omg.s());
187  if (bet.c() == 0 && omg.s() == 0 && !(_k == 0 || _kp == 0)) {
188  // umbilical point (not oblate or prolate)
189  real sa2 = 2 * alp.s() * alp.c(),
190  ca2 = (alp.c() - alp.s()) * (alp.c() + alp.s());
191  // sign on 2nd component is -sign(cos(bet)*sin(omg)). negative sign
192  // gives normal convention of alpha measured clockwise.
193  V = {_a*_k/_b * omg.c() * ca2,
194  -omg.c() * bet.s() * sa2,
195  -_c*_kp/_b * bet.s() * ca2};
196  } else {
197  vec3 N, E;
198  if (tx == 0) {
199  // At an oblate pole tx -> |cos(bet)|
200  real scb = signbit(bet.c()) ? -1 : 1;
201  N = {-omg.c() * bet.s() * scb, -omg.s() * bet.s(), 0};
202  E = {-omg.s() , omg.c() * scb , 0};
203  } else if (tz == 0) {
204  // At a prolate pole tz -> |sin(omg)|
205  real sso = signbit(omg.s()) ? -1 : 1;
206  N = {0, -bet.s() * sso , bet.c() };
207  E = {0, bet.c() * omg.c(), bet.s() * omg.c() * sso};
208  } else {
209  // The general case
210  N = { -_a * _k2 * bet.c() * bet.s() * omg.c() / tx,
211  -_b * bet.s() * omg.s(),
212  _c * bet.c() * tz};
213  E = { -_a * tx * omg.s(),
214  _b * bet.c() * omg.c(),
215  _c * _kp2 * bet.s() * omg.c() * omg.s() / tz};
216  }
217  normvec(N);
218  normvec(E);
219  V = {alp.c() * N[0] + alp.s() * E[0],
220  alp.c() * N[1] + alp.s() * E[1],
221  alp.c() * N[2] + alp.s() * E[2]};
222  }
223  // normvec(V); V is already normalized
224  }
225 
227  static const Ellipsoid3 earth(Constants::Triaxial_Earth_a(),
230  return earth;
231  }
232  } // namespace Triaxial
233 } // namespace GeographicLib
void elliptocart2(Angle bet, Angle omg, vec3 &R) const
Definition: Ellipsoid3.cpp:176
std::array< Math::real, 3 > vec3
Definition: Ellipsoid3.hpp:88
static void norm(T &x, T &y)
Definition: Math.hpp:219
void cart2toellip(vec3 R, Angle &bet, Angle &omg) const
Definition: Ellipsoid3.cpp:112
static const Ellipsoid3 & Earth()
Definition: Ellipsoid3.cpp:226
static T sq(T x)
Definition: Math.hpp:209
Namespace for GeographicLib.
Definition: Accumulator.cpp:12
Header for GeographicLib::Triaxial::Ellipsoid3 class.
static T hypot3(T x, T y, T z)
Definition: Math.cpp:285
GeographicLib::Math::real real
Definition: Geod3Solve.cpp:25
Exception handling for GeographicLib.
Definition: Constants.hpp:344