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