GeographicLib  2.6
Geodesic3.hpp
Go to the documentation of this file.
1 /**
2  * \file Geodesic3.hpp
3  * \brief Header for GeographicLib::Triaxial::Geodesic3 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_GEODESIC3_HPP)
11 #define GEOGRAPHICLIB_GEODESIC3_HPP 1
12 
13 #include <functional>
14 #include <memory>
16 
17 #if defined(_MSC_VER)
18 // Squelch warnings about dll vs shared_ptr
19 # pragma warning (push)
20 # pragma warning (disable: 4251)
21 #endif
22 
23 namespace GeographicLib {
24  namespace Triaxial {
25  class GeodesicLine3;
26 
27  /**
28  * \brief The solution of the geodesic problem for a triaxial ellipsoid
29  *
30  * This is an implementation of
31  * <a href="https://books.google.com/books?id=RbwGAAAAYAAJ&pg=PA309">
32  * Jacobi's method for finding geodesics on a triaxial ellipsoid</a>
33  * (1839). This class offers an interface to the solutions to the direct
34  * geodesic problem via the GeodesicLine3 class which contains the meat of
35  * Jacobi's direct solution. In addition it provides a solution to the
36  * inverse problem which closely parallels the solution for the biaxial
37  * problem given by GeodesicExact. For more details see \ref triaxial.
38  *
39  * Data for testing the geodesic routines is availble at
40  * <a href="https://doi.org/10.5281/zenodo.12510796"> Test set of geodesics
41  * on a triaxial ellipsoid (2024)</a>.
42  *
43  * \note There's a lot of new code here and testing is two orders of
44  * magnitude more difficult compared with the biaxial case (an extra
45  * parameter to fix the shape of the ellipsoid and geodesics now depend on
46  * the longitude of the two end points separately). I've limited by testing
47  * to ellipsoids with \e a/\e c &le; 2. I don't expect any problems if \e
48  * a/\e c &le; 10; but you might run into problems with more eccentric
49  * ellipsoids. The code treats oblate and prolate (biaxial) ellipsoids
50  * correctly; but, again, there may be problems with triaxial ellipsoids
51  * which are \e extremely close to oblate or prolate i.e., with either \e k
52  * or \e k' very small. (However the triaxial model of the Earth where the
53  * difference in the equatorial semiaxes is 70 m, \f$k' = 0.057\f$, is
54  * treated just fine.) While I have made every effort to ensure that the
55  * code is error free, it's likely that some bugs remain. Please use caution
56  * with the results and report any problems (via email or with a Github
57  * issue).
58  *
59  * Example of use:
60  * \include example-Geodesic3.cpp
61  *
62  * <a href="Geod3Solve.1.html">Geod3Solve</a> is a command-line utility
63  * providing access to the functionality of Geodesic3 and GeodesicLine3.
64  **********************************************************************/
66  private:
67  /// \cond SKIP
68  // For access to BigValue, _ellipthresh, _biaxp
69  friend class GeodesicLine3;
70  /// \endcond
71  using real = Math::real;
72  using ang = Angle;
73  static constexpr bool debug_ = false; // print out diagnostics
74  static constexpr bool throw_ = true; // exception on convergence failure
75  // special treatment for biaxial non-meridional
76  static constexpr bool biaxp_ = true;
77  // favor hybrid solution in terms of omg
78  static constexpr bool hybridalt_ = true;
79  // allow swapping of omega{1,2}
80  static constexpr bool swapomg_ = false;
81  static constexpr int maxit_ = 200;
82  Ellipsoid3 _t;
83 
84  // Run geodesic from bet1, omg1, alp1, find its first intersection with bet
85  // = bet2a and return omg2a - omg2b
86  real HybridA(ang bet1, ang omg1, ang alp1,
87  ang bet2a, ang omg2b, bool betp) const;
88  static ang findroot(const std::function<real(const ang&)>& f,
89  ang xa, ang xb,
90  real fa, real fb,
91  int* countn = nullptr, int* countb = nullptr);
92  bool _umbalt; // how coordinates wrap with umbilical lines
93  // If k'^2 < ellipthresh transform phi -> F(phi, k^2)
94  real _ellipthresh;
95  mutable std::shared_ptr<GeodesicLine3> _umbline;
96  static real BigValue() {
97  using std::log;
98  static real bigval = -3*log(std::numeric_limits<real>::epsilon());
99  return bigval;
100  }
101  class gamblk {
102  public:
103  bool transpolar;
104  // gamma = (k * cbet * salp)^2 - (kp * somg * calp)^2
105  // = k2*cb2*sa2 - kp2*so2*ca2
106  // Need accurate expressions for
107  // k2 - gamma = k2*(sb2+ca2*cb2) + kp2*so2*ca2
108  // kp2 + gamma = k2*cb2*sa2 + kp2*(co2+sa2*so2)
109  // If gamma is given, eval new alp given new bet and new omg
110  // gamma < 0
111  // ca2 = (k2*cb2-gamma) / (k2*cb2+kp2*so2)
112  // sa2 = (kp2+gamma - kp2*co2) / (k2*cb2+kp2*so2)
113  // gamma > 0
114  // ca2 = (k2-gamma - k2*sb2) / (k2*cb2+kp2*so2)
115  // sa2 = (kp2*so2+gamma) / (k2*cb2+kp2*so2)
116  // gamma > 0
117  // k2*sb2 = spsi2 * (k2-gamma)
118  // (k2-gamma - k2*sb2) = (k2-gamma)*(1-spsi2) = (k2-gamma)*cpsi2
119  // spsi2 = k2*sb2/(k2-gamma)
120  // cpsi2 = (k2*cb2-gamma)/(k2-gamma)
121  real gamma,
122  // [nu, nup]
123  // = [sqrt(gam)/k, sqrt(1 - gam/k2)] for !signbit(gam)
124  // = [sqrt(-gam)/kp, sqrt(1 + gam/kp2)] for signbit(gam)
125  // unused for umbilics
126  nu, nup,
127  gammax, kx2, kxp2, kx, kxp;
128  // Default values for gamma = +/-0
129  gamblk() {}
130  gamblk(const Geodesic3& tg, bool neg = false);
131  gamblk(const Geodesic3& tg, ang bet, ang omg, ang alp);
132  // gamblk(real gammax, real nux, real nupx)
133  // : gamma(gammax), nu(nux), nup(nupx) {}
134  };
135  gamblk gamma(ang bet, ang omg, ang alp)
136  const;
137  // real a() const { return t().a(); } // not needed
138  real b() const { return t().b(); }
139  // real c() const { return t().c(); } // not needed
140  real e2() const { return t().e2(); }
141  real k2() const { return t().k2(); }
142  real kp2() const { return t().kp2(); }
143  real k() const { return t().k(); }
144  real kp() const { return t().kp(); }
145  bool oblate() const { return t().oblate(); }
146  bool prolate() const { return t().prolate(); }
147  bool biaxial() const { return t().biaxial(); }
148  public:
149  /**
150  * Constructor for a triaxial ellipsoid defined by Ellipsoid3 object.
151  *
152  * @param[in] t the Ellipsoid3 object.
153  **********************************************************************/
154  Geodesic3(const Ellipsoid3& t = Ellipsoid3{});
155  /**
156  * Constructor for a trixial ellipsoid with semi-axes.
157  *
158  * @param[in] a the largest semi-axis.
159  * @param[in] b the middle semi-axis.
160  * @param[in] c the smallest semi-axis.
161  * @exception GeographicErr if the required ordering is semiaxes is
162  * violated.
163  *
164  * The semi-axes must satisfy \e a &ge; \e b &ge; \e c &gt; 0.
165  * If \e a = \e c (a sphere), then the oblate limit is taken.
166  **********************************************************************/
167  Geodesic3(real a, real b, real c);
168  /**
169  * Alternate constructor for a triaxial ellipsoid.
170  *
171  * @param[in] b the middle semi-axis.
172  * @param[in] e2 the eccentricity squared \f$e^2 = (a^2 - c^2)/b^2\f$.
173  * @param[in] k2 the oblateness parameter squared \f$k^2 = (b^2 - c^2) /
174  * (a^2 - c^2)\f$.
175  * @param[in] kp2 the prolateness parameter squared \f$k'^2= (a^2 - b^2) /
176  * (a^2 - c^2)\f$.
177  * @exception GeographicErr if the required ordering is semiaxes is
178  * violated.
179  *
180  * \note The constructor normalizes \e k2 and \e kp2 to ensure then \e k2 +
181  * \e kp2 = 1.
182  **********************************************************************/
183  Geodesic3(real b, real e2, real k2, real kp2);
184  /**
185  * @return the Ellipsoid3 object for this projection.
186  **********************************************************************/
187  const Ellipsoid3& t() const { return _t; }
188  /**
189  * Solve the inverse problem
190  *
191  * @param[in] bet1 the ellipsoidal latitude of point 1.
192  * @param[in] omg1 the ellipsoidal longitude of point 1.
193  * @param[in] bet2 the ellipsoidal latitude of point 2.
194  * @param[in] omg2 the ellipsoidal longitude of point 2.
195  * @param[out] s12 the shortest distance between the points.
196  * @param[out] alp1 the forward azimuth of the geodesic at point 1.
197  * @param[out] alp2 the forward azimuth of the geodesic at point 2.
198  * @return a GeodesicLine3 object defining the geodesic.
199  **********************************************************************/
200  GeodesicLine3 Inverse(Angle bet1, Angle omg1, Angle bet2, Angle omg2,
201  real& s12, Angle& alp1, Angle& alp2) const;
202  /**
203  * Solve the inverse problem in degrees
204  *
205  * @param[in] bet1 the ellipsoidal latitude of point 1 (in degrees).
206  * @param[in] omg1 the ellipsoidal longitude of point 1 (in degrees).
207  * @param[in] bet2 the ellipsoidal latitude of point 2 (in degrees).
208  * @param[in] omg2 the ellipsoidal longitude of point 2 (in degrees).
209  * @param[out] s12 the shortest distance between the points.
210  * @param[out] alp1 the forward azimuth of the geodesic at point 1 (in
211  * degrees).
212  * @param[out] alp2 the forward azimuth of the geodesic at point 2 (in
213  * degrees).
214  * @return a GeodesicLine3 object defining the geodesic.
215  **********************************************************************/
216  GeodesicLine3 Inverse(real bet1, real omg1, real bet2, real omg2,
217  real& s12, real& alp1, real& alp2) const;
218  /**
219  * Solve the direct problem
220  *
221  * @param[in] bet1 the ellipsoidal latitude of point 1.
222  * @param[in] omg1 the ellipsoidal longitude of point 1.
223  * @param[in] alp1 the forward azimuth of the geodesic at point 1.
224  * @param[in] s12 the distance from point 1 to point 2.
225  * @param[out] bet2 the ellipsoidal latitude of point 2.
226  * @param[out] omg2 the ellipsoidal longitude of point 2.
227  * @param[out] alp2 the forward azimuth of the geodesic at point 2.
228  * @return a GeodesicLine3 object defining the geodesic.
229  **********************************************************************/
230  GeodesicLine3 Direct(Angle bet1, Angle omg1, Angle alp1, real s12,
231  Angle& bet2, Angle& omg2, Angle& alp2) const;
232  /**
233  * Solve the direct problem in degrees
234  *
235  * @param[in] bet1 the ellipsoidal latitude of point 1 (in degrees).
236  * @param[in] omg1 the ellipsoidal longitude of point 1 (in degrees).
237  * @param[in] alp1 the forward azimuth of the geodesic at point 1 (in
238  * degrees).
239  * @param[in] s12 the distance from point 1 to point 2.
240  * @param[out] bet2 the ellipsoidal latitude of point 2 (in degrees).
241  * @param[out] omg2 the ellipsoidal longitude of point 2 (in degrees).
242  * @param[out] alp2 the forward azimuth of the geodesic at point 2 (in
243  * degrees).
244  * @return a GeodesicLine3 object defining the geodesic.
245  **********************************************************************/
246  GeodesicLine3 Direct(real bet1, real omg1, real alp1, real s12,
247  real& bet2, real& omg2, real& alp2) const;
248  /**
249  * A geodesic line defined at a starting point
250  *
251  * @param[in] bet1 the ellipsoidal latitude of point 1.
252  * @param[in] omg1 the ellipsoidal longitude of point 1.
253  * @param[in] alp1 the forward azimuth of the geodesic at point 1.
254  * @return a GeodesicLine3 object defining the geodesic.
255  **********************************************************************/
256  GeodesicLine3 Line(Angle bet1, Angle omg1, Angle alp1) const;
257  /**
258  * A geodesic line defined at a starting point specified in degrees
259  *
260  * @param[in] bet1 the ellipsoidal latitude of point 1 (in degrees).
261  * @param[in] omg1 the ellipsoidal longitude of point 1 (in degrees).
262  * @param[in] alp1 the forward azimuth of the geodesic at point 1 (in
263  * degrees).
264  * @return a GeodesicLine3 object defining the geodesic.
265  **********************************************************************/
266  GeodesicLine3 Line(real bet1, real omg1, real alp1) const;
267  /**
268  * Specify the behavior of umbilical geodesics
269  *
270  * @param[in] numbalt the new value of the \e umbalt flag.
271  *
272  * If \e umbalt is true (resp. false) then the latitude (resp. longitude)
273  * of an umbilical geodesic is the librating coordinate and the longitude
274  * (resp. latitude) is the rotating coordinate. This has no effect for
275  * biaxial ellipsoids. In this case the latitude (resp. longitude) is
276  * always the librating coordinate for oblate (resp. prolate) ellipsoids.
277  **********************************************************************/
278  void umbalt(bool numbalt) {
279  if (_t.k2() > 0 && _t.kp2() > 0) _umbalt = numbalt;
280  }
281  /**
282  * @return the value of the \e umbalt flag; see umbalt(bool)).
283  **********************************************************************/
284  bool umbalt() const { return _umbalt; }
285  };
286 
287  } // namespace Triaxial
288 } // namespace GeographicLib
289 
290 #if defined(_MSC_VER)
291 # pragma warning (pop)
292 #endif
293 
294 // Include this because all the Geodesic3 methods return a GeodesicLine3.
296 
297 #endif // GEOGRAPHICLIB_GEODESIC3_HPP
const Ellipsoid3 & t() const
Definition: Geodesic3.hpp:187
#define GEOGRAPHICLIB_EXPORT
Definition: Constants.hpp:59
Header for GeographicLib::Triaxial::GeodesicLine3 class.
AngleT< Math::real > Angle
Definition: Angle.hpp:760
The direct geodesic problem for a triaxial ellipsoid.
Namespace for GeographicLib.
Definition: Accumulator.cpp:12
Header for GeographicLib::Triaxial::Ellipsoid3 class.
GeographicLib::Math::real real
Definition: Geod3Solve.cpp:25
The solution of the geodesic problem for a triaxial ellipsoid.
Definition: Geodesic3.hpp:65