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