GeographicLib 2.6
Loading...
Searching...
No Matches
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
12namespace 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
Header for GeographicLib::Triaxial::Ellipsoid3 class.
GeographicLib::Angle ang
GeographicLib::Math::real real
Exception handling for GeographicLib.
static void norm(T &x, T &y)
Definition Math.hpp:219
static T sq(T x)
Definition Math.hpp:209
static T hypot3(T x, T y, T z)
Definition Math.cpp:285
void cart2toellip(vec3 R, Angle &bet, Angle &omg) const
static const Ellipsoid3 & Earth()
void elliptocart2(Angle bet, Angle omg, vec3 &R) const
std::array< Math::real, 3 > vec3
Namespace for operations on triaxial ellipsoids.
Namespace for GeographicLib.
AngleT< Math::real > Angle
Definition Angle.hpp:760