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) <charles@karney.com>
00006  * and licensed under the LGPL.
00007  **********************************************************************/
00008 
00009 #if !defined(MGRS_HPP)
00010 #define MGRS_HPP "$Id: MGRS.hpp 6559 2009-02-28 16:49:53Z ckarney $"
00011 
00012 #include <cmath>
00013 #include <algorithm>
00014 #include <string>
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     // The smallest length s.t., 1.0e7 - eps < 1.0e7 (approx 1.9 nm)
00059     static const double eps;
00060     // The smallest angle s.t., 90 - eps < 90 (approx 50e-12 arcsec)
00061     static const double angeps;
00062     static const std::string hemispheres;
00063     static const std::string utmcols[3];
00064     static const std::string utmrow;
00065     static const std::string upscols[4];
00066     static const std::string upsrows[2];
00067     static const std::string latband;
00068     static const std::string upsband;
00069     static const std::string digits;
00070 
00071     static const int mineasting[4];
00072     static const int maxeasting[4];
00073     static const int minnorthing[4];
00074     static const int maxnorthing[4];
00075     enum {
00076       base = 10,
00077       // Top-level tiles are 10^5 m = 100km on a side
00078       tilelevel = 5,
00079       // Period of UTM row letters
00080       utmrowperiod = 20,
00081       // Row letters are shifted by 5 for even zones
00082       utmevenrowshift = 5,
00083       // Maximum precision is um
00084       maxprec = 5 + 6
00085     };
00086     static void CheckCoords(bool utmp, bool& northp, double& x, double& y);
00087     static int lookup(const std::string& s, char c) throw() {
00088       std::string::size_type r = s.find(toupper(c));
00089       return r == std::string::npos ? -1 : int(r);
00090     }
00091     template<typename T> static std::string str(T x) {
00092       std::ostringstream s; s << x; return s.str();
00093     }
00094     static int UTMRow(int iband, int icol, int irow) throw();
00095 
00096     friend class UTMUPS;        // UTMUPS::StandardZone calls LatitudeBand
00097     // Return latitude band number [-10, 10) for the give latitude (degrees).
00098     // The bands are reckoned in include their southern edges.
00099     static int LatitudeBand(double lat) throw() {
00100       int ilat = int(std::floor(lat));
00101       return (std::max)(-10, (std::min)(9, (ilat + 80)/8 - 10));
00102     }
00103     // These are protected also so that UTMUPS can access them.
00104     friend class GeoCoords;     // GeoCoords accesses utmNshift
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   public:
00123 
00124     /**
00125      * Convert UTM or UPS coordinate to an MGRS coordinate.  \e zone and \e
00126      * northp give input zone (with \e zone = 0 indicating UPS) and hemisphere,
00127      * \e x and \e y are the easting and northing (meters).  \e prec indicates
00128      * the desired precision with \e prec = 0 (the minimum) meaning 100 km, \e
00129      * prec = 5 meaning 1 m, and \e prec == 11 (the maximum) meaning 1 um.
00130      *
00131      * UTM eastings are allowed to be in the range [100 km, 900 km], northings
00132      * are allowed to be in in [0 km, 9500 km] for the northern hemisphere and
00133      * in [1000 km, 10000 km] for the southern hemisphere.  (However UTM
00134      * northings can be continued across the equator.  So the actual limits on
00135      * the northings are [-9000 km, 9500 km] for the "northern" hemisphere and
00136      * [1000 km, 19500 km] for the "southern" hemisphere.)
00137      *
00138      * UPS eastings/northings are allowed to be in the range [1300 km, 2700 km]
00139      * in the northern hemisphere and in [800 km, 3200 km] in the southern
00140      * hemisphere.
00141      *
00142      * The ranges are 100 km more restrictive that for the conversion between
00143      * geographic coordinates and UTM and UPS given by UTMUPS.  These
00144      * restrictions are dictated by the allowed letters in MGRS coordinates.
00145      * The choice of 9500 km for the maximum northing for northern hemisphere
00146      * and of 1000 km as the minimum northing for southern hemisphere provide
00147      * at least 0.5 degree extension into standard UPS zones.  The upper ends
00148      * of the ranges for the UPS coordinates is dictated by requiring symmetry
00149      * about the meridans 0E and 90E.
00150      *
00151      * All allowed UTM and UPS coordinates may now be converted to legal MGRS
00152      * coordinates with the proviso that eastings and northings on the upper
00153      * boundaries are silently reduced by about 4nm to place them \e within the
00154      * allowed range.  (This includes reducing a southern hemisphere northing
00155      * of 10000km by 4nm so that it is placed in latitude band M.)  The UTM or
00156      * UPS coordinates are truncated to requested precision to determine the
00157      * MGRS coordinate.  Thus in UTM zone 38N, the square area with easting in
00158      * [444 km, 445 km) and northing in [3688 km, 3689 km) maps to MGRS
00159      * coordinate 38SMB4488 (at \e prec = 2, 1km), Kulani Sq., Baghdad.
00160      *
00161      * The UTM/UPS selection and the UTM zone is preserved in the conversion to
00162      * MGRS coordinate.  Thus for \e zone > 0, the MGRS coordinate begins with
00163      * the zone number followed by one of [C&ndash;M] for the southern
00164      * hemisphere and [N&ndash;X] for the northern hemisphere.  For \e zone =
00165      * 0, the MGRS coordinates begins with one of [AB] for the southern
00166      * hemisphere and [XY] for the northern hemisphere.
00167      *
00168      * The conversion to the MGRS is exact for prec in [0, 5] except that a
00169      * neighboring latitude band letter may be given if the point is within 5nm
00170      * of a band boundary.  For prec in [6, 11], the conversion is accurate to
00171      * roundoff.
00172      *
00173      **********************************************************************/
00174     static void Forward(int zone, bool northp, double x, double y,
00175                         int prec, std::string& mgrs);
00176 
00177     /**
00178      * Convert UTM or UPS coordinates to an MGRS coordinate in case that
00179      * latitude is already known.  The latitude is ignored for \e zone = 0
00180      * (UPS); otherwise the latitude is used to determine the latitude band and
00181      * this is checked for consistency using the same tests as Reverse.
00182      **********************************************************************/
00183     static void Forward(int zone, bool northp, double x, double y, double lat,
00184                         int prec, std::string& mgrs);
00185 
00186     /**
00187      * Convert a MGRS coordinate to UTM or UPS coordinates returning zone \e
00188      * zone, hemisphere \e northp, easting \e x (meters), northing \e y
00189      * (meters) .  Also return the precision of the MGRS string (see Forward).
00190      * If \e centerp = true (default), return center of the MGRS square, else
00191      * return SW (lower left) corner.
00192      *
00193      * All conversions from MGRS to UTM/UPS are permitted provided the MGRS
00194      * coordinate is a possible result of a conversion in the other direction.
00195      * (The leading 0 may be dropped from an input MGRS coordinate for UTM
00196      * zones 1&ndash;9.)  In addition, MGRS coordinates with a neighboring
00197      * latitude band letter are permitted provided that some portion of the
00198      * 100km block is within the given latitude band.  Thus
00199      *   - 38VLS and 38WLS are allowed (latitude 64N intersects the square
00200      *     38[VW]LS); but 38VMS is not permitted (all of 38VMS is north of 64N)
00201      *   - 38MPE and 38NPF are permitted (they straddle the equator); but 38NPE
00202      *     and 38MPF are not permitted (the equator does not intersect either
00203      *     block).
00204      *   - Similarly ZAB and YZB are permitted (they straddle the prime
00205      *     meridian); but YAB and ZZB are not (the prime meridian does not
00206      *     intersect either block).
00207      *
00208      * The UTM/UPS selection and the UTM zone is preserved in the conversion
00209      * from MGRS coordinate.  The conversion is exact for prec in [0, 5].  With
00210      * centerp = true the conversion from MGRS to geographic and back is
00211      * stable.  This is not assured if \e centerp = false.
00212      **********************************************************************/
00213     static void Reverse(const std::string& mgrs,
00214                         int& zone, bool& northp, double& x, double& y,
00215                         int& prec, bool centerp = true);
00216 
00217   };
00218 
00219 } // namespace GeographicLib
00220 #endif