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