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