GeographicLib
1.21
|
00001 /** 00002 * \file UTMUPS.hpp 00003 * \brief Header for GeographicLib::UTMUPS class 00004 * 00005 * Copyright (c) Charles Karney (2008-2011) <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 \ 00012 "$Id: a529ed8aeaeffb02994254bbc7eb1209aa41b9ca $" 00013 00014 #include <sstream> 00015 #include <GeographicLib/Constants.hpp> 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 * Example of use: 00061 * \include example-UTMUPS.cpp 00062 **********************************************************************/ 00063 class GEOGRAPHIC_EXPORT UTMUPS { 00064 private: 00065 typedef Math::real real; 00066 static const real falseeasting_[4]; 00067 static const real falsenorthing_[4]; 00068 static const real mineasting_[4]; 00069 static const real maxeasting_[4]; 00070 static const real minnorthing_[4]; 00071 static const real maxnorthing_[4]; 00072 static real CentralMeridian(int zone) throw() 00073 { return real(6 * zone - 183); } 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 = -4, 00096 /** 00097 * A marker for an undefined or invalid zone. Equivalent to NaN. 00098 **********************************************************************/ 00099 INVALID = -4, 00100 /** 00101 * If a coordinate already include zone information (e.g., it is an MGRS 00102 * coordinate), use that, otherwise apply the UTMUPS::STANDARD rules. 00103 **********************************************************************/ 00104 MATCH = -3, 00105 /** 00106 * Apply the standard rules for UTM zone assigment extending the UTM zone 00107 * to each pole to give a zone number in [1, 60]. For example, use UTM 00108 * zone 38 for longitude in [42, 48). The rules include the Norway and 00109 * Svalbard exceptions. 00110 **********************************************************************/ 00111 UTM = -2, 00112 /** 00113 * Apply the standard rules for zone assignment to give a zone number in 00114 * [0, 60]. If the latitude is not in [-80, 84), then use UTMUPS::UPS = 00115 * 0, otherwise apply the rules for UTMUPS::UTM. The tests on latitudes 00116 * and longitudes are all closed on the lower end open on the upper. 00117 * Thus for UTM zone 38, latitude is in [-80, 84) and longitude is in 00118 * [42, 48). 00119 **********************************************************************/ 00120 STANDARD = -1, 00121 /** 00122 * The largest pseudo-zone number. 00123 **********************************************************************/ 00124 MAXPSEUDOZONE = -1, 00125 /** 00126 * The smallest physical zone number. 00127 **********************************************************************/ 00128 MINZONE = 0, 00129 /** 00130 * The zone number used for UPS 00131 **********************************************************************/ 00132 UPS = 0, 00133 /** 00134 * The smallest UTM zone number. 00135 **********************************************************************/ 00136 MINUTMZONE = 1, 00137 /** 00138 * The largest UTM zone number. 00139 **********************************************************************/ 00140 MAXUTMZONE = 60, 00141 /** 00142 * The largest physical zone number. 00143 **********************************************************************/ 00144 MAXZONE = 60, 00145 }; 00146 00147 /** 00148 * The standard zone. 00149 * 00150 * @param[in] lat latitude (degrees). 00151 * @param[in] lon longitude (degrees). 00152 * @param[in] setzone zone override (optional). 00153 * 00154 * This is exact. If the optional argument \e setzone is given then use 00155 * that zone if it is non-negative, otherwise apply the rules given in 00156 * UTMUPS::zonespec. Throws an error if \e setzone is outsize the range 00157 * [UTMUPS::MINPSEUDOZONE, UTMUPS::MAXZONE] = [-4, 60]. 00158 **********************************************************************/ 00159 static int StandardZone(real lat, real lon, int setzone = STANDARD); 00160 00161 /** 00162 * Forward projection, from geographic to UTM/UPS. 00163 * 00164 * @param[in] lat latitude of point (degrees). 00165 * @param[in] lon longitude of point (degrees). 00166 * @param[out] zone the UTM zone (zero means UPS). 00167 * @param[out] northp hemisphere (true means north, false means south). 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 stricter MGRS limits on the 00174 * coordinates (default = false). 00175 * 00176 * The preferred 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 (true means north, false means south). 00192 * @param[in] x easting of point (meters). 00193 * @param[in] y northing of point (meters). 00194 * @param[out] lat latitude of point (degrees). 00195 * @param[out] lon longitude of point (degrees). 00196 * @param[out] gamma meridian convergence at point (degrees). 00197 * @param[out] k scale of projection at point. 00198 * @param[in] mgrslimits if true enforce the stricter MGRS limits on the 00199 * coordinates (default = false). 00200 * 00201 * Throw error if easting or northing is outside the allowed range (see 00202 * below), in which case the arguments are unchanged. The accuracy of the 00203 * conversion is about 5nm. 00204 * 00205 * UTM eastings are allowed to be in the range [0km, 1000km], northings are 00206 * allowed to be in in [0km, 9600km] for the northern hemisphere and in 00207 * [900km, 10000km] for the southern hemisphere. (However UTM northings 00208 * can be continued across the equator. So the actual limits on the 00209 * northings are [-9100km, 9600km] for the "northern" hemisphere and 00210 * [900km, 19600km] for the "southern" hemisphere.) 00211 * 00212 * UPS eastings and northings are allowed to be in the range [1200km, 00213 * 2800km] in the northern hemisphere and in [700km, 3100km] in the 00214 * southern hemisphere. 00215 * 00216 * These ranges are 100km larger than allowed for the conversions to MGRS. 00217 * (100km is the maximum extra padding consistent with eastings remaining 00218 * non-negative.) This allows generous overlaps between zones and UTM and 00219 * UPS. If \e mgrslimits = true, then all the ranges are shrunk by 100km 00220 * so that they agree with the stricter MGRS ranges. No checks are 00221 * performed besides these (e.g., to limit the distance outside the 00222 * standard zone boundaries). 00223 **********************************************************************/ 00224 static void Reverse(int zone, bool northp, real x, real y, 00225 real& lat, real& lon, real& gamma, real& k, 00226 bool mgrslimits = false); 00227 00228 /** 00229 * UTMUPS::Forward without returning convergence and scale. 00230 **********************************************************************/ 00231 static void Forward(real lat, real lon, 00232 int& zone, bool& northp, real& x, real& y, 00233 int setzone = STANDARD, bool mgrslimits = false) { 00234 real gamma, k; 00235 Forward(lat, lon, zone, northp, x, y, gamma, k, setzone, mgrslimits); 00236 } 00237 00238 /** 00239 * UTMUPS::Reverse without returning convergence and scale. 00240 **********************************************************************/ 00241 static void Reverse(int zone, bool northp, real x, real y, 00242 real& lat, real& lon, bool mgrslimits = false) { 00243 real gamma, k; 00244 Reverse(zone, northp, x, y, lat, lon, gamma, k, mgrslimits); 00245 } 00246 00247 /** 00248 * Decode a UTM/UPS zone string. 00249 * 00250 * @param[in] zonestr string representation of zone and hemisphere. 00251 * @param[out] zone the UTM zone (zero means UPS). 00252 * @param[out] northp hemisphere (true means north, false means south). 00253 * 00254 * For UTM, \e zonestr has the form of a zone number in the range 00255 * [UTMUPS::MINUTMZONE, UTMUPS::MAXUTMZONE] = [1, 60] followed by a 00256 * hemisphere letter, N or S. For UPS, it consists just of the hemisphere 00257 * letter. The returned value of \e zone is UTMUPS::UPS = 0 for UPS. Note 00258 * well that "38S" indicates the southern hemisphere of zone 38 and not 00259 * latitude band S, [32, 40]. N, 01S, 2N, 38S are legal. 0N, 001S, 61N, 00260 * 38P are illegal. INV is a special value for which the returned value of 00261 * \e is UTMUPS::INVALID. Throws an error is the zone string is malformed. 00262 **********************************************************************/ 00263 static void DecodeZone(const std::string& zonestr, int& zone, bool& northp); 00264 00265 /** 00266 * Encode a UTM/UPS zone string. 00267 * 00268 * @param[out] zone the UTM zone (zero means UPS). 00269 * @param[out] northp hemisphere (true means north, false means south). 00270 * @return string representation of zone and hemisphere. 00271 * 00272 * \e zone must be in the range [UTMUPS::MINZONE, UTMUPS::MAXZONE] = [0, 00273 * 60] with \e zone = UTMUPS::UPS, 0, indicating UPS (but the resulting 00274 * string does not contain "0"). \e zone may also be UTMUPS::INVALID, in 00275 * which case the returned string is "INV". This reverses 00276 * UTMUPS::DecodeZone. 00277 **********************************************************************/ 00278 static std::string EncodeZone(int zone, bool northp); 00279 00280 /** 00281 * @return shift (meters) necessary to align N and S halves of a UTM zone 00282 * (10<sup>7</sup>). 00283 **********************************************************************/ 00284 static Math::real UTMShift() throw(); 00285 00286 /** \name Inspector functions 00287 **********************************************************************/ 00288 ///@{ 00289 /** 00290 * @return \e a the equatorial radius of the WGS84 ellipsoid (meters). 00291 * 00292 * (The WGS84 value is returned because the UTM and UPS projections are 00293 * based on this ellipsoid.) 00294 **********************************************************************/ 00295 static Math::real MajorRadius() throw() 00296 { return Constants::WGS84_a<real>(); } 00297 00298 /** 00299 * @return \e f the flattening of the WGS84 ellipsoid. 00300 * 00301 * (The WGS84 value is returned because the UTM and UPS projections are 00302 * based on this ellipsoid.) 00303 **********************************************************************/ 00304 static Math::real Flattening() throw() 00305 { return Constants::WGS84_f<real>(); } 00306 ///@} 00307 00308 /// \cond SKIP 00309 /** 00310 * <b>DEPRECATED</b> 00311 * @return \e r the inverse flattening of the WGS84 ellipsoid. 00312 **********************************************************************/ 00313 static Math::real InverseFlattening() throw() 00314 { return 1/Constants::WGS84_f<real>(); } 00315 /// \endcond 00316 }; 00317 00318 } // namespace GeographicLib 00319 00320 #endif // GEOGRAPHICLIB_UTMUPS_HPP