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,321 @@
1
+ /**
2
+ * \file GeodesicLine.cpp
3
+ * \brief Implementation for GeographicLib::GeodesicLine class
4
+ *
5
+ * Copyright (c) Charles Karney (2009-2016) <charles@karney.com> and licensed
6
+ * under the MIT/X11 License. For more information, see
7
+ * http://geographiclib.sourceforge.net/
8
+ *
9
+ * This is a reformulation of the geodesic problem. The notation is as
10
+ * follows:
11
+ * - at a general point (no suffix or 1 or 2 as suffix)
12
+ * - phi = latitude
13
+ * - beta = latitude on auxiliary sphere
14
+ * - omega = longitude on auxiliary sphere
15
+ * - lambda = longitude
16
+ * - alpha = azimuth of great circle
17
+ * - sigma = arc length along great circle
18
+ * - s = distance
19
+ * - tau = scaled distance (= sigma at multiples of pi/2)
20
+ * - at northwards equator crossing
21
+ * - beta = phi = 0
22
+ * - omega = lambda = 0
23
+ * - alpha = alpha0
24
+ * - sigma = s = 0
25
+ * - a 12 suffix means a difference, e.g., s12 = s2 - s1.
26
+ * - s and c prefixes mean sin and cos
27
+ **********************************************************************/
28
+
29
+ #include <GeographicLib/GeodesicLine.hpp>
30
+
31
+ namespace GeographicLib {
32
+
33
+ using namespace std;
34
+
35
+ void GeodesicLine::LineInit(const Geodesic& g,
36
+ real lat1, real lon1,
37
+ real azi1, real salp1, real calp1,
38
+ unsigned caps) {
39
+ tiny_ = g.tiny_;
40
+ _lat1 = Math::LatFix(lat1);
41
+ _lon1 = lon1;
42
+ _azi1 = azi1;
43
+ _salp1 = salp1;
44
+ _calp1 = calp1;
45
+ _a = g._a;
46
+ _f = g._f;
47
+ _b = g._b;
48
+ _c2 = g._c2;
49
+ _f1 = g._f1;
50
+ // Always allow latitude and azimuth and unrolling of longitude
51
+ _caps = caps | LATITUDE | AZIMUTH | LONG_UNROLL;
52
+
53
+ real cbet1, sbet1;
54
+ Math::sincosd(Math::AngRound(_lat1), sbet1, cbet1); sbet1 *= _f1;
55
+ // Ensure cbet1 = +epsilon at poles
56
+ Math::norm(sbet1, cbet1); cbet1 = max(tiny_, cbet1);
57
+ _dn1 = sqrt(1 + g._ep2 * Math::sq(sbet1));
58
+
59
+ // Evaluate alp0 from sin(alp1) * cos(bet1) = sin(alp0),
60
+ _salp0 = _salp1 * cbet1; // alp0 in [0, pi/2 - |bet1|]
61
+ // Alt: calp0 = hypot(sbet1, calp1 * cbet1). The following
62
+ // is slightly better (consider the case salp1 = 0).
63
+ _calp0 = Math::hypot(_calp1, _salp1 * sbet1);
64
+ // Evaluate sig with tan(bet1) = tan(sig1) * cos(alp1).
65
+ // sig = 0 is nearest northward crossing of equator.
66
+ // With bet1 = 0, alp1 = pi/2, we have sig1 = 0 (equatorial line).
67
+ // With bet1 = pi/2, alp1 = -pi, sig1 = pi/2
68
+ // With bet1 = -pi/2, alp1 = 0 , sig1 = -pi/2
69
+ // Evaluate omg1 with tan(omg1) = sin(alp0) * tan(sig1).
70
+ // With alp0 in (0, pi/2], quadrants for sig and omg coincide.
71
+ // No atan2(0,0) ambiguity at poles since cbet1 = +epsilon.
72
+ // With alp0 = 0, omg1 = 0 for alp1 = 0, omg1 = pi for alp1 = pi.
73
+ _ssig1 = sbet1; _somg1 = _salp0 * sbet1;
74
+ _csig1 = _comg1 = sbet1 != 0 || _calp1 != 0 ? cbet1 * _calp1 : 1;
75
+ Math::norm(_ssig1, _csig1); // sig1 in (-pi, pi]
76
+ // Math::norm(_somg1, _comg1); -- don't need to normalize!
77
+
78
+ _k2 = Math::sq(_calp0) * g._ep2;
79
+ real eps = _k2 / (2 * (1 + sqrt(1 + _k2)) + _k2);
80
+
81
+ if (_caps & CAP_C1) {
82
+ _A1m1 = Geodesic::A1m1f(eps);
83
+ Geodesic::C1f(eps, _C1a);
84
+ _B11 = Geodesic::SinCosSeries(true, _ssig1, _csig1, _C1a, nC1_);
85
+ real s = sin(_B11), c = cos(_B11);
86
+ // tau1 = sig1 + B11
87
+ _stau1 = _ssig1 * c + _csig1 * s;
88
+ _ctau1 = _csig1 * c - _ssig1 * s;
89
+ // Not necessary because C1pa reverts C1a
90
+ // _B11 = -SinCosSeries(true, _stau1, _ctau1, _C1pa, nC1p_);
91
+ }
92
+
93
+ if (_caps & CAP_C1p)
94
+ Geodesic::C1pf(eps, _C1pa);
95
+
96
+ if (_caps & CAP_C2) {
97
+ _A2m1 = Geodesic::A2m1f(eps);
98
+ Geodesic::C2f(eps, _C2a);
99
+ _B21 = Geodesic::SinCosSeries(true, _ssig1, _csig1, _C2a, nC2_);
100
+ }
101
+
102
+ if (_caps & CAP_C3) {
103
+ g.C3f(eps, _C3a);
104
+ _A3c = -_f * _salp0 * g.A3f(eps);
105
+ _B31 = Geodesic::SinCosSeries(true, _ssig1, _csig1, _C3a, nC3_-1);
106
+ }
107
+
108
+ if (_caps & CAP_C4) {
109
+ g.C4f(eps, _C4a);
110
+ // Multiplier = a^2 * e^2 * cos(alpha0) * sin(alpha0)
111
+ _A4 = Math::sq(_a) * _calp0 * _salp0 * g._e2;
112
+ _B41 = Geodesic::SinCosSeries(false, _ssig1, _csig1, _C4a, nC4_);
113
+ }
114
+
115
+ _a13 = _s13 = Math::NaN();
116
+ }
117
+
118
+ GeodesicLine::GeodesicLine(const Geodesic& g,
119
+ real lat1, real lon1, real azi1,
120
+ unsigned caps) {
121
+ azi1 = Math::AngNormalize(azi1);
122
+ real salp1, calp1;
123
+ // Guard against underflow in salp0. Also -0 is converted to +0.
124
+ Math::sincosd(Math::AngRound(azi1), salp1, calp1);
125
+ LineInit(g, lat1, lon1, azi1, salp1, calp1, caps);
126
+ }
127
+
128
+ GeodesicLine::GeodesicLine(const Geodesic& g,
129
+ real lat1, real lon1,
130
+ real azi1, real salp1, real calp1,
131
+ unsigned caps, bool arcmode, real s13_a13) {
132
+ LineInit(g, lat1, lon1, azi1, salp1, calp1, caps);
133
+ GenSetDistance(arcmode, s13_a13);
134
+ }
135
+
136
+ Math::real GeodesicLine::GenPosition(bool arcmode, real s12_a12,
137
+ unsigned outmask,
138
+ real& lat2, real& lon2, real& azi2,
139
+ real& s12, real& m12,
140
+ real& M12, real& M21,
141
+ real& S12)
142
+ const {
143
+ outmask &= _caps & OUT_MASK;
144
+ if (!( Init() && (arcmode || (_caps & (OUT_MASK & DISTANCE_IN))) ))
145
+ // Uninitialized or impossible distance calculation requested
146
+ return Math::NaN();
147
+
148
+ // Avoid warning about uninitialized B12.
149
+ real sig12, ssig12, csig12, B12 = 0, AB1 = 0;
150
+ if (arcmode) {
151
+ // Interpret s12_a12 as spherical arc length
152
+ sig12 = s12_a12 * Math::degree();
153
+ Math::sincosd(s12_a12, ssig12, csig12);
154
+ } else {
155
+ // Interpret s12_a12 as distance
156
+ real
157
+ tau12 = s12_a12 / (_b * (1 + _A1m1)),
158
+ s = sin(tau12),
159
+ c = cos(tau12);
160
+ // tau2 = tau1 + tau12
161
+ B12 = - Geodesic::SinCosSeries(true,
162
+ _stau1 * c + _ctau1 * s,
163
+ _ctau1 * c - _stau1 * s,
164
+ _C1pa, nC1p_);
165
+ sig12 = tau12 - (B12 - _B11);
166
+ ssig12 = sin(sig12); csig12 = cos(sig12);
167
+ if (abs(_f) > 0.01) {
168
+ // Reverted distance series is inaccurate for |f| > 1/100, so correct
169
+ // sig12 with 1 Newton iteration. The following table shows the
170
+ // approximate maximum error for a = WGS_a() and various f relative to
171
+ // GeodesicExact.
172
+ // erri = the error in the inverse solution (nm)
173
+ // errd = the error in the direct solution (series only) (nm)
174
+ // errda = the error in the direct solution (series + 1 Newton) (nm)
175
+ //
176
+ // f erri errd errda
177
+ // -1/5 12e6 1.2e9 69e6
178
+ // -1/10 123e3 12e6 765e3
179
+ // -1/20 1110 108e3 7155
180
+ // -1/50 18.63 200.9 27.12
181
+ // -1/100 18.63 23.78 23.37
182
+ // -1/150 18.63 21.05 20.26
183
+ // 1/150 22.35 24.73 25.83
184
+ // 1/100 22.35 25.03 25.31
185
+ // 1/50 29.80 231.9 30.44
186
+ // 1/20 5376 146e3 10e3
187
+ // 1/10 829e3 22e6 1.5e6
188
+ // 1/5 157e6 3.8e9 280e6
189
+ real
190
+ ssig2 = _ssig1 * csig12 + _csig1 * ssig12,
191
+ csig2 = _csig1 * csig12 - _ssig1 * ssig12;
192
+ B12 = Geodesic::SinCosSeries(true, ssig2, csig2, _C1a, nC1_);
193
+ real serr = (1 + _A1m1) * (sig12 + (B12 - _B11)) - s12_a12 / _b;
194
+ sig12 = sig12 - serr / sqrt(1 + _k2 * Math::sq(ssig2));
195
+ ssig12 = sin(sig12); csig12 = cos(sig12);
196
+ // Update B12 below
197
+ }
198
+ }
199
+
200
+ real ssig2, csig2, sbet2, cbet2, salp2, calp2;
201
+ // sig2 = sig1 + sig12
202
+ ssig2 = _ssig1 * csig12 + _csig1 * ssig12;
203
+ csig2 = _csig1 * csig12 - _ssig1 * ssig12;
204
+ real dn2 = sqrt(1 + _k2 * Math::sq(ssig2));
205
+ if (outmask & (DISTANCE | REDUCEDLENGTH | GEODESICSCALE)) {
206
+ if (arcmode || abs(_f) > 0.01)
207
+ B12 = Geodesic::SinCosSeries(true, ssig2, csig2, _C1a, nC1_);
208
+ AB1 = (1 + _A1m1) * (B12 - _B11);
209
+ }
210
+ // sin(bet2) = cos(alp0) * sin(sig2)
211
+ sbet2 = _calp0 * ssig2;
212
+ // Alt: cbet2 = hypot(csig2, salp0 * ssig2);
213
+ cbet2 = Math::hypot(_salp0, _calp0 * csig2);
214
+ if (cbet2 == 0)
215
+ // I.e., salp0 = 0, csig2 = 0. Break the degeneracy in this case
216
+ cbet2 = csig2 = tiny_;
217
+ // tan(alp0) = cos(sig2)*tan(alp2)
218
+ salp2 = _salp0; calp2 = _calp0 * csig2; // No need to normalize
219
+
220
+ if (outmask & DISTANCE)
221
+ s12 = arcmode ? _b * ((1 + _A1m1) * sig12 + AB1) : s12_a12;
222
+
223
+ if (outmask & LONGITUDE) {
224
+ // tan(omg2) = sin(alp0) * tan(sig2)
225
+ real somg2 = _salp0 * ssig2, comg2 = csig2, // No need to normalize
226
+ E = Math::copysign(real(1), _salp0); // east-going?
227
+ // omg12 = omg2 - omg1
228
+ real omg12 = outmask & LONG_UNROLL
229
+ ? E * (sig12
230
+ - (atan2( ssig2, csig2) - atan2( _ssig1, _csig1))
231
+ + (atan2(E * somg2, comg2) - atan2(E * _somg1, _comg1)))
232
+ : atan2(somg2 * _comg1 - comg2 * _somg1,
233
+ comg2 * _comg1 + somg2 * _somg1);
234
+ real lam12 = omg12 + _A3c *
235
+ ( sig12 + (Geodesic::SinCosSeries(true, ssig2, csig2, _C3a, nC3_-1)
236
+ - _B31));
237
+ real lon12 = lam12 / Math::degree();
238
+ lon2 = outmask & LONG_UNROLL ? _lon1 + lon12 :
239
+ Math::AngNormalize(Math::AngNormalize(_lon1) +
240
+ Math::AngNormalize(lon12));
241
+ }
242
+
243
+ if (outmask & LATITUDE)
244
+ lat2 = Math::atan2d(sbet2, _f1 * cbet2);
245
+
246
+ if (outmask & AZIMUTH)
247
+ azi2 = Math::atan2d(salp2, calp2);
248
+
249
+ if (outmask & (REDUCEDLENGTH | GEODESICSCALE)) {
250
+ real
251
+ B22 = Geodesic::SinCosSeries(true, ssig2, csig2, _C2a, nC2_),
252
+ AB2 = (1 + _A2m1) * (B22 - _B21),
253
+ J12 = (_A1m1 - _A2m1) * sig12 + (AB1 - AB2);
254
+ if (outmask & REDUCEDLENGTH)
255
+ // Add parens around (_csig1 * ssig2) and (_ssig1 * csig2) to ensure
256
+ // accurate cancellation in the case of coincident points.
257
+ m12 = _b * ((dn2 * (_csig1 * ssig2) - _dn1 * (_ssig1 * csig2))
258
+ - _csig1 * csig2 * J12);
259
+ if (outmask & GEODESICSCALE) {
260
+ real t = _k2 * (ssig2 - _ssig1) * (ssig2 + _ssig1) / (_dn1 + dn2);
261
+ M12 = csig12 + (t * ssig2 - csig2 * J12) * _ssig1 / _dn1;
262
+ M21 = csig12 - (t * _ssig1 - _csig1 * J12) * ssig2 / dn2;
263
+ }
264
+ }
265
+
266
+ if (outmask & AREA) {
267
+ real
268
+ B42 = Geodesic::SinCosSeries(false, ssig2, csig2, _C4a, nC4_);
269
+ real salp12, calp12;
270
+ if (_calp0 == 0 || _salp0 == 0) {
271
+ // alp12 = alp2 - alp1, used in atan2 so no need to normalize
272
+ salp12 = salp2 * _calp1 - calp2 * _salp1;
273
+ calp12 = calp2 * _calp1 + salp2 * _salp1;
274
+ // We used to include here some patch up code that purported to deal
275
+ // with nearly meridional geodesics properly. However, this turned out
276
+ // to be wrong once _salp1 = -0 was allowed (via
277
+ // Geodesic::InverseLine). In fact, the calculation of {s,c}alp12
278
+ // was already correct (following the IEEE rules for handling signed
279
+ // zeros). So the patch up code was unnecessary (as well as
280
+ // dangerous).
281
+ } else {
282
+ // tan(alp) = tan(alp0) * sec(sig)
283
+ // tan(alp2-alp1) = (tan(alp2) -tan(alp1)) / (tan(alp2)*tan(alp1)+1)
284
+ // = calp0 * salp0 * (csig1-csig2) / (salp0^2 + calp0^2 * csig1*csig2)
285
+ // If csig12 > 0, write
286
+ // csig1 - csig2 = ssig12 * (csig1 * ssig12 / (1 + csig12) + ssig1)
287
+ // else
288
+ // csig1 - csig2 = csig1 * (1 - csig12) + ssig12 * ssig1
289
+ // No need to normalize
290
+ salp12 = _calp0 * _salp0 *
291
+ (csig12 <= 0 ? _csig1 * (1 - csig12) + ssig12 * _ssig1 :
292
+ ssig12 * (_csig1 * ssig12 / (1 + csig12) + _ssig1));
293
+ calp12 = Math::sq(_salp0) + Math::sq(_calp0) * _csig1 * csig2;
294
+ }
295
+ S12 = _c2 * atan2(salp12, calp12) + _A4 * (B42 - _B41);
296
+ }
297
+
298
+ return arcmode ? s12_a12 : sig12 / Math::degree();
299
+ }
300
+
301
+ void GeodesicLine::SetDistance(real s13) {
302
+ _s13 = s13;
303
+ real t;
304
+ // This will set _a13 to NaN if the GeodesicLine doesn't have the
305
+ // DISTANCE_IN capability.
306
+ _a13 = GenPosition(false, _s13, 0u, t, t, t, t, t, t, t, t);
307
+ }
308
+
309
+ void GeodesicLine::SetArc(real a13) {
310
+ _a13 = a13;
311
+ // In case the GeodesicLine doesn't have the DISTANCE capability.
312
+ _s13 = Math::NaN();
313
+ real t;
314
+ GenPosition(true, _a13, DISTANCE, t, t, t, _s13, t, t, t, t);
315
+ }
316
+
317
+ void GeodesicLine::GenSetDistance(bool arcmode, real s13_a13) {
318
+ arcmode ? SetArc(s13_a13) : SetDistance(s13_a13);
319
+ }
320
+
321
+ } // namespace GeographicLib
@@ -0,0 +1,289 @@
1
+ /**
2
+ * \file GeodesicLineExact.cpp
3
+ * \brief Implementation for GeographicLib::GeodesicLineExact class
4
+ *
5
+ * Copyright (c) Charles Karney (2012-2016) <charles@karney.com> and licensed
6
+ * under the MIT/X11 License. For more information, see
7
+ * http://geographiclib.sourceforge.net/
8
+ *
9
+ * This is a reformulation of the geodesic problem. The notation is as
10
+ * follows:
11
+ * - at a general point (no suffix or 1 or 2 as suffix)
12
+ * - phi = latitude
13
+ * - beta = latitude on auxiliary sphere
14
+ * - omega = longitude on auxiliary sphere
15
+ * - lambda = longitude
16
+ * - alpha = azimuth of great circle
17
+ * - sigma = arc length along great circle
18
+ * - s = distance
19
+ * - tau = scaled distance (= sigma at multiples of pi/2)
20
+ * - at northwards equator crossing
21
+ * - beta = phi = 0
22
+ * - omega = lambda = 0
23
+ * - alpha = alpha0
24
+ * - sigma = s = 0
25
+ * - a 12 suffix means a difference, e.g., s12 = s2 - s1.
26
+ * - s and c prefixes mean sin and cos
27
+ **********************************************************************/
28
+
29
+ #include <GeographicLib/GeodesicLineExact.hpp>
30
+
31
+ namespace GeographicLib {
32
+
33
+ using namespace std;
34
+
35
+ void GeodesicLineExact::LineInit(const GeodesicExact& g,
36
+ real lat1, real lon1,
37
+ real azi1, real salp1, real calp1,
38
+ unsigned caps) {
39
+ tiny_ = g.tiny_;
40
+ _lat1 = Math::LatFix(lat1);
41
+ _lon1 = lon1;
42
+ _azi1 = azi1;
43
+ _salp1 = salp1;
44
+ _calp1 = calp1;
45
+ _a = g._a;
46
+ _f = g._f;
47
+ _b = g._b;
48
+ _c2 = g._c2;
49
+ _f1 = g._f1;
50
+ _e2 = g._e2;
51
+ // Always allow latitude and azimuth and unrolling of longitude
52
+ _caps = caps | LATITUDE | AZIMUTH | LONG_UNROLL;
53
+
54
+ real cbet1, sbet1;
55
+ Math::sincosd(Math::AngRound(_lat1), sbet1, cbet1); sbet1 *= _f1;
56
+ // Ensure cbet1 = +epsilon at poles
57
+ Math::norm(sbet1, cbet1); cbet1 = max(tiny_, cbet1);
58
+ _dn1 = (_f >= 0 ? sqrt(1 + g._ep2 * Math::sq(sbet1)) :
59
+ sqrt(1 - _e2 * Math::sq(cbet1)) / _f1);
60
+
61
+ // Evaluate alp0 from sin(alp1) * cos(bet1) = sin(alp0),
62
+ _salp0 = _salp1 * cbet1; // alp0 in [0, pi/2 - |bet1|]
63
+ // Alt: calp0 = hypot(sbet1, calp1 * cbet1). The following
64
+ // is slightly better (consider the case salp1 = 0).
65
+ _calp0 = Math::hypot(_calp1, _salp1 * sbet1);
66
+ // Evaluate sig with tan(bet1) = tan(sig1) * cos(alp1).
67
+ // sig = 0 is nearest northward crossing of equator.
68
+ // With bet1 = 0, alp1 = pi/2, we have sig1 = 0 (equatorial line).
69
+ // With bet1 = pi/2, alp1 = -pi, sig1 = pi/2
70
+ // With bet1 = -pi/2, alp1 = 0 , sig1 = -pi/2
71
+ // Evaluate omg1 with tan(omg1) = sin(alp0) * tan(sig1).
72
+ // With alp0 in (0, pi/2], quadrants for sig and omg coincide.
73
+ // No atan2(0,0) ambiguity at poles since cbet1 = +epsilon.
74
+ // With alp0 = 0, omg1 = 0 for alp1 = 0, omg1 = pi for alp1 = pi.
75
+ _ssig1 = sbet1; _somg1 = _salp0 * sbet1;
76
+ _csig1 = _comg1 = sbet1 != 0 || _calp1 != 0 ? cbet1 * _calp1 : 1;
77
+ // Without normalization we have schi1 = somg1.
78
+ _cchi1 = _f1 * _dn1 * _comg1;
79
+ Math::norm(_ssig1, _csig1); // sig1 in (-pi, pi]
80
+ // Math::norm(_somg1, _comg1); -- don't need to normalize!
81
+ // Math::norm(_schi1, _cchi1); -- don't need to normalize!
82
+
83
+ _k2 = Math::sq(_calp0) * g._ep2;
84
+ _E.Reset(-_k2, -g._ep2, 1 + _k2, 1 + g._ep2);
85
+
86
+ if (_caps & CAP_E) {
87
+ _E0 = _E.E() / (Math::pi() / 2);
88
+ _E1 = _E.deltaE(_ssig1, _csig1, _dn1);
89
+ real s = sin(_E1), c = cos(_E1);
90
+ // tau1 = sig1 + B11
91
+ _stau1 = _ssig1 * c + _csig1 * s;
92
+ _ctau1 = _csig1 * c - _ssig1 * s;
93
+ // Not necessary because Einv inverts E
94
+ // _E1 = -_E.deltaEinv(_stau1, _ctau1);
95
+ }
96
+
97
+ if (_caps & CAP_D) {
98
+ _D0 = _E.D() / (Math::pi() / 2);
99
+ _D1 = _E.deltaD(_ssig1, _csig1, _dn1);
100
+ }
101
+
102
+ if (_caps & CAP_H) {
103
+ _H0 = _E.H() / (Math::pi() / 2);
104
+ _H1 = _E.deltaH(_ssig1, _csig1, _dn1);
105
+ }
106
+
107
+ if (_caps & CAP_C4) {
108
+ real eps = _k2 / (2 * (1 + sqrt(1 + _k2)) + _k2);
109
+ g.C4f(eps, _C4a);
110
+ // Multiplier = a^2 * e^2 * cos(alpha0) * sin(alpha0)
111
+ _A4 = Math::sq(_a) * _calp0 * _salp0 * _e2;
112
+ _B41 = GeodesicExact::CosSeries(_ssig1, _csig1, _C4a, nC4_);
113
+ }
114
+
115
+ _a13 = _s13 = Math::NaN();
116
+ }
117
+
118
+ GeodesicLineExact::GeodesicLineExact(const GeodesicExact& g,
119
+ real lat1, real lon1, real azi1,
120
+ unsigned caps) {
121
+ azi1 = Math::AngNormalize(azi1);
122
+ real salp1, calp1;
123
+ // Guard against underflow in salp0. Also -0 is converted to +0.
124
+ Math::sincosd(Math::AngRound(azi1), salp1, calp1);
125
+ LineInit(g, lat1, lon1, azi1, salp1, calp1, caps);
126
+ }
127
+
128
+ GeodesicLineExact::GeodesicLineExact(const GeodesicExact& g,
129
+ real lat1, real lon1,
130
+ real azi1, real salp1, real calp1,
131
+ unsigned caps,
132
+ bool arcmode, real s13_a13) {
133
+ LineInit(g, lat1, lon1, azi1, salp1, calp1, caps);
134
+ GenSetDistance(arcmode, s13_a13);
135
+ }
136
+
137
+ Math::real GeodesicLineExact::GenPosition(bool arcmode, real s12_a12,
138
+ unsigned outmask,
139
+ real& lat2, real& lon2, real& azi2,
140
+ real& s12, real& m12,
141
+ real& M12, real& M21,
142
+ real& S12)
143
+ const {
144
+ outmask &= _caps & OUT_MASK;
145
+ if (!( Init() && (arcmode || (_caps & (OUT_MASK & DISTANCE_IN))) ))
146
+ // Uninitialized or impossible distance calculation requested
147
+ return Math::NaN();
148
+
149
+ // Avoid warning about uninitialized B12.
150
+ real sig12, ssig12, csig12, E2 = 0, AB1 = 0;
151
+ if (arcmode) {
152
+ // Interpret s12_a12 as spherical arc length
153
+ sig12 = s12_a12 * Math::degree();
154
+ real s12a = abs(s12_a12);
155
+ s12a -= 180 * floor(s12a / 180);
156
+ ssig12 = s12a == 0 ? 0 : sin(sig12);
157
+ csig12 = s12a == 90 ? 0 : cos(sig12);
158
+ } else {
159
+ // Interpret s12_a12 as distance
160
+ real
161
+ tau12 = s12_a12 / (_b * _E0),
162
+ s = sin(tau12),
163
+ c = cos(tau12);
164
+ // tau2 = tau1 + tau12
165
+ E2 = - _E.deltaEinv(_stau1 * c + _ctau1 * s, _ctau1 * c - _stau1 * s);
166
+ sig12 = tau12 - (E2 - _E1);
167
+ ssig12 = sin(sig12);
168
+ csig12 = cos(sig12);
169
+ }
170
+
171
+ real ssig2, csig2, sbet2, cbet2, salp2, calp2;
172
+ // sig2 = sig1 + sig12
173
+ ssig2 = _ssig1 * csig12 + _csig1 * ssig12;
174
+ csig2 = _csig1 * csig12 - _ssig1 * ssig12;
175
+ real dn2 = _E.Delta(ssig2, csig2);
176
+ if (outmask & (DISTANCE | REDUCEDLENGTH | GEODESICSCALE)) {
177
+ if (arcmode) {
178
+ E2 = _E.deltaE(ssig2, csig2, dn2);
179
+ }
180
+ AB1 = _E0 * (E2 - _E1);
181
+ }
182
+ // sin(bet2) = cos(alp0) * sin(sig2)
183
+ sbet2 = _calp0 * ssig2;
184
+ // Alt: cbet2 = hypot(csig2, salp0 * ssig2);
185
+ cbet2 = Math::hypot(_salp0, _calp0 * csig2);
186
+ if (cbet2 == 0)
187
+ // I.e., salp0 = 0, csig2 = 0. Break the degeneracy in this case
188
+ cbet2 = csig2 = tiny_;
189
+ // tan(alp0) = cos(sig2)*tan(alp2)
190
+ salp2 = _salp0; calp2 = _calp0 * csig2; // No need to normalize
191
+
192
+ if (outmask & DISTANCE)
193
+ s12 = arcmode ? _b * (_E0 * sig12 + AB1) : s12_a12;
194
+
195
+ if (outmask & LONGITUDE) {
196
+ real somg2 = _salp0 * ssig2, comg2 = csig2, // No need to normalize
197
+ E = Math::copysign(real(1), _salp0); // east-going?
198
+ // Without normalization we have schi2 = somg2.
199
+ real cchi2 = _f1 * dn2 * comg2;
200
+ real chi12 = outmask & LONG_UNROLL
201
+ ? E * (sig12
202
+ - (atan2( ssig2, csig2) - atan2( _ssig1, _csig1))
203
+ + (atan2(E * somg2, cchi2) - atan2(E * _somg1, _cchi1)))
204
+ : atan2(somg2 * _cchi1 - cchi2 * _somg1,
205
+ cchi2 * _cchi1 + somg2 * _somg1);
206
+ real lam12 = chi12 -
207
+ _e2/_f1 * _salp0 * _H0 * (sig12 + (_E.deltaH(ssig2, csig2, dn2) - _H1));
208
+ real lon12 = lam12 / Math::degree();
209
+ lon2 = outmask & LONG_UNROLL ? _lon1 + lon12 :
210
+ Math::AngNormalize(Math::AngNormalize(_lon1) +
211
+ Math::AngNormalize(lon12));
212
+ }
213
+
214
+ if (outmask & LATITUDE)
215
+ lat2 = Math::atan2d(sbet2, _f1 * cbet2);
216
+
217
+ if (outmask & AZIMUTH)
218
+ azi2 = Math::atan2d(salp2, calp2);
219
+
220
+ if (outmask & (REDUCEDLENGTH | GEODESICSCALE)) {
221
+ real J12 = _k2 * _D0 * (sig12 + (_E.deltaD(ssig2, csig2, dn2) - _D1));
222
+ if (outmask & REDUCEDLENGTH)
223
+ // Add parens around (_csig1 * ssig2) and (_ssig1 * csig2) to ensure
224
+ // accurate cancellation in the case of coincident points.
225
+ m12 = _b * ((dn2 * (_csig1 * ssig2) - _dn1 * (_ssig1 * csig2))
226
+ - _csig1 * csig2 * J12);
227
+ if (outmask & GEODESICSCALE) {
228
+ real t = _k2 * (ssig2 - _ssig1) * (ssig2 + _ssig1) / (_dn1 + dn2);
229
+ M12 = csig12 + (t * ssig2 - csig2 * J12) * _ssig1 / _dn1;
230
+ M21 = csig12 - (t * _ssig1 - _csig1 * J12) * ssig2 / dn2;
231
+ }
232
+ }
233
+
234
+ if (outmask & AREA) {
235
+ real
236
+ B42 = GeodesicExact::CosSeries(ssig2, csig2, _C4a, nC4_);
237
+ real salp12, calp12;
238
+ if (_calp0 == 0 || _salp0 == 0) {
239
+ // alp12 = alp2 - alp1, used in atan2 so no need to normalize
240
+ salp12 = salp2 * _calp1 - calp2 * _salp1;
241
+ calp12 = calp2 * _calp1 + salp2 * _salp1;
242
+ // We used to include here some patch up code that purported to deal
243
+ // with nearly meridional geodesics properly. However, this turned out
244
+ // to be wrong once _salp1 = -0 was allowed (via
245
+ // GeodesicExact::InverseLine). In fact, the calculation of {s,c}alp12
246
+ // was already correct (following the IEEE rules for handling signed
247
+ // zeros). So the patch up code was unnecessary (as well as
248
+ // dangerous).
249
+ } else {
250
+ // tan(alp) = tan(alp0) * sec(sig)
251
+ // tan(alp2-alp1) = (tan(alp2) -tan(alp1)) / (tan(alp2)*tan(alp1)+1)
252
+ // = calp0 * salp0 * (csig1-csig2) / (salp0^2 + calp0^2 * csig1*csig2)
253
+ // If csig12 > 0, write
254
+ // csig1 - csig2 = ssig12 * (csig1 * ssig12 / (1 + csig12) + ssig1)
255
+ // else
256
+ // csig1 - csig2 = csig1 * (1 - csig12) + ssig12 * ssig1
257
+ // No need to normalize
258
+ salp12 = _calp0 * _salp0 *
259
+ (csig12 <= 0 ? _csig1 * (1 - csig12) + ssig12 * _ssig1 :
260
+ ssig12 * (_csig1 * ssig12 / (1 + csig12) + _ssig1));
261
+ calp12 = Math::sq(_salp0) + Math::sq(_calp0) * _csig1 * csig2;
262
+ }
263
+ S12 = _c2 * atan2(salp12, calp12) + _A4 * (B42 - _B41);
264
+ }
265
+
266
+ return arcmode ? s12_a12 : sig12 / Math::degree();
267
+ }
268
+
269
+ void GeodesicLineExact::SetDistance(real s13) {
270
+ _s13 = s13;
271
+ real t;
272
+ // This will set _a13 to NaN if the GeodesicLineExact doesn't have the
273
+ // DISTANCE_IN capability.
274
+ _a13 = GenPosition(false, _s13, 0u, t, t, t, t, t, t, t, t);
275
+ }
276
+
277
+ void GeodesicLineExact::SetArc(real a13) {
278
+ _a13 = a13;
279
+ // In case the GeodesicLineExact doesn't have the DISTANCE capability.
280
+ _s13 = Math::NaN();
281
+ real t;
282
+ GenPosition(true, _a13, DISTANCE, t, t, t, _s13, t, t, t, t);
283
+ }
284
+
285
+ void GeodesicLineExact::GenSetDistance(bool arcmode, real s13_a13) {
286
+ arcmode ? SetArc(s13_a13) : SetDistance(s13_a13);
287
+ }
288
+
289
+ } // namespace GeographicLib