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