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