MGRS.hpp

Go to the documentation of this file.
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 6911 2010-12-09 23:13:55Z 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    * - All the transformations are implemented as static methods in the MGRS
00044    *   class.
00045    *
00046    * The <a href="http://www.nga.mil">NGA</a> software package
00047    * <a href="http://earth-info.nga.mil/GandG/geotrans/index.html">geotrans</a>
00048    * also provides conversions to and from MGRS.  Version 3.0 (and earlier)
00049    * suffers from some drawbacks:
00050    * - Inconsistent rules are used to determine the whether a particular MGRS
00051    *   coordinate is legal.  A more systematic approach is taken here.
00052    * - The underlying projections are not very accurately implemented.
00053    **********************************************************************/
00054   class MGRS {
00055   private:
00056     typedef Math::real real;
00057     // The smallest length s.t., 1.0e7 - eps < 1.0e7 (approx 1.9 nm)
00058     static const real eps;
00059     // The smallest angle s.t., 90 - eps < 90 (approx 50e-12 arcsec)
00060     static const real angeps;
00061     static const std::string hemispheres;
00062     static const std::string utmcols[3];
00063     static const std::string utmrow;
00064     static const std::string upscols[4];
00065     static const std::string upsrows[2];
00066     static const std::string latband;
00067     static const std::string upsband;
00068     static const std::string digits;
00069 
00070     static const int mineasting[4];
00071     static const int maxeasting[4];
00072     static const int minnorthing[4];
00073     static const int maxnorthing[4];
00074     enum {
00075       base = 10,
00076       // Top-level tiles are 10^5 m = 100km on a side
00077       tilelevel = 5,
00078       // Period of UTM row letters
00079       utmrowperiod = 20,
00080       // Row letters are shifted by 5 for even zones
00081       utmevenrowshift = 5,
00082       // Maximum precision is um
00083       maxprec = 5 + 6,
00084     };
00085     static void CheckCoords(bool utmp, bool& northp, real& x, real& y);
00086     static int lookup(const std::string& s, char c) throw() {
00087       std::string::size_type r = s.find(toupper(c));
00088       return r == std::string::npos ? -1 : int(r);
00089     }
00090     template<typename T> static std::string str(T x) {
00091       std::ostringstream s; s << x; return s.str();
00092     }
00093     static int UTMRow(int iband, int icol, int irow) throw();
00094 
00095     friend class UTMUPS;        // UTMUPS::StandardZone calls LatitudeBand
00096     // Return latitude band number [-10, 10) for the give latitude (degrees).
00097     // The bands are reckoned in include their southern edges.
00098     static int LatitudeBand(real lat) throw() {
00099       int ilat = int(std::floor(lat));
00100       return (std::max)(-10, (std::min)(9, (ilat + 80)/8 - 10));
00101     }
00102     // UTMUPS access these enums
00103     enum {
00104       tile = 100000,            // Size MGRS blocks
00105       minutmcol = 1,
00106       maxutmcol = 9,
00107       minutmSrow = 10,
00108       maxutmSrow = 100,         // Also used for UTM S false northing
00109       minutmNrow = 0,           // Also used for UTM N false northing
00110       maxutmNrow = 95,
00111       minupsSind = 8,           // These 4 ind's apply to easting and northing
00112       maxupsSind = 32,
00113       minupsNind = 13,
00114       maxupsNind = 27,
00115       upseasting = 20,          // Also used for UPS false northing
00116       utmeasting = 5,           // UTM false easting
00117       // Difference between S hemisphere northing and N hemisphere northing
00118       utmNshift = (maxutmSrow - minutmNrow) * tile
00119     };
00120     MGRS();                     // Disable constructor
00121 
00122   public:
00123 
00124     /**
00125      * Convert UTM or UPS coordinate to an MGRS coordinate.
00126      *
00127      * @param[in] zone UTM zone (zero means UPS).
00128      * @param[in] northp hemisphere (true means north, false means south).
00129      * @param[in] x (meters).
00130      * @param[in] y (meters).
00131      * @param[in] prec precision relative to 100 km.
00132      * @param[out] mgrs MGRS string.
00133      *
00134      * \e prec specifies the precision of the MSGRS string as follows:
00135      * - prec = 0 (min), 100km
00136      * - prec = 1, 10km
00137      * - prec = 2, 1km
00138      * - prec = 3, 100m
00139      * - prec = 4, 10m
00140      * - prec = 5, 1m
00141      * - prec = 6, 0.1m
00142      * - prec = 11 (max), 1um
00143      *
00144      * UTM eastings are allowed to be in the range [100 km, 900 km], northings
00145      * are allowed to be in in [0 km, 9500 km] for the northern hemisphere and
00146      * in [1000 km, 10000 km] for the southern hemisphere.  (However UTM
00147      * northings can be continued across the equator.  So the actual limits on
00148      * the northings are [-9000 km, 9500 km] for the "northern" hemisphere and
00149      * [1000 km, 19500 km] for the "southern" hemisphere.)
00150      *
00151      * UPS eastings/northings are allowed to be in the range [1300 km, 2700 km]
00152      * in the northern hemisphere and in [800 km, 3200 km] in the southern
00153      * hemisphere.
00154      *
00155      * The ranges are 100 km more restrictive that for the conversion between
00156      * geographic coordinates and UTM and UPS given by UTMUPS.  These
00157      * restrictions are dictated by the allowed letters in MGRS coordinates.
00158      * The choice of 9500 km for the maximum northing for northern hemisphere
00159      * and of 1000 km as the minimum northing for southern hemisphere provide
00160      * at least 0.5 degree extension into standard UPS zones.  The upper ends
00161      * of the ranges for the UPS coordinates is dictated by requiring symmetry
00162      * about the meridans 0E and 90E.
00163      *
00164      * All allowed UTM and UPS coordinates may now be converted to legal MGRS
00165      * coordinates with the proviso that eastings and northings on the upper
00166      * boundaries are silently reduced by about 4nm to place them \e within the
00167      * allowed range.  (This includes reducing a southern hemisphere northing
00168      * of 10000km by 4nm so that it is placed in latitude band M.)  The UTM or
00169      * UPS coordinates are truncated to requested precision to determine the
00170      * MGRS coordinate.  Thus in UTM zone 38N, the square area with easting in
00171      * [444 km, 445 km) and northing in [3688 km, 3689 km) maps to MGRS
00172      * coordinate 38SMB4488 (at \e prec = 2, 1km), Khulani Sq., Baghdad.
00173      *
00174      * The UTM/UPS selection and the UTM zone is preserved in the conversion to
00175      * MGRS coordinate.  Thus for \e zone > 0, the MGRS coordinate begins with
00176      * the zone number followed by one of [C&ndash;M] for the southern
00177      * hemisphere and [N&ndash;X] for the northern hemisphere.  For \e zone =
00178      * 0, the MGRS coordinates begins with one of [AB] for the southern
00179      * hemisphere and [XY] for the northern hemisphere.
00180      *
00181      * The conversion to the MGRS is exact for prec in [0, 5] except that a
00182      * neighboring latitude band letter may be given if the point is within 5nm
00183      * of a band boundary.  For prec in [6, 11], the conversion is accurate to
00184      * roundoff.
00185      *
00186      * If \e x or \e y is NaN or if \e zone is UTMUPS::INVALID, the returned
00187      * MGRS string is "INVALID".
00188      *
00189      * Return the result via a reference argument to avoid the overhead of
00190      * allocating a potentially large number of small strings.  If an error is
00191      * thrown, then \e mgrs is unchanged.
00192      **********************************************************************/
00193     static void Forward(int zone, bool northp, real x, real y,
00194                         int prec, std::string& mgrs);
00195 
00196     /**
00197      * Convert UTM or UPS coordinate to an MGRS coordinate when the latitude is
00198      * known.
00199      *
00200      * @param[in] zone UTM zone (zero means UPS).
00201      * @param[in] northp hemisphere (true means north, false means south).
00202      * @param[in] x (meters).
00203      * @param[in] y (meters).
00204      * @param[in] lat latitude (degrees).
00205      * @param[in] prec precision relative to 100 km.
00206      * @param[out] mgrs MGRS string.
00207      *
00208      * The latitude is ignored for \e zone = 0 (UPS); otherwise the latitude is
00209      * used to determine the latitude band and this is checked for consistency
00210      * using the same tests as Reverse.
00211      **********************************************************************/
00212     static void Forward(int zone, bool northp, real x, real y, real lat,
00213                         int prec, std::string& mgrs);
00214 
00215     /**
00216      * Convert a MGRS coordinate to UTM or UPS coordinates.
00217      *
00218      * @param[in] mgrs MGRS string.
00219      * @param[out] zone UTM zone (zero means UPS).
00220      * @param[out] northp hemisphere (true means north, false means south).
00221      * @param[out] x (meters).
00222      * @param[out] y (meters).
00223      * @param[out] prec precision relative to 100 km.
00224      * @param[in] centerp if true (default), return center of the MGRS square,
00225      *   else return SW (lower left) corner.
00226      *
00227      * All conversions from MGRS to UTM/UPS are permitted provided the MGRS
00228      * coordinate is a possible result of a conversion in the other direction.
00229      * (The leading 0 may be dropped from an input MGRS coordinate for UTM
00230      * zones 1&ndash;9.)  In addition, MGRS coordinates with a neighboring
00231      * latitude band letter are permitted provided that some portion of the
00232      * 100km block is within the given latitude band.  Thus
00233      *   - 38VLS and 38WLS are allowed (latitude 64N intersects the square
00234      *     38[VW]LS); but 38VMS is not permitted (all of 38VMS is north of 64N)
00235      *   - 38MPE and 38NPF are permitted (they straddle the equator); but 38NPE
00236      *     and 38MPF are not permitted (the equator does not intersect either
00237      *     block).
00238      *   - Similarly ZAB and YZB are permitted (they straddle the prime
00239      *     meridian); but YAB and ZZB are not (the prime meridian does not
00240      *     intersect either block).
00241      *
00242      * The UTM/UPS selection and the UTM zone is preserved in the conversion
00243      * from MGRS coordinate.  The conversion is exact for prec in [0, 5].  With
00244      * centerp = true the conversion from MGRS to geographic and back is
00245      * stable.  This is not assured if \e centerp = false.
00246      *
00247      * If an error is thrown, then the arguments are unchanged.
00248      **********************************************************************/
00249     static void Reverse(const std::string& mgrs,
00250                         int& zone, bool& northp, real& x, real& y,
00251                         int& prec, bool centerp = true);
00252 
00253     /** \name Inspector functions
00254      **********************************************************************/
00255     ///@{
00256     /**
00257      * @return \e a the equatorial radius of the WGS84 ellipsoid (meters).
00258      *
00259      * (The WGS84 value is returned because the UTM and UPS projections are
00260      * based on this ellipsoid.)
00261      **********************************************************************/
00262     static Math::real MajorRadius() throw() { return UTMUPS::MajorRadius(); }
00263 
00264     /**
00265      * @return \e r the inverse flattening of the WGS84 ellipsoid.
00266      *
00267      * (The WGS84 value is returned because the UTM and UPS projections are
00268      * based on this ellipsoid.)
00269      **********************************************************************/
00270     static Math::real InverseFlattening() throw()
00271     { return UTMUPS::InverseFlattening(); }
00272     ///@}
00273   };
00274 
00275 } // namespace GeographicLib
00276 #endif