00001 /** 00002 * \file MGRS.hpp 00003 * \brief Header for GeographicLib::MGRS class 00004 * 00005 * Copyright (c) Charles Karney (2008, 2009, 2010) <charles@karney.com> 00006 * and licensed under the LGPL. For more information, see 00007 * http://geographiclib.sourceforge.net/ 00008 **********************************************************************/ 00009 00010 #if !defined(GEOGRAPHICLIB_MGRS_HPP) 00011 #define GEOGRAPHICLIB_MGRS_HPP "$Id: MGRS.hpp 6785 2010-01-05 22:15:42Z karney $" 00012 00013 #include "GeographicLib/Constants.hpp" 00014 #include "GeographicLib/UTMUPS.hpp" 00015 #include <sstream> 00016 00017 namespace GeographicLib { 00018 00019 /** 00020 * \brief Convert between UTM/UPS and %MGRS 00021 * 00022 * MGRS is defined in Chapter 3 of 00023 * - J. W. Hager, L. L. Fry, S. S. Jacks, D. R. Hill, 00024 * <a href="http://earth-info.nga.mil/GandG/publications/tm8358.1/pdf/TM8358_1.pdf"> 00025 00026 * Datums, Ellipsoids, Grids, and Grid Reference Systems</a>, 00027 * Defense Mapping Agency, Technical Manual TM8358.1 (1990). 00028 * 00029 * This implementation has the following properties: 00030 * - The conversions are closed, i.e., output from Forward is legal input for 00031 * Reverse and vice versa. Conversion in both directions preserve the 00032 * UTM/UPS selection and the UTM zone. 00033 * - Forward followed by Reverse and vice versa is approximately the 00034 * identity. (This is affected in predictable ways by errors in 00035 * determining the latitude band and by loss of precision in the MGRS 00036 * coordinates.) 00037 * - All MGRS coordinates truncate to legal 100km blocks. All MGRS 00038 * coordinates with a legal 100km block prefix are legal (even though the 00039 * latitude band letter may now belong to a neighboring band). 00040 * - The range of UTM/UPS coordinates allowed for conversion to MGRS 00041 * coordinates is the maximum consistent with staying within the letter 00042 * ranges of the MGRS scheme. 00043 * 00044 * The <a href="http://www.nga.mil">NGA</a> software package 00045 * <a href="http://earth-info.nga.mil/GandG/geotrans/index.html">geotrans</a> 00046 * also provides conversions to and from MGRS. Version 2.4.2 (and earlier) 00047 * suffers from some drawbacks: 00048 * - Conversions to MGRS coordinate return the closest grid corner. This is 00049 * contrary to the normal standard of grid systems (which is to return the 00050 * coordinates of the enclosing square) and results in illegal MGRS 00051 * coordinates being returned 00052 * - Inconsistent rules are used to determine the whether a particular MGRS 00053 * coordinate is legal. A more systematic approach is taken here. 00054 * - The underlying projections are not very accurately implemented. 00055 **********************************************************************/ 00056 class MGRS { 00057 private: 00058 typedef Math::real real; 00059 // The smallest length s.t., 1.0e7 - eps < 1.0e7 (approx 1.9 nm) 00060 static const real eps; 00061 // The smallest angle s.t., 90 - eps < 90 (approx 50e-12 arcsec) 00062 static const real angeps; 00063 static const std::string hemispheres; 00064 static const std::string utmcols[3]; 00065 static const std::string utmrow; 00066 static const std::string upscols[4]; 00067 static const std::string upsrows[2]; 00068 static const std::string latband; 00069 static const std::string upsband; 00070 static const std::string digits; 00071 00072 static const int mineasting[4]; 00073 static const int maxeasting[4]; 00074 static const int minnorthing[4]; 00075 static const int maxnorthing[4]; 00076 enum { 00077 base = 10, 00078 // Top-level tiles are 10^5 m = 100km on a side 00079 tilelevel = 5, 00080 // Period of UTM row letters 00081 utmrowperiod = 20, 00082 // Row letters are shifted by 5 for even zones 00083 utmevenrowshift = 5, 00084 // Maximum precision is um 00085 maxprec = 5 + 6 00086 }; 00087 static void CheckCoords(bool utmp, bool& northp, real& x, real& y); 00088 static int lookup(const std::string& s, char c) throw() { 00089 std::string::size_type r = s.find(toupper(c)); 00090 return r == std::string::npos ? -1 : int(r); 00091 } 00092 template<typename T> static std::string str(T x) { 00093 std::ostringstream s; s << x; return s.str(); 00094 } 00095 static int UTMRow(int iband, int icol, int irow) throw(); 00096 00097 friend class UTMUPS; // UTMUPS::StandardZone calls LatitudeBand 00098 // Return latitude band number [-10, 10) for the give latitude (degrees). 00099 // The bands are reckoned in include their southern edges. 00100 static int LatitudeBand(real lat) throw() { 00101 int ilat = int(std::floor(lat)); 00102 return (std::max)(-10, (std::min)(9, (ilat + 80)/8 - 10)); 00103 } 00104 // UTMUPS access these enums 00105 enum { 00106 tile = 100000, // Size MGRS blocks 00107 minutmcol = 1, 00108 maxutmcol = 9, 00109 minutmSrow = 10, 00110 maxutmSrow = 100, // Also used for UTM S false northing 00111 minutmNrow = 0, // Also used for UTM N false northing 00112 maxutmNrow = 95, 00113 minupsSind = 8, // These 4 ind's apply to easting and northing 00114 maxupsSind = 32, 00115 minupsNind = 13, 00116 maxupsNind = 27, 00117 upseasting = 20, // Also used for UPS false northing 00118 utmeasting = 5, // UTM false easting 00119 // Difference between S hemisphere northing and N hemisphere northing 00120 utmNshift = (maxutmSrow - minutmNrow) * tile 00121 }; 00122 MGRS(); // Disable constructor 00123 00124 public: 00125 00126 /** 00127 * Convert UTM or UPS coordinate to an MGRS coordinate. \e zone and \e 00128 * northp give input zone (with \e zone = 0 indicating UPS) and hemisphere, 00129 * \e x and \e y are the easting and northing (meters). \e prec indicates 00130 * the desired precision with \e prec = 0 (the minimum) meaning 100 km, \e 00131 * prec = 5 meaning 1 m, and \e prec == 11 (the maximum) meaning 1 um. 00132 * 00133 * UTM eastings are allowed to be in the range [100 km, 900 km], northings 00134 * are allowed to be in in [0 km, 9500 km] for the northern hemisphere and 00135 * in [1000 km, 10000 km] for the southern hemisphere. (However UTM 00136 * northings can be continued across the equator. So the actual limits on 00137 * the northings are [-9000 km, 9500 km] for the "northern" hemisphere and 00138 * [1000 km, 19500 km] for the "southern" hemisphere.) 00139 * 00140 * UPS eastings/northings are allowed to be in the range [1300 km, 2700 km] 00141 * in the northern hemisphere and in [800 km, 3200 km] in the southern 00142 * hemisphere. 00143 * 00144 * The ranges are 100 km more restrictive that for the conversion between 00145 * geographic coordinates and UTM and UPS given by UTMUPS. These 00146 * restrictions are dictated by the allowed letters in MGRS coordinates. 00147 * The choice of 9500 km for the maximum northing for northern hemisphere 00148 * and of 1000 km as the minimum northing for southern hemisphere provide 00149 * at least 0.5 degree extension into standard UPS zones. The upper ends 00150 * of the ranges for the UPS coordinates is dictated by requiring symmetry 00151 * about the meridans 0E and 90E. 00152 * 00153 * All allowed UTM and UPS coordinates may now be converted to legal MGRS 00154 * coordinates with the proviso that eastings and northings on the upper 00155 * boundaries are silently reduced by about 4nm to place them \e within the 00156 * allowed range. (This includes reducing a southern hemisphere northing 00157 * of 10000km by 4nm so that it is placed in latitude band M.) The UTM or 00158 * UPS coordinates are truncated to requested precision to determine the 00159 * MGRS coordinate. Thus in UTM zone 38N, the square area with easting in 00160 * [444 km, 445 km) and northing in [3688 km, 3689 km) maps to MGRS 00161 * coordinate 38SMB4488 (at \e prec = 2, 1km), Khulani Sq., Baghdad. 00162 * 00163 * The UTM/UPS selection and the UTM zone is preserved in the conversion to 00164 * MGRS coordinate. Thus for \e zone > 0, the MGRS coordinate begins with 00165 * the zone number followed by one of [C–M] for the southern 00166 * hemisphere and [N–X] for the northern hemisphere. For \e zone = 00167 * 0, the MGRS coordinates begins with one of [AB] for the southern 00168 * hemisphere and [XY] for the northern hemisphere. 00169 * 00170 * The conversion to the MGRS is exact for prec in [0, 5] except that a 00171 * neighboring latitude band letter may be given if the point is within 5nm 00172 * of a band boundary. For prec in [6, 11], the conversion is accurate to 00173 * roundoff. 00174 * 00175 * Return the result via a reference argument to avoid the overhead of 00176 * allocating a potentially large number of small strings. If an error is 00177 * thrown, then \e mgrs is unchanged. 00178 **********************************************************************/ 00179 static void Forward(int zone, bool northp, real x, real y, 00180 int prec, std::string& mgrs); 00181 00182 /** 00183 * Convert UTM or UPS coordinates to an MGRS coordinate in case that 00184 * latitude is already known. The latitude is ignored for \e zone = 0 00185 * (UPS); otherwise the latitude is used to determine the latitude band and 00186 * this is checked for consistency using the same tests as Reverse. 00187 **********************************************************************/ 00188 static void Forward(int zone, bool northp, real x, real y, real lat, 00189 int prec, std::string& mgrs); 00190 00191 /** 00192 * Convert a MGRS coordinate to UTM or UPS coordinates returning zone \e 00193 * zone, hemisphere \e northp, easting \e x (meters), northing \e y 00194 * (meters) . Also return the precision of the MGRS string (see Forward). 00195 * If \e centerp = true (default), return center of the MGRS square, else 00196 * return SW (lower left) corner. 00197 * 00198 * All conversions from MGRS to UTM/UPS are permitted provided the MGRS 00199 * coordinate is a possible result of a conversion in the other direction. 00200 * (The leading 0 may be dropped from an input MGRS coordinate for UTM 00201 * zones 1–9.) In addition, MGRS coordinates with a neighboring 00202 * latitude band letter are permitted provided that some portion of the 00203 * 100km block is within the given latitude band. Thus 00204 * - 38VLS and 38WLS are allowed (latitude 64N intersects the square 00205 * 38[VW]LS); but 38VMS is not permitted (all of 38VMS is north of 64N) 00206 * - 38MPE and 38NPF are permitted (they straddle the equator); but 38NPE 00207 * and 38MPF are not permitted (the equator does not intersect either 00208 * block). 00209 * - Similarly ZAB and YZB are permitted (they straddle the prime 00210 * meridian); but YAB and ZZB are not (the prime meridian does not 00211 * intersect either block). 00212 * 00213 * The UTM/UPS selection and the UTM zone is preserved in the conversion 00214 * from MGRS coordinate. The conversion is exact for prec in [0, 5]. With 00215 * centerp = true the conversion from MGRS to geographic and back is 00216 * stable. This is not assured if \e centerp = false. 00217 * 00218 * If an error is thrown, then the arguments are unchanged. 00219 **********************************************************************/ 00220 static void Reverse(const std::string& mgrs, 00221 int& zone, bool& northp, real& x, real& y, 00222 int& prec, bool centerp = true); 00223 00224 /** 00225 * The major radius of the ellipsoid (meters). This is the value for the 00226 * WGS84 ellipsoid because MGRS coordinates are based on this ellipsoid. 00227 **********************************************************************/ 00228 static Math::real MajorRadius() throw() { return UTMUPS::MajorRadius(); } 00229 00230 /** 00231 * The inverse flattening of the ellipsoid. This is the value for the 00232 * WGS84 ellipsoid because MGRS coordinates are based on this ellipsoid. 00233 **********************************************************************/ 00234 static Math::real InverseFlattening() throw() 00235 { return UTMUPS::InverseFlattening(); } 00236 }; 00237 00238 } // namespace GeographicLib 00239 #endif