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