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,122 @@
1
+ /**
2
+ * \file GARS.cpp
3
+ * \brief Implementation for GeographicLib::GARS class
4
+ *
5
+ * Copyright (c) Charles Karney (2015) <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/GARS.hpp>
11
+ #include <GeographicLib/Utility.hpp>
12
+
13
+ namespace GeographicLib {
14
+
15
+ using namespace std;
16
+
17
+ const string GARS::digits_ = "0123456789";
18
+ const string GARS::letters_ = "ABCDEFGHJKLMNPQRSTUVWXYZ";
19
+
20
+ void GARS::Forward(real lat, real lon, int prec, std::string& gars) {
21
+ if (abs(lat) > 90)
22
+ throw GeographicErr("Latitude " + Utility::str(lat)
23
+ + "d not in [-90d, 90d]");
24
+ if (Math::isnan(lat) || Math::isnan(lon)) {
25
+ gars = "INVALID";
26
+ return;
27
+ }
28
+ lon = Math::AngNormalize(lon); // lon in [-180,180)
29
+ if (lat == 90) lat *= (1 - numeric_limits<real>::epsilon() / 2);
30
+ prec = max(0, min(int(maxprec_), prec));
31
+ int
32
+ x = int(floor(lon * m_)) - lonorig_ * m_,
33
+ y = int(floor(lat * m_)) - latorig_ * m_,
34
+ ilon = x * mult1_ / m_,
35
+ ilat = y * mult1_ / m_;
36
+ x -= ilon * m_ / mult1_; y -= ilat * m_ / mult1_;
37
+ char gars1[maxlen_];
38
+ ++ilon;
39
+ for (int c = lonlen_; c--;) {
40
+ gars1[c] = digits_[ ilon % baselon_]; ilon /= baselon_;
41
+ }
42
+ for (int c = latlen_; c--;) {
43
+ gars1[lonlen_ + c] = letters_[ilat % baselat_]; ilat /= baselat_;
44
+ }
45
+ if (prec > 0) {
46
+ ilon = x / mult3_; ilat = y / mult3_;
47
+ gars1[baselen_] = digits_[mult2_ * (mult2_ - 1 - ilat) + ilon + 1];
48
+ if (prec > 1) {
49
+ ilon = x % mult3_; ilat = y % mult3_;
50
+ gars1[baselen_ + 1] = digits_[mult3_ * (mult3_ - 1 - ilat) + ilon + 1];
51
+ }
52
+ }
53
+ gars.resize(baselen_ + prec);
54
+ copy(gars1, gars1 + baselen_ + prec, gars.begin());
55
+ }
56
+
57
+ void GARS::Reverse(const std::string& gars, real& lat, real& lon,
58
+ int& prec, bool centerp) {
59
+ int len = int(gars.length());
60
+ if (len >= 3 &&
61
+ toupper(gars[0]) == 'I' &&
62
+ toupper(gars[1]) == 'N' &&
63
+ toupper(gars[2]) == 'V') {
64
+ lat = lon = Math::NaN();
65
+ return;
66
+ }
67
+ if (len < baselen_)
68
+ throw GeographicErr("GARS must have at least 5 characters " + gars);
69
+ if (len > maxlen_)
70
+ throw GeographicErr("GARS can have at most 7 characters " + gars);
71
+ int prec1 = len - baselen_;
72
+ int ilon = 0;
73
+ for (int c = 0; c < lonlen_; ++c) {
74
+ int k = Utility::lookup(digits_, gars[c]);
75
+ if (k < 0)
76
+ throw GeographicErr("GARS must start with 3 digits " + gars);
77
+ ilon = ilon * baselon_ + k;
78
+ }
79
+ if (!(ilon >= 1 && ilon <= 720))
80
+ throw GeographicErr("Initial digits in GARS must lie in [1, 720] " +
81
+ gars);
82
+ --ilon;
83
+ int ilat = 0;
84
+ for (int c = 0; c < latlen_; ++c) {
85
+ int k = Utility::lookup(letters_, gars[lonlen_ + c]);
86
+ if (k < 0)
87
+ throw GeographicErr("Illegal letters in GARS " + gars.substr(3,2));
88
+ ilat = ilat * baselat_ + k;
89
+ }
90
+ if (!(ilat < 360))
91
+ throw GeographicErr("GARS letters must lie in [AA, QZ] " + gars);
92
+ real
93
+ unit = mult1_,
94
+ lat1 = ilat + latorig_ * unit,
95
+ lon1 = ilon + lonorig_ * unit;
96
+ if (prec1 > 0) {
97
+ int k = Utility::lookup(digits_, gars[baselen_]);
98
+ if (!(k >= 1 && k <= mult2_ * mult2_))
99
+ throw GeographicErr("6th character in GARS must [1, 4] " + gars);
100
+ --k;
101
+ unit *= mult2_;
102
+ lat1 = mult2_ * lat1 + (mult2_ - 1 - k / mult2_);
103
+ lon1 = mult2_ * lon1 + (k % mult2_);
104
+ if (prec1 > 1) {
105
+ k = Utility::lookup(digits_, gars[baselen_ + 1]);
106
+ if (!(k >= 1 /* && k <= mult3_ * mult3_ */))
107
+ throw GeographicErr("7th character in GARS must [1, 9] " + gars);
108
+ --k;
109
+ unit *= mult3_;
110
+ lat1 = mult3_ * lat1 + (mult3_ - 1 - k / mult3_);
111
+ lon1 = mult3_ * lon1 + (k % mult3_);
112
+ }
113
+ }
114
+ if (centerp) {
115
+ unit *= 2; lat1 = 2 * lat1 + 1; lon1 = 2 * lon1 + 1;
116
+ }
117
+ lat = lat1 / unit;
118
+ lon = lon1 / unit;
119
+ prec = prec1;
120
+ }
121
+
122
+ } // namespace GeographicLib
@@ -0,0 +1,175 @@
1
+ /**
2
+ * \file GeoCoords.cpp
3
+ * \brief Implementation for GeographicLib::GeoCoords 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/GeoCoords.hpp>
11
+ #include <GeographicLib/MGRS.hpp>
12
+ #include <GeographicLib/DMS.hpp>
13
+ #include <GeographicLib/Utility.hpp>
14
+
15
+ namespace GeographicLib {
16
+
17
+ using namespace std;
18
+
19
+ void GeoCoords::Reset(const std::string& s, bool centerp, bool longfirst) {
20
+ vector<string> sa;
21
+ const char* spaces = " \t\n\v\f\r,"; // Include comma as a space
22
+ for (string::size_type pos0 = 0, pos1; pos0 != string::npos;) {
23
+ pos1 = s.find_first_not_of(spaces, pos0);
24
+ if (pos1 == string::npos)
25
+ break;
26
+ pos0 = s.find_first_of(spaces, pos1);
27
+ sa.push_back(s.substr(pos1, pos0 == string::npos ? pos0 : pos0 - pos1));
28
+ }
29
+ if (sa.size() == 1) {
30
+ int prec;
31
+ MGRS::Reverse(sa[0], _zone, _northp, _easting, _northing, prec, centerp);
32
+ UTMUPS::Reverse(_zone, _northp, _easting, _northing,
33
+ _lat, _long, _gamma, _k);
34
+ } else if (sa.size() == 2) {
35
+ DMS::DecodeLatLon(sa[0], sa[1], _lat, _long, longfirst);
36
+ _long = Math::AngNormalize(_long);
37
+ UTMUPS::Forward( _lat, _long,
38
+ _zone, _northp, _easting, _northing, _gamma, _k);
39
+ } else if (sa.size() == 3) {
40
+ unsigned zoneind, coordind;
41
+ if (sa[0].size() > 0 && isalpha(sa[0][sa[0].size() - 1])) {
42
+ zoneind = 0;
43
+ coordind = 1;
44
+ } else if (sa[2].size() > 0 && isalpha(sa[2][sa[2].size() - 1])) {
45
+ zoneind = 2;
46
+ coordind = 0;
47
+ } else
48
+ throw GeographicErr("Neither " + sa[0] + " nor " + sa[2]
49
+ + " of the form UTM/UPS Zone + Hemisphere"
50
+ + " (ex: 38n, 09s, n)");
51
+ UTMUPS::DecodeZone(sa[zoneind], _zone, _northp);
52
+ for (unsigned i = 0; i < 2; ++i)
53
+ (i ? _northing : _easting) = Utility::num<real>(sa[coordind + i]);
54
+ UTMUPS::Reverse(_zone, _northp, _easting, _northing,
55
+ _lat, _long, _gamma, _k);
56
+ FixHemisphere();
57
+ } else
58
+ throw GeographicErr("Coordinate requires 1, 2, or 3 elements");
59
+ CopyToAlt();
60
+ }
61
+
62
+ string GeoCoords::GeoRepresentation(int prec, bool longfirst) const {
63
+ prec = max(0, min(9 + Math::extra_digits(), prec) + 5);
64
+ ostringstream os;
65
+ os << fixed << setprecision(prec);
66
+ real a = longfirst ? _long : _lat;
67
+ real b = longfirst ? _lat : _long;
68
+ if (!Math::isnan(a))
69
+ os << a;
70
+ else
71
+ os << "nan";
72
+ os << " ";
73
+ if (!Math::isnan(b))
74
+ os << b;
75
+ else
76
+ os << "nan";
77
+ return os.str();
78
+ }
79
+
80
+ string GeoCoords::DMSRepresentation(int prec, bool longfirst,
81
+ char dmssep) const {
82
+ prec = max(0, min(10 + Math::extra_digits(), prec) + 5);
83
+ return DMS::Encode(longfirst ? _long : _lat, unsigned(prec),
84
+ longfirst ? DMS::LONGITUDE : DMS::LATITUDE, dmssep) +
85
+ " " + DMS::Encode(longfirst ? _lat : _long, unsigned(prec),
86
+ longfirst ? DMS::LATITUDE : DMS::LONGITUDE, dmssep);
87
+ }
88
+
89
+ string GeoCoords::MGRSRepresentation(int prec) const {
90
+ // Max precision is um
91
+ prec = max(-1, min(6, prec) + 5);
92
+ string mgrs;
93
+ MGRS::Forward(_zone, _northp, _easting, _northing, _lat, prec, mgrs);
94
+ return mgrs;
95
+ }
96
+
97
+ string GeoCoords::AltMGRSRepresentation(int prec) const {
98
+ // Max precision is um
99
+ prec = max(-1, min(6, prec) + 5);
100
+ string mgrs;
101
+ MGRS::Forward(_alt_zone, _northp, _alt_easting, _alt_northing, _lat, prec,
102
+ mgrs);
103
+ return mgrs;
104
+ }
105
+
106
+ void GeoCoords::UTMUPSString(int zone, bool northp,
107
+ real easting, real northing, int prec,
108
+ bool abbrev, std::string& utm) {
109
+ ostringstream os;
110
+ prec = max(-5, min(9 + Math::extra_digits(), prec));
111
+ real scale = prec < 0 ? pow(real(10), -prec) : real(1);
112
+ os << UTMUPS::EncodeZone(zone, northp, abbrev) << fixed << setfill('0');
113
+ if (Math::isfinite(easting)) {
114
+ os << " " << Utility::str(easting / scale, max(0, prec));
115
+ if (prec < 0 && abs(easting / scale) > real(0.5))
116
+ os << setw(-prec) << 0;
117
+ } else
118
+ os << " nan";
119
+ if (Math::isfinite(northing)) {
120
+ os << " " << Utility::str(northing / scale, max(0, prec));
121
+ if (prec < 0 && abs(northing / scale) > real(0.5))
122
+ os << setw(-prec) << 0;
123
+ } else
124
+ os << " nan";
125
+ utm = os.str();
126
+ }
127
+
128
+ string GeoCoords::UTMUPSRepresentation(int prec, bool abbrev) const {
129
+ string utm;
130
+ UTMUPSString(_zone, _northp, _easting, _northing, prec, abbrev, utm);
131
+ return utm;
132
+ }
133
+
134
+ string GeoCoords::UTMUPSRepresentation(bool northp, int prec, bool abbrev)
135
+ const {
136
+ real e, n;
137
+ int z;
138
+ UTMUPS::Transfer(_zone, _northp, _easting, _northing,
139
+ _zone, northp, e, n, z);
140
+ string utm;
141
+ UTMUPSString(_zone, northp, e, n, prec, abbrev, utm);
142
+ return utm;
143
+ }
144
+
145
+ string GeoCoords::AltUTMUPSRepresentation(int prec, bool abbrev) const {
146
+ string utm;
147
+ UTMUPSString(_alt_zone, _northp, _alt_easting, _alt_northing, prec,
148
+ abbrev, utm);
149
+ return utm;
150
+ }
151
+
152
+ string GeoCoords::AltUTMUPSRepresentation(bool northp, int prec, bool abbrev)
153
+ const {
154
+ real e, n;
155
+ int z;
156
+ UTMUPS::Transfer(_alt_zone, _northp, _alt_easting, _alt_northing,
157
+ _alt_zone, northp, e, n, z);
158
+ string utm;
159
+ UTMUPSString(_alt_zone, northp, e, n, prec, abbrev, utm);
160
+ return utm;
161
+ }
162
+
163
+ void GeoCoords::FixHemisphere() {
164
+ if (_lat == 0 || (_northp && _lat >= 0) || (!_northp && _lat < 0) ||
165
+ Math::isnan(_lat))
166
+ // Allow either hemisphere for equator
167
+ return;
168
+ if (_zone != UTMUPS::UPS) {
169
+ _northing += (_northp ? 1 : -1) * UTMUPS::UTMShift();
170
+ _northp = !_northp;
171
+ } else
172
+ throw GeographicErr("Hemisphere mixup");
173
+ }
174
+
175
+ } // namespace GeographicLib
@@ -0,0 +1,172 @@
1
+ /**
2
+ * \file Geocentric.cpp
3
+ * \brief Implementation for GeographicLib::Geocentric 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/Geocentric.hpp>
11
+
12
+ namespace GeographicLib {
13
+
14
+ using namespace std;
15
+
16
+ Geocentric::Geocentric(real a, real f)
17
+ : _a(a)
18
+ , _f(f)
19
+ , _e2(_f * (2 - _f))
20
+ , _e2m(Math::sq(1 - _f)) // 1 - _e2
21
+ , _e2a(abs(_e2))
22
+ , _e4a(Math::sq(_e2))
23
+ , _maxrad(2 * _a / numeric_limits<real>::epsilon())
24
+ {
25
+ if (!(Math::isfinite(_a) && _a > 0))
26
+ throw GeographicErr("Major radius is not positive");
27
+ if (!(Math::isfinite(_f) && _f < 1))
28
+ throw GeographicErr("Minor radius is not positive");
29
+ }
30
+
31
+ const Geocentric& Geocentric::WGS84() {
32
+ static const Geocentric wgs84(Constants::WGS84_a(), Constants::WGS84_f());
33
+ return wgs84;
34
+ }
35
+
36
+ void Geocentric::IntForward(real lat, real lon, real h,
37
+ real& X, real& Y, real& Z,
38
+ real M[dim2_]) const {
39
+ real sphi, cphi, slam, clam;
40
+ Math::sincosd(Math::LatFix(lat), sphi, cphi);
41
+ Math::sincosd(lon, slam, clam);
42
+ real n = _a/sqrt(1 - _e2 * Math::sq(sphi));
43
+ Z = (_e2m * n + h) * sphi;
44
+ X = (n + h) * cphi;
45
+ Y = X * slam;
46
+ X *= clam;
47
+ if (M)
48
+ Rotation(sphi, cphi, slam, clam, M);
49
+ }
50
+
51
+ void Geocentric::IntReverse(real X, real Y, real Z,
52
+ real& lat, real& lon, real& h,
53
+ real M[dim2_]) const {
54
+ real
55
+ R = Math::hypot(X, Y),
56
+ slam = R ? Y / R : 0,
57
+ clam = R ? X / R : 1;
58
+ h = Math::hypot(R, Z); // Distance to center of earth
59
+ real sphi, cphi;
60
+ if (h > _maxrad) {
61
+ // We really far away (> 12 million light years); treat the earth as a
62
+ // point and h, above, is an acceptable approximation to the height.
63
+ // This avoids overflow, e.g., in the computation of disc below. It's
64
+ // possible that h has overflowed to inf; but that's OK.
65
+ //
66
+ // Treat the case X, Y finite, but R overflows to +inf by scaling by 2.
67
+ R = Math::hypot(X/2, Y/2);
68
+ slam = R ? (Y/2) / R : 0;
69
+ clam = R ? (X/2) / R : 1;
70
+ real H = Math::hypot(Z/2, R);
71
+ sphi = (Z/2) / H;
72
+ cphi = R / H;
73
+ } else if (_e4a == 0) {
74
+ // Treat the spherical case. Dealing with underflow in the general case
75
+ // with _e2 = 0 is difficult. Origin maps to N pole same as with
76
+ // ellipsoid.
77
+ real H = Math::hypot(h == 0 ? 1 : Z, R);
78
+ sphi = (h == 0 ? 1 : Z) / H;
79
+ cphi = R / H;
80
+ h -= _a;
81
+ } else {
82
+ // Treat prolate spheroids by swapping R and Z here and by switching
83
+ // the arguments to phi = atan2(...) at the end.
84
+ real
85
+ p = Math::sq(R / _a),
86
+ q = _e2m * Math::sq(Z / _a),
87
+ r = (p + q - _e4a) / 6;
88
+ if (_f < 0) swap(p, q);
89
+ if ( !(_e4a * q == 0 && r <= 0) ) {
90
+ real
91
+ // Avoid possible division by zero when r = 0 by multiplying
92
+ // equations for s and t by r^3 and r, resp.
93
+ S = _e4a * p * q / 4, // S = r^3 * s
94
+ r2 = Math::sq(r),
95
+ r3 = r * r2,
96
+ disc = S * (2 * r3 + S);
97
+ real u = r;
98
+ if (disc >= 0) {
99
+ real T3 = S + r3;
100
+ // Pick the sign on the sqrt to maximize abs(T3). This minimizes
101
+ // loss of precision due to cancellation. The result is unchanged
102
+ // because of the way the T is used in definition of u.
103
+ T3 += T3 < 0 ? -sqrt(disc) : sqrt(disc); // T3 = (r * t)^3
104
+ // N.B. cbrt always returns the real root. cbrt(-8) = -2.
105
+ real T = Math::cbrt(T3); // T = r * t
106
+ // T can be zero; but then r2 / T -> 0.
107
+ u += T + (T ? r2 / T : 0);
108
+ } else {
109
+ // T is complex, but the way u is defined the result is real.
110
+ real ang = atan2(sqrt(-disc), -(S + r3));
111
+ // There are three possible cube roots. We choose the root which
112
+ // avoids cancellation. Note that disc < 0 implies that r < 0.
113
+ u += 2 * r * cos(ang / 3);
114
+ }
115
+ real
116
+ v = sqrt(Math::sq(u) + _e4a * q), // guaranteed positive
117
+ // Avoid loss of accuracy when u < 0. Underflow doesn't occur in
118
+ // e4 * q / (v - u) because u ~ e^4 when q is small and u < 0.
119
+ uv = u < 0 ? _e4a * q / (v - u) : u + v, // u+v, guaranteed positive
120
+ // Need to guard against w going negative due to roundoff in uv - q.
121
+ w = max(real(0), _e2a * (uv - q) / (2 * v)),
122
+ // Rearrange expression for k to avoid loss of accuracy due to
123
+ // subtraction. Division by 0 not possible because uv > 0, w >= 0.
124
+ k = uv / (sqrt(uv + Math::sq(w)) + w),
125
+ k1 = _f >= 0 ? k : k - _e2,
126
+ k2 = _f >= 0 ? k + _e2 : k,
127
+ d = k1 * R / k2,
128
+ H = Math::hypot(Z/k1, R/k2);
129
+ sphi = (Z/k1) / H;
130
+ cphi = (R/k2) / H;
131
+ h = (1 - _e2m/k1) * Math::hypot(d, Z);
132
+ } else { // e4 * q == 0 && r <= 0
133
+ // This leads to k = 0 (oblate, equatorial plane) and k + e^2 = 0
134
+ // (prolate, rotation axis) and the generation of 0/0 in the general
135
+ // formulas for phi and h. using the general formula and division by 0
136
+ // in formula for h. So handle this case by taking the limits:
137
+ // f > 0: z -> 0, k -> e2 * sqrt(q)/sqrt(e4 - p)
138
+ // f < 0: R -> 0, k + e2 -> - e2 * sqrt(q)/sqrt(e4 - p)
139
+ real
140
+ zz = sqrt((_f >= 0 ? _e4a - p : p) / _e2m),
141
+ xx = sqrt( _f < 0 ? _e4a - p : p ),
142
+ H = Math::hypot(zz, xx);
143
+ sphi = zz / H;
144
+ cphi = xx / H;
145
+ if (Z < 0) sphi = -sphi; // for tiny negative Z (not for prolate)
146
+ h = - _a * (_f >= 0 ? _e2m : 1) * H / _e2a;
147
+ }
148
+ }
149
+ lat = Math::atan2d(sphi, cphi);
150
+ lon = Math::atan2d(slam, clam);
151
+ if (M)
152
+ Rotation(sphi, cphi, slam, clam, M);
153
+ }
154
+
155
+ void Geocentric::Rotation(real sphi, real cphi, real slam, real clam,
156
+ real M[dim2_]) {
157
+ // This rotation matrix is given by the following quaternion operations
158
+ // qrot(lam, [0,0,1]) * qrot(phi, [0,-1,0]) * [1,1,1,1]/2
159
+ // or
160
+ // qrot(pi/2 + lam, [0,0,1]) * qrot(-pi/2 + phi , [-1,0,0])
161
+ // where
162
+ // qrot(t,v) = [cos(t/2), sin(t/2)*v[1], sin(t/2)*v[2], sin(t/2)*v[3]]
163
+
164
+ // Local X axis (east) in geocentric coords
165
+ M[0] = -slam; M[3] = clam; M[6] = 0;
166
+ // Local Y axis (north) in geocentric coords
167
+ M[1] = -clam * sphi; M[4] = -slam * sphi; M[7] = cphi;
168
+ // Local Z axis (up) in geocentric coords
169
+ M[2] = clam * cphi; M[5] = slam * cphi; M[8] = sphi;
170
+ }
171
+
172
+ } // namespace GeographicLib