1 /** 2 * \file GeoCoords.hpp 3 * \brief Header for GeographicLib::GeoCoords class 4 * 5 * Copyright (c) Charles Karney (2008-2020) <charles@karney.com> and licensed 6 * under the MIT/X11 License. For more information, see 7 * https://geographiclib.sourceforge.io/ 8 **********************************************************************/ 9 10 #if !defined(GEOGRAPHICLIB_GEOCOORDS_HPP) 11 #define GEOGRAPHICLIB_GEOCOORDS_HPP 1 12 13 #include <GeographicLib/UTMUPS.hpp> 14 #include <GeographicLib/Constants.hpp> 15 16 namespace GeographicLib { 17 18 /** 19 * \brief Conversion between geographic coordinates 20 * 21 * This class stores a geographic position which may be set via the 22 * constructors or Reset via 23 * - latitude and longitude 24 * - UTM or UPS coordinates 25 * - a string representation of these or an MGRS coordinate string 26 * 27 * The state consists of the latitude and longitude and the supplied UTM or 28 * UPS coordinates (possibly derived from the MGRS coordinates). If latitude 29 * and longitude were given then the UTM/UPS coordinates follows the standard 30 * conventions. 31 * 32 * The mutable state consists of the UTM or UPS coordinates for a alternate 33 * zone. A method SetAltZone is provided to set the alternate UPS/UTM zone. 34 * 35 * Methods are provided to return the geographic coordinates, the input UTM 36 * or UPS coordinates (and associated meridian convergence and scale), or 37 * alternate UTM or UPS coordinates (and their associated meridian 38 * convergence and scale). 39 * 40 * Once the input string has been parsed, you can print the result out in any 41 * of the formats, decimal degrees, degrees minutes seconds, MGRS, UTM/UPS. 42 * 43 * Example of use: 44 * \include example-GeoCoords.cpp 45 * 46 * <a href="GeoConvert.1.html">GeoConvert</a> is a command-line utility 47 * providing access to the functionality of GeoCoords. 48 **********************************************************************/ 49 class GEOGRAPHICLIB_EXPORT GeoCoords { 50 private: 51 typedef Math::real real; 52 real _lat, _long, _easting, _northing, _gamma, _k; 53 bool _northp; 54 int _zone; // See UTMUPS::zonespec 55 mutable real _alt_easting, _alt_northing, _alt_gamma, _alt_k; 56 mutable int _alt_zone; 57 CopyToAlt() const58 void CopyToAlt() const { 59 _alt_easting = _easting; 60 _alt_northing = _northing; 61 _alt_gamma = _gamma; 62 _alt_k = _k; 63 _alt_zone = _zone; 64 } 65 static void UTMUPSString(int zone, bool northp, 66 real easting, real northing, 67 int prec, bool abbrev, std::string& utm); 68 void FixHemisphere(); 69 public: 70 71 /** \name Initializing the GeoCoords object 72 **********************************************************************/ 73 ///@{ 74 /** 75 * The default constructor sets the coordinate as undefined. 76 **********************************************************************/ GeoCoords()77 GeoCoords() 78 : _lat(Math::NaN()) 79 , _long(Math::NaN()) 80 , _easting(Math::NaN()) 81 , _northing(Math::NaN()) 82 , _gamma(Math::NaN()) 83 , _k(Math::NaN()) 84 , _northp(false) 85 , _zone(UTMUPS::INVALID) 86 { CopyToAlt(); } 87 88 /** 89 * Construct from a string. 90 * 91 * @param[in] s 1-element, 2-element, or 3-element string representation of 92 * the position. 93 * @param[in] centerp governs the interpretation of MGRS coordinates (see 94 * below). 95 * @param[in] longfirst governs the interpretation of geographic 96 * coordinates (see below). 97 * @exception GeographicErr if the \e s is malformed (see below). 98 * 99 * Parse as a string and interpret it as a geographic position. The input 100 * string is broken into space (or comma) separated pieces and Basic 101 * decision on which format is based on number of components 102 * -# MGRS 103 * -# "Lat Long" or "Long Lat" 104 * -# "Zone Easting Northing" or "Easting Northing Zone" 105 * 106 * The following inputs are approximately the same (Ar Ramadi Bridge, Iraq) 107 * - Latitude and Longitude 108 * - 33.44 43.27 109 * - N33d26.4' E43d16.2' 110 * - 43d16'12"E 33d26'24"N 111 * - 43:16:12E 33:26:24 112 * - MGRS 113 * - 38SLC30 114 * - 38SLC391014 115 * - 38SLC3918701405 116 * - 37SHT9708 117 * - UTM 118 * - 38n 339188 3701405 119 * - 897039 3708229 37n 120 * 121 * <b>Latitude and Longitude parsing</b>: Latitude precedes longitude, 122 * unless a N, S, E, W hemisphere designator is used on one or both 123 * coordinates. If \e longfirst = true (default is false), then 124 * longitude precedes latitude in the absence of a hemisphere designator. 125 * Thus (with \e longfirst = false) 126 * - 40 -75 127 * - N40 W75 128 * - -75 N40 129 * - 75W 40N 130 * - E-75 -40S 131 * . 132 * are all the same position. The coordinates may be given in 133 * decimal degrees, degrees and decimal minutes, degrees, minutes, 134 * seconds, etc. Use d, ', and " to mark off the degrees, 135 * minutes and seconds. Various alternative symbols for degrees, minutes, 136 * and seconds are allowed. Alternatively, use : to separate these 137 * components. A single addition or subtraction is allowed. (See 138 * DMS::Decode for details.) Thus 139 * - 40d30'30" 140 * - 40d30'30 141 * - 40°30'30 142 * - 40d30.5' 143 * - 40d30.5 144 * - 40:30:30 145 * - 40:30.5 146 * - 40.508333333 147 * - 40:30+0:0:30 148 * - 40:31-0:0.5 149 * . 150 * all specify the same angle. The leading sign applies to the following 151 * components so -1d30 is -(1+30/60) = −1.5. However, note 152 * that -1:30-0:0:15 is parsed as (-1:30) + (-0:0:15) = −(1+30/60) 153 * − (15/3600). Latitudes must be in the range [−90°, 154 * 90°]. Internally longitudes are reduced to the range 155 * [−180°, 180°]. 156 * 157 * <b>UTM/UPS parsing</b>: For UTM zones (−80° ≤ Lat < 158 * 84°), the zone designator is made up of a zone number (for 1 to 60) 159 * and a hemisphere letter (n or s), e.g., 38n (38north can also be used). 160 * The latitude band designer ([C--M] in the southern hemisphere and [N--X] 161 * in the northern) should NOT be used. (This is part of the MGRS 162 * coordinate.) The zone designator for the poles (where UPS is employed) 163 * is a hemisphere letter by itself, i.e., n or s (north or south can also 164 * be used). 165 * 166 * <b>MGRS parsing</b> interprets the grid references as square area at the 167 * specified precision (1m, 10m, 100m, etc.). If \e centerp = true (the 168 * default), the center of this square is then taken to be the precise 169 * position; thus: 170 * - 38SMB = 38n 450000 3650000 171 * - 38SMB4484 = 38n 444500 3684500 172 * - 38SMB44148470 = 38n 444145 3684705 173 * . 174 * Otherwise, the "south-west" corner of the square is used, i.e., 175 * - 38SMB = 38n 400000 3600000 176 * - 38SMB4484 = 38n 444000 3684000 177 * - 38SMB44148470 = 38n 444140 3684700 178 **********************************************************************/ GeoCoords(const std::string & s,bool centerp=true,bool longfirst=false)179 explicit GeoCoords(const std::string& s, 180 bool centerp = true, bool longfirst = false) 181 { Reset(s, centerp, longfirst); } 182 183 /** 184 * Construct from geographic coordinates. 185 * 186 * @param[in] latitude (degrees). 187 * @param[in] longitude (degrees). 188 * @param[in] zone if specified, force the UTM/UPS representation to use a 189 * specified zone using the rules given in UTMUPS::zonespec. 190 * @exception GeographicErr if \e latitude is not in [−90°, 191 * 90°]. 192 * @exception GeographicErr if \e zone cannot be used for this location. 193 **********************************************************************/ GeoCoords(real latitude,real longitude,int zone=UTMUPS::STANDARD)194 GeoCoords(real latitude, real longitude, int zone = UTMUPS::STANDARD) { 195 Reset(latitude, longitude, zone); 196 } 197 198 /** 199 * Construct from UTM/UPS coordinates. 200 * 201 * @param[in] zone UTM zone (zero means UPS). 202 * @param[in] northp hemisphere (true means north, false means south). 203 * @param[in] easting (meters). 204 * @param[in] northing (meters). 205 * @exception GeographicErr if \e zone, \e easting, or \e northing is 206 * outside its allowed range. 207 **********************************************************************/ GeoCoords(int zone,bool northp,real easting,real northing)208 GeoCoords(int zone, bool northp, real easting, real northing) { 209 Reset(zone, northp, easting, northing); 210 } 211 212 /** 213 * Reset the location from a string. See 214 * GeoCoords(const std::string& s, bool centerp, bool longfirst). 215 * 216 * @param[in] s 1-element, 2-element, or 3-element string representation of 217 * the position. 218 * @param[in] centerp governs the interpretation of MGRS coordinates. 219 * @param[in] longfirst governs the interpretation of geographic 220 * coordinates. 221 * @exception GeographicErr if the \e s is malformed. 222 **********************************************************************/ 223 void Reset(const std::string& s, 224 bool centerp = true, bool longfirst = false); 225 226 /** 227 * Reset the location in terms of geographic coordinates. See 228 * GeoCoords(real latitude, real longitude, int zone). 229 * 230 * @param[in] latitude (degrees). 231 * @param[in] longitude (degrees). 232 * @param[in] zone if specified, force the UTM/UPS representation to use a 233 * specified zone using the rules given in UTMUPS::zonespec. 234 * @exception GeographicErr if \e latitude is not in [−90°, 235 * 90°]. 236 * @exception GeographicErr if \e zone cannot be used for this location. 237 **********************************************************************/ Reset(real latitude,real longitude,int zone=UTMUPS::STANDARD)238 void Reset(real latitude, real longitude, int zone = UTMUPS::STANDARD) { 239 UTMUPS::Forward(latitude, longitude, 240 _zone, _northp, _easting, _northing, _gamma, _k, 241 zone); 242 _lat = latitude; 243 _long = longitude; 244 if (_long >= 180) _long -= 360; 245 else if (_long < -180) _long += 360; 246 CopyToAlt(); 247 } 248 249 /** 250 * Reset the location in terms of UPS/UPS coordinates. See 251 * GeoCoords(int zone, bool northp, real easting, real northing). 252 * 253 * @param[in] zone UTM zone (zero means UPS). 254 * @param[in] northp hemisphere (true means north, false means south). 255 * @param[in] easting (meters). 256 * @param[in] northing (meters). 257 * @exception GeographicErr if \e zone, \e easting, or \e northing is 258 * outside its allowed range. 259 **********************************************************************/ Reset(int zone,bool northp,real easting,real northing)260 void Reset(int zone, bool northp, real easting, real northing) { 261 UTMUPS::Reverse(zone, northp, easting, northing, 262 _lat, _long, _gamma, _k); 263 _zone = zone; 264 _northp = northp; 265 _easting = easting; 266 _northing = northing; 267 FixHemisphere(); 268 CopyToAlt(); 269 } 270 ///@} 271 272 /** \name Querying the GeoCoords object 273 **********************************************************************/ 274 ///@{ 275 /** 276 * @return latitude (degrees) 277 **********************************************************************/ Latitude() const278 Math::real Latitude() const { return _lat; } 279 280 /** 281 * @return longitude (degrees) 282 **********************************************************************/ Longitude() const283 Math::real Longitude() const { return _long; } 284 285 /** 286 * @return easting (meters) 287 **********************************************************************/ Easting() const288 Math::real Easting() const { return _easting; } 289 290 /** 291 * @return northing (meters) 292 **********************************************************************/ Northing() const293 Math::real Northing() const { return _northing; } 294 295 /** 296 * @return meridian convergence (degrees) for the UTM/UPS projection. 297 **********************************************************************/ Convergence() const298 Math::real Convergence() const { return _gamma; } 299 300 /** 301 * @return scale for the UTM/UPS projection. 302 **********************************************************************/ Scale() const303 Math::real Scale() const { return _k; } 304 305 /** 306 * @return hemisphere (false means south, true means north). 307 **********************************************************************/ Northp() const308 bool Northp() const { return _northp; } 309 310 /** 311 * @return hemisphere letter n or s. 312 **********************************************************************/ Hemisphere() const313 char Hemisphere() const { return _northp ? 'n' : 's'; } 314 315 /** 316 * @return the zone corresponding to the input (return 0 for UPS). 317 **********************************************************************/ Zone() const318 int Zone() const { return _zone; } 319 320 ///@} 321 322 /** \name Setting and querying the alternate zone 323 **********************************************************************/ 324 ///@{ 325 /** 326 * Specify alternate zone number. 327 * 328 * @param[in] zone zone number for the alternate representation. 329 * @exception GeographicErr if \e zone cannot be used for this location. 330 * 331 * See UTMUPS::zonespec for more information on the interpretation of \e 332 * zone. Note that \e zone == UTMUPS::STANDARD (the default) use the 333 * standard UPS or UTM zone, UTMUPS::MATCH does nothing retaining the 334 * existing alternate representation. Before this is called the alternate 335 * zone is the input zone. 336 **********************************************************************/ SetAltZone(int zone=UTMUPS::STANDARD) const337 void SetAltZone(int zone = UTMUPS::STANDARD) const { 338 if (zone == UTMUPS::MATCH) 339 return; 340 zone = UTMUPS::StandardZone(_lat, _long, zone); 341 if (zone == _zone) 342 CopyToAlt(); 343 else { 344 bool northp; 345 UTMUPS::Forward(_lat, _long, 346 _alt_zone, northp, 347 _alt_easting, _alt_northing, _alt_gamma, _alt_k, 348 zone); 349 } 350 } 351 352 /** 353 * @return current alternate zone (return 0 for UPS). 354 **********************************************************************/ AltZone() const355 int AltZone() const { return _alt_zone; } 356 357 /** 358 * @return easting (meters) for alternate zone. 359 **********************************************************************/ AltEasting() const360 Math::real AltEasting() const { return _alt_easting; } 361 362 /** 363 * @return northing (meters) for alternate zone. 364 **********************************************************************/ AltNorthing() const365 Math::real AltNorthing() const { return _alt_northing; } 366 367 /** 368 * @return meridian convergence (degrees) for alternate zone. 369 **********************************************************************/ AltConvergence() const370 Math::real AltConvergence() const { return _alt_gamma; } 371 372 /** 373 * @return scale for alternate zone. 374 **********************************************************************/ AltScale() const375 Math::real AltScale() const { return _alt_k; } 376 ///@} 377 378 /** \name String representations of the GeoCoords object 379 **********************************************************************/ 380 ///@{ 381 /** 382 * String representation with latitude and longitude as signed decimal 383 * degrees. 384 * 385 * @param[in] prec precision (relative to about 1m). 386 * @param[in] longfirst if true give longitude first (default = false) 387 * @exception std::bad_alloc if memory for the string can't be allocated. 388 * @return decimal latitude/longitude string representation. 389 * 390 * Precision specifies accuracy of representation as follows: 391 * - prec = −5 (min), 1° 392 * - prec = 0, 10<sup>−5</sup>° (about 1m) 393 * - prec = 3, 10<sup>−8</sup>° 394 * - prec = 9 (max), 10<sup>−14</sup>° 395 **********************************************************************/ 396 std::string GeoRepresentation(int prec = 0, bool longfirst = false) const; 397 398 /** 399 * String representation with latitude and longitude as degrees, minutes, 400 * seconds, and hemisphere. 401 * 402 * @param[in] prec precision (relative to about 1m) 403 * @param[in] longfirst if true give longitude first (default = false) 404 * @param[in] dmssep if non-null, use as the DMS separator character 405 * (instead of d, ', " delimiters). 406 * @exception std::bad_alloc if memory for the string can't be allocated. 407 * @return DMS latitude/longitude string representation. 408 * 409 * Precision specifies accuracy of representation as follows: 410 * - prec = −5 (min), 1° 411 * - prec = −4, 0.1° 412 * - prec = −3, 1' 413 * - prec = −2, 0.1' 414 * - prec = −1, 1" 415 * - prec = 0, 0.1" (about 3m) 416 * - prec = 1, 0.01" 417 * - prec = 10 (max), 10<sup>−11</sup>" 418 **********************************************************************/ 419 std::string DMSRepresentation(int prec = 0, bool longfirst = false, 420 char dmssep = char(0)) 421 const; 422 423 /** 424 * MGRS string. 425 * 426 * @param[in] prec precision (relative to about 1m). 427 * @exception std::bad_alloc if memory for the string can't be allocated. 428 * @return MGRS string. 429 * 430 * This gives the coordinates of the enclosing grid square with size given 431 * by the precision. Thus 38n 444180 3684790 converted to a MGRS 432 * coordinate at precision −2 (100m) is 38SMB441847 and not 433 * 38SMB442848. \e prec specifies the precision of the MGRS string as 434 * follows: 435 * - prec = −6 (min), only the grid zone is returned, e.g., 38S 436 * - prec = −5, 100km, e.g., 38SMB 437 * - prec = −4, 10km 438 * - prec = −3, 1km 439 * - prec = −2, 100m 440 * - prec = −1, 10m 441 * - prec = 0, 1m 442 * - prec = 1, 0.1m 443 * - prec = 6 (max), 1μm 444 **********************************************************************/ 445 std::string MGRSRepresentation(int prec = 0) const; 446 447 /** 448 * UTM/UPS string. 449 * 450 * @param[in] prec precision (relative to about 1m) 451 * @param[in] abbrev if true (the default) use abbreviated (n/s) notation 452 * for hemisphere; otherwise spell out the hemisphere (north/south) 453 * @exception std::bad_alloc if memory for the string can't be allocated. 454 * @return UTM/UPS string representation: zone designator, easting, and 455 * northing. 456 * 457 * Precision specifies accuracy of representation as follows: 458 * - prec = −5 (min), 100km 459 * - prec = −3, 1km 460 * - prec = 0, 1m 461 * - prec = 3, 1mm 462 * - prec = 6, 1μm 463 * - prec = 9 (max), 1nm 464 **********************************************************************/ 465 std::string UTMUPSRepresentation(int prec = 0, bool abbrev = true) const; 466 467 /** 468 * UTM/UPS string with hemisphere override. 469 * 470 * @param[in] northp hemisphere override 471 * @param[in] prec precision (relative to about 1m) 472 * @param[in] abbrev if true (the default) use abbreviated (n/s) notation 473 * for hemisphere; otherwise spell out the hemisphere (north/south) 474 * @exception GeographicErr if the hemisphere override attempts to change 475 * UPS N to UPS S or vice versa. 476 * @exception std::bad_alloc if memory for the string can't be allocated. 477 * @return UTM/UPS string representation: zone designator, easting, and 478 * northing. 479 **********************************************************************/ 480 std::string UTMUPSRepresentation(bool northp, int prec = 0, 481 bool abbrev = true) const; 482 483 /** 484 * MGRS string for the alternate zone. See GeoCoords::MGRSRepresentation. 485 * 486 * @param[in] prec precision (relative to about 1m). 487 * @exception std::bad_alloc if memory for the string can't be allocated. 488 * @return MGRS string. 489 **********************************************************************/ 490 std::string AltMGRSRepresentation(int prec = 0) const; 491 492 /** 493 * UTM/UPS string for the alternate zone. See 494 * GeoCoords::UTMUPSRepresentation. 495 * 496 * @param[in] prec precision (relative to about 1m) 497 * @param[in] abbrev if true (the default) use abbreviated (n/s) notation 498 * for hemisphere; otherwise spell out the hemisphere (north/south) 499 * @exception std::bad_alloc if memory for the string can't be allocated. 500 * @return UTM/UPS string representation: zone designator, easting, and 501 * northing. 502 **********************************************************************/ 503 std::string AltUTMUPSRepresentation(int prec = 0, bool abbrev = true) 504 const; 505 506 /** 507 * UTM/UPS string for the alternate zone, with hemisphere override. 508 * 509 * @param[in] northp hemisphere override 510 * @param[in] prec precision (relative to about 1m) 511 * @param[in] abbrev if true (the default) use abbreviated (n/s) notation 512 * for hemisphere; otherwise spell out the hemisphere (north/south) 513 * @exception GeographicErr if the hemisphere override attempts to change 514 * UPS n to UPS s or vice verse. 515 * @exception std::bad_alloc if memory for the string can't be allocated. 516 * @return UTM/UPS string representation: zone designator, easting, and 517 * northing. 518 **********************************************************************/ 519 std::string AltUTMUPSRepresentation(bool northp, int prec = 0, 520 bool abbrev = true) const; 521 ///@} 522 523 /** \name Inspector functions 524 **********************************************************************/ 525 ///@{ 526 /** 527 * @return \e a the equatorial radius of the WGS84 ellipsoid (meters). 528 * 529 * (The WGS84 value is returned because the UTM and UPS projections are 530 * based on this ellipsoid.) 531 **********************************************************************/ EquatorialRadius() const532 Math::real EquatorialRadius() const { return UTMUPS::EquatorialRadius(); } 533 534 /** 535 * @return \e f the flattening of the WGS84 ellipsoid. 536 * 537 * (The WGS84 value is returned because the UTM and UPS projections are 538 * based on this ellipsoid.) 539 **********************************************************************/ Flattening() const540 Math::real Flattening() const { return UTMUPS::Flattening(); } 541 542 /** 543 * \deprecated An old name for EquatorialRadius(). 544 **********************************************************************/ 545 GEOGRAPHICLIB_DEPRECATED("Use EquatorialRadius()") MajorRadius() const546 Math::real MajorRadius() const { return EquatorialRadius(); } 547 ///@} 548 549 }; 550 551 } // namespace GeographicLib 552 553 #endif // GEOGRAPHICLIB_GEOCOORDS_HPP 554