GeographicLib  1.21
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-2011) <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 "$Id: 80e08da6eca9d9cf92c5adad148c64302df2573d $"
00012 
00013 #include <sstream>
00014 #include <GeographicLib/Constants.hpp>
00015 #include <GeographicLib/UTMUPS.hpp>
00016 
00017 #if defined(_MSC_VER)
00018 // Squelch warnings about dll vs string
00019 #pragma warning (push)
00020 #pragma warning (disable: 4251)
00021 #endif
00022 
00023 namespace GeographicLib {
00024 
00025   /**
00026    * \brief Convert between UTM/UPS and %MGRS
00027    *
00028    * MGRS is defined in Chapter 3 of
00029    * - J. W. Hager, L. L. Fry, S. S. Jacks, D. R. Hill,
00030    *   <a href="http://earth-info.nga.mil/GandG/publications/tm8358.1/pdf/TM8358_1.pdf">
00031 
00032    *   Datums, Ellipsoids, Grids, and Grid Reference Systems</a>,
00033    *   Defense Mapping Agency, Technical Manual TM8358.1 (1990).
00034    *
00035    * This implementation has the following properties:
00036    * - The conversions are closed, i.e., output from Forward is legal input for
00037    *   Reverse and vice versa.  Conversion in both directions preserve the
00038    *   UTM/UPS selection and the UTM zone.
00039    * - Forward followed by Reverse and vice versa is approximately the
00040    *   identity.  (This is affected in predictable ways by errors in
00041    *   determining the latitude band and by loss of precision in the MGRS
00042    *   coordinates.)
00043    * - All MGRS coordinates truncate to legal 100 km blocks.  All MGRS
00044    *   coordinates with a legal 100 km block prefix are legal (even though the
00045    *   latitude band letter may now belong to a neighboring band).
00046    * - The range of UTM/UPS coordinates allowed for conversion to MGRS
00047    *   coordinates is the maximum consistent with staying within the letter
00048    *   ranges of the MGRS scheme.
00049    * - All the transformations are implemented as static methods in the MGRS
00050    *   class.
00051    *
00052    * The <a href="http://www.nga.mil">NGA</a> software package
00053    * <a href="http://earth-info.nga.mil/GandG/geotrans/index.html">geotrans</a>
00054    * also provides conversions to and from MGRS.  Version 3.0 (and earlier)
00055    * suffers from some drawbacks:
00056    * - Inconsistent rules are used to determine the whether a particular MGRS
00057    *   coordinate is legal.  A more systematic approach is taken here.
00058    * - The underlying projections are not very accurately implemented.
00059    *
00060    * Example of use:
00061    * \include example-MGRS.cpp
00062    **********************************************************************/
00063   class GEOGRAPHIC_EXPORT MGRS {
00064   private:
00065     typedef Math::real real;
00066     // The smallest length s.t., 1.0e7 - eps_ < 1.0e7 (approx 1.9 nm)
00067     static const real eps_;
00068     // The smallest angle s.t., 90 - eps_ < 90 (approx 50e-12 arcsec)
00069     static const real angeps_;
00070     static const std::string hemispheres_;
00071     static const std::string utmcols_[3];
00072     static const std::string utmrow_;
00073     static const std::string upscols_[4];
00074     static const std::string upsrows_[2];
00075     static const std::string latband_;
00076     static const std::string upsband_;
00077     static const std::string digits_;
00078 
00079     static const int mineasting_[4];
00080     static const int maxeasting_[4];
00081     static const int minnorthing_[4];
00082     static const int maxnorthing_[4];
00083     enum {
00084       base_ = 10,
00085       // Top-level tiles are 10^5 m = 100 km on a side
00086       tilelevel_ = 5,
00087       // Period of UTM row letters
00088       utmrowperiod_ = 20,
00089       // Row letters are shifted by 5 for even zones
00090       utmevenrowshift_ = 5,
00091       // Maximum precision is um
00092       maxprec_ = 5 + 6,
00093     };
00094     static void CheckCoords(bool utmp, bool& northp, real& x, real& y);
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.
00128      *
00129      * @param[in] zone UTM zone (zero means UPS).
00130      * @param[in] northp hemisphere (true means north, false means south).
00131      * @param[in] x easting of point (meters).
00132      * @param[in] y northing of point (meters).
00133      * @param[in] prec precision relative to 100 km.
00134      * @param[out] mgrs MGRS string.
00135      *
00136      * \e prec specifies the precision of the MGRS string as follows:
00137      * - prec = 0 (min), 100 km
00138      * - prec = 1, 10 km
00139      * - prec = 2, 1 km
00140      * - prec = 3, 100 m
00141      * - prec = 4, 10 m
00142      * - prec = 5, 1 m
00143      * - prec = 6, 0.1 m
00144      * - prec = 11 (max), 1 um
00145      *
00146      * UTM eastings are allowed to be in the range [100 km, 900 km], northings
00147      * are allowed to be in in [0 km, 9500 km] for the northern hemisphere and
00148      * in [1000 km, 10000 km] for the southern hemisphere.  (However UTM
00149      * northings can be continued across the equator.  So the actual limits on
00150      * the northings are [-9000 km, 9500 km] for the "northern" hemisphere and
00151      * [1000 km, 19500 km] for the "southern" hemisphere.)
00152      *
00153      * UPS eastings/northings are allowed to be in the range [1300 km, 2700 km]
00154      * in the northern hemisphere and in [800 km, 3200 km] in the southern
00155      * hemisphere.
00156      *
00157      * The ranges are 100 km more restrictive that for the conversion between
00158      * geographic coordinates and UTM and UPS given by UTMUPS.  These
00159      * restrictions are dictated by the allowed letters in MGRS coordinates.
00160      * The choice of 9500 km for the maximum northing for northern hemisphere
00161      * and of 1000 km as the minimum northing for southern hemisphere provide
00162      * at least 0.5 degree extension into standard UPS zones.  The upper ends
00163      * of the ranges for the UPS coordinates is dictated by requiring symmetry
00164      * about the meridians 0E and 90E.
00165      *
00166      * All allowed UTM and UPS coordinates may now be converted to legal MGRS
00167      * coordinates with the proviso that eastings and northings on the upper
00168      * boundaries are silently reduced by about 4 nm (4 nanometers) to place
00169      * them \e within the allowed range.  (This includes reducing a southern
00170      * hemisphere northing of 10000 km by 4 nm so that it is placed in latitude
00171      * band M.)  The UTM or UPS coordinates are truncated to requested
00172      * precision to determine the MGRS coordinate.  Thus in UTM zone 38N, the
00173      * square area with easting in [444 km, 445 km) and northing in [3688 km,
00174      * 3689 km) maps to MGRS coordinate 38SMB4488 (at \e prec = 2, 1 km),
00175      * Khulani Sq., Baghdad.
00176      *
00177      * The UTM/UPS selection and the UTM zone is preserved in the conversion to
00178      * MGRS coordinate.  Thus for \e zone > 0, the MGRS coordinate begins with
00179      * the zone number followed by one of [C&ndash;M] for the southern
00180      * hemisphere and [N&ndash;X] for the northern hemisphere.  For \e zone =
00181      * 0, the MGRS coordinates begins with one of [AB] for the southern
00182      * hemisphere and [XY] for the northern hemisphere.
00183      *
00184      * The conversion to the MGRS is exact for prec in [0, 5] except that a
00185      * neighboring latitude band letter may be given if the point is within 5nm
00186      * of a band boundary.  For prec in [6, 11], the conversion is accurate to
00187      * roundoff.
00188      *
00189      * If \e x or \e y is NaN or if \e zone is UTMUPS::INVALID, the returned
00190      * MGRS string is "INVALID".
00191      *
00192      * Return the result via a reference argument to avoid the overhead of
00193      * allocating a potentially large number of small strings.  If an error is
00194      * thrown, then \e mgrs is unchanged.
00195      **********************************************************************/
00196     static void Forward(int zone, bool northp, real x, real y,
00197                         int prec, std::string& mgrs);
00198 
00199     /**
00200      * Convert UTM or UPS coordinate to an MGRS coordinate when the latitude is
00201      * known.
00202      *
00203      * @param[in] zone UTM zone (zero means UPS).
00204      * @param[in] northp hemisphere (true means north, false means south).
00205      * @param[in] x easting of point (meters).
00206      * @param[in] y northing of point (meters).
00207      * @param[in] lat latitude (degrees).
00208      * @param[in] prec precision relative to 100 km.
00209      * @param[out] mgrs MGRS string.
00210      *
00211      * The latitude is ignored for \e zone = 0 (UPS); otherwise the latitude is
00212      * used to determine the latitude band and this is checked for consistency
00213      * using the same tests as Reverse.
00214      **********************************************************************/
00215     static void Forward(int zone, bool northp, real x, real y, real lat,
00216                         int prec, std::string& mgrs);
00217 
00218     /**
00219      * Convert a MGRS coordinate to UTM or UPS coordinates.
00220      *
00221      * @param[in] mgrs MGRS string.
00222      * @param[out] zone UTM zone (zero means UPS).
00223      * @param[out] northp hemisphere (true means north, false means south).
00224      * @param[out] x easting of point (meters).
00225      * @param[out] y northing of point (meters).
00226      * @param[out] prec precision relative to 100 km.
00227      * @param[in] centerp if true (default), return center of the MGRS square,
00228      *   else return SW (lower left) corner.
00229      *
00230      * All conversions from MGRS to UTM/UPS are permitted provided the MGRS
00231      * coordinate is a possible result of a conversion in the other direction.
00232      * (The leading 0 may be dropped from an input MGRS coordinate for UTM
00233      * zones 1&ndash;9.)  In addition, MGRS coordinates with a neighboring
00234      * latitude band letter are permitted provided that some portion of the
00235      * 100 km block is within the given latitude band.  Thus
00236      *   - 38VLS and 38WLS are allowed (latitude 64N intersects the square
00237      *     38[VW]LS); but 38VMS is not permitted (all of 38VMS is north of 64N)
00238      *   - 38MPE and 38NPF are permitted (they straddle the equator); but 38NPE
00239      *     and 38MPF are not permitted (the equator does not intersect either
00240      *     block).
00241      *   - Similarly ZAB and YZB are permitted (they straddle the prime
00242      *     meridian); but YAB and ZZB are not (the prime meridian does not
00243      *     intersect either block).
00244      *
00245      * The UTM/UPS selection and the UTM zone is preserved in the conversion
00246      * from MGRS coordinate.  The conversion is exact for prec in [0, 5].  With
00247      * centerp = true the conversion from MGRS to geographic and back is
00248      * stable.  This is not assured if \e centerp = false.
00249      *
00250      * If an error is thrown, then the arguments are unchanged.
00251      **********************************************************************/
00252     static void Reverse(const std::string& mgrs,
00253                         int& zone, bool& northp, real& x, real& y,
00254                         int& prec, bool centerp = true);
00255 
00256     /** \name Inspector functions
00257      **********************************************************************/
00258     ///@{
00259     /**
00260      * @return \e a the equatorial radius of the WGS84 ellipsoid (meters).
00261      *
00262      * (The WGS84 value is returned because the UTM and UPS projections are
00263      * based on this ellipsoid.)
00264      **********************************************************************/
00265     static Math::real MajorRadius() throw() { return UTMUPS::MajorRadius(); }
00266 
00267     /**
00268      * @return \e f the flattening of the WGS84 ellipsoid.
00269      *
00270      * (The WGS84 value is returned because the UTM and UPS projections are
00271      * based on this ellipsoid.)
00272      **********************************************************************/
00273     static Math::real Flattening() throw() { return UTMUPS::Flattening(); }
00274     ///@}
00275 
00276     /// \cond SKIP
00277     /**
00278      * <b>DEPRECATED</b>
00279      * @return \e r the inverse flattening of the WGS84 ellipsoid.
00280      **********************************************************************/
00281     static Math::real InverseFlattening() throw()
00282     { return UTMUPS::InverseFlattening(); }
00283     /// \endcond
00284   };
00285 
00286 } // namespace GeographicLib
00287 
00288 #if defined(_MSC_VER)
00289 #pragma warning (pop)
00290 #endif
00291 
00292 #endif  // GEOGRAPHICLIB_MGRS_HPP