GeographicLib 2.7

Loading...
Searching...
No Matches
DAuxLatitude.cpp
Go to the documentation of this file.
1/**
2 * \file DAuxLatitude.cpp
3 * \brief Implementation for the GeographicLib::DAuxLatitude class.
4 *
5 * This file is an implementation of the methods described in
6 * - C. F. F. Karney,
7 * <a href="https://doi.org/10.1080/00396265.2023.2217604">
8 * On auxiliary latitudes,</a>
9 * Survey Review 56(395), 165--180 (2024);
10 * preprint
11 * <a href="https://arxiv.org/abs/2212.05818">arXiv:2212.05818</a>.
12 * .
13 * Copyright (c) Charles Karney (2022-2024) <karney@alum.mit.edu> and licensed
14 * under the MIT/X11 License. For more information, see
15 * https://geographiclib.sourceforge.io/
16 **********************************************************************/
17
18#include <GeographicLib/DAuxLatitude.hpp>
19#include <GeographicLib/EllipticFunction.hpp>
20
21 namespace GeographicLib {
22
23 using namespace std;
24
25 Math::real DAuxLatitude::DRectifying(const AuxAngle& phi1,
26 const AuxAngle& phi2)
27 const {
28 // Stipulate that phi1 and phi2 are in [-90d, 90d]
29 real x = phi1.radians(), y = phi2.radians();
30 if (x == y) {
31 real d;
32 AuxAngle mu1(base::Rectifying(phi1, &d));
33 real tphi1 = phi1.tan(), tmu1 = mu1.tan();
34 return
35 isfinite(tphi1) ? d * Math::sq(base::sc(tphi1)/base::sc(tmu1)) : 1/d;
36 } else if (x * y < 0)
37 return (base::Rectifying(phi2).radians() -
38 base::Rectifying(phi1).radians()) / (y - x);
39 else {
40 AuxAngle bet1(base::Parametric(phi1)), bet2(base::Parametric(phi2));
41 real dEdbet = DE(bet1, bet2), dbetdphi = DParametric(phi1, phi2);
42 return base::_b * dEdbet / base::RectifyingRadius(true) * dbetdphi;
43 }
44 }
45
46 Math::real DAuxLatitude::DParametric(const AuxAngle& phi1,
47 const AuxAngle& phi2)
48 const {
49 real tx = phi1.tan(), ty = phi2.tan(), r;
50 // DbetaDphi = Datan(fm1*tx, fm1*ty) * fm1 / Datan(tx, ty)
51 // Datan(x, y) = 1/(1 + x^2), for x = y
52 // = (atan(y) - atan(x)) / (y-x), for x*y < 0
53 // = atan( (y-x) / (1 + x*y) ) / (y-x), for x*y > 0
54 if (!(tx * ty >= 0)) // This includes, e.g., tx = 0, ty = inf
55 r = (atan(base::_fm1 * ty) - atan(base::_fm1 * tx)) /
56 (atan(ty) - atan(tx));
57 else if (tx == ty) { // This includes the case tx = ty = inf
58 tx *= tx;
59 if (tx <= 1)
60 r = base::_fm1 * (1 + tx) / (1 + base::_e2m1 * tx);
61 else {
62 tx = 1/tx;
63 r = base::_fm1 * (1 + tx) / (base::_e2m1 + tx);
64 }
65 } else {
66 if (tx * ty <= 1)
67 r = atan2(base::_fm1 * (ty - tx), 1 + base::_e2m1 * tx * ty)
68 / atan2( ty - tx , 1 + tx * ty);
69 else {
70 tx = 1/tx; ty = 1/ty;
71 r = atan2(base::_fm1 * (ty - tx), base::_e2m1 + tx * ty)
72 / atan2( ty - tx , 1 + tx * ty);
73 }
74 }
75 return r;
76 }
77
78 Math::real DAuxLatitude::DE(const AuxAngle& X, const AuxAngle& Y) const {
79 AuxAngle Xn(X.normalized()), Yn(Y.normalized());
80 // We assume that X and Y are in [-90d, 90d] and have the same sign
81 // If not we would include
82 // if (Xn.y() * Yn.y() < 0)
83 // return d != 0 ? (E(X) - E(Y)) / d : 1;
84
85 // The general formula fails for x = y = 0d and x = y = 90d. Probably this
86 // is fixable (the formula works for other x = y. But let's also stipulate
87 // that x != y .
88
89 // Make both positive, so we can do the swap a <-> b trick
90 Xn.y() = fabs(Xn.y()); Yn.y() = fabs(Yn.y());
91 real k2 = -base::_e12;
92 bool flip = base::_f < 0;
93 // Switch prolate to oblate; we then can use the formulas for k2 < 0
94 if (flip) {
95 swap(Xn.x(), Xn.y());
96 swap(Yn.x(), Yn.y());
97 k2 = base::_e2;
98 }
99 real x = Xn.radians(), y = Yn.radians(), d = y - x,
100 sx = Xn.y(), sy = Yn.y(), cx = Xn.x(), cy = Yn.x();
101 // See DLMF: Eqs (19.11.2) and (19.11.4) letting
102 // theta -> x, phi -> -y, psi -> z
103 //
104 // (E(y) - E(x)) / d = E(z)/d - k2 * sin(x) * sin(y) * sin(z)/d
105 // = (E(z)/sin(z) - k2 * sin(x) * sin(y)) * sin(z)/d
106 // tan(z/2) = (sin(x)*Delta(y) - sin(y)*Delta(x)) / (cos(x) + cos(y))
107 // = d * Dsin(x,y) * (sin(x) + sin(y))/(cos(x) + cos(y)) /
108 // (sin(x)*Delta(y) + sin(y)*Delta(x))
109 // = t = d * Dt
110 // Delta(x) = sqrt(1 - k2 * sin(x)^2)
111 // sin(z) = 2*t/(1+t^2); cos(z) = (1-t^2)/(1+t^2)
112 real Dt = Dsin(x, y) * (sx + sy) /
113 ((cx + cy) * (sx * sqrt(1 - k2 * sy*sy) + sy * sqrt(1 - k2 * sx*sx))),
114 t = d * Dt, Dsz = 2 * Dt / (1 + t*t),
115 sz = d * Dsz, cz = (1 - t) * (1 + t) / (1 + t*t),
116 sz2 = sz*sz, cz2 = cz*cz, dz2 = 1 - k2 * sz2,
117 // E(z)/sin(z)
118 Ezbsz = (EllipticFunction::RF(cz2, dz2, 1)
119 - k2 * sz2 * EllipticFunction::RD(cz2, dz2, 1) / 3);
120 return (Ezbsz - k2 * sx * sy) * Dsz / (flip ? 1 - base::_f : 1);
121 }
122
123 /// \cond SKIP
124 Math::real DAuxLatitude::Dsn(real x, real y) {
125 real sc1 = base::sc(x);
126 if (x == y) return 1 / (sc1 * (1 + x*x));
127 real sc2 = base::sc(y), sn1 = base::sn(x), sn2 = base::sn(y);
128 return x * y > 0 ?
129 (sn1/sc2 + sn2/sc1) / ((sn1 + sn2) * sc1 * sc2) :
130 (sn2 - sn1) / (y - x);
131 }
132 Math::real DAuxLatitude::Datan(real x, real y) {
133 real d = y - x, xy = x*y;
134 return x == y ? 1 / (1 + xy) :
135 (isinf(xy) && xy > 0 ? 0 :
136 (2 * xy > -1 ? atan( d / (1 + xy) ) : atan(y) - atan(x)) / d);
137 }
138 Math::real DAuxLatitude::Dasinh(real x, real y) {
139 real d = y - x, xy = x*y, hx = base::sc(x), hy = base::sc(y);
140 // KF formula for x*y < 0 is asinh(y*hx - x*hy) / (y - x)
141 // but this has problem if x*y overflows to -inf
142 return x == y ? 1 / hx :
143 (isinf(d) ? 0 :
144 (xy > 0 ? asinh(d * (x*y < 1 ? (x + y) / (x*hy + y*hx) :
145 (1/x + 1/y) / (hy/y + hx/x))) :
146 asinh(y) - asinh(x)) / d);
147 }
148 Math::real DAuxLatitude::Dh(real x, real y) {
149 if (isnan(x + y))
150 return x + y; // N.B. nan for inf-inf
151 if (isinf(x))
152 return copysign(1/real(2), x);
153 if (isinf(y))
154 return copysign(1/real(2), y);
155 real sx = base::sn(x), sy = base::sn(y), d = sx*x + sy*y;
156 if (d / 2 == 0)
157 return (x + y) / 2; // Handle underflow
158 if (x * y <= 0)
159 return (h(y) - h(x)) / (y - x); // Does not include x = y = 0
160 real scx = base::sc(x), scy = base::sc(y);
161 return ((x + y) / (2 * d)) *
162 (Math::sq(sx*sy) + Math::sq(sy/scx) + Math::sq(sx/scy));
163 }
164 Math::real DAuxLatitude::Datanhee(real x, real y) const {
165 // atan(e*sn(tphi))/e:
166 // Datan(e*sn(x),e*sn(y))*Dsn(x,y)/Datan(x,y)
167 // asinh(e1*sn(fm1*tphi)):
168 // Dasinh(e1*sn(fm1*x)), e1*sn(fm1*y)) *
169 // e1 * Dsn(fm1*x, fm1*y) *fm1 / (e * Datan(x,y))
170 // = Dasinh(e1*sn(fm1*x)), e1*sn(fm1*y)) *
171 // Dsn(fm1*x, fm1*y) / Datan(x,y)
172 return base::_f < 0 ?
173 Datan(base::_e * base::sn(x), base::_e * base::sn(y)) * Dsn(x, y) :
174 Dasinh(base::_e1 * base::sn(base::_fm1 * x),
175 base::_e1 * base::sn(base::_fm1 * y)) *
176 Dsn(base::_fm1 * x, base::_fm1 * y);
177 }
178 /// \endcond
179
180 Math::real DAuxLatitude::DIsometric(const AuxAngle& phi1,
181 const AuxAngle& phi2)
182 const {
183 // psi = asinh(tan(phi)) - e^2 * atanhee(tan(phi))
184 real tphi1 = phi1.tan(), tphi2 = phi2.tan();
185 return isnan(tphi1) || isnan(tphi2) ? numeric_limits<real>::quiet_NaN() :
186 (isinf(tphi1) || isinf(tphi2) ? numeric_limits<real>::infinity() :
187 (Dasinh(tphi1, tphi2) - base::_e2 * Datanhee(tphi1, tphi2)) /
188 Datan(tphi1, tphi2));
189 }
190
191 Math::real DAuxLatitude::DConvert(int auxin, int auxout,
192 const AuxAngle& zeta1,
193 const AuxAngle& zeta2)
194 const {
195 int k = base::ind(auxout, auxin);
196 if (k < 0) return numeric_limits<real>::quiet_NaN();
197 if (auxin == auxout) return 1;
198 if ( isnan(base::_c[base::Lmax * (k + 1) - 1]) )
199 base::fillcoeff(auxin, auxout, k);
200 AuxAngle zeta1n(zeta1.normalized()), zeta2n(zeta2.normalized());
201 return 1 + DClenshaw(true, zeta2n.radians() - zeta1n.radians(),
202 zeta1n.y(), zeta1n.x(), zeta2n.y(), zeta2n.x(),
203 base::_c + base::Lmax * k, base::Lmax);
204 }
205
206 Math::real DAuxLatitude::DClenshaw(bool sinp, real Delta,
207 real szeta1, real czeta1,
208 real szeta2, real czeta2,
209 const real c[], int K) {
210 // Evaluate
211 // (Clenshaw(sinp, szeta2, czeta2, c, K) -
212 // Clenshaw(sinp, szeta1, czeta1, c, K)) / Delta
213 // or
214 // sum(c[k] * (sin( (2*k+2) * zeta2) - sin( (2*k+2) * zeta2)), i, 0, K-1)
215 // / Delta
216 // (if !sinp, then change sin->cos here.)
217 //
218 // Delta is EITHER 1, giving the plain difference OR (zeta2 - zeta1) in
219 // radians, giving the divided difference. Other values will give
220 // nonsense.
221 //
222 int k = K;
223 // suffixes a b denote [1,1], [2,1] elements of matrix/vector
224 real D2 = Delta * Delta,
225 czetap = czeta2 * czeta1 - szeta2 * szeta1,
226 szetap = szeta2 * czeta1 + czeta2 * szeta1,
227 czetam = czeta2 * czeta1 + szeta2 * szeta1,
228 // sin(zetam) / Delta
229 szetamd = (Delta == 1 ? szeta2 * czeta1 - czeta2 * szeta1 :
230 (Delta != 0 ? sin(Delta) / Delta : 1)),
231 Xa = 2 * czetap * czetam,
232 Xb = -2 * szetap * szetamd,
233 u0a = 0, u0b = 0, u1a = 0, u1b = 0; // accumulators for sum
234 for (--k; k >= 0; --k) {
235 // temporary real = X . U0 - U1 + c[k] * I
236 real ta = Xa * u0a + D2 * Xb * u0b - u1a + c[k],
237 tb = Xb * u0a + Xa * u0b - u1b;
238 // U1 = U0; U0 = real
239 u1a = u0a; u0a = ta;
240 u1b = u0b; u0b = tb;
241 }
242 // P = U0 . F[0] - U1 . F[-1]
243 // if sinp:
244 // F[0] = [ sin(2*zeta2) + sin(2*zeta1),
245 // (sin(2*zeta2) - sin(2*zeta1)) / Delta]
246 // = 2 * [ szetap * czetam, czetap * szetamd ]
247 // F[-1] = [0, 0]
248 // else:
249 // F[0] = [ cos(2*zeta2) + cos(2*zeta1),
250 // (cos(2*zeta2) - cos(2*zeta1)) / Delta]
251 // = 2 * [ czetap * czetam, -szetap * szetamd ]
252 // F[-1] = [2, 0]
253 real F0a = (sinp ? szetap : czetap) * czetam,
254 F0b = (sinp ? czetap : -szetap) * szetamd,
255 Fm1a = sinp ? 0 : 1; // Fm1b = 0;
256 // Don't both to compute sum...
257 // divided difference (or difference if Delta == 1)
258 return 2 * (F0a * u0b + F0b * u0a - Fm1a * u1b);
259 }
260
261} // namespace GeographicLib
DAuxLatitude.hpp
Header for the GeographicLib::DAuxLatitude class.
EllipticFunction.hpp
Header for GeographicLib::EllipticFunction class.
real
GeographicLib::Math::real real
Definition Geod3Solve.cpp:25
GeographicLib::AuxAngle
An accurate representation of angles.
Definition AuxAngle.hpp:51
GeographicLib::AuxAngle::y
Math::real y() const
Definition AuxAngle.hpp:74
GeographicLib::AuxAngle::x
Math::real x() const
Definition AuxAngle.hpp:79
GeographicLib::AuxAngle::radians
Math::real radians() const
Definition AuxAngle.hpp:232
GeographicLib::AuxAngle::normalized
AuxAngle normalized() const
Definition AuxAngle.cpp:28
GeographicLib::AuxAngle::tan
Math::real tan() const
Definition AuxAngle.hpp:117
GeographicLib::AuxLatitude::Lmax
static const int Lmax
Definition AuxLatitude.hpp:316
GeographicLib::AuxLatitude::RectifyingRadius
Math::real RectifyingRadius(bool exact=false) const
Definition AuxLatitude.cpp:321
GeographicLib::AuxLatitude::Parametric
AuxAngle Parametric(const AuxAngle &phi, real *diff=nullptr) const
Definition AuxLatitude.cpp:84
GeographicLib::AuxLatitude::Rectifying
AuxAngle Rectifying(const AuxAngle &phi, real *diff=nullptr) const
Definition AuxLatitude.cpp:94
GeographicLib::DAuxLatitude::DParametric
Math::real DParametric(const AuxAngle &phi1, const AuxAngle &phi2) const
Definition DAuxLatitude.cpp:46
GeographicLib::DAuxLatitude::DConvert
Math::real DConvert(int auxin, int auxout, const AuxAngle &zeta1, const AuxAngle &zeta2) const
Definition DAuxLatitude.cpp:191
GeographicLib::DAuxLatitude::DIsometric
Math::real DIsometric(const AuxAngle &phi1, const AuxAngle &phi2) const
Definition DAuxLatitude.cpp:180
GeographicLib::DAuxLatitude::DClenshaw
static Math::real DClenshaw(bool sinp, real Delta, real szeta1, real czeta1, real szeta2, real czeta2, const real c[], int K)
Definition DAuxLatitude.cpp:206
GeographicLib::DAuxLatitude::DRectifying
Math::real DRectifying(const AuxAngle &phi1, const AuxAngle &phi2) const
Definition DAuxLatitude.cpp:25
GeographicLib::EllipticFunction::RD
static real RD(real x, real y, real z)
Definition EllipticFunction.cpp:177
GeographicLib::EllipticFunction::RF
static real RF(real x, real y, real z)
Definition EllipticFunction.cpp:24
GeographicLib::Math::sq
static T sq(T x)
Definition Math.hpp:209
GeographicLib::Math::real
double real
Definition Math.hpp:115
GeographicLib
Namespace for GeographicLib.
Definition Accumulator.cpp:12
std::swap
void swap(GeographicLib::NearestNeighbor< dist_t, pos_t, distfun_t > &a, GeographicLib::NearestNeighbor< dist_t, pos_t, distfun_t > &b)
Definition NearestNeighbor.hpp:819