GeographicLib 2.6
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 *
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
#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