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