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,62 @@
|
|
1
|
+
/**
|
2
|
+
* \file LocalCartesian.cpp
|
3
|
+
* \brief Implementation for GeographicLib::LocalCartesian class
|
4
|
+
*
|
5
|
+
* Copyright (c) Charles Karney (2008-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/LocalCartesian.hpp>
|
11
|
+
|
12
|
+
namespace GeographicLib {
|
13
|
+
|
14
|
+
using namespace std;
|
15
|
+
|
16
|
+
void LocalCartesian::Reset(real lat0, real lon0, real h0) {
|
17
|
+
_lat0 = Math::LatFix(lat0);
|
18
|
+
_lon0 = Math::AngNormalize(lon0);
|
19
|
+
_h0 = h0;
|
20
|
+
_earth.Forward(_lat0, _lon0, _h0, _x0, _y0, _z0);
|
21
|
+
real sphi, cphi, slam, clam;
|
22
|
+
Math::sincosd(_lat0, sphi, cphi);
|
23
|
+
Math::sincosd(_lon0, slam, clam);
|
24
|
+
Geocentric::Rotation(sphi, cphi, slam, clam, _r);
|
25
|
+
}
|
26
|
+
|
27
|
+
void LocalCartesian::MatrixMultiply(real M[dim2_]) const {
|
28
|
+
// M = r' . M
|
29
|
+
real t[dim2_];
|
30
|
+
copy(M, M + dim2_, t);
|
31
|
+
for (size_t i = 0; i < dim2_; ++i) {
|
32
|
+
size_t row = i / dim_, col = i % dim_;
|
33
|
+
M[i] = _r[row] * t[col] + _r[row+3] * t[col+3] + _r[row+6] * t[col+6];
|
34
|
+
}
|
35
|
+
}
|
36
|
+
|
37
|
+
void LocalCartesian::IntForward(real lat, real lon, real h,
|
38
|
+
real& x, real& y, real& z,
|
39
|
+
real M[dim2_]) const {
|
40
|
+
real xc, yc, zc;
|
41
|
+
_earth.IntForward(lat, lon, h, xc, yc, zc, M);
|
42
|
+
xc -= _x0; yc -= _y0; zc -= _z0;
|
43
|
+
x = _r[0] * xc + _r[3] * yc + _r[6] * zc;
|
44
|
+
y = _r[1] * xc + _r[4] * yc + _r[7] * zc;
|
45
|
+
z = _r[2] * xc + _r[5] * yc + _r[8] * zc;
|
46
|
+
if (M)
|
47
|
+
MatrixMultiply(M);
|
48
|
+
}
|
49
|
+
|
50
|
+
void LocalCartesian::IntReverse(real x, real y, real z,
|
51
|
+
real& lat, real& lon, real& h,
|
52
|
+
real M[dim2_]) const {
|
53
|
+
real
|
54
|
+
xc = _x0 + _r[0] * x + _r[1] * y + _r[2] * z,
|
55
|
+
yc = _y0 + _r[3] * x + _r[4] * y + _r[5] * z,
|
56
|
+
zc = _z0 + _r[6] * x + _r[7] * y + _r[8] * z;
|
57
|
+
_earth.IntReverse(xc, yc, zc, lat, lon, h, M);
|
58
|
+
if (M)
|
59
|
+
MatrixMultiply(M);
|
60
|
+
}
|
61
|
+
|
62
|
+
} // namespace GeographicLib
|
@@ -0,0 +1,461 @@
|
|
1
|
+
/**
|
2
|
+
* \file MGRS.cpp
|
3
|
+
* \brief Implementation for GeographicLib::MGRS class
|
4
|
+
*
|
5
|
+
* Copyright (c) Charles Karney (2008-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/MGRS.hpp>
|
11
|
+
#include <GeographicLib/Utility.hpp>
|
12
|
+
|
13
|
+
namespace GeographicLib {
|
14
|
+
|
15
|
+
using namespace std;
|
16
|
+
|
17
|
+
const string MGRS::hemispheres_ = "SN";
|
18
|
+
const string MGRS::utmcols_[3] = { "ABCDEFGH", "JKLMNPQR", "STUVWXYZ" };
|
19
|
+
const string MGRS::utmrow_ = "ABCDEFGHJKLMNPQRSTUV";
|
20
|
+
const string MGRS::upscols_[4] =
|
21
|
+
{ "JKLPQRSTUXYZ", "ABCFGHJKLPQR", "RSTUXYZ", "ABCFGHJ" };
|
22
|
+
const string MGRS::upsrows_[2] =
|
23
|
+
{ "ABCDEFGHJKLMNPQRSTUVWXYZ", "ABCDEFGHJKLMNP" };
|
24
|
+
const string MGRS::latband_ = "CDEFGHJKLMNPQRSTUVWX";
|
25
|
+
const string MGRS::upsband_ = "ABYZ";
|
26
|
+
const string MGRS::digits_ = "0123456789";
|
27
|
+
|
28
|
+
const int MGRS::mineasting_[4] =
|
29
|
+
{ minupsSind_, minupsNind_, minutmcol_, minutmcol_ };
|
30
|
+
const int MGRS::maxeasting_[4] =
|
31
|
+
{ maxupsSind_, maxupsNind_, maxutmcol_, maxutmcol_ };
|
32
|
+
const int MGRS::minnorthing_[4] =
|
33
|
+
{ minupsSind_, minupsNind_,
|
34
|
+
minutmSrow_, minutmSrow_ - (maxutmSrow_ - minutmNrow_) };
|
35
|
+
const int MGRS::maxnorthing_[4] =
|
36
|
+
{ maxupsSind_, maxupsNind_,
|
37
|
+
maxutmNrow_ + (maxutmSrow_ - minutmNrow_), maxutmNrow_ };
|
38
|
+
|
39
|
+
void MGRS::Forward(int zone, bool northp, real x, real y, real lat,
|
40
|
+
int prec, std::string& mgrs) {
|
41
|
+
// The smallest angle s.t., 90 - angeps() < 90 (approx 50e-12 arcsec)
|
42
|
+
// 7 = ceil(log_2(90))
|
43
|
+
static const real angeps = pow(real(0.5), Math::digits() - 7);
|
44
|
+
if (zone == UTMUPS::INVALID ||
|
45
|
+
Math::isnan(x) || Math::isnan(y) || Math::isnan(lat)) {
|
46
|
+
mgrs = "INVALID";
|
47
|
+
return;
|
48
|
+
}
|
49
|
+
bool utmp = zone != 0;
|
50
|
+
CheckCoords(utmp, northp, x, y);
|
51
|
+
if (!(zone >= UTMUPS::MINZONE && zone <= UTMUPS::MAXZONE))
|
52
|
+
throw GeographicErr("Zone " + Utility::str(zone) + " not in [0,60]");
|
53
|
+
if (!(prec >= -1 && prec <= maxprec_))
|
54
|
+
throw GeographicErr("MGRS precision " + Utility::str(prec)
|
55
|
+
+ " not in [-1, "
|
56
|
+
+ Utility::str(int(maxprec_)) + "]");
|
57
|
+
// Fixed char array for accumulating string. Allow space for zone, 3 block
|
58
|
+
// letters, easting + northing. Don't need to allow for terminating null.
|
59
|
+
char mgrs1[2 + 3 + 2 * maxprec_];
|
60
|
+
int
|
61
|
+
zone1 = zone - 1,
|
62
|
+
z = utmp ? 2 : 0,
|
63
|
+
mlen = z + 3 + 2 * prec;
|
64
|
+
if (utmp) {
|
65
|
+
mgrs1[0] = digits_[ zone / base_ ];
|
66
|
+
mgrs1[1] = digits_[ zone % base_ ];
|
67
|
+
// This isn't necessary...! Keep y non-neg
|
68
|
+
// if (!northp) y -= maxutmSrow_ * tile_;
|
69
|
+
}
|
70
|
+
// The C++ standard mandates 64 bits for long long. But
|
71
|
+
// check, to make sure.
|
72
|
+
GEOGRAPHICLIB_STATIC_ASSERT(numeric_limits<long long>::digits >= 44,
|
73
|
+
"long long not wide enough to store 10e12");
|
74
|
+
long long
|
75
|
+
ix = (long long)(floor(x * mult_)),
|
76
|
+
iy = (long long)(floor(y * mult_)),
|
77
|
+
m = (long long)(mult_) * (long long)(tile_);
|
78
|
+
int xh = int(ix / m), yh = int(iy / m);
|
79
|
+
if (utmp) {
|
80
|
+
int
|
81
|
+
// Correct fuzziness in latitude near equator
|
82
|
+
iband = abs(lat) > angeps ? LatitudeBand(lat) : (northp ? 0 : -1),
|
83
|
+
icol = xh - minutmcol_,
|
84
|
+
irow = UTMRow(iband, icol, yh % utmrowperiod_);
|
85
|
+
if (irow != yh - (northp ? minutmNrow_ : maxutmSrow_))
|
86
|
+
throw GeographicErr("Latitude " + Utility::str(lat)
|
87
|
+
+ " is inconsistent with UTM coordinates");
|
88
|
+
mgrs1[z++] = latband_[10 + iband];
|
89
|
+
mgrs1[z++] = utmcols_[zone1 % 3][icol];
|
90
|
+
mgrs1[z++] = utmrow_[(yh + (zone1 & 1 ? utmevenrowshift_ : 0))
|
91
|
+
% utmrowperiod_];
|
92
|
+
} else {
|
93
|
+
bool eastp = xh >= upseasting_;
|
94
|
+
int iband = (northp ? 2 : 0) + (eastp ? 1 : 0);
|
95
|
+
mgrs1[z++] = upsband_[iband];
|
96
|
+
mgrs1[z++] = upscols_[iband][xh - (eastp ? upseasting_ :
|
97
|
+
(northp ? minupsNind_ : minupsSind_))];
|
98
|
+
mgrs1[z++] = upsrows_[northp][yh - (northp ? minupsNind_ : minupsSind_)];
|
99
|
+
}
|
100
|
+
if (prec > 0) {
|
101
|
+
ix -= m * xh; iy -= m * yh;
|
102
|
+
long long d = (long long)(pow(real(base_), maxprec_ - prec));
|
103
|
+
ix /= d; iy /= d;
|
104
|
+
for (int c = prec; c--;) {
|
105
|
+
mgrs1[z + c ] = digits_[ix % base_]; ix /= base_;
|
106
|
+
mgrs1[z + c + prec] = digits_[iy % base_]; iy /= base_;
|
107
|
+
}
|
108
|
+
}
|
109
|
+
mgrs.resize(mlen);
|
110
|
+
copy(mgrs1, mgrs1 + mlen, mgrs.begin());
|
111
|
+
}
|
112
|
+
|
113
|
+
void MGRS::Forward(int zone, bool northp, real x, real y,
|
114
|
+
int prec, std::string& mgrs) {
|
115
|
+
real lat, lon;
|
116
|
+
if (zone > 0) {
|
117
|
+
// Does a rough estimate for latitude determine the latitude band?
|
118
|
+
real ys = northp ? y : y - utmNshift_;
|
119
|
+
// A cheap calculation of the latitude which results in an "allowed"
|
120
|
+
// latitude band would be
|
121
|
+
// lat = ApproxLatitudeBand(ys) * 8 + 4;
|
122
|
+
//
|
123
|
+
// Here we do a more careful job using the band letter corresponding to
|
124
|
+
// the actual latitude.
|
125
|
+
ys /= tile_;
|
126
|
+
if (abs(ys) < 1)
|
127
|
+
lat = 0.9 * ys; // accurate enough estimate near equator
|
128
|
+
else {
|
129
|
+
real
|
130
|
+
// The poleward bound is a fit from above of lat(x,y)
|
131
|
+
// for x = 500km and y = [0km, 950km]
|
132
|
+
latp = real(0.901) * ys + (ys > 0 ? 1 : -1) * real(0.135),
|
133
|
+
// The equatorward bound is a fit from below of lat(x,y)
|
134
|
+
// for x = 900km and y = [0km, 950km]
|
135
|
+
late = real(0.902) * ys * (1 - real(1.85e-6) * ys * ys);
|
136
|
+
if (LatitudeBand(latp) == LatitudeBand(late))
|
137
|
+
lat = latp;
|
138
|
+
else
|
139
|
+
// bounds straddle a band boundary so need to compute lat accurately
|
140
|
+
UTMUPS::Reverse(zone, northp, x, y, lat, lon);
|
141
|
+
}
|
142
|
+
} else
|
143
|
+
// Latitude isn't needed for UPS specs or for INVALID
|
144
|
+
lat = 0;
|
145
|
+
Forward(zone, northp, x, y, lat, prec, mgrs);
|
146
|
+
}
|
147
|
+
|
148
|
+
void MGRS::Reverse(const std::string& mgrs,
|
149
|
+
int& zone, bool& northp, real& x, real& y,
|
150
|
+
int& prec, bool centerp) {
|
151
|
+
int
|
152
|
+
p = 0,
|
153
|
+
len = int(mgrs.length());
|
154
|
+
if (len >= 3 &&
|
155
|
+
toupper(mgrs[0]) == 'I' &&
|
156
|
+
toupper(mgrs[1]) == 'N' &&
|
157
|
+
toupper(mgrs[2]) == 'V') {
|
158
|
+
zone = UTMUPS::INVALID;
|
159
|
+
northp = false;
|
160
|
+
x = y = Math::NaN();
|
161
|
+
prec = -2;
|
162
|
+
return;
|
163
|
+
}
|
164
|
+
int zone1 = 0;
|
165
|
+
while (p < len) {
|
166
|
+
int i = Utility::lookup(digits_, mgrs[p]);
|
167
|
+
if (i < 0)
|
168
|
+
break;
|
169
|
+
zone1 = 10 * zone1 + i;
|
170
|
+
++p;
|
171
|
+
}
|
172
|
+
if (p > 0 && !(zone1 >= UTMUPS::MINUTMZONE && zone1 <= UTMUPS::MAXUTMZONE))
|
173
|
+
throw GeographicErr("Zone " + Utility::str(zone1) + " not in [1,60]");
|
174
|
+
if (p > 2)
|
175
|
+
throw GeographicErr("More than 2 digits at start of MGRS "
|
176
|
+
+ mgrs.substr(0, p));
|
177
|
+
if (len - p < 1)
|
178
|
+
throw GeographicErr("MGRS string too short " + mgrs);
|
179
|
+
bool utmp = zone1 != UTMUPS::UPS;
|
180
|
+
int zonem1 = zone1 - 1;
|
181
|
+
const string& band = utmp ? latband_ : upsband_;
|
182
|
+
int iband = Utility::lookup(band, mgrs[p++]);
|
183
|
+
if (iband < 0)
|
184
|
+
throw GeographicErr("Band letter " + Utility::str(mgrs[p-1]) + " not in "
|
185
|
+
+ (utmp ? "UTM" : "UPS") + " set " + band);
|
186
|
+
bool northp1 = iband >= (utmp ? 10 : 2);
|
187
|
+
if (p == len) { // Grid zone only (ignore centerp)
|
188
|
+
// Approx length of a degree of meridian arc in units of tile.
|
189
|
+
real deg = real(utmNshift_) / (90 * tile_);
|
190
|
+
zone = zone1;
|
191
|
+
northp = northp1;
|
192
|
+
if (utmp) {
|
193
|
+
// Pick central meridian except for 31V
|
194
|
+
x = ((zone == 31 && iband == 17) ? 4 : 5) * tile_;
|
195
|
+
// Pick center of 8deg latitude bands
|
196
|
+
y = floor(8 * (iband - real(9.5)) * deg + real(0.5)) * tile_
|
197
|
+
+ (northp ? 0 : utmNshift_);
|
198
|
+
} else {
|
199
|
+
// Pick point at lat 86N or 86S
|
200
|
+
x = ((iband & 1 ? 1 : -1) * floor(4 * deg + real(0.5))
|
201
|
+
+ upseasting_) * tile_;
|
202
|
+
// Pick point at lon 90E or 90W.
|
203
|
+
y = upseasting_ * tile_;
|
204
|
+
}
|
205
|
+
prec = -1;
|
206
|
+
return;
|
207
|
+
} else if (len - p < 2)
|
208
|
+
throw GeographicErr("Missing row letter in " + mgrs);
|
209
|
+
const string& col = utmp ? utmcols_[zonem1 % 3] : upscols_[iband];
|
210
|
+
const string& row = utmp ? utmrow_ : upsrows_[northp1];
|
211
|
+
int icol = Utility::lookup(col, mgrs[p++]);
|
212
|
+
if (icol < 0)
|
213
|
+
throw GeographicErr("Column letter " + Utility::str(mgrs[p-1])
|
214
|
+
+ " not in "
|
215
|
+
+ (utmp ? "zone " + mgrs.substr(0, p-2) :
|
216
|
+
"UPS band " + Utility::str(mgrs[p-2]))
|
217
|
+
+ " set " + col );
|
218
|
+
int irow = Utility::lookup(row, mgrs[p++]);
|
219
|
+
if (irow < 0)
|
220
|
+
throw GeographicErr("Row letter " + Utility::str(mgrs[p-1]) + " not in "
|
221
|
+
+ (utmp ? "UTM" :
|
222
|
+
"UPS " + Utility::str(hemispheres_[northp1]))
|
223
|
+
+ " set " + row);
|
224
|
+
if (utmp) {
|
225
|
+
if (zonem1 & 1)
|
226
|
+
irow = (irow + utmrowperiod_ - utmevenrowshift_) % utmrowperiod_;
|
227
|
+
iband -= 10;
|
228
|
+
irow = UTMRow(iband, icol, irow);
|
229
|
+
if (irow == maxutmSrow_)
|
230
|
+
throw GeographicErr("Block " + mgrs.substr(p-2, 2)
|
231
|
+
+ " not in zone/band " + mgrs.substr(0, p-2));
|
232
|
+
|
233
|
+
irow = northp1 ? irow : irow + 100;
|
234
|
+
icol = icol + minutmcol_;
|
235
|
+
} else {
|
236
|
+
bool eastp = iband & 1;
|
237
|
+
icol += eastp ? upseasting_ : (northp1 ? minupsNind_ : minupsSind_);
|
238
|
+
irow += northp1 ? minupsNind_ : minupsSind_;
|
239
|
+
}
|
240
|
+
int prec1 = (len - p)/2;
|
241
|
+
real
|
242
|
+
unit = 1,
|
243
|
+
x1 = icol,
|
244
|
+
y1 = irow;
|
245
|
+
for (int i = 0; i < prec1; ++i) {
|
246
|
+
unit *= base_;
|
247
|
+
int
|
248
|
+
ix = Utility::lookup(digits_, mgrs[p + i]),
|
249
|
+
iy = Utility::lookup(digits_, mgrs[p + i + prec1]);
|
250
|
+
if (ix < 0 || iy < 0)
|
251
|
+
throw GeographicErr("Encountered a non-digit in " + mgrs.substr(p));
|
252
|
+
x1 = base_ * x1 + ix;
|
253
|
+
y1 = base_ * y1 + iy;
|
254
|
+
}
|
255
|
+
if ((len - p) % 2) {
|
256
|
+
if (Utility::lookup(digits_, mgrs[len - 1]) < 0)
|
257
|
+
throw GeographicErr("Encountered a non-digit in " + mgrs.substr(p));
|
258
|
+
else
|
259
|
+
throw GeographicErr("Not an even number of digits in "
|
260
|
+
+ mgrs.substr(p));
|
261
|
+
}
|
262
|
+
if (prec1 > maxprec_)
|
263
|
+
throw GeographicErr("More than " + Utility::str(2*maxprec_)
|
264
|
+
+ " digits in " + mgrs.substr(p));
|
265
|
+
if (centerp) {
|
266
|
+
unit *= 2; x1 = 2 * x1 + 1; y1 = 2 * y1 + 1;
|
267
|
+
}
|
268
|
+
zone = zone1;
|
269
|
+
northp = northp1;
|
270
|
+
x = (tile_ * x1) / unit;
|
271
|
+
y = (tile_ * y1) / unit;
|
272
|
+
prec = prec1;
|
273
|
+
}
|
274
|
+
|
275
|
+
void MGRS::CheckCoords(bool utmp, bool& northp, real& x, real& y) {
|
276
|
+
// Limits are all multiples of 100km and are all closed on the lower end
|
277
|
+
// and open on the upper end -- and this is reflected in the error
|
278
|
+
// messages. However if a coordinate lies on the excluded upper end (e.g.,
|
279
|
+
// after rounding), it is shifted down by eps. This also folds UTM
|
280
|
+
// northings to the correct N/S hemisphere.
|
281
|
+
|
282
|
+
// The smallest length s.t., 1.0e7 - eps() < 1.0e7 (approx 1.9 nm)
|
283
|
+
// 25 = ceil(log_2(2e7)) -- use half circumference here because
|
284
|
+
// northing 195e5 is a legal in the "southern" hemisphere.
|
285
|
+
static const real eps = pow(real(0.5), Math::digits() - 25);
|
286
|
+
int
|
287
|
+
ix = int(floor(x / tile_)),
|
288
|
+
iy = int(floor(y / tile_)),
|
289
|
+
ind = (utmp ? 2 : 0) + (northp ? 1 : 0);
|
290
|
+
if (! (ix >= mineasting_[ind] && ix < maxeasting_[ind]) ) {
|
291
|
+
if (ix == maxeasting_[ind] && x == maxeasting_[ind] * tile_)
|
292
|
+
x -= eps;
|
293
|
+
else
|
294
|
+
throw GeographicErr("Easting " + Utility::str(int(floor(x/1000)))
|
295
|
+
+ "km not in MGRS/"
|
296
|
+
+ (utmp ? "UTM" : "UPS") + " range for "
|
297
|
+
+ (northp ? "N" : "S" ) + " hemisphere ["
|
298
|
+
+ Utility::str(mineasting_[ind]*tile_/1000)
|
299
|
+
+ "km, "
|
300
|
+
+ Utility::str(maxeasting_[ind]*tile_/1000)
|
301
|
+
+ "km)");
|
302
|
+
}
|
303
|
+
if (! (iy >= minnorthing_[ind] && iy < maxnorthing_[ind]) ) {
|
304
|
+
if (iy == maxnorthing_[ind] && y == maxnorthing_[ind] * tile_)
|
305
|
+
y -= eps;
|
306
|
+
else
|
307
|
+
throw GeographicErr("Northing " + Utility::str(int(floor(y/1000)))
|
308
|
+
+ "km not in MGRS/"
|
309
|
+
+ (utmp ? "UTM" : "UPS") + " range for "
|
310
|
+
+ (northp ? "N" : "S" ) + " hemisphere ["
|
311
|
+
+ Utility::str(minnorthing_[ind]*tile_/1000)
|
312
|
+
+ "km, "
|
313
|
+
+ Utility::str(maxnorthing_[ind]*tile_/1000)
|
314
|
+
+ "km)");
|
315
|
+
}
|
316
|
+
|
317
|
+
// Correct the UTM northing and hemisphere if necessary
|
318
|
+
if (utmp) {
|
319
|
+
if (northp && iy < minutmNrow_) {
|
320
|
+
northp = false;
|
321
|
+
y += utmNshift_;
|
322
|
+
} else if (!northp && iy >= maxutmSrow_) {
|
323
|
+
if (y == maxutmSrow_ * tile_)
|
324
|
+
// If on equator retain S hemisphere
|
325
|
+
y -= eps;
|
326
|
+
else {
|
327
|
+
northp = true;
|
328
|
+
y -= utmNshift_;
|
329
|
+
}
|
330
|
+
}
|
331
|
+
}
|
332
|
+
}
|
333
|
+
|
334
|
+
int MGRS::UTMRow(int iband, int icol, int irow) {
|
335
|
+
// Input is iband = band index in [-10, 10) (as returned by LatitudeBand),
|
336
|
+
// icol = column index in [0,8) with origin of easting = 100km, and irow =
|
337
|
+
// periodic row index in [0,20) with origin = equator. Output is true row
|
338
|
+
// index in [-90, 95). Returns maxutmSrow_ = 100, if irow and iband are
|
339
|
+
// incompatible.
|
340
|
+
|
341
|
+
// Estimate center row number for latitude band
|
342
|
+
// 90 deg = 100 tiles; 1 band = 8 deg = 100*8/90 tiles
|
343
|
+
real c = 100 * (8 * iband + 4)/real(90);
|
344
|
+
bool northp = iband >= 0;
|
345
|
+
// These are safe bounds on the rows
|
346
|
+
// iband minrow maxrow
|
347
|
+
// -10 -90 -81
|
348
|
+
// -9 -80 -72
|
349
|
+
// -8 -71 -63
|
350
|
+
// -7 -63 -54
|
351
|
+
// -6 -54 -45
|
352
|
+
// -5 -45 -36
|
353
|
+
// -4 -36 -27
|
354
|
+
// -3 -27 -18
|
355
|
+
// -2 -18 -9
|
356
|
+
// -1 -9 -1
|
357
|
+
// 0 0 8
|
358
|
+
// 1 8 17
|
359
|
+
// 2 17 26
|
360
|
+
// 3 26 35
|
361
|
+
// 4 35 44
|
362
|
+
// 5 44 53
|
363
|
+
// 6 53 62
|
364
|
+
// 7 62 70
|
365
|
+
// 8 71 79
|
366
|
+
// 9 80 94
|
367
|
+
int
|
368
|
+
minrow = iband > -10 ?
|
369
|
+
int(floor(c - real(4.3) - real(0.1) * northp)) : -90,
|
370
|
+
maxrow = iband < 9 ?
|
371
|
+
int(floor(c + real(4.4) - real(0.1) * northp)) : 94,
|
372
|
+
baserow = (minrow + maxrow) / 2 - utmrowperiod_ / 2;
|
373
|
+
// Offset irow by the multiple of utmrowperiod_ which brings it as close as
|
374
|
+
// possible to the center of the latitude band, (minrow + maxrow) / 2.
|
375
|
+
// (Add maxutmSrow_ = 5 * utmrowperiod_ to ensure operand is positive.)
|
376
|
+
irow = (irow - baserow + maxutmSrow_) % utmrowperiod_ + baserow;
|
377
|
+
if (!( irow >= minrow && irow <= maxrow )) {
|
378
|
+
// Outside the safe bounds, so need to check...
|
379
|
+
// Northing = 71e5 and 80e5 intersect band boundaries
|
380
|
+
// y = 71e5 in scol = 2 (x = [3e5,4e5] and x = [6e5,7e5])
|
381
|
+
// y = 80e5 in scol = 1 (x = [2e5,3e5] and x = [7e5,8e5])
|
382
|
+
// This holds for all the ellipsoids given in NGA.SIG.0012_2.0.0_UTMUPS.
|
383
|
+
// The following deals with these special cases.
|
384
|
+
int
|
385
|
+
// Fold [-10,-1] -> [9,0]
|
386
|
+
sband = iband >= 0 ? iband : -iband - 1,
|
387
|
+
// Fold [-90,-1] -> [89,0]
|
388
|
+
srow = irow >= 0 ? irow : -irow - 1,
|
389
|
+
// Fold [4,7] -> [3,0]
|
390
|
+
scol = icol < 4 ? icol : -icol + 7;
|
391
|
+
// For example, the safe rows for band 8 are 71 - 79. However row 70 is
|
392
|
+
// allowed if scol = [2,3] and row 80 is allowed if scol = [0,1].
|
393
|
+
if ( ! ( (srow == 70 && sband == 8 && scol >= 2) ||
|
394
|
+
(srow == 71 && sband == 7 && scol <= 2) ||
|
395
|
+
(srow == 79 && sband == 9 && scol >= 1) ||
|
396
|
+
(srow == 80 && sband == 8 && scol <= 1) ) )
|
397
|
+
irow = maxutmSrow_;
|
398
|
+
}
|
399
|
+
return irow;
|
400
|
+
}
|
401
|
+
|
402
|
+
void MGRS::Check() {
|
403
|
+
real lat, lon, x, y, t = tile_; int zone; bool northp;
|
404
|
+
UTMUPS::Reverse(31, true , 1*t, 0*t, lat, lon);
|
405
|
+
if (!( lon < 0 ))
|
406
|
+
throw GeographicErr("MGRS::Check: equator coverage failure");
|
407
|
+
UTMUPS::Reverse(31, true , 1*t, 95*t, lat, lon);
|
408
|
+
if (!( lat > 84 ))
|
409
|
+
throw GeographicErr("MGRS::Check: UTM doesn't reach latitude = 84");
|
410
|
+
UTMUPS::Reverse(31, false, 1*t, 10*t, lat, lon);
|
411
|
+
if (!( lat < -80 ))
|
412
|
+
throw GeographicErr("MGRS::Check: UTM doesn't reach latitude = -80");
|
413
|
+
UTMUPS::Forward(56, 3, zone, northp, x, y, 32);
|
414
|
+
if (!( x > 1*t ))
|
415
|
+
throw GeographicErr("MGRS::Check: Norway exception creates a gap");
|
416
|
+
UTMUPS::Forward(72, 21, zone, northp, x, y, 35);
|
417
|
+
if (!( x > 1*t ))
|
418
|
+
throw GeographicErr("MGRS::Check: Svalbard exception creates a gap");
|
419
|
+
UTMUPS::Reverse(0, true , 20*t, 13*t, lat, lon);
|
420
|
+
if (!( lat < 84 ))
|
421
|
+
throw GeographicErr("MGRS::Check: North UPS doesn't reach latitude = 84");
|
422
|
+
UTMUPS::Reverse(0, false, 20*t, 8*t, lat, lon);
|
423
|
+
if (!( lat > -80 ))
|
424
|
+
throw
|
425
|
+
GeographicErr("MGRS::Check: South UPS doesn't reach latitude = -80");
|
426
|
+
// Entries are [band, x, y] either side of the band boundaries. Units for
|
427
|
+
// x, y are t = 100km.
|
428
|
+
const short tab[] = {
|
429
|
+
0, 5, 0, 0, 9, 0, // south edge of band 0
|
430
|
+
0, 5, 8, 0, 9, 8, // north edge of band 0
|
431
|
+
1, 5, 9, 1, 9, 9, // south edge of band 1
|
432
|
+
1, 5, 17, 1, 9, 17, // north edge of band 1
|
433
|
+
2, 5, 18, 2, 9, 18, // etc.
|
434
|
+
2, 5, 26, 2, 9, 26,
|
435
|
+
3, 5, 27, 3, 9, 27,
|
436
|
+
3, 5, 35, 3, 9, 35,
|
437
|
+
4, 5, 36, 4, 9, 36,
|
438
|
+
4, 5, 44, 4, 9, 44,
|
439
|
+
5, 5, 45, 5, 9, 45,
|
440
|
+
5, 5, 53, 5, 9, 53,
|
441
|
+
6, 5, 54, 6, 9, 54,
|
442
|
+
6, 5, 62, 6, 9, 62,
|
443
|
+
7, 5, 63, 7, 9, 63,
|
444
|
+
7, 5, 70, 7, 7, 70, 7, 7, 71, 7, 9, 71, // y = 71t crosses boundary
|
445
|
+
8, 5, 71, 8, 6, 71, 8, 6, 72, 8, 9, 72, // between bands 7 and 8.
|
446
|
+
8, 5, 79, 8, 8, 79, 8, 8, 80, 8, 9, 80, // y = 80t crosses boundary
|
447
|
+
9, 5, 80, 9, 7, 80, 9, 7, 81, 9, 9, 81, // between bands 8 and 9.
|
448
|
+
9, 5, 95, 9, 9, 95, // north edge of band 9
|
449
|
+
};
|
450
|
+
const int bandchecks = sizeof(tab) / (3 * sizeof(short));
|
451
|
+
for (int i = 0; i < bandchecks; ++i) {
|
452
|
+
UTMUPS::Reverse(38, true, tab[3*i+1]*t, tab[3*i+2]*t, lat, lon);
|
453
|
+
if (!( LatitudeBand(lat) == tab[3*i+0] ))
|
454
|
+
throw GeographicErr("MGRS::Check: Band error, b = " +
|
455
|
+
Utility::str(tab[3*i+0]) + ", x = " +
|
456
|
+
Utility::str(tab[3*i+1]) + "00km, y = " +
|
457
|
+
Utility::str(tab[3*i+2]) + "00km");
|
458
|
+
}
|
459
|
+
}
|
460
|
+
|
461
|
+
} // namespace GeographicLib
|