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,102 @@
1
+ /**
2
+ * \file Geohash.cpp
3
+ * \brief Implementation for GeographicLib::Geohash class
4
+ *
5
+ * Copyright (c) Charles Karney (2012-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/Geohash.hpp>
11
+ #include <GeographicLib/Utility.hpp>
12
+
13
+ namespace GeographicLib {
14
+
15
+ using namespace std;
16
+
17
+ const string Geohash::lcdigits_ = "0123456789bcdefghjkmnpqrstuvwxyz";
18
+ const string Geohash::ucdigits_ = "0123456789BCDEFGHJKMNPQRSTUVWXYZ";
19
+
20
+ void Geohash::Forward(real lat, real lon, int len, std::string& geohash) {
21
+ static const real shift = pow(real(2), 45);
22
+ static const real loneps = 180 / shift;
23
+ static const real lateps = 90 / shift;
24
+ if (abs(lat) > 90)
25
+ throw GeographicErr("Latitude " + Utility::str(lat)
26
+ + "d not in [-90d, 90d]");
27
+ if (Math::isnan(lat) || Math::isnan(lon)) {
28
+ geohash = "invalid";
29
+ return;
30
+ }
31
+ if (lat == 90) lat -= lateps / 2;
32
+ lon = Math::AngNormalize(lon); // lon in [-180,180)
33
+ // lon/loneps in [-2^45,2^45); lon/loneps + shift in [0,2^46)
34
+ // similarly for lat
35
+ len = max(0, min(int(maxlen_), len));
36
+ unsigned long long
37
+ ulon = (unsigned long long)(floor(lon/loneps) + shift),
38
+ ulat = (unsigned long long)(floor(lat/lateps) + shift);
39
+ char geohash1[maxlen_];
40
+ unsigned byte = 0;
41
+ for (unsigned i = 0; i < 5 * unsigned(len);) {
42
+ if ((i & 1) == 0) {
43
+ byte = (byte << 1) + unsigned((ulon & mask_) != 0);
44
+ ulon <<= 1;
45
+ } else {
46
+ byte = (byte << 1) + unsigned((ulat & mask_) != 0);
47
+ ulat <<= 1;
48
+ }
49
+ ++i;
50
+ if (i % 5 == 0) {
51
+ geohash1[(i/5)-1] = lcdigits_[byte];
52
+ byte = 0;
53
+ }
54
+ }
55
+ geohash.resize(len);
56
+ copy(geohash1, geohash1 + len, geohash.begin());
57
+ }
58
+
59
+ void Geohash::Reverse(const std::string& geohash, real& lat, real& lon,
60
+ int& len, bool centerp) {
61
+ static const real shift = pow(real(2), 45);
62
+ static const real loneps = 180 / shift;
63
+ static const real lateps = 90 / shift;
64
+ int len1 = min(int(maxlen_), int(geohash.length()));
65
+ if (len1 >= 3 &&
66
+ ((toupper(geohash[0]) == 'I' &&
67
+ toupper(geohash[1]) == 'N' &&
68
+ toupper(geohash[2]) == 'V') ||
69
+ // Check A first because it is not in a standard geohash
70
+ (toupper(geohash[1]) == 'A' &&
71
+ toupper(geohash[0]) == 'N' &&
72
+ toupper(geohash[2]) == 'N'))) {
73
+ lat = lon = Math::NaN();
74
+ return;
75
+ }
76
+ unsigned long long ulon = 0, ulat = 0;
77
+ for (unsigned k = 0, j = 0; k < unsigned(len1); ++k) {
78
+ int byte = Utility::lookup(ucdigits_, geohash[k]);
79
+ if (byte < 0)
80
+ throw GeographicErr("Illegal character in geohash " + geohash);
81
+ for (unsigned m = 16; m; m >>= 1) {
82
+ if (j == 0)
83
+ ulon = (ulon << 1) + unsigned((byte & m) != 0);
84
+ else
85
+ ulat = (ulat << 1) + unsigned((byte & m) != 0);
86
+ j ^= 1;
87
+ }
88
+ }
89
+ ulon <<= 1; ulat <<= 1;
90
+ if (centerp) {
91
+ ulon += 1;
92
+ ulat += 1;
93
+ }
94
+ int s = 5 * (maxlen_ - len1);
95
+ ulon <<= (s / 2);
96
+ ulat <<= s - (s / 2);
97
+ lon = ulon * loneps - 180;
98
+ lat = ulat * lateps - 90;
99
+ len = len1;
100
+ }
101
+
102
+ } // namespace GeographicLib
@@ -0,0 +1,509 @@
1
+ /**
2
+ * \file Geoid.cpp
3
+ * \brief Implementation for GeographicLib::Geoid class
4
+ *
5
+ * Copyright (c) Charles Karney (2009-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/Geoid.hpp>
11
+ // For getenv
12
+ #include <cstdlib>
13
+ #include <GeographicLib/Utility.hpp>
14
+
15
+ #if !defined(GEOGRAPHICLIB_DATA)
16
+ # if defined(_WIN32)
17
+ # define GEOGRAPHICLIB_DATA "C:/ProgramData/GeographicLib"
18
+ # else
19
+ # define GEOGRAPHICLIB_DATA "/usr/local/share/GeographicLib"
20
+ # endif
21
+ #endif
22
+
23
+ #if !defined(GEOGRAPHICLIB_GEOID_DEFAULT_NAME)
24
+ # define GEOGRAPHICLIB_GEOID_DEFAULT_NAME "egm96-5"
25
+ #endif
26
+
27
+ #if defined(_MSC_VER)
28
+ // Squelch warnings about unsafe use of getenv
29
+ # pragma warning (disable: 4996)
30
+ #endif
31
+
32
+ namespace GeographicLib {
33
+
34
+ using namespace std;
35
+
36
+ // This is the transfer matrix for a 3rd order fit with a 12-point stencil
37
+ // with weights
38
+ //
39
+ // \x -1 0 1 2
40
+ // y
41
+ // -1 . 1 1 .
42
+ // 0 1 2 2 1
43
+ // 1 1 2 2 1
44
+ // 2 . 1 1 .
45
+ //
46
+ // A algorithm for n-dimensional polynomial fits is described in
47
+ // F. H. Lesh,
48
+ // Multi-dimensional least-squares polynomial curve fitting,
49
+ // CACM 2, 29-30 (1959).
50
+ //
51
+ // Here's the Maxima code to generate this matrix:
52
+ //
53
+ // /* The stencil and the weights */
54
+ // xarr:[
55
+ // 0, 1,
56
+ // -1, 0, 1, 2,
57
+ // -1, 0, 1, 2,
58
+ // 0, 1]$
59
+ // yarr:[
60
+ // -1,-1,
61
+ // 0, 0, 0, 0,
62
+ // 1, 1, 1, 1,
63
+ // 2, 2]$
64
+ // warr:[
65
+ // 1, 1,
66
+ // 1, 2, 2, 1,
67
+ // 1, 2, 2, 1,
68
+ // 1, 1]$
69
+ //
70
+ // /* [x exponent, y exponent] for cubic fit */
71
+ // pows:[
72
+ // [0,0],
73
+ // [1,0],[0,1],
74
+ // [2,0],[1,1],[0,2],
75
+ // [3,0],[2,1],[1,2],[0,3]]$
76
+ //
77
+ // basisvec(x,y,pows):=map(lambda([ex],(if ex[1]=0 then 1 else x^ex[1])*
78
+ // (if ex[2]=0 then 1 else y^ex[2])),pows)$
79
+ // addterm(x,y,f,w,pows):=block([a,b,bb:basisvec(x,y,pows)],
80
+ // a:w*(transpose(bb).bb),
81
+ // b:(w*f) * bb,
82
+ // [a,b])$
83
+ //
84
+ // c3row(k):=block([a,b,c,pows:pows,n],
85
+ // n:length(pows),
86
+ // a:zeromatrix(n,n),
87
+ // b:copylist(part(a,1)),
88
+ // c:[a,b],
89
+ // for i:1 thru length(xarr) do
90
+ // c:c+addterm(xarr[i],yarr[i],if i=k then 1 else 0,warr[i],pows),
91
+ // a:c[1],b:c[2],
92
+ // part(transpose( a^^-1 . transpose(b)),1))$
93
+ // c3:[]$
94
+ // for k:1 thru length(warr) do c3:endcons(c3row(k),c3)$
95
+ // c3:apply(matrix,c3)$
96
+ // c0:part(ratsimp(
97
+ // genmatrix(yc,1,length(warr)).abs(c3).genmatrix(yd,length(pows),1)),2)$
98
+ // c3:c0*c3$
99
+
100
+ const int Geoid::c0_ = 240; // Common denominator
101
+ const int Geoid::c3_[stencilsize_ * nterms_] = {
102
+ 9, -18, -88, 0, 96, 90, 0, 0, -60, -20,
103
+ -9, 18, 8, 0, -96, 30, 0, 0, 60, -20,
104
+ 9, -88, -18, 90, 96, 0, -20, -60, 0, 0,
105
+ 186, -42, -42, -150, -96, -150, 60, 60, 60, 60,
106
+ 54, 162, -78, 30, -24, -90, -60, 60, -60, 60,
107
+ -9, -32, 18, 30, 24, 0, 20, -60, 0, 0,
108
+ -9, 8, 18, 30, -96, 0, -20, 60, 0, 0,
109
+ 54, -78, 162, -90, -24, 30, 60, -60, 60, -60,
110
+ -54, 78, 78, 90, 144, 90, -60, -60, -60, -60,
111
+ 9, -8, -18, -30, -24, 0, 20, 60, 0, 0,
112
+ -9, 18, -32, 0, 24, 30, 0, 0, -60, 20,
113
+ 9, -18, -8, 0, -24, -30, 0, 0, 60, 20,
114
+ };
115
+
116
+ // Like c3, but with the coeffs of x, x^2, and x^3 constrained to be zero.
117
+ // Use this at the N pole so that the height in independent of the longitude
118
+ // there.
119
+ //
120
+ // Here's the Maxima code to generate this matrix (continued from above).
121
+ //
122
+ // /* figure which terms to exclude so that fit is indep of x at y=0 */
123
+ // mask:part(zeromatrix(1,length(pows)),1)+1$
124
+ // for i:1 thru length(pows) do
125
+ // if pows[i][1]>0 and pows[i][2]=0 then mask[i]:0$
126
+ //
127
+ // /* Same as c3row but with masked pows. */
128
+ // c3nrow(k):=block([a,b,c,powsa:[],n,d,e],
129
+ // for i:1 thru length(mask) do if mask[i]>0 then
130
+ // powsa:endcons(pows[i],powsa),
131
+ // n:length(powsa),
132
+ // a:zeromatrix(n,n),
133
+ // b:copylist(part(a,1)),
134
+ // c:[a,b],
135
+ // for i:1 thru length(xarr) do
136
+ // c:c+addterm(xarr[i],yarr[i],if i=k then 1 else 0,warr[i],powsa),
137
+ // a:c[1],b:c[2],
138
+ // d:part(transpose( a^^-1 . transpose(b)),1),
139
+ // e:[],
140
+ // for i:1 thru length(mask) do
141
+ // if mask[i]>0 then (e:endcons(first(d),e),d:rest(d)) else e:endcons(0,e),
142
+ // e)$
143
+ // c3n:[]$
144
+ // for k:1 thru length(warr) do c3n:endcons(c3nrow(k),c3n)$
145
+ // c3n:apply(matrix,c3n)$
146
+ // c0n:part(ratsimp(
147
+ // genmatrix(yc,1,length(warr)).abs(c3n).genmatrix(yd,length(pows),1)),2)$
148
+ // c3n:c0n*c3n$
149
+
150
+ const int Geoid::c0n_ = 372; // Common denominator
151
+ const int Geoid::c3n_[stencilsize_ * nterms_] = {
152
+ 0, 0, -131, 0, 138, 144, 0, 0, -102, -31,
153
+ 0, 0, 7, 0, -138, 42, 0, 0, 102, -31,
154
+ 62, 0, -31, 0, 0, -62, 0, 0, 0, 31,
155
+ 124, 0, -62, 0, 0, -124, 0, 0, 0, 62,
156
+ 124, 0, -62, 0, 0, -124, 0, 0, 0, 62,
157
+ 62, 0, -31, 0, 0, -62, 0, 0, 0, 31,
158
+ 0, 0, 45, 0, -183, -9, 0, 93, 18, 0,
159
+ 0, 0, 216, 0, 33, 87, 0, -93, 12, -93,
160
+ 0, 0, 156, 0, 153, 99, 0, -93, -12, -93,
161
+ 0, 0, -45, 0, -3, 9, 0, 93, -18, 0,
162
+ 0, 0, -55, 0, 48, 42, 0, 0, -84, 31,
163
+ 0, 0, -7, 0, -48, -42, 0, 0, 84, 31,
164
+ };
165
+
166
+ // Like c3n, but y -> 1-y so that h is independent of x at y = 1. Use this
167
+ // at the S pole so that the height in independent of the longitude there.
168
+ //
169
+ // Here's the Maxima code to generate this matrix (continued from above).
170
+ //
171
+ // /* Transform c3n to c3s by transforming y -> 1-y */
172
+ // vv:[
173
+ // v[11],v[12],
174
+ // v[7],v[8],v[9],v[10],
175
+ // v[3],v[4],v[5],v[6],
176
+ // v[1],v[2]]$
177
+ // poly:expand(vv.(c3n/c0n).transpose(basisvec(x,1-y,pows)))$
178
+ // c3sf[i,j]:=coeff(coeff(coeff(poly,v[i]),x,pows[j][1]),y,pows[j][2])$
179
+ // c3s:genmatrix(c3sf,length(vv),length(pows))$
180
+ // c0s:part(ratsimp(
181
+ // genmatrix(yc,1,length(warr)).abs(c3s).genmatrix(yd,length(pows),1)),2)$
182
+ // c3s:c0s*c3s$
183
+
184
+ const int Geoid::c0s_ = 372; // Common denominator
185
+ const int Geoid::c3s_[stencilsize_ * nterms_] = {
186
+ 18, -36, -122, 0, 120, 135, 0, 0, -84, -31,
187
+ -18, 36, -2, 0, -120, 51, 0, 0, 84, -31,
188
+ 36, -165, -27, 93, 147, -9, 0, -93, 18, 0,
189
+ 210, 45, -111, -93, -57, -192, 0, 93, 12, 93,
190
+ 162, 141, -75, -93, -129, -180, 0, 93, -12, 93,
191
+ -36, -21, 27, 93, 39, 9, 0, -93, -18, 0,
192
+ 0, 0, 62, 0, 0, 31, 0, 0, 0, -31,
193
+ 0, 0, 124, 0, 0, 62, 0, 0, 0, -62,
194
+ 0, 0, 124, 0, 0, 62, 0, 0, 0, -62,
195
+ 0, 0, 62, 0, 0, 31, 0, 0, 0, -31,
196
+ -18, 36, -64, 0, 66, 51, 0, 0, -102, 31,
197
+ 18, -36, 2, 0, -66, -51, 0, 0, 102, 31,
198
+ };
199
+
200
+ Geoid::Geoid(const std::string& name, const std::string& path, bool cubic,
201
+ bool threadsafe)
202
+ : _name(name)
203
+ , _dir(path)
204
+ , _cubic(cubic)
205
+ , _a( Constants::WGS84_a() )
206
+ , _e2( (2 - Constants::WGS84_f()) * Constants::WGS84_f() )
207
+ , _degree( Math::degree() )
208
+ , _eps( sqrt(numeric_limits<real>::epsilon()) )
209
+ , _threadsafe(false) // Set after cache is read
210
+ {
211
+ GEOGRAPHICLIB_STATIC_ASSERT(sizeof(pixel_t) == pixel_size_,
212
+ "pixel_t has the wrong size");
213
+ if (_dir.empty())
214
+ _dir = DefaultGeoidPath();
215
+ _filename = _dir + "/" + _name + (pixel_size_ != 4 ? ".pgm" : ".pgm4");
216
+ _file.open(_filename.c_str(), ios::binary);
217
+ if (!(_file.good()))
218
+ throw GeographicErr("File not readable " + _filename);
219
+ string s;
220
+ if (!(getline(_file, s) && s == "P5"))
221
+ throw GeographicErr("File not in PGM format " + _filename);
222
+ _offset = numeric_limits<real>::max();
223
+ _scale = 0;
224
+ _maxerror = _rmserror = -1;
225
+ _description = "NONE";
226
+ _datetime = "UNKNOWN";
227
+ while (getline(_file, s)) {
228
+ if (s.empty())
229
+ continue;
230
+ if (s[0] == '#') {
231
+ istringstream is(s);
232
+ string commentid, key;
233
+ if (!(is >> commentid >> key) || commentid != "#")
234
+ continue;
235
+ if (key == "Description" || key =="DateTime") {
236
+ string::size_type p =
237
+ s.find_first_not_of(" \t", unsigned(is.tellg()));
238
+ if (p != string::npos)
239
+ (key == "Description" ? _description : _datetime) = s.substr(p);
240
+ } else if (key == "Offset") {
241
+ if (!(is >> _offset))
242
+ throw GeographicErr("Error reading offset " + _filename);
243
+ } else if (key == "Scale") {
244
+ if (!(is >> _scale))
245
+ throw GeographicErr("Error reading scale " + _filename);
246
+ } else if (key == (_cubic ? "MaxCubicError" : "MaxBilinearError")) {
247
+ // It's not an error if the error can't be read
248
+ is >> _maxerror;
249
+ } else if (key == (_cubic ? "RMSCubicError" : "RMSBilinearError")) {
250
+ // It's not an error if the error can't be read
251
+ is >> _rmserror;
252
+ }
253
+ } else {
254
+ istringstream is(s);
255
+ if (!(is >> _width >> _height))
256
+ throw GeographicErr("Error reading raster size " + _filename);
257
+ break;
258
+ }
259
+ }
260
+ {
261
+ unsigned maxval;
262
+ if (!(_file >> maxval))
263
+ throw GeographicErr("Error reading maxval " + _filename);
264
+ if (maxval != pixel_max_)
265
+ throw GeographicErr("Incorrect value of maxval " + _filename);
266
+ // Add 1 for whitespace after maxval
267
+ _datastart = (unsigned long long)(_file.tellg()) + 1ULL;
268
+ _swidth = (unsigned long long)(_width);
269
+ }
270
+ if (_offset == numeric_limits<real>::max())
271
+ throw GeographicErr("Offset not set " + _filename);
272
+ if (_scale == 0)
273
+ throw GeographicErr("Scale not set " + _filename);
274
+ if (_scale < 0)
275
+ throw GeographicErr("Scale must be positive " + _filename);
276
+ if (_height < 2 || _width < 2)
277
+ // Coarsest grid spacing is 180deg.
278
+ throw GeographicErr("Raster size too small " + _filename);
279
+ if (_width & 1)
280
+ // This is so that longitude grids can be extended thru the poles.
281
+ throw GeographicErr("Raster width is odd " + _filename);
282
+ if (!(_height & 1))
283
+ // This is so that latitude grid includes the equator.
284
+ throw GeographicErr("Raster height is even " + _filename);
285
+ _file.seekg(0, ios::end);
286
+ if (!_file.good() ||
287
+ _datastart + pixel_size_ * _swidth * (unsigned long long)(_height) !=
288
+ (unsigned long long)(_file.tellg()))
289
+ // Possibly this test should be "<" because the file contains, e.g., a
290
+ // second image. However, for now we are more strict.
291
+ throw GeographicErr("File has the wrong length " + _filename);
292
+ _rlonres = _width / real(360);
293
+ _rlatres = (_height - 1) / real(180);
294
+ _cache = false;
295
+ _ix = _width;
296
+ _iy = _height;
297
+ // Ensure that file errors throw exceptions
298
+ _file.exceptions(ifstream::eofbit | ifstream::failbit | ifstream::badbit);
299
+ if (threadsafe) {
300
+ CacheAll();
301
+ _file.close();
302
+ _threadsafe = true;
303
+ }
304
+ }
305
+
306
+ Math::real Geoid::height(real lat, real lon) const {
307
+ lat = Math::LatFix(lat);
308
+ if (Math::isnan(lat) || Math::isnan(lon)) {
309
+ return Math::NaN();
310
+ }
311
+ lon = Math::AngNormalize(lon);
312
+ real
313
+ fx = lon * _rlonres,
314
+ fy = -lat * _rlatres;
315
+ int
316
+ ix = int(floor(fx)),
317
+ iy = min((_height - 1)/2 - 1, int(floor(fy)));
318
+ fx -= ix;
319
+ fy -= iy;
320
+ iy += (_height - 1)/2;
321
+ ix += ix < 0 ? _width : (ix >= _width ? -_width : 0);
322
+ real v00 = 0, v01 = 0, v10 = 0, v11 = 0;
323
+ real t[nterms_];
324
+
325
+ if (_threadsafe || !(ix == _ix && iy == _iy)) {
326
+ if (!_cubic) {
327
+ v00 = rawval(ix , iy );
328
+ v01 = rawval(ix + 1, iy );
329
+ v10 = rawval(ix , iy + 1);
330
+ v11 = rawval(ix + 1, iy + 1);
331
+ } else {
332
+ real v[stencilsize_];
333
+ int k = 0;
334
+ v[k++] = rawval(ix , iy - 1);
335
+ v[k++] = rawval(ix + 1, iy - 1);
336
+ v[k++] = rawval(ix - 1, iy );
337
+ v[k++] = rawval(ix , iy );
338
+ v[k++] = rawval(ix + 1, iy );
339
+ v[k++] = rawval(ix + 2, iy );
340
+ v[k++] = rawval(ix - 1, iy + 1);
341
+ v[k++] = rawval(ix , iy + 1);
342
+ v[k++] = rawval(ix + 1, iy + 1);
343
+ v[k++] = rawval(ix + 2, iy + 1);
344
+ v[k++] = rawval(ix , iy + 2);
345
+ v[k++] = rawval(ix + 1, iy + 2);
346
+
347
+ const int* c3x = iy == 0 ? c3n_ : (iy == _height - 2 ? c3s_ : c3_);
348
+ int c0x = iy == 0 ? c0n_ : (iy == _height - 2 ? c0s_ : c0_);
349
+ for (unsigned i = 0; i < nterms_; ++i) {
350
+ t[i] = 0;
351
+ for (unsigned j = 0; j < stencilsize_; ++j)
352
+ t[i] += v[j] * c3x[nterms_ * j + i];
353
+ t[i] /= c0x;
354
+ }
355
+ }
356
+ } else { // same cell; used cached coefficients
357
+ if (!_cubic) {
358
+ v00 = _v00;
359
+ v01 = _v01;
360
+ v10 = _v10;
361
+ v11 = _v11;
362
+ } else
363
+ copy(_t, _t + nterms_, t);
364
+ }
365
+ if (!_cubic) {
366
+ real
367
+ a = (1 - fx) * v00 + fx * v01,
368
+ b = (1 - fx) * v10 + fx * v11,
369
+ c = (1 - fy) * a + fy * b,
370
+ h = _offset + _scale * c;
371
+ if (!_threadsafe) {
372
+ _ix = ix;
373
+ _iy = iy;
374
+ _v00 = v00;
375
+ _v01 = v01;
376
+ _v10 = v10;
377
+ _v11 = v11;
378
+ }
379
+ return h;
380
+ } else {
381
+ real h = t[0] + fx * (t[1] + fx * (t[3] + fx * t[6])) +
382
+ fy * (t[2] + fx * (t[4] + fx * t[7]) +
383
+ fy * (t[5] + fx * t[8] + fy * t[9]));
384
+ h = _offset + _scale * h;
385
+ if (!_threadsafe) {
386
+ _ix = ix;
387
+ _iy = iy;
388
+ copy(t, t + nterms_, _t);
389
+ }
390
+ return h;
391
+ }
392
+ }
393
+
394
+ void Geoid::CacheClear() const {
395
+ if (!_threadsafe) {
396
+ _cache = false;
397
+ try {
398
+ _data.clear();
399
+ // Use swap to release memory back to system
400
+ vector< vector<pixel_t> >().swap(_data);
401
+ }
402
+ catch (const exception&) {
403
+ }
404
+ }
405
+ }
406
+
407
+ void Geoid::CacheArea(real south, real west, real north, real east) const {
408
+ if (_threadsafe)
409
+ throw GeographicErr("Attempt to change cache of threadsafe Geoid");
410
+ if (south > north) {
411
+ CacheClear();
412
+ return;
413
+ }
414
+ south = Math::LatFix(south);
415
+ north = Math::LatFix(north);
416
+ west = Math::AngNormalize(west); // west in [-180, 180)
417
+ east = Math::AngNormalize(east);
418
+ if (east <= west)
419
+ east += 360; // east - west in (0, 360]
420
+ int
421
+ iw = int(floor(west * _rlonres)),
422
+ ie = int(floor(east * _rlonres)),
423
+ in = int(floor(-north * _rlatres)) + (_height - 1)/2,
424
+ is = int(floor(-south * _rlatres)) + (_height - 1)/2;
425
+ in = max(0, min(_height - 2, in));
426
+ is = max(0, min(_height - 2, is));
427
+ is += 1;
428
+ ie += 1;
429
+ if (_cubic) {
430
+ in -= 1;
431
+ is += 1;
432
+ iw -= 1;
433
+ ie += 1;
434
+ }
435
+ if (ie - iw >= _width - 1) {
436
+ // Include entire longitude range
437
+ iw = 0;
438
+ ie = _width - 1;
439
+ } else {
440
+ ie += iw < 0 ? _width : (iw >= _width ? -_width : 0);
441
+ iw += iw < 0 ? _width : (iw >= _width ? -_width : 0);
442
+ }
443
+ int oysize = int(_data.size());
444
+ _xsize = ie - iw + 1;
445
+ _ysize = is - in + 1;
446
+ _xoffset = iw;
447
+ _yoffset = in;
448
+
449
+ try {
450
+ _data.resize(_ysize, vector<pixel_t>(_xsize));
451
+ for (int iy = min(oysize, _ysize); iy--;)
452
+ _data[iy].resize(_xsize);
453
+ }
454
+ catch (const bad_alloc&) {
455
+ CacheClear();
456
+ throw GeographicErr("Insufficient memory for caching " + _filename);
457
+ }
458
+
459
+ try {
460
+ for (int iy = in; iy <= is; ++iy) {
461
+ int iy1 = iy, iw1 = iw;
462
+ if (iy < 0 || iy >= _height) {
463
+ // Allow points "beyond" the poles to support interpolation
464
+ iy1 = iy1 < 0 ? -iy1 : 2 * (_height - 1) - iy1;
465
+ iw1 += _width/2;
466
+ if (iw1 >= _width)
467
+ iw1 -= _width;
468
+ }
469
+ int xs1 = min(_width - iw1, _xsize);
470
+ filepos(iw1, iy1);
471
+ Utility::readarray<pixel_t, pixel_t, true>
472
+ (_file, &(_data[iy - in][0]), xs1);
473
+ if (xs1 < _xsize) {
474
+ // Wrap around longitude = 0
475
+ filepos(0, iy1);
476
+ Utility::readarray<pixel_t, pixel_t, true>
477
+ (_file, &(_data[iy - in][xs1]), _xsize - xs1);
478
+ }
479
+ }
480
+ _cache = true;
481
+ }
482
+ catch (const exception& e) {
483
+ CacheClear();
484
+ throw GeographicErr(string("Error filling cache ") + e.what());
485
+ }
486
+ }
487
+
488
+ std::string Geoid::DefaultGeoidPath() {
489
+ string path;
490
+ char* geoidpath = getenv("GEOGRAPHICLIB_GEOID_PATH");
491
+ if (geoidpath)
492
+ path = string(geoidpath);
493
+ if (!path.empty())
494
+ return path;
495
+ char* datapath = getenv("GEOGRAPHICLIB_DATA");
496
+ if (datapath)
497
+ path = string(datapath);
498
+ return (!path.empty() ? path : string(GEOGRAPHICLIB_DATA)) + "/geoids";
499
+ }
500
+
501
+ std::string Geoid::DefaultGeoidName() {
502
+ string name;
503
+ char* geoidname = getenv("GEOGRAPHICLIB_GEOID_NAME");
504
+ if (geoidname)
505
+ name = string(geoidname);
506
+ return !name.empty() ? name : string(GEOGRAPHICLIB_GEOID_DEFAULT_NAME);
507
+ }
508
+
509
+ } // namespace GeographicLib