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,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