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
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
18namespace 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