GeographicLib 2.6
Loading...
Searching...
No Matches
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 // 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
GeographicLib::Math::real real
Header for GeographicLib::MGRS class.
#define GEOGRAPHICLIB_VOLATILE
Definition Math.hpp:64
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:158
static void Check()
Definition MGRS.cpp:449
static void Decode(const std::string &mgrs, std::string &gridzone, std::string &block, std::string &easting, std::string &northing)
Definition MGRS.cpp:412
static void Forward(int zone, bool northp, real x, real y, int prec, std::string &mgrs)
Definition MGRS.cpp:123
static constexpr int qd
degrees per quarter turn
Definition Math.hpp:142
static int digits()
Definition Math.cpp:20
static T NaN()
Definition Math.cpp:301
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 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 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.