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,52 @@
1
+ /**
2
+ * \file MagneticCircle.cpp
3
+ * \brief Implementation for GeographicLib::MagneticCircle 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/MagneticCircle.hpp>
11
+ #include <fstream>
12
+ #include <sstream>
13
+ #include <GeographicLib/Geocentric.hpp>
14
+
15
+ namespace GeographicLib {
16
+
17
+ using namespace std;
18
+
19
+ void MagneticCircle::Field(real lon, bool diffp,
20
+ real& Bx, real& By, real& Bz,
21
+ real& Bxt, real& Byt, real& Bzt) const {
22
+ real slam, clam;
23
+ Math::sincosd(lon, slam, clam);
24
+ real M[Geocentric::dim2_];
25
+ Geocentric::Rotation(_sphi, _cphi, slam, clam, M);
26
+ real BX0, BY0, BZ0, BX1, BY1, BZ1; // Components in geocentric basis
27
+ real BXc = 0, BYc = 0, BZc = 0;
28
+ _circ0(slam, clam, BX0, BY0, BZ0);
29
+ _circ1(slam, clam, BX1, BY1, BZ1);
30
+ if (_constterm)
31
+ _circ2(slam, clam, BXc, BYc, BZc);
32
+ if (_interpolate) {
33
+ BX1 = (BX1 - BX0) / _dt0;
34
+ BY1 = (BY1 - BY0) / _dt0;
35
+ BZ1 = (BZ1 - BZ0) / _dt0;
36
+ }
37
+ BX0 += _t1 * BX1 + BXc;
38
+ BY0 += _t1 * BY1 + BYc;
39
+ BZ0 += _t1 * BZ1 + BZc;
40
+ if (diffp) {
41
+ Geocentric::Unrotate(M, BX1, BY1, BZ1, Bxt, Byt, Bzt);
42
+ Bxt *= - _a;
43
+ Byt *= - _a;
44
+ Bzt *= - _a;
45
+ }
46
+ Geocentric::Unrotate(M, BX0, BY0, BZ0, Bx, By, Bz);
47
+ Bx *= - _a;
48
+ By *= - _a;
49
+ Bz *= - _a;
50
+ }
51
+
52
+ } // namespace GeographicLib
@@ -0,0 +1,269 @@
1
+ /**
2
+ * \file MagneticModel.cpp
3
+ * \brief Implementation for GeographicLib::MagneticModel 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/MagneticModel.hpp>
11
+ #include <fstream>
12
+ #include <GeographicLib/SphericalEngine.hpp>
13
+ #include <GeographicLib/MagneticCircle.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_MAGNETIC_DEFAULT_NAME)
25
+ # define GEOGRAPHICLIB_MAGNETIC_DEFAULT_NAME "wmm2015"
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
+ MagneticModel::MagneticModel(const std::string& name,const std::string& path,
38
+ const Geocentric& earth)
39
+ : _name(name)
40
+ , _dir(path)
41
+ , _description("NONE")
42
+ , _date("UNKNOWN")
43
+ , _t0(Math::NaN())
44
+ , _dt0(1)
45
+ , _tmin(Math::NaN())
46
+ , _tmax(Math::NaN())
47
+ , _a(Math::NaN())
48
+ , _hmin(Math::NaN())
49
+ , _hmax(Math::NaN())
50
+ , _Nmodels(1)
51
+ , _Nconstants(0)
52
+ , _norm(SphericalHarmonic::SCHMIDT)
53
+ , _earth(earth)
54
+ {
55
+ if (_dir.empty())
56
+ _dir = DefaultMagneticPath();
57
+ ReadMetadata(_name);
58
+ _G.resize(_Nmodels + 1 + _Nconstants);
59
+ _H.resize(_Nmodels + 1 + _Nconstants);
60
+ {
61
+ string coeff = _filename + ".cof";
62
+ ifstream coeffstr(coeff.c_str(), ios::binary);
63
+ if (!coeffstr.good())
64
+ throw GeographicErr("Error opening " + coeff);
65
+ char id[idlength_ + 1];
66
+ coeffstr.read(id, idlength_);
67
+ if (!coeffstr.good())
68
+ throw GeographicErr("No header in " + coeff);
69
+ id[idlength_] = '\0';
70
+ if (_id != string(id))
71
+ throw GeographicErr("ID mismatch: " + _id + " vs " + id);
72
+ for (int i = 0; i < _Nmodels + 1 + _Nconstants; ++i) {
73
+ int N, M;
74
+ SphericalEngine::coeff::readcoeffs(coeffstr, N, M, _G[i], _H[i]);
75
+ if (!(M < 0 || _G[i][0] == 0))
76
+ throw GeographicErr("A degree 0 term is not permitted");
77
+ _harm.push_back(SphericalHarmonic(_G[i], _H[i], N, N, M, _a, _norm));
78
+ }
79
+ int pos = int(coeffstr.tellg());
80
+ coeffstr.seekg(0, ios::end);
81
+ if (pos != coeffstr.tellg())
82
+ throw GeographicErr("Extra data in " + coeff);
83
+ }
84
+ }
85
+
86
+ void MagneticModel::ReadMetadata(const std::string& name) {
87
+ const char* spaces = " \t\n\v\f\r";
88
+ _filename = _dir + "/" + name + ".wmm";
89
+ ifstream metastr(_filename.c_str());
90
+ if (!metastr.good())
91
+ throw GeographicErr("Cannot open " + _filename);
92
+ string line;
93
+ getline(metastr, line);
94
+ if (!(line.size() >= 6 && line.substr(0,5) == "WMMF-"))
95
+ throw GeographicErr(_filename + " does not contain WMMF-n signature");
96
+ string::size_type n = line.find_first_of(spaces, 5);
97
+ if (n != string::npos)
98
+ n -= 5;
99
+ string version = line.substr(5, n);
100
+ if (!(version == "1" || version == "2"))
101
+ throw GeographicErr("Unknown version in " + _filename + ": " + version);
102
+ string key, val;
103
+ while (getline(metastr, line)) {
104
+ if (!Utility::ParseLine(line, key, val))
105
+ continue;
106
+ // Process key words
107
+ if (key == "Name")
108
+ _name = val;
109
+ else if (key == "Description")
110
+ _description = val;
111
+ else if (key == "ReleaseDate")
112
+ _date = val;
113
+ else if (key == "Radius")
114
+ _a = Utility::num<real>(val);
115
+ else if (key == "Type") {
116
+ if (!(val == "Linear" || val == "linear"))
117
+ throw GeographicErr("Only linear models are supported");
118
+ } else if (key == "Epoch")
119
+ _t0 = Utility::num<real>(val);
120
+ else if (key == "DeltaEpoch")
121
+ _dt0 = Utility::num<real>(val);
122
+ else if (key == "NumModels")
123
+ _Nmodels = Utility::num<int>(val);
124
+ else if (key == "NumConstants")
125
+ _Nconstants = Utility::num<int>(val);
126
+ else if (key == "MinTime")
127
+ _tmin = Utility::num<real>(val);
128
+ else if (key == "MaxTime")
129
+ _tmax = Utility::num<real>(val);
130
+ else if (key == "MinHeight")
131
+ _hmin = Utility::num<real>(val);
132
+ else if (key == "MaxHeight")
133
+ _hmax = Utility::num<real>(val);
134
+ else if (key == "Normalization") {
135
+ if (val == "FULL" || val == "Full" || val == "full")
136
+ _norm = SphericalHarmonic::FULL;
137
+ else if (val == "SCHMIDT" || val == "Schmidt" || val == "schmidt")
138
+ _norm = SphericalHarmonic::SCHMIDT;
139
+ else
140
+ throw GeographicErr("Unknown normalization " + val);
141
+ } else if (key == "ByteOrder") {
142
+ if (val == "Big" || val == "big")
143
+ throw GeographicErr("Only little-endian ordering is supported");
144
+ else if (!(val == "Little" || val == "little"))
145
+ throw GeographicErr("Unknown byte ordering " + val);
146
+ } else if (key == "ID")
147
+ _id = val;
148
+ // else unrecognized keywords are skipped
149
+ }
150
+ // Check values
151
+ if (!(Math::isfinite(_a) && _a > 0))
152
+ throw GeographicErr("Reference radius must be positive");
153
+ if (!(_t0 > 0))
154
+ throw GeographicErr("Epoch time not defined");
155
+ if (_tmin >= _tmax)
156
+ throw GeographicErr("Min time exceeds max time");
157
+ if (_hmin >= _hmax)
158
+ throw GeographicErr("Min height exceeds max height");
159
+ if (int(_id.size()) != idlength_)
160
+ throw GeographicErr("Invalid ID");
161
+ if (_Nmodels < 1)
162
+ throw GeographicErr("NumModels must be positive");
163
+ if (!(_Nconstants == 0 || _Nconstants == 1))
164
+ throw GeographicErr("NumConstants must be 0 or 1");
165
+ if (!(_dt0 > 0)) {
166
+ if (_Nmodels > 1)
167
+ throw GeographicErr("DeltaEpoch must be positive");
168
+ else
169
+ _dt0 = 1;
170
+ }
171
+ }
172
+
173
+ void MagneticModel::Field(real t, real lat, real lon, real h, bool diffp,
174
+ real& Bx, real& By, real& Bz,
175
+ real& Bxt, real& Byt, real& Bzt) const {
176
+ t -= _t0;
177
+ int n = max(min(int(floor(t / _dt0)), _Nmodels - 1), 0);
178
+ bool interpolate = n + 1 < _Nmodels;
179
+ t -= n * _dt0;
180
+ real X, Y, Z;
181
+ real M[Geocentric::dim2_];
182
+ _earth.IntForward(lat, lon, h, X, Y, Z, M);
183
+ // Components in geocentric basis
184
+ // initial values to suppress warning
185
+ real BX0 = 0, BY0 = 0, BZ0 = 0, BX1 = 0, BY1 = 0, BZ1 = 0;
186
+ real BXc = 0, BYc = 0, BZc = 0;
187
+ _harm[n](X, Y, Z, BX0, BY0, BZ0);
188
+ _harm[n + 1](X, Y, Z, BX1, BY1, BZ1);
189
+ if (_Nconstants)
190
+ _harm[_Nmodels + 1](X, Y, Z, BXc, BYc, BZc);
191
+ if (interpolate) {
192
+ // Convert to a time derivative
193
+ BX1 = (BX1 - BX0) / _dt0;
194
+ BY1 = (BY1 - BY0) / _dt0;
195
+ BZ1 = (BZ1 - BZ0) / _dt0;
196
+ }
197
+ BX0 += t * BX1 + BXc;
198
+ BY0 += t * BY1 + BYc;
199
+ BZ0 += t * BZ1 + BZc;
200
+ if (diffp) {
201
+ Geocentric::Unrotate(M, BX1, BY1, BZ1, Bxt, Byt, Bzt);
202
+ Bxt *= - _a;
203
+ Byt *= - _a;
204
+ Bzt *= - _a;
205
+ }
206
+ Geocentric::Unrotate(M, BX0, BY0, BZ0, Bx, By, Bz);
207
+ Bx *= - _a;
208
+ By *= - _a;
209
+ Bz *= - _a;
210
+ }
211
+
212
+ MagneticCircle MagneticModel::Circle(real t, real lat, real h) const {
213
+ real t1 = t - _t0;
214
+ int n = max(min(int(floor(t1 / _dt0)), _Nmodels - 1), 0);
215
+ bool interpolate = n + 1 < _Nmodels;
216
+ t1 -= n * _dt0;
217
+ real X, Y, Z, M[Geocentric::dim2_];
218
+ _earth.IntForward(lat, 0, h, X, Y, Z, M);
219
+ // Y = 0, cphi = M[7], sphi = M[8];
220
+
221
+ return (_Nconstants == 0 ?
222
+ MagneticCircle(_a, _earth._f, lat, h, t,
223
+ M[7], M[8], t1, _dt0, interpolate,
224
+ _harm[n].Circle(X, Z, true),
225
+ _harm[n + 1].Circle(X, Z, true)) :
226
+ MagneticCircle(_a, _earth._f, lat, h, t,
227
+ M[7], M[8], t1, _dt0, interpolate,
228
+ _harm[n].Circle(X, Z, true),
229
+ _harm[n + 1].Circle(X, Z, true),
230
+ _harm[_Nmodels + 1].Circle(X, Z, true)));
231
+ }
232
+
233
+ void MagneticModel::FieldComponents(real Bx, real By, real Bz,
234
+ real Bxt, real Byt, real Bzt,
235
+ real& H, real& F, real& D, real& I,
236
+ real& Ht, real& Ft,
237
+ real& Dt, real& It) {
238
+ H = Math::hypot(Bx, By);
239
+ Ht = H ? (Bx * Bxt + By * Byt) / H : Math::hypot(Bxt, Byt);
240
+ D = H ? Math::atan2d(Bx, By) : Math::atan2d(Bxt, Byt);
241
+ Dt = (H ? (By * Bxt - Bx * Byt) / Math::sq(H) : 0) / Math::degree();
242
+ F = Math::hypot(H, Bz);
243
+ Ft = F ? (H * Ht + Bz * Bzt) / F : Math::hypot(Ht, Bzt);
244
+ I = F ? Math::atan2d(-Bz, H) : Math::atan2d(-Bzt, Ht);
245
+ It = (F ? (Bz * Ht - H * Bzt) / Math::sq(F) : 0) / Math::degree();
246
+ }
247
+
248
+ std::string MagneticModel::DefaultMagneticPath() {
249
+ string path;
250
+ char* magneticpath = getenv("GEOGRAPHICLIB_MAGNETIC_PATH");
251
+ if (magneticpath)
252
+ path = string(magneticpath);
253
+ if (!path.empty())
254
+ return path;
255
+ char* datapath = getenv("GEOGRAPHICLIB_DATA");
256
+ if (datapath)
257
+ path = string(datapath);
258
+ return (!path.empty() ? path : string(GEOGRAPHICLIB_DATA)) + "/magnetic";
259
+ }
260
+
261
+ std::string MagneticModel::DefaultMagneticName() {
262
+ string name;
263
+ char* magneticname = getenv("GEOGRAPHICLIB_MAGNETIC_NAME");
264
+ if (magneticname)
265
+ name = string(magneticname);
266
+ return !name.empty() ? name : string(GEOGRAPHICLIB_MAGNETIC_DEFAULT_NAME);
267
+ }
268
+
269
+ } // namespace GeographicLib
@@ -0,0 +1,63 @@
1
+ /**
2
+ * \file Math.cpp
3
+ * \brief Implementation for GeographicLib::Math class
4
+ *
5
+ * Copyright (c) Charles Karney (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/Math.hpp>
11
+
12
+ #if defined(_MSC_VER)
13
+ // Squelch warnings about constant conditional expressions
14
+ # pragma warning (disable: 4127)
15
+ #endif
16
+
17
+ namespace GeographicLib {
18
+
19
+ using namespace std;
20
+
21
+ template<typename T> T Math::eatanhe(T x, T es) {
22
+ return es > T(0) ? es * atanh(es * x) : -es * atan(es * x);
23
+ }
24
+
25
+ template<typename T> T Math::taupf(T tau, T es) {
26
+ T tau1 = hypot(T(1), tau),
27
+ sig = sinh( eatanhe(tau / tau1, es ) );
28
+ return hypot(T(1), sig) * tau - sig * tau1;
29
+ }
30
+
31
+ template<typename T> T Math::tauf(T taup, T es) {
32
+ static const int numit = 5;
33
+ static const T tol = sqrt(numeric_limits<T>::epsilon()) / T(10);
34
+ T e2m = T(1) - sq(es),
35
+ // To lowest order in e^2, taup = (1 - e^2) * tau = _e2m * tau; so use
36
+ // tau = taup/_e2m as a starting guess. (This starting guess is the
37
+ // geocentric latitude which, to first order in the flattening, is equal
38
+ // to the conformal latitude.) Only 1 iteration is needed for |lat| <
39
+ // 3.35 deg, otherwise 2 iterations are needed. If, instead, tau = taup
40
+ // is used the mean number of iterations increases to 1.99 (2 iterations
41
+ // are needed except near tau = 0).
42
+ tau = taup/e2m,
43
+ stol = tol * max(T(1), abs(taup));
44
+ // min iterations = 1, max iterations = 2; mean = 1.94
45
+ for (int i = 0; i < numit || GEOGRAPHICLIB_PANIC; ++i) {
46
+ T taupa = taupf(tau, es),
47
+ dtau = (taup - taupa) * (1 + e2m * sq(tau)) /
48
+ ( e2m * hypot(T(1), tau) * hypot(T(1), taupa) );
49
+ tau += dtau;
50
+ if (!(abs(dtau) >= stol))
51
+ break;
52
+ }
53
+ return tau;
54
+ }
55
+
56
+ /// \cond SKIP
57
+ // Instantiate
58
+ template Math::real Math::eatanhe<Math::real>(Math::real, Math::real);
59
+ template Math::real Math::taupf<Math::real>(Math::real, Math::real);
60
+ template Math::real Math::tauf<Math::real>(Math::real, Math::real);
61
+ /// \endcond
62
+
63
+ } // namespace GeographicLib
@@ -0,0 +1,262 @@
1
+ /**
2
+ * \file NormalGravity.cpp
3
+ * \brief Implementation for GeographicLib::NormalGravity 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/NormalGravity.hpp>
11
+
12
+ #if defined(_MSC_VER)
13
+ // Squelch warnings about constant conditional expressions
14
+ # pragma warning (disable: 4127)
15
+ #endif
16
+
17
+ namespace GeographicLib {
18
+
19
+ using namespace std;
20
+
21
+ NormalGravity::NormalGravity(real a, real GM, real omega, real f, real J2)
22
+ : _a(a)
23
+ , _GM(GM)
24
+ , _omega(omega)
25
+ , _f(f)
26
+ , _J2(J2)
27
+ , _omega2(Math::sq(_omega))
28
+ , _aomega2(Math::sq(_omega * _a))
29
+ {
30
+ if (!(Math::isfinite(_a) && _a > 0))
31
+ throw GeographicErr("Major radius is not positive");
32
+ if (!(Math::isfinite(_GM) && _GM > 0))
33
+ throw GeographicErr("Gravitational constant is not positive");
34
+ if (!(_omega == 0 && _f == 0 && _J2 == 0)) {
35
+ bool flatp = _f > 0 && Math::isfinite(_f);
36
+ if (_J2 > 0 && Math::isfinite(_J2) && flatp)
37
+ throw GeographicErr("Cannot specify both f and J2");
38
+ if (!(_J2 > 0 && Math::isfinite(_J2)) && !flatp)
39
+ throw GeographicErr("Must specify one of f and J2");
40
+ if (!(Math::isfinite(_omega) && _omega != 0))
41
+ throw GeographicErr("Angular velocity is not non-zero");
42
+ if (flatp)
43
+ _J2 = FlatteningToJ2(_a, _GM, _omega, _f);
44
+ else
45
+ _f = J2ToFlattening(_a, _GM, _omega, _J2);
46
+ } // else we have a sphere: omega = f = J2 = 0
47
+ _e2 = _f * (2 - _f);
48
+ _ep2 = _e2 / (1 - _e2);
49
+ _q0 = qf(_ep2);
50
+ _earth = Geocentric(_a, _f);
51
+ _b = _a * (1 - _f);
52
+ _E = _a * sqrt(_e2); // H+M, Eq 2-54
53
+ // H+M, Eq 2-61
54
+ _U0 = _GM / (_E ? _E / atan(sqrt(_ep2)) : _b) + _aomega2 / 3;
55
+ // The approximate ratio of the centrifugal acceleration (at the equator)
56
+ // to gravity.
57
+ _m = _aomega2 * _b / _GM; // H+M, Eq 2-70
58
+ real
59
+ Q = _m * (_q0 ? sqrt(_ep2) * qpf(_ep2) / (3 * _q0) : 1),
60
+ G = (1 - _m - Q / 2);
61
+ _gammae = _GM / (_a * _b) * G; // H+M, Eq 2-73
62
+ _gammap = _GM / (_a * _a) * (1 + Q); // H+M, Eq 2-74
63
+ // k = b * gammap / (a * gammae) - 1
64
+ _k = (_m + 3 * Q / 2 - _e2 * (1 + Q)) / G;
65
+ // f* = (gammap - gammae) / gammae
66
+ _fstar = (_m + 3 * Q / 2 - _f * (1 + Q)) / G;
67
+ }
68
+
69
+ const NormalGravity& NormalGravity::WGS84() {
70
+ static const NormalGravity wgs84(Constants::WGS84_a(),
71
+ Constants::WGS84_GM(),
72
+ Constants::WGS84_omega(),
73
+ Constants::WGS84_f(), 0);
74
+ return wgs84;
75
+ }
76
+
77
+ const NormalGravity& NormalGravity::GRS80() {
78
+ static const NormalGravity grs80(Constants::GRS80_a(),
79
+ Constants::GRS80_GM(),
80
+ Constants::GRS80_omega(),
81
+ 0, Constants::GRS80_J2());
82
+ return grs80;
83
+ }
84
+
85
+ // (atan(y)-(y-y^3/3+y^5/5))/y^7 (y = sqrt(x)) = -1/7+x/9-x^2/11+x^3/13...
86
+ Math::real NormalGravity::atan7(real x) {
87
+ if (abs(x) >= real(0.5)) {
88
+ real y = sqrt(abs(x)), x2 = Math::sq(x);
89
+ return ((x > 0 ? atan(y) : Math::atanh(y))- y * (1 - x / 3 + x2 / 5)) /
90
+ (x * x2 * y);
91
+ } else {
92
+ real xn = -1, q = 0;
93
+ for (int n = 7; ; n += 2) {
94
+ real qn = q + xn / n;
95
+ if (qn == q)
96
+ break;
97
+ q = qn;
98
+ xn *= -x;
99
+ }
100
+ return q;
101
+ }
102
+ }
103
+
104
+ // (atan(y)-(y-y^3/3))/y^5 (y = sqrt(x)) = 1/5-x/7+x^2/9-x^3/11...
105
+ Math::real NormalGravity::atan5(real x)
106
+ { return 1/real(5) + x * atan7(x); }
107
+
108
+ Math::real NormalGravity::qf(real ep2) {
109
+ // Compute
110
+ //
111
+ // ((1 + 3/e'^2) * atan(e') - 3/e')/2
112
+ //
113
+ // See H+M, Eq 2-57, with E/u = e'. This suffers from two levels of
114
+ // cancelation. The e'^-1 and e'^1 terms drop out, so that the leading
115
+ // term is O(e'^3). Substitute atan(e') = e' - e'^3/3 + e'^5*atan5(e'^2)
116
+ return sqrt(ep2) * ep2 * (3 * (3 + ep2) * atan5(ep2) - 1) / 6;
117
+ }
118
+
119
+ Math::real NormalGravity::dq(real ep2) {
120
+ // Compute d qf(ep2) / d ep2 and substitute
121
+ // atan(e') = e' - e'^3/3 + e'^5/5 + e'^7*atan7(e'^2)
122
+ return sqrt(ep2) * (5 - 3 * (1 + ep2) * (1 + 5 * ep2 * atan7(ep2))) /
123
+ (10 * (1 + ep2));
124
+ }
125
+
126
+ Math::real NormalGravity::qpf(real ep2) {
127
+ // Compute
128
+ //
129
+ // 3*(1 + 1/e'^2) * (1 - atan(e')/e') - 1
130
+ //
131
+ // See H+M, Eq 2-67, with E/u = e'. This suffers from two levels of
132
+ // cancelation. The e'^-2 and e'^0 terms drop out, so that the leading
133
+ // term is O(e'^2).
134
+ return ep2 * (1 - 3 * (1 + ep2) * atan5(ep2));
135
+ }
136
+
137
+ Math::real NormalGravity::Jn(int n) const {
138
+ // Note Jn(0) = -1; Jn(2) = _J2; Jn(odd) = 0
139
+ if (n & 1 || n < 0)
140
+ return 0;
141
+ n /= 2;
142
+ real e2n = 1; // Perhaps this should just be e2n = pow(-_e2, n);
143
+ for (int j = n; j--;)
144
+ e2n *= -_e2;
145
+ return // H+M, Eq 2-92
146
+ -3 * e2n * ((1 - n) + 5 * n * _J2 / _e2) / ((2 * n + 1) * (2 * n + 3));
147
+ }
148
+
149
+ Math::real NormalGravity::SurfaceGravity(real lat) const {
150
+ real sphi = Math::sind(Math::LatFix(lat));
151
+ // H+M, Eq 2-78
152
+ return _gammae * (1 + _k * Math::sq(sphi)) / sqrt(1 - _e2 * Math::sq(sphi));
153
+ }
154
+
155
+ Math::real NormalGravity::V0(real X, real Y, real Z,
156
+ real& GammaX, real& GammaY, real& GammaZ)
157
+ const {
158
+ // See H+M, Sec 6-2
159
+ real
160
+ p = Math::hypot(X, Y),
161
+ clam = p ? X/p : 1,
162
+ slam = p ? Y/p : 0,
163
+ r = Math::hypot(p, Z),
164
+ Q = Math::sq(r) - Math::sq(_E),
165
+ t2 = Math::sq(2 * _E * Z),
166
+ disc = sqrt(Math::sq(Q) + t2),
167
+ // This is H+M, Eq 6-8a, but generalized to deal with Q negative
168
+ // accurately.
169
+ u = sqrt((Q >= 0 ? (Q + disc) : t2 / (disc - Q)) / 2),
170
+ uE = Math::hypot(u, _E),
171
+ // H+M, Eq 6-8b
172
+ sbet = Z * uE,
173
+ cbet = p * u,
174
+ s = Math::hypot(cbet, sbet);
175
+ cbet = s ? cbet/s : 0;
176
+ sbet = s ? sbet/s : 1;
177
+ real
178
+ invw = uE / Math::hypot(u, _E * sbet), // H+M, Eq 2-63
179
+ ep = _E/u,
180
+ ep2 = Math::sq(ep),
181
+ q = _q0 ? qf(ep2) / _q0 : pow(_a / u, 3),
182
+ qp = qpf(ep2) / _q0,
183
+ // H+M, Eqs 2-62 + 6-9, but omitting last (rotational) term .
184
+ Vres = (_GM / (_E ? _E / atan(_E / u) : u)
185
+ + _aomega2 * q * (Math::sq(sbet) - 1/real(3)) / 2),
186
+ // H+M, Eq 6-10
187
+ gamu = - invw * (_GM
188
+ + (_aomega2 * (_q0 ? _E * qp : 3 * q * u)
189
+ * (Math::sq(sbet) - 1/real(3)) / 2)) / Math::sq(uE),
190
+ gamb = _aomega2 * q * sbet * cbet * invw / uE,
191
+ t = u * invw / uE;
192
+ // H+M, Eq 6-12
193
+ GammaX = t * cbet * gamu - invw * sbet * gamb;
194
+ GammaY = GammaX * slam;
195
+ GammaX *= clam;
196
+ GammaZ = invw * sbet * gamu + t * cbet * gamb;
197
+ return Vres;
198
+ }
199
+
200
+ Math::real NormalGravity::Phi(real X, real Y, real& fX, real& fY)
201
+ const {
202
+ fX = _omega2 * X;
203
+ fY = _omega2 * Y;
204
+ // N.B. fZ = 0;
205
+ return _omega2 * (Math::sq(X) + Math::sq(Y)) / 2;
206
+ }
207
+
208
+ Math::real NormalGravity::U(real X, real Y, real Z,
209
+ real& gammaX, real& gammaY, real& gammaZ)
210
+ const {
211
+ real fX, fY;
212
+ real Ures = V0(X, Y, Z, gammaX, gammaY, gammaZ) + Phi(X, Y, fX, fY);
213
+ gammaX += fX;
214
+ gammaY += fY;
215
+ return Ures;
216
+ }
217
+
218
+ Math::real NormalGravity::Gravity(real lat, real h,
219
+ real& gammay, real& gammaz)
220
+ const {
221
+ real X, Y, Z;
222
+ real M[Geocentric::dim2_];
223
+ _earth.IntForward(lat, 0, h, X, Y, Z, M);
224
+ real gammaX, gammaY, gammaZ,
225
+ Ures = U(X, Y, Z, gammaX, gammaY, gammaZ);
226
+ // gammax = M[0] * gammaX + M[3] * gammaY + M[6] * gammaZ;
227
+ gammay = M[1] * gammaX + M[4] * gammaY + M[7] * gammaZ;
228
+ gammaz = M[2] * gammaX + M[5] * gammaY + M[8] * gammaZ;
229
+ return Ures;
230
+ }
231
+
232
+ Math::real NormalGravity::J2ToFlattening(real a, real GM,
233
+ real omega, real J2) {
234
+ real
235
+ K = 2 * Math::sq(a * omega) * a / (15 * GM),
236
+ e2 = 3 * J2; // See Moritz (1980), p 398.
237
+ // Solve using Newton's method
238
+ for (int j = 0; j < maxit_ || GEOGRAPHICLIB_PANIC; ++j) {
239
+ real e2a = e2,
240
+ ep2 = e2 / (1 - e2),
241
+ q0 = qf(ep2),
242
+ dq0 = dq(ep2) / Math::sq(1 - e2),
243
+ h = e2 * (1 - sqrt(e2) * K / q0) - 3 * J2,
244
+ dh = 1 - sqrt(e2) * K * (3 * q0 - 2 * e2 * dq0) / (2 * Math::sq(q0)),
245
+ de2 = - h / dh;
246
+ e2 = e2a + de2;
247
+ if (e2 == e2a)
248
+ break;
249
+ }
250
+ return e2 / (1 + sqrt(1 - e2));
251
+ }
252
+
253
+ Math::real NormalGravity::FlatteningToJ2(real a, real GM,
254
+ real omega, real f) {
255
+ real
256
+ K = 2 * Math::sq(a * omega) * a / (15 * GM),
257
+ e2 = f * (2 - f),
258
+ q0 = qf(e2 / (1 - e2));
259
+ return e2 * (1 - K * sqrt(e2) / q0) / 3; // H+M, Eq 2-90
260
+ }
261
+
262
+ } // namespace GeographicLib