geographiclib 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/AUTHORS +12 -0
- data/LICENSE +24 -0
- data/ext/geographiclib/Accumulator.cpp +23 -0
- data/ext/geographiclib/AlbersEqualArea.cpp +445 -0
- data/ext/geographiclib/AzimuthalEquidistant.cpp +41 -0
- data/ext/geographiclib/CassiniSoldner.cpp +89 -0
- data/ext/geographiclib/CircularEngine.cpp +96 -0
- data/ext/geographiclib/DMS.cpp +381 -0
- data/ext/geographiclib/Ellipsoid.cpp +125 -0
- data/ext/geographiclib/EllipticFunction.cpp +512 -0
- data/ext/geographiclib/GARS.cpp +122 -0
- data/ext/geographiclib/GeoCoords.cpp +175 -0
- data/ext/geographiclib/Geocentric.cpp +172 -0
- data/ext/geographiclib/Geodesic.cpp +1908 -0
- data/ext/geographiclib/GeodesicExact.cpp +927 -0
- data/ext/geographiclib/GeodesicExactC4.cpp +7879 -0
- data/ext/geographiclib/GeodesicLine.cpp +321 -0
- data/ext/geographiclib/GeodesicLineExact.cpp +289 -0
- data/ext/geographiclib/GeographicLib/Accumulator.hpp +184 -0
- data/ext/geographiclib/GeographicLib/AlbersEqualArea.hpp +312 -0
- data/ext/geographiclib/GeographicLib/AzimuthalEquidistant.hpp +139 -0
- data/ext/geographiclib/GeographicLib/CassiniSoldner.hpp +204 -0
- data/ext/geographiclib/GeographicLib/CircularEngine.hpp +195 -0
- data/ext/geographiclib/GeographicLib/Config.h +12 -0
- data/ext/geographiclib/GeographicLib/Constants.hpp +387 -0
- data/ext/geographiclib/GeographicLib/DMS.hpp +370 -0
- data/ext/geographiclib/GeographicLib/Ellipsoid.hpp +534 -0
- data/ext/geographiclib/GeographicLib/EllipticFunction.hpp +692 -0
- data/ext/geographiclib/GeographicLib/GARS.hpp +143 -0
- data/ext/geographiclib/GeographicLib/GeoCoords.hpp +544 -0
- data/ext/geographiclib/GeographicLib/Geocentric.hpp +267 -0
- data/ext/geographiclib/GeographicLib/Geodesic.hpp +970 -0
- data/ext/geographiclib/GeographicLib/GeodesicExact.hpp +862 -0
- data/ext/geographiclib/GeographicLib/GeodesicLine.hpp +701 -0
- data/ext/geographiclib/GeographicLib/GeodesicLineExact.hpp +667 -0
- data/ext/geographiclib/GeographicLib/Geohash.hpp +180 -0
- data/ext/geographiclib/GeographicLib/Geoid.hpp +472 -0
- data/ext/geographiclib/GeographicLib/Georef.hpp +160 -0
- data/ext/geographiclib/GeographicLib/Gnomonic.hpp +206 -0
- data/ext/geographiclib/GeographicLib/GravityCircle.hpp +301 -0
- data/ext/geographiclib/GeographicLib/GravityModel.hpp +520 -0
- data/ext/geographiclib/GeographicLib/LambertConformalConic.hpp +313 -0
- data/ext/geographiclib/GeographicLib/LocalCartesian.hpp +236 -0
- data/ext/geographiclib/GeographicLib/MGRS.hpp +355 -0
- data/ext/geographiclib/GeographicLib/MagneticCircle.hpp +178 -0
- data/ext/geographiclib/GeographicLib/MagneticModel.hpp +347 -0
- data/ext/geographiclib/GeographicLib/Math.hpp +920 -0
- data/ext/geographiclib/GeographicLib/NormalGravity.hpp +350 -0
- data/ext/geographiclib/GeographicLib/OSGB.hpp +249 -0
- data/ext/geographiclib/GeographicLib/PolarStereographic.hpp +150 -0
- data/ext/geographiclib/GeographicLib/PolygonArea.hpp +288 -0
- data/ext/geographiclib/GeographicLib/Rhumb.hpp +589 -0
- data/ext/geographiclib/GeographicLib/SphericalEngine.hpp +376 -0
- data/ext/geographiclib/GeographicLib/SphericalHarmonic.hpp +354 -0
- data/ext/geographiclib/GeographicLib/SphericalHarmonic1.hpp +281 -0
- data/ext/geographiclib/GeographicLib/SphericalHarmonic2.hpp +315 -0
- data/ext/geographiclib/GeographicLib/TransverseMercator.hpp +196 -0
- data/ext/geographiclib/GeographicLib/TransverseMercatorExact.hpp +254 -0
- data/ext/geographiclib/GeographicLib/UTMUPS.hpp +421 -0
- data/ext/geographiclib/GeographicLib/Utility.hpp +612 -0
- data/ext/geographiclib/Geohash.cpp +102 -0
- data/ext/geographiclib/Geoid.cpp +509 -0
- data/ext/geographiclib/Georef.cpp +135 -0
- data/ext/geographiclib/Gnomonic.cpp +85 -0
- data/ext/geographiclib/GravityCircle.cpp +129 -0
- data/ext/geographiclib/GravityModel.cpp +360 -0
- data/ext/geographiclib/LambertConformalConic.cpp +456 -0
- data/ext/geographiclib/LocalCartesian.cpp +62 -0
- data/ext/geographiclib/MGRS.cpp +461 -0
- data/ext/geographiclib/MagneticCircle.cpp +52 -0
- data/ext/geographiclib/MagneticModel.cpp +269 -0
- data/ext/geographiclib/Math.cpp +63 -0
- data/ext/geographiclib/NormalGravity.cpp +262 -0
- data/ext/geographiclib/OSGB.cpp +167 -0
- data/ext/geographiclib/PolarStereographic.cpp +108 -0
- data/ext/geographiclib/PolygonArea.cpp +204 -0
- data/ext/geographiclib/Rhumb.cpp +383 -0
- data/ext/geographiclib/SphericalEngine.cpp +477 -0
- data/ext/geographiclib/TransverseMercator.cpp +603 -0
- data/ext/geographiclib/TransverseMercatorExact.cpp +464 -0
- data/ext/geographiclib/UTMUPS.cpp +296 -0
- data/ext/geographiclib/Utility.cpp +61 -0
- data/ext/geographiclib/extconf.rb +3 -0
- data/ext/geographiclib/geographiclib.cpp +62 -0
- data/lib/geographiclib.rb +20 -0
- metadata +140 -0
|
@@ -0,0 +1,464 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* \file TransverseMercatorExact.cpp
|
|
3
|
+
* \brief Implementation for GeographicLib::TransverseMercatorExact class
|
|
4
|
+
*
|
|
5
|
+
* Copyright (c) Charles Karney (2008-2015) <charles@karney.com> and licensed
|
|
6
|
+
* under the MIT/X11 License. For more information, see
|
|
7
|
+
* http://geographiclib.sourceforge.net/
|
|
8
|
+
*
|
|
9
|
+
* The relevant section of Lee's paper is part V, pp 67--101,
|
|
10
|
+
* <a href="https://dx.doi.org/10.3138/X687-1574-4325-WM62">Conformal
|
|
11
|
+
* Projections Based On Jacobian Elliptic Functions</a>.
|
|
12
|
+
*
|
|
13
|
+
* The method entails using the Thompson Transverse Mercator as an
|
|
14
|
+
* intermediate projection. The projections from the intermediate
|
|
15
|
+
* coordinates to [\e phi, \e lam] and [\e x, \e y] are given by elliptic
|
|
16
|
+
* functions. The inverse of these projections are found by Newton's method
|
|
17
|
+
* with a suitable starting guess.
|
|
18
|
+
*
|
|
19
|
+
* This implementation and notation closely follows Lee, with the following
|
|
20
|
+
* exceptions:
|
|
21
|
+
* <center><table>
|
|
22
|
+
* <tr><th>Lee <th>here <th>Description
|
|
23
|
+
* <tr><td>x/a <td>xi <td>Northing (unit Earth)
|
|
24
|
+
* <tr><td>y/a <td>eta <td>Easting (unit Earth)
|
|
25
|
+
* <tr><td>s/a <td>sigma <td>xi + i * eta
|
|
26
|
+
* <tr><td>y <td>x <td>Easting
|
|
27
|
+
* <tr><td>x <td>y <td>Northing
|
|
28
|
+
* <tr><td>k <td>e <td>eccentricity
|
|
29
|
+
* <tr><td>k^2 <td>mu <td>elliptic function parameter
|
|
30
|
+
* <tr><td>k'^2 <td>mv <td>elliptic function complementary parameter
|
|
31
|
+
* <tr><td>m <td>k <td>scale
|
|
32
|
+
* <tr><td>zeta <td>zeta <td>complex longitude = Mercator = chi in paper
|
|
33
|
+
* <tr><td>s <td>sigma <td>complex GK = zeta in paper
|
|
34
|
+
* </table></center>
|
|
35
|
+
*
|
|
36
|
+
* Minor alterations have been made in some of Lee's expressions in an
|
|
37
|
+
* attempt to control round-off. For example atanh(sin(phi)) is replaced by
|
|
38
|
+
* asinh(tan(phi)) which maintains accuracy near phi = pi/2. Such changes
|
|
39
|
+
* are noted in the code.
|
|
40
|
+
**********************************************************************/
|
|
41
|
+
|
|
42
|
+
#include <GeographicLib/TransverseMercatorExact.hpp>
|
|
43
|
+
|
|
44
|
+
#if defined(_MSC_VER)
|
|
45
|
+
// Squelch warnings about constant conditional expressions
|
|
46
|
+
# pragma warning (disable: 4127)
|
|
47
|
+
#endif
|
|
48
|
+
|
|
49
|
+
namespace GeographicLib {
|
|
50
|
+
|
|
51
|
+
using namespace std;
|
|
52
|
+
|
|
53
|
+
TransverseMercatorExact::TransverseMercatorExact(real a, real f, real k0,
|
|
54
|
+
bool extendp)
|
|
55
|
+
: tol_(numeric_limits<real>::epsilon())
|
|
56
|
+
, tol1_(real(0.1) * sqrt(tol_))
|
|
57
|
+
, tol2_(real(0.1) * tol_)
|
|
58
|
+
, taytol_(pow(tol_, real(0.6)))
|
|
59
|
+
, _a(a)
|
|
60
|
+
, _f(f)
|
|
61
|
+
, _k0(k0)
|
|
62
|
+
, _mu(_f * (2 - _f)) // e^2
|
|
63
|
+
, _mv(1 - _mu) // 1 - e^2
|
|
64
|
+
, _e(sqrt(_mu))
|
|
65
|
+
, _extendp(extendp)
|
|
66
|
+
, _Eu(_mu)
|
|
67
|
+
, _Ev(_mv)
|
|
68
|
+
{
|
|
69
|
+
if (!(Math::isfinite(_a) && _a > 0))
|
|
70
|
+
throw GeographicErr("Major radius is not positive");
|
|
71
|
+
if (!(_f > 0))
|
|
72
|
+
throw GeographicErr("Flattening is not positive");
|
|
73
|
+
if (!(_f < 1))
|
|
74
|
+
throw GeographicErr("Minor radius is not positive");
|
|
75
|
+
if (!(Math::isfinite(_k0) && _k0 > 0))
|
|
76
|
+
throw GeographicErr("Scale is not positive");
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const TransverseMercatorExact& TransverseMercatorExact::UTM() {
|
|
80
|
+
static const TransverseMercatorExact utm(Constants::WGS84_a(),
|
|
81
|
+
Constants::WGS84_f(),
|
|
82
|
+
Constants::UTM_k0());
|
|
83
|
+
return utm;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
void TransverseMercatorExact::zeta(real /*u*/, real snu, real cnu, real dnu,
|
|
87
|
+
real /*v*/, real snv, real cnv, real dnv,
|
|
88
|
+
real& taup, real& lam) const {
|
|
89
|
+
// Lee 54.17 but write
|
|
90
|
+
// atanh(snu * dnv) = asinh(snu * dnv / sqrt(cnu^2 + _mv * snu^2 * snv^2))
|
|
91
|
+
// atanh(_e * snu / dnv) =
|
|
92
|
+
// asinh(_e * snu / sqrt(_mu * cnu^2 + _mv * cnv^2))
|
|
93
|
+
// Overflow value s.t. atan(overflow) = pi/2
|
|
94
|
+
static const real
|
|
95
|
+
overflow = 1 / Math::sq(std::numeric_limits<real>::epsilon());
|
|
96
|
+
real
|
|
97
|
+
d1 = sqrt(Math::sq(cnu) + _mv * Math::sq(snu * snv)),
|
|
98
|
+
d2 = sqrt(_mu * Math::sq(cnu) + _mv * Math::sq(cnv)),
|
|
99
|
+
t1 = (d1 ? snu * dnv / d1 : (snu < 0 ? -overflow : overflow)),
|
|
100
|
+
t2 = (d2 ? sinh( _e * Math::asinh(_e * snu / d2) ) :
|
|
101
|
+
(snu < 0 ? -overflow : overflow));
|
|
102
|
+
// psi = asinh(t1) - asinh(t2)
|
|
103
|
+
// taup = sinh(psi)
|
|
104
|
+
taup = t1 * Math::hypot(real(1), t2) - t2 * Math::hypot(real(1), t1);
|
|
105
|
+
lam = (d1 != 0 && d2 != 0) ?
|
|
106
|
+
atan2(dnu * snv, cnu * cnv) - _e * atan2(_e * cnu * snv, dnu * cnv) :
|
|
107
|
+
0;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
void TransverseMercatorExact::dwdzeta(real /*u*/,
|
|
111
|
+
real snu, real cnu, real dnu,
|
|
112
|
+
real /*v*/,
|
|
113
|
+
real snv, real cnv, real dnv,
|
|
114
|
+
real& du, real& dv) const {
|
|
115
|
+
// Lee 54.21 but write (1 - dnu^2 * snv^2) = (cnv^2 + _mu * snu^2 * snv^2)
|
|
116
|
+
// (see A+S 16.21.4)
|
|
117
|
+
real d = _mv * Math::sq(Math::sq(cnv) + _mu * Math::sq(snu * snv));
|
|
118
|
+
du = cnu * dnu * dnv * (Math::sq(cnv) - _mu * Math::sq(snu * snv)) / d;
|
|
119
|
+
dv = -snu * snv * cnv * (Math::sq(dnu * dnv) + _mu * Math::sq(cnu)) / d;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Starting point for zetainv
|
|
123
|
+
bool TransverseMercatorExact::zetainv0(real psi, real lam, real& u, real& v)
|
|
124
|
+
const {
|
|
125
|
+
bool retval = false;
|
|
126
|
+
if (psi < -_e * Math::pi()/4 &&
|
|
127
|
+
lam > (1 - 2 * _e) * Math::pi()/2 &&
|
|
128
|
+
psi < lam - (1 - _e) * Math::pi()/2) {
|
|
129
|
+
// N.B. this branch is normally not taken because psi < 0 is converted
|
|
130
|
+
// psi > 0 by Forward.
|
|
131
|
+
//
|
|
132
|
+
// There's a log singularity at w = w0 = Eu.K() + i * Ev.K(),
|
|
133
|
+
// corresponding to the south pole, where we have, approximately
|
|
134
|
+
//
|
|
135
|
+
// psi = _e + i * pi/2 - _e * atanh(cos(i * (w - w0)/(1 + _mu/2)))
|
|
136
|
+
//
|
|
137
|
+
// Inverting this gives:
|
|
138
|
+
real
|
|
139
|
+
psix = 1 - psi / _e,
|
|
140
|
+
lamx = (Math::pi()/2 - lam) / _e;
|
|
141
|
+
u = Math::asinh(sin(lamx) / Math::hypot(cos(lamx), sinh(psix))) *
|
|
142
|
+
(1 + _mu/2);
|
|
143
|
+
v = atan2(cos(lamx), sinh(psix)) * (1 + _mu/2);
|
|
144
|
+
u = _Eu.K() - u;
|
|
145
|
+
v = _Ev.K() - v;
|
|
146
|
+
} else if (psi < _e * Math::pi()/2 &&
|
|
147
|
+
lam > (1 - 2 * _e) * Math::pi()/2) {
|
|
148
|
+
// At w = w0 = i * Ev.K(), we have
|
|
149
|
+
//
|
|
150
|
+
// zeta = zeta0 = i * (1 - _e) * pi/2
|
|
151
|
+
// zeta' = zeta'' = 0
|
|
152
|
+
//
|
|
153
|
+
// including the next term in the Taylor series gives:
|
|
154
|
+
//
|
|
155
|
+
// zeta = zeta0 - (_mv * _e) / 3 * (w - w0)^3
|
|
156
|
+
//
|
|
157
|
+
// When inverting this, we map arg(w - w0) = [-90, 0] to
|
|
158
|
+
// arg(zeta - zeta0) = [-90, 180]
|
|
159
|
+
real
|
|
160
|
+
dlam = lam - (1 - _e) * Math::pi()/2,
|
|
161
|
+
rad = Math::hypot(psi, dlam),
|
|
162
|
+
// atan2(dlam-psi, psi+dlam) + 45d gives arg(zeta - zeta0) in range
|
|
163
|
+
// [-135, 225). Subtracting 180 (since multiplier is negative) makes
|
|
164
|
+
// range [-315, 45). Multiplying by 1/3 (for cube root) gives range
|
|
165
|
+
// [-105, 15). In particular the range [-90, 180] in zeta space maps
|
|
166
|
+
// to [-90, 0] in w space as required.
|
|
167
|
+
ang = atan2(dlam-psi, psi+dlam) - real(0.75) * Math::pi();
|
|
168
|
+
// Error using this guess is about 0.21 * (rad/e)^(5/3)
|
|
169
|
+
retval = rad < _e * taytol_;
|
|
170
|
+
rad = Math::cbrt(3 / (_mv * _e) * rad);
|
|
171
|
+
ang /= 3;
|
|
172
|
+
u = rad * cos(ang);
|
|
173
|
+
v = rad * sin(ang) + _Ev.K();
|
|
174
|
+
} else {
|
|
175
|
+
// Use spherical TM, Lee 12.6 -- writing atanh(sin(lam) / cosh(psi)) =
|
|
176
|
+
// asinh(sin(lam) / hypot(cos(lam), sinh(psi))). This takes care of the
|
|
177
|
+
// log singularity at zeta = Eu.K() (corresponding to the north pole)
|
|
178
|
+
v = Math::asinh(sin(lam) / Math::hypot(cos(lam), sinh(psi)));
|
|
179
|
+
u = atan2(sinh(psi), cos(lam));
|
|
180
|
+
// But scale to put 90,0 on the right place
|
|
181
|
+
u *= _Eu.K() / (Math::pi()/2);
|
|
182
|
+
v *= _Eu.K() / (Math::pi()/2);
|
|
183
|
+
}
|
|
184
|
+
return retval;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Invert zeta using Newton's method
|
|
188
|
+
void TransverseMercatorExact::zetainv(real taup, real lam, real& u, real& v)
|
|
189
|
+
const {
|
|
190
|
+
real
|
|
191
|
+
psi = Math::asinh(taup),
|
|
192
|
+
scal = 1/Math::hypot(real(1), taup);
|
|
193
|
+
if (zetainv0(psi, lam, u, v))
|
|
194
|
+
return;
|
|
195
|
+
real stol2 = tol2_ / Math::sq(max(psi, real(1)));
|
|
196
|
+
// min iterations = 2, max iterations = 6; mean = 4.0
|
|
197
|
+
for (int i = 0, trip = 0; i < numit_ || GEOGRAPHICLIB_PANIC; ++i) {
|
|
198
|
+
real snu, cnu, dnu, snv, cnv, dnv;
|
|
199
|
+
_Eu.sncndn(u, snu, cnu, dnu);
|
|
200
|
+
_Ev.sncndn(v, snv, cnv, dnv);
|
|
201
|
+
real tau1, lam1, du1, dv1;
|
|
202
|
+
zeta(u, snu, cnu, dnu, v, snv, cnv, dnv, tau1, lam1);
|
|
203
|
+
dwdzeta(u, snu, cnu, dnu, v, snv, cnv, dnv, du1, dv1);
|
|
204
|
+
tau1 -= taup;
|
|
205
|
+
lam1 -= lam;
|
|
206
|
+
tau1 *= scal;
|
|
207
|
+
real
|
|
208
|
+
delu = tau1 * du1 - lam1 * dv1,
|
|
209
|
+
delv = tau1 * dv1 + lam1 * du1;
|
|
210
|
+
u -= delu;
|
|
211
|
+
v -= delv;
|
|
212
|
+
if (trip)
|
|
213
|
+
break;
|
|
214
|
+
real delw2 = Math::sq(delu) + Math::sq(delv);
|
|
215
|
+
if (!(delw2 >= stol2))
|
|
216
|
+
++trip;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
void TransverseMercatorExact::sigma(real /*u*/, real snu, real cnu, real dnu,
|
|
221
|
+
real v, real snv, real cnv, real dnv,
|
|
222
|
+
real& xi, real& eta) const {
|
|
223
|
+
// Lee 55.4 writing
|
|
224
|
+
// dnu^2 + dnv^2 - 1 = _mu * cnu^2 + _mv * cnv^2
|
|
225
|
+
real d = _mu * Math::sq(cnu) + _mv * Math::sq(cnv);
|
|
226
|
+
xi = _Eu.E(snu, cnu, dnu) - _mu * snu * cnu * dnu / d;
|
|
227
|
+
eta = v - _Ev.E(snv, cnv, dnv) + _mv * snv * cnv * dnv / d;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
void TransverseMercatorExact::dwdsigma(real /*u*/,
|
|
231
|
+
real snu, real cnu, real dnu,
|
|
232
|
+
real /*v*/,
|
|
233
|
+
real snv, real cnv, real dnv,
|
|
234
|
+
real& du, real& dv) const {
|
|
235
|
+
// Reciprocal of 55.9: dw/ds = dn(w)^2/_mv, expanding complex dn(w) using
|
|
236
|
+
// A+S 16.21.4
|
|
237
|
+
real d = _mv * Math::sq(Math::sq(cnv) + _mu * Math::sq(snu * snv));
|
|
238
|
+
real
|
|
239
|
+
dnr = dnu * cnv * dnv,
|
|
240
|
+
dni = - _mu * snu * cnu * snv;
|
|
241
|
+
du = (Math::sq(dnr) - Math::sq(dni)) / d;
|
|
242
|
+
dv = 2 * dnr * dni / d;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Starting point for sigmainv
|
|
246
|
+
bool TransverseMercatorExact::sigmainv0(real xi, real eta, real& u, real& v)
|
|
247
|
+
const {
|
|
248
|
+
bool retval = false;
|
|
249
|
+
if (eta > real(1.25) * _Ev.KE() ||
|
|
250
|
+
(xi < -real(0.25) * _Eu.E() && xi < eta - _Ev.KE())) {
|
|
251
|
+
// sigma as a simple pole at w = w0 = Eu.K() + i * Ev.K() and sigma is
|
|
252
|
+
// approximated by
|
|
253
|
+
//
|
|
254
|
+
// sigma = (Eu.E() + i * Ev.KE()) + 1/(w - w0)
|
|
255
|
+
real
|
|
256
|
+
x = xi - _Eu.E(),
|
|
257
|
+
y = eta - _Ev.KE(),
|
|
258
|
+
r2 = Math::sq(x) + Math::sq(y);
|
|
259
|
+
u = _Eu.K() + x/r2;
|
|
260
|
+
v = _Ev.K() - y/r2;
|
|
261
|
+
} else if ((eta > real(0.75) * _Ev.KE() && xi < real(0.25) * _Eu.E())
|
|
262
|
+
|| eta > _Ev.KE()) {
|
|
263
|
+
// At w = w0 = i * Ev.K(), we have
|
|
264
|
+
//
|
|
265
|
+
// sigma = sigma0 = i * Ev.KE()
|
|
266
|
+
// sigma' = sigma'' = 0
|
|
267
|
+
//
|
|
268
|
+
// including the next term in the Taylor series gives:
|
|
269
|
+
//
|
|
270
|
+
// sigma = sigma0 - _mv / 3 * (w - w0)^3
|
|
271
|
+
//
|
|
272
|
+
// When inverting this, we map arg(w - w0) = [-pi/2, -pi/6] to
|
|
273
|
+
// arg(sigma - sigma0) = [-pi/2, pi/2]
|
|
274
|
+
// mapping arg = [-pi/2, -pi/6] to [-pi/2, pi/2]
|
|
275
|
+
real
|
|
276
|
+
deta = eta - _Ev.KE(),
|
|
277
|
+
rad = Math::hypot(xi, deta),
|
|
278
|
+
// Map the range [-90, 180] in sigma space to [-90, 0] in w space. See
|
|
279
|
+
// discussion in zetainv0 on the cut for ang.
|
|
280
|
+
ang = atan2(deta-xi, xi+deta) - real(0.75) * Math::pi();
|
|
281
|
+
// Error using this guess is about 0.068 * rad^(5/3)
|
|
282
|
+
retval = rad < 2 * taytol_;
|
|
283
|
+
rad = Math::cbrt(3 / _mv * rad);
|
|
284
|
+
ang /= 3;
|
|
285
|
+
u = rad * cos(ang);
|
|
286
|
+
v = rad * sin(ang) + _Ev.K();
|
|
287
|
+
} else {
|
|
288
|
+
// Else use w = sigma * Eu.K/Eu.E (which is correct in the limit _e -> 0)
|
|
289
|
+
u = xi * _Eu.K()/_Eu.E();
|
|
290
|
+
v = eta * _Eu.K()/_Eu.E();
|
|
291
|
+
}
|
|
292
|
+
return retval;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Invert sigma using Newton's method
|
|
296
|
+
void TransverseMercatorExact::sigmainv(real xi, real eta, real& u, real& v)
|
|
297
|
+
const {
|
|
298
|
+
if (sigmainv0(xi, eta, u, v))
|
|
299
|
+
return;
|
|
300
|
+
// min iterations = 2, max iterations = 7; mean = 3.9
|
|
301
|
+
for (int i = 0, trip = 0; i < numit_ || GEOGRAPHICLIB_PANIC; ++i) {
|
|
302
|
+
real snu, cnu, dnu, snv, cnv, dnv;
|
|
303
|
+
_Eu.sncndn(u, snu, cnu, dnu);
|
|
304
|
+
_Ev.sncndn(v, snv, cnv, dnv);
|
|
305
|
+
real xi1, eta1, du1, dv1;
|
|
306
|
+
sigma(u, snu, cnu, dnu, v, snv, cnv, dnv, xi1, eta1);
|
|
307
|
+
dwdsigma(u, snu, cnu, dnu, v, snv, cnv, dnv, du1, dv1);
|
|
308
|
+
xi1 -= xi;
|
|
309
|
+
eta1 -= eta;
|
|
310
|
+
real
|
|
311
|
+
delu = xi1 * du1 - eta1 * dv1,
|
|
312
|
+
delv = xi1 * dv1 + eta1 * du1;
|
|
313
|
+
u -= delu;
|
|
314
|
+
v -= delv;
|
|
315
|
+
if (trip)
|
|
316
|
+
break;
|
|
317
|
+
real delw2 = Math::sq(delu) + Math::sq(delv);
|
|
318
|
+
if (!(delw2 >= tol2_))
|
|
319
|
+
++trip;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
void TransverseMercatorExact::Scale(real tau, real /*lam*/,
|
|
324
|
+
real snu, real cnu, real dnu,
|
|
325
|
+
real snv, real cnv, real dnv,
|
|
326
|
+
real& gamma, real& k) const {
|
|
327
|
+
real sec2 = 1 + Math::sq(tau); // sec(phi)^2
|
|
328
|
+
// Lee 55.12 -- negated for our sign convention. gamma gives the bearing
|
|
329
|
+
// (clockwise from true north) of grid north
|
|
330
|
+
gamma = atan2(_mv * snu * snv * cnv, cnu * dnu * dnv);
|
|
331
|
+
// Lee 55.13 with nu given by Lee 9.1 -- in sqrt change the numerator
|
|
332
|
+
// from
|
|
333
|
+
//
|
|
334
|
+
// (1 - snu^2 * dnv^2) to (_mv * snv^2 + cnu^2 * dnv^2)
|
|
335
|
+
//
|
|
336
|
+
// to maintain accuracy near phi = 90 and change the denomintor from
|
|
337
|
+
//
|
|
338
|
+
// (dnu^2 + dnv^2 - 1) to (_mu * cnu^2 + _mv * cnv^2)
|
|
339
|
+
//
|
|
340
|
+
// to maintain accuracy near phi = 0, lam = 90 * (1 - e). Similarly
|
|
341
|
+
// rewrite sqrt term in 9.1 as
|
|
342
|
+
//
|
|
343
|
+
// _mv + _mu * c^2 instead of 1 - _mu * sin(phi)^2
|
|
344
|
+
k = sqrt(_mv + _mu / sec2) * sqrt(sec2) *
|
|
345
|
+
sqrt( (_mv * Math::sq(snv) + Math::sq(cnu * dnv)) /
|
|
346
|
+
(_mu * Math::sq(cnu) + _mv * Math::sq(cnv)) );
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
void TransverseMercatorExact::Forward(real lon0, real lat, real lon,
|
|
350
|
+
real& x, real& y, real& gamma, real& k)
|
|
351
|
+
const {
|
|
352
|
+
lat = Math::LatFix(lat);
|
|
353
|
+
lon = Math::AngDiff(lon0, lon);
|
|
354
|
+
// Explicitly enforce the parity
|
|
355
|
+
int
|
|
356
|
+
latsign = (!_extendp && lat < 0) ? -1 : 1,
|
|
357
|
+
lonsign = (!_extendp && lon < 0) ? -1 : 1;
|
|
358
|
+
lon *= lonsign;
|
|
359
|
+
lat *= latsign;
|
|
360
|
+
bool backside = !_extendp && lon > 90;
|
|
361
|
+
if (backside) {
|
|
362
|
+
if (lat == 0)
|
|
363
|
+
latsign = -1;
|
|
364
|
+
lon = 180 - lon;
|
|
365
|
+
}
|
|
366
|
+
real
|
|
367
|
+
lam = lon * Math::degree(),
|
|
368
|
+
tau = Math::tand(lat);
|
|
369
|
+
|
|
370
|
+
// u,v = coordinates for the Thompson TM, Lee 54
|
|
371
|
+
real u, v;
|
|
372
|
+
if (lat == 90) {
|
|
373
|
+
u = _Eu.K();
|
|
374
|
+
v = 0;
|
|
375
|
+
} else if (lat == 0 && lon == 90 * (1 - _e)) {
|
|
376
|
+
u = 0;
|
|
377
|
+
v = _Ev.K();
|
|
378
|
+
} else
|
|
379
|
+
// tau = tan(phi), taup = sinh(psi)
|
|
380
|
+
zetainv(Math::taupf(tau, _e), lam, u, v);
|
|
381
|
+
|
|
382
|
+
real snu, cnu, dnu, snv, cnv, dnv;
|
|
383
|
+
_Eu.sncndn(u, snu, cnu, dnu);
|
|
384
|
+
_Ev.sncndn(v, snv, cnv, dnv);
|
|
385
|
+
|
|
386
|
+
real xi, eta;
|
|
387
|
+
sigma(u, snu, cnu, dnu, v, snv, cnv, dnv, xi, eta);
|
|
388
|
+
if (backside)
|
|
389
|
+
xi = 2 * _Eu.E() - xi;
|
|
390
|
+
y = xi * _a * _k0 * latsign;
|
|
391
|
+
x = eta * _a * _k0 * lonsign;
|
|
392
|
+
|
|
393
|
+
if (lat == 90) {
|
|
394
|
+
gamma = lon;
|
|
395
|
+
k = 1;
|
|
396
|
+
} else {
|
|
397
|
+
// Recompute (tau, lam) from (u, v) to improve accuracy of Scale
|
|
398
|
+
zeta(u, snu, cnu, dnu, v, snv, cnv, dnv, tau, lam);
|
|
399
|
+
tau = Math::tauf(tau, _e);
|
|
400
|
+
Scale(tau, lam, snu, cnu, dnu, snv, cnv, dnv, gamma, k);
|
|
401
|
+
gamma /= Math::degree();
|
|
402
|
+
}
|
|
403
|
+
if (backside)
|
|
404
|
+
gamma = 180 - gamma;
|
|
405
|
+
gamma *= latsign * lonsign;
|
|
406
|
+
k *= _k0;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
void TransverseMercatorExact::Reverse(real lon0, real x, real y,
|
|
410
|
+
real& lat, real& lon,
|
|
411
|
+
real& gamma, real& k)
|
|
412
|
+
const {
|
|
413
|
+
// This undoes the steps in Forward.
|
|
414
|
+
real
|
|
415
|
+
xi = y / (_a * _k0),
|
|
416
|
+
eta = x / (_a * _k0);
|
|
417
|
+
// Explicitly enforce the parity
|
|
418
|
+
int
|
|
419
|
+
latsign = !_extendp && y < 0 ? -1 : 1,
|
|
420
|
+
lonsign = !_extendp && x < 0 ? -1 : 1;
|
|
421
|
+
xi *= latsign;
|
|
422
|
+
eta *= lonsign;
|
|
423
|
+
bool backside = !_extendp && xi > _Eu.E();
|
|
424
|
+
if (backside)
|
|
425
|
+
xi = 2 * _Eu.E()- xi;
|
|
426
|
+
|
|
427
|
+
// u,v = coordinates for the Thompson TM, Lee 54
|
|
428
|
+
real u, v;
|
|
429
|
+
if (xi == 0 && eta == _Ev.KE()) {
|
|
430
|
+
u = 0;
|
|
431
|
+
v = _Ev.K();
|
|
432
|
+
} else
|
|
433
|
+
sigmainv(xi, eta, u, v);
|
|
434
|
+
|
|
435
|
+
real snu, cnu, dnu, snv, cnv, dnv;
|
|
436
|
+
_Eu.sncndn(u, snu, cnu, dnu);
|
|
437
|
+
_Ev.sncndn(v, snv, cnv, dnv);
|
|
438
|
+
real phi, lam, tau;
|
|
439
|
+
if (v != 0 || u != _Eu.K()) {
|
|
440
|
+
zeta(u, snu, cnu, dnu, v, snv, cnv, dnv, tau, lam);
|
|
441
|
+
tau = Math::tauf(tau, _e);
|
|
442
|
+
phi = atan(tau);
|
|
443
|
+
lat = phi / Math::degree();
|
|
444
|
+
lon = lam / Math::degree();
|
|
445
|
+
Scale(tau, lam, snu, cnu, dnu, snv, cnv, dnv, gamma, k);
|
|
446
|
+
gamma /= Math::degree();
|
|
447
|
+
} else {
|
|
448
|
+
lat = 90;
|
|
449
|
+
lon = lam = gamma = 0;
|
|
450
|
+
k = 1;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
if (backside)
|
|
454
|
+
lon = 180 - lon;
|
|
455
|
+
lon *= lonsign;
|
|
456
|
+
lon = Math::AngNormalize(lon + Math::AngNormalize(lon0));
|
|
457
|
+
lat *= latsign;
|
|
458
|
+
if (backside)
|
|
459
|
+
gamma = 180 - gamma;
|
|
460
|
+
gamma *= latsign * lonsign;
|
|
461
|
+
k *= _k0;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
} // namespace GeographicLib
|