/** * \file GeoCoords.hpp * \brief Header for GeographicLib::GeoCoords class * * Copyright (c) Charles Karney (2008-2020) and licensed * under the MIT/X11 License. For more information, see * https://geographiclib.sourceforge.io/ **********************************************************************/ #if !defined(GEOGRAPHICLIB_GEOCOORDS_HPP) #define GEOGRAPHICLIB_GEOCOORDS_HPP 1 #include #include namespace GeographicLib { /** * \brief Conversion between geographic coordinates * * This class stores a geographic position which may be set via the * constructors or Reset via * - latitude and longitude * - UTM or UPS coordinates * - a string representation of these or an MGRS coordinate string * * The state consists of the latitude and longitude and the supplied UTM or * UPS coordinates (possibly derived from the MGRS coordinates). If latitude * and longitude were given then the UTM/UPS coordinates follows the standard * conventions. * * The mutable state consists of the UTM or UPS coordinates for a alternate * zone. A method SetAltZone is provided to set the alternate UPS/UTM zone. * * Methods are provided to return the geographic coordinates, the input UTM * or UPS coordinates (and associated meridian convergence and scale), or * alternate UTM or UPS coordinates (and their associated meridian * convergence and scale). * * Once the input string has been parsed, you can print the result out in any * of the formats, decimal degrees, degrees minutes seconds, MGRS, UTM/UPS. * * Example of use: * \include example-GeoCoords.cpp * * GeoConvert is a command-line utility * providing access to the functionality of GeoCoords. **********************************************************************/ class GEOGRAPHICLIB_EXPORT GeoCoords { private: typedef Math::real real; real _lat, _long, _easting, _northing, _gamma, _k; bool _northp; int _zone; // See UTMUPS::zonespec mutable real _alt_easting, _alt_northing, _alt_gamma, _alt_k; mutable int _alt_zone; void CopyToAlt() const { _alt_easting = _easting; _alt_northing = _northing; _alt_gamma = _gamma; _alt_k = _k; _alt_zone = _zone; } static void UTMUPSString(int zone, bool northp, real easting, real northing, int prec, bool abbrev, std::string& utm); void FixHemisphere(); public: /** \name Initializing the GeoCoords object **********************************************************************/ ///@{ /** * The default constructor sets the coordinate as undefined. **********************************************************************/ GeoCoords() : _lat(Math::NaN()) , _long(Math::NaN()) , _easting(Math::NaN()) , _northing(Math::NaN()) , _gamma(Math::NaN()) , _k(Math::NaN()) , _northp(false) , _zone(UTMUPS::INVALID) { CopyToAlt(); } /** * Construct from a string. * * @param[in] s 1-element, 2-element, or 3-element string representation of * the position. * @param[in] centerp governs the interpretation of MGRS coordinates (see * below). * @param[in] longfirst governs the interpretation of geographic * coordinates (see below). * @exception GeographicErr if the \e s is malformed (see below). * * Parse as a string and interpret it as a geographic position. The input * string is broken into space (or comma) separated pieces and Basic * decision on which format is based on number of components * -# MGRS * -# "Lat Long" or "Long Lat" * -# "Zone Easting Northing" or "Easting Northing Zone" * * The following inputs are approximately the same (Ar Ramadi Bridge, Iraq) * - Latitude and Longitude * - 33.44 43.27 * - N33d26.4' E43d16.2' * - 43d16'12"E 33d26'24"N * - 43:16:12E 33:26:24 * - MGRS * - 38SLC30 * - 38SLC391014 * - 38SLC3918701405 * - 37SHT9708 * - UTM * - 38n 339188 3701405 * - 897039 3708229 37n * * Latitude and Longitude parsing: Latitude precedes longitude, * unless a N, S, E, W hemisphere designator is used on one or both * coordinates. If \e longfirst = true (default is false), then * longitude precedes latitude in the absence of a hemisphere designator. * Thus (with \e longfirst = false) * - 40 -75 * - N40 W75 * - -75 N40 * - 75W 40N * - E-75 -40S * . * are all the same position. The coordinates may be given in * decimal degrees, degrees and decimal minutes, degrees, minutes, * seconds, etc. Use d, ', and " to mark off the degrees, * minutes and seconds. Various alternative symbols for degrees, minutes, * and seconds are allowed. Alternatively, use : to separate these * components. A single addition or subtraction is allowed. (See * DMS::Decode for details.) Thus * - 40d30'30" * - 40d30'30 * - 40°30'30 * - 40d30.5' * - 40d30.5 * - 40:30:30 * - 40:30.5 * - 40.508333333 * - 40:30+0:0:30 * - 40:31-0:0.5 * . * all specify the same angle. The leading sign applies to the following * components so -1d30 is -(1+30/60) = −1.5. However, note * that -1:30-0:0:15 is parsed as (-1:30) + (-0:0:15) = −(1+30/60) * − (15/3600). Latitudes must be in the range [−90°, * 90°]. Internally longitudes are reduced to the range * [−180°, 180°]. * * UTM/UPS parsing: For UTM zones (−80° ≤ Lat < * 84°), the zone designator is made up of a zone number (for 1 to 60) * and a hemisphere letter (n or s), e.g., 38n (38north can also be used). * The latitude band designer ([C--M] in the southern hemisphere and [N--X] * in the northern) should NOT be used. (This is part of the MGRS * coordinate.) The zone designator for the poles (where UPS is employed) * is a hemisphere letter by itself, i.e., n or s (north or south can also * be used). * * MGRS parsing interprets the grid references as square area at the * specified precision (1m, 10m, 100m, etc.). If \e centerp = true (the * default), the center of this square is then taken to be the precise * position; thus: * - 38SMB = 38n 450000 3650000 * - 38SMB4484 = 38n 444500 3684500 * - 38SMB44148470 = 38n 444145 3684705 * . * Otherwise, the "south-west" corner of the square is used, i.e., * - 38SMB = 38n 400000 3600000 * - 38SMB4484 = 38n 444000 3684000 * - 38SMB44148470 = 38n 444140 3684700 **********************************************************************/ explicit GeoCoords(const std::string& s, bool centerp = true, bool longfirst = false) { Reset(s, centerp, longfirst); } /** * Construct from geographic coordinates. * * @param[in] latitude (degrees). * @param[in] longitude (degrees). * @param[in] zone if specified, force the UTM/UPS representation to use a * specified zone using the rules given in UTMUPS::zonespec. * @exception GeographicErr if \e latitude is not in [−90°, * 90°]. * @exception GeographicErr if \e zone cannot be used for this location. **********************************************************************/ GeoCoords(real latitude, real longitude, int zone = UTMUPS::STANDARD) { Reset(latitude, longitude, zone); } /** * Construct from UTM/UPS coordinates. * * @param[in] zone UTM zone (zero means UPS). * @param[in] northp hemisphere (true means north, false means south). * @param[in] easting (meters). * @param[in] northing (meters). * @exception GeographicErr if \e zone, \e easting, or \e northing is * outside its allowed range. **********************************************************************/ GeoCoords(int zone, bool northp, real easting, real northing) { Reset(zone, northp, easting, northing); } /** * Reset the location from a string. See * GeoCoords(const std::string& s, bool centerp, bool longfirst). * * @param[in] s 1-element, 2-element, or 3-element string representation of * the position. * @param[in] centerp governs the interpretation of MGRS coordinates. * @param[in] longfirst governs the interpretation of geographic * coordinates. * @exception GeographicErr if the \e s is malformed. **********************************************************************/ void Reset(const std::string& s, bool centerp = true, bool longfirst = false); /** * Reset the location in terms of geographic coordinates. See * GeoCoords(real latitude, real longitude, int zone). * * @param[in] latitude (degrees). * @param[in] longitude (degrees). * @param[in] zone if specified, force the UTM/UPS representation to use a * specified zone using the rules given in UTMUPS::zonespec. * @exception GeographicErr if \e latitude is not in [−90°, * 90°]. * @exception GeographicErr if \e zone cannot be used for this location. **********************************************************************/ void Reset(real latitude, real longitude, int zone = UTMUPS::STANDARD) { UTMUPS::Forward(latitude, longitude, _zone, _northp, _easting, _northing, _gamma, _k, zone); _lat = latitude; _long = longitude; if (_long >= 180) _long -= 360; else if (_long < -180) _long += 360; CopyToAlt(); } /** * Reset the location in terms of UPS/UPS coordinates. See * GeoCoords(int zone, bool northp, real easting, real northing). * * @param[in] zone UTM zone (zero means UPS). * @param[in] northp hemisphere (true means north, false means south). * @param[in] easting (meters). * @param[in] northing (meters). * @exception GeographicErr if \e zone, \e easting, or \e northing is * outside its allowed range. **********************************************************************/ void Reset(int zone, bool northp, real easting, real northing) { UTMUPS::Reverse(zone, northp, easting, northing, _lat, _long, _gamma, _k); _zone = zone; _northp = northp; _easting = easting; _northing = northing; FixHemisphere(); CopyToAlt(); } ///@} /** \name Querying the GeoCoords object **********************************************************************/ ///@{ /** * @return latitude (degrees) **********************************************************************/ Math::real Latitude() const { return _lat; } /** * @return longitude (degrees) **********************************************************************/ Math::real Longitude() const { return _long; } /** * @return easting (meters) **********************************************************************/ Math::real Easting() const { return _easting; } /** * @return northing (meters) **********************************************************************/ Math::real Northing() const { return _northing; } /** * @return meridian convergence (degrees) for the UTM/UPS projection. **********************************************************************/ Math::real Convergence() const { return _gamma; } /** * @return scale for the UTM/UPS projection. **********************************************************************/ Math::real Scale() const { return _k; } /** * @return hemisphere (false means south, true means north). **********************************************************************/ bool Northp() const { return _northp; } /** * @return hemisphere letter n or s. **********************************************************************/ char Hemisphere() const { return _northp ? 'n' : 's'; } /** * @return the zone corresponding to the input (return 0 for UPS). **********************************************************************/ int Zone() const { return _zone; } ///@} /** \name Setting and querying the alternate zone **********************************************************************/ ///@{ /** * Specify alternate zone number. * * @param[in] zone zone number for the alternate representation. * @exception GeographicErr if \e zone cannot be used for this location. * * See UTMUPS::zonespec for more information on the interpretation of \e * zone. Note that \e zone == UTMUPS::STANDARD (the default) use the * standard UPS or UTM zone, UTMUPS::MATCH does nothing retaining the * existing alternate representation. Before this is called the alternate * zone is the input zone. **********************************************************************/ void SetAltZone(int zone = UTMUPS::STANDARD) const { if (zone == UTMUPS::MATCH) return; zone = UTMUPS::StandardZone(_lat, _long, zone); if (zone == _zone) CopyToAlt(); else { bool northp; UTMUPS::Forward(_lat, _long, _alt_zone, northp, _alt_easting, _alt_northing, _alt_gamma, _alt_k, zone); } } /** * @return current alternate zone (return 0 for UPS). **********************************************************************/ int AltZone() const { return _alt_zone; } /** * @return easting (meters) for alternate zone. **********************************************************************/ Math::real AltEasting() const { return _alt_easting; } /** * @return northing (meters) for alternate zone. **********************************************************************/ Math::real AltNorthing() const { return _alt_northing; } /** * @return meridian convergence (degrees) for alternate zone. **********************************************************************/ Math::real AltConvergence() const { return _alt_gamma; } /** * @return scale for alternate zone. **********************************************************************/ Math::real AltScale() const { return _alt_k; } ///@} /** \name String representations of the GeoCoords object **********************************************************************/ ///@{ /** * String representation with latitude and longitude as signed decimal * degrees. * * @param[in] prec precision (relative to about 1m). * @param[in] longfirst if true give longitude first (default = false) * @exception std::bad_alloc if memory for the string can't be allocated. * @return decimal latitude/longitude string representation. * * Precision specifies accuracy of representation as follows: * - prec = −5 (min), 1° * - prec = 0, 10−5° (about 1m) * - prec = 3, 10−8° * - prec = 9 (max), 10−14° **********************************************************************/ std::string GeoRepresentation(int prec = 0, bool longfirst = false) const; /** * String representation with latitude and longitude as degrees, minutes, * seconds, and hemisphere. * * @param[in] prec precision (relative to about 1m) * @param[in] longfirst if true give longitude first (default = false) * @param[in] dmssep if non-null, use as the DMS separator character * (instead of d, ', " delimiters). * @exception std::bad_alloc if memory for the string can't be allocated. * @return DMS latitude/longitude string representation. * * Precision specifies accuracy of representation as follows: * - prec = −5 (min), 1° * - prec = −4, 0.1° * - prec = −3, 1' * - prec = −2, 0.1' * - prec = −1, 1" * - prec = 0, 0.1" (about 3m) * - prec = 1, 0.01" * - prec = 10 (max), 10−11" **********************************************************************/ std::string DMSRepresentation(int prec = 0, bool longfirst = false, char dmssep = char(0)) const; /** * MGRS string. * * @param[in] prec precision (relative to about 1m). * @exception std::bad_alloc if memory for the string can't be allocated. * @return MGRS string. * * This gives the coordinates of the enclosing grid square with size given * by the precision. Thus 38n 444180 3684790 converted to a MGRS * coordinate at precision −2 (100m) is 38SMB441847 and not * 38SMB442848. \e prec specifies the precision of the MGRS string as * follows: * - prec = −6 (min), only the grid zone is returned, e.g., 38S * - prec = −5, 100km, e.g., 38SMB * - prec = −4, 10km * - prec = −3, 1km * - prec = −2, 100m * - prec = −1, 10m * - prec = 0, 1m * - prec = 1, 0.1m * - prec = 6 (max), 1μm **********************************************************************/ std::string MGRSRepresentation(int prec = 0) const; /** * UTM/UPS string. * * @param[in] prec precision (relative to about 1m) * @param[in] abbrev if true (the default) use abbreviated (n/s) notation * for hemisphere; otherwise spell out the hemisphere (north/south) * @exception std::bad_alloc if memory for the string can't be allocated. * @return UTM/UPS string representation: zone designator, easting, and * northing. * * Precision specifies accuracy of representation as follows: * - prec = −5 (min), 100km * - prec = −3, 1km * - prec = 0, 1m * - prec = 3, 1mm * - prec = 6, 1μm * - prec = 9 (max), 1nm **********************************************************************/ std::string UTMUPSRepresentation(int prec = 0, bool abbrev = true) const; /** * UTM/UPS string with hemisphere override. * * @param[in] northp hemisphere override * @param[in] prec precision (relative to about 1m) * @param[in] abbrev if true (the default) use abbreviated (n/s) notation * for hemisphere; otherwise spell out the hemisphere (north/south) * @exception GeographicErr if the hemisphere override attempts to change * UPS N to UPS S or vice versa. * @exception std::bad_alloc if memory for the string can't be allocated. * @return UTM/UPS string representation: zone designator, easting, and * northing. **********************************************************************/ std::string UTMUPSRepresentation(bool northp, int prec = 0, bool abbrev = true) const; /** * MGRS string for the alternate zone. See GeoCoords::MGRSRepresentation. * * @param[in] prec precision (relative to about 1m). * @exception std::bad_alloc if memory for the string can't be allocated. * @return MGRS string. **********************************************************************/ std::string AltMGRSRepresentation(int prec = 0) const; /** * UTM/UPS string for the alternate zone. See * GeoCoords::UTMUPSRepresentation. * * @param[in] prec precision (relative to about 1m) * @param[in] abbrev if true (the default) use abbreviated (n/s) notation * for hemisphere; otherwise spell out the hemisphere (north/south) * @exception std::bad_alloc if memory for the string can't be allocated. * @return UTM/UPS string representation: zone designator, easting, and * northing. **********************************************************************/ std::string AltUTMUPSRepresentation(int prec = 0, bool abbrev = true) const; /** * UTM/UPS string for the alternate zone, with hemisphere override. * * @param[in] northp hemisphere override * @param[in] prec precision (relative to about 1m) * @param[in] abbrev if true (the default) use abbreviated (n/s) notation * for hemisphere; otherwise spell out the hemisphere (north/south) * @exception GeographicErr if the hemisphere override attempts to change * UPS n to UPS s or vice verse. * @exception std::bad_alloc if memory for the string can't be allocated. * @return UTM/UPS string representation: zone designator, easting, and * northing. **********************************************************************/ std::string AltUTMUPSRepresentation(bool northp, int prec = 0, bool abbrev = true) const; ///@} /** \name Inspector functions **********************************************************************/ ///@{ /** * @return \e a the equatorial radius of the WGS84 ellipsoid (meters). * * (The WGS84 value is returned because the UTM and UPS projections are * based on this ellipsoid.) **********************************************************************/ Math::real EquatorialRadius() const { return UTMUPS::EquatorialRadius(); } /** * @return \e f the flattening of the WGS84 ellipsoid. * * (The WGS84 value is returned because the UTM and UPS projections are * based on this ellipsoid.) **********************************************************************/ Math::real Flattening() const { return UTMUPS::Flattening(); } /** * \deprecated An old name for EquatorialRadius(). **********************************************************************/ GEOGRAPHICLIB_DEPRECATED("Use EquatorialRadius()") Math::real MajorRadius() const { return EquatorialRadius(); } ///@} }; } // namespace GeographicLib #endif // GEOGRAPHICLIB_GEOCOORDS_HPP