geographiclib 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. checksums.yaml +7 -0
  2. data/AUTHORS +12 -0
  3. data/LICENSE +24 -0
  4. data/ext/geographiclib/Accumulator.cpp +23 -0
  5. data/ext/geographiclib/AlbersEqualArea.cpp +445 -0
  6. data/ext/geographiclib/AzimuthalEquidistant.cpp +41 -0
  7. data/ext/geographiclib/CassiniSoldner.cpp +89 -0
  8. data/ext/geographiclib/CircularEngine.cpp +96 -0
  9. data/ext/geographiclib/DMS.cpp +381 -0
  10. data/ext/geographiclib/Ellipsoid.cpp +125 -0
  11. data/ext/geographiclib/EllipticFunction.cpp +512 -0
  12. data/ext/geographiclib/GARS.cpp +122 -0
  13. data/ext/geographiclib/GeoCoords.cpp +175 -0
  14. data/ext/geographiclib/Geocentric.cpp +172 -0
  15. data/ext/geographiclib/Geodesic.cpp +1908 -0
  16. data/ext/geographiclib/GeodesicExact.cpp +927 -0
  17. data/ext/geographiclib/GeodesicExactC4.cpp +7879 -0
  18. data/ext/geographiclib/GeodesicLine.cpp +321 -0
  19. data/ext/geographiclib/GeodesicLineExact.cpp +289 -0
  20. data/ext/geographiclib/GeographicLib/Accumulator.hpp +184 -0
  21. data/ext/geographiclib/GeographicLib/AlbersEqualArea.hpp +312 -0
  22. data/ext/geographiclib/GeographicLib/AzimuthalEquidistant.hpp +139 -0
  23. data/ext/geographiclib/GeographicLib/CassiniSoldner.hpp +204 -0
  24. data/ext/geographiclib/GeographicLib/CircularEngine.hpp +195 -0
  25. data/ext/geographiclib/GeographicLib/Config.h +12 -0
  26. data/ext/geographiclib/GeographicLib/Constants.hpp +387 -0
  27. data/ext/geographiclib/GeographicLib/DMS.hpp +370 -0
  28. data/ext/geographiclib/GeographicLib/Ellipsoid.hpp +534 -0
  29. data/ext/geographiclib/GeographicLib/EllipticFunction.hpp +692 -0
  30. data/ext/geographiclib/GeographicLib/GARS.hpp +143 -0
  31. data/ext/geographiclib/GeographicLib/GeoCoords.hpp +544 -0
  32. data/ext/geographiclib/GeographicLib/Geocentric.hpp +267 -0
  33. data/ext/geographiclib/GeographicLib/Geodesic.hpp +970 -0
  34. data/ext/geographiclib/GeographicLib/GeodesicExact.hpp +862 -0
  35. data/ext/geographiclib/GeographicLib/GeodesicLine.hpp +701 -0
  36. data/ext/geographiclib/GeographicLib/GeodesicLineExact.hpp +667 -0
  37. data/ext/geographiclib/GeographicLib/Geohash.hpp +180 -0
  38. data/ext/geographiclib/GeographicLib/Geoid.hpp +472 -0
  39. data/ext/geographiclib/GeographicLib/Georef.hpp +160 -0
  40. data/ext/geographiclib/GeographicLib/Gnomonic.hpp +206 -0
  41. data/ext/geographiclib/GeographicLib/GravityCircle.hpp +301 -0
  42. data/ext/geographiclib/GeographicLib/GravityModel.hpp +520 -0
  43. data/ext/geographiclib/GeographicLib/LambertConformalConic.hpp +313 -0
  44. data/ext/geographiclib/GeographicLib/LocalCartesian.hpp +236 -0
  45. data/ext/geographiclib/GeographicLib/MGRS.hpp +355 -0
  46. data/ext/geographiclib/GeographicLib/MagneticCircle.hpp +178 -0
  47. data/ext/geographiclib/GeographicLib/MagneticModel.hpp +347 -0
  48. data/ext/geographiclib/GeographicLib/Math.hpp +920 -0
  49. data/ext/geographiclib/GeographicLib/NormalGravity.hpp +350 -0
  50. data/ext/geographiclib/GeographicLib/OSGB.hpp +249 -0
  51. data/ext/geographiclib/GeographicLib/PolarStereographic.hpp +150 -0
  52. data/ext/geographiclib/GeographicLib/PolygonArea.hpp +288 -0
  53. data/ext/geographiclib/GeographicLib/Rhumb.hpp +589 -0
  54. data/ext/geographiclib/GeographicLib/SphericalEngine.hpp +376 -0
  55. data/ext/geographiclib/GeographicLib/SphericalHarmonic.hpp +354 -0
  56. data/ext/geographiclib/GeographicLib/SphericalHarmonic1.hpp +281 -0
  57. data/ext/geographiclib/GeographicLib/SphericalHarmonic2.hpp +315 -0
  58. data/ext/geographiclib/GeographicLib/TransverseMercator.hpp +196 -0
  59. data/ext/geographiclib/GeographicLib/TransverseMercatorExact.hpp +254 -0
  60. data/ext/geographiclib/GeographicLib/UTMUPS.hpp +421 -0
  61. data/ext/geographiclib/GeographicLib/Utility.hpp +612 -0
  62. data/ext/geographiclib/Geohash.cpp +102 -0
  63. data/ext/geographiclib/Geoid.cpp +509 -0
  64. data/ext/geographiclib/Georef.cpp +135 -0
  65. data/ext/geographiclib/Gnomonic.cpp +85 -0
  66. data/ext/geographiclib/GravityCircle.cpp +129 -0
  67. data/ext/geographiclib/GravityModel.cpp +360 -0
  68. data/ext/geographiclib/LambertConformalConic.cpp +456 -0
  69. data/ext/geographiclib/LocalCartesian.cpp +62 -0
  70. data/ext/geographiclib/MGRS.cpp +461 -0
  71. data/ext/geographiclib/MagneticCircle.cpp +52 -0
  72. data/ext/geographiclib/MagneticModel.cpp +269 -0
  73. data/ext/geographiclib/Math.cpp +63 -0
  74. data/ext/geographiclib/NormalGravity.cpp +262 -0
  75. data/ext/geographiclib/OSGB.cpp +167 -0
  76. data/ext/geographiclib/PolarStereographic.cpp +108 -0
  77. data/ext/geographiclib/PolygonArea.cpp +204 -0
  78. data/ext/geographiclib/Rhumb.cpp +383 -0
  79. data/ext/geographiclib/SphericalEngine.cpp +477 -0
  80. data/ext/geographiclib/TransverseMercator.cpp +603 -0
  81. data/ext/geographiclib/TransverseMercatorExact.cpp +464 -0
  82. data/ext/geographiclib/UTMUPS.cpp +296 -0
  83. data/ext/geographiclib/Utility.cpp +61 -0
  84. data/ext/geographiclib/extconf.rb +3 -0
  85. data/ext/geographiclib/geographiclib.cpp +62 -0
  86. data/lib/geographiclib.rb +20 -0
  87. metadata +140 -0
@@ -0,0 +1,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