GeographicLib 2.7
Loading...
Searching...
No Matches
Ellipsoid3.hpp
Go to the documentation of this file.
1/**
2 * \file Ellipsoid3.hpp
3 * \brief Header 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
10#if !defined(GEOGRAPHICLIB_ELLIPSOID3_HPP)
11#define GEOGRAPHICLIB_ELLIPSOID3_HPP 1
12
13#include <iostream>
14#include <array>
17
18namespace GeographicLib {
19 /**
20 * \brief Namespace for operations on triaxial ellipsoids
21 *
22 * The algorithms on triaxial ellipsoids are, for the most part, distinct
23 * from those in the rest of %GeographicLib, which deal with biaxial
24 * ellipsoids; it is therefore convenient to put them in a distinct
25 * namespace. In order to minimize the change of name clashes, the classes
26 * in the namespace include "3" in their names. The header files are
27 * included via GeographicLib/Triaxial/Class.hpp.
28 **********************************************************************/
29 namespace Triaxial {
30 /**
31 * \brief A triaxial ellipsoid.
32 *
33 * The class holds the basic information about a triaxial ellipsoid
34 * given by
35 * \f[ S(\mathbf R) =
36 * \frac{X^2}{a^2} + \frac{Y^2}{b^2} + \frac{Z^2}{c^2} - 1 = 0, \f]
37 * where the semiaxes satisfy \f$ a \ge b \ge c > 0\f$.
38 * It is useful to characterize the shape of the ellipsoid by
39 * \f[
40 * \begin{align}
41 * e &= \frac{\sqrt{a^2-c^2}}b,\\
42 * k &= \frac{\sqrt{b^2-c^2}}{\sqrt{a^2-c^2}},\\
43 * k' &= \frac{\sqrt{a^2-b^2}}{\sqrt{a^2-c^2}};
44 * \end{align}
45 * \f]
46 * note that \f$k^2 + k'^2 = 1\f$. The spherical limit \f$ e\rightarrow 0
47 * \f$ is nonuniform since the values of \f$k\f$ and \f$k'\f$ depend on how
48 * the limit is taken. In this cases, it's convenient to specify the
49 * ellipsoid in terms of these parameters. The semiaxes are related to these
50 * parameters by
51 * \f[
52 * [a,b,c] = b \bigl[ \sqrt{1 + e^2k'^2}, 1, \sqrt{1 - e^2k^2} \bigr].
53 * \f]
54 *
55 * Positions on the ellipsoid are given in term so the ellipsoidal
56 * coordinates given in Section 2 of
57 * - C. F. F. Karney,<br>
58 * <a href="https://arxiv.org/abs/2511.01621">
59 * Jacobi's solution for geodesics on a triaxial ellipsoid</a>,<br>
60 * Technical Report, SRI International, Nov. 2025.<br>
61 * <a href="https://arxiv.org/abs/2511.01621">arxiv:2511.01621</a>
62 * .
63 * Ellipsoidal latitude * \f$\beta\f$ and the ellipsoidal longitude
64 * \f$\omega\f$ which are defined * by
65 * \f[
66 * \mathbf R = \begin{bmatrix}
67 * a \cos\omega \sqrt{k^2\cos^2\beta + k'^2} \\
68 * b \cos\beta \sin\omega \\
69 * c \sin\beta \sqrt{k^2 + k'^2\sin^2\omega}
70 * \end{bmatrix}.
71 * \f]
72 * Headings are given by the direction \f$ \alpha \f$ measured clockwise from
73 * a line of constant \f$ \omega \f$. Conversions between cartesian and
74 * ellipsoidal coordinates is provided by cart2toellip() and elliptocart2().
75 *
76 * The ellipsoid coordinates "cover" the ellipsoid twice; the replacement
77 * \f[
78 * \begin{align}
79 * \omega & \rightarrow -\omega,\\
80 * \beta & \rightarrow \pi-\beta,\\
81 * \alpha & \rightarrow \pi+\alpha,
82 * \end{align}
83 * \f]
84 * leaves the position and direction unchanged; see AngNorm(),
85 *
86 * Example of use:
87 * \include example-Ellipsoid3.cpp
88 **********************************************************************/
90 public:
91 /**
92 * A type to hold three-dimensional positions and directions in cartesian
93 * coordinates.
94 **********************************************************************/
95 using vec3 = std::array<Math::real, 3>;
96 private:
97 /// \cond SKIP
98 friend class Cartesian3; // For access to cart2toellipint normvec
99 friend class Geodesic3; // For Flip
100 /// \endcond
101 using real = Math::real;
102 using ang = Angle;
103 static void normvec(vec3& R) {
104 real h = Math::hypot3(R[0], R[1], R[2]);
105 // No checking for h = 0. Result will be NaNs (we rely on this in
106 // Cartesian3::cart2rand).
107 R[0] /= h; R[1] /= h; R[2] /= h;
108 }
109 static void Flip(ang& bet, ang& omg, ang& alp) {
110 bet.reflect(false, true);
111 omg.reflect(true);
112 alp.reflect(true, true);
113 }
114 real _a, _b, _c; // semiaxes
115 real _e2, _k2, _kp2, _k, _kp;
116 bool _oblate, _prolate, _biaxial;
117 void cart2toellipint(vec3 R, ang& bet, ang& omg, vec3 axes) const;
118 /**
119 * @return \e k the oblateness parameter.
120 **********************************************************************/
121 real k() const { return _k; }
122 /**
123 * @return \e kp the prolateness parameter.
124 **********************************************************************/
125 real kp() const { return _kp; }
126 /**
127 * @return whether the ellipsoid is oblate.
128 *
129 * This is determined by the condition kp2() == 0.
130 **********************************************************************/
131 bool oblate() const { return _oblate; }
132 /**
133 * @return whether the ellipsoid is prolate.
134 *
135 * This is determined by the condition k2() == 0.
136 **********************************************************************/
137 bool prolate() const { return _prolate; }
138 /**
139 * @return whether the ellipsoid is oblate or prolate.
140 **********************************************************************/
141 bool biaxial() const { return _biaxial; }
142 public:
143 /**
144 * The default constructor for a unit sphere in the oblate limit.
145 **********************************************************************/
146 Ellipsoid3();
147 /**
148 * An ellipsoid specified by its semiaxes.
149 *
150 * @param[in] a the major semiaxis.
151 * @param[in] b the median semiaxis.
152 * @param[in] c the minor semiaxis.
153 * @exception GeographicErr if the required ordering is semiaxes is
154 * violated.
155 *
156 * The semiaxes must satisfy \e a &ge; \e b &ge; \e c &gt; 0.
157 * If \e a = \e c (a sphere), then the oblate limit is taken.
158 **********************************************************************/
159 Ellipsoid3(real a, real b, real c);
160 /**
161 * An ellipsoid specified by its median semiaxis and shape.
162 *
163 * @param[in] b the middle semiaxis.
164 * @param[in] e2 the eccentricity squared \f$e^2 = (a^2 - c^2)/b^2\f$.
165 * @param[in] k2 the oblateness parameter squared \f$k^2 = (b^2 - c^2) /
166 * (a^2 - c^2)\f$.
167 * @param[in] kp2 the prolateness parameter squared \f$k'^2= (a^2 - b^2) /
168 * (a^2 - c^2)\f$.
169 * @exception GeographicErr if the required ordering is semiaxes is
170 * violated.
171 *
172 * This form of the constructor is important when the eccentricity is small
173 * and giving \e e2 allows for more precision.
174 *
175 * In the case of a sphere with \e e2 = 0, this constructor distinguishes
176 * between and "oblate sphere" (\e k2 = 1), a "prolate sphere" (\e k2 = 0),
177 * and a "triaxial sphere" (\e k2 &isin (0,1)). These distinctions matter
178 * when ellipsoidal coordinates are used.
179 *
180 * \note The constructor normalizes \e k2 and \e kp2 so that \e k2 + \e kp2
181 * = 1.
182 **********************************************************************/
183 Ellipsoid3(real b, real e2, real k2, real kp2);
184 /** \name Inspector functions
185 **********************************************************************/
186 ///@{
187 /**
188 * @return \e a the major semiaxis.
189 **********************************************************************/
190 real a() const { return _a; }
191 /**
192 * @return \e b the median semiaxis.
193 **********************************************************************/
194 real b() const { return _b; }
195 /**
196 * @return \e c the minor semiaxis.
197 **********************************************************************/
198 real c() const { return _c; }
199 /**
200 * @return \e e2 the eccentricity squared.
201 **********************************************************************/
202 real e2() const { return _e2; }
203 /**
204 * @return \e k2 the oblateness parameter squared.
205 **********************************************************************/
206 real k2() const { return _k2; }
207 /**
208 * @return \e kp2 the prolateness parameter squared.
209 **********************************************************************/
210 real kp2() const { return _kp2; }
211 ///@}
212 /** \name Normalizing functions
213 **********************************************************************/
214 ///@{
215 /**
216 * Scale a position to ensure it lies on the ellipsoid
217 *
218 * @param[inout] R the position.
219 *
220 * The components of \e R are scaled so that it lies on the ellipsoid. The
221 * resulting position is not in general the closest point on the ellipsoid.
222 * Use Cartesian3::carttocart2() for that.
223 **********************************************************************/
224 void Norm(vec3& R) const;
225 /**
226 * Scale a position and direction to the ellipsoid
227 *
228 * @param[inout] R the position.
229 * @param[inout] V the position.
230 *
231 * The components of \e R are scaled so that it lies on the ellipsoid. Then
232 * \e V is projected to be tangent to the surface and is normalized to a
233 * unit vector.
234 **********************************************************************/
235 void Norm(vec3& R, vec3& V) const;
236 /**
237 * Set the sheet for coordinates.
238 *
239 * @param[inout] bet the ellipsoidal latitude.
240 * @param[inout] omg the ellipsoidal longitude.
241 * @param[inout] alp the heading.
242 * @param[in] alt if true switch to the alternate sheet.
243 * @return whether the coordinates were changed.
244 *
245 * If alt = false (the default), the conventional sheet is used switching
246 * the values of \e bet, \e omg, and \e alp, so that \e bet &isin;
247 * [-&pi;/2, &pi/2].
248 *
249 * If alt = true, the alternate sheet is used switching the values of \e
250 * bet, \e omg, and \e alp, so that \e omg &isin; [0, &pi;].
251 *
252 * This routine does not change \e n (the number of turns) for the
253 * coordinates.
254 **********************************************************************/
255 static bool AngNorm(Angle& bet, Angle& omg, Angle& alp, bool alt = false) {
256 using std::signbit;
257 // If !alt, put bet in [-pi/2,pi/2]
258 // If alt, put omg in [0, pi]
259 bool flip = alt ? signbit(omg.s()) : signbit(bet.c());
260 if (flip)
261 Flip(bet, omg, alp);
262 return flip;
263 }
264 /**
265 * Set the sheet for coordinates.
266 *
267 * @param[inout] bet the ellipsoidal latitude.
268 * @param[inout] omg the ellipsoidal longitude.
269 * @param[in] alt if true switch to the alternate sheet.
270 * @return whether the coordinated were changed.
271 *
272 * This acts precisely the same and AngNorm(Angle&, Angle&, Angle&, bool)
273 * except that \e alp is omitted.
274 **********************************************************************/
275 static bool AngNorm(Angle& bet, Angle& omg, bool alt = false) {
276 using std::signbit;
277 // If !alt, put bet in [-pi/2,pi/2]
278 // If alt, put omg in [0, pi]
279 bool flip = alt ? signbit(omg.s()) : signbit(bet.c());
280 if (flip) {
281 ang alp;
282 Flip(bet, omg, alp);
283 }
284 return flip;
285 }
286 ///@}
287 /** \name Coordinate conversions.
288 **********************************************************************/
289 ///@{
290 /**
291 * Convert a cartesian position to ellipsoidal coordinates.
292 *
293 * @param[in] R the cartesian position.
294 * @param[out] bet the ellipsoidal latitude.
295 * @param[out] omg the ellipsoidal longitude.
296 *
297 * \note \e R must lie on the surface of the ellipsoid. The "2" in "cart2"
298 * is used to emphasize this.
299 **********************************************************************/
300 void cart2toellip(vec3 R, Angle& bet, Angle& omg) const;
301 /**
302 * Convert a cartesian position and direction to ellipsoidal coordinates.
303 *
304 * @param[in] R the cartesian position.
305 * @param[in] V the cartesian direction.
306 * @param[out] bet the ellipsoidal latitude.
307 * @param[out] omg the ellipsoidal longitude.
308 * @param[out] alp the azimuth.
309 *
310 * \note \e R must lie on the surface of the ellipsoid and \e V must be
311 * tangent to the surface at that point. The "2" in "cart2" is used to
312 * emphasize this.
313 **********************************************************************/
314 void cart2toellip(vec3 R, vec3 V,
315 Angle& bet, Angle& omg, Angle& alp) const;
316 /**
317 * Convert an ellipsoid position and cartesian direction to a heading.
318 *
319 * @param[in] bet the ellipsoidal latitude.
320 * @param[in] omg the ellipsoidal longitude.
321 * @param[in] V the cartesian direction.
322 * @param[out] alp the azimuth.
323 *
324 * This is a variant of cart2toellip(vec3, vec3, Angle&, Angle&, Angle&)
325 * where \e bet and \e omg are used to ensure that the correct sheet is
326 * used in determining \e alp.
327 *
328 * \note \e V must be tangent to the surface of the ellipsoid. The "2" in
329 * "cart2" is used to emphasize this.
330 **********************************************************************/
331 void cart2toellip(Angle bet, Angle omg,
332 vec3 V, Angle& alp) const;
333 /**
334 * Convert ellipsoidal coordinates to a cartesian position.
335 *
336 * @param[in] bet the ellipsoidal latitude.
337 * @param[in] omg the ellipsoidal longitude.
338 * @param[out] R the cartesian position.
339 **********************************************************************/
340 void elliptocart2(Angle bet, Angle omg, vec3& R) const;
341 /**
342 * Convert coordinates and heading to a cartesian position and direction.
343 *
344 * @param[in] bet the ellipsoidal latitude.
345 * @param[in] omg the ellipsoidal longitude.
346 * @param[in] alp the azimuth.
347 * @param[out] R the cartesian position.
348 * @param[out] V the cartesian direction.
349 **********************************************************************/
350 void elliptocart2(Angle bet, Angle omg, Angle alp,
351 vec3& R, vec3& V) const;
352 ///@}
353 /**
354 * A global instantiation of Ellipsoid3 with the parameters for the
355 * Earth.
356 **********************************************************************/
357 static const Ellipsoid3& Earth();
358
359 };
360
361 } // namespace Triaxial
362} // namespace GeographicLib
363
364#endif // GEOGRAPHICLIB_TRIAXIAL_HPP
Header for the GeographicLib::AngleT class.
Header for GeographicLib::Constants class.
#define GEOGRAPHICLIB_EXPORT
Definition Constants.hpp:59
GeographicLib::Angle ang
GeographicLib::Math::real real
AngleT & reflect(bool flips, bool flipc=false, bool swapp=false)
Definition Angle.hpp:696
static T hypot3(T x, T y, T z)
Definition Math.cpp:285
Transformations between cartesian and triaxial coordinates.
static bool AngNorm(Angle &bet, Angle &omg, Angle &alp, bool alt=false)
static bool AngNorm(Angle &bet, Angle &omg, bool alt=false)
std::array< Math::real, 3 > vec3
The solution of the geodesic problem for a triaxial ellipsoid.
Definition Geodesic3.hpp:71
Namespace for operations on triaxial ellipsoids.
Namespace for GeographicLib.
AngleT< Math::real > Angle
Definition Angle.hpp:760