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