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