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