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,603 @@
1
+ /**
2
+ * \file TransverseMercator.cpp
3
+ * \brief Implementation for GeographicLib::TransverseMercator 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
+ * This implementation follows closely
10
+ * <a href="http://www.jhs-suositukset.fi/suomi/jhs154"> JHS 154, ETRS89 -
11
+ * j&auml;rjestelm&auml;&auml;n liittyv&auml;t karttaprojektiot,
12
+ * tasokoordinaatistot ja karttalehtijako</a> (Map projections, plane
13
+ * coordinates, and map sheet index for ETRS89), published by JUHTA, Finnish
14
+ * Geodetic Institute, and the National Land Survey of Finland (2006).
15
+ *
16
+ * The relevant section is available as the 2008 PDF file
17
+ * http://docs.jhs-suositukset.fi/jhs-suositukset/JHS154/JHS154_liite1.pdf
18
+ *
19
+ * This is a straight transcription of the formulas in this paper with the
20
+ * following exceptions:
21
+ * - use of 6th order series instead of 4th order series. This reduces the
22
+ * error to about 5nm for the UTM range of coordinates (instead of 200nm),
23
+ * with a speed penalty of only 1%;
24
+ * - use Newton's method instead of plain iteration to solve for latitude in
25
+ * terms of isometric latitude in the Reverse method;
26
+ * - use of Horner's representation for evaluating polynomials and Clenshaw's
27
+ * method for summing trigonometric series;
28
+ * - several modifications of the formulas to improve the numerical accuracy;
29
+ * - evaluating the convergence and scale using the expression for the
30
+ * projection or its inverse.
31
+ *
32
+ * If the preprocessor variable GEOGRAPHICLIB_TRANSVERSEMERCATOR_ORDER is set
33
+ * to an integer between 4 and 8, then this specifies the order of the series
34
+ * used for the forward and reverse transformations. The default value is 6.
35
+ * (The series accurate to 12th order is given in \ref tmseries.)
36
+ *
37
+ * Other equivalent implementations are given in
38
+ * - http://www.ign.fr/DISPLAY/000/526/702/5267021/NTG_76.pdf
39
+ * - http://www.lantmateriet.se/upload/filer/kartor/geodesi_gps_och_detaljmatning/geodesi/Formelsamling/Gauss_Conformal_Projection.pdf
40
+ **********************************************************************/
41
+
42
+ #include <GeographicLib/TransverseMercator.hpp>
43
+
44
+ namespace GeographicLib {
45
+
46
+ using namespace std;
47
+
48
+ TransverseMercator::TransverseMercator(real a, real f, real k0)
49
+ : _a(a)
50
+ , _f(f)
51
+ , _k0(k0)
52
+ , _e2(_f * (2 - _f))
53
+ , _es((f < 0 ? -1 : 1) * sqrt(abs(_e2)))
54
+ , _e2m(1 - _e2)
55
+ // _c = sqrt( pow(1 + _e, 1 + _e) * pow(1 - _e, 1 - _e) ) )
56
+ // See, for example, Lee (1976), p 100.
57
+ , _c( sqrt(_e2m) * exp(Math::eatanhe(real(1), _es)) )
58
+ , _n(_f / (2 - _f))
59
+ {
60
+ if (!(Math::isfinite(_a) && _a > 0))
61
+ throw GeographicErr("Major radius is not positive");
62
+ if (!(Math::isfinite(_f) && _f < 1))
63
+ throw GeographicErr("Minor radius is not positive");
64
+ if (!(Math::isfinite(_k0) && _k0 > 0))
65
+ throw GeographicErr("Scale is not positive");
66
+
67
+ // Generated by Maxima on 2015-05-14 22:55:13-04:00
68
+ #if GEOGRAPHICLIB_TRANSVERSEMERCATOR_ORDER/2 == 2
69
+ static const real b1coeff[] = {
70
+ // b1*(n+1), polynomial in n2 of order 2
71
+ 1, 16, 64, 64,
72
+ }; // count = 4
73
+ #elif GEOGRAPHICLIB_TRANSVERSEMERCATOR_ORDER/2 == 3
74
+ static const real b1coeff[] = {
75
+ // b1*(n+1), polynomial in n2 of order 3
76
+ 1, 4, 64, 256, 256,
77
+ }; // count = 5
78
+ #elif GEOGRAPHICLIB_TRANSVERSEMERCATOR_ORDER/2 == 4
79
+ static const real b1coeff[] = {
80
+ // b1*(n+1), polynomial in n2 of order 4
81
+ 25, 64, 256, 4096, 16384, 16384,
82
+ }; // count = 6
83
+ #else
84
+ #error "Bad value for GEOGRAPHICLIB_TRANSVERSEMERCATOR_ORDER"
85
+ #endif
86
+
87
+ #if GEOGRAPHICLIB_TRANSVERSEMERCATOR_ORDER == 4
88
+ static const real alpcoeff[] = {
89
+ // alp[1]/n^1, polynomial in n of order 3
90
+ 164, 225, -480, 360, 720,
91
+ // alp[2]/n^2, polynomial in n of order 2
92
+ 557, -864, 390, 1440,
93
+ // alp[3]/n^3, polynomial in n of order 1
94
+ -1236, 427, 1680,
95
+ // alp[4]/n^4, polynomial in n of order 0
96
+ 49561, 161280,
97
+ }; // count = 14
98
+ #elif GEOGRAPHICLIB_TRANSVERSEMERCATOR_ORDER == 5
99
+ static const real alpcoeff[] = {
100
+ // alp[1]/n^1, polynomial in n of order 4
101
+ -635, 328, 450, -960, 720, 1440,
102
+ // alp[2]/n^2, polynomial in n of order 3
103
+ 4496, 3899, -6048, 2730, 10080,
104
+ // alp[3]/n^3, polynomial in n of order 2
105
+ 15061, -19776, 6832, 26880,
106
+ // alp[4]/n^4, polynomial in n of order 1
107
+ -171840, 49561, 161280,
108
+ // alp[5]/n^5, polynomial in n of order 0
109
+ 34729, 80640,
110
+ }; // count = 20
111
+ #elif GEOGRAPHICLIB_TRANSVERSEMERCATOR_ORDER == 6
112
+ static const real alpcoeff[] = {
113
+ // alp[1]/n^1, polynomial in n of order 5
114
+ 31564, -66675, 34440, 47250, -100800, 75600, 151200,
115
+ // alp[2]/n^2, polynomial in n of order 4
116
+ -1983433, 863232, 748608, -1161216, 524160, 1935360,
117
+ // alp[3]/n^3, polynomial in n of order 3
118
+ 670412, 406647, -533952, 184464, 725760,
119
+ // alp[4]/n^4, polynomial in n of order 2
120
+ 6601661, -7732800, 2230245, 7257600,
121
+ // alp[5]/n^5, polynomial in n of order 1
122
+ -13675556, 3438171, 7983360,
123
+ // alp[6]/n^6, polynomial in n of order 0
124
+ 212378941, 319334400,
125
+ }; // count = 27
126
+ #elif GEOGRAPHICLIB_TRANSVERSEMERCATOR_ORDER == 7
127
+ static const real alpcoeff[] = {
128
+ // alp[1]/n^1, polynomial in n of order 6
129
+ 1804025, 2020096, -4267200, 2204160, 3024000, -6451200, 4838400, 9676800,
130
+ // alp[2]/n^2, polynomial in n of order 5
131
+ 4626384, -9917165, 4316160, 3743040, -5806080, 2620800, 9676800,
132
+ // alp[3]/n^3, polynomial in n of order 4
133
+ -67102379, 26816480, 16265880, -21358080, 7378560, 29030400,
134
+ // alp[4]/n^4, polynomial in n of order 3
135
+ 155912000, 72618271, -85060800, 24532695, 79833600,
136
+ // alp[5]/n^5, polynomial in n of order 2
137
+ 102508609, -109404448, 27505368, 63866880,
138
+ // alp[6]/n^6, polynomial in n of order 1
139
+ -12282192400LL, 2760926233LL, 4151347200LL,
140
+ // alp[7]/n^7, polynomial in n of order 0
141
+ 1522256789, 1383782400,
142
+ }; // count = 35
143
+ #elif GEOGRAPHICLIB_TRANSVERSEMERCATOR_ORDER == 8
144
+ static const real alpcoeff[] = {
145
+ // alp[1]/n^1, polynomial in n of order 7
146
+ -75900428, 37884525, 42422016, -89611200, 46287360, 63504000, -135475200,
147
+ 101606400, 203212800,
148
+ // alp[2]/n^2, polynomial in n of order 6
149
+ 148003883, 83274912, -178508970, 77690880, 67374720, -104509440,
150
+ 47174400, 174182400,
151
+ // alp[3]/n^3, polynomial in n of order 5
152
+ 318729724, -738126169, 294981280, 178924680, -234938880, 81164160,
153
+ 319334400,
154
+ // alp[4]/n^4, polynomial in n of order 4
155
+ -40176129013LL, 14967552000LL, 6971354016LL, -8165836800LL, 2355138720LL,
156
+ 7664025600LL,
157
+ // alp[5]/n^5, polynomial in n of order 3
158
+ 10421654396LL, 3997835751LL, -4266773472LL, 1072709352, 2490808320LL,
159
+ // alp[6]/n^6, polynomial in n of order 2
160
+ 175214326799LL, -171950693600LL, 38652967262LL, 58118860800LL,
161
+ // alp[7]/n^7, polynomial in n of order 1
162
+ -67039739596LL, 13700311101LL, 12454041600LL,
163
+ // alp[8]/n^8, polynomial in n of order 0
164
+ 1424729850961LL, 743921418240LL,
165
+ }; // count = 44
166
+ #else
167
+ #error "Bad value for GEOGRAPHICLIB_TRANSVERSEMERCATOR_ORDER"
168
+ #endif
169
+
170
+ #if GEOGRAPHICLIB_TRANSVERSEMERCATOR_ORDER == 4
171
+ static const real betcoeff[] = {
172
+ // bet[1]/n^1, polynomial in n of order 3
173
+ -4, 555, -960, 720, 1440,
174
+ // bet[2]/n^2, polynomial in n of order 2
175
+ -437, 96, 30, 1440,
176
+ // bet[3]/n^3, polynomial in n of order 1
177
+ -148, 119, 3360,
178
+ // bet[4]/n^4, polynomial in n of order 0
179
+ 4397, 161280,
180
+ }; // count = 14
181
+ #elif GEOGRAPHICLIB_TRANSVERSEMERCATOR_ORDER == 5
182
+ static const real betcoeff[] = {
183
+ // bet[1]/n^1, polynomial in n of order 4
184
+ -3645, -64, 8880, -15360, 11520, 23040,
185
+ // bet[2]/n^2, polynomial in n of order 3
186
+ 4416, -3059, 672, 210, 10080,
187
+ // bet[3]/n^3, polynomial in n of order 2
188
+ -627, -592, 476, 13440,
189
+ // bet[4]/n^4, polynomial in n of order 1
190
+ -3520, 4397, 161280,
191
+ // bet[5]/n^5, polynomial in n of order 0
192
+ 4583, 161280,
193
+ }; // count = 20
194
+ #elif GEOGRAPHICLIB_TRANSVERSEMERCATOR_ORDER == 6
195
+ static const real betcoeff[] = {
196
+ // bet[1]/n^1, polynomial in n of order 5
197
+ 384796, -382725, -6720, 932400, -1612800, 1209600, 2419200,
198
+ // bet[2]/n^2, polynomial in n of order 4
199
+ -1118711, 1695744, -1174656, 258048, 80640, 3870720,
200
+ // bet[3]/n^3, polynomial in n of order 3
201
+ 22276, -16929, -15984, 12852, 362880,
202
+ // bet[4]/n^4, polynomial in n of order 2
203
+ -830251, -158400, 197865, 7257600,
204
+ // bet[5]/n^5, polynomial in n of order 1
205
+ -435388, 453717, 15966720,
206
+ // bet[6]/n^6, polynomial in n of order 0
207
+ 20648693, 638668800,
208
+ }; // count = 27
209
+ #elif GEOGRAPHICLIB_TRANSVERSEMERCATOR_ORDER == 7
210
+ static const real betcoeff[] = {
211
+ // bet[1]/n^1, polynomial in n of order 6
212
+ -5406467, 6156736, -6123600, -107520, 14918400, -25804800, 19353600,
213
+ 38707200,
214
+ // bet[2]/n^2, polynomial in n of order 5
215
+ 829456, -5593555, 8478720, -5873280, 1290240, 403200, 19353600,
216
+ // bet[3]/n^3, polynomial in n of order 4
217
+ 9261899, 3564160, -2708640, -2557440, 2056320, 58060800,
218
+ // bet[4]/n^4, polynomial in n of order 3
219
+ 14928352, -9132761, -1742400, 2176515, 79833600,
220
+ // bet[5]/n^5, polynomial in n of order 2
221
+ -8005831, -1741552, 1814868, 63866880,
222
+ // bet[6]/n^6, polynomial in n of order 1
223
+ -261810608, 268433009, 8302694400LL,
224
+ // bet[7]/n^7, polynomial in n of order 0
225
+ 219941297, 5535129600LL,
226
+ }; // count = 35
227
+ #elif GEOGRAPHICLIB_TRANSVERSEMERCATOR_ORDER == 8
228
+ static const real betcoeff[] = {
229
+ // bet[1]/n^1, polynomial in n of order 7
230
+ 31777436, -37845269, 43097152, -42865200, -752640, 104428800, -180633600,
231
+ 135475200, 270950400,
232
+ // bet[2]/n^2, polynomial in n of order 6
233
+ 24749483, 14930208, -100683990, 152616960, -105719040, 23224320, 7257600,
234
+ 348364800,
235
+ // bet[3]/n^3, polynomial in n of order 5
236
+ -232468668, 101880889, 39205760, -29795040, -28131840, 22619520,
237
+ 638668800,
238
+ // bet[4]/n^4, polynomial in n of order 4
239
+ 324154477, 1433121792, -876745056, -167270400, 208945440, 7664025600LL,
240
+ // bet[5]/n^5, polynomial in n of order 3
241
+ 457888660, -312227409, -67920528, 70779852, 2490808320LL,
242
+ // bet[6]/n^6, polynomial in n of order 2
243
+ -19841813847LL, -3665348512LL, 3758062126LL, 116237721600LL,
244
+ // bet[7]/n^7, polynomial in n of order 1
245
+ -1989295244, 1979471673, 49816166400LL,
246
+ // bet[8]/n^8, polynomial in n of order 0
247
+ 191773887257LL, 3719607091200LL,
248
+ }; // count = 44
249
+ #else
250
+ #error "Bad value for GEOGRAPHICLIB_TRANSVERSEMERCATOR_ORDER"
251
+ #endif
252
+
253
+ GEOGRAPHICLIB_STATIC_ASSERT(sizeof(b1coeff) / sizeof(real) ==
254
+ maxpow_/2 + 2,
255
+ "Coefficient array size mismatch for b1");
256
+ GEOGRAPHICLIB_STATIC_ASSERT(sizeof(alpcoeff) / sizeof(real) ==
257
+ (maxpow_ * (maxpow_ + 3))/2,
258
+ "Coefficient array size mismatch for alp");
259
+ GEOGRAPHICLIB_STATIC_ASSERT(sizeof(betcoeff) / sizeof(real) ==
260
+ (maxpow_ * (maxpow_ + 3))/2,
261
+ "Coefficient array size mismatch for bet");
262
+ int m = maxpow_/2;
263
+ _b1 = Math::polyval(m, b1coeff, Math::sq(_n)) / (b1coeff[m + 1] * (1+_n));
264
+ // _a1 is the equivalent radius for computing the circumference of
265
+ // ellipse.
266
+ _a1 = _b1 * _a;
267
+ int o = 0;
268
+ real d = _n;
269
+ for (int l = 1; l <= maxpow_; ++l) {
270
+ m = maxpow_ - l;
271
+ _alp[l] = d * Math::polyval(m, alpcoeff + o, _n) / alpcoeff[o + m + 1];
272
+ _bet[l] = d * Math::polyval(m, betcoeff + o, _n) / betcoeff[o + m + 1];
273
+ o += m + 2;
274
+ d *= _n;
275
+ }
276
+ // Post condition: o == sizeof(alpcoeff) / sizeof(real) &&
277
+ // o == sizeof(betcoeff) / sizeof(real)
278
+ }
279
+
280
+ const TransverseMercator& TransverseMercator::UTM() {
281
+ static const TransverseMercator utm(Constants::WGS84_a(),
282
+ Constants::WGS84_f(),
283
+ Constants::UTM_k0());
284
+ return utm;
285
+ }
286
+
287
+ // Engsager and Poder (2007) use trigonometric series to convert between phi
288
+ // and phip. Here are the series...
289
+ //
290
+ // Conversion from phi to phip:
291
+ //
292
+ // phip = phi + sum(c[j] * sin(2*j*phi), j, 1, 6)
293
+ //
294
+ // c[1] = - 2 * n
295
+ // + 2/3 * n^2
296
+ // + 4/3 * n^3
297
+ // - 82/45 * n^4
298
+ // + 32/45 * n^5
299
+ // + 4642/4725 * n^6;
300
+ // c[2] = 5/3 * n^2
301
+ // - 16/15 * n^3
302
+ // - 13/9 * n^4
303
+ // + 904/315 * n^5
304
+ // - 1522/945 * n^6;
305
+ // c[3] = - 26/15 * n^3
306
+ // + 34/21 * n^4
307
+ // + 8/5 * n^5
308
+ // - 12686/2835 * n^6;
309
+ // c[4] = 1237/630 * n^4
310
+ // - 12/5 * n^5
311
+ // - 24832/14175 * n^6;
312
+ // c[5] = - 734/315 * n^5
313
+ // + 109598/31185 * n^6;
314
+ // c[6] = 444337/155925 * n^6;
315
+ //
316
+ // Conversion from phip to phi:
317
+ //
318
+ // phi = phip + sum(d[j] * sin(2*j*phip), j, 1, 6)
319
+ //
320
+ // d[1] = 2 * n
321
+ // - 2/3 * n^2
322
+ // - 2 * n^3
323
+ // + 116/45 * n^4
324
+ // + 26/45 * n^5
325
+ // - 2854/675 * n^6;
326
+ // d[2] = 7/3 * n^2
327
+ // - 8/5 * n^3
328
+ // - 227/45 * n^4
329
+ // + 2704/315 * n^5
330
+ // + 2323/945 * n^6;
331
+ // d[3] = 56/15 * n^3
332
+ // - 136/35 * n^4
333
+ // - 1262/105 * n^5
334
+ // + 73814/2835 * n^6;
335
+ // d[4] = 4279/630 * n^4
336
+ // - 332/35 * n^5
337
+ // - 399572/14175 * n^6;
338
+ // d[5] = 4174/315 * n^5
339
+ // - 144838/6237 * n^6;
340
+ // d[6] = 601676/22275 * n^6;
341
+ //
342
+ // In order to maintain sufficient relative accuracy close to the pole use
343
+ //
344
+ // S = sum(c[i]*sin(2*i*phi),i,1,6)
345
+ // taup = (tau + tan(S)) / (1 - tau * tan(S))
346
+
347
+ // In Math::taupf and Math::tauf we evaluate the forward transform explicitly
348
+ // and solve the reverse one by Newton's method.
349
+ //
350
+ // There are adapted from TransverseMercatorExact (taup and taupinv). tau =
351
+ // tan(phi), taup = sinh(psi)
352
+
353
+ void TransverseMercator::Forward(real lon0, real lat, real lon,
354
+ real& x, real& y, real& gamma, real& k)
355
+ const {
356
+ lat = Math::LatFix(lat);
357
+ lon = Math::AngDiff(lon0, lon);
358
+ // Explicitly enforce the parity
359
+ int
360
+ latsign = lat < 0 ? -1 : 1,
361
+ lonsign = lon < 0 ? -1 : 1;
362
+ lon *= lonsign;
363
+ lat *= latsign;
364
+ bool backside = lon > 90;
365
+ if (backside) {
366
+ if (lat == 0)
367
+ latsign = -1;
368
+ lon = 180 - lon;
369
+ }
370
+ real sphi, cphi, slam, clam;
371
+ Math::sincosd(lat, sphi, cphi);
372
+ Math::sincosd(lon, slam, clam);
373
+ // phi = latitude
374
+ // phi' = conformal latitude
375
+ // psi = isometric latitude
376
+ // tau = tan(phi)
377
+ // tau' = tan(phi')
378
+ // [xi', eta'] = Gauss-Schreiber TM coordinates
379
+ // [xi, eta] = Gauss-Krueger TM coordinates
380
+ //
381
+ // We use
382
+ // tan(phi') = sinh(psi)
383
+ // sin(phi') = tanh(psi)
384
+ // cos(phi') = sech(psi)
385
+ // denom^2 = 1-cos(phi')^2*sin(lam)^2 = 1-sech(psi)^2*sin(lam)^2
386
+ // sin(xip) = sin(phi')/denom = tanh(psi)/denom
387
+ // cos(xip) = cos(phi')*cos(lam)/denom = sech(psi)*cos(lam)/denom
388
+ // cosh(etap) = 1/denom = 1/denom
389
+ // sinh(etap) = cos(phi')*sin(lam)/denom = sech(psi)*sin(lam)/denom
390
+ real etap, xip;
391
+ if (lat != 90) {
392
+ real
393
+ tau = sphi / cphi,
394
+ taup = Math::taupf(tau, _es);
395
+ xip = atan2(taup, clam);
396
+ // Used to be
397
+ // etap = Math::atanh(sin(lam) / cosh(psi));
398
+ etap = Math::asinh(slam / Math::hypot(taup, clam));
399
+ // convergence and scale for Gauss-Schreiber TM (xip, etap) -- gamma0 =
400
+ // atan(tan(xip) * tanh(etap)) = atan(tan(lam) * sin(phi'));
401
+ // sin(phi') = tau'/sqrt(1 + tau'^2)
402
+ // Krueger p 22 (44)
403
+ gamma = Math::atan2d(slam * taup, clam * Math::hypot(real(1), taup));
404
+ // k0 = sqrt(1 - _e2 * sin(phi)^2) * (cos(phi') / cos(phi)) * cosh(etap)
405
+ // Note 1/cos(phi) = cosh(psip);
406
+ // and cos(phi') * cosh(etap) = 1/hypot(sinh(psi), cos(lam))
407
+ //
408
+ // This form has cancelling errors. This property is lost if cosh(psip)
409
+ // is replaced by 1/cos(phi), even though it's using "primary" data (phi
410
+ // instead of psip).
411
+ k = sqrt(_e2m + _e2 * Math::sq(cphi)) * Math::hypot(real(1), tau)
412
+ / Math::hypot(taup, clam);
413
+ } else {
414
+ xip = Math::pi()/2;
415
+ etap = 0;
416
+ gamma = lon;
417
+ k = _c;
418
+ }
419
+ // {xi',eta'} is {northing,easting} for Gauss-Schreiber transverse Mercator
420
+ // (for eta' = 0, xi' = bet). {xi,eta} is {northing,easting} for transverse
421
+ // Mercator with constant scale on the central meridian (for eta = 0, xip =
422
+ // rectifying latitude). Define
423
+ //
424
+ // zeta = xi + i*eta
425
+ // zeta' = xi' + i*eta'
426
+ //
427
+ // The conversion from conformal to rectifying latitude can be expressed as
428
+ // a series in _n:
429
+ //
430
+ // zeta = zeta' + sum(h[j-1]' * sin(2 * j * zeta'), j = 1..maxpow_)
431
+ //
432
+ // where h[j]' = O(_n^j). The reversion of this series gives
433
+ //
434
+ // zeta' = zeta - sum(h[j-1] * sin(2 * j * zeta), j = 1..maxpow_)
435
+ //
436
+ // which is used in Reverse.
437
+ //
438
+ // Evaluate sums via Clenshaw method. See
439
+ // http://mathworld.wolfram.com/ClenshawRecurrenceFormula.html
440
+ //
441
+ // Let
442
+ //
443
+ // S = sum(c[k] * F[k](x), k = 0..N)
444
+ // F[n+1](x) = alpha(n,x) * F[n](x) + beta(n,x) * F[n-1](x)
445
+ //
446
+ // Evaluate S with
447
+ //
448
+ // y[N+2] = y[N+1] = 0
449
+ // y[k] = alpha(k,x) * y[k+1] + beta(k+1,x) * y[k+2] + c[k]
450
+ // S = c[0] * F[0](x) + y[1] * F[1](x) + beta(1,x) * F[0](x) * y[2]
451
+ //
452
+ // Here we have
453
+ //
454
+ // x = 2 * zeta'
455
+ // F[n](x) = sin(n * x)
456
+ // a(n, x) = 2 * cos(x)
457
+ // b(n, x) = -1
458
+ // [ sin(A+B) - 2*cos(B)*sin(A) + sin(A-B) = 0, A = n*x, B = x ]
459
+ // N = maxpow_
460
+ // c[k] = _alp[k]
461
+ // S = y[1] * sin(x)
462
+ //
463
+ // For the derivative we have
464
+ //
465
+ // x = 2 * zeta'
466
+ // F[n](x) = cos(n * x)
467
+ // a(n, x) = 2 * cos(x)
468
+ // b(n, x) = -1
469
+ // [ cos(A+B) - 2*cos(B)*cos(A) + cos(A-B) = 0, A = n*x, B = x ]
470
+ // c[0] = 1; c[k] = 2*k*_alp[k]
471
+ // S = (c[0] - y[2]) + y[1] * cos(x)
472
+ real
473
+ c0 = cos(2 * xip), ch0 = cosh(2 * etap),
474
+ s0 = sin(2 * xip), sh0 = sinh(2 * etap),
475
+ ar = 2 * c0 * ch0, ai = -2 * s0 * sh0; // 2 * cos(2*zeta')
476
+ int n = maxpow_;
477
+ real
478
+ xi0 = (n & 1 ? _alp[n] : 0), eta0 = 0,
479
+ xi1 = 0, eta1 = 0;
480
+ real // Accumulators for dzeta/dzeta'
481
+ yr0 = (n & 1 ? 2 * maxpow_ * _alp[n--] : 0), yi0 = 0,
482
+ yr1 = 0, yi1 = 0;
483
+ while (n) {
484
+ xi1 = ar * xi0 - ai * eta0 - xi1 + _alp[n];
485
+ eta1 = ai * xi0 + ar * eta0 - eta1;
486
+ yr1 = ar * yr0 - ai * yi0 - yr1 + 2 * n * _alp[n];
487
+ yi1 = ai * yr0 + ar * yi0 - yi1;
488
+ --n;
489
+ xi0 = ar * xi1 - ai * eta1 - xi0 + _alp[n];
490
+ eta0 = ai * xi1 + ar * eta1 - eta0;
491
+ yr0 = ar * yr1 - ai * yi1 - yr0 + 2 * n * _alp[n];
492
+ yi0 = ai * yr1 + ar * yi1 - yi0;
493
+ --n;
494
+ }
495
+ ar /= 2; ai /= 2; // cos(2*zeta')
496
+ yr1 = 1 - yr1 + ar * yr0 - ai * yi0;
497
+ yi1 = - yi1 + ai * yr0 + ar * yi0;
498
+ ar = s0 * ch0; ai = c0 * sh0; // sin(2*zeta')
499
+ real
500
+ xi = xip + ar * xi0 - ai * eta0,
501
+ eta = etap + ai * xi0 + ar * eta0;
502
+ // Fold in change in convergence and scale for Gauss-Schreiber TM to
503
+ // Gauss-Krueger TM.
504
+ gamma -= Math::atan2d(yi1, yr1);
505
+ k *= _b1 * Math::hypot(yr1, yi1);
506
+ y = _a1 * _k0 * (backside ? Math::pi() - xi : xi) * latsign;
507
+ x = _a1 * _k0 * eta * lonsign;
508
+ if (backside)
509
+ gamma = 180 - gamma;
510
+ gamma *= latsign * lonsign;
511
+ gamma = Math::AngNormalize(gamma);
512
+ k *= _k0;
513
+ }
514
+
515
+ void TransverseMercator::Reverse(real lon0, real x, real y,
516
+ real& lat, real& lon, real& gamma, real& k)
517
+ const {
518
+ // This undoes the steps in Forward. The wrinkles are: (1) Use of the
519
+ // reverted series to express zeta' in terms of zeta. (2) Newton's method
520
+ // to solve for phi in terms of tan(phi).
521
+ real
522
+ xi = y / (_a1 * _k0),
523
+ eta = x / (_a1 * _k0);
524
+ // Explicitly enforce the parity
525
+ int
526
+ xisign = xi < 0 ? -1 : 1,
527
+ etasign = eta < 0 ? -1 : 1;
528
+ xi *= xisign;
529
+ eta *= etasign;
530
+ bool backside = xi > Math::pi()/2;
531
+ if (backside)
532
+ xi = Math::pi() - xi;
533
+ real
534
+ c0 = cos(2 * xi), ch0 = cosh(2 * eta),
535
+ s0 = sin(2 * xi), sh0 = sinh(2 * eta),
536
+ ar = 2 * c0 * ch0, ai = -2 * s0 * sh0; // 2 * cos(2*zeta)
537
+ int n = maxpow_;
538
+ real // Accumulators for zeta'
539
+ xip0 = (n & 1 ? -_bet[n] : 0), etap0 = 0,
540
+ xip1 = 0, etap1 = 0;
541
+ real // Accumulators for dzeta'/dzeta
542
+ yr0 = (n & 1 ? - 2 * maxpow_ * _bet[n--] : 0), yi0 = 0,
543
+ yr1 = 0, yi1 = 0;
544
+ while (n) {
545
+ xip1 = ar * xip0 - ai * etap0 - xip1 - _bet[n];
546
+ etap1 = ai * xip0 + ar * etap0 - etap1;
547
+ yr1 = ar * yr0 - ai * yi0 - yr1 - 2 * n * _bet[n];
548
+ yi1 = ai * yr0 + ar * yi0 - yi1;
549
+ --n;
550
+ xip0 = ar * xip1 - ai * etap1 - xip0 - _bet[n];
551
+ etap0 = ai * xip1 + ar * etap1 - etap0;
552
+ yr0 = ar * yr1 - ai * yi1 - yr0 - 2 * n * _bet[n];
553
+ yi0 = ai * yr1 + ar * yi1 - yi0;
554
+ --n;
555
+ }
556
+ ar /= 2; ai /= 2; // cos(2*zeta')
557
+ yr1 = 1 - yr1 + ar * yr0 - ai * yi0;
558
+ yi1 = - yi1 + ai * yr0 + ar * yi0;
559
+ ar = s0 * ch0; ai = c0 * sh0; // sin(2*zeta)
560
+ real
561
+ xip = xi + ar * xip0 - ai * etap0,
562
+ etap = eta + ai * xip0 + ar * etap0;
563
+ // Convergence and scale for Gauss-Schreiber TM to Gauss-Krueger TM.
564
+ gamma = Math::atan2d(yi1, yr1);
565
+ k = _b1 / Math::hypot(yr1, yi1);
566
+ // JHS 154 has
567
+ //
568
+ // phi' = asin(sin(xi') / cosh(eta')) (Krueger p 17 (25))
569
+ // lam = asin(tanh(eta') / cos(phi')
570
+ // psi = asinh(tan(phi'))
571
+ real
572
+ s = sinh(etap),
573
+ c = max(real(0), cos(xip)), // cos(pi/2) might be negative
574
+ r = Math::hypot(s, c);
575
+ if (r != 0) {
576
+ lon = Math::atan2d(s, c); // Krueger p 17 (25)
577
+ // Use Newton's method to solve for tau
578
+ real
579
+ sxip = sin(xip),
580
+ tau = Math::tauf(sxip/r, _es);
581
+ gamma += Math::atan2d(sxip * tanh(etap), c); // Krueger p 19 (31)
582
+ lat = Math::atand(tau);
583
+ // Note cos(phi') * cosh(eta') = r
584
+ k *= sqrt(_e2m + _e2 / (1 + Math::sq(tau))) *
585
+ Math::hypot(real(1), tau) * r;
586
+ } else {
587
+ lat = 90;
588
+ lon = 0;
589
+ k *= _c;
590
+ }
591
+ lat *= xisign;
592
+ if (backside)
593
+ lon = 180 - lon;
594
+ lon *= etasign;
595
+ lon = Math::AngNormalize(lon + lon0);
596
+ if (backside)
597
+ gamma = 180 - gamma;
598
+ gamma *= xisign * etasign;
599
+ gamma = Math::AngNormalize(gamma);
600
+ k *= _k0;
601
+ }
602
+
603
+ } // namespace GeographicLib