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,41 @@
|
|
1
|
+
/**
|
2
|
+
* \file AzimuthalEquidistant.cpp
|
3
|
+
* \brief Implementation for GeographicLib::AzimuthalEquidistant class
|
4
|
+
*
|
5
|
+
* Copyright (c) Charles Karney (2009-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/AzimuthalEquidistant.hpp>
|
11
|
+
|
12
|
+
namespace GeographicLib {
|
13
|
+
|
14
|
+
using namespace std;
|
15
|
+
|
16
|
+
AzimuthalEquidistant::AzimuthalEquidistant(const Geodesic& earth)
|
17
|
+
: eps_(real(0.01) * sqrt(numeric_limits<real>::min()))
|
18
|
+
, _earth(earth) {}
|
19
|
+
|
20
|
+
void AzimuthalEquidistant::Forward(real lat0, real lon0, real lat, real lon,
|
21
|
+
real& x, real& y, real& azi, real& rk)
|
22
|
+
const {
|
23
|
+
real sig, s, azi0, m;
|
24
|
+
sig = _earth.Inverse(lat0, lon0, lat, lon, s, azi0, azi, m);
|
25
|
+
Math::sincosd(azi0, x, y);
|
26
|
+
x *= s; y *= s;
|
27
|
+
rk = !(sig <= eps_) ? m / s : 1;
|
28
|
+
}
|
29
|
+
|
30
|
+
void AzimuthalEquidistant::Reverse(real lat0, real lon0, real x, real y,
|
31
|
+
real& lat, real& lon, real& azi, real& rk)
|
32
|
+
const {
|
33
|
+
real
|
34
|
+
azi0 = Math::atan2d(x, y),
|
35
|
+
s = Math::hypot(x, y);
|
36
|
+
real sig, m;
|
37
|
+
sig = _earth.Direct(lat0, lon0, azi0, s, lat, lon, azi, m);
|
38
|
+
rk = !(sig <= eps_) ? m / s : 1;
|
39
|
+
}
|
40
|
+
|
41
|
+
} // namespace GeographicLib
|
@@ -0,0 +1,89 @@
|
|
1
|
+
/**
|
2
|
+
* \file CassiniSoldner.cpp
|
3
|
+
* \brief Implementation for GeographicLib::CassiniSoldner class
|
4
|
+
*
|
5
|
+
* Copyright (c) Charles Karney (2009-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/CassiniSoldner.hpp>
|
11
|
+
|
12
|
+
namespace GeographicLib {
|
13
|
+
|
14
|
+
using namespace std;
|
15
|
+
|
16
|
+
CassiniSoldner::CassiniSoldner(const Geodesic& earth)
|
17
|
+
: _earth(earth) {}
|
18
|
+
|
19
|
+
CassiniSoldner::CassiniSoldner(real lat0, real lon0, const Geodesic& earth)
|
20
|
+
: _earth(earth)
|
21
|
+
{ Reset(lat0, lon0); }
|
22
|
+
|
23
|
+
void CassiniSoldner::Reset(real lat0, real lon0) {
|
24
|
+
_meridian = _earth.Line(lat0, lon0, real(0),
|
25
|
+
Geodesic::LATITUDE | Geodesic::LONGITUDE |
|
26
|
+
Geodesic::DISTANCE | Geodesic::DISTANCE_IN |
|
27
|
+
Geodesic::AZIMUTH);
|
28
|
+
real f = _earth.Flattening();
|
29
|
+
Math::sincosd(LatitudeOrigin(), _sbet0, _cbet0);
|
30
|
+
_sbet0 *= (1 - f);
|
31
|
+
Math::norm(_sbet0, _cbet0);
|
32
|
+
}
|
33
|
+
|
34
|
+
void CassiniSoldner::Forward(real lat, real lon, real& x, real& y,
|
35
|
+
real& azi, real& rk) const {
|
36
|
+
if (!Init())
|
37
|
+
return;
|
38
|
+
real dlon = Math::AngDiff(LongitudeOrigin(), lon);
|
39
|
+
real sig12, s12, azi1, azi2;
|
40
|
+
sig12 = _earth.Inverse(lat, -abs(dlon), lat, abs(dlon), s12, azi1, azi2);
|
41
|
+
sig12 *= real(0.5);
|
42
|
+
s12 *= real(0.5);
|
43
|
+
if (s12 == 0) {
|
44
|
+
real da = Math::AngDiff(azi1, azi2)/2;
|
45
|
+
if (abs(dlon) <= 90) {
|
46
|
+
azi1 = 90 - da;
|
47
|
+
azi2 = 90 + da;
|
48
|
+
} else {
|
49
|
+
azi1 = -90 - da;
|
50
|
+
azi2 = -90 + da;
|
51
|
+
}
|
52
|
+
}
|
53
|
+
if (dlon < 0) {
|
54
|
+
azi2 = azi1;
|
55
|
+
s12 = -s12;
|
56
|
+
sig12 = -sig12;
|
57
|
+
}
|
58
|
+
x = s12;
|
59
|
+
azi = Math::AngNormalize(azi2);
|
60
|
+
GeodesicLine perp(_earth.Line(lat, dlon, azi, Geodesic::GEODESICSCALE));
|
61
|
+
real t;
|
62
|
+
perp.GenPosition(true, -sig12,
|
63
|
+
Geodesic::GEODESICSCALE,
|
64
|
+
t, t, t, t, t, t, rk, t);
|
65
|
+
|
66
|
+
real salp0, calp0;
|
67
|
+
Math::sincosd(perp.EquatorialAzimuth(), salp0, calp0);
|
68
|
+
real
|
69
|
+
sbet1 = lat >=0 ? calp0 : -calp0,
|
70
|
+
cbet1 = abs(dlon) <= 90 ? abs(salp0) : -abs(salp0),
|
71
|
+
sbet01 = sbet1 * _cbet0 - cbet1 * _sbet0,
|
72
|
+
cbet01 = cbet1 * _cbet0 + sbet1 * _sbet0,
|
73
|
+
sig01 = atan2(sbet01, cbet01) / Math::degree();
|
74
|
+
_meridian.GenPosition(true, sig01,
|
75
|
+
Geodesic::DISTANCE,
|
76
|
+
t, t, t, y, t, t, t, t);
|
77
|
+
}
|
78
|
+
|
79
|
+
void CassiniSoldner::Reverse(real x, real y, real& lat, real& lon,
|
80
|
+
real& azi, real& rk) const {
|
81
|
+
if (!Init())
|
82
|
+
return;
|
83
|
+
real lat1, lon1;
|
84
|
+
real azi0, t;
|
85
|
+
_meridian.Position(y, lat1, lon1, azi0);
|
86
|
+
_earth.Direct(lat1, lon1, azi0 + 90, x, lat, lon, azi, rk, t);
|
87
|
+
}
|
88
|
+
|
89
|
+
} // namespace GeographicLib
|
@@ -0,0 +1,96 @@
|
|
1
|
+
/**
|
2
|
+
* \file CircularEngine.cpp
|
3
|
+
* \brief Implementation for GeographicLib::CircularEngine class
|
4
|
+
*
|
5
|
+
* Copyright (c) Charles Karney (2011) <charles@karney.com> and licensed under
|
6
|
+
* the MIT/X11 License. For more information, see
|
7
|
+
* http://geographiclib.sourceforge.net/
|
8
|
+
**********************************************************************/
|
9
|
+
|
10
|
+
#include <GeographicLib/CircularEngine.hpp>
|
11
|
+
|
12
|
+
namespace GeographicLib {
|
13
|
+
|
14
|
+
using namespace std;
|
15
|
+
|
16
|
+
Math::real CircularEngine::Value(bool gradp, real sl, real cl,
|
17
|
+
real& gradx, real& grady, real& gradz)
|
18
|
+
const {
|
19
|
+
gradp = _gradp && gradp;
|
20
|
+
const vector<real>& root_( SphericalEngine::root_ );
|
21
|
+
|
22
|
+
// Initialize outer sum
|
23
|
+
real vc = 0, vc2 = 0, vs = 0, vs2 = 0; // v [N + 1], v [N + 2]
|
24
|
+
// vr, vt, vl and similar w variable accumulate the sums for the
|
25
|
+
// derivatives wrt r, theta, and lambda, respectively.
|
26
|
+
real vrc = 0, vrc2 = 0, vrs = 0, vrs2 = 0; // vr[N + 1], vr[N + 2]
|
27
|
+
real vtc = 0, vtc2 = 0, vts = 0, vts2 = 0; // vt[N + 1], vt[N + 2]
|
28
|
+
real vlc = 0, vlc2 = 0, vls = 0, vls2 = 0; // vl[N + 1], vl[N + 2]
|
29
|
+
for (int m = _M; m >= 0; --m) { // m = M .. 0
|
30
|
+
// Now Sc[m] = wc, Ss[m] = ws
|
31
|
+
// Sc'[m] = wtc, Ss'[m] = wtc
|
32
|
+
if (m) {
|
33
|
+
real v, A, B; // alpha[m], beta[m + 1]
|
34
|
+
switch (_norm) {
|
35
|
+
case FULL:
|
36
|
+
v = root_[2] * root_[2 * m + 3] / root_[m + 1];
|
37
|
+
A = cl * v * _uq;
|
38
|
+
B = - v * root_[2 * m + 5] / (root_[8] * root_[m + 2]) * _uq2;
|
39
|
+
break;
|
40
|
+
case SCHMIDT:
|
41
|
+
v = root_[2] * root_[2 * m + 1] / root_[m + 1];
|
42
|
+
A = cl * v * _uq;
|
43
|
+
B = - v * root_[2 * m + 3] / (root_[8] * root_[m + 2]) * _uq2;
|
44
|
+
break;
|
45
|
+
default:
|
46
|
+
A = B = 0;
|
47
|
+
}
|
48
|
+
v = A * vc + B * vc2 + _wc[m] ; vc2 = vc ; vc = v;
|
49
|
+
v = A * vs + B * vs2 + _ws[m] ; vs2 = vs ; vs = v;
|
50
|
+
if (gradp) {
|
51
|
+
v = A * vrc + B * vrc2 + _wrc[m]; vrc2 = vrc; vrc = v;
|
52
|
+
v = A * vrs + B * vrs2 + _wrs[m]; vrs2 = vrs; vrs = v;
|
53
|
+
v = A * vtc + B * vtc2 + _wtc[m]; vtc2 = vtc; vtc = v;
|
54
|
+
v = A * vts + B * vts2 + _wts[m]; vts2 = vts; vts = v;
|
55
|
+
v = A * vlc + B * vlc2 + m*_ws[m]; vlc2 = vlc; vlc = v;
|
56
|
+
v = A * vls + B * vls2 - m*_wc[m]; vls2 = vls; vls = v;
|
57
|
+
}
|
58
|
+
} else {
|
59
|
+
real A, B, qs;
|
60
|
+
switch (_norm) {
|
61
|
+
case FULL:
|
62
|
+
A = root_[3] * _uq; // F[1]/(q*cl) or F[1]/(q*sl)
|
63
|
+
B = - root_[15]/2 * _uq2; // beta[1]/q
|
64
|
+
break;
|
65
|
+
case SCHMIDT:
|
66
|
+
A = _uq;
|
67
|
+
B = - root_[3]/2 * _uq2;
|
68
|
+
break;
|
69
|
+
default:
|
70
|
+
A = B = 0;
|
71
|
+
}
|
72
|
+
qs = _q / SphericalEngine::scale();
|
73
|
+
vc = qs * (_wc[m] + A * (cl * vc + sl * vs ) + B * vc2);
|
74
|
+
if (gradp) {
|
75
|
+
qs /= _r;
|
76
|
+
// The components of the gradient in circular coordinates are
|
77
|
+
// r: dV/dr
|
78
|
+
// theta: 1/r * dV/dtheta
|
79
|
+
// lambda: 1/(r*u) * dV/dlambda
|
80
|
+
vrc = - qs * (_wrc[m] + A * (cl * vrc + sl * vrs) + B * vrc2);
|
81
|
+
vtc = qs * (_wtc[m] + A * (cl * vtc + sl * vts) + B * vtc2);
|
82
|
+
vlc = qs / _u * ( A * (cl * vlc + sl * vls) + B * vlc2);
|
83
|
+
}
|
84
|
+
}
|
85
|
+
}
|
86
|
+
|
87
|
+
if (gradp) {
|
88
|
+
// Rotate into cartesian (geocentric) coordinates
|
89
|
+
gradx = cl * (_u * vrc + _t * vtc) - sl * vlc;
|
90
|
+
grady = sl * (_u * vrc + _t * vtc) + cl * vlc;
|
91
|
+
gradz = _t * vrc - _u * vtc ;
|
92
|
+
}
|
93
|
+
return vc;
|
94
|
+
}
|
95
|
+
|
96
|
+
} // namespace GeographicLib
|
@@ -0,0 +1,381 @@
|
|
1
|
+
/**
|
2
|
+
* \file DMS.cpp
|
3
|
+
* \brief Implementation for GeographicLib::DMS class
|
4
|
+
*
|
5
|
+
* Copyright (c) Charles Karney (2008-2015) <charles@karney.com> and licensed
|
6
|
+
* under the MIT/X11 License. For more information, see
|
7
|
+
* http://geographiclib.sourceforge.net/
|
8
|
+
**********************************************************************/
|
9
|
+
|
10
|
+
#include <GeographicLib/DMS.hpp>
|
11
|
+
#include <GeographicLib/Utility.hpp>
|
12
|
+
|
13
|
+
#if defined(_MSC_VER)
|
14
|
+
// Squelch warnings about constant conditional expressions
|
15
|
+
# pragma warning (disable: 4127)
|
16
|
+
#endif
|
17
|
+
|
18
|
+
namespace GeographicLib {
|
19
|
+
|
20
|
+
using namespace std;
|
21
|
+
|
22
|
+
const string DMS::hemispheres_ = "SNWE";
|
23
|
+
const string DMS::signs_ = "-+";
|
24
|
+
const string DMS::digits_ = "0123456789";
|
25
|
+
const string DMS::dmsindicators_ = "D'\":";
|
26
|
+
const string DMS::components_[] = {"degrees", "minutes", "seconds"};
|
27
|
+
|
28
|
+
Math::real DMS::Decode(const std::string& dms, flag& ind) {
|
29
|
+
string dmsa = dms;
|
30
|
+
replace(dmsa, "\xc2\xb0", 'd'); // U+00b0 degree symbol
|
31
|
+
replace(dmsa, "\xc2\xba", 'd'); // U+00ba alt symbol
|
32
|
+
replace(dmsa, "\xe2\x81\xb0", 'd'); // U+2070 sup zero
|
33
|
+
replace(dmsa, "\xcb\x9a", 'd'); // U+02da ring above
|
34
|
+
replace(dmsa, "\xe2\x80\xb2", '\''); // U+2032 prime
|
35
|
+
replace(dmsa, "\xc2\xb4", '\''); // U+00b4 acute accent
|
36
|
+
replace(dmsa, "\xe2\x80\x99", '\''); // U+2019 right single quote
|
37
|
+
replace(dmsa, "\xe2\x80\xb3", '"'); // U+2033 double prime
|
38
|
+
replace(dmsa, "\xe2\x80\x9d", '"'); // U+201d right double quote
|
39
|
+
replace(dmsa, "\xe2\x88\x92", '-'); // U+2212 minus sign
|
40
|
+
replace(dmsa, "\xb0", 'd'); // 0xb0 bare degree symbol
|
41
|
+
replace(dmsa, "\xba", 'd'); // 0xba bare alt symbol
|
42
|
+
replace(dmsa, "\xb4", '\''); // 0xb4 bare acute accent
|
43
|
+
replace(dmsa, "''", '"'); // '' -> "
|
44
|
+
string::size_type
|
45
|
+
beg = 0,
|
46
|
+
end = unsigned(dmsa.size());
|
47
|
+
while (beg < end && isspace(dmsa[beg]))
|
48
|
+
++beg;
|
49
|
+
while (beg < end && isspace(dmsa[end - 1]))
|
50
|
+
--end;
|
51
|
+
// The trimmed string in [beg, end)
|
52
|
+
real v = 0;
|
53
|
+
int i = 0;
|
54
|
+
flag ind1 = NONE;
|
55
|
+
// p is pointer to the next piece that needs decoding
|
56
|
+
for (string::size_type p = beg, pb; p < end; p = pb, ++i) {
|
57
|
+
string::size_type pa = p;
|
58
|
+
// Skip over initial hemisphere letter (for i == 0)
|
59
|
+
if (i == 0 && Utility::lookup(hemispheres_, dmsa[pa]) >= 0)
|
60
|
+
++pa;
|
61
|
+
// Skip over initial sign (checking for it if i == 0)
|
62
|
+
if (i > 0 || (pa < end && Utility::lookup(signs_, dmsa[pa]) >= 0))
|
63
|
+
++pa;
|
64
|
+
// Find next sign
|
65
|
+
pb = min(dmsa.find_first_of(signs_, pa), end);
|
66
|
+
flag ind2 = NONE;
|
67
|
+
v += InternalDecode(dmsa.substr(p, pb - p), ind2);
|
68
|
+
if (ind1 == NONE)
|
69
|
+
ind1 = ind2;
|
70
|
+
else if (!(ind2 == NONE || ind1 == ind2))
|
71
|
+
throw GeographicErr("Incompatible hemisphere specifies in " +
|
72
|
+
dmsa.substr(beg, pb - beg));
|
73
|
+
}
|
74
|
+
if (i == 0)
|
75
|
+
throw GeographicErr("Empty or incomplete DMS string " +
|
76
|
+
dmsa.substr(beg, end - beg));
|
77
|
+
ind = ind1;
|
78
|
+
return v;
|
79
|
+
}
|
80
|
+
|
81
|
+
Math::real DMS::InternalDecode(const std::string& dmsa, flag& ind) {
|
82
|
+
string errormsg;
|
83
|
+
do { // Executed once (provides the ability to break)
|
84
|
+
int sign = 1;
|
85
|
+
unsigned
|
86
|
+
beg = 0,
|
87
|
+
end = unsigned(dmsa.size());
|
88
|
+
flag ind1 = NONE;
|
89
|
+
int k = -1;
|
90
|
+
if (end > beg && (k = Utility::lookup(hemispheres_, dmsa[beg])) >= 0) {
|
91
|
+
ind1 = (k / 2) ? LONGITUDE : LATITUDE;
|
92
|
+
sign = k % 2 ? 1 : -1;
|
93
|
+
++beg;
|
94
|
+
}
|
95
|
+
if (end > beg && (k = Utility::lookup(hemispheres_, dmsa[end-1])) >= 0) {
|
96
|
+
if (k >= 0) {
|
97
|
+
if (ind1 != NONE) {
|
98
|
+
if (toupper(dmsa[beg - 1]) == toupper(dmsa[end - 1]))
|
99
|
+
errormsg = "Repeated hemisphere indicators "
|
100
|
+
+ Utility::str(dmsa[beg - 1])
|
101
|
+
+ " in " + dmsa.substr(beg - 1, end - beg + 1);
|
102
|
+
else
|
103
|
+
errormsg = "Contradictory hemisphere indicators "
|
104
|
+
+ Utility::str(dmsa[beg - 1]) + " and "
|
105
|
+
+ Utility::str(dmsa[end - 1]) + " in "
|
106
|
+
+ dmsa.substr(beg - 1, end - beg + 1);
|
107
|
+
break;
|
108
|
+
}
|
109
|
+
ind1 = (k / 2) ? LONGITUDE : LATITUDE;
|
110
|
+
sign = k % 2 ? 1 : -1;
|
111
|
+
--end;
|
112
|
+
}
|
113
|
+
}
|
114
|
+
if (end > beg && (k = Utility::lookup(signs_, dmsa[beg])) >= 0) {
|
115
|
+
if (k >= 0) {
|
116
|
+
sign *= k ? 1 : -1;
|
117
|
+
++beg;
|
118
|
+
}
|
119
|
+
}
|
120
|
+
if (end == beg) {
|
121
|
+
errormsg = "Empty or incomplete DMS string " + dmsa;
|
122
|
+
break;
|
123
|
+
}
|
124
|
+
real ipieces[] = {0, 0, 0};
|
125
|
+
real fpieces[] = {0, 0, 0};
|
126
|
+
unsigned npiece = 0;
|
127
|
+
real icurrent = 0;
|
128
|
+
real fcurrent = 0;
|
129
|
+
unsigned ncurrent = 0, p = beg;
|
130
|
+
bool pointseen = false;
|
131
|
+
unsigned digcount = 0, intcount = 0;
|
132
|
+
while (p < end) {
|
133
|
+
char x = dmsa[p++];
|
134
|
+
if ((k = Utility::lookup(digits_, x)) >= 0) {
|
135
|
+
++ncurrent;
|
136
|
+
if (digcount > 0)
|
137
|
+
++digcount; // Count of decimal digits
|
138
|
+
else {
|
139
|
+
icurrent = 10 * icurrent + k;
|
140
|
+
++intcount;
|
141
|
+
}
|
142
|
+
} else if (x == '.') {
|
143
|
+
if (pointseen) {
|
144
|
+
errormsg = "Multiple decimal points in "
|
145
|
+
+ dmsa.substr(beg, end - beg);
|
146
|
+
break;
|
147
|
+
}
|
148
|
+
pointseen = true;
|
149
|
+
digcount = 1;
|
150
|
+
} else if ((k = Utility::lookup(dmsindicators_, x)) >= 0) {
|
151
|
+
if (k >= 3) {
|
152
|
+
if (p == end) {
|
153
|
+
errormsg = "Illegal for : to appear at the end of " +
|
154
|
+
dmsa.substr(beg, end - beg);
|
155
|
+
break;
|
156
|
+
}
|
157
|
+
k = npiece;
|
158
|
+
}
|
159
|
+
if (unsigned(k) == npiece - 1) {
|
160
|
+
errormsg = "Repeated " + components_[k] +
|
161
|
+
" component in " + dmsa.substr(beg, end - beg);
|
162
|
+
break;
|
163
|
+
} else if (unsigned(k) < npiece) {
|
164
|
+
errormsg = components_[k] + " component follows "
|
165
|
+
+ components_[npiece - 1] + " component in "
|
166
|
+
+ dmsa.substr(beg, end - beg);
|
167
|
+
break;
|
168
|
+
}
|
169
|
+
if (ncurrent == 0) {
|
170
|
+
errormsg = "Missing numbers in " + components_[k] +
|
171
|
+
" component of " + dmsa.substr(beg, end - beg);
|
172
|
+
break;
|
173
|
+
}
|
174
|
+
if (digcount > 0) {
|
175
|
+
istringstream s(dmsa.substr(p - intcount - digcount - 1,
|
176
|
+
intcount + digcount));
|
177
|
+
s >> fcurrent;
|
178
|
+
icurrent = 0;
|
179
|
+
}
|
180
|
+
ipieces[k] = icurrent;
|
181
|
+
fpieces[k] = icurrent + fcurrent;
|
182
|
+
if (p < end) {
|
183
|
+
npiece = k + 1;
|
184
|
+
icurrent = fcurrent = 0;
|
185
|
+
ncurrent = digcount = intcount = 0;
|
186
|
+
}
|
187
|
+
} else if (Utility::lookup(signs_, x) >= 0) {
|
188
|
+
errormsg = "Internal sign in DMS string "
|
189
|
+
+ dmsa.substr(beg, end - beg);
|
190
|
+
break;
|
191
|
+
} else {
|
192
|
+
errormsg = "Illegal character " + Utility::str(x) + " in DMS string "
|
193
|
+
+ dmsa.substr(beg, end - beg);
|
194
|
+
break;
|
195
|
+
}
|
196
|
+
}
|
197
|
+
if (!errormsg.empty())
|
198
|
+
break;
|
199
|
+
if (Utility::lookup(dmsindicators_, dmsa[p - 1]) < 0) {
|
200
|
+
if (npiece >= 3) {
|
201
|
+
errormsg = "Extra text following seconds in DMS string "
|
202
|
+
+ dmsa.substr(beg, end - beg);
|
203
|
+
break;
|
204
|
+
}
|
205
|
+
if (ncurrent == 0) {
|
206
|
+
errormsg = "Missing numbers in trailing component of "
|
207
|
+
+ dmsa.substr(beg, end - beg);
|
208
|
+
break;
|
209
|
+
}
|
210
|
+
if (digcount > 0) {
|
211
|
+
istringstream s(dmsa.substr(p - intcount - digcount,
|
212
|
+
intcount + digcount));
|
213
|
+
s >> fcurrent;
|
214
|
+
icurrent = 0;
|
215
|
+
}
|
216
|
+
ipieces[npiece] = icurrent;
|
217
|
+
fpieces[npiece] = icurrent + fcurrent;
|
218
|
+
}
|
219
|
+
if (pointseen && digcount == 0) {
|
220
|
+
errormsg = "Decimal point in non-terminal component of "
|
221
|
+
+ dmsa.substr(beg, end - beg);
|
222
|
+
break;
|
223
|
+
}
|
224
|
+
// Note that we accept 59.999999... even though it rounds to 60.
|
225
|
+
if (ipieces[1] >= 60 || fpieces[1] > 60 ) {
|
226
|
+
errormsg = "Minutes " + Utility::str(fpieces[1])
|
227
|
+
+ " not in range [0, 60)";
|
228
|
+
break;
|
229
|
+
}
|
230
|
+
if (ipieces[2] >= 60 || fpieces[2] > 60) {
|
231
|
+
errormsg = "Seconds " + Utility::str(fpieces[2])
|
232
|
+
+ " not in range [0, 60)";
|
233
|
+
break;
|
234
|
+
}
|
235
|
+
ind = ind1;
|
236
|
+
// Assume check on range of result is made by calling routine (which
|
237
|
+
// might be able to offer a better diagnostic).
|
238
|
+
return real(sign) *
|
239
|
+
( fpieces[2] ? (60*(60*fpieces[0] + fpieces[1]) + fpieces[2]) / 3600 :
|
240
|
+
( fpieces[1] ? (60*fpieces[0] + fpieces[1]) / 60 : fpieces[0] ) );
|
241
|
+
} while (false);
|
242
|
+
real val = Utility::nummatch<real>(dmsa);
|
243
|
+
if (val == 0)
|
244
|
+
throw GeographicErr(errormsg);
|
245
|
+
else
|
246
|
+
ind = NONE;
|
247
|
+
return val;
|
248
|
+
}
|
249
|
+
|
250
|
+
void DMS::DecodeLatLon(const std::string& stra, const std::string& strb,
|
251
|
+
real& lat, real& lon,
|
252
|
+
bool longfirst) {
|
253
|
+
real a, b;
|
254
|
+
flag ia, ib;
|
255
|
+
a = Decode(stra, ia);
|
256
|
+
b = Decode(strb, ib);
|
257
|
+
if (ia == NONE && ib == NONE) {
|
258
|
+
// Default to lat, long unless longfirst
|
259
|
+
ia = longfirst ? LONGITUDE : LATITUDE;
|
260
|
+
ib = longfirst ? LATITUDE : LONGITUDE;
|
261
|
+
} else if (ia == NONE)
|
262
|
+
ia = flag(LATITUDE + LONGITUDE - ib);
|
263
|
+
else if (ib == NONE)
|
264
|
+
ib = flag(LATITUDE + LONGITUDE - ia);
|
265
|
+
if (ia == ib)
|
266
|
+
throw GeographicErr("Both " + stra + " and "
|
267
|
+
+ strb + " interpreted as "
|
268
|
+
+ (ia == LATITUDE ? "latitudes" : "longitudes"));
|
269
|
+
real
|
270
|
+
lat1 = ia == LATITUDE ? a : b,
|
271
|
+
lon1 = ia == LATITUDE ? b : a;
|
272
|
+
if (abs(lat1) > 90)
|
273
|
+
throw GeographicErr("Latitude " + Utility::str(lat1)
|
274
|
+
+ "d not in [-90d, 90d]");
|
275
|
+
lat = lat1;
|
276
|
+
lon = lon1;
|
277
|
+
}
|
278
|
+
|
279
|
+
Math::real DMS::DecodeAngle(const std::string& angstr) {
|
280
|
+
flag ind;
|
281
|
+
real ang = Decode(angstr, ind);
|
282
|
+
if (ind != NONE)
|
283
|
+
throw GeographicErr("Arc angle " + angstr
|
284
|
+
+ " includes a hemisphere, N/E/W/S");
|
285
|
+
return ang;
|
286
|
+
}
|
287
|
+
|
288
|
+
Math::real DMS::DecodeAzimuth(const std::string& azistr) {
|
289
|
+
flag ind;
|
290
|
+
real azi = Decode(azistr, ind);
|
291
|
+
if (ind == LATITUDE)
|
292
|
+
throw GeographicErr("Azimuth " + azistr
|
293
|
+
+ " has a latitude hemisphere, N/S");
|
294
|
+
return Math::AngNormalize(azi);
|
295
|
+
}
|
296
|
+
|
297
|
+
string DMS::Encode(real angle, component trailing, unsigned prec, flag ind,
|
298
|
+
char dmssep) {
|
299
|
+
// Assume check on range of input angle has been made by calling
|
300
|
+
// routine (which might be able to offer a better diagnostic).
|
301
|
+
if (!Math::isfinite(angle))
|
302
|
+
return angle < 0 ? string("-inf") :
|
303
|
+
(angle > 0 ? string("inf") : string("nan"));
|
304
|
+
|
305
|
+
// 15 - 2 * trailing = ceiling(log10(2^53/90/60^trailing)).
|
306
|
+
// This suffices to give full real precision for numbers in [-90,90]
|
307
|
+
prec = min(15 + Math::extra_digits() - 2 * unsigned(trailing), prec);
|
308
|
+
real scale = 1;
|
309
|
+
for (unsigned i = 0; i < unsigned(trailing); ++i)
|
310
|
+
scale *= 60;
|
311
|
+
for (unsigned i = 0; i < prec; ++i)
|
312
|
+
scale *= 10;
|
313
|
+
if (ind == AZIMUTH)
|
314
|
+
angle -= floor(angle/360) * 360;
|
315
|
+
int sign = angle < 0 ? -1 : 1;
|
316
|
+
angle *= sign;
|
317
|
+
|
318
|
+
// Break off integer part to preserve precision in manipulation of
|
319
|
+
// fractional part.
|
320
|
+
real
|
321
|
+
idegree = floor(angle),
|
322
|
+
fdegree = (angle - idegree) * scale + real(0.5);
|
323
|
+
{
|
324
|
+
// Implement the "round ties to even" rule
|
325
|
+
real f = floor(fdegree);
|
326
|
+
fdegree = (f == fdegree && fmod(f, real(2)) == 1) ? f - 1 : f;
|
327
|
+
}
|
328
|
+
fdegree /= scale;
|
329
|
+
if (fdegree >= 1) {
|
330
|
+
idegree += 1;
|
331
|
+
fdegree -= 1;
|
332
|
+
}
|
333
|
+
real pieces[3] = {fdegree, 0, 0};
|
334
|
+
for (unsigned i = 1; i <= unsigned(trailing); ++i) {
|
335
|
+
real
|
336
|
+
ip = floor(pieces[i - 1]),
|
337
|
+
fp = pieces[i - 1] - ip;
|
338
|
+
pieces[i] = fp * 60;
|
339
|
+
pieces[i - 1] = ip;
|
340
|
+
}
|
341
|
+
pieces[0] += idegree;
|
342
|
+
ostringstream s;
|
343
|
+
s << fixed << setfill('0');
|
344
|
+
if (ind == NONE && sign < 0)
|
345
|
+
s << '-';
|
346
|
+
switch (trailing) {
|
347
|
+
case DEGREE:
|
348
|
+
if (ind != NONE)
|
349
|
+
s << setw(1 + min(int(ind), 2) + prec + (prec ? 1 : 0));
|
350
|
+
s << Utility::str(pieces[0], prec);
|
351
|
+
// Don't include degree designator (d) if it is the trailing component.
|
352
|
+
break;
|
353
|
+
default:
|
354
|
+
if (ind != NONE)
|
355
|
+
s << setw(1 + min(int(ind), 2));
|
356
|
+
s << int(pieces[0])
|
357
|
+
<< (dmssep ? dmssep : char(tolower(dmsindicators_[0])));
|
358
|
+
switch (trailing) {
|
359
|
+
case MINUTE:
|
360
|
+
s << setw(2 + prec + (prec ? 1 : 0)) << Utility::str(pieces[1], prec);
|
361
|
+
if (!dmssep)
|
362
|
+
s << char(tolower(dmsindicators_[1]));
|
363
|
+
break;
|
364
|
+
case SECOND:
|
365
|
+
s << setw(2)
|
366
|
+
<< int(pieces[1])
|
367
|
+
<< (dmssep ? dmssep : char(tolower(dmsindicators_[1])))
|
368
|
+
<< setw(2 + prec + (prec ? 1 : 0)) << Utility::str(pieces[2], prec);
|
369
|
+
if (!dmssep)
|
370
|
+
s << char(tolower(dmsindicators_[2]));
|
371
|
+
break;
|
372
|
+
default:
|
373
|
+
break;
|
374
|
+
}
|
375
|
+
}
|
376
|
+
if (ind != NONE && ind != AZIMUTH)
|
377
|
+
s << hemispheres_[(ind == LATITUDE ? 0 : 2) + (sign < 0 ? 0 : 1)];
|
378
|
+
return s.str();
|
379
|
+
}
|
380
|
+
|
381
|
+
} // namespace GeographicLib
|