00001 /** 00002 * \file UTMUPS.hpp 00003 * \brief Header for GeographicLib::UTMUPS class 00004 * 00005 * Copyright (c) Charles Karney (2008-2014) <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 1 00012 00013 #include <GeographicLib/Constants.hpp> 00014 00015 namespace GeographicLib { 00016 00017 /** 00018 * \brief Convert between geographic coordinates and UTM/UPS 00019 * 00020 * UTM and UPS are defined 00021 * - J. W. Hager, J. F. Behensky, and B. W. Drew, 00022 * <a href="http://earth-info.nga.mil/GandG/publications/tm8358.2/TM8358_2.pdf"> 00023 * The Universal Grids: Universal Transverse Mercator (UTM) and Universal 00024 * Polar Stereographic (UPS)</a>, Defense Mapping Agency, Technical Manual 00025 * TM8358.2 (1989). 00026 * . 00027 * Section 2-3 defines UTM and section 3-2.4 defines UPS. This document also 00028 * includes approximate algorithms for the computation of the underlying 00029 * transverse Mercator and polar stereographic projections. Here we 00030 * substitute much more accurate algorithms given by 00031 * GeographicLib:TransverseMercator and GeographicLib:PolarStereographic. 00032 * These are the algorithms recommended by the NGA document 00033 * - <a href="http://earth-info.nga.mil/GandG/publications/NGA_SIG_0012_2_0_0_UTMUPS/NGA.SIG.0012_2.0.0_UTMUPS.pdf"> 00034 * The Universal Grids and the Transverse Mercator and Polar Stereographic 00035 * Map Projections</a>, NGA.SIG.0012_2.0.0_UTMUPS (2014). 00036 * 00037 * In this implementation, the conversions are closed, i.e., output from 00038 * Forward is legal input for Reverse and vice versa. The error is about 5nm 00039 * in each direction. However, the conversion from legal UTM/UPS coordinates 00040 * to geographic coordinates and back might throw an error if the initial 00041 * point is within 5nm of the edge of the allowed range for the UTM/UPS 00042 * coordinates. 00043 * 00044 * The simplest way to guarantee the closed property is to define allowed 00045 * ranges for the eastings and northings for UTM and UPS coordinates. The 00046 * UTM boundaries are the same for all zones. (The only place the 00047 * exceptional nature of the zone boundaries is evident is when converting to 00048 * UTM/UPS coordinates requesting the standard zone.) The MGRS lettering 00049 * scheme imposes natural limits on UTM/UPS coordinates which may be 00050 * converted into MGRS coordinates. For the conversion to/from geographic 00051 * coordinates these ranges have been extended by 100km in order to provide a 00052 * generous overlap between UTM and UPS and between UTM zones. 00053 * 00054 * The <a href="http://www.nga.mil">NGA</a> software package 00055 * <a href="http://earth-info.nga.mil/GandG/geotrans/index.html">geotrans</a> 00056 * also provides conversions to and from UTM and UPS. Version 2.4.2 (and 00057 * earlier) suffers from some drawbacks: 00058 * - Inconsistent rules are used to determine the whether a particular UTM or 00059 * UPS coordinate is legal. A more systematic approach is taken here. 00060 * - The underlying projections are not very accurately implemented. 00061 * 00062 * The GeographicLib::UTMUPS::EncodeZone encodes the UTM zone and hemisphere 00063 * to allow UTM/UPS coordinated to be displayed as, for example, "38N 444500 00064 * 3688500". According to NGA.SIG.0012_2.0.0_UTMUPS the use of "N" to denote 00065 * "north" in the context is not allowed (since a upper case letter in this 00066 * context denotes the MGRS latitude band). Consequently, as of version 00067 * 1.36, EncodeZone uses the lower case letters "n" and "s" to denote the 00068 * hemisphere. In addition EncodeZone accepts an optional final argument \e 00069 * abbrev, which, if false, results in the hemisphere being spelled out as in 00070 * "38north". 00071 * 00072 * Example of use: 00073 * \include example-UTMUPS.cpp 00074 **********************************************************************/ 00075 class GEOGRAPHICLIB_EXPORT UTMUPS { 00076 private: 00077 typedef Math::real real; 00078 static const int falseeasting_[4]; 00079 static const int falsenorthing_[4]; 00080 static const int mineasting_[4]; 00081 static const int maxeasting_[4]; 00082 static const int minnorthing_[4]; 00083 static const int maxnorthing_[4]; 00084 static const int epsg01N = 32601; // EPSG code for UTM 01N 00085 static const int epsg60N = 32660; // EPSG code for UTM 60N 00086 static const int epsgN = 32661; // EPSG code for UPS N 00087 static const int epsg01S = 32701; // EPSG code for UTM 01S 00088 static const int epsg60S = 32760; // EPSG code for UTM 60S 00089 static const int epsgS = 32761; // EPSG code for UPS S 00090 static real CentralMeridian(int zone) 00091 { return real(6 * zone - 183); } 00092 static void CheckLatLon(real lat, real lon); 00093 // Throw an error if easting or northing are outside standard ranges. If 00094 // throwp = false, return bool instead. 00095 static bool CheckCoords(bool utmp, bool northp, real x, real y, 00096 bool msgrlimits = false, bool throwp = true); 00097 UTMUPS(); // Disable constructor 00098 00099 public: 00100 00101 /** 00102 * In this class we bring together the UTM and UPS coordinates systems. 00103 * The UTM divides the earth between latitudes −80° and 84° 00104 * into 60 zones numbered 1 thru 60. Zone assign zone number 0 to the UPS 00105 * regions, covering the two poles. Within UTMUPS, non-negative zone 00106 * numbers refer to one of the "physical" zones, 0 for UPS and [1, 60] for 00107 * UTM. Negative "pseudo-zone" numbers are used to select one of the 00108 * physical zones. 00109 **********************************************************************/ 00110 enum zonespec { 00111 /** 00112 * The smallest pseudo-zone number. 00113 **********************************************************************/ 00114 MINPSEUDOZONE = -4, 00115 /** 00116 * A marker for an undefined or invalid zone. Equivalent to NaN. 00117 **********************************************************************/ 00118 INVALID = -4, 00119 /** 00120 * If a coordinate already include zone information (e.g., it is an MGRS 00121 * coordinate), use that, otherwise apply the UTMUPS::STANDARD rules. 00122 **********************************************************************/ 00123 MATCH = -3, 00124 /** 00125 * Apply the standard rules for UTM zone assigment extending the UTM zone 00126 * to each pole to give a zone number in [1, 60]. For example, use UTM 00127 * zone 38 for longitude in [42°, 48°). The rules include the 00128 * Norway and Svalbard exceptions. 00129 **********************************************************************/ 00130 UTM = -2, 00131 /** 00132 * Apply the standard rules for zone assignment to give a zone number in 00133 * [0, 60]. If the latitude is not in [−80°, 84°), then 00134 * use UTMUPS::UPS = 0, otherwise apply the rules for UTMUPS::UTM. The 00135 * tests on latitudes and longitudes are all closed on the lower end open 00136 * on the upper. Thus for UTM zone 38, latitude is in [−80°, 00137 * 84°) and longitude is in [42°, 48°). 00138 **********************************************************************/ 00139 STANDARD = -1, 00140 /** 00141 * The largest pseudo-zone number. 00142 **********************************************************************/ 00143 MAXPSEUDOZONE = -1, 00144 /** 00145 * The smallest physical zone number. 00146 **********************************************************************/ 00147 MINZONE = 0, 00148 /** 00149 * The zone number used for UPS 00150 **********************************************************************/ 00151 UPS = 0, 00152 /** 00153 * The smallest UTM zone number. 00154 **********************************************************************/ 00155 MINUTMZONE = 1, 00156 /** 00157 * The largest UTM zone number. 00158 **********************************************************************/ 00159 MAXUTMZONE = 60, 00160 /** 00161 * The largest physical zone number. 00162 **********************************************************************/ 00163 MAXZONE = 60, 00164 }; 00165 00166 /** 00167 * The standard zone. 00168 * 00169 * @param[in] lat latitude (degrees). 00170 * @param[in] lon longitude (degrees). 00171 * @param[in] setzone zone override (optional). If omitted, use the 00172 * standard rules for picking the zone. If \e setzone is given then use 00173 * that zone if it is non-negative, otherwise apply the rules given in 00174 * UTMUPS::zonespec. 00175 * @exception GeographicErr if \e setzone is outside the range 00176 * [UTMUPS::MINPSEUDOZONE, UTMUPS::MAXZONE] = [−4, 60]. 00177 * 00178 * This is exact. 00179 **********************************************************************/ 00180 static int StandardZone(real lat, real lon, int setzone = STANDARD); 00181 00182 /** 00183 * Forward projection, from geographic to UTM/UPS. 00184 * 00185 * @param[in] lat latitude of point (degrees). 00186 * @param[in] lon longitude of point (degrees). 00187 * @param[out] zone the UTM zone (zero means UPS). 00188 * @param[out] northp hemisphere (true means north, false means south). 00189 * @param[out] x easting of point (meters). 00190 * @param[out] y northing of point (meters). 00191 * @param[out] gamma meridian convergence at point (degrees). 00192 * @param[out] k scale of projection at point. 00193 * @param[in] setzone zone override (optional). 00194 * @param[in] mgrslimits if true enforce the stricter MGRS limits on the 00195 * coordinates (default = false). 00196 * @exception GeographicErr if \e lat is not in [−90°, 00197 * 90°]. 00198 * @exception GeographicErr if \e lon is not in [−540°, 00199 * 540°). 00200 * @exception GeographicErr if the resulting \e x or \e y is out of allowed 00201 * range (see Reverse); in this case, these arguments are unchanged. 00202 * 00203 * If \e setzone is omitted, use the standard rules for picking the zone. 00204 * If \e setzone is given then use that zone if it is non-negative, 00205 * otherwise apply the rules given in UTMUPS::zonespec. The accuracy of 00206 * the conversion is about 5nm. 00207 * 00208 * The northing \e y jumps by UTMUPS::UTMShift() when crossing the equator 00209 * in the southerly direction. Sometimes it is useful to remove this 00210 * discontinuity in \e y by extending the "northern" hemisphere using 00211 * UTMUPS::Transfer: 00212 * \code 00213 double lat = -1, lon = 123; 00214 int zone; 00215 bool northp; 00216 double x, y, gamma, k; 00217 GeographicLib::UTMUPS::Forward(lat, lon, zone, northp, x, y, gamma, k); 00218 GeographicLib::UTMUPS::Transfer(zone, northp, x, y, 00219 zone, true, x, y, zone); 00220 northp = true; 00221 \endcode 00222 **********************************************************************/ 00223 static void Forward(real lat, real lon, 00224 int& zone, bool& northp, real& x, real& y, 00225 real& gamma, real& k, 00226 int setzone = STANDARD, bool mgrslimits = false); 00227 00228 /** 00229 * Reverse projection, from UTM/UPS to geographic. 00230 * 00231 * @param[in] zone the UTM zone (zero means UPS). 00232 * @param[in] northp hemisphere (true means north, false means south). 00233 * @param[in] x easting of point (meters). 00234 * @param[in] y northing of point (meters). 00235 * @param[out] lat latitude of point (degrees). 00236 * @param[out] lon longitude of point (degrees). 00237 * @param[out] gamma meridian convergence at point (degrees). 00238 * @param[out] k scale of projection at point. 00239 * @param[in] mgrslimits if true enforce the stricter MGRS limits on the 00240 * coordinates (default = false). 00241 * @exception GeographicErr if \e zone, \e x, or \e y is out of allowed 00242 * range; this this case the arguments are unchanged. 00243 * 00244 * The accuracy of the conversion is about 5nm. 00245 * 00246 * UTM eastings are allowed to be in the range [0km, 1000km], northings are 00247 * allowed to be in in [0km, 9600km] for the northern hemisphere and in 00248 * [900km, 10000km] for the southern hemisphere. However UTM northings 00249 * can be continued across the equator. So the actual limits on the 00250 * northings are [-9100km, 9600km] for the "northern" hemisphere and 00251 * [900km, 19600km] for the "southern" hemisphere. 00252 * 00253 * UPS eastings and northings are allowed to be in the range [1200km, 00254 * 2800km] in the northern hemisphere and in [700km, 3100km] in the 00255 * southern hemisphere. 00256 * 00257 * These ranges are 100km larger than allowed for the conversions to MGRS. 00258 * (100km is the maximum extra padding consistent with eastings remaining 00259 * non-negative.) This allows generous overlaps between zones and UTM and 00260 * UPS. If \e mgrslimits = true, then all the ranges are shrunk by 100km 00261 * so that they agree with the stricter MGRS ranges. No checks are 00262 * performed besides these (e.g., to limit the distance outside the 00263 * standard zone boundaries). 00264 **********************************************************************/ 00265 static void Reverse(int zone, bool northp, real x, real y, 00266 real& lat, real& lon, real& gamma, real& k, 00267 bool mgrslimits = false); 00268 00269 /** 00270 * UTMUPS::Forward without returning convergence and scale. 00271 **********************************************************************/ 00272 static void Forward(real lat, real lon, 00273 int& zone, bool& northp, real& x, real& y, 00274 int setzone = STANDARD, bool mgrslimits = false) { 00275 real gamma, k; 00276 Forward(lat, lon, zone, northp, x, y, gamma, k, setzone, mgrslimits); 00277 } 00278 00279 /** 00280 * UTMUPS::Reverse without returning convergence and scale. 00281 **********************************************************************/ 00282 static void Reverse(int zone, bool northp, real x, real y, 00283 real& lat, real& lon, bool mgrslimits = false) { 00284 real gamma, k; 00285 Reverse(zone, northp, x, y, lat, lon, gamma, k, mgrslimits); 00286 } 00287 00288 /** 00289 * Transfer UTM/UPS coordinated from one zone to another. 00290 * 00291 * @param[in] zonein the UTM zone for \e xin and \e yin (or zero for UPS). 00292 * @param[in] northpin hemisphere for \e xin and \e yin (true means north, 00293 * false means south). 00294 * @param[in] xin easting of point (meters) in \e zonein. 00295 * @param[in] yin northing of point (meters) in \e zonein. 00296 * @param[in] zoneout the requested UTM zone for \e xout and \e yout (or 00297 * zero for UPS). 00298 * @param[in] northpout hemisphere for \e xout output and \e yout. 00299 * @param[out] xout easting of point (meters) in \e zoneout. 00300 * @param[out] yout northing of point (meters) in \e zoneout. 00301 * @param[out] zone the actual UTM zone for \e xout and \e yout (or zero 00302 * for UPS); this equals \e zoneout if \e zoneout ≥ 0. 00303 * @exception GeographicErr if \e zonein is out of range (see below). 00304 * @exception GeographicErr if \e zoneout is out of range (see below). 00305 * @exception GeographicErr if \e xin or \e yin fall outside their allowed 00306 * ranges (see UTMUPS::Reverse). 00307 * @exception GeographicErr if \e xout or \e yout fall outside their 00308 * allowed ranges (see UTMUPS::Reverse). 00309 * 00310 * \e zonein must be in the range [UTMUPS::MINZONE, UTMUPS::MAXZONE] = [0, 00311 * 60] with \e zonein = UTMUPS::UPS, 0, indicating UPS. \e zonein may 00312 * also be UTMUPS::INVALID. 00313 * 00314 * \e zoneout must be in the range [UTMUPS::MINPSEUDOZONE, UTMUPS::MAXZONE] 00315 * = [-4, 60]. If \e zoneout < UTMUPS::MINZONE then the rules give in 00316 * the documentation of UTMUPS::zonespec are applied, and \e zone is set to 00317 * the actual zone used for output. 00318 * 00319 * (\e xout, \e yout) can overlap with (\e xin, \e yin). 00320 **********************************************************************/ 00321 static void Transfer(int zonein, bool northpin, real xin, real yin, 00322 int zoneout, bool northpout, real& xout, real& yout, 00323 int& zone); 00324 00325 /** 00326 * Decode a UTM/UPS zone string. 00327 * 00328 * @param[in] zonestr string representation of zone and hemisphere. 00329 * @param[out] zone the UTM zone (zero means UPS). 00330 * @param[out] northp hemisphere (true means north, false means south). 00331 * @exception GeographicErr if \e zonestr is malformed. 00332 * 00333 * For UTM, \e zonestr has the form of a zone number in the range 00334 * [UTMUPS::MINUTMZONE, UTMUPS::MAXUTMZONE] = [1, 60] followed by a 00335 * hemisphere letter, n or s (or "north" or "south" spelled out). For UPS, 00336 * it consists just of the hemisphere letter (or the spelled out 00337 * hemisphere). The returned value of \e zone is UTMUPS::UPS = 0 for UPS. 00338 * Note well that "38s" indicates the southern hemisphere of zone 38 and 00339 * not latitude band S, 32° ≤ \e lat < 40°. n, 01s, 2n, 38s, 00340 * south, 3north are legal. 0n, 001s, +3n, 61n, 38P are illegal. INV is a 00341 * special value for which the returned value of \e is UTMUPS::INVALID. 00342 **********************************************************************/ 00343 static void DecodeZone(const std::string& zonestr, int& zone, bool& northp); 00344 00345 /** 00346 * Encode a UTM/UPS zone string. 00347 * 00348 * @param[in] zone the UTM zone (zero means UPS). 00349 * @param[in] northp hemisphere (true means north, false means south). 00350 * @param[in] abbrev if true (the default) use abbreviated (n/s) notation 00351 * for hemisphere; otherwise spell out the hemisphere (north/south) 00352 * @exception GeographicErr if \e zone is out of range (see below). 00353 * @exception std::bad_alloc if memoy for the string can't be allocated. 00354 * @return string representation of zone and hemisphere. 00355 * 00356 * \e zone must be in the range [UTMUPS::MINZONE, UTMUPS::MAXZONE] = [0, 00357 * 60] with \e zone = UTMUPS::UPS, 0, indicating UPS (but the resulting 00358 * string does not contain "0"). \e zone may also be UTMUPS::INVALID, in 00359 * which case the returned string is "inv". This reverses 00360 * UTMUPS::DecodeZone. 00361 **********************************************************************/ 00362 static std::string EncodeZone(int zone, bool northp, bool abbrev = true); 00363 00364 /** 00365 * Decode EPSG. 00366 * 00367 * @param[in] epsg the EPSG code. 00368 * @param[out] zone the UTM zone (zero means UPS). 00369 * @param[out] northp hemisphere (true means north, false means south). 00370 * 00371 * EPSG (European Petroleum Survery Group) codes are a way to refer to many 00372 * different projections. DecodeEPSG decodes those refering to UTM or UPS 00373 * projections for the WGS84 ellipsoid. If the code does not refer to one 00374 * of these projections, \e zone is set to UTMUPS::INVALID. See 00375 * http://spatialreference.org/ref/epsg/ 00376 **********************************************************************/ 00377 static void DecodeEPSG(int epsg, int& zone, bool& northp); 00378 00379 /** 00380 * Encode zone as EPSG. 00381 * 00382 * @param[in] zone the UTM zone (zero means UPS). 00383 * @param[in] northp hemisphere (true means north, false means south). 00384 * @return EPSG code (or -1 if \e zone is not in the range 00385 * [UTMUPS::MINZONE, UTMUPS::MAXZONE] = [0, 60]) 00386 * 00387 * Convert \e zone and \e northp to the corresponding EPSG (European 00388 * Petroleum Survery Group) codes 00389 **********************************************************************/ 00390 static int EncodeEPSG(int zone, bool northp); 00391 00392 /** 00393 * @return shift (meters) necessary to align north and south halves of a 00394 * UTM zone (10<sup>7</sup>). 00395 **********************************************************************/ 00396 static Math::real UTMShift(); 00397 00398 /** \name Inspector functions 00399 **********************************************************************/ 00400 ///@{ 00401 /** 00402 * @return \e a the equatorial radius of the WGS84 ellipsoid (meters). 00403 * 00404 * (The WGS84 value is returned because the UTM and UPS projections are 00405 * based on this ellipsoid.) 00406 **********************************************************************/ 00407 static Math::real MajorRadius() 00408 { return Constants::WGS84_a(); } 00409 00410 /** 00411 * @return \e f the flattening of the WGS84 ellipsoid. 00412 * 00413 * (The WGS84 value is returned because the UTM and UPS projections are 00414 * based on this ellipsoid.) 00415 **********************************************************************/ 00416 static Math::real Flattening() 00417 { return Constants::WGS84_f(); } 00418 ///@} 00419 00420 /// \cond SKIP 00421 /** 00422 * <b>DEPRECATED</b> 00423 * @return \e r the inverse flattening of the WGS84 ellipsoid. 00424 **********************************************************************/ 00425 static Math::real InverseFlattening() 00426 { return 1/Constants::WGS84_f(); } 00427 /// \endcond 00428 }; 00429 00430 } // namespace GeographicLib 00431 00432 #endif // GEOGRAPHICLIB_UTMUPS_HPP