GeographicLib  2.1
UTMUPS.cpp
Go to the documentation of this file.
1 /**
2  * \file UTMUPS.cpp
3  * \brief Implementation for GeographicLib::UTMUPS class
4  *
5  * Copyright (c) Charles Karney (2008-2022) <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/UTMUPS.hpp>
11 #include <GeographicLib/MGRS.hpp>
15 
16 #if defined(_MSC_VER)
17 // Squelch warnings about enum-float expressions
18 # pragma warning (disable: 5055)
19 #endif
20 
21 namespace GeographicLib {
22 
23  using namespace std;
24 
25  const int UTMUPS::falseeasting_[] =
26  { MGRS::upseasting_ * MGRS::tile_, MGRS::upseasting_ * MGRS::tile_,
27  MGRS::utmeasting_ * MGRS::tile_, MGRS::utmeasting_ * MGRS::tile_ };
28  const int UTMUPS::falsenorthing_[] =
29  { MGRS::upseasting_ * MGRS::tile_, MGRS::upseasting_ * MGRS::tile_,
30  MGRS::maxutmSrow_ * MGRS::tile_, MGRS::minutmNrow_ * MGRS::tile_ };
31  const int UTMUPS::mineasting_[] =
32  { MGRS::minupsSind_ * MGRS::tile_, MGRS::minupsNind_ * MGRS::tile_,
33  MGRS::minutmcol_ * MGRS::tile_, MGRS::minutmcol_ * MGRS::tile_ };
34  const int UTMUPS::maxeasting_[] =
35  { MGRS::maxupsSind_ * MGRS::tile_, MGRS::maxupsNind_ * MGRS::tile_,
36  MGRS::maxutmcol_ * MGRS::tile_, MGRS::maxutmcol_ * MGRS::tile_ };
37  const int UTMUPS::minnorthing_[] =
38  { MGRS::minupsSind_ * MGRS::tile_, MGRS::minupsNind_ * MGRS::tile_,
39  MGRS::minutmSrow_ * MGRS::tile_,
40  (MGRS::minutmNrow_ + MGRS::minutmSrow_ - MGRS::maxutmSrow_)
41  * MGRS::tile_ };
42  const int UTMUPS::maxnorthing_[] =
43  { MGRS::maxupsSind_ * MGRS::tile_, MGRS::maxupsNind_ * MGRS::tile_,
44  (MGRS::maxutmSrow_ + MGRS::maxutmNrow_ - MGRS::minutmNrow_) *
45  MGRS::tile_,
46  MGRS::maxutmNrow_ * MGRS::tile_ };
47 
48  int UTMUPS::StandardZone(real lat, real lon, int setzone) {
49  using std::isnan; // Needed for Centos 7, ubuntu 14
50  if (!(setzone >= MINPSEUDOZONE && setzone <= MAXZONE))
51  throw GeographicErr("Illegal zone requested " + Utility::str(setzone));
52  if (setzone >= MINZONE || setzone == INVALID)
53  return setzone;
54  if (isnan(lat) || isnan(lon)) // Check if lat or lon is a NaN
55  return INVALID;
56  if (setzone == UTM || (lat >= -80 && lat < 84)) {
57  int ilon = int(floor(Math::AngNormalize(lon)));
58  if (ilon == Math::hd) ilon = -Math::hd; // ilon now in [-180,180)
59  int zone = (ilon + 186)/6;
60  int band = MGRS::LatitudeBand(lat);
61  if (band == 7 && zone == 31 && ilon >= 3) // The Norway exception
62  zone = 32;
63  else if (band == 9 && ilon >= 0 && ilon < 42) // The Svalbard exception
64  zone = 2 * ((ilon + 183)/12) + 1;
65  return zone;
66  } else
67  return UPS;
68  }
69 
70  void UTMUPS::Forward(real lat, real lon,
71  int& zone, bool& northp, real& x, real& y,
72  real& gamma, real& k,
73  int setzone, bool mgrslimits) {
74  if (fabs(lat) > Math::qd)
75  throw GeographicErr("Latitude " + Utility::str(lat)
76  + "d not in [-" + to_string(Math::qd)
77  + "d, " + to_string(Math::qd) + "d]");
78  bool northp1 = !(signbit(lat));
79  int zone1 = StandardZone(lat, lon, setzone);
80  if (zone1 == INVALID) {
81  zone = zone1;
82  northp = northp1;
83  x = y = gamma = k = Math::NaN();
84  return;
85  }
86  real x1, y1, gamma1, k1;
87  bool utmp = zone1 != UPS;
88  if (utmp) {
89  real
90  lon0 = CentralMeridian(zone1),
91  dlon = Math::AngDiff(lon0, lon);
92  if (!(dlon <= 60))
93  // Check isn't really necessary because CheckCoords catches this case.
94  // But this allows a more meaningful error message to be given.
95  throw GeographicErr("Longitude " + Utility::str(lon)
96  + "d more than 60d from center of UTM zone "
97  + Utility::str(zone1));
98  TransverseMercator::UTM().Forward(lon0, lat, lon, x1, y1, gamma1, k1);
99  } else {
100  if (fabs(lat) < 70)
101  // Check isn't really necessary ... (see above).
102  throw GeographicErr("Latitude " + Utility::str(lat)
103  + "d more than 20d from "
104  + (northp1 ? "N" : "S") + " pole");
105  PolarStereographic::UPS().Forward(northp1, lat, lon, x1, y1, gamma1, k1);
106  }
107  int ind = (utmp ? 2 : 0) + (northp1 ? 1 : 0);
108  x1 += falseeasting_[ind];
109  y1 += falsenorthing_[ind];
110  if (! CheckCoords(zone1 != UPS, northp1, x1, y1, mgrslimits, false) )
111  throw GeographicErr("Latitude " + Utility::str(lat)
112  + ", longitude " + Utility::str(lon)
113  + " out of legal range for "
114  + (utmp ? "UTM zone " + Utility::str(zone1) :
115  "UPS"));
116  zone = zone1;
117  northp = northp1;
118  x = x1;
119  y = y1;
120  gamma = gamma1;
121  k = k1;
122  }
123 
124  void UTMUPS::Reverse(int zone, bool northp, real x, real y,
125  real& lat, real& lon, real& gamma, real& k,
126  bool mgrslimits) {
127  using std::isnan; // Needed for Centos 7, ubuntu 14
128  if (zone == INVALID || isnan(x) || isnan(y)) {
129  lat = lon = gamma = k = Math::NaN();
130  return;
131  }
132  if (!(zone >= MINZONE && zone <= MAXZONE))
133  throw GeographicErr("Zone " + Utility::str(zone)
134  + " not in range [0, 60]");
135  bool utmp = zone != UPS;
136  CheckCoords(utmp, northp, x, y, mgrslimits);
137  int ind = (utmp ? 2 : 0) + (northp ? 1 : 0);
138  x -= falseeasting_[ind];
139  y -= falsenorthing_[ind];
140  if (utmp)
141  TransverseMercator::UTM().Reverse(CentralMeridian(zone),
142  x, y, lat, lon, gamma, k);
143  else
144  PolarStereographic::UPS().Reverse(northp, x, y, lat, lon, gamma, k);
145  }
146 
147  bool UTMUPS::CheckCoords(bool utmp, bool northp, real x, real y,
148  bool mgrslimits, bool throwp) {
149  // Limits are all multiples of 100km and are all closed on the both ends.
150  // Failure tests are such that NaNs succeed.
151  real slop = mgrslimits ? 0 : MGRS::tile_;
152  int ind = (utmp ? 2 : 0) + (northp ? 1 : 0);
153  if (x < mineasting_[ind] - slop || x > maxeasting_[ind] + slop) {
154  if (!throwp) return false;
155  throw GeographicErr("Easting " + Utility::str(x/1000) + "km not in "
156  + (mgrslimits ? "MGRS/" : "")
157  + (utmp ? "UTM" : "UPS") + " range for "
158  + (northp ? "N" : "S" ) + " hemisphere ["
159  + Utility::str((mineasting_[ind] - slop)/1000)
160  + "km, "
161  + Utility::str((maxeasting_[ind] + slop)/1000)
162  + "km]");
163  }
164  if (y < minnorthing_[ind] - slop || y > maxnorthing_[ind] + slop) {
165  if (!throwp) return false;
166  throw GeographicErr("Northing " + Utility::str(y/1000) + "km not in "
167  + (mgrslimits ? "MGRS/" : "")
168  + (utmp ? "UTM" : "UPS") + " range for "
169  + (northp ? "N" : "S" ) + " hemisphere ["
170  + Utility::str((minnorthing_[ind] - slop)/1000)
171  + "km, "
172  + Utility::str((maxnorthing_[ind] + slop)/1000)
173  + "km]");
174  }
175  return true;
176  }
177 
178  void UTMUPS::Transfer(int zonein, bool northpin, real xin, real yin,
179  int zoneout, bool northpout, real& xout, real& yout,
180  int& zone) {
181  bool northp = northpin;
182  if (zonein != zoneout) {
183  // Determine lat, lon
184  real lat, lon;
185  GeographicLib::UTMUPS::Reverse(zonein, northpin, xin, yin, lat, lon);
186  // Try converting to zoneout
187  real x, y;
188  int zone1;
189  GeographicLib::UTMUPS::Forward(lat, lon, zone1, northp, x, y,
190  zoneout == UTMUPS::MATCH
191  ? zonein : zoneout);
192  if (zone1 == 0 && northp != northpout)
193  throw GeographicErr
194  ("Attempt to transfer UPS coordinates between hemispheres");
195  zone = zone1;
196  xout = x;
197  yout = y;
198  } else {
199  if (zoneout == 0 && northp != northpout)
200  throw GeographicErr
201  ("Attempt to transfer UPS coordinates between hemispheres");
202  zone = zoneout;
203  xout = xin;
204  yout = yin;
205  }
206  if (northp != northpout)
207  // Can't get here if UPS
208  yout += (northpout ? -1 : 1) * MGRS::utmNshift_;
209  return;
210  }
211 
212  void UTMUPS::DecodeZone(const string& zonestr, int& zone, bool& northp)
213  {
214  unsigned zlen = unsigned(zonestr.size());
215  if (zlen == 0)
216  throw GeographicErr("Empty zone specification");
217  // Longest zone spec is 32north, 42south, invalid = 7
218  if (zlen > 7)
219  throw GeographicErr("More than 7 characters in zone specification "
220  + zonestr);
221 
222  const char* c = zonestr.c_str();
223  char* q;
224  int zone1 = strtol(c, &q, 10);
225  // if (zone1 == 0) zone1 = UPS; (not necessary)
226 
227  if (zone1 == UPS) {
228  if (!(q == c))
229  // Don't allow 0n as an alternative to n for UPS coordinates
230  throw GeographicErr("Illegal zone 0 in " + zonestr +
231  ", use just the hemisphere for UPS");
232  } else if (!(zone1 >= MINUTMZONE && zone1 <= MAXUTMZONE))
233  throw GeographicErr("Zone " + Utility::str(zone1)
234  + " not in range [1, 60]");
235  else if (!isdigit(zonestr[0]))
236  throw GeographicErr("Must use unsigned number for zone "
237  + Utility::str(zone1));
238  else if (q - c > 2)
239  throw GeographicErr("More than 2 digits use to specify zone "
240  + Utility::str(zone1));
241 
242  string hemi(zonestr, q - c);
243  for (string::iterator p = hemi.begin(); p != hemi.end(); ++p)
244  *p = char(tolower(*p));
245  if (q == c && (hemi == "inv" || hemi == "invalid")) {
246  zone = INVALID;
247  northp = false;
248  return;
249  }
250  bool northp1 = hemi == "north" || hemi == "n";
251  if (!(northp1 || hemi == "south" || hemi == "s"))
252  throw GeographicErr(string("Illegal hemisphere ") + hemi + " in "
253  + zonestr + ", specify north or south");
254  zone = zone1;
255  northp = northp1;
256  }
257 
258  string UTMUPS::EncodeZone(int zone, bool northp, bool abbrev) {
259  if (zone == INVALID)
260  return string(abbrev ? "inv" : "invalid");
261  if (!(zone >= MINZONE && zone <= MAXZONE))
262  throw GeographicErr("Zone " + Utility::str(zone)
263  + " not in range [0, 60]");
264  ostringstream os;
265  if (zone != UPS)
266  os << setfill('0') << setw(2) << zone;
267  if (abbrev)
268  os << (northp ? 'n' : 's');
269  else
270  os << (northp ? "north" : "south");
271  return os.str();
272  }
273 
274  void UTMUPS::DecodeEPSG(int epsg, int& zone, bool& northp) {
275  northp = false;
276  if (epsg >= epsg01N && epsg <= epsg60N) {
277  zone = (epsg - epsg01N) + MINUTMZONE;
278  northp = true;
279  } else if (epsg == epsgN) {
280  zone = UPS;
281  northp = true;
282  } else if (epsg >= epsg01S && epsg <= epsg60S) {
283  zone = (epsg - epsg01S) + MINUTMZONE;
284  } else if (epsg == epsgS) {
285  zone = UPS;
286  } else {
287  zone = INVALID;
288  }
289  }
290 
291  int UTMUPS::EncodeEPSG(int zone, bool northp) {
292  int epsg = -1;
293  if (zone == UPS)
294  epsg = epsgS;
295  else if (zone >= MINUTMZONE && zone <= MAXUTMZONE)
296  epsg = (zone - MINUTMZONE) + epsg01S;
297  if (epsg >= 0 && northp)
298  epsg += epsgN - epsgS;
299  return epsg;
300  }
301 
302  Math::real UTMUPS::UTMShift() { return real(MGRS::utmNshift_); }
303 
304 } // namespace GeographicLib
GeographicLib::Math::real real
Definition: GeodSolve.cpp:31
Header for GeographicLib::MGRS class.
Header for GeographicLib::PolarStereographic class.
Header for GeographicLib::TransverseMercator class.
Header for GeographicLib::UTMUPS class.
Header for GeographicLib::Utility class.
Exception handling for GeographicLib.
Definition: Constants.hpp:316
static T AngNormalize(T x)
Definition: Math.cpp:71
static T NaN()
Definition: Math.cpp:250
static T AngDiff(T x, T y, T &e)
Definition: Math.cpp:82
@ hd
degrees per half turn
Definition: Math.hpp:144
@ qd
degrees per quarter turn
Definition: Math.hpp:141
void Reverse(bool northp, real x, real y, real &lat, real &lon, real &gamma, real &k) const
static const PolarStereographic & UPS()
void Forward(bool northp, real lat, real lon, real &x, real &y, real &gamma, real &k) const
void Reverse(real lon0, real x, real y, real &lat, real &lon, real &gamma, real &k) const
static const TransverseMercator & UTM()
void Forward(real lon0, real lat, real lon, real &x, real &y, real &gamma, real &k) const
static int EncodeEPSG(int zone, bool northp)
Definition: UTMUPS.cpp:291
static int StandardZone(real lat, real lon, int setzone=STANDARD)
Definition: UTMUPS.cpp:48
static std::string EncodeZone(int zone, bool northp, bool abbrev=true)
Definition: UTMUPS.cpp:258
static void Forward(real lat, real lon, int &zone, bool &northp, real &x, real &y, real &gamma, real &k, int setzone=STANDARD, bool mgrslimits=false)
Definition: UTMUPS.cpp:70
static void DecodeEPSG(int epsg, int &zone, bool &northp)
Definition: UTMUPS.cpp:274
static void Reverse(int zone, bool northp, real x, real y, real &lat, real &lon, real &gamma, real &k, bool mgrslimits=false)
Definition: UTMUPS.cpp:124
static Math::real UTMShift()
Definition: UTMUPS.cpp:302
static void Transfer(int zonein, bool northpin, real xin, real yin, int zoneout, bool northpout, real &xout, real &yout, int &zone)
Definition: UTMUPS.cpp:178
static void DecodeZone(const std::string &zonestr, int &zone, bool &northp)
Definition: UTMUPS.cpp:212
static std::string str(T x, int p=-1)
Definition: Utility.hpp:161
Namespace for GeographicLib.
Definition: Accumulator.cpp:12