GeographicLib: MGRS.cpp Source File (original) (raw)

1

2

3

4

5

6

7

8

9

12

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_ =

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;

44

45

46 static const real angeps = ldexp(real(1), -(Math::digits() - 7));

48 isnan(x) || isnan(y) || isnan(lat)) {

49 mgrs = "INVALID";

50 return;

51 }

52 bool utmp = zone != 0;

53 CheckCoords(utmp, northp, x, y);

56 if (!(prec >= -1 && prec <= maxprec_))

58 + " not in [-1, "

60

61

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

71

72 }

73

74

75 static_assert(numeric_limits::digits >= 44,

76 "long long not wide enough to store 10e12");

77

78

79

80

81

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

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_))

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

125 int prec, std::string& mgrs) {

126 real lat, lon;

127 if (zone > 0) {

128

129 real ys = northp ? y : y - utmNshift_;

130

131

132

133

134

135

136 ys /= tile_;

137 if (fabs(ys) < 1)

138 lat = real(0.9) * ys;

139 else {

140 real

141

142

143 latp = real(0.901) * ys + (ys > 0 ? 1 : -1) * real(0.135),

144

145

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

152 }

153 } else

154

155 lat = 0;

156 Forward(zone, northp, x, y, lat, prec, mgrs);

157 }

158

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') {

170 northp = false;

172 prec = -2;

173 return;

174 }

175 int zone1 = 0;

176 while (p < len) {

178 if (i < 0)

179 break;

180 zone1 = 10 * zone1 + i;

181 ++p;

182 }

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);

191 int zonem1 = zone1 - 1;

192 const char* band = utmp ? latband_ : upsband_;

194 if (iband < 0)

196 + (utmp ? "UTM" : "UPS") + " set " + band);

197 bool northp1 = iband >= (utmp ? 10 : 2);

198 if (p == len) {

199

200 real deg = real(utmNshift_) / (Math::qd * tile_);

201 zone = zone1;

202 northp = northp1;

203 if (utmp) {

204

205 x = ((zone == 31 && iband == 17) ? 4 : 5) * tile_;

206

207 y = floor(8 * (iband - real(9.5)) * deg + real(0.5)) * tile_

208 + (northp ? 0 : utmNshift_);

209 } else {

210

211 x = ((iband & 1 ? 1 : -1) * floor(4 * deg + real(0.5))

212 + upseasting_) * tile_;

213

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];

223 if (icol < 0)

225 + " not in "

226 + (utmp ? "zone " + mgrs.substr(0, p-2) :

228 + " set " + col );

230 if (irow < 0)

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

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) {

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_)

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

288

289

290

291

292

293

294

295

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

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

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

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

347

348

349

350

351

352

353

355 bool northp = iband >= 0;

356

357

358

359

360

361

362

363

364

365

366

367

368

369

370

371

372

373

374

375

376

377

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

385

386

387 irow = (irow - baserow + maxutmSrow_) % utmrowperiod_ + baserow;

388 if (!( irow >= minrow && irow <= maxrow )) {

389

390

391

392

393

394

395 int

396

397 sband = iband >= 0 ? iband : -iband - 1,

398

399 srow = irow >= 0 ? irow : -irow - 1,

400

401 scol = icol < 4 ? icol : -icol + 7;

402

403

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

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

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

451 real lat, lon, x, y, t = tile_; int zone; bool northp;

453 if (!( lon < 0 ))

454 throw GeographicErr("MGRS::Check: equator coverage failure");

456 if (!( lat > 84 ))

457 throw GeographicErr("MGRS::Check: UTM doesn't reach latitude = 84");

459 if (!( lat < -80 ))

460 throw GeographicErr("MGRS::Check: UTM doesn't reach latitude = -80");

462 if (!( x > 1*t ))

463 throw GeographicErr("MGRS::Check: Norway exception creates a gap");

465 if (!( x > 1*t ))

466 throw GeographicErr("MGRS::Check: Svalbard exception creates a gap");

468 if (!( lat < 84 ))

469 throw

470 GeographicErr("MGRS::Check: North UPS doesn't reach latitude = 84");

472 if (!( lat > -80 ))

473 throw

474 GeographicErr("MGRS::Check: South UPS doesn't reach latitude = -80");

475

476

477 const short tab[] = {

478 0, 5, 0, 0, 9, 0,

479 0, 5, 8, 0, 9, 8,

480 1, 5, 9, 1, 9, 9,

481 1, 5, 17, 1, 9, 17,

482 2, 5, 18, 2, 9, 18,

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,

494 8, 5, 71, 8, 6, 71, 8, 6, 72, 8, 9, 72,

495 8, 5, 79, 8, 8, 79, 8, 8, 80, 8, 9, 80,

496 9, 5, 80, 9, 7, 80, 9, 7, 81, 9, 9, 81,

497 9, 5, 95, 9, 9, 95,

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 = " +

507 }

508 }

509

510}

GeographicLib::Math::real real

Header for GeographicLib::MGRS class.

#define GEOGRAPHICLIB_VOLATILE

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

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)

static void Reverse(int zone, bool northp, real x, real y, real &lat, real &lon, real &gamma, real &k, bool mgrslimits=false)

Some utility routines for GeographicLib.

static int lookup(const std::string &s, char c)

static std::string str(T x, int p=-1)

Namespace for GeographicLib.