1 /** 2 * \file MGRS.hpp 3 * \brief Header for GeographicLib::MGRS class 4 * 5 * Copyright (c) Charles Karney (2008-2020) <charles@karney.com> and licensed 6 * under the MIT/X11 License. For more information, see 7 * https://geographiclib.sourceforge.io/ 8 **********************************************************************/ 9 10 #if !defined(GEOGRAPHICLIB_MGRS_HPP) 11 #define GEOGRAPHICLIB_MGRS_HPP 1 12 13 #include <GeographicLib/Constants.hpp> 14 #include <GeographicLib/UTMUPS.hpp> 15 16 #if defined(_MSC_VER) 17 // Squelch warnings about dll vs string 18 # pragma warning (push) 19 # pragma warning (disable: 4251) 20 #endif 21 22 namespace GeographicLib { 23 24 /** 25 * \brief Convert between UTM/UPS and %MGRS 26 * 27 * MGRS is defined in Chapter 3 of 28 * - J. W. Hager, L. L. Fry, S. S. Jacks, D. R. Hill, 29 * <a href="https://web.archive.org/web/20161214054445/http://earth-info.nga.mil/GandG/publications/tm8358.1/pdf/TM8358_1.pdf"> 30 * Datums, Ellipsoids, Grids, and Grid Reference Systems</a>, 31 * Defense Mapping Agency, Technical Manual TM8358.1 (1990). 32 * . 33 * This document has been updated by the two NGA documents 34 * - <a href="https://earth-info.nga.mil/php/download.php?file=coord-grids"> 35 * Universal Grids and Grid Reference Systems</a>, 36 * NGA.STND.0037 (2014). 37 * - <a href="https://earth-info.nga.mil/php/download.php?file=coord-utmups"> 38 * The Universal Grids and the Transverse Mercator and Polar Stereographic 39 * Map Projections</a>, NGA.SIG.0012 (2014). 40 * 41 * This implementation has the following properties: 42 * - The conversions are closed, i.e., output from Forward is legal input for 43 * Reverse and vice versa. Conversion in both directions preserve the 44 * UTM/UPS selection and the UTM zone. 45 * - Forward followed by Reverse and vice versa is approximately the 46 * identity. (This is affected in predictable ways by errors in 47 * determining the latitude band and by loss of precision in the MGRS 48 * coordinates.) 49 * - The trailing digits produced by Forward are consistent as the precision 50 * is varied. Specifically, the digits are obtained by operating on the 51 * easting with ⌊10<sup>6</sup> <i>x</i>⌋ and extracting the 52 * required digits from the resulting number (and similarly for the 53 * northing). 54 * - All MGRS coordinates truncate to legal 100 km blocks. All MGRS 55 * coordinates with a legal 100 km block prefix are legal (even though the 56 * latitude band letter may now belong to a neighboring band). 57 * - The range of UTM/UPS coordinates allowed for conversion to MGRS 58 * coordinates is the maximum consistent with staying within the letter 59 * ranges of the MGRS scheme. 60 * - All the transformations are implemented as static methods in the MGRS 61 * class. 62 * 63 * The <a href="http://www.nga.mil">NGA</a> software package 64 * <a href="https://earth-info.nga.mil/index.php?dir=wgs84&action=wgs84#tab_geotrans">geotrans</a> 65 * also provides conversions to and from MGRS. Version 3.0 (and earlier) 66 * suffers from some drawbacks: 67 * - Inconsistent rules are used to determine the whether a particular MGRS 68 * coordinate is legal. A more systematic approach is taken here. 69 * - The underlying projections are not very accurately implemented. 70 * 71 * Example of use: 72 * \include example-MGRS.cpp 73 **********************************************************************/ 74 class GEOGRAPHICLIB_EXPORT MGRS { 75 private: 76 typedef Math::real real; 77 static const char* const hemispheres_; 78 static const char* const utmcols_[3]; 79 static const char* const utmrow_; 80 static const char* const upscols_[4]; 81 static const char* const upsrows_[2]; 82 static const char* const latband_; 83 static const char* const upsband_; 84 static const char* const digits_; 85 86 static const int mineasting_[4]; 87 static const int maxeasting_[4]; 88 static const int minnorthing_[4]; 89 static const int maxnorthing_[4]; 90 enum { 91 base_ = 10, 92 // Top-level tiles are 10^5 m = 100 km on a side 93 tilelevel_ = 5, 94 // Period of UTM row letters 95 utmrowperiod_ = 20, 96 // Row letters are shifted by 5 for even zones 97 utmevenrowshift_ = 5, 98 // Maximum precision is um 99 maxprec_ = 5 + 6, 100 // For generating digits at maxprec 101 mult_ = 1000000, 102 }; 103 static void CheckCoords(bool utmp, bool& northp, real& x, real& y); 104 static int UTMRow(int iband, int icol, int irow); 105 106 friend class UTMUPS; // UTMUPS::StandardZone calls LatitudeBand 107 // Return latitude band number [-10, 10) for the given latitude (degrees). 108 // The bands are reckoned in include their southern edges. LatitudeBand(real lat)109 static int LatitudeBand(real lat) { 110 using std::floor; 111 int ilat = int(floor(lat)); 112 return (std::max)(-10, (std::min)(9, (ilat + 80)/8 - 10)); 113 } 114 // Return approximate latitude band number [-10, 10) for the given northing 115 // (meters). With this rule, each 100km tile would have a unique band 116 // letter corresponding to the latitude at the center of the tile. This 117 // function isn't currently used. ApproxLatitudeBand(real y)118 static int ApproxLatitudeBand(real y) { 119 // northing at tile center in units of tile = 100km 120 using std::floor; using std::abs; 121 real ya = floor( (std::min)(real(88), abs(y/tile_)) ) + 122 real(0.5); 123 // convert to lat (mult by 90/100) and then to band (divide by 8) 124 // the +1 fine tunes the boundary between bands 3 and 4 125 int b = int(floor( ((ya * 9 + 1) / 10) / 8 )); 126 // For the northern hemisphere we have 127 // band rows num 128 // N 0 0:8 9 129 // P 1 9:17 9 130 // Q 2 18:26 9 131 // R 3 27:34 8 132 // S 4 35:43 9 133 // T 5 44:52 9 134 // U 6 53:61 9 135 // V 7 62:70 9 136 // W 8 71:79 9 137 // X 9 80:94 15 138 return y >= 0 ? b : -(b + 1); 139 } 140 // UTMUPS access these enums 141 enum { 142 tile_ = 100000, // Size MGRS blocks 143 minutmcol_ = 1, 144 maxutmcol_ = 9, 145 minutmSrow_ = 10, 146 maxutmSrow_ = 100, // Also used for UTM S false northing 147 minutmNrow_ = 0, // Also used for UTM N false northing 148 maxutmNrow_ = 95, 149 minupsSind_ = 8, // These 4 ind's apply to easting and northing 150 maxupsSind_ = 32, 151 minupsNind_ = 13, 152 maxupsNind_ = 27, 153 upseasting_ = 20, // Also used for UPS false northing 154 utmeasting_ = 5, // UTM false easting 155 // Difference between S hemisphere northing and N hemisphere northing 156 utmNshift_ = (maxutmSrow_ - minutmNrow_) * tile_ 157 }; 158 MGRS(); // Disable constructor 159 160 public: 161 162 /** 163 * Convert UTM or UPS coordinate to an MGRS coordinate. 164 * 165 * @param[in] zone UTM zone (zero means UPS). 166 * @param[in] northp hemisphere (true means north, false means south). 167 * @param[in] x easting of point (meters). 168 * @param[in] y northing of point (meters). 169 * @param[in] prec precision relative to 100 km. 170 * @param[out] mgrs MGRS string. 171 * @exception GeographicErr if \e zone, \e x, or \e y is outside its 172 * allowed range. 173 * @exception GeographicErr if the memory for the MGRS string can't be 174 * allocated. 175 * 176 * \e prec specifies the precision of the MGRS string as follows: 177 * - \e prec = −1 (min), only the grid zone is returned 178 * - \e prec = 0, 100 km 179 * - \e prec = 1, 10 km 180 * - \e prec = 2, 1 km 181 * - \e prec = 3, 100 m 182 * - \e prec = 4, 10 m 183 * - \e prec = 5, 1 m 184 * - \e prec = 6, 0.1 m 185 * - … 186 * - \e prec = 11 (max), 1 μm 187 * 188 * UTM eastings are allowed to be in the range [100 km, 900 km], northings 189 * are allowed to be in in [0 km, 9500 km] for the northern hemisphere and 190 * in [1000 km, 10000 km] for the southern hemisphere. (However UTM 191 * northings can be continued across the equator. So the actual limits on 192 * the northings are [−9000 km, 9500 km] for the "northern" 193 * hemisphere and [1000 km, 19500 km] for the "southern" hemisphere.) 194 * 195 * UPS eastings/northings are allowed to be in the range [1300 km, 2700 km] 196 * in the northern hemisphere and in [800 km, 3200 km] in the southern 197 * hemisphere. 198 * 199 * The ranges are 100 km more restrictive than for the conversion between 200 * geographic coordinates and UTM and UPS given by UTMUPS. These 201 * restrictions are dictated by the allowed letters in MGRS coordinates. 202 * The choice of 9500 km for the maximum northing for northern hemisphere 203 * and of 1000 km as the minimum northing for southern hemisphere provide 204 * at least 0.5 degree extension into standard UPS zones. The upper ends 205 * of the ranges for the UPS coordinates is dictated by requiring symmetry 206 * about the meridians 0E and 90E. 207 * 208 * All allowed UTM and UPS coordinates may now be converted to legal MGRS 209 * coordinates with the proviso that eastings and northings on the upper 210 * boundaries are silently reduced by about 4 nm (4 nanometers) to place 211 * them \e within the allowed range. (This includes reducing a southern 212 * hemisphere northing of 10000 km by 4 nm so that it is placed in latitude 213 * band M.) The UTM or UPS coordinates are truncated to requested 214 * precision to determine the MGRS coordinate. Thus in UTM zone 38n, the 215 * square area with easting in [444 km, 445 km) and northing in [3688 km, 216 * 3689 km) maps to MGRS coordinate 38SMB4488 (at \e prec = 2, 1 km), 217 * Khulani Sq., Baghdad. 218 * 219 * The UTM/UPS selection and the UTM zone is preserved in the conversion to 220 * MGRS coordinate. Thus for \e zone > 0, the MGRS coordinate begins with 221 * the zone number followed by one of [C--M] for the southern 222 * hemisphere and [N--X] for the northern hemisphere. For \e zone = 223 * 0, the MGRS coordinates begins with one of [AB] for the southern 224 * hemisphere and [XY] for the northern hemisphere. 225 * 226 * The conversion to the MGRS is exact for prec in [0, 5] except that a 227 * neighboring latitude band letter may be given if the point is within 5nm 228 * of a band boundary. For prec in [6, 11], the conversion is accurate to 229 * roundoff. 230 * 231 * If \e prec = −1, then the "grid zone designation", e.g., 18T, is 232 * returned. This consists of the UTM zone number (absent for UPS) and the 233 * first letter of the MGRS string which labels the latitude band for UTM 234 * and the hemisphere for UPS. 235 * 236 * If \e x or \e y is NaN or if \e zone is UTMUPS::INVALID, the returned 237 * MGRS string is "INVALID". 238 * 239 * Return the result via a reference argument to avoid the overhead of 240 * allocating a potentially large number of small strings. If an error is 241 * thrown, then \e mgrs is unchanged. 242 **********************************************************************/ 243 static void Forward(int zone, bool northp, real x, real y, 244 int prec, std::string& mgrs); 245 246 /** 247 * Convert UTM or UPS coordinate to an MGRS coordinate when the latitude is 248 * known. 249 * 250 * @param[in] zone UTM zone (zero means UPS). 251 * @param[in] northp hemisphere (true means north, false means south). 252 * @param[in] x easting of point (meters). 253 * @param[in] y northing of point (meters). 254 * @param[in] lat latitude (degrees). 255 * @param[in] prec precision relative to 100 km. 256 * @param[out] mgrs MGRS string. 257 * @exception GeographicErr if \e zone, \e x, or \e y is outside its 258 * allowed range. 259 * @exception GeographicErr if \e lat is inconsistent with the given UTM 260 * coordinates. 261 * @exception std::bad_alloc if the memory for \e mgrs can't be allocated. 262 * 263 * The latitude is ignored for \e zone = 0 (UPS); otherwise the latitude is 264 * used to determine the latitude band and this is checked for consistency 265 * using the same tests as Reverse. 266 **********************************************************************/ 267 static void Forward(int zone, bool northp, real x, real y, real lat, 268 int prec, std::string& mgrs); 269 270 /** 271 * Convert a MGRS coordinate to UTM or UPS coordinates. 272 * 273 * @param[in] mgrs MGRS string. 274 * @param[out] zone UTM zone (zero means UPS). 275 * @param[out] northp hemisphere (true means north, false means south). 276 * @param[out] x easting of point (meters). 277 * @param[out] y northing of point (meters). 278 * @param[out] prec precision relative to 100 km. 279 * @param[in] centerp if true (default), return center of the MGRS square, 280 * else return SW (lower left) corner. 281 * @exception GeographicErr if \e mgrs is illegal. 282 * 283 * All conversions from MGRS to UTM/UPS are permitted provided the MGRS 284 * coordinate is a possible result of a conversion in the other direction. 285 * (The leading 0 may be dropped from an input MGRS coordinate for UTM 286 * zones 1--9.) In addition, MGRS coordinates with a neighboring 287 * latitude band letter are permitted provided that some portion of the 288 * 100 km block is within the given latitude band. Thus 289 * - 38VLS and 38WLS are allowed (latitude 64N intersects the square 290 * 38[VW]LS); but 38VMS is not permitted (all of 38WMS is north of 64N) 291 * - 38MPE and 38NPF are permitted (they straddle the equator); but 38NPE 292 * and 38MPF are not permitted (the equator does not intersect either 293 * block). 294 * - Similarly ZAB and YZB are permitted (they straddle the prime 295 * meridian); but YAB and ZZB are not (the prime meridian does not 296 * intersect either block). 297 * 298 * The UTM/UPS selection and the UTM zone is preserved in the conversion 299 * from MGRS coordinate. The conversion is exact for prec in [0, 5]. With 300 * \e centerp = true, the conversion from MGRS to geographic and back is 301 * stable. This is not assured if \e centerp = false. 302 * 303 * If a "grid zone designation" (for example, 18T or A) is given, then some 304 * suitable (but essentially arbitrary) point within that grid zone is 305 * returned. The main utility of the conversion is to allow \e zone and \e 306 * northp to be determined. In this case, the \e centerp parameter is 307 * ignored and \e prec is set to −1. 308 * 309 * If the first 3 characters of \e mgrs are "INV", then \e x and \e y are 310 * set to NaN, \e zone is set to UTMUPS::INVALID, and \e prec is set to 311 * −2. 312 * 313 * If an exception is thrown, then the arguments are unchanged. 314 **********************************************************************/ 315 static void Reverse(const std::string& mgrs, 316 int& zone, bool& northp, real& x, real& y, 317 int& prec, bool centerp = true); 318 319 /** \name Inspector functions 320 **********************************************************************/ 321 ///@{ 322 /** 323 * @return \e a the equatorial radius of the WGS84 ellipsoid (meters). 324 * 325 * (The WGS84 value is returned because the UTM and UPS projections are 326 * based on this ellipsoid.) 327 **********************************************************************/ EquatorialRadius()328 static Math::real EquatorialRadius() { return UTMUPS::EquatorialRadius(); } 329 330 /** 331 * @return \e f the flattening of the WGS84 ellipsoid. 332 * 333 * (The WGS84 value is returned because the UTM and UPS projections are 334 * based on this ellipsoid.) 335 **********************************************************************/ Flattening()336 static Math::real Flattening() { return UTMUPS::Flattening(); } 337 338 /** 339 * \deprecated An old name for EquatorialRadius(). 340 **********************************************************************/ 341 GEOGRAPHICLIB_DEPRECATED("Use EquatorialRadius()") MajorRadius()342 static Math::real MajorRadius() { return EquatorialRadius(); } 343 ///@} 344 345 /** 346 * Perform some checks on the UTMUPS coordinates on this ellipsoid. Throw 347 * an error if any of the assumptions made in the MGRS class is not true. 348 * This check needs to be carried out if the ellipsoid parameters (or the 349 * UTM/UPS scales) are ever changed. 350 **********************************************************************/ 351 static void Check(); 352 353 }; 354 355 } // namespace GeographicLib 356 357 #if defined(_MSC_VER) 358 # pragma warning (pop) 359 #endif 360 361 #endif // GEOGRAPHICLIB_MGRS_HPP 362