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