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