GeographicLib  1.21
UTMUPS.hpp
Go to the documentation of this file.
00001 /**
00002  * \file UTMUPS.hpp
00003  * \brief Header for GeographicLib::UTMUPS 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_UTMUPS_HPP)
00011 #define GEOGRAPHICLIB_UTMUPS_HPP \
00012   "$Id: a529ed8aeaeffb02994254bbc7eb1209aa41b9ca $"
00013 
00014 #include <sstream>
00015 #include <GeographicLib/Constants.hpp>
00016 
00017 namespace GeographicLib {
00018 
00019   /**
00020    * \brief Convert between Geographic coordinates and UTM/UPS
00021    *
00022    * UTM and UPS are defined
00023    * - J. W. Hager, J. F. Behensky, and B. W. Drew,
00024    *   <a href="http://earth-info.nga.mil/GandG/publications/tm8358.2/TM8358_2.pdf">
00025    *   The Universal Grids: Universal Transverse Mercator (UTM) and Universal
00026    *   Polar Stereographic (UPS)</a>, Defense Mapping Agency, Technical Manual
00027    *   TM8358.2 (1989).
00028    * .
00029    * Section 2-3 defines UTM and section 3-2.4 defines UPS.  This document also
00030    * includes approximate algorithms for the computation of the underlying
00031    * transverse Mercator and polar stereographic projections.  Here we
00032    * substitute much more accurate algorithms given by
00033    * GeographicLib:TransverseMercator and GeographicLib:PolarStereographic.
00034    *
00035    * In this implementation, the conversions are closed, i.e., output from
00036    * Forward is legal input for Reverse and vice versa.  The error is about 5nm
00037    * in each direction.  However, the conversion from legal UTM/UPS coordinates
00038    * to geographic coordinates and back might throw an error if the initial
00039    * point is within 5nm of the edge of the allowed range for the UTM/UPS
00040    * coordinates.
00041    *
00042    * The simplest way to guarantee the closed property is to define allowed
00043    * ranges for the eastings and northings for UTM and UPS coordinates.  The
00044    * UTM boundaries are the same for all zones.  (The only place the
00045    * exceptional nature of the zone boundaries is evident is when converting to
00046    * UTM/UPS coordinates requesting the standard zone.)  The MGRS lettering
00047    * scheme imposes natural limits on UTM/UPS coordinates which may be
00048    * converted into MGRS coordinates.  For the conversion to/from geographic
00049    * coordinates these ranges have been extended by 100km in order to provide a
00050    * generous overlap between UTM and UPS and between UTM zones.
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 UTM and UPS.  Version 2.4.2 (and
00055    * earlier) suffers from some drawbacks:
00056    * - Inconsistent rules are used to determine the whether a particular UTM or
00057    *   UPS 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-UTMUPS.cpp
00062    **********************************************************************/
00063   class GEOGRAPHIC_EXPORT UTMUPS {
00064   private:
00065     typedef Math::real real;
00066     static const real falseeasting_[4];
00067     static const real falsenorthing_[4];
00068     static const real mineasting_[4];
00069     static const real maxeasting_[4];
00070     static const real minnorthing_[4];
00071     static const real maxnorthing_[4];
00072     static real CentralMeridian(int zone) throw()
00073     { return real(6 * zone - 183); }
00074     static void CheckLatLon(real lat, real lon);
00075     // Throw an error if easting or northing are outside standard ranges.  If
00076     // throwp = false, return bool instead.
00077     static bool CheckCoords(bool utmp, bool northp, real x, real y,
00078                             bool msgrlimits = false, bool throwp = true);
00079     UTMUPS();                   // Disable constructor
00080 
00081   public:
00082 
00083     /**
00084      * In this class we bring together the UTM and UPS coordinates systems.
00085      * The UTM divides the earth between latitudes -80 and 84 into 60 zones
00086      * numbered 1 thru 60.  Zone assign zone number 0 to the UPS regions,
00087      * covering the two poles.  Within UTMUPS, non-negative zone numbers refer
00088      * to one of the "physical" zones, 0 for UPS and [1, 60] for UTM.  Negative
00089      * "pseudo-zone" numbers are used to select one of the physical zones.
00090      **********************************************************************/
00091     enum zonespec {
00092       /**
00093        * The smallest pseudo-zone number.
00094        **********************************************************************/
00095       MINPSEUDOZONE = -4,
00096       /**
00097        * A marker for an undefined or invalid zone.  Equivalent to NaN.
00098        **********************************************************************/
00099       INVALID = -4,
00100       /**
00101        * If a coordinate already include zone information (e.g., it is an MGRS
00102        * coordinate), use that, otherwise apply the UTMUPS::STANDARD rules.
00103        **********************************************************************/
00104       MATCH = -3,
00105       /**
00106        * Apply the standard rules for UTM zone assigment extending the UTM zone
00107        * to each pole to give a zone number in [1, 60].  For example, use UTM
00108        * zone 38 for longitude in [42, 48).  The rules include the Norway and
00109        * Svalbard exceptions.
00110        **********************************************************************/
00111       UTM = -2,
00112       /**
00113        * Apply the standard rules for zone assignment to give a zone number in
00114        * [0, 60].  If the latitude is not in [-80, 84), then use UTMUPS::UPS =
00115        * 0, otherwise apply the rules for UTMUPS::UTM.  The tests on latitudes
00116        * and longitudes are all closed on the lower end open on the upper.
00117        * Thus for UTM zone 38, latitude is in [-80, 84) and longitude is in
00118        * [42, 48).
00119        **********************************************************************/
00120       STANDARD = -1,
00121       /**
00122        * The largest pseudo-zone number.
00123        **********************************************************************/
00124       MAXPSEUDOZONE = -1,
00125       /**
00126        * The smallest physical zone number.
00127        **********************************************************************/
00128       MINZONE = 0,
00129       /**
00130        * The zone number used for UPS
00131        **********************************************************************/
00132       UPS = 0,
00133       /**
00134        * The smallest UTM zone number.
00135        **********************************************************************/
00136       MINUTMZONE = 1,
00137       /**
00138        * The largest UTM zone number.
00139        **********************************************************************/
00140       MAXUTMZONE = 60,
00141       /**
00142        * The largest physical zone number.
00143        **********************************************************************/
00144       MAXZONE = 60,
00145     };
00146 
00147     /**
00148      * The standard zone.
00149      *
00150      * @param[in] lat latitude (degrees).
00151      * @param[in] lon longitude (degrees).
00152      * @param[in] setzone zone override (optional).
00153      *
00154      * This is exact.  If the optional argument \e setzone is given then use
00155      * that zone if it is non-negative, otherwise apply the rules given in
00156      * UTMUPS::zonespec.  Throws an error if \e setzone is outsize the range
00157      * [UTMUPS::MINPSEUDOZONE, UTMUPS::MAXZONE] = [-4, 60].
00158      **********************************************************************/
00159     static int StandardZone(real lat, real lon, int setzone = STANDARD);
00160 
00161     /**
00162      * Forward projection, from geographic to UTM/UPS.
00163      *
00164      * @param[in] lat latitude of point (degrees).
00165      * @param[in] lon longitude of point (degrees).
00166      * @param[out] zone the UTM zone (zero means UPS).
00167      * @param[out] northp hemisphere (true means north, false means south).
00168      * @param[out] x easting of point (meters).
00169      * @param[out] y northing of point (meters).
00170      * @param[out] gamma meridian convergence at point (degrees).
00171      * @param[out] k scale of projection at point.
00172      * @param[in] setzone zone override.
00173      * @param[in] mgrslimits if true enforce the stricter MGRS limits on the
00174      *   coordinates (default = false).
00175      *
00176      * The preferred zone for the result can be specified with \e setzone, see
00177      * UTMUPS::StandardZone.  Throw error if the resulting easting or northing
00178      * is outside the allowed range (see Reverse), in which case the arguments
00179      * are unchanged.  This also returns meridian convergence \e gamma
00180      * (degrees) and scale \e k.  The accuracy of the conversion is about 5nm.
00181      **********************************************************************/
00182     static void Forward(real lat, real lon,
00183                         int& zone, bool& northp, real& x, real& y,
00184                         real& gamma, real& k,
00185                         int setzone = STANDARD, bool mgrslimits = false);
00186 
00187     /**
00188      * Reverse projection, from  UTM/UPS to geographic.
00189      *
00190      * @param[in] zone the UTM zone (zero means UPS).
00191      * @param[in] northp hemisphere (true means north, false means south).
00192      * @param[in] x easting of point (meters).
00193      * @param[in] y northing of point (meters).
00194      * @param[out] lat latitude of point (degrees).
00195      * @param[out] lon longitude of point (degrees).
00196      * @param[out] gamma meridian convergence at point (degrees).
00197      * @param[out] k scale of projection at point.
00198      * @param[in] mgrslimits if true enforce the stricter MGRS limits on the
00199      *   coordinates (default = false).
00200      *
00201      * Throw error if easting or northing is outside the allowed range (see
00202      * below), in which case the arguments are unchanged.  The accuracy of the
00203      * conversion is about 5nm.
00204      *
00205      * UTM eastings are allowed to be in the range [0km, 1000km], northings are
00206      * allowed to be in in [0km, 9600km] for the northern hemisphere and in
00207      * [900km, 10000km] for the southern hemisphere.  (However UTM northings
00208      * can be continued across the equator.  So the actual limits on the
00209      * northings are [-9100km, 9600km] for the "northern" hemisphere and
00210      * [900km, 19600km] for the "southern" hemisphere.)
00211      *
00212      * UPS eastings and northings are allowed to be in the range [1200km,
00213      * 2800km] in the northern hemisphere and in [700km, 3100km] in the
00214      * southern hemisphere.
00215      *
00216      * These ranges are 100km larger than allowed for the conversions to MGRS.
00217      * (100km is the maximum extra padding consistent with eastings remaining
00218      * non-negative.)  This allows generous overlaps between zones and UTM and
00219      * UPS.  If \e mgrslimits = true, then all the ranges are shrunk by 100km
00220      * so that they agree with the stricter MGRS ranges.  No checks are
00221      * performed besides these (e.g., to limit the distance outside the
00222      * standard zone boundaries).
00223      **********************************************************************/
00224     static void Reverse(int zone, bool northp, real x, real y,
00225                         real& lat, real& lon, real& gamma, real& k,
00226                         bool mgrslimits = false);
00227 
00228     /**
00229      * UTMUPS::Forward without returning convergence and scale.
00230      **********************************************************************/
00231     static void Forward(real lat, real lon,
00232                         int& zone, bool& northp, real& x, real& y,
00233                         int setzone = STANDARD, bool mgrslimits = false) {
00234       real gamma, k;
00235       Forward(lat, lon, zone, northp, x, y, gamma, k, setzone, mgrslimits);
00236     }
00237 
00238     /**
00239      * UTMUPS::Reverse without returning convergence and scale.
00240      **********************************************************************/
00241     static void Reverse(int zone, bool northp, real x, real y,
00242                         real& lat, real& lon, bool mgrslimits = false) {
00243       real gamma, k;
00244       Reverse(zone, northp, x, y, lat, lon, gamma, k, mgrslimits);
00245     }
00246 
00247     /**
00248      * Decode a UTM/UPS zone string.
00249      *
00250      * @param[in] zonestr string representation of zone and hemisphere.
00251      * @param[out] zone the UTM zone (zero means UPS).
00252      * @param[out] northp hemisphere (true means north, false means south).
00253      *
00254      * For UTM, \e zonestr has the form of a zone number in the range
00255      * [UTMUPS::MINUTMZONE, UTMUPS::MAXUTMZONE] = [1, 60] followed by a
00256      * hemisphere letter, N or S.  For UPS, it consists just of the hemisphere
00257      * letter.  The returned value of \e zone is UTMUPS::UPS = 0 for UPS.  Note
00258      * well that "38S" indicates the southern hemisphere of zone 38 and not
00259      * latitude band S, [32, 40].  N, 01S, 2N, 38S are legal.  0N, 001S, 61N,
00260      * 38P are illegal.  INV is a special value for which the returned value of
00261      * \e is UTMUPS::INVALID.  Throws an error is the zone string is malformed.
00262      **********************************************************************/
00263     static void DecodeZone(const std::string& zonestr, int& zone, bool& northp);
00264 
00265     /**
00266      * Encode a UTM/UPS zone string.
00267      *
00268      * @param[out] zone the UTM zone (zero means UPS).
00269      * @param[out] northp hemisphere (true means north, false means south).
00270      * @return string representation of zone and hemisphere.
00271      *
00272      * \e zone must be in the range [UTMUPS::MINZONE, UTMUPS::MAXZONE] = [0,
00273      * 60] with \e zone = UTMUPS::UPS, 0, indicating UPS (but the resulting
00274      * string does not contain "0").  \e zone may also be UTMUPS::INVALID, in
00275      * which case the returned string is "INV".  This reverses
00276      * UTMUPS::DecodeZone.
00277      **********************************************************************/
00278     static std::string EncodeZone(int zone, bool northp);
00279 
00280     /**
00281      * @return shift (meters) necessary to align N and S halves of a UTM zone
00282      * (10<sup>7</sup>).
00283      **********************************************************************/
00284     static Math::real UTMShift() throw();
00285 
00286     /** \name Inspector functions
00287      **********************************************************************/
00288     ///@{
00289     /**
00290      * @return \e a the equatorial radius of the WGS84 ellipsoid (meters).
00291      *
00292      * (The WGS84 value is returned because the UTM and UPS projections are
00293      * based on this ellipsoid.)
00294      **********************************************************************/
00295     static Math::real MajorRadius() throw()
00296     { return Constants::WGS84_a<real>(); }
00297 
00298     /**
00299      * @return \e f the flattening of the WGS84 ellipsoid.
00300      *
00301      * (The WGS84 value is returned because the UTM and UPS projections are
00302      * based on this ellipsoid.)
00303      **********************************************************************/
00304     static Math::real Flattening() throw()
00305     { return Constants::WGS84_f<real>(); }
00306     ///@}
00307 
00308     /// \cond SKIP
00309     /**
00310      * <b>DEPRECATED</b>
00311      * @return \e r the inverse flattening of the WGS84 ellipsoid.
00312      **********************************************************************/
00313     static Math::real InverseFlattening() throw()
00314     { return 1/Constants::WGS84_f<real>(); }
00315     /// \endcond
00316   };
00317 
00318 } // namespace GeographicLib
00319 
00320 #endif  // GEOGRAPHICLIB_UTMUPS_HPP