1 /** 2 * \file GeoCoords.cpp 3 * \brief Implementation 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 #include <GeographicLib/GeoCoords.hpp> 11 #include <GeographicLib/MGRS.hpp> 12 #include <GeographicLib/DMS.hpp> 13 #include <GeographicLib/Utility.hpp> 14 15 namespace GeographicLib { 16 17 using namespace std; 18 Reset(const std::string & s,bool centerp,bool longfirst)19 void GeoCoords::Reset(const std::string& s, bool centerp, bool longfirst) { 20 vector<string> sa; 21 const char* spaces = " \t\n\v\f\r,"; // Include comma as a space 22 for (string::size_type pos0 = 0, pos1; pos0 != string::npos;) { 23 pos1 = s.find_first_not_of(spaces, pos0); 24 if (pos1 == string::npos) 25 break; 26 pos0 = s.find_first_of(spaces, pos1); 27 sa.push_back(s.substr(pos1, pos0 == string::npos ? pos0 : pos0 - pos1)); 28 } 29 if (sa.size() == 1) { 30 int prec; 31 MGRS::Reverse(sa[0], _zone, _northp, _easting, _northing, prec, centerp); 32 UTMUPS::Reverse(_zone, _northp, _easting, _northing, 33 _lat, _long, _gamma, _k); 34 } else if (sa.size() == 2) { 35 DMS::DecodeLatLon(sa[0], sa[1], _lat, _long, longfirst); 36 _long = Math::AngNormalize(_long); 37 UTMUPS::Forward( _lat, _long, 38 _zone, _northp, _easting, _northing, _gamma, _k); 39 } else if (sa.size() == 3) { 40 unsigned zoneind, coordind; 41 if (sa[0].size() > 0 && isalpha(sa[0][sa[0].size() - 1])) { 42 zoneind = 0; 43 coordind = 1; 44 } else if (sa[2].size() > 0 && isalpha(sa[2][sa[2].size() - 1])) { 45 zoneind = 2; 46 coordind = 0; 47 } else 48 throw GeographicErr("Neither " + sa[0] + " nor " + sa[2] 49 + " of the form UTM/UPS Zone + Hemisphere" 50 + " (ex: 38n, 09s, n)"); 51 UTMUPS::DecodeZone(sa[zoneind], _zone, _northp); 52 for (unsigned i = 0; i < 2; ++i) 53 (i ? _northing : _easting) = Utility::val<real>(sa[coordind + i]); 54 UTMUPS::Reverse(_zone, _northp, _easting, _northing, 55 _lat, _long, _gamma, _k); 56 FixHemisphere(); 57 } else 58 throw GeographicErr("Coordinate requires 1, 2, or 3 elements"); 59 CopyToAlt(); 60 } 61 GeoRepresentation(int prec,bool longfirst) const62 string GeoCoords::GeoRepresentation(int prec, bool longfirst) const { 63 using std::isnan; // Needed for Centos 7, ubuntu 14 64 prec = max(0, min(9 + Math::extra_digits(), prec) + 5); 65 ostringstream os; 66 os << fixed << setprecision(prec); 67 real a = longfirst ? _long : _lat; 68 real b = longfirst ? _lat : _long; 69 if (!isnan(a)) 70 os << a; 71 else 72 os << "nan"; 73 os << " "; 74 if (!isnan(b)) 75 os << b; 76 else 77 os << "nan"; 78 return os.str(); 79 } 80 DMSRepresentation(int prec,bool longfirst,char dmssep) const81 string GeoCoords::DMSRepresentation(int prec, bool longfirst, 82 char dmssep) const { 83 prec = max(0, min(10 + Math::extra_digits(), prec) + 5); 84 return DMS::Encode(longfirst ? _long : _lat, unsigned(prec), 85 longfirst ? DMS::LONGITUDE : DMS::LATITUDE, dmssep) + 86 " " + DMS::Encode(longfirst ? _lat : _long, unsigned(prec), 87 longfirst ? DMS::LATITUDE : DMS::LONGITUDE, dmssep); 88 } 89 MGRSRepresentation(int prec) const90 string GeoCoords::MGRSRepresentation(int prec) const { 91 // Max precision is um 92 prec = max(-1, min(6, prec) + 5); 93 string mgrs; 94 MGRS::Forward(_zone, _northp, _easting, _northing, _lat, prec, mgrs); 95 return mgrs; 96 } 97 AltMGRSRepresentation(int prec) const98 string GeoCoords::AltMGRSRepresentation(int prec) const { 99 // Max precision is um 100 prec = max(-1, min(6, prec) + 5); 101 string mgrs; 102 MGRS::Forward(_alt_zone, _northp, _alt_easting, _alt_northing, _lat, prec, 103 mgrs); 104 return mgrs; 105 } 106 UTMUPSString(int zone,bool northp,real easting,real northing,int prec,bool abbrev,string & utm)107 void GeoCoords::UTMUPSString(int zone, bool northp, 108 real easting, real northing, int prec, 109 bool abbrev, string& utm) { 110 ostringstream os; 111 prec = max(-5, min(9 + Math::extra_digits(), prec)); 112 // Need extra real because, since C++11, pow(float, int) returns double 113 real scale = prec < 0 ? real(pow(real(10), -prec)) : real(1); 114 os << UTMUPS::EncodeZone(zone, northp, abbrev) << fixed << setfill('0'); 115 if (isfinite(easting)) { 116 os << " " << Utility::str(easting / scale, max(0, prec)); 117 if (prec < 0 && abs(easting / scale) > real(0.5)) 118 os << setw(-prec) << 0; 119 } else 120 os << " nan"; 121 if (isfinite(northing)) { 122 os << " " << Utility::str(northing / scale, max(0, prec)); 123 if (prec < 0 && abs(northing / scale) > real(0.5)) 124 os << setw(-prec) << 0; 125 } else 126 os << " nan"; 127 utm = os.str(); 128 } 129 UTMUPSRepresentation(int prec,bool abbrev) const130 string GeoCoords::UTMUPSRepresentation(int prec, bool abbrev) const { 131 string utm; 132 UTMUPSString(_zone, _northp, _easting, _northing, prec, abbrev, utm); 133 return utm; 134 } 135 UTMUPSRepresentation(bool northp,int prec,bool abbrev) const136 string GeoCoords::UTMUPSRepresentation(bool northp, int prec, 137 bool abbrev) const { 138 real e, n; 139 int z; 140 UTMUPS::Transfer(_zone, _northp, _easting, _northing, 141 _zone, northp, e, n, z); 142 string utm; 143 UTMUPSString(_zone, northp, e, n, prec, abbrev, utm); 144 return utm; 145 } 146 AltUTMUPSRepresentation(int prec,bool abbrev) const147 string GeoCoords::AltUTMUPSRepresentation(int prec, bool abbrev) const { 148 string utm; 149 UTMUPSString(_alt_zone, _northp, _alt_easting, _alt_northing, prec, 150 abbrev, utm); 151 return utm; 152 } 153 AltUTMUPSRepresentation(bool northp,int prec,bool abbrev) const154 string GeoCoords::AltUTMUPSRepresentation(bool northp, int prec, 155 bool abbrev) const { 156 real e, n; 157 int z; 158 UTMUPS::Transfer(_alt_zone, _northp, _alt_easting, _alt_northing, 159 _alt_zone, northp, e, n, z); 160 string utm; 161 UTMUPSString(_alt_zone, northp, e, n, prec, abbrev, utm); 162 return utm; 163 } 164 FixHemisphere()165 void GeoCoords::FixHemisphere() { 166 using std::isnan; // Needed for Centos 7, ubuntu 14 167 if (_lat == 0 || (_northp && _lat >= 0) || (!_northp && _lat < 0) || 168 isnan(_lat)) 169 // Allow either hemisphere for equator 170 return; 171 if (_zone != UTMUPS::UPS) { 172 _northing += (_northp ? 1 : -1) * UTMUPS::UTMShift(); 173 _northp = !_northp; 174 } else 175 throw GeographicErr("Hemisphere mixup"); 176 } 177 178 } // namespace GeographicLib 179