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