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