geographiclib 0.0.1

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