00001 /** 00002 * \file MGRS.hpp 00003 * \brief Header for GeographicLib::MGRS class 00004 * 00005 * Copyright (c) Charles Karney (2008-2014) <charles@karney.com> and licensed 00006 * under the MIT/X11 License. For more information, see 00007 * http://geographiclib.sourceforge.net/ 00008 **********************************************************************/ 00009 00010 #if !defined(GEOGRAPHICLIB_MGRS_HPP) 00011 #define GEOGRAPHICLIB_MGRS_HPP 1 00012 00013 #include <GeographicLib/Constants.hpp> 00014 #include <GeographicLib/UTMUPS.hpp> 00015 00016 #if defined(_MSC_VER) 00017 // Squelch warnings about dll vs string 00018 # pragma warning (push) 00019 # pragma warning (disable: 4251) 00020 #endif 00021 00022 namespace GeographicLib { 00023 00024 /** 00025 * \brief Convert between UTM/UPS and %MGRS 00026 * 00027 * MGRS is defined in Chapter 3 of 00028 * - J. W. Hager, L. L. Fry, S. S. Jacks, D. R. Hill, 00029 * <a href="http://earth-info.nga.mil/GandG/publications/tm8358.1/pdf/TM8358_1.pdf"> 00030 * Datums, Ellipsoids, Grids, and Grid Reference Systems</a>, 00031 * Defense Mapping Agency, Technical Manual TM8358.1 (1990). 00032 * . 00033 * This document has been updated by the two NGA documents 00034 * - <a href="http://earth-info.nga.mil/GandG/publications/NGA_STND_0037_2_0_0_GRIDS/NGA.STND.0037_2.0.0_GRIDS.pdf"> 00035 * Universal Grids and Grid Reference Systems</a>, 00036 * NGA.STND.0037_2.0.0_GRIDS (2014). 00037 * - <a href="http://earth-info.nga.mil/GandG/publications/NGA_SIG_0012_2_0_0_UTMUPS/NGA.SIG.0012_2.0.0_UTMUPS.pdf"> 00038 * The Universal Grids and the Transverse Mercator and Polar Stereographic 00039 * Map Projections</a>, NGA.SIG.0012_2.0.0_UTMUPS (2014). 00040 * 00041 * This implementation has the following properties: 00042 * - The conversions are closed, i.e., output from Forward is legal input for 00043 * Reverse and vice versa. Conversion in both directions preserve the 00044 * UTM/UPS selection and the UTM zone. 00045 * - Forward followed by Reverse and vice versa is approximately the 00046 * identity. (This is affected in predictable ways by errors in 00047 * determining the latitude band and by loss of precision in the MGRS 00048 * coordinates.) 00049 * - All MGRS coordinates truncate to legal 100 km blocks. All MGRS 00050 * coordinates with a legal 100 km block prefix are legal (even though the 00051 * latitude band letter may now belong to a neighboring band). 00052 * - The range of UTM/UPS coordinates allowed for conversion to MGRS 00053 * coordinates is the maximum consistent with staying within the letter 00054 * ranges of the MGRS scheme. 00055 * - All the transformations are implemented as static methods in the MGRS 00056 * class. 00057 * 00058 * The <a href="http://www.nga.mil">NGA</a> software package 00059 * <a href="http://earth-info.nga.mil/GandG/geotrans/index.html">geotrans</a> 00060 * also provides conversions to and from MGRS. Version 3.0 (and earlier) 00061 * suffers from some drawbacks: 00062 * - Inconsistent rules are used to determine the whether a particular MGRS 00063 * coordinate is legal. A more systematic approach is taken here. 00064 * - The underlying projections are not very accurately implemented. 00065 * 00066 * Example of use: 00067 * \include example-MGRS.cpp 00068 **********************************************************************/ 00069 class GEOGRAPHICLIB_EXPORT MGRS { 00070 private: 00071 typedef Math::real real; 00072 // The smallest length s.t., 1.0e7 - eps() < 1.0e7 (approx 1.9 nm) 00073 static inline real eps() { 00074 using std::pow; 00075 // 25 = ceil(log_2(2e7)) -- use half circumference here because 00076 // northing 195e5 is a legal in the "southern" hemisphere. 00077 static const real eps = pow(real(0.5), Math::digits() - 25); 00078 return eps; 00079 } 00080 // The smallest angle s.t., 90 - angeps() < 90 (approx 50e-12 arcsec) 00081 static inline real angeps() { 00082 using std::pow; 00083 // 7 = ceil(log_2(90)) 00084 static const real angeps = pow(real(0.5), Math::digits() - 7); 00085 return angeps; 00086 } 00087 static const std::string hemispheres_; 00088 static const std::string utmcols_[3]; 00089 static const std::string utmrow_; 00090 static const std::string upscols_[4]; 00091 static const std::string upsrows_[2]; 00092 static const std::string latband_; 00093 static const std::string upsband_; 00094 static const std::string digits_; 00095 00096 static const int mineasting_[4]; 00097 static const int maxeasting_[4]; 00098 static const int minnorthing_[4]; 00099 static const int maxnorthing_[4]; 00100 enum { 00101 base_ = 10, 00102 // Top-level tiles are 10^5 m = 100 km on a side 00103 tilelevel_ = 5, 00104 // Period of UTM row letters 00105 utmrowperiod_ = 20, 00106 // Row letters are shifted by 5 for even zones 00107 utmevenrowshift_ = 5, 00108 // Maximum precision is um 00109 maxprec_ = 5 + 6, 00110 }; 00111 static void CheckCoords(bool utmp, bool& northp, real& x, real& y); 00112 static int UTMRow(int iband, int icol, int irow); 00113 00114 friend class UTMUPS; // UTMUPS::StandardZone calls LatitudeBand 00115 // Return latitude band number [-10, 10) for the given latitude (degrees). 00116 // The bands are reckoned in include their southern edges. 00117 static int LatitudeBand(real lat) { 00118 using std::floor; 00119 int ilat = int(floor(lat)); 00120 return (std::max)(-10, (std::min)(9, (ilat + 80)/8 - 10)); 00121 } 00122 // Return approximate latitude band number [-10, 10) for the given northing 00123 // (meters). With this rule, each 100km tile would have a unique band 00124 // letter corresponding to the latitude at the center of the tile. This 00125 // function isn't currently used. 00126 static int ApproxLatitudeBand(real y) { 00127 // northing at tile center in units of tile = 100km 00128 using std::floor; using std::abs; 00129 real ya = floor( (std::min)(real(88), abs(y/tile_)) ) + 00130 real(0.5); 00131 // convert to lat (mult by 90/100) and then to band (divide by 8) 00132 // the +1 fine tunes the boundary between bands 3 and 4 00133 int b = int(floor( ((ya * 9 + 1) / 10) / 8 )); 00134 // For the northern hemisphere we have 00135 // band rows num 00136 // N 0 0:8 9 00137 // P 1 9:17 9 00138 // Q 2 18:26 9 00139 // R 3 27:34 8 00140 // S 4 35:43 9 00141 // T 5 44:52 9 00142 // U 6 53:61 9 00143 // V 7 62:70 9 00144 // W 8 71:79 9 00145 // X 9 80:94 15 00146 return y >= 0 ? b : -(b + 1); 00147 } 00148 // UTMUPS access these enums 00149 enum { 00150 tile_ = 100000, // Size MGRS blocks 00151 minutmcol_ = 1, 00152 maxutmcol_ = 9, 00153 minutmSrow_ = 10, 00154 maxutmSrow_ = 100, // Also used for UTM S false northing 00155 minutmNrow_ = 0, // Also used for UTM N false northing 00156 maxutmNrow_ = 95, 00157 minupsSind_ = 8, // These 4 ind's apply to easting and northing 00158 maxupsSind_ = 32, 00159 minupsNind_ = 13, 00160 maxupsNind_ = 27, 00161 upseasting_ = 20, // Also used for UPS false northing 00162 utmeasting_ = 5, // UTM false easting 00163 // Difference between S hemisphere northing and N hemisphere northing 00164 utmNshift_ = (maxutmSrow_ - minutmNrow_) * tile_ 00165 }; 00166 MGRS(); // Disable constructor 00167 00168 public: 00169 00170 /** 00171 * Convert UTM or UPS coordinate to an MGRS coordinate. 00172 * 00173 * @param[in] zone UTM zone (zero means UPS). 00174 * @param[in] northp hemisphere (true means north, false means south). 00175 * @param[in] x easting of point (meters). 00176 * @param[in] y northing of point (meters). 00177 * @param[in] prec precision relative to 100 km. 00178 * @param[out] mgrs MGRS string. 00179 * @exception GeographicErr if \e zone, \e x, or \e y is outside its 00180 * allowed range. 00181 * @exception GeographicErr if the memory for the MGRS string can't be 00182 * allocated. 00183 * 00184 * \e prec specifies the precision of the MGRS string as follows: 00185 * - prec = −1 (min), only the grid zone is returned 00186 * - prec = 0 (min), 100 km 00187 * - prec = 1, 10 km 00188 * - prec = 2, 1 km 00189 * - prec = 3, 100 m 00190 * - prec = 4, 10 m 00191 * - prec = 5, 1 m 00192 * - prec = 6, 0.1 m 00193 * - prec = 11 (max), 1 μm 00194 * 00195 * UTM eastings are allowed to be in the range [100 km, 900 km], northings 00196 * are allowed to be in in [0 km, 9500 km] for the northern hemisphere and 00197 * in [1000 km, 10000 km] for the southern hemisphere. (However UTM 00198 * northings can be continued across the equator. So the actual limits on 00199 * the northings are [−9000 km, 9500 km] for the "northern" 00200 * hemisphere and [1000 km, 19500 km] for the "southern" hemisphere.) 00201 * 00202 * UPS eastings/northings are allowed to be in the range [1300 km, 2700 km] 00203 * in the northern hemisphere and in [800 km, 3200 km] in the southern 00204 * hemisphere. 00205 * 00206 * The ranges are 100 km more restrictive that for the conversion between 00207 * geographic coordinates and UTM and UPS given by UTMUPS. These 00208 * restrictions are dictated by the allowed letters in MGRS coordinates. 00209 * The choice of 9500 km for the maximum northing for northern hemisphere 00210 * and of 1000 km as the minimum northing for southern hemisphere provide 00211 * at least 0.5 degree extension into standard UPS zones. The upper ends 00212 * of the ranges for the UPS coordinates is dictated by requiring symmetry 00213 * about the meridians 0E and 90E. 00214 * 00215 * All allowed UTM and UPS coordinates may now be converted to legal MGRS 00216 * coordinates with the proviso that eastings and northings on the upper 00217 * boundaries are silently reduced by about 4 nm (4 nanometers) to place 00218 * them \e within the allowed range. (This includes reducing a southern 00219 * hemisphere northing of 10000 km by 4 nm so that it is placed in latitude 00220 * band M.) The UTM or UPS coordinates are truncated to requested 00221 * precision to determine the MGRS coordinate. Thus in UTM zone 38n, the 00222 * square area with easting in [444 km, 445 km) and northing in [3688 km, 00223 * 3689 km) maps to MGRS coordinate 38SMB4488 (at \e prec = 2, 1 km), 00224 * Khulani Sq., Baghdad. 00225 * 00226 * The UTM/UPS selection and the UTM zone is preserved in the conversion to 00227 * MGRS coordinate. Thus for \e zone > 0, the MGRS coordinate begins with 00228 * the zone number followed by one of [C--M] for the southern 00229 * hemisphere and [N--X] for the northern hemisphere. For \e zone = 00230 * 0, the MGRS coordinates begins with one of [AB] for the southern 00231 * hemisphere and [XY] for the northern hemisphere. 00232 * 00233 * The conversion to the MGRS is exact for prec in [0, 5] except that a 00234 * neighboring latitude band letter may be given if the point is within 5nm 00235 * of a band boundary. For prec in [6, 11], the conversion is accurate to 00236 * roundoff. 00237 * 00238 * If \e prec = -1, then the "grid zone designation", e.g., 18T, is 00239 * returned. This consists of the UTM zone number (absent for UPS) and the 00240 * first letter of the MGRS string which labels the latitude band for UTM 00241 * and the hemisphere for UPS. 00242 * 00243 * If \e x or \e y is NaN or if \e zone is UTMUPS::INVALID, the returned 00244 * MGRS string is "INVALID". 00245 * 00246 * Return the result via a reference argument to avoid the overhead of 00247 * allocating a potentially large number of small strings. If an error is 00248 * thrown, then \e mgrs is unchanged. 00249 **********************************************************************/ 00250 static void Forward(int zone, bool northp, real x, real y, 00251 int prec, std::string& mgrs); 00252 00253 /** 00254 * Convert UTM or UPS coordinate to an MGRS coordinate when the latitude is 00255 * known. 00256 * 00257 * @param[in] zone UTM zone (zero means UPS). 00258 * @param[in] northp hemisphere (true means north, false means south). 00259 * @param[in] x easting of point (meters). 00260 * @param[in] y northing of point (meters). 00261 * @param[in] lat latitude (degrees). 00262 * @param[in] prec precision relative to 100 km. 00263 * @param[out] mgrs MGRS string. 00264 * @exception GeographicErr if \e zone, \e x, or \e y is outside its 00265 * allowed range. 00266 * @exception GeographicErr if \e lat is inconsistent with the given UTM 00267 * coordinates. 00268 * @exception std::bad_alloc if the memory for \e mgrs can't be allocated. 00269 * 00270 * The latitude is ignored for \e zone = 0 (UPS); otherwise the latitude is 00271 * used to determine the latitude band and this is checked for consistency 00272 * using the same tests as Reverse. 00273 **********************************************************************/ 00274 static void Forward(int zone, bool northp, real x, real y, real lat, 00275 int prec, std::string& mgrs); 00276 00277 /** 00278 * Convert a MGRS coordinate to UTM or UPS coordinates. 00279 * 00280 * @param[in] mgrs MGRS string. 00281 * @param[out] zone UTM zone (zero means UPS). 00282 * @param[out] northp hemisphere (true means north, false means south). 00283 * @param[out] x easting of point (meters). 00284 * @param[out] y northing of point (meters). 00285 * @param[out] prec precision relative to 100 km. 00286 * @param[in] centerp if true (default), return center of the MGRS square, 00287 * else return SW (lower left) corner. 00288 * @exception GeographicErr if \e mgrs is illegal. 00289 * 00290 * All conversions from MGRS to UTM/UPS are permitted provided the MGRS 00291 * coordinate is a possible result of a conversion in the other direction. 00292 * (The leading 0 may be dropped from an input MGRS coordinate for UTM 00293 * zones 1--9.) In addition, MGRS coordinates with a neighboring 00294 * latitude band letter are permitted provided that some portion of the 00295 * 100 km block is within the given latitude band. Thus 00296 * - 38VLS and 38WLS are allowed (latitude 64N intersects the square 00297 * 38[VW]LS); but 38VMS is not permitted (all of 38VMS is north of 64N) 00298 * - 38MPE and 38NPF are permitted (they straddle the equator); but 38NPE 00299 * and 38MPF are not permitted (the equator does not intersect either 00300 * block). 00301 * - Similarly ZAB and YZB are permitted (they straddle the prime 00302 * meridian); but YAB and ZZB are not (the prime meridian does not 00303 * intersect either block). 00304 * 00305 * The UTM/UPS selection and the UTM zone is preserved in the conversion 00306 * from MGRS coordinate. The conversion is exact for prec in [0, 5]. With 00307 * centerp = true the conversion from MGRS to geographic and back is 00308 * stable. This is not assured if \e centerp = false. 00309 * 00310 * If a "grid zone designation" (for example, 18T or A) is given, then some 00311 * suitable (but essentially arbitrary) point within that grid zone is 00312 * returned. The main utility of the conversion is to allow \e zone and \e 00313 * northp to be determined. In this case, the \e centerp parameter is 00314 * ignored. 00315 * 00316 * If the first 3 characters of \e mgrs are "INV", then \e x and \e y are 00317 * set to NaN and \e zone is set to UTMUPS::INVALID. 00318 * 00319 * If an exception is thrown, then the arguments are unchanged. 00320 **********************************************************************/ 00321 static void Reverse(const std::string& mgrs, 00322 int& zone, bool& northp, real& x, real& y, 00323 int& prec, bool centerp = true); 00324 00325 /** \name Inspector functions 00326 **********************************************************************/ 00327 ///@{ 00328 /** 00329 * @return \e a the equatorial radius of the WGS84 ellipsoid (meters). 00330 * 00331 * (The WGS84 value is returned because the UTM and UPS projections are 00332 * based on this ellipsoid.) 00333 **********************************************************************/ 00334 static Math::real MajorRadius() { return UTMUPS::MajorRadius(); } 00335 00336 /** 00337 * @return \e f the flattening of the WGS84 ellipsoid. 00338 * 00339 * (The WGS84 value is returned because the UTM and UPS projections are 00340 * based on this ellipsoid.) 00341 **********************************************************************/ 00342 static Math::real Flattening() { return UTMUPS::Flattening(); } 00343 ///@} 00344 00345 /// \cond SKIP 00346 /** 00347 * <b>DEPRECATED</b> 00348 * @return \e r the inverse flattening of the WGS84 ellipsoid. 00349 **********************************************************************/ 00350 static Math::real InverseFlattening() 00351 { return UTMUPS::InverseFlattening(); } 00352 /// \endcond 00353 }; 00354 00355 } // namespace GeographicLib 00356 00357 #if defined(_MSC_VER) 00358 # pragma warning (pop) 00359 #endif 00360 00361 #endif // GEOGRAPHICLIB_MGRS_HPP