GeographicLib 2.1.2
Rhumb.cpp
Go to the documentation of this file.
1/**
2 * \file Rhumb.cpp
3 * \brief Implementation for GeographicLib::Rhumb and GeographicLib::RhumbLine
4 * classes
5 *
6 * Copyright (c) Charles Karney (2014-2022) <charles@karney.com> and licensed
7 * under the MIT/X11 License. For more information, see
8 * https://geographiclib.sourceforge.io/
9 **********************************************************************/
10
11#include <algorithm>
13
14#if defined(_MSC_VER)
15// Squelch warnings about enum-float expressions
16# pragma warning (disable: 5055)
17#endif
18
19namespace GeographicLib {
20
21 using namespace std;
22
23 Rhumb::Rhumb(real a, real f, bool exact)
24 : _ell(a, f)
25 , _exact(exact)
26 , _c2(_ell.Area() / (2 * Math::td))
27 {
28 // Generated by Maxima on 2015-05-15 08:24:04-04:00
29#if GEOGRAPHICLIB_RHUMBAREA_ORDER == 4
30 static const real coeff[] = {
31 // R[0]/n^0, polynomial in n of order 4
32 691, 7860, -20160, 18900, 0, 56700,
33 // R[1]/n^1, polynomial in n of order 3
34 1772, -5340, 6930, -4725, 14175,
35 // R[2]/n^2, polynomial in n of order 2
36 -1747, 1590, -630, 4725,
37 // R[3]/n^3, polynomial in n of order 1
38 104, -31, 315,
39 // R[4]/n^4, polynomial in n of order 0
40 -41, 420,
41 }; // count = 20
42#elif GEOGRAPHICLIB_RHUMBAREA_ORDER == 5
43 static const real coeff[] = {
44 // R[0]/n^0, polynomial in n of order 5
45 -79036, 22803, 259380, -665280, 623700, 0, 1871100,
46 // R[1]/n^1, polynomial in n of order 4
47 41662, 58476, -176220, 228690, -155925, 467775,
48 // R[2]/n^2, polynomial in n of order 3
49 18118, -57651, 52470, -20790, 155925,
50 // R[3]/n^3, polynomial in n of order 2
51 -23011, 17160, -5115, 51975,
52 // R[4]/n^4, polynomial in n of order 1
53 5480, -1353, 13860,
54 // R[5]/n^5, polynomial in n of order 0
55 -668, 5775,
56 }; // count = 27
57#elif GEOGRAPHICLIB_RHUMBAREA_ORDER == 6
58 static const real coeff[] = {
59 // R[0]/n^0, polynomial in n of order 6
60 128346268, -107884140, 31126095, 354053700, -908107200, 851350500, 0,
61 2554051500LL,
62 // R[1]/n^1, polynomial in n of order 5
63 -114456994, 56868630, 79819740, -240540300, 312161850, -212837625,
64 638512875,
65 // R[2]/n^2, polynomial in n of order 4
66 51304574, 24731070, -78693615, 71621550, -28378350, 212837625,
67 // R[3]/n^3, polynomial in n of order 3
68 1554472, -6282003, 4684680, -1396395, 14189175,
69 // R[4]/n^4, polynomial in n of order 2
70 -4913956, 3205800, -791505, 8108100,
71 // R[5]/n^5, polynomial in n of order 1
72 1092376, -234468, 2027025,
73 // R[6]/n^6, polynomial in n of order 0
74 -313076, 2027025,
75 }; // count = 35
76#elif GEOGRAPHICLIB_RHUMBAREA_ORDER == 7
77 static const real coeff[] = {
78 // R[0]/n^0, polynomial in n of order 7
79 -317195588, 385038804, -323652420, 93378285, 1062161100, -2724321600LL,
80 2554051500LL, 0, 7662154500LL,
81 // R[1]/n^1, polynomial in n of order 6
82 258618446, -343370982, 170605890, 239459220, -721620900, 936485550,
83 -638512875, 1915538625,
84 // R[2]/n^2, polynomial in n of order 5
85 -248174686, 153913722, 74193210, -236080845, 214864650, -85135050,
86 638512875,
87 // R[3]/n^3, polynomial in n of order 4
88 114450437, 23317080, -94230045, 70270200, -20945925, 212837625,
89 // R[4]/n^4, polynomial in n of order 3
90 15445736, -103193076, 67321800, -16621605, 170270100,
91 // R[5]/n^5, polynomial in n of order 2
92 -27766753, 16385640, -3517020, 30405375,
93 // R[6]/n^6, polynomial in n of order 1
94 4892722, -939228, 6081075,
95 // R[7]/n^7, polynomial in n of order 0
96 -3189007, 14189175,
97 }; // count = 44
98#elif GEOGRAPHICLIB_RHUMBAREA_ORDER == 8
99 static const real coeff[] = {
100 // R[0]/n^0, polynomial in n of order 8
101 71374704821LL, -161769749880LL, 196369790040LL, -165062734200LL,
102 47622925350LL, 541702161000LL, -1389404016000LL, 1302566265000LL, 0,
103 3907698795000LL,
104 // R[1]/n^1, polynomial in n of order 7
105 -13691187484LL, 65947703730LL, -87559600410LL, 43504501950LL,
106 61062101100LL, -184013329500LL, 238803815250LL, -162820783125LL,
107 488462349375LL,
108 // R[2]/n^2, polynomial in n of order 6
109 30802104839LL, -63284544930LL, 39247999110LL, 18919268550LL,
110 -60200615475LL, 54790485750LL, -21709437750LL, 162820783125LL,
111 // R[3]/n^3, polynomial in n of order 5
112 -8934064508LL, 5836972287LL, 1189171080, -4805732295LL, 3583780200LL,
113 -1068242175, 10854718875LL,
114 // R[4]/n^4, polynomial in n of order 4
115 50072287748LL, 3938662680LL, -26314234380LL, 17167059000LL,
116 -4238509275LL, 43418875500LL,
117 // R[5]/n^5, polynomial in n of order 3
118 359094172, -9912730821LL, 5849673480LL, -1255576140, 10854718875LL,
119 // R[6]/n^6, polynomial in n of order 2
120 -16053944387LL, 8733508770LL, -1676521980, 10854718875LL,
121 // R[7]/n^7, polynomial in n of order 1
122 930092876, -162639357, 723647925,
123 // R[8]/n^8, polynomial in n of order 0
124 -673429061, 1929727800,
125 }; // count = 54
126#else
127#error "Bad value for GEOGRAPHICLIB_RHUMBAREA_ORDER"
128#endif
129 static_assert(sizeof(coeff) / sizeof(real) ==
130 ((maxpow_ + 1) * (maxpow_ + 4))/2,
131 "Coefficient array size mismatch for Rhumb");
132 real d = 1;
133 int o = 0;
134 for (int l = 0; l <= maxpow_; ++l) {
135 int m = maxpow_ - l;
136 // R[0] is just an integration constant so it cancels when evaluating a
137 // definite integral. So don't bother computing it. It won't be used
138 // when invoking SinCosSeries.
139 if (l)
140 _rR[l] = d * Math::polyval(m, coeff + o, _ell._n) / coeff[o + m + 1];
141 o += m + 2;
142 d *= _ell._n;
143 }
144 // Post condition: o == sizeof(alpcoeff) / sizeof(real)
145 }
146
148 static const Rhumb
149 wgs84(Constants::WGS84_a(), Constants::WGS84_f(), false);
150 return wgs84;
151 }
152
153 void Rhumb::GenInverse(real lat1, real lon1, real lat2, real lon2,
154 unsigned outmask,
155 real& s12, real& azi12, real& S12) const {
156 real
157 lon12 = Math::AngDiff(lon1, lon2),
158 psi1 = _ell.IsometricLatitude(lat1),
159 psi2 = _ell.IsometricLatitude(lat2),
160 psi12 = psi2 - psi1,
161 h = hypot(lon12, psi12);
162 if (outmask & AZIMUTH)
163 azi12 = Math::atan2d(lon12, psi12);
164 if (outmask & DISTANCE) {
165 real dmudpsi = DIsometricToRectifying(psi2, psi1);
166 s12 = h * dmudpsi * _ell.QuarterMeridian() / Math::qd;
167 }
168 if (outmask & AREA)
169 S12 = _c2 * lon12 *
170 MeanSinXi(psi2 * Math::degree(), psi1 * Math::degree());
171 }
172
173 RhumbLine Rhumb::Line(real lat1, real lon1, real azi12) const
174 { return RhumbLine(*this, lat1, lon1, azi12); }
175
176 void Rhumb::GenDirect(real lat1, real lon1, real azi12, real s12,
177 unsigned outmask,
178 real& lat2, real& lon2, real& S12) const
179 { Line(lat1, lon1, azi12).GenPosition(s12, outmask, lat2, lon2, S12); }
180
181 Math::real Rhumb::DE(real x, real y) const {
182 const EllipticFunction& ei = _ell._ell;
183 real d = x - y;
184 if (x * y <= 0)
185 return d != 0 ? (ei.E(x) - ei.E(y)) / d : 1;
186 // See DLMF: Eqs (19.11.2) and (19.11.4) letting
187 // theta -> x, phi -> -y, psi -> z
188 //
189 // (E(x) - E(y)) / d = E(z)/d - k2 * sin(x) * sin(y) * sin(z)/d
190 //
191 // tan(z/2) = (sin(x)*Delta(y) - sin(y)*Delta(x)) / (cos(x) + cos(y))
192 // = d * Dsin(x,y) * (sin(x) + sin(y))/(cos(x) + cos(y)) /
193 // (sin(x)*Delta(y) + sin(y)*Delta(x))
194 // = t = d * Dt
195 // sin(z) = 2*t/(1+t^2); cos(z) = (1-t^2)/(1+t^2)
196 // Alt (this only works for |z| <= pi/2 -- however, this conditions holds
197 // if x*y > 0):
198 // sin(z) = d * Dsin(x,y) * (sin(x) + sin(y))/
199 // (sin(x)*cos(y)*Delta(y) + sin(y)*cos(x)*Delta(x))
200 // cos(z) = sqrt((1-sin(z))*(1+sin(z)))
201 real sx = sin(x), sy = sin(y), cx = cos(x), cy = cos(y);
202 real Dt = Dsin(x, y) * (sx + sy) /
203 ((cx + cy) * (sx * ei.Delta(sy, cy) + sy * ei.Delta(sx, cx))),
204 t = d * Dt, Dsz = 2 * Dt / (1 + t*t),
205 sz = d * Dsz, cz = (1 - t) * (1 + t) / (1 + t*t);
206 return ((sz != 0 ? ei.E(sz, cz, ei.Delta(sz, cz)) / sz : 1)
207 - ei.k2() * sx * sy) * Dsz;
208 }
209
210 Math::real Rhumb::DRectifying(real latx, real laty) const {
211 real
212 tbetx = _ell._f1 * Math::tand(latx),
213 tbety = _ell._f1 * Math::tand(laty);
214 return (Math::pi()/2) * _ell._b * _ell._f1 * DE(atan(tbetx), atan(tbety))
215 * Dtan(latx, laty) * Datan(tbetx, tbety) / _ell.QuarterMeridian();
216 }
217
218 Math::real Rhumb::DIsometric(real latx, real laty) const {
219 real
220 phix = latx * Math::degree(), tx = Math::tand(latx),
221 phiy = laty * Math::degree(), ty = Math::tand(laty);
222 return Dasinh(tx, ty) * Dtan(latx, laty)
223 - Deatanhe(sin(phix), sin(phiy)) * Dsin(phix, phiy);
224 }
225
226 Math::real Rhumb::SinCosSeries(bool sinp,
227 real x, real y, const real c[], int n) {
228 // N.B. n >= 0 and c[] has n+1 elements 0..n, of which c[0] is ignored.
229 //
230 // Use Clenshaw summation to evaluate
231 // m = (g(x) + g(y)) / 2 -- mean value
232 // s = (g(x) - g(y)) / (x - y) -- average slope
233 // where
234 // g(x) = sum(c[j]*SC(2*j*x), j = 1..n)
235 // SC = sinp ? sin : cos
236 // CS = sinp ? cos : sin
237 //
238 // This function returns only s; m is discarded.
239 //
240 // Write
241 // t = [m; s]
242 // t = sum(c[j] * f[j](x,y), j = 1..n)
243 // where
244 // f[j](x,y) = [ (SC(2*j*x)+SC(2*j*y))/2 ]
245 // [ (SC(2*j*x)-SC(2*j*y))/d ]
246 //
247 // = [ cos(j*d)*SC(j*p) ]
248 // [ +/-(2/d)*sin(j*d)*CS(j*p) ]
249 // (+/- = sinp ? + : -) and
250 // p = x+y, d = x-y
251 //
252 // f[j+1](x,y) = A * f[j](x,y) - f[j-1](x,y)
253 //
254 // A = [ 2*cos(p)*cos(d) -sin(p)*sin(d)*d]
255 // [ -4*sin(p)*sin(d)/d 2*cos(p)*cos(d) ]
256 //
257 // Let b[n+1] = b[n+2] = [0 0; 0 0]
258 // b[j] = A * b[j+1] - b[j+2] + c[j] * I for j = n..1
259 // t = (c[0] * I - b[2]) * f[0](x,y) + b[1] * f[1](x,y)
260 // c[0] is not accessed for s = t[2]
261 real p = x + y, d = x - y,
262 cp = cos(p), cd = cos(d),
263 sp = sin(p), sd = d != 0 ? sin(d)/d : 1,
264 m = 2 * cp * cd, s = sp * sd;
265 // 2x2 matrices stored in row-major order
266 const real a[4] = {m, -s * d * d, -4 * s, m};
267 real ba[4] = {0, 0, 0, 0};
268 real bb[4] = {0, 0, 0, 0};
269 real* b1 = ba;
270 real* b2 = bb;
271 if (n > 0) b1[0] = b1[3] = c[n];
272 for (int j = n - 1; j > 0; --j) { // j = n-1 .. 1
273 swap(b1, b2);
274 // b1 = A * b2 - b1 + c[j] * I
275 b1[0] = a[0] * b2[0] + a[1] * b2[2] - b1[0] + c[j];
276 b1[1] = a[0] * b2[1] + a[1] * b2[3] - b1[1];
277 b1[2] = a[2] * b2[0] + a[3] * b2[2] - b1[2];
278 b1[3] = a[2] * b2[1] + a[3] * b2[3] - b1[3] + c[j];
279 }
280 // Here are the full expressions for m and s
281 // m = (c[0] - b2[0]) * f01 - b2[1] * f02 + b1[0] * f11 + b1[1] * f12;
282 // s = - b2[2] * f01 + (c[0] - b2[3]) * f02 + b1[2] * f11 + b1[3] * f12;
283 if (sinp) {
284 // real f01 = 0, f02 = 0;
285 real f11 = cd * sp, f12 = 2 * sd * cp;
286 // m = b1[0] * f11 + b1[1] * f12;
287 s = b1[2] * f11 + b1[3] * f12;
288 } else {
289 // real f01 = 1, f02 = 0;
290 real f11 = cd * cp, f12 = - 2 * sd * sp;
291 // m = c[0] - b2[0] + b1[0] * f11 + b1[1] * f12;
292 s = - b2[2] + b1[2] * f11 + b1[3] * f12;
293 }
294 return s;
295 }
296
297 Math::real Rhumb::DConformalToRectifying(real chix, real chiy) const {
298 return 1 + SinCosSeries(true, chix, chiy,
299 _ell.ConformalToRectifyingCoeffs(), tm_maxord);
300 }
301
302 Math::real Rhumb::DRectifyingToConformal(real mux, real muy) const {
303 return 1 - SinCosSeries(true, mux, muy,
304 _ell.RectifyingToConformalCoeffs(), tm_maxord);
305 }
306
307 Math::real Rhumb::DIsometricToRectifying(real psix, real psiy) const {
308 if (_exact) {
309 real
310 latx = _ell.InverseIsometricLatitude(psix),
311 laty = _ell.InverseIsometricLatitude(psiy);
312 return DRectifying(latx, laty) / DIsometric(latx, laty);
313 } else {
314 psix *= Math::degree();
315 psiy *= Math::degree();
316 return DConformalToRectifying(gd(psix), gd(psiy)) * Dgd(psix, psiy);
317 }
318 }
319
320 Math::real Rhumb::DRectifyingToIsometric(real mux, real muy) const {
321 real
322 latx = _ell.InverseRectifyingLatitude(mux/Math::degree()),
323 laty = _ell.InverseRectifyingLatitude(muy/Math::degree());
324 return _exact ?
325 DIsometric(latx, laty) / DRectifying(latx, laty) :
326 Dgdinv(Math::taupf(Math::tand(latx), _ell._es),
327 Math::taupf(Math::tand(laty), _ell._es)) *
328 DRectifyingToConformal(mux, muy);
329 }
330
331 Math::real Rhumb::MeanSinXi(real psix, real psiy) const {
332 return Dlog(cosh(psix), cosh(psiy)) * Dcosh(psix, psiy)
333 + SinCosSeries(false, gd(psix), gd(psiy), _rR, maxpow_) * Dgd(psix, psiy);
334 }
335
336 RhumbLine::RhumbLine(const Rhumb& rh, real lat1, real lon1, real azi12)
337 : _rh(rh)
338 , _lat1(Math::LatFix(lat1))
339 , _lon1(lon1)
340 , _azi12(Math::AngNormalize(azi12))
341 {
342 real alp12 = _azi12 * Math::degree();
343 _salp = _azi12 == -Math::hd ? 0 : sin(alp12);
344 _calp = fabs(_azi12) == Math::qd ? 0 : cos(alp12);
345 _mu1 = _rh._ell.RectifyingLatitude(lat1);
346 _psi1 = _rh._ell.IsometricLatitude(lat1);
347 _r1 = _rh._ell.CircleRadius(lat1);
348 }
349
350 void RhumbLine::GenPosition(real s12, unsigned outmask,
351 real& lat2, real& lon2, real& S12) const {
352 real
353 mu12 = s12 * _calp * Math::qd / _rh._ell.QuarterMeridian(),
354 mu2 = _mu1 + mu12;
355 real psi2, lat2x, lon2x;
356 if (fabs(mu2) <= Math::qd) {
357 if (_calp != 0) {
358 lat2x = _rh._ell.InverseRectifyingLatitude(mu2);
359 real psi12 = _rh.DRectifyingToIsometric( mu2 * Math::degree(),
360 _mu1 * Math::degree()) * mu12;
361 lon2x = _salp * psi12 / _calp;
362 psi2 = _psi1 + psi12;
363 } else {
364 lat2x = _lat1;
365 lon2x = _salp * s12 / (_r1 * Math::degree());
366 psi2 = _psi1;
367 }
368 if (outmask & AREA)
369 S12 = _rh._c2 * lon2x *
370 _rh.MeanSinXi(_psi1 * Math::degree(), psi2 * Math::degree());
371 lon2x = outmask & LONG_UNROLL ? _lon1 + lon2x :
373 } else {
374 // Reduce to the interval [-180, 180)
375 mu2 = Math::AngNormalize(mu2);
376 // Deal with points on the anti-meridian
377 if (fabs(mu2) > Math::qd) mu2 = Math::AngNormalize(Math::hd - mu2);
378 lat2x = _rh._ell.InverseRectifyingLatitude(mu2);
379 lon2x = Math::NaN();
380 if (outmask & AREA)
381 S12 = Math::NaN();
382 }
383 if (outmask & LATITUDE) lat2 = lat2x;
384 if (outmask & LONGITUDE) lon2 = lon2x;
385 }
386
387} // namespace GeographicLib
GeographicLib::Math::real real
Definition: GeodSolve.cpp:31
Header for GeographicLib::Rhumb and GeographicLib::RhumbLine classes.
Math::real IsometricLatitude(real phi) const
Definition: Ellipsoid.cpp:89
Math::real QuarterMeridian() const
Definition: Ellipsoid.cpp:42
Math::real InverseIsometricLatitude(real psi) const
Definition: Ellipsoid.cpp:93
Math::real InverseRectifyingLatitude(real mu) const
Definition: Ellipsoid.cpp:70
Elliptic integrals and functions.
Math::real Delta(real sn, real cn) const
Mathematical functions needed by GeographicLib.
Definition: Math.hpp:76
static T degree()
Definition: Math.hpp:200
static T tand(T x)
Definition: Math.cpp:172
static T atan2d(T y, T x)
Definition: Math.cpp:183
static T AngNormalize(T x)
Definition: Math.cpp:71
static T pi()
Definition: Math.hpp:190
static T NaN()
Definition: Math.cpp:250
static T polyval(int N, const T p[], T x)
Definition: Math.hpp:271
static T AngDiff(T x, T y, T &e)
Definition: Math.cpp:82
@ hd
degrees per half turn
Definition: Math.hpp:144
@ qd
degrees per quarter turn
Definition: Math.hpp:141
Find a sequence of points on a single rhumb line.
Definition: Rhumb.hpp:458
void GenPosition(real s12, unsigned outmask, real &lat2, real &lon2, real &S12) const
Definition: Rhumb.cpp:350
Solve of the direct and inverse rhumb problems.
Definition: Rhumb.hpp:66
RhumbLine Line(real lat1, real lon1, real azi12) const
Definition: Rhumb.cpp:173
Rhumb(real a, real f, bool exact=true)
Definition: Rhumb.cpp:23
friend class RhumbLine
Definition: Rhumb.hpp:69
static const Rhumb & WGS84()
Definition: Rhumb.cpp:147
Namespace for GeographicLib.
Definition: Accumulator.cpp:12
void swap(GeographicLib::NearestNeighbor< dist_t, pos_t, distfun_t > &a, GeographicLib::NearestNeighbor< dist_t, pos_t, distfun_t > &b)