geographiclib 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/AUTHORS +12 -0
- data/LICENSE +24 -0
- data/ext/geographiclib/Accumulator.cpp +23 -0
- data/ext/geographiclib/AlbersEqualArea.cpp +445 -0
- data/ext/geographiclib/AzimuthalEquidistant.cpp +41 -0
- data/ext/geographiclib/CassiniSoldner.cpp +89 -0
- data/ext/geographiclib/CircularEngine.cpp +96 -0
- data/ext/geographiclib/DMS.cpp +381 -0
- data/ext/geographiclib/Ellipsoid.cpp +125 -0
- data/ext/geographiclib/EllipticFunction.cpp +512 -0
- data/ext/geographiclib/GARS.cpp +122 -0
- data/ext/geographiclib/GeoCoords.cpp +175 -0
- data/ext/geographiclib/Geocentric.cpp +172 -0
- data/ext/geographiclib/Geodesic.cpp +1908 -0
- data/ext/geographiclib/GeodesicExact.cpp +927 -0
- data/ext/geographiclib/GeodesicExactC4.cpp +7879 -0
- data/ext/geographiclib/GeodesicLine.cpp +321 -0
- data/ext/geographiclib/GeodesicLineExact.cpp +289 -0
- data/ext/geographiclib/GeographicLib/Accumulator.hpp +184 -0
- data/ext/geographiclib/GeographicLib/AlbersEqualArea.hpp +312 -0
- data/ext/geographiclib/GeographicLib/AzimuthalEquidistant.hpp +139 -0
- data/ext/geographiclib/GeographicLib/CassiniSoldner.hpp +204 -0
- data/ext/geographiclib/GeographicLib/CircularEngine.hpp +195 -0
- data/ext/geographiclib/GeographicLib/Config.h +12 -0
- data/ext/geographiclib/GeographicLib/Constants.hpp +387 -0
- data/ext/geographiclib/GeographicLib/DMS.hpp +370 -0
- data/ext/geographiclib/GeographicLib/Ellipsoid.hpp +534 -0
- data/ext/geographiclib/GeographicLib/EllipticFunction.hpp +692 -0
- data/ext/geographiclib/GeographicLib/GARS.hpp +143 -0
- data/ext/geographiclib/GeographicLib/GeoCoords.hpp +544 -0
- data/ext/geographiclib/GeographicLib/Geocentric.hpp +267 -0
- data/ext/geographiclib/GeographicLib/Geodesic.hpp +970 -0
- data/ext/geographiclib/GeographicLib/GeodesicExact.hpp +862 -0
- data/ext/geographiclib/GeographicLib/GeodesicLine.hpp +701 -0
- data/ext/geographiclib/GeographicLib/GeodesicLineExact.hpp +667 -0
- data/ext/geographiclib/GeographicLib/Geohash.hpp +180 -0
- data/ext/geographiclib/GeographicLib/Geoid.hpp +472 -0
- data/ext/geographiclib/GeographicLib/Georef.hpp +160 -0
- data/ext/geographiclib/GeographicLib/Gnomonic.hpp +206 -0
- data/ext/geographiclib/GeographicLib/GravityCircle.hpp +301 -0
- data/ext/geographiclib/GeographicLib/GravityModel.hpp +520 -0
- data/ext/geographiclib/GeographicLib/LambertConformalConic.hpp +313 -0
- data/ext/geographiclib/GeographicLib/LocalCartesian.hpp +236 -0
- data/ext/geographiclib/GeographicLib/MGRS.hpp +355 -0
- data/ext/geographiclib/GeographicLib/MagneticCircle.hpp +178 -0
- data/ext/geographiclib/GeographicLib/MagneticModel.hpp +347 -0
- data/ext/geographiclib/GeographicLib/Math.hpp +920 -0
- data/ext/geographiclib/GeographicLib/NormalGravity.hpp +350 -0
- data/ext/geographiclib/GeographicLib/OSGB.hpp +249 -0
- data/ext/geographiclib/GeographicLib/PolarStereographic.hpp +150 -0
- data/ext/geographiclib/GeographicLib/PolygonArea.hpp +288 -0
- data/ext/geographiclib/GeographicLib/Rhumb.hpp +589 -0
- data/ext/geographiclib/GeographicLib/SphericalEngine.hpp +376 -0
- data/ext/geographiclib/GeographicLib/SphericalHarmonic.hpp +354 -0
- data/ext/geographiclib/GeographicLib/SphericalHarmonic1.hpp +281 -0
- data/ext/geographiclib/GeographicLib/SphericalHarmonic2.hpp +315 -0
- data/ext/geographiclib/GeographicLib/TransverseMercator.hpp +196 -0
- data/ext/geographiclib/GeographicLib/TransverseMercatorExact.hpp +254 -0
- data/ext/geographiclib/GeographicLib/UTMUPS.hpp +421 -0
- data/ext/geographiclib/GeographicLib/Utility.hpp +612 -0
- data/ext/geographiclib/Geohash.cpp +102 -0
- data/ext/geographiclib/Geoid.cpp +509 -0
- data/ext/geographiclib/Georef.cpp +135 -0
- data/ext/geographiclib/Gnomonic.cpp +85 -0
- data/ext/geographiclib/GravityCircle.cpp +129 -0
- data/ext/geographiclib/GravityModel.cpp +360 -0
- data/ext/geographiclib/LambertConformalConic.cpp +456 -0
- data/ext/geographiclib/LocalCartesian.cpp +62 -0
- data/ext/geographiclib/MGRS.cpp +461 -0
- data/ext/geographiclib/MagneticCircle.cpp +52 -0
- data/ext/geographiclib/MagneticModel.cpp +269 -0
- data/ext/geographiclib/Math.cpp +63 -0
- data/ext/geographiclib/NormalGravity.cpp +262 -0
- data/ext/geographiclib/OSGB.cpp +167 -0
- data/ext/geographiclib/PolarStereographic.cpp +108 -0
- data/ext/geographiclib/PolygonArea.cpp +204 -0
- data/ext/geographiclib/Rhumb.cpp +383 -0
- data/ext/geographiclib/SphericalEngine.cpp +477 -0
- data/ext/geographiclib/TransverseMercator.cpp +603 -0
- data/ext/geographiclib/TransverseMercatorExact.cpp +464 -0
- data/ext/geographiclib/UTMUPS.cpp +296 -0
- data/ext/geographiclib/Utility.cpp +61 -0
- data/ext/geographiclib/extconf.rb +3 -0
- data/ext/geographiclib/geographiclib.cpp +62 -0
- data/lib/geographiclib.rb +20 -0
- 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
|