geographiclib 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|