geographiclib 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/AUTHORS +12 -0
- data/LICENSE +24 -0
- data/ext/geographiclib/Accumulator.cpp +23 -0
- data/ext/geographiclib/AlbersEqualArea.cpp +445 -0
- data/ext/geographiclib/AzimuthalEquidistant.cpp +41 -0
- data/ext/geographiclib/CassiniSoldner.cpp +89 -0
- data/ext/geographiclib/CircularEngine.cpp +96 -0
- data/ext/geographiclib/DMS.cpp +381 -0
- data/ext/geographiclib/Ellipsoid.cpp +125 -0
- data/ext/geographiclib/EllipticFunction.cpp +512 -0
- data/ext/geographiclib/GARS.cpp +122 -0
- data/ext/geographiclib/GeoCoords.cpp +175 -0
- data/ext/geographiclib/Geocentric.cpp +172 -0
- data/ext/geographiclib/Geodesic.cpp +1908 -0
- data/ext/geographiclib/GeodesicExact.cpp +927 -0
- data/ext/geographiclib/GeodesicExactC4.cpp +7879 -0
- data/ext/geographiclib/GeodesicLine.cpp +321 -0
- data/ext/geographiclib/GeodesicLineExact.cpp +289 -0
- data/ext/geographiclib/GeographicLib/Accumulator.hpp +184 -0
- data/ext/geographiclib/GeographicLib/AlbersEqualArea.hpp +312 -0
- data/ext/geographiclib/GeographicLib/AzimuthalEquidistant.hpp +139 -0
- data/ext/geographiclib/GeographicLib/CassiniSoldner.hpp +204 -0
- data/ext/geographiclib/GeographicLib/CircularEngine.hpp +195 -0
- data/ext/geographiclib/GeographicLib/Config.h +12 -0
- data/ext/geographiclib/GeographicLib/Constants.hpp +387 -0
- data/ext/geographiclib/GeographicLib/DMS.hpp +370 -0
- data/ext/geographiclib/GeographicLib/Ellipsoid.hpp +534 -0
- data/ext/geographiclib/GeographicLib/EllipticFunction.hpp +692 -0
- data/ext/geographiclib/GeographicLib/GARS.hpp +143 -0
- data/ext/geographiclib/GeographicLib/GeoCoords.hpp +544 -0
- data/ext/geographiclib/GeographicLib/Geocentric.hpp +267 -0
- data/ext/geographiclib/GeographicLib/Geodesic.hpp +970 -0
- data/ext/geographiclib/GeographicLib/GeodesicExact.hpp +862 -0
- data/ext/geographiclib/GeographicLib/GeodesicLine.hpp +701 -0
- data/ext/geographiclib/GeographicLib/GeodesicLineExact.hpp +667 -0
- data/ext/geographiclib/GeographicLib/Geohash.hpp +180 -0
- data/ext/geographiclib/GeographicLib/Geoid.hpp +472 -0
- data/ext/geographiclib/GeographicLib/Georef.hpp +160 -0
- data/ext/geographiclib/GeographicLib/Gnomonic.hpp +206 -0
- data/ext/geographiclib/GeographicLib/GravityCircle.hpp +301 -0
- data/ext/geographiclib/GeographicLib/GravityModel.hpp +520 -0
- data/ext/geographiclib/GeographicLib/LambertConformalConic.hpp +313 -0
- data/ext/geographiclib/GeographicLib/LocalCartesian.hpp +236 -0
- data/ext/geographiclib/GeographicLib/MGRS.hpp +355 -0
- data/ext/geographiclib/GeographicLib/MagneticCircle.hpp +178 -0
- data/ext/geographiclib/GeographicLib/MagneticModel.hpp +347 -0
- data/ext/geographiclib/GeographicLib/Math.hpp +920 -0
- data/ext/geographiclib/GeographicLib/NormalGravity.hpp +350 -0
- data/ext/geographiclib/GeographicLib/OSGB.hpp +249 -0
- data/ext/geographiclib/GeographicLib/PolarStereographic.hpp +150 -0
- data/ext/geographiclib/GeographicLib/PolygonArea.hpp +288 -0
- data/ext/geographiclib/GeographicLib/Rhumb.hpp +589 -0
- data/ext/geographiclib/GeographicLib/SphericalEngine.hpp +376 -0
- data/ext/geographiclib/GeographicLib/SphericalHarmonic.hpp +354 -0
- data/ext/geographiclib/GeographicLib/SphericalHarmonic1.hpp +281 -0
- data/ext/geographiclib/GeographicLib/SphericalHarmonic2.hpp +315 -0
- data/ext/geographiclib/GeographicLib/TransverseMercator.hpp +196 -0
- data/ext/geographiclib/GeographicLib/TransverseMercatorExact.hpp +254 -0
- data/ext/geographiclib/GeographicLib/UTMUPS.hpp +421 -0
- data/ext/geographiclib/GeographicLib/Utility.hpp +612 -0
- data/ext/geographiclib/Geohash.cpp +102 -0
- data/ext/geographiclib/Geoid.cpp +509 -0
- data/ext/geographiclib/Georef.cpp +135 -0
- data/ext/geographiclib/Gnomonic.cpp +85 -0
- data/ext/geographiclib/GravityCircle.cpp +129 -0
- data/ext/geographiclib/GravityModel.cpp +360 -0
- data/ext/geographiclib/LambertConformalConic.cpp +456 -0
- data/ext/geographiclib/LocalCartesian.cpp +62 -0
- data/ext/geographiclib/MGRS.cpp +461 -0
- data/ext/geographiclib/MagneticCircle.cpp +52 -0
- data/ext/geographiclib/MagneticModel.cpp +269 -0
- data/ext/geographiclib/Math.cpp +63 -0
- data/ext/geographiclib/NormalGravity.cpp +262 -0
- data/ext/geographiclib/OSGB.cpp +167 -0
- data/ext/geographiclib/PolarStereographic.cpp +108 -0
- data/ext/geographiclib/PolygonArea.cpp +204 -0
- data/ext/geographiclib/Rhumb.cpp +383 -0
- data/ext/geographiclib/SphericalEngine.cpp +477 -0
- data/ext/geographiclib/TransverseMercator.cpp +603 -0
- data/ext/geographiclib/TransverseMercatorExact.cpp +464 -0
- data/ext/geographiclib/UTMUPS.cpp +296 -0
- data/ext/geographiclib/Utility.cpp +61 -0
- data/ext/geographiclib/extconf.rb +3 -0
- data/ext/geographiclib/geographiclib.cpp +62 -0
- data/lib/geographiclib.rb +20 -0
- 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
|