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) <charles@karney.com>
00006  * and licensed under the LGPL.  For more information, see
00007  * http://geographiclib.sourceforge.net/
00008  **********************************************************************/
00009 
00010 
00011 #if !defined(GEOGRAPHICLIB_UTMUPS_HPP)
00012 #define GEOGRAPHICLIB_UTMUPS_HPP "$Id: UTMUPS.hpp 6805 2010-01-28 21:18:14Z karney $"
00013 
00014 #include "GeographicLib/Constants.hpp"
00015 #include <sstream>
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   class UTMUPS {
00061   private:
00062     typedef Math::real real;
00063     static const real falseeasting[4];
00064     static const real falsenorthing[4];
00065     static const real mineasting[4];
00066     static const real maxeasting[4];
00067     static const real minnorthing[4];
00068     static const real maxnorthing[4];
00069     static real CentralMeridian(int zone) throw()
00070     { return real(6 * zone - 183); }
00071     template<typename T> static std::string str(T x) {
00072       std::ostringstream s; s << x; return s.str();
00073     }
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 = -3,
00096       /**
00097        * If a coordinate already include zone information (e.g., it is an MGRS
00098        * coordinate), use that, otherwise apply the UTMUPS::STANDARD rules.
00099        **********************************************************************/
00100       MATCH = -3,
00101       /**
00102        * Apply the standard rules for UTM zone assigment extending the UTM zone
00103        * to each pole to give a zone number in [1, 60].  For example, use UTM
00104        * zone 38 for longitude in [42, 48).  The rules include the Norway and
00105        * Svalbard exceptions.
00106        **********************************************************************/
00107       UTM = -2,
00108       /**
00109        * Apply the standard rules for zone assignment to give a zone number in
00110        * [0, 60].  If the latitude is not in [-80, 84), then use UTMUPS::UPS =
00111        * 0, otherwise apply the rules for UTMUPS::UTM.  The tests on latitudes
00112        * and longitudes are all closed on the lower end open on the upper.
00113        * Thus for UTM zone 38, latitude is in [-80, 84) and longitude is in
00114        * [42, 48).
00115        **********************************************************************/
00116       STANDARD = -1,
00117       /**
00118        * The largest pseudo-zone number.
00119        **********************************************************************/
00120       MAXPSEUDOZONE = -1,
00121       /**
00122        * The smallest physical zone number.
00123        **********************************************************************/
00124       MINZONE = 0,
00125       /**
00126        * The zone number used for UPS
00127        **********************************************************************/
00128       UPS = 0,
00129       /**
00130        * The smallest UTM zone number.
00131        **********************************************************************/
00132       MINUTMZONE = 1,
00133       /**
00134        * The largest UTM zone number.
00135        **********************************************************************/
00136       MAXUTMZONE = 60,
00137       /**
00138        * The largest physical zone number.
00139        **********************************************************************/
00140       MAXZONE = 60 };
00141     /**
00142      * Return the standard zone for latitude \e lat (degrees) and longitude \e
00143      * lon (degrees); see UTMUPS::STANDARD.  This is exact.  If the optional
00144      * argument \e setzone is given then use that zone if it is non-negative,
00145      * otherwise apply the rules given in UTMUPS::zonespec.  Throws an error if
00146      * \e setzone is outsize the range [UTMUPS::MINPSEUDOZONE, UTMUPS::MAXZONE]
00147      * = [-3, 60].
00148      **********************************************************************/
00149     static int StandardZone(real lat, real lon, int setzone = STANDARD);
00150 
00151     /**
00152      * Convert geographic coordinates to UTM or UPS coordinate.  Given latitude
00153      * \e lat (degrees), and longitude \e lon (degrees), return \e zone (zero
00154      * indicates UPS), hemisphere \e northp (false means south, true means
00155      * north), easting \e x (meters), and northing \e y (meters).  The prefered
00156      * zone for the result can be specified with \e setzone, see
00157      * UTMUPS::StandardZone.  Throw error if the resulting easting or northing
00158      * is outside the allowed range (see Reverse), in which case the arguments
00159      * are unchanged.  If \e mgrslimits == true, then use the stricter MGRS
00160      * limits (see Reverse).  This also returns meridian convergence \e gamma
00161      * (degrees) and scale \e k.  The accuracy of the conversion is about 5nm.
00162      **********************************************************************/
00163     static void Forward(real lat, real lon,
00164                         int& zone, bool& northp, real& x, real& y,
00165                         real& gamma, real& k,
00166                         int setzone = STANDARD, bool mgrslimits = false);
00167 
00168     /**
00169      * Convert UTM or UPS coordinate to geographic coordinates .  Given zone \e
00170      * zone (\e zone == UTMUPS::UPS, 0, indicates UPS), hemisphere \e
00171      * northp (false means south, true means north), easting \e x (meters), and
00172      * northing \e y (meters), return latitude \e lat (degrees) and longitude
00173      * \e lon (degrees).  Throw error if easting or northing is outside the
00174      * allowed range (see below), in which case the arguments are unchanged.
00175      * This also returns meridian convergence \e gamma (degrees) and scale \e
00176      * k.  The accuracy of the conversion is about 5nm.
00177      *
00178      * UTM eastings are allowed to be in the range [0km, 1000km], northings are
00179      * allowed to be in in [0km, 9600km] for the northern hemisphere and in
00180      * [900km, 10000km] for the southern hemisphere.  (However UTM northings
00181      * can be continued across the equator.  So the actual limits on the
00182      * northings are [-9100km, 9600km] for the "northern" hemisphere and
00183      * [900km, 19600km] for the "southern" hemisphere.)
00184      *
00185      * UPS eastings and northings are allowed to be in the range [1200km,
00186      * 2800km] in the northern hemisphere and in [700km, 3100km] in the
00187      * southern hemisphere.
00188      *
00189      * These ranges are 100km larger than allowed for the conversions to MGRS.
00190      * (100km is the maximum extra padding consistent with eastings remaining
00191      * non-negative.)  This allows generous overlaps between zones and UTM and
00192      * UPS.  If \e mgrslimits = true, then all the ranges are shrunk by 100km
00193      * so that they agree with the stricter MGRS ranges.  No checks are
00194      * performed besides these (e.g., to limit the distance outside the
00195      * standard zone boundaries).
00196      **********************************************************************/
00197     static void Reverse(int zone, bool northp, real x, real y,
00198                         real& lat, real& lon, real& gamma, real& k,
00199                         bool mgrslimits = false);
00200 
00201     /**
00202      * Forward without returning convergence and scale.
00203      **********************************************************************/
00204     static void Forward(real lat, real lon,
00205                         int& zone, bool& northp, real& x, real& y,
00206                         int setzone = STANDARD, bool mgrslimits = false) {
00207       real gamma, k;
00208       Forward(lat, lon, zone, northp, x, y, gamma, k, setzone, mgrslimits);
00209     }
00210 
00211     /**
00212      * Reverse without returning convergence and scale.
00213      **********************************************************************/
00214     static void Reverse(int zone, bool northp, real x, real y,
00215                         real& lat, real& lon, bool mgrslimits = false) {
00216       real gamma, k;
00217       Reverse(zone, northp, x, y, lat, lon, gamma, k, mgrslimits);
00218     }
00219 
00220     /**
00221      * Decode a UTM/UPS zone string, \e zonestr, returning the resulting \e
00222      * zone and hemisphere thru \e northp (true for northern and false for
00223      * southern hemispheres).  For UTM, \e zonestr has the form of a zone
00224      * number in the range [UTMUPS::MINUTMZONE, UTMUPS::MAXUTMZONE] = [1, 60]
00225      * followed by a hemisphere letter, N or S.  For UPS, it consists just of
00226      * the hemisphere letter.  The returned value of \e zone is UTMUPS::UPS = 0
00227      * for UPS.  Note well that "38S" indicates the southern hemisphere of zone
00228      * 38 and not latitude band S, [32, 40].  N, 01S, 2N, 38S are legal.  0N,
00229      * 001S, 61N, 38P are illegal.
00230      **********************************************************************/
00231     static void DecodeZone(const std::string& zonestr, int& zone, bool& northp);
00232 
00233     /**
00234      * Encode a UTM/UPS zone string given the \e zone and hemisphere \e
00235      * northp.  \e zone must be in the range [UTMUPS::MINZONE,
00236      * UTMUPS::MAXZONE] = [0, 60] with \e zone = UTMUPS::UPS, 0, indicating
00237      * UPS (but the resulting string does not contain "0").  This reverses
00238      * DecodeZone.
00239      **********************************************************************/
00240     static std::string EncodeZone(int zone, bool northp);
00241 
00242     /**
00243      * The shift necessary to align N and S halves of a UTM zone
00244      * (10<sup>7</sup>).
00245      **********************************************************************/
00246     static Math::real UTMShift() throw();
00247 
00248     /**
00249      * The major radius of the ellipsoid (meters).  This is the value for the
00250      * WGS84 ellipsoid because the UTM and UPS projections are based on this
00251      * ellipsoid.
00252      **********************************************************************/
00253     static Math::real MajorRadius() throw() { return Constants::WGS84_a(); }
00254 
00255     /**
00256      * The inverse flattening of the ellipsoid.  This is the value for the
00257      * WGS84 ellipsoid because the UTM and UPS projections are based on this
00258      * ellipsoid.
00259      **********************************************************************/
00260     static Math::real InverseFlattening() throw()
00261     { return Constants::WGS84_r(); }
00262   };
00263 
00264 } // namespace GeographicLib
00265 #endif

Generated on 21 May 2010 for GeographicLib by  doxygen 1.6.1