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,456 @@
1
+ /**
2
+ * \file LambertConformalConic.cpp
3
+ * \brief Implementation for GeographicLib::LambertConformalConic class
4
+ *
5
+ * Copyright (c) Charles Karney (2010-2015) <charles@karney.com> and licensed
6
+ * under the MIT/X11 License. For more information, see
7
+ * http://geographiclib.sourceforge.net/
8
+ **********************************************************************/
9
+
10
+ #include <GeographicLib/LambertConformalConic.hpp>
11
+
12
+ namespace GeographicLib {
13
+
14
+ using namespace std;
15
+
16
+ LambertConformalConic::LambertConformalConic(real a, real f,
17
+ real stdlat, real k0)
18
+ : eps_(numeric_limits<real>::epsilon())
19
+ , epsx_(Math::sq(eps_))
20
+ , ahypover_(Math::digits() * log(real(numeric_limits<real>::radix)) + 2)
21
+ , _a(a)
22
+ , _f(f)
23
+ , _fm(1 - _f)
24
+ , _e2(_f * (2 - _f))
25
+ , _es((_f < 0 ? -1 : 1) * sqrt(abs(_e2)))
26
+ {
27
+ if (!(Math::isfinite(_a) && _a > 0))
28
+ throw GeographicErr("Major radius is not positive");
29
+ if (!(Math::isfinite(_f) && _f < 1))
30
+ throw GeographicErr("Minor radius is not positive");
31
+ if (!(Math::isfinite(k0) && k0 > 0))
32
+ throw GeographicErr("Scale is not positive");
33
+ if (!(abs(stdlat) <= 90))
34
+ throw GeographicErr("Standard latitude not in [-90d, 90d]");
35
+ real sphi, cphi;
36
+ Math::sincosd(stdlat, sphi, cphi);
37
+ Init(sphi, cphi, sphi, cphi, k0);
38
+ }
39
+
40
+ LambertConformalConic::LambertConformalConic(real a, real f,
41
+ real stdlat1, real stdlat2,
42
+ real k1)
43
+ : eps_(numeric_limits<real>::epsilon())
44
+ , epsx_(Math::sq(eps_))
45
+ , ahypover_(Math::digits() * log(real(numeric_limits<real>::radix)) + 2)
46
+ , _a(a)
47
+ , _f(f)
48
+ , _fm(1 - _f)
49
+ , _e2(_f * (2 - _f))
50
+ , _es((_f < 0 ? -1 : 1) * sqrt(abs(_e2)))
51
+ {
52
+ if (!(Math::isfinite(_a) && _a > 0))
53
+ throw GeographicErr("Major radius is not positive");
54
+ if (!(Math::isfinite(_f) && _f < 1))
55
+ throw GeographicErr("Minor radius is not positive");
56
+ if (!(Math::isfinite(k1) && k1 > 0))
57
+ throw GeographicErr("Scale is not positive");
58
+ if (!(abs(stdlat1) <= 90))
59
+ throw GeographicErr("Standard latitude 1 not in [-90d, 90d]");
60
+ if (!(abs(stdlat2) <= 90))
61
+ throw GeographicErr("Standard latitude 2 not in [-90d, 90d]");
62
+ real sphi1, cphi1, sphi2, cphi2;
63
+ Math::sincosd(stdlat1, sphi1, cphi1);
64
+ Math::sincosd(stdlat2, sphi2, cphi2);
65
+ Init(sphi1, cphi1, sphi2, cphi2, k1);
66
+ }
67
+
68
+ LambertConformalConic::LambertConformalConic(real a, real f,
69
+ real sinlat1, real coslat1,
70
+ real sinlat2, real coslat2,
71
+ real k1)
72
+ : eps_(numeric_limits<real>::epsilon())
73
+ , epsx_(Math::sq(eps_))
74
+ , ahypover_(Math::digits() * log(real(numeric_limits<real>::radix)) + 2)
75
+ , _a(a)
76
+ , _f(f)
77
+ , _fm(1 - _f)
78
+ , _e2(_f * (2 - _f))
79
+ , _es((_f < 0 ? -1 : 1) * sqrt(abs(_e2)))
80
+ {
81
+ if (!(Math::isfinite(_a) && _a > 0))
82
+ throw GeographicErr("Major radius is not positive");
83
+ if (!(Math::isfinite(_f) && _f < 1))
84
+ throw GeographicErr("Minor radius is not positive");
85
+ if (!(Math::isfinite(k1) && k1 > 0))
86
+ throw GeographicErr("Scale is not positive");
87
+ if (!(coslat1 >= 0))
88
+ throw GeographicErr("Standard latitude 1 not in [-90d, 90d]");
89
+ if (!(coslat2 >= 0))
90
+ throw GeographicErr("Standard latitude 2 not in [-90d, 90d]");
91
+ if (!(abs(sinlat1) <= 1 && coslat1 <= 1) || (coslat1 == 0 && sinlat1 == 0))
92
+ throw GeographicErr("Bad sine/cosine of standard latitude 1");
93
+ if (!(abs(sinlat2) <= 1 && coslat2 <= 1) || (coslat2 == 0 && sinlat2 == 0))
94
+ throw GeographicErr("Bad sine/cosine of standard latitude 2");
95
+ if (coslat1 == 0 || coslat2 == 0)
96
+ if (!(coslat1 == coslat2 && sinlat1 == sinlat2))
97
+ throw GeographicErr
98
+ ("Standard latitudes must be equal is either is a pole");
99
+ Init(sinlat1, coslat1, sinlat2, coslat2, k1);
100
+ }
101
+
102
+ void LambertConformalConic::Init(real sphi1, real cphi1,
103
+ real sphi2, real cphi2, real k1) {
104
+ {
105
+ real r;
106
+ r = Math::hypot(sphi1, cphi1);
107
+ sphi1 /= r; cphi1 /= r;
108
+ r = Math::hypot(sphi2, cphi2);
109
+ sphi2 /= r; cphi2 /= r;
110
+ }
111
+ bool polar = (cphi1 == 0);
112
+ cphi1 = max(epsx_, cphi1); // Avoid singularities at poles
113
+ cphi2 = max(epsx_, cphi2);
114
+ // Determine hemisphere of tangent latitude
115
+ _sign = sphi1 + sphi2 >= 0 ? 1 : -1;
116
+ // Internally work with tangent latitude positive
117
+ sphi1 *= _sign; sphi2 *= _sign;
118
+ if (sphi1 > sphi2) {
119
+ swap(sphi1, sphi2); swap(cphi1, cphi2); // Make phi1 < phi2
120
+ }
121
+ real
122
+ tphi1 = sphi1/cphi1, tphi2 = sphi2/cphi2, tphi0;
123
+ //
124
+ // Snyder: 15-8: n = (log(m1) - log(m2))/(log(t1)-log(t2))
125
+ //
126
+ // m = cos(bet) = 1/sec(bet) = 1/sqrt(1+tan(bet)^2)
127
+ // bet = parametric lat, tan(bet) = (1-f)*tan(phi)
128
+ //
129
+ // t = tan(pi/4-chi/2) = 1/(sec(chi) + tan(chi)) = sec(chi) - tan(chi)
130
+ // log(t) = -asinh(tan(chi)) = -psi
131
+ // chi = conformal lat
132
+ // tan(chi) = tan(phi)*cosh(xi) - sinh(xi)*sec(phi)
133
+ // xi = eatanhe(sin(phi)), eatanhe(x) = e * atanh(e*x)
134
+ //
135
+ // n = (log(sec(bet2))-log(sec(bet1)))/(asinh(tan(chi2))-asinh(tan(chi1)))
136
+ //
137
+ // Let log(sec(bet)) = b(tphi), asinh(tan(chi)) = c(tphi)
138
+ // Then n = Db(tphi2, tphi1)/Dc(tphi2, tphi1)
139
+ // In limit tphi2 -> tphi1, n -> sphi1
140
+ //
141
+ real
142
+ tbet1 = _fm * tphi1, scbet1 = hyp(tbet1),
143
+ tbet2 = _fm * tphi2, scbet2 = hyp(tbet2);
144
+ real
145
+ scphi1 = 1/cphi1,
146
+ xi1 = Math::eatanhe(sphi1, _es), shxi1 = sinh(xi1), chxi1 = hyp(shxi1),
147
+ tchi1 = chxi1 * tphi1 - shxi1 * scphi1, scchi1 = hyp(tchi1),
148
+ scphi2 = 1/cphi2,
149
+ xi2 = Math::eatanhe(sphi2, _es), shxi2 = sinh(xi2), chxi2 = hyp(shxi2),
150
+ tchi2 = chxi2 * tphi2 - shxi2 * scphi2, scchi2 = hyp(tchi2),
151
+ psi1 = Math::asinh(tchi1);
152
+ if (tphi2 - tphi1 != 0) {
153
+ // Db(tphi2, tphi1)
154
+ real num = Dlog1p(Math::sq(tbet2)/(1 + scbet2),
155
+ Math::sq(tbet1)/(1 + scbet1))
156
+ * Dhyp(tbet2, tbet1, scbet2, scbet1) * _fm;
157
+ // Dc(tphi2, tphi1)
158
+ real den = Dasinh(tphi2, tphi1, scphi2, scphi1)
159
+ - Deatanhe(sphi2, sphi1) * Dsn(tphi2, tphi1, sphi2, sphi1);
160
+ _n = num/den;
161
+
162
+ if (_n < 0.25)
163
+ _nc = sqrt((1 - _n) * (1 + _n));
164
+ else {
165
+ // Compute nc = cos(phi0) = sqrt((1 - n) * (1 + n)), evaluating 1 - n
166
+ // carefully. First write
167
+ //
168
+ // Dc(tphi2, tphi1) * (tphi2 - tphi1)
169
+ // = log(tchi2 + scchi2) - log(tchi1 + scchi1)
170
+ //
171
+ // then den * (1 - n) =
172
+ // (log((tchi2 + scchi2)/(2*scbet2)) - log((tchi1 + scchi1)/(2*scbet1)))
173
+ // / (tphi2 - tphi1)
174
+ // = Dlog1p(a2, a1) * (tchi2+scchi2 + tchi1+scchi1)/(4*scbet1*scbet2)
175
+ // * fm * Q
176
+ //
177
+ // where
178
+ // a1 = ( (tchi1 - scbet1) + (scchi1 - scbet1) ) / (2 * scbet1)
179
+ // Q = ((scbet2 + scbet1)/fm)/((scchi2 + scchi1)/D(tchi2, tchi1))
180
+ // - (tbet2 + tbet1)/(scbet2 + scbet1)
181
+ real t;
182
+ {
183
+ real
184
+ // s1 = (scbet1 - scchi1) * (scbet1 + scchi1)
185
+ s1 = (tphi1 * (2 * shxi1 * chxi1 * scphi1 - _e2 * tphi1) -
186
+ Math::sq(shxi1) * (1 + 2 * Math::sq(tphi1))),
187
+ s2 = (tphi2 * (2 * shxi2 * chxi2 * scphi2 - _e2 * tphi2) -
188
+ Math::sq(shxi2) * (1 + 2 * Math::sq(tphi2))),
189
+ // t1 = scbet1 - tchi1
190
+ t1 = tchi1 < 0 ? scbet1 - tchi1 : (s1 + 1)/(scbet1 + tchi1),
191
+ t2 = tchi2 < 0 ? scbet2 - tchi2 : (s2 + 1)/(scbet2 + tchi2),
192
+ a2 = -(s2 / (scbet2 + scchi2) + t2) / (2 * scbet2),
193
+ a1 = -(s1 / (scbet1 + scchi1) + t1) / (2 * scbet1);
194
+ t = Dlog1p(a2, a1) / den;
195
+ }
196
+ // multiply by (tchi2 + scchi2 + tchi1 + scchi1)/(4*scbet1*scbet2) * fm
197
+ t *= ( ( (tchi2 >= 0 ? scchi2 + tchi2 : 1/(scchi2 - tchi2)) +
198
+ (tchi1 >= 0 ? scchi1 + tchi1 : 1/(scchi1 - tchi1)) ) /
199
+ (4 * scbet1 * scbet2) ) * _fm;
200
+
201
+ // Rewrite
202
+ // Q = (1 - (tbet2 + tbet1)/(scbet2 + scbet1)) -
203
+ // (1 - ((scbet2 + scbet1)/fm)/((scchi2 + scchi1)/D(tchi2, tchi1)))
204
+ // = tbm - tam
205
+ // where
206
+ real tbm = ( ((tbet1 > 0 ? 1/(scbet1+tbet1) : scbet1 - tbet1) +
207
+ (tbet2 > 0 ? 1/(scbet2+tbet2) : scbet2 - tbet2)) /
208
+ (scbet1+scbet2) );
209
+
210
+ // tam = (1 - ((scbet2+scbet1)/fm)/((scchi2+scchi1)/D(tchi2, tchi1)))
211
+ //
212
+ // Let
213
+ // (scbet2 + scbet1)/fm = scphi2 + scphi1 + dbet
214
+ // (scchi2 + scchi1)/D(tchi2, tchi1) = scphi2 + scphi1 + dchi
215
+ // then
216
+ // tam = D(tchi2, tchi1) * (dchi - dbet) / (scchi1 + scchi2)
217
+ real
218
+ // D(tchi2, tchi1)
219
+ dtchi = den / Dasinh(tchi2, tchi1, scchi2, scchi1),
220
+ // (scbet2 + scbet1)/fm - (scphi2 + scphi1)
221
+ dbet = (_e2/_fm) * ( 1 / (scbet2 + _fm * scphi2) +
222
+ 1 / (scbet1 + _fm * scphi1) );
223
+
224
+ // dchi = (scchi2 + scchi1)/D(tchi2, tchi1) - (scphi2 + scphi1)
225
+ // Let
226
+ // tzet = chxiZ * tphi - shxiZ * scphi
227
+ // tchi = tzet + nu
228
+ // scchi = sczet + mu
229
+ // where
230
+ // xiZ = eatanhe(1), shxiZ = sinh(xiZ), chxiZ = cosh(xiZ)
231
+ // nu = scphi * (shxiZ - shxi) - tphi * (chxiZ - chxi)
232
+ // mu = - scphi * (chxiZ - chxi) + tphi * (shxiZ - shxi)
233
+ // then
234
+ // dchi = ((mu2 + mu1) - D(nu2, nu1) * (scphi2 + scphi1)) /
235
+ // D(tchi2, tchi1)
236
+ real
237
+ xiZ = Math::eatanhe(real(1), _es),
238
+ shxiZ = sinh(xiZ), chxiZ = hyp(shxiZ),
239
+ // These are differences not divided differences
240
+ // dxiZ1 = xiZ - xi1; dshxiZ1 = shxiZ - shxi; dchxiZ1 = chxiZ - chxi
241
+ dxiZ1 = Deatanhe(real(1), sphi1)/(scphi1*(tphi1+scphi1)),
242
+ dxiZ2 = Deatanhe(real(1), sphi2)/(scphi2*(tphi2+scphi2)),
243
+ dshxiZ1 = Dsinh(xiZ, xi1, shxiZ, shxi1, chxiZ, chxi1) * dxiZ1,
244
+ dshxiZ2 = Dsinh(xiZ, xi2, shxiZ, shxi2, chxiZ, chxi2) * dxiZ2,
245
+ dchxiZ1 = Dhyp(shxiZ, shxi1, chxiZ, chxi1) * dshxiZ1,
246
+ dchxiZ2 = Dhyp(shxiZ, shxi2, chxiZ, chxi2) * dshxiZ2,
247
+ // mu1 + mu2
248
+ amu12 = (- scphi1 * dchxiZ1 + tphi1 * dshxiZ1
249
+ - scphi2 * dchxiZ2 + tphi2 * dshxiZ2),
250
+ // D(xi2, xi1)
251
+ dxi = Deatanhe(sphi1, sphi2) * Dsn(tphi2, tphi1, sphi2, sphi1),
252
+ // D(nu2, nu1)
253
+ dnu12 =
254
+ ( (_f * 4 * scphi2 * dshxiZ2 > _f * scphi1 * dshxiZ1 ?
255
+ // Use divided differences
256
+ (dshxiZ1 + dshxiZ2)/2 * Dhyp(tphi1, tphi2, scphi1, scphi2)
257
+ - ( (scphi1 + scphi2)/2
258
+ * Dsinh(xi1, xi2, shxi1, shxi2, chxi1, chxi2) * dxi ) :
259
+ // Use ratio of differences
260
+ (scphi2 * dshxiZ2 - scphi1 * dshxiZ1)/(tphi2 - tphi1))
261
+ + ( (tphi1 + tphi2)/2 * Dhyp(shxi1, shxi2, chxi1, chxi2)
262
+ * Dsinh(xi1, xi2, shxi1, shxi2, chxi1, chxi2) * dxi )
263
+ - (dchxiZ1 + dchxiZ2)/2 ),
264
+ // dtchi * dchi
265
+ dchia = (amu12 - dnu12 * (scphi2 + scphi1)),
266
+ tam = (dchia - dtchi * dbet) / (scchi1 + scchi2);
267
+ t *= tbm - tam;
268
+ _nc = sqrt(max(real(0), t) * (1 + _n));
269
+ }
270
+ {
271
+ real r = Math::hypot(_n, _nc);
272
+ _n /= r;
273
+ _nc /= r;
274
+ }
275
+ tphi0 = _n / _nc;
276
+ } else {
277
+ tphi0 = tphi1;
278
+ _nc = 1/hyp(tphi0);
279
+ _n = tphi0 * _nc;
280
+ if (polar)
281
+ _nc = 0;
282
+ }
283
+
284
+ _scbet0 = hyp(_fm * tphi0);
285
+ real shxi0 = sinh(Math::eatanhe(_n, _es));
286
+ _tchi0 = tphi0 * hyp(shxi0) - shxi0 * hyp(tphi0); _scchi0 = hyp(_tchi0);
287
+ _psi0 = Math::asinh(_tchi0);
288
+
289
+ _lat0 = atan(_sign * tphi0) / Math::degree();
290
+ _t0nm1 = Math::expm1(- _n * _psi0); // Snyder's t0^n - 1
291
+ // a * k1 * m1/t1^n = a * k1 * m2/t2^n = a * k1 * n * (Snyder's F)
292
+ // = a * k1 / (scbet1 * exp(-n * psi1))
293
+ _scale = _a * k1 / scbet1 *
294
+ // exp(n * psi1) = exp(- (1 - n) * psi1) * exp(psi1)
295
+ // with (1-n) = nc^2/(1+n) and exp(-psi1) = scchi1 + tchi1
296
+ exp( - (Math::sq(_nc)/(1 + _n)) * psi1 )
297
+ * (tchi1 >= 0 ? scchi1 + tchi1 : 1 / (scchi1 - tchi1));
298
+ // Scale at phi0 = k0 = k1 * (scbet0*exp(-n*psi0))/(scbet1*exp(-n*psi1))
299
+ // = k1 * scbet0/scbet1 * exp(n * (psi1 - psi0))
300
+ // psi1 - psi0 = Dasinh(tchi1, tchi0) * (tchi1 - tchi0)
301
+ _k0 = k1 * (_scbet0/scbet1) *
302
+ exp( - (Math::sq(_nc)/(1 + _n)) *
303
+ Dasinh(tchi1, _tchi0, scchi1, _scchi0) * (tchi1 - _tchi0))
304
+ * (tchi1 >= 0 ? scchi1 + tchi1 : 1 / (scchi1 - tchi1)) /
305
+ (_scchi0 + _tchi0);
306
+ _nrho0 = polar ? 0 : _a * _k0 / _scbet0;
307
+ {
308
+ // Figure _drhomax using code at beginning of Forward with lat = -90
309
+ real
310
+ sphi = -1, cphi = epsx_,
311
+ tphi = sphi/cphi,
312
+ scphi = 1/cphi, shxi = sinh(Math::eatanhe(sphi, _es)),
313
+ tchi = hyp(shxi) * tphi - shxi * scphi, scchi = hyp(tchi),
314
+ psi = Math::asinh(tchi),
315
+ dpsi = Dasinh(tchi, _tchi0, scchi, _scchi0) * (tchi - _tchi0);
316
+ _drhomax = - _scale * (2 * _nc < 1 && dpsi != 0 ?
317
+ (exp(Math::sq(_nc)/(1 + _n) * psi ) *
318
+ (tchi > 0 ? 1/(scchi + tchi) : (scchi - tchi))
319
+ - (_t0nm1 + 1))/(-_n) :
320
+ Dexp(-_n * psi, -_n * _psi0) * dpsi);
321
+ }
322
+ }
323
+
324
+ const LambertConformalConic& LambertConformalConic::Mercator() {
325
+ static const LambertConformalConic mercator(Constants::WGS84_a(),
326
+ Constants::WGS84_f(),
327
+ real(0), real(1));
328
+ return mercator;
329
+ }
330
+
331
+ void LambertConformalConic::Forward(real lon0, real lat, real lon,
332
+ real& x, real& y, real& gamma, real& k)
333
+ const {
334
+ lon = Math::AngDiff(lon0, lon);
335
+ // From Snyder, we have
336
+ //
337
+ // theta = n * lambda
338
+ // x = rho * sin(theta)
339
+ // = (nrho0 + n * drho) * sin(theta)/n
340
+ // y = rho0 - rho * cos(theta)
341
+ // = nrho0 * (1-cos(theta))/n - drho * cos(theta)
342
+ //
343
+ // where nrho0 = n * rho0, drho = rho - rho0
344
+ // and drho is evaluated with divided differences
345
+ real sphi, cphi;
346
+ Math::sincosd(Math::LatFix(lat) * _sign, sphi, cphi);
347
+ cphi = max(epsx_, cphi);
348
+ real
349
+ lam = lon * Math::degree(),
350
+ tphi = sphi/cphi, scbet = hyp(_fm * tphi),
351
+ scphi = 1/cphi, shxi = sinh(Math::eatanhe(sphi, _es)),
352
+ tchi = hyp(shxi) * tphi - shxi * scphi, scchi = hyp(tchi),
353
+ psi = Math::asinh(tchi),
354
+ theta = _n * lam, stheta = sin(theta), ctheta = cos(theta),
355
+ dpsi = Dasinh(tchi, _tchi0, scchi, _scchi0) * (tchi - _tchi0),
356
+ drho = - _scale * (2 * _nc < 1 && dpsi != 0 ?
357
+ (exp(Math::sq(_nc)/(1 + _n) * psi ) *
358
+ (tchi > 0 ? 1/(scchi + tchi) : (scchi - tchi))
359
+ - (_t0nm1 + 1))/(-_n) :
360
+ Dexp(-_n * psi, -_n * _psi0) * dpsi);
361
+ x = (_nrho0 + _n * drho) * (_n ? stheta / _n : lam);
362
+ y = _nrho0 *
363
+ (_n ?
364
+ (ctheta < 0 ? 1 - ctheta : Math::sq(stheta)/(1 + ctheta)) / _n : 0)
365
+ - drho * ctheta;
366
+ k = _k0 * (scbet/_scbet0) /
367
+ (exp( - (Math::sq(_nc)/(1 + _n)) * dpsi )
368
+ * (tchi >= 0 ? scchi + tchi : 1 / (scchi - tchi)) / (_scchi0 + _tchi0));
369
+ y *= _sign;
370
+ gamma = _sign * theta / Math::degree();
371
+ }
372
+
373
+ void LambertConformalConic::Reverse(real lon0, real x, real y,
374
+ real& lat, real& lon,
375
+ real& gamma, real& k)
376
+ const {
377
+ // From Snyder, we have
378
+ //
379
+ // x = rho * sin(theta)
380
+ // rho0 - y = rho * cos(theta)
381
+ //
382
+ // rho = hypot(x, rho0 - y)
383
+ // drho = (n*x^2 - 2*y*nrho0 + n*y^2)/(hypot(n*x, nrho0-n*y) + nrho0)
384
+ // theta = atan2(n*x, nrho0-n*y)
385
+ //
386
+ // From drho, obtain t^n-1
387
+ // psi = -log(t), so
388
+ // dpsi = - Dlog1p(t^n-1, t0^n-1) * drho / scale
389
+ y *= _sign;
390
+ real
391
+ // Guard against 0 * inf in computation of ny
392
+ nx = _n * x, ny = _n ? _n * y : 0, y1 = _nrho0 - ny,
393
+ den = Math::hypot(nx, y1) + _nrho0, // 0 implies origin with polar aspect
394
+ // isfinite test is to avoid inf/inf
395
+ drho = ((den != 0 && Math::isfinite(den))
396
+ ? (x*nx + y * (ny - 2*_nrho0)) / den
397
+ : den);
398
+ drho = min(drho, _drhomax);
399
+ if (_n == 0)
400
+ drho = max(drho, -_drhomax);
401
+ real
402
+ tnm1 = _t0nm1 + _n * drho/_scale,
403
+ dpsi = (den == 0 ? 0 :
404
+ (tnm1 + 1 != 0 ? - Dlog1p(tnm1, _t0nm1) * drho / _scale :
405
+ ahypover_));
406
+ real tchi;
407
+ if (2 * _n <= 1) {
408
+ // tchi = sinh(psi)
409
+ real
410
+ psi = _psi0 + dpsi, tchia = sinh(psi), scchi = hyp(tchia),
411
+ dtchi = Dsinh(psi, _psi0, tchia, _tchi0, scchi, _scchi0) * dpsi;
412
+ tchi = _tchi0 + dtchi; // Update tchi using divided difference
413
+ } else {
414
+ // tchi = sinh(-1/n * log(tn))
415
+ // = sinh((1-1/n) * log(tn) - log(tn))
416
+ // = + sinh((1-1/n) * log(tn)) * cosh(log(tn))
417
+ // - cosh((1-1/n) * log(tn)) * sinh(log(tn))
418
+ // (1-1/n) = - nc^2/(n*(1+n))
419
+ // cosh(log(tn)) = (tn + 1/tn)/2; sinh(log(tn)) = (tn - 1/tn)/2
420
+ real
421
+ tn = tnm1 + 1 == 0 ? epsx_ : tnm1 + 1,
422
+ sh = sinh( -Math::sq(_nc)/(_n * (1 + _n)) *
423
+ (2 * tn > 1 ? Math::log1p(tnm1) : log(tn)) );
424
+ tchi = sh * (tn + 1/tn)/2 - hyp(sh) * (tnm1 * (tn + 1)/tn)/2;
425
+ }
426
+
427
+ // log(t) = -asinh(tan(chi)) = -psi
428
+ gamma = atan2(nx, y1);
429
+ real
430
+ tphi = Math::tauf(tchi, _es),
431
+ scbet = hyp(_fm * tphi), scchi = hyp(tchi),
432
+ lam = _n ? gamma / _n : x / y1;
433
+ lat = Math::atand(_sign * tphi);
434
+ lon = lam / Math::degree();
435
+ lon = Math::AngNormalize(lon + Math::AngNormalize(lon0));
436
+ k = _k0 * (scbet/_scbet0) /
437
+ (exp(_nc ? - (Math::sq(_nc)/(1 + _n)) * dpsi : 0)
438
+ * (tchi >= 0 ? scchi + tchi : 1 / (scchi - tchi)) / (_scchi0 + _tchi0));
439
+ gamma /= _sign * Math::degree();
440
+ }
441
+
442
+ void LambertConformalConic::SetScale(real lat, real k) {
443
+ if (!(Math::isfinite(k) && k > 0))
444
+ throw GeographicErr("Scale is not positive");
445
+ if (!(abs(lat) <= 90))
446
+ throw GeographicErr("Latitude for SetScale not in [-90d, 90d]");
447
+ if (abs(lat) == 90 && !(_nc == 0 && lat * _n > 0))
448
+ throw GeographicErr("Incompatible polar latitude in SetScale");
449
+ real x, y, gamma, kold;
450
+ Forward(0, lat, 0, x, y, gamma, kold);
451
+ k /= kold;
452
+ _scale *= k;
453
+ _k0 *= k;
454
+ }
455
+
456
+ } // namespace GeographicLib