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