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