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.
Files changed (87) hide show
  1. checksums.yaml +7 -0
  2. data/AUTHORS +12 -0
  3. data/LICENSE +24 -0
  4. data/ext/geographiclib/Accumulator.cpp +23 -0
  5. data/ext/geographiclib/AlbersEqualArea.cpp +445 -0
  6. data/ext/geographiclib/AzimuthalEquidistant.cpp +41 -0
  7. data/ext/geographiclib/CassiniSoldner.cpp +89 -0
  8. data/ext/geographiclib/CircularEngine.cpp +96 -0
  9. data/ext/geographiclib/DMS.cpp +381 -0
  10. data/ext/geographiclib/Ellipsoid.cpp +125 -0
  11. data/ext/geographiclib/EllipticFunction.cpp +512 -0
  12. data/ext/geographiclib/GARS.cpp +122 -0
  13. data/ext/geographiclib/GeoCoords.cpp +175 -0
  14. data/ext/geographiclib/Geocentric.cpp +172 -0
  15. data/ext/geographiclib/Geodesic.cpp +1908 -0
  16. data/ext/geographiclib/GeodesicExact.cpp +927 -0
  17. data/ext/geographiclib/GeodesicExactC4.cpp +7879 -0
  18. data/ext/geographiclib/GeodesicLine.cpp +321 -0
  19. data/ext/geographiclib/GeodesicLineExact.cpp +289 -0
  20. data/ext/geographiclib/GeographicLib/Accumulator.hpp +184 -0
  21. data/ext/geographiclib/GeographicLib/AlbersEqualArea.hpp +312 -0
  22. data/ext/geographiclib/GeographicLib/AzimuthalEquidistant.hpp +139 -0
  23. data/ext/geographiclib/GeographicLib/CassiniSoldner.hpp +204 -0
  24. data/ext/geographiclib/GeographicLib/CircularEngine.hpp +195 -0
  25. data/ext/geographiclib/GeographicLib/Config.h +12 -0
  26. data/ext/geographiclib/GeographicLib/Constants.hpp +387 -0
  27. data/ext/geographiclib/GeographicLib/DMS.hpp +370 -0
  28. data/ext/geographiclib/GeographicLib/Ellipsoid.hpp +534 -0
  29. data/ext/geographiclib/GeographicLib/EllipticFunction.hpp +692 -0
  30. data/ext/geographiclib/GeographicLib/GARS.hpp +143 -0
  31. data/ext/geographiclib/GeographicLib/GeoCoords.hpp +544 -0
  32. data/ext/geographiclib/GeographicLib/Geocentric.hpp +267 -0
  33. data/ext/geographiclib/GeographicLib/Geodesic.hpp +970 -0
  34. data/ext/geographiclib/GeographicLib/GeodesicExact.hpp +862 -0
  35. data/ext/geographiclib/GeographicLib/GeodesicLine.hpp +701 -0
  36. data/ext/geographiclib/GeographicLib/GeodesicLineExact.hpp +667 -0
  37. data/ext/geographiclib/GeographicLib/Geohash.hpp +180 -0
  38. data/ext/geographiclib/GeographicLib/Geoid.hpp +472 -0
  39. data/ext/geographiclib/GeographicLib/Georef.hpp +160 -0
  40. data/ext/geographiclib/GeographicLib/Gnomonic.hpp +206 -0
  41. data/ext/geographiclib/GeographicLib/GravityCircle.hpp +301 -0
  42. data/ext/geographiclib/GeographicLib/GravityModel.hpp +520 -0
  43. data/ext/geographiclib/GeographicLib/LambertConformalConic.hpp +313 -0
  44. data/ext/geographiclib/GeographicLib/LocalCartesian.hpp +236 -0
  45. data/ext/geographiclib/GeographicLib/MGRS.hpp +355 -0
  46. data/ext/geographiclib/GeographicLib/MagneticCircle.hpp +178 -0
  47. data/ext/geographiclib/GeographicLib/MagneticModel.hpp +347 -0
  48. data/ext/geographiclib/GeographicLib/Math.hpp +920 -0
  49. data/ext/geographiclib/GeographicLib/NormalGravity.hpp +350 -0
  50. data/ext/geographiclib/GeographicLib/OSGB.hpp +249 -0
  51. data/ext/geographiclib/GeographicLib/PolarStereographic.hpp +150 -0
  52. data/ext/geographiclib/GeographicLib/PolygonArea.hpp +288 -0
  53. data/ext/geographiclib/GeographicLib/Rhumb.hpp +589 -0
  54. data/ext/geographiclib/GeographicLib/SphericalEngine.hpp +376 -0
  55. data/ext/geographiclib/GeographicLib/SphericalHarmonic.hpp +354 -0
  56. data/ext/geographiclib/GeographicLib/SphericalHarmonic1.hpp +281 -0
  57. data/ext/geographiclib/GeographicLib/SphericalHarmonic2.hpp +315 -0
  58. data/ext/geographiclib/GeographicLib/TransverseMercator.hpp +196 -0
  59. data/ext/geographiclib/GeographicLib/TransverseMercatorExact.hpp +254 -0
  60. data/ext/geographiclib/GeographicLib/UTMUPS.hpp +421 -0
  61. data/ext/geographiclib/GeographicLib/Utility.hpp +612 -0
  62. data/ext/geographiclib/Geohash.cpp +102 -0
  63. data/ext/geographiclib/Geoid.cpp +509 -0
  64. data/ext/geographiclib/Georef.cpp +135 -0
  65. data/ext/geographiclib/Gnomonic.cpp +85 -0
  66. data/ext/geographiclib/GravityCircle.cpp +129 -0
  67. data/ext/geographiclib/GravityModel.cpp +360 -0
  68. data/ext/geographiclib/LambertConformalConic.cpp +456 -0
  69. data/ext/geographiclib/LocalCartesian.cpp +62 -0
  70. data/ext/geographiclib/MGRS.cpp +461 -0
  71. data/ext/geographiclib/MagneticCircle.cpp +52 -0
  72. data/ext/geographiclib/MagneticModel.cpp +269 -0
  73. data/ext/geographiclib/Math.cpp +63 -0
  74. data/ext/geographiclib/NormalGravity.cpp +262 -0
  75. data/ext/geographiclib/OSGB.cpp +167 -0
  76. data/ext/geographiclib/PolarStereographic.cpp +108 -0
  77. data/ext/geographiclib/PolygonArea.cpp +204 -0
  78. data/ext/geographiclib/Rhumb.cpp +383 -0
  79. data/ext/geographiclib/SphericalEngine.cpp +477 -0
  80. data/ext/geographiclib/TransverseMercator.cpp +603 -0
  81. data/ext/geographiclib/TransverseMercatorExact.cpp +464 -0
  82. data/ext/geographiclib/UTMUPS.cpp +296 -0
  83. data/ext/geographiclib/Utility.cpp +61 -0
  84. data/ext/geographiclib/extconf.rb +3 -0
  85. data/ext/geographiclib/geographiclib.cpp +62 -0
  86. data/lib/geographiclib.rb +20 -0
  87. 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