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,135 @@
1
+ /**
2
+ * \file Georef.cpp
3
+ * \brief Implementation for GeographicLib::Georef 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/Georef.hpp>
11
+ #include <GeographicLib/Utility.hpp>
12
+
13
+ namespace GeographicLib {
14
+
15
+ using namespace std;
16
+
17
+ const string Georef::digits_ = "0123456789";
18
+ const string Georef::lontile_ = "ABCDEFGHJKLMNPQRSTUVWXYZ";
19
+ const string Georef::lattile_ = "ABCDEFGHJKLM";
20
+ const string Georef::degrees_ = "ABCDEFGHJKLMNPQ";
21
+
22
+ void Georef::Forward(real lat, real lon, int prec, std::string& georef) {
23
+ if (abs(lat) > 90)
24
+ throw GeographicErr("Latitude " + Utility::str(lat)
25
+ + "d not in [-90d, 90d]");
26
+ if (Math::isnan(lat) || Math::isnan(lon)) {
27
+ georef = "INVALID";
28
+ return;
29
+ }
30
+ lon = Math::AngNormalize(lon); // lon in [-180,180)
31
+ if (lat == 90) lat *= (1 - numeric_limits<real>::epsilon() / 2);
32
+ prec = max(-1, min(int(maxprec_), prec));
33
+ if (prec == 1) ++prec; // Disallow prec = 1
34
+ // The C++ standard mandates 64 bits for long long. But
35
+ // check, to make sure.
36
+ GEOGRAPHICLIB_STATIC_ASSERT(numeric_limits<long long>::digits >= 45,
37
+ "long long not wide enough to store 21600e9");
38
+ const long long m = 60000000000LL;
39
+ long long
40
+ x = (long long)(floor(lon * m)) - lonorig_ * m,
41
+ y = (long long)(floor(lat * m)) - latorig_ * m;
42
+ int ilon = int(x / m); int ilat = int(y / m);
43
+ char georef1[maxlen_];
44
+ georef1[0] = lontile_[ilon / tile_];
45
+ georef1[1] = lattile_[ilat / tile_];
46
+ if (prec >= 0) {
47
+ georef1[2] = degrees_[ilon % tile_];
48
+ georef1[3] = degrees_[ilat % tile_];
49
+ if (prec > 0) {
50
+ x -= m * ilon; y -= m * ilat;
51
+ long long d = (long long)pow(real(base_), maxprec_ - prec);
52
+ x /= d; y /= d;
53
+ for (int c = prec; c--;) {
54
+ georef1[baselen_ + c ] = digits_[x % base_]; x /= base_;
55
+ georef1[baselen_ + c + prec] = digits_[y % base_]; y /= base_;
56
+ }
57
+ }
58
+ }
59
+ georef.resize(baselen_ + 2 * prec);
60
+ copy(georef1, georef1 + baselen_ + 2 * prec, georef.begin());
61
+ }
62
+
63
+ void Georef::Reverse(const std::string& georef, real& lat, real& lon,
64
+ int& prec, bool centerp) {
65
+ int len = int(georef.length());
66
+ if (len >= 3 &&
67
+ toupper(georef[0]) == 'I' &&
68
+ toupper(georef[1]) == 'N' &&
69
+ toupper(georef[2]) == 'V') {
70
+ lat = lon = Math::NaN();
71
+ return;
72
+ }
73
+ if (len < baselen_ - 2)
74
+ throw GeographicErr("Georef must start with at least 2 letters "
75
+ + georef);
76
+ int prec1 = (2 + len - baselen_) / 2 - 1;
77
+ int k;
78
+ k = Utility::lookup(lontile_, georef[0]);
79
+ if (k < 0)
80
+ throw GeographicErr("Bad longitude tile letter in georef " + georef);
81
+ real lon1 = k + lonorig_ / tile_;
82
+ k = Utility::lookup(lattile_, georef[1]);
83
+ if (k < 0)
84
+ throw GeographicErr("Bad latitude tile letter in georef " + georef);
85
+ real lat1 = k + latorig_ / tile_;
86
+ real unit = 1;
87
+ if (len > 2) {
88
+ unit *= tile_;
89
+ k = Utility::lookup(degrees_, georef[2]);
90
+ if (k < 0)
91
+ throw GeographicErr("Bad longitude degree letter in georef " + georef);
92
+ lon1 = lon1 * tile_ + k;
93
+ if (len < 4)
94
+ throw GeographicErr("Missing latitude degree letter in georef "
95
+ + georef);
96
+ k = Utility::lookup(degrees_, georef[3]);
97
+ if (k < 0)
98
+ throw GeographicErr("Bad latitude degree letter in georef " + georef);
99
+ lat1 = lat1 * tile_ + k;
100
+ if (prec1 > 0) {
101
+ if (georef.find_first_not_of(digits_, baselen_) != string::npos)
102
+ throw GeographicErr("Non digits in trailing portion of georef "
103
+ + georef.substr(baselen_));
104
+ if (len % 2)
105
+ throw GeographicErr("Georef must end with an even number of digits "
106
+ + georef.substr(baselen_));
107
+ if (prec1 == 1)
108
+ throw GeographicErr("Georef needs at least 4 digits for minutes "
109
+ + georef.substr(baselen_));
110
+ if (prec1 > maxprec_)
111
+ throw GeographicErr("More than " + Utility::str(2*maxprec_)
112
+ + " digits in georef " + georef.substr(baselen_));
113
+ for (int i = 0; i < prec1; ++i) {
114
+ int m = i ? base_ : 6;
115
+ unit *= m;
116
+ int
117
+ x = Utility::lookup(digits_, georef[baselen_ + i]),
118
+ y = Utility::lookup(digits_, georef[baselen_ + i + prec1]);
119
+ if (!(i || (x < m && y < m)))
120
+ throw GeographicErr("Minutes terms in georef must be less than 60 "
121
+ + georef.substr(baselen_));
122
+ lon1 = m * lon1 + x;
123
+ lat1 = m * lat1 + y;
124
+ }
125
+ }
126
+ }
127
+ if (centerp) {
128
+ unit *= 2; lat1 = 2 * lat1 + 1; lon1 = 2 * lon1 + 1;
129
+ }
130
+ lat = (tile_ * lat1) / unit;
131
+ lon = (tile_ * lon1) / unit;
132
+ prec = prec1;
133
+ }
134
+
135
+ } // namespace GeographicLib
@@ -0,0 +1,85 @@
1
+ /**
2
+ * \file Gnomonic.cpp
3
+ * \brief Implementation for GeographicLib::Gnomonic class
4
+ *
5
+ * Copyright (c) Charles Karney (2010-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/Gnomonic.hpp>
11
+
12
+ #if defined(_MSC_VER)
13
+ // Squelch warnings about potentially uninitialized local variables and
14
+ // constant conditional expressions
15
+ # pragma warning (disable: 4701 4127)
16
+ #endif
17
+
18
+ namespace GeographicLib {
19
+
20
+ using namespace std;
21
+
22
+ Gnomonic::Gnomonic(const Geodesic& earth)
23
+ : eps0_(numeric_limits<real>::epsilon())
24
+ , eps_(real(0.01) * sqrt(eps0_))
25
+ , _earth(earth)
26
+ , _a(_earth.MajorRadius())
27
+ , _f(_earth.Flattening())
28
+ {}
29
+
30
+ void Gnomonic::Forward(real lat0, real lon0, real lat, real lon,
31
+ real& x, real& y, real& azi, real& rk)
32
+ const {
33
+ real azi0, m, M, t;
34
+ _earth.GenInverse(lat0, lon0, lat, lon,
35
+ Geodesic::AZIMUTH | Geodesic::REDUCEDLENGTH |
36
+ Geodesic::GEODESICSCALE,
37
+ t, azi0, azi, m, M, t, t);
38
+ rk = M;
39
+ if (M <= 0)
40
+ x = y = Math::NaN();
41
+ else {
42
+ real rho = m/M;
43
+ Math::sincosd(azi0, x, y);
44
+ x *= rho; y *= rho;
45
+ }
46
+ }
47
+
48
+ void Gnomonic::Reverse(real lat0, real lon0, real x, real y,
49
+ real& lat, real& lon, real& azi, real& rk)
50
+ const {
51
+ real
52
+ azi0 = Math::atan2d(x, y),
53
+ rho = Math::hypot(x, y),
54
+ s = _a * atan(rho/_a);
55
+ bool little = rho <= _a;
56
+ if (!little)
57
+ rho = 1/rho;
58
+ GeodesicLine line(_earth.Line(lat0, lon0, azi0,
59
+ Geodesic::LATITUDE | Geodesic::LONGITUDE |
60
+ Geodesic::AZIMUTH | Geodesic::DISTANCE_IN |
61
+ Geodesic::REDUCEDLENGTH |
62
+ Geodesic::GEODESICSCALE));
63
+ int count = numit_, trip = 0;
64
+ real lat1, lon1, azi1, M;
65
+ while (count-- || GEOGRAPHICLIB_PANIC) {
66
+ real m, t;
67
+ line.Position(s, lat1, lon1, azi1, m, M, t);
68
+ if (trip)
69
+ break;
70
+ // If little, solve rho(s) = rho with drho(s)/ds = 1/M^2
71
+ // else solve 1/rho(s) = 1/rho with d(1/rho(s))/ds = -1/m^2
72
+ real ds = little ? (m/M - rho) * M * M : (rho - M/m) * m * m;
73
+ s -= ds;
74
+ // Reversed test to allow escape with NaNs
75
+ if (!(abs(ds) >= eps_ * _a))
76
+ ++trip;
77
+ }
78
+ if (trip) {
79
+ lat = lat1; lon = lon1; azi = azi1; rk = M;
80
+ } else
81
+ lat = lon = azi = rk = Math::NaN();
82
+ return;
83
+ }
84
+
85
+ } // namespace GeographicLib
@@ -0,0 +1,129 @@
1
+ /**
2
+ * \file GravityCircle.cpp
3
+ * \brief Implementation for GeographicLib::GravityCircle class
4
+ *
5
+ * Copyright (c) Charles Karney (2011-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/GravityCircle.hpp>
11
+ #include <fstream>
12
+ #include <sstream>
13
+ #include <GeographicLib/Geocentric.hpp>
14
+
15
+ namespace GeographicLib {
16
+
17
+ using namespace std;
18
+
19
+ Math::real GravityCircle::Gravity(real lon, real& gx, real& gy, real& gz)
20
+ const {
21
+ real slam, clam, M[Geocentric::dim2_];
22
+ Math::sincosd(lon, slam, clam);
23
+ real Wres = W(slam, clam, gx, gy, gz);
24
+ Geocentric::Rotation(_sphi, _cphi, slam, clam, M);
25
+ Geocentric::Unrotate(M, gx, gy, gz, gx, gy, gz);
26
+ return Wres;
27
+ }
28
+
29
+ Math::real GravityCircle::Disturbance(real lon, real& deltax, real& deltay,
30
+ real& deltaz) const {
31
+ real slam, clam, M[Geocentric::dim2_];
32
+ Math::sincosd(lon, slam, clam);
33
+ real Tres = InternalT(slam, clam, deltax, deltay, deltaz, true, true);
34
+ Geocentric::Rotation(_sphi, _cphi, slam, clam, M);
35
+ Geocentric::Unrotate(M, deltax, deltay, deltaz, deltax, deltay, deltaz);
36
+ return Tres;
37
+ }
38
+
39
+ Math::real GravityCircle::GeoidHeight(real lon) const {
40
+ if ((_caps & GEOID_HEIGHT) != GEOID_HEIGHT)
41
+ return Math::NaN();
42
+ real slam, clam, dummy;
43
+ Math::sincosd(lon, slam, clam);
44
+ real T = InternalT(slam, clam, dummy, dummy, dummy, false, false);
45
+ real correction = _corrmult * _correction(slam, clam);
46
+ return T/_gamma0 + correction;
47
+ }
48
+
49
+ void GravityCircle::SphericalAnomaly(real lon,
50
+ real& Dg01, real& xi, real& eta)
51
+ const {
52
+ if ((_caps & SPHERICAL_ANOMALY) != SPHERICAL_ANOMALY) {
53
+ Dg01 = xi = eta = Math::NaN();
54
+ return;
55
+ }
56
+ real slam, clam;
57
+ Math::sincosd(lon, slam, clam);
58
+ real
59
+ deltax, deltay, deltaz,
60
+ T = InternalT(slam, clam, deltax, deltay, deltaz, true, false);
61
+ // Rotate cartesian into spherical coordinates
62
+ real MC[Geocentric::dim2_];
63
+ Geocentric::Rotation(_spsi, _cpsi, slam, clam, MC);
64
+ Geocentric::Unrotate(MC, deltax, deltay, deltaz, deltax, deltay, deltaz);
65
+ // H+M, Eq 2-151c
66
+ Dg01 = - deltaz - 2 * T * _invR;
67
+ xi = -(deltay/_gamma) / Math::degree();
68
+ eta = -(deltax/_gamma) / Math::degree();
69
+ }
70
+
71
+ Math::real GravityCircle::W(real slam, real clam,
72
+ real& gX, real& gY, real& gZ) const {
73
+ real Wres = V(slam, clam, gX, gY, gZ) + _frot * _Px / 2;
74
+ gX += _frot * clam;
75
+ gY += _frot * slam;
76
+ return Wres;
77
+ }
78
+
79
+ Math::real GravityCircle::V(real slam, real clam,
80
+ real& GX, real& GY, real& GZ)
81
+ const {
82
+ if ((_caps & GRAVITY) != GRAVITY) {
83
+ GX = GY = GZ = Math::NaN();
84
+ return Math::NaN();
85
+ }
86
+ real
87
+ Vres = _gravitational(slam, clam, GX, GY, GZ),
88
+ f = _GMmodel / _amodel;
89
+ Vres *= f;
90
+ GX *= f;
91
+ GY *= f;
92
+ GZ *= f;
93
+ return Vres;
94
+ }
95
+
96
+ Math::real GravityCircle::InternalT(real slam, real clam,
97
+ real& deltaX, real& deltaY, real& deltaZ,
98
+ bool gradp, bool correct) const {
99
+ if (gradp) {
100
+ if ((_caps & DISTURBANCE) != DISTURBANCE) {
101
+ deltaX = deltaY = deltaZ = Math::NaN();
102
+ return Math::NaN();
103
+ }
104
+ } else {
105
+ if ((_caps & DISTURBING_POTENTIAL) != DISTURBING_POTENTIAL)
106
+ return Math::NaN();
107
+ }
108
+ if (_dzonal0 == 0)
109
+ correct = false;
110
+ real T = (gradp
111
+ ? _disturbing(slam, clam, deltaX, deltaY, deltaZ)
112
+ : _disturbing(slam, clam));
113
+ T = (T / _amodel - (correct ? _dzonal0 : 0) * _invR) * _GMmodel;
114
+ if (gradp) {
115
+ real f = _GMmodel / _amodel;
116
+ deltaX *= f;
117
+ deltaY *= f;
118
+ deltaZ *= f;
119
+ if (correct) {
120
+ real r3 = _GMmodel * _dzonal0 * _invR * _invR * _invR;
121
+ deltaX += _Px * clam * r3;
122
+ deltaY += _Px * slam * r3;
123
+ deltaZ += _Z * r3;
124
+ }
125
+ }
126
+ return T;
127
+ }
128
+
129
+ } // namespace GeographicLib
@@ -0,0 +1,360 @@
1
+ /**
2
+ * \file GravityModel.cpp
3
+ * \brief Implementation for GeographicLib::GravityModel class
4
+ *
5
+ * Copyright (c) Charles Karney (2011-2012) <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/GravityModel.hpp>
11
+ #include <fstream>
12
+ #include <GeographicLib/SphericalEngine.hpp>
13
+ #include <GeographicLib/GravityCircle.hpp>
14
+ #include <GeographicLib/Utility.hpp>
15
+
16
+ #if !defined(GEOGRAPHICLIB_DATA)
17
+ # if defined(_WIN32)
18
+ # define GEOGRAPHICLIB_DATA "C:/ProgramData/GeographicLib"
19
+ # else
20
+ # define GEOGRAPHICLIB_DATA "/usr/local/share/GeographicLib"
21
+ # endif
22
+ #endif
23
+
24
+ #if !defined(GEOGRAPHICLIB_GRAVITY_DEFAULT_NAME)
25
+ # define GEOGRAPHICLIB_GRAVITY_DEFAULT_NAME "egm96"
26
+ #endif
27
+
28
+ #if defined(_MSC_VER)
29
+ // Squelch warnings about unsafe use of getenv
30
+ # pragma warning (disable: 4996)
31
+ #endif
32
+
33
+ namespace GeographicLib {
34
+
35
+ using namespace std;
36
+
37
+ GravityModel::GravityModel(const std::string& name,const std::string& path)
38
+ : _name(name)
39
+ , _dir(path)
40
+ , _description("NONE")
41
+ , _date("UNKNOWN")
42
+ , _amodel(Math::NaN())
43
+ , _GMmodel(Math::NaN())
44
+ , _zeta0(0)
45
+ , _corrmult(1)
46
+ , _norm(SphericalHarmonic::FULL)
47
+ {
48
+ if (_dir.empty())
49
+ _dir = DefaultGravityPath();
50
+ ReadMetadata(_name);
51
+ {
52
+ string coeff = _filename + ".cof";
53
+ ifstream coeffstr(coeff.c_str(), ios::binary);
54
+ if (!coeffstr.good())
55
+ throw GeographicErr("Error opening " + coeff);
56
+ char id[idlength_ + 1];
57
+ coeffstr.read(id, idlength_);
58
+ if (!coeffstr.good())
59
+ throw GeographicErr("No header in " + coeff);
60
+ id[idlength_] = '\0';
61
+ if (_id != string(id))
62
+ throw GeographicErr("ID mismatch: " + _id + " vs " + id);
63
+ int N, M;
64
+ SphericalEngine::coeff::readcoeffs(coeffstr, N, M, _Cx, _Sx);
65
+ if (!(N >= 0 && M >= 0))
66
+ throw GeographicErr("Degree and order must be at least 0");
67
+ if (_Cx[0] != 0)
68
+ throw GeographicErr("A degree 0 term should be zero");
69
+ _Cx[0] = 1; // Include the 1/r term in the sum
70
+ _gravitational = SphericalHarmonic(_Cx, _Sx, N, N, M, _amodel, _norm);
71
+ SphericalEngine::coeff::readcoeffs(coeffstr, N, M, _CC, _CS);
72
+ if (N < 0) {
73
+ N = M = 0;
74
+ _CC.resize(1, real(0));
75
+ }
76
+ _CC[0] += _zeta0 / _corrmult;
77
+ _correction = SphericalHarmonic(_CC, _CS, N, N, M, real(1), _norm);
78
+ int pos = int(coeffstr.tellg());
79
+ coeffstr.seekg(0, ios::end);
80
+ if (pos != coeffstr.tellg())
81
+ throw GeographicErr("Extra data in " + coeff);
82
+ }
83
+ int nmx = _gravitational.Coefficients().nmx();
84
+ // Adjust the normalization of the normal potential to match the model.
85
+ real mult = _earth._GM / _GMmodel;
86
+ real amult = Math::sq(_earth._a / _amodel);
87
+ // The 0th term in _zonal should be is 1 + _dzonal0. Instead set it to 1
88
+ // to give exact cancellation with the (0,0) term in the model and account
89
+ // for _dzonal0 separately.
90
+ _zonal.clear(); _zonal.push_back(1);
91
+ _dzonal0 = (_earth.MassConstant() - _GMmodel) / _GMmodel;
92
+ for (int n = 2; n <= nmx; n += 2) {
93
+ // Only include as many normal zonal terms as matter. Figuring the limit
94
+ // in this way works because the coefficients of the normal potential
95
+ // (which is smooth) decay much more rapidly that the corresponding
96
+ // coefficient of the model potential (which is bumpy). Typically this
97
+ // goes out to n = 18.
98
+ mult *= amult;
99
+ real
100
+ r = _Cx[n], // the model term
101
+ s = - mult * _earth.Jn(n) / sqrt(real(2 * n + 1)), // the normal term
102
+ t = r - s; // the difference
103
+ if (t == r) // the normal term is negligible
104
+ break;
105
+ _zonal.push_back(0); // index = n - 1; the odd terms are 0
106
+ _zonal.push_back(s);
107
+ }
108
+ int nmx1 = int(_zonal.size()) - 1;
109
+ _disturbing = SphericalHarmonic1(_Cx, _Sx,
110
+ _gravitational.Coefficients().N(),
111
+ nmx, _gravitational.Coefficients().mmx(),
112
+ _zonal,
113
+ _zonal, // This is not accessed!
114
+ nmx1, nmx1, 0,
115
+ _amodel,
116
+ SphericalHarmonic1::normalization(_norm));
117
+ }
118
+
119
+ void GravityModel::ReadMetadata(const std::string& name) {
120
+ const char* spaces = " \t\n\v\f\r";
121
+ _filename = _dir + "/" + name + ".egm";
122
+ ifstream metastr(_filename.c_str());
123
+ if (!metastr.good())
124
+ throw GeographicErr("Cannot open " + _filename);
125
+ string line;
126
+ getline(metastr, line);
127
+ if (!(line.size() >= 6 && line.substr(0,5) == "EGMF-"))
128
+ throw GeographicErr(_filename + " does not contain EGMF-n signature");
129
+ string::size_type n = line.find_first_of(spaces, 5);
130
+ if (n != string::npos)
131
+ n -= 5;
132
+ string version = line.substr(5, n);
133
+ if (version != "1")
134
+ throw GeographicErr("Unknown version in " + _filename + ": " + version);
135
+ string key, val;
136
+ real a = Math::NaN(), GM = a, omega = a, f = a, J2 = a;
137
+ while (getline(metastr, line)) {
138
+ if (!Utility::ParseLine(line, key, val))
139
+ continue;
140
+ // Process key words
141
+ if (key == "Name")
142
+ _name = val;
143
+ else if (key == "Description")
144
+ _description = val;
145
+ else if (key == "ReleaseDate")
146
+ _date = val;
147
+ else if (key == "ModelRadius")
148
+ _amodel = Utility::num<real>(val);
149
+ else if (key == "ModelMass")
150
+ _GMmodel = Utility::num<real>(val);
151
+ else if (key == "AngularVelocity")
152
+ omega = Utility::num<real>(val);
153
+ else if (key == "ReferenceRadius")
154
+ a = Utility::num<real>(val);
155
+ else if (key == "ReferenceMass")
156
+ GM = Utility::num<real>(val);
157
+ else if (key == "Flattening")
158
+ f = Utility::fract<real>(val);
159
+ else if (key == "DynamicalFormFactor")
160
+ J2 = Utility::fract<real>(val);
161
+ else if (key == "HeightOffset")
162
+ _zeta0 = Utility::fract<real>(val);
163
+ else if (key == "CorrectionMultiplier")
164
+ _corrmult = Utility::fract<real>(val);
165
+ else if (key == "Normalization") {
166
+ if (val == "FULL" || val == "Full" || val == "full")
167
+ _norm = SphericalHarmonic::FULL;
168
+ else if (val == "SCHMIDT" || val == "Schmidt" || val == "schmidt")
169
+ _norm = SphericalHarmonic::SCHMIDT;
170
+ else
171
+ throw GeographicErr("Unknown normalization " + val);
172
+ } else if (key == "ByteOrder") {
173
+ if (val == "Big" || val == "big")
174
+ throw GeographicErr("Only little-endian ordering is supported");
175
+ else if (!(val == "Little" || val == "little"))
176
+ throw GeographicErr("Unknown byte ordering " + val);
177
+ } else if (key == "ID")
178
+ _id = val;
179
+ // else unrecognized keywords are skipped
180
+ }
181
+ // Check values
182
+ if (!(Math::isfinite(_amodel) && _amodel > 0))
183
+ throw GeographicErr("Model radius must be positive");
184
+ if (!(Math::isfinite(_GMmodel) && _GMmodel > 0))
185
+ throw GeographicErr("Model mass constant must be positive");
186
+ if (!(Math::isfinite(_corrmult) && _corrmult > 0))
187
+ throw GeographicErr("Correction multiplier must be positive");
188
+ if (!(Math::isfinite(_zeta0)))
189
+ throw GeographicErr("Height offset must be finite");
190
+ if (int(_id.size()) != idlength_)
191
+ throw GeographicErr("Invalid ID");
192
+ _earth = NormalGravity(a, GM, omega, f, J2);
193
+ }
194
+
195
+ Math::real GravityModel::InternalT(real X, real Y, real Z,
196
+ real& deltaX, real& deltaY, real& deltaZ,
197
+ bool gradp, bool correct) const {
198
+ // If correct, then produce the correct T = W - U. Otherwise, neglect the
199
+ // n = 0 term (which is proportial to the difference in the model and
200
+ // reference values of GM).
201
+ if (_dzonal0 == 0)
202
+ // No need to do the correction
203
+ correct = false;
204
+ real T, invR = correct ? 1 / Math::hypot(Math::hypot(X, Y), Z) : 1;
205
+ if (gradp) {
206
+ // initial values to suppress warnings
207
+ deltaX = deltaY = deltaZ = 0;
208
+ T = _disturbing(-1, X, Y, Z, deltaX, deltaY, deltaZ);
209
+ real f = _GMmodel / _amodel;
210
+ deltaX *= f;
211
+ deltaY *= f;
212
+ deltaZ *= f;
213
+ if (correct) {
214
+ invR = _GMmodel * _dzonal0 * invR * invR * invR;
215
+ deltaX += X * invR;
216
+ deltaY += Y * invR;
217
+ deltaZ += Z * invR;
218
+ }
219
+ } else
220
+ T = _disturbing(-1, X, Y, Z);
221
+ T = (T / _amodel - (correct ? _dzonal0 : 0) * invR) * _GMmodel;
222
+ return T;
223
+ }
224
+
225
+ Math::real GravityModel::V(real X, real Y, real Z,
226
+ real& GX, real& GY, real& GZ) const {
227
+ real
228
+ Vres = _gravitational(X, Y, Z, GX, GY, GZ),
229
+ f = _GMmodel / _amodel;
230
+ Vres *= f;
231
+ GX *= f;
232
+ GY *= f;
233
+ GZ *= f;
234
+ return Vres;
235
+ }
236
+
237
+ Math::real GravityModel::W(real X, real Y, real Z,
238
+ real& gX, real& gY, real& gZ) const {
239
+ real fX, fY,
240
+ Wres = V(X, Y, Z, gX, gY, gZ) + _earth.Phi(X, Y, fX, fY);
241
+ gX += fX;
242
+ gY += fY;
243
+ return Wres;
244
+ }
245
+
246
+ void GravityModel::SphericalAnomaly(real lat, real lon, real h,
247
+ real& Dg01, real& xi, real& eta)
248
+ const {
249
+ real X, Y, Z, M[Geocentric::dim2_];
250
+ _earth.Earth().IntForward(lat, lon, h, X, Y, Z, M);
251
+ real
252
+ deltax, deltay, deltaz,
253
+ T = InternalT(X, Y, Z, deltax, deltay, deltaz, true, false),
254
+ clam = M[3], slam = -M[0],
255
+ P = Math::hypot(X, Y),
256
+ R = Math::hypot(P, Z),
257
+ // psi is geocentric latitude
258
+ cpsi = R ? P / R : M[7],
259
+ spsi = R ? Z / R : M[8];
260
+ // Rotate cartesian into spherical coordinates
261
+ real MC[Geocentric::dim2_];
262
+ Geocentric::Rotation(spsi, cpsi, slam, clam, MC);
263
+ Geocentric::Unrotate(MC, deltax, deltay, deltaz, deltax, deltay, deltaz);
264
+ // H+M, Eq 2-151c
265
+ Dg01 = - deltaz - 2 * T / R;
266
+ real gammaX, gammaY, gammaZ;
267
+ _earth.U(X, Y, Z, gammaX, gammaY, gammaZ);
268
+ real gamma = Math::hypot( Math::hypot(gammaX, gammaY), gammaZ);
269
+ xi = -(deltay/gamma) / Math::degree();
270
+ eta = -(deltax/gamma) / Math::degree();
271
+ }
272
+
273
+ Math::real GravityModel::GeoidHeight(real lat, real lon) const
274
+ {
275
+ real X, Y, Z;
276
+ _earth.Earth().IntForward(lat, lon, 0, X, Y, Z, NULL);
277
+ real
278
+ gamma0 = _earth.SurfaceGravity(lat),
279
+ dummy,
280
+ T = InternalT(X, Y, Z, dummy, dummy, dummy, false, false),
281
+ invR = 1 / Math::hypot(Math::hypot(X, Y), Z),
282
+ correction = _corrmult * _correction(invR * X, invR * Y, invR * Z);
283
+ // _zeta0 has been included in _correction
284
+ return T/gamma0 + correction;
285
+ }
286
+
287
+ Math::real GravityModel::Gravity(real lat, real lon, real h,
288
+ real& gx, real& gy, real& gz) const {
289
+ real X, Y, Z, M[Geocentric::dim2_];
290
+ _earth.Earth().IntForward(lat, lon, h, X, Y, Z, M);
291
+ real Wres = W(X, Y, Z, gx, gy, gz);
292
+ Geocentric::Unrotate(M, gx, gy, gz, gx, gy, gz);
293
+ return Wres;
294
+ }
295
+ Math::real GravityModel::Disturbance(real lat, real lon, real h,
296
+ real& deltax, real& deltay, real& deltaz)
297
+ const {
298
+ real X, Y, Z, M[Geocentric::dim2_];
299
+ _earth.Earth().IntForward(lat, lon, h, X, Y, Z, M);
300
+ real Tres = InternalT(X, Y, Z, deltax, deltay, deltaz, true, true);
301
+ Geocentric::Unrotate(M, deltax, deltay, deltaz, deltax, deltay, deltaz);
302
+ return Tres;
303
+ }
304
+
305
+ GravityCircle GravityModel::Circle(real lat, real h, unsigned caps) const {
306
+ if (h != 0)
307
+ // Disallow invoking GeoidHeight unless h is zero.
308
+ caps &= ~(CAP_GAMMA0 | CAP_C);
309
+ real X, Y, Z, M[Geocentric::dim2_];
310
+ _earth.Earth().IntForward(lat, 0, h, X, Y, Z, M);
311
+ // Y = 0, cphi = M[7], sphi = M[8];
312
+ real
313
+ invR = 1 / Math::hypot(X, Z),
314
+ gamma0 = (caps & CAP_GAMMA0 ?_earth.SurfaceGravity(lat)
315
+ : Math::NaN()),
316
+ fx, fy, fz, gamma;
317
+ if (caps & CAP_GAMMA) {
318
+ _earth.U(X, Y, Z, fx, fy, fz); // fy = 0
319
+ gamma = Math::hypot(fx, fz);
320
+ } else
321
+ gamma = Math::NaN();
322
+ _earth.Phi(X, Y, fx, fy);
323
+ return GravityCircle(GravityCircle::mask(caps),
324
+ _earth._a, _earth._f, lat, h, Z, X, M[7], M[8],
325
+ _amodel, _GMmodel, _dzonal0, _corrmult,
326
+ gamma0, gamma, fx,
327
+ caps & CAP_G ?
328
+ _gravitational.Circle(X, Z, true) :
329
+ CircularEngine(),
330
+ // N.B. If CAP_DELTA is set then CAP_T should be too.
331
+ caps & CAP_T ?
332
+ _disturbing.Circle(-1, X, Z, (caps & CAP_DELTA) != 0) :
333
+ CircularEngine(),
334
+ caps & CAP_C ?
335
+ _correction.Circle(invR * X, invR * Z, false) :
336
+ CircularEngine());
337
+ }
338
+
339
+ std::string GravityModel::DefaultGravityPath() {
340
+ string path;
341
+ char* gravitypath = getenv("GEOGRAPHICLIB_GRAVITY_PATH");
342
+ if (gravitypath)
343
+ path = string(gravitypath);
344
+ if (!path.empty())
345
+ return path;
346
+ char* datapath = getenv("GEOGRAPHICLIB_DATA");
347
+ if (datapath)
348
+ path = string(datapath);
349
+ return (!path.empty() ? path : string(GEOGRAPHICLIB_DATA)) + "/gravity";
350
+ }
351
+
352
+ std::string GravityModel::DefaultGravityName() {
353
+ string name;
354
+ char* gravityname = getenv("GEOGRAPHICLIB_GRAVITY_NAME");
355
+ if (gravityname)
356
+ name = string(gravityname);
357
+ return !name.empty() ? name : string(GEOGRAPHICLIB_GRAVITY_DEFAULT_NAME);
358
+ }
359
+
360
+ } // namespace GeographicLib