17 const char*
const MGRS::hemispheres_ =
"SN";
18 const char*
const MGRS::utmcols_[] = {
"ABCDEFGH",
"JKLMNPQR",
"STUVWXYZ" };
19 const char*
const MGRS::utmrow_ =
"ABCDEFGHJKLMNPQRSTUV";
20 const char*
const MGRS::upscols_[] =
21 {
"JKLPQRSTUXYZ",
"ABCFGHJKLPQR",
"RSTUXYZ",
"ABCFGHJ" };
22 const char*
const MGRS::upsrows_[] =
23 {
"ABCDEFGHJKLMNPQRSTUVWXYZ",
"ABCDEFGHJKLMNP" };
24 const char*
const MGRS::latband_ =
"CDEFGHJKLMNPQRSTUVWX";
25 const char*
const MGRS::upsband_ =
"ABYZ";
26 const char*
const MGRS::digits_ =
"0123456789";
27 const char*
const MGRS::alpha_ =
28 "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjklmnpqrstuvwxyz";
30 const int MGRS::mineasting_[] =
31 { minupsSind_, minupsNind_, minutmcol_, minutmcol_ };
32 const int MGRS::maxeasting_[] =
33 { maxupsSind_, maxupsNind_, maxutmcol_, maxutmcol_ };
34 const int MGRS::minnorthing_[] =
35 { minupsSind_, minupsNind_,
36 minutmSrow_, minutmSrow_ - (maxutmSrow_ - minutmNrow_) };
37 const int MGRS::maxnorthing_[] =
38 { maxupsSind_, maxupsNind_,
39 maxutmNrow_ + (maxutmSrow_ - minutmNrow_), maxutmNrow_ };
42 int prec, std::string& mgrs) {
46 static const real angeps = ldexp(real(1), -(
Math::digits() - 7));
48 isnan(x) || isnan(y) || isnan(lat)) {
52 bool utmp = zone != 0;
53 CheckCoords(utmp, northp, x, y);
56 if (!(prec >= -1 && prec <= maxprec_))
62 char mgrs1[2 + 3 + 2 * maxprec_];
66 mlen = z + 3 + 2 * prec;
68 mgrs1[0] = digits_[ zone / base_ ];
69 mgrs1[1] = digits_[ zone % base_ ];
75 static_assert(numeric_limits<long long>::digits >= 44,
76 "long long not wide enough to store 10e12");
85 ix = (
long long)(floor(xx)),
86 iy = (
long long)(floor(yy)),
87 m = (
long long)(mult_) * (
long long)(tile_);
88 int xh = int(ix / m), yh = int(iy / m);
92 iband = fabs(lat) < angeps ? (northp ? 0 : -1) : LatitudeBand(lat),
93 icol = xh - minutmcol_,
94 irow = UTMRow(iband, icol, yh % utmrowperiod_);
95 if (irow != yh - (northp ? minutmNrow_ : maxutmSrow_))
97 +
" is inconsistent with UTM coordinates");
98 mgrs1[z++] = latband_[10 + iband];
99 mgrs1[z++] = utmcols_[zone1 % 3][icol];
100 mgrs1[z++] = utmrow_[(yh + (zone1 & 1 ? utmevenrowshift_ : 0))
103 bool eastp = xh >= upseasting_;
104 int iband = (northp ? 2 : 0) + (eastp ? 1 : 0);
105 mgrs1[z++] = upsband_[iband];
106 mgrs1[z++] = upscols_[iband][xh - (eastp ? upseasting_ :
107 (northp ? minupsNind_ :
109 mgrs1[z++] = upsrows_[northp][yh - (northp ? minupsNind_ : minupsSind_)];
112 ix -= m * xh; iy -= m * yh;
113 long long d = (
long long)(pow(real(base_), maxprec_ - prec));
115 for (
int c = prec; c--;) {
116 mgrs1[z + c ] = digits_[ix % base_]; ix /= base_;
117 mgrs1[z + c + prec] = digits_[iy % base_]; iy /= base_;
121 copy(mgrs1, mgrs1 + mlen, mgrs.begin());
125 int prec, std::string& mgrs) {
129 real ys = northp ? y : y - utmNshift_;
138 lat = real(0.9) * ys;
143 latp = real(0.901) * ys + (ys > 0 ? 1 : -1) * real(0.135),
146 late = real(0.902) * ys * (1 - real(1.85e-6) * ys * ys);
147 if (LatitudeBand(latp) == LatitudeBand(late))
156 Forward(zone, northp, x, y, lat, prec, mgrs);
160 int& zone,
bool& northp, real& x, real& y,
161 int& prec,
bool centerp) {
164 len = int(mgrs.length());
166 toupper(mgrs[0]) ==
'I' &&
167 toupper(mgrs[1]) ==
'N' &&
168 toupper(mgrs[2]) ==
'V') {
180 zone1 = 10 * zone1 + i;
187 + mgrs.substr(0, p));
191 int zonem1 = zone1 - 1;
192 const char* band = utmp ? latband_ : upsband_;
196 + (utmp ?
"UTM" :
"UPS") +
" set " + band);
197 bool northp1 = iband >= (utmp ? 10 : 2);
200 real deg = real(utmNshift_) / (
Math::qd * tile_);
205 x = ((zone == 31 && iband == 17) ? 4 : 5) * tile_;
207 y = floor(8 * (iband - real(9.5)) * deg + real(0.5)) * tile_
208 + (northp ? 0 : utmNshift_);
211 x = ((iband & 1 ? 1 : -1) * floor(4 * deg + real(0.5))
212 + upseasting_) * tile_;
214 y = upseasting_ * tile_;
218 }
else if (len - p < 2)
220 const char* col = utmp ? utmcols_[zonem1 % 3] : upscols_[iband];
221 const char* row = utmp ? utmrow_ : upsrows_[northp1];
226 + (utmp ?
"zone " + mgrs.substr(0, p-2) :
237 irow = (irow + utmrowperiod_ - utmevenrowshift_) % utmrowperiod_;
239 irow = UTMRow(iband, icol, irow);
240 if (irow == maxutmSrow_)
242 +
" not in zone/band " + mgrs.substr(0, p-2));
244 irow = northp1 ? irow : irow + 100;
245 icol = icol + minutmcol_;
247 bool eastp = iband & 1;
248 icol += eastp ? upseasting_ : (northp1 ? minupsNind_ : minupsSind_);
249 irow += northp1 ? minupsNind_ : minupsSind_;
251 int prec1 = (len - p)/2;
256 for (
int i = 0; i < prec1; ++i) {
261 if (ix < 0 || iy < 0)
262 throw GeographicErr(
"Encountered a non-digit in " + mgrs.substr(p));
263 x1 = base_ * x1 + ix;
264 y1 = base_ * y1 + iy;
268 throw GeographicErr(
"Encountered a non-digit in " + mgrs.substr(p));
273 if (prec1 > maxprec_)
275 +
" digits in " + mgrs.substr(p));
277 unit *= 2; x1 = 2 * x1 + 1; y1 = 2 * y1 + 1;
281 x = (tile_ * x1) / unit;
282 y = (tile_ * y1) / unit;
286 void MGRS::CheckCoords(
bool utmp,
bool& northp,
real& x,
real& y) {
298 ix = int(floor(x / tile_)),
299 iy = int(floor(y / tile_)),
300 ind = (utmp ? 2 : 0) + (northp ? 1 : 0);
301 if (! (ix >= mineasting_[ind] && ix < maxeasting_[ind]) ) {
302 if (ix == maxeasting_[ind] && x == maxeasting_[ind] * tile_)
307 + (utmp ?
"UTM" :
"UPS") +
" range for "
308 + (northp ?
"N" :
"S" ) +
" hemisphere ["
309 +
Utility::str(mineasting_[ind]*tile_/1000)
311 +
Utility::str(maxeasting_[ind]*tile_/1000)
314 if (! (iy >= minnorthing_[ind] && iy < maxnorthing_[ind]) ) {
315 if (iy == maxnorthing_[ind] && y == maxnorthing_[ind] * tile_)
318 throw GeographicErr(
"Northing " +
Utility::str(
int(floor(y/1000)))
320 + (utmp ?
"UTM" :
"UPS") +
" range for "
321 + (northp ?
"N" :
"S" ) +
" hemisphere ["
322 + Utility::str(minnorthing_[ind]*tile_/1000)
324 + Utility::str(maxnorthing_[ind]*tile_/1000)
330 if (northp && iy < minutmNrow_) {
333 }
else if (!northp && iy >= maxutmSrow_) {
334 if (y == maxutmSrow_ * tile_)
345 int MGRS::UTMRow(
int iband,
int icol,
int irow) {
355 bool northp = iband >= 0;
379 minrow = iband > -10 ?
380 int(floor(c -
real(4.3) -
real(0.1) * northp)) : -90,
382 int(floor(c +
real(4.4) -
real(0.1) * northp)) : 94,
383 baserow = (minrow + maxrow) / 2 - utmrowperiod_ / 2;
387 irow = (irow - baserow + maxutmSrow_) % utmrowperiod_ + baserow;
388 if (!( irow >= minrow && irow <= maxrow )) {
397 sband = iband >= 0 ? iband : -iband - 1,
399 srow = irow >= 0 ? irow : -irow - 1,
401 scol = icol < 4 ? icol : -icol + 7;
404 if ( ! ( (srow == 70 && sband == 8 && scol >= 2) ||
405 (srow == 71 && sband == 7 && scol <= 2) ||
406 (srow == 79 && sband == 9 && scol >= 1) ||
407 (srow == 80 && sband == 8 && scol <= 1) ) )
414 string& gridzone,
string& block,
415 string& easting,
string& northing) {
416 string::size_type n = mgrs.length();
418 toupper(mgrs[0]) ==
'I' &&
419 toupper(mgrs[1]) ==
'N' &&
420 toupper(mgrs[2]) ==
'V') {
421 gridzone = mgrs.substr(0, 3);
422 block = easting = northing =
"";
425 string::size_type p0 = mgrs.find_first_not_of(digits_);
426 if (p0 == string::npos)
427 throw GeographicErr(
"MGRS::Decode: ref does not contain alpha chars");
429 throw GeographicErr(
"MGRS::Decode: ref does not start with 0-2 digits");
430 string::size_type p1 = mgrs.find_first_of(alpha_, p0);
432 throw GeographicErr(
"MGRS::Decode: ref contains non alphanumeric chars");
433 p1 = min(mgrs.find_first_not_of(alpha_, p0), n);
434 if (!(p1 == p0 + 1 || p1 == p0 + 3))
435 throw GeographicErr(
"MGRS::Decode: ref must contain 1 or 3 alpha chars");
436 if (p1 == p0 + 1 && p1 < n)
437 throw GeographicErr(
"MGRS::Decode: ref contains junk after 1 alpha char");
438 if (p1 < n && (mgrs.find_first_of(digits_, p1) != p1 ||
439 mgrs.find_first_not_of(digits_, p1) != string::npos))
440 throw GeographicErr(
"MGRS::Decode: ref contains junk at end");
442 throw GeographicErr(
"MGRS::Decode: ref must end with even no of digits");
444 gridzone = mgrs.substr(0, p0+1);
445 block = mgrs.substr(p0+1, p1 - (p0 + 1));
446 easting = mgrs.substr(p1, (n - p1) / 2);
447 northing = mgrs.substr(p1 + (n - p1) / 2);
451 real lat, lon, x, y, t = tile_;
int zone;
bool northp;
454 throw GeographicErr(
"MGRS::Check: equator coverage failure");
457 throw GeographicErr(
"MGRS::Check: UTM doesn't reach latitude = 84");
460 throw GeographicErr(
"MGRS::Check: UTM doesn't reach latitude = -80");
463 throw GeographicErr(
"MGRS::Check: Norway exception creates a gap");
466 throw GeographicErr(
"MGRS::Check: Svalbard exception creates a gap");
470 GeographicErr(
"MGRS::Check: North UPS doesn't reach latitude = 84");
474 GeographicErr(
"MGRS::Check: South UPS doesn't reach latitude = -80");
477 const short tab[] = {
493 7, 5, 70, 7, 7, 70, 7, 7, 71, 7, 9, 71,
494 8, 5, 71, 8, 6, 71, 8, 6, 72, 8, 9, 72,
495 8, 5, 79, 8, 8, 79, 8, 8, 80, 8, 9, 80,
496 9, 5, 80, 9, 7, 80, 9, 7, 81, 9, 9, 81,
499 const int bandchecks =
sizeof(tab) / (3 *
sizeof(
short));
500 for (
int i = 0; i < bandchecks; ++i) {
502 if (!( LatitudeBand(lat) == tab[3*i+0] ))
GeographicLib::Math::real real
Header for GeographicLib::MGRS class.
#define GEOGRAPHICLIB_VOLATILE
Header for GeographicLib::Utility class.
Exception handling for GeographicLib.
static void Reverse(const std::string &mgrs, int &zone, bool &northp, real &x, real &y, int &prec, bool centerp=true)
static void Decode(const std::string &mgrs, std::string &gridzone, std::string &block, std::string &easting, std::string &northing)
static void Forward(int zone, bool northp, real x, real y, int prec, std::string &mgrs)
static constexpr int qd
degrees per quarter turn
static void Forward(real lat, real lon, int &zone, bool &northp, real &x, real &y, real &gamma, real &k, int setzone=STANDARD, bool mgrslimits=false)
static void Reverse(int zone, bool northp, real x, real y, real &lat, real &lon, real &gamma, real &k, bool mgrslimits=false)
Some utility routines for GeographicLib.
static int lookup(const std::string &s, char c)
static std::string str(T x, int p=-1)
Namespace for GeographicLib.