GeographicLib 2.1.2
UTMUPS.hpp
Go to the documentation of this file.
1/**
2 * \file UTMUPS.hpp
3 * \brief Header 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#if !defined(GEOGRAPHICLIB_UTMUPS_HPP)
11#define GEOGRAPHICLIB_UTMUPS_HPP 1
12
14
15namespace GeographicLib {
16
17 /**
18 * \brief Convert between geographic coordinates and UTM/UPS
19 *
20 * UTM and UPS are defined
21 * - J. W. Hager, J. F. Behensky, and B. W. Drew,
22 * <a href="https://web.archive.org/web/20161214054445/http://earth-info.nga.mil/GandG/publications/tm8358.2/TM8358_2.pdf">
23 * The Universal Grids: Universal Transverse Mercator (UTM) and Universal
24 * Polar Stereographic (UPS)</a>, Defense Mapping Agency, Technical Manual
25 * TM8358.2 (1989).
26 * .
27 * Section 2-3 defines UTM and section 3-2.4 defines UPS. This document also
28 * includes approximate algorithms for the computation of the underlying
29 * transverse Mercator and polar stereographic projections. Here we
30 * substitute much more accurate algorithms given by
31 * GeographicLib:TransverseMercator and GeographicLib:PolarStereographic.
32 * These are the algorithms recommended by the NGA document
33 * - <a href="https://earth-info.nga.mil/php/download.php?file=coord-utmups">
34 * The Universal Grids and the Transverse Mercator and Polar Stereographic
35 * Map Projections</a>, NGA.SIG.0012 (2014).
36 *
37 * In this implementation, the conversions are closed, i.e., output from
38 * Forward is legal input for Reverse and vice versa. The error is about 5nm
39 * in each direction. However, the conversion from legal UTM/UPS coordinates
40 * to geographic coordinates and back might throw an error if the initial
41 * point is within 5nm of the edge of the allowed range for the UTM/UPS
42 * coordinates.
43 *
44 * The simplest way to guarantee the closed property is to define allowed
45 * ranges for the eastings and northings for UTM and UPS coordinates. The
46 * UTM boundaries are the same for all zones. (The only place the
47 * exceptional nature of the zone boundaries is evident is when converting to
48 * UTM/UPS coordinates requesting the standard zone.) The MGRS lettering
49 * scheme imposes natural limits on UTM/UPS coordinates which may be
50 * converted into MGRS coordinates. For the conversion to/from geographic
51 * coordinates these ranges have been extended by 100km in order to provide a
52 * generous overlap between UTM and UPS and between UTM zones.
53 *
54 * The <a href="http://www.nga.mil">NGA</a> software package
55 * <a href="https://earth-info.nga.mil/index.php?dir=wgs84&action=wgs84#tab_geotrans">geotrans</a>
56 * also provides conversions to and from UTM and UPS. Version 2.4.2 (and
57 * earlier) suffers from some drawbacks:
58 * - Inconsistent rules are used to determine the whether a particular UTM or
59 * UPS coordinate is legal. A more systematic approach is taken here.
60 * - The underlying projections are not very accurately implemented.
61 *
62 * The GeographicLib::UTMUPS::EncodeZone encodes the UTM zone and hemisphere
63 * to allow UTM/UPS coordinated to be displayed as, for example, "38N 444500
64 * 3688500". According to NGA.SIG.0012_2.0.0_UTMUPS the use of "N" to denote
65 * "north" in the context is not allowed (since a upper case letter in this
66 * context denotes the MGRS latitude band). Consequently, as of version
67 * 1.36, EncodeZone uses the lower case letters "n" and "s" to denote the
68 * hemisphere. In addition EncodeZone accepts an optional final argument \e
69 * abbrev, which, if false, results in the hemisphere being spelled out as in
70 * "38north".
71 *
72 * Example of use:
73 * \include example-UTMUPS.cpp
74 **********************************************************************/
76 private:
77 typedef Math::real real;
78 static const int falseeasting_[4];
79 static const int falsenorthing_[4];
80 static const int mineasting_[4];
81 static const int maxeasting_[4];
82 static const int minnorthing_[4];
83 static const int maxnorthing_[4];
84 static const int epsg01N = 32601; // EPSG code for UTM 01N
85 static const int epsg60N = 32660; // EPSG code for UTM 60N
86 static const int epsgN = 32661; // EPSG code for UPS N
87 static const int epsg01S = 32701; // EPSG code for UTM 01S
88 static const int epsg60S = 32760; // EPSG code for UTM 60S
89 static const int epsgS = 32761; // EPSG code for UPS S
90 static real CentralMeridian(int zone)
91 { return real(6 * zone - 183); }
92 // Throw an error if easting or northing are outside standard ranges. If
93 // throwp = false, return bool instead.
94 static bool CheckCoords(bool utmp, bool northp, real x, real y,
95 bool msgrlimits = false, bool throwp = true);
96 UTMUPS() = delete; // Disable constructor
97
98 public:
99
100 /**
101 * In this class we bring together the UTM and UPS coordinates systems.
102 * The UTM divides the earth between latitudes &minus;80&deg; and 84&deg;
103 * into 60 zones numbered 1 thru 60. Zone assign zone number 0 to the UPS
104 * regions, covering the two poles. Within UTMUPS, non-negative zone
105 * numbers refer to one of the "physical" zones, 0 for UPS and [1, 60] for
106 * UTM. Negative "pseudo-zone" numbers are used to select one of the
107 * physical zones.
108 **********************************************************************/
109 enum zonespec {
110 /**
111 * The smallest pseudo-zone number.
112 **********************************************************************/
113 MINPSEUDOZONE = -4,
114 /**
115 * A marker for an undefined or invalid zone. Equivalent to NaN.
116 **********************************************************************/
117 INVALID = -4,
118 /**
119 * If a coordinate already include zone information (e.g., it is an MGRS
120 * coordinate), use that, otherwise apply the UTMUPS::STANDARD rules.
121 **********************************************************************/
122 MATCH = -3,
123 /**
124 * Apply the standard rules for UTM zone assigment extending the UTM zone
125 * to each pole to give a zone number in [1, 60]. For example, use UTM
126 * zone 38 for longitude in [42&deg;, 48&deg;). The rules include the
127 * Norway and Svalbard exceptions.
128 **********************************************************************/
129 UTM = -2,
130 /**
131 * Apply the standard rules for zone assignment to give a zone number in
132 * [0, 60]. If the latitude is not in [&minus;80&deg;, 84&deg;), then
133 * use UTMUPS::UPS = 0, otherwise apply the rules for UTMUPS::UTM. The
134 * tests on latitudes and longitudes are all closed on the lower end open
135 * on the upper. Thus for UTM zone 38, latitude is in [&minus;80&deg;,
136 * 84&deg;) and longitude is in [42&deg;, 48&deg;).
137 **********************************************************************/
138 STANDARD = -1,
139 /**
140 * The largest pseudo-zone number.
141 **********************************************************************/
142 MAXPSEUDOZONE = -1,
143 /**
144 * The smallest physical zone number.
145 **********************************************************************/
146 MINZONE = 0,
147 /**
148 * The zone number used for UPS
149 **********************************************************************/
150 UPS = 0,
151 /**
152 * The smallest UTM zone number.
153 **********************************************************************/
154 MINUTMZONE = 1,
155 /**
156 * The largest UTM zone number.
157 **********************************************************************/
158 MAXUTMZONE = 60,
159 /**
160 * The largest physical zone number.
161 **********************************************************************/
162 MAXZONE = 60,
163 };
164
165 /**
166 * The standard zone.
167 *
168 * @param[in] lat latitude (degrees).
169 * @param[in] lon longitude (degrees).
170 * @param[in] setzone zone override (optional). If omitted, use the
171 * standard rules for picking the zone. If \e setzone is given then use
172 * that zone if it is non-negative, otherwise apply the rules given in
173 * UTMUPS::zonespec.
174 * @exception GeographicErr if \e setzone is outside the range
175 * [UTMUPS::MINPSEUDOZONE, UTMUPS::MAXZONE] = [&minus;4, 60].
176 *
177 * This is exact.
178 **********************************************************************/
179 static int StandardZone(real lat, real lon, int setzone = STANDARD);
180
181 /**
182 * Forward projection, from geographic to UTM/UPS.
183 *
184 * @param[in] lat latitude of point (degrees).
185 * @param[in] lon longitude of point (degrees).
186 * @param[out] zone the UTM zone (zero means UPS).
187 * @param[out] northp hemisphere (true means north, false means south).
188 * @param[out] x easting of point (meters).
189 * @param[out] y northing of point (meters).
190 * @param[out] gamma meridian convergence at point (degrees).
191 * @param[out] k scale of projection at point.
192 * @param[in] setzone zone override (optional).
193 * @param[in] mgrslimits if true enforce the stricter MGRS limits on the
194 * coordinates (default = false).
195 * @exception GeographicErr if \e lat is not in [&minus;90&deg;,
196 * 90&deg;].
197 * @exception GeographicErr if the resulting \e x or \e y is out of allowed
198 * range (see Reverse); in this case, these arguments are unchanged.
199 *
200 * If \e setzone is omitted, use the standard rules for picking the zone.
201 * If \e setzone is given then use that zone if it is non-negative,
202 * otherwise apply the rules given in UTMUPS::zonespec. The accuracy of
203 * the conversion is about 5nm.
204 *
205 * The northing \e y jumps by UTMUPS::UTMShift() when crossing the equator
206 * in the southerly direction. Sometimes it is useful to remove this
207 * discontinuity in \e y by extending the "northern" hemisphere using
208 * UTMUPS::Transfer:
209 * \code
210 double lat = -1, lon = 123;
211 int zone;
212 bool northp;
213 double x, y, gamma, k;
214 GeographicLib::UTMUPS::Forward(lat, lon, zone, northp, x, y, gamma, k);
215 GeographicLib::UTMUPS::Transfer(zone, northp, x, y,
216 zone, true, x, y, zone);
217 northp = true;
218 \endcode
219 **********************************************************************/
220 static void Forward(real lat, real lon,
221 int& zone, bool& northp, real& x, real& y,
222 real& gamma, real& k,
223 int setzone = STANDARD, bool mgrslimits = false);
224
225 /**
226 * Reverse projection, from UTM/UPS to geographic.
227 *
228 * @param[in] zone the UTM zone (zero means UPS).
229 * @param[in] northp hemisphere (true means north, false means south).
230 * @param[in] x easting of point (meters).
231 * @param[in] y northing of point (meters).
232 * @param[out] lat latitude of point (degrees).
233 * @param[out] lon longitude of point (degrees).
234 * @param[out] gamma meridian convergence at point (degrees).
235 * @param[out] k scale of projection at point.
236 * @param[in] mgrslimits if true enforce the stricter MGRS limits on the
237 * coordinates (default = false).
238 * @exception GeographicErr if \e zone, \e x, or \e y is out of allowed
239 * range; this this case the arguments are unchanged.
240 *
241 * The accuracy of the conversion is about 5nm.
242 *
243 * UTM eastings are allowed to be in the range [0km, 1000km], northings are
244 * allowed to be in in [0km, 9600km] for the northern hemisphere and in
245 * [900km, 10000km] for the southern hemisphere. However UTM northings
246 * can be continued across the equator. So the actual limits on the
247 * northings are [-9100km, 9600km] for the "northern" hemisphere and
248 * [900km, 19600km] for the "southern" hemisphere.
249 *
250 * UPS eastings and northings are allowed to be in the range [1200km,
251 * 2800km] in the northern hemisphere and in [700km, 3300km] in the
252 * southern hemisphere.
253 *
254 * These ranges are 100km larger than allowed for the conversions to MGRS.
255 * (100km is the maximum extra padding consistent with eastings remaining
256 * non-negative.) This allows generous overlaps between zones and UTM and
257 * UPS. If \e mgrslimits = true, then all the ranges are shrunk by 100km
258 * so that they agree with the stricter MGRS ranges. No checks are
259 * performed besides these (e.g., to limit the distance outside the
260 * standard zone boundaries).
261 **********************************************************************/
262 static void Reverse(int zone, bool northp, real x, real y,
263 real& lat, real& lon, real& gamma, real& k,
264 bool mgrslimits = false);
265
266 /**
267 * UTMUPS::Forward without returning convergence and scale.
268 **********************************************************************/
269 static void Forward(real lat, real lon,
270 int& zone, bool& northp, real& x, real& y,
271 int setzone = STANDARD, bool mgrslimits = false) {
272 real gamma, k;
273 Forward(lat, lon, zone, northp, x, y, gamma, k, setzone, mgrslimits);
274 }
275
276 /**
277 * UTMUPS::Reverse without returning convergence and scale.
278 **********************************************************************/
279 static void Reverse(int zone, bool northp, real x, real y,
280 real& lat, real& lon, bool mgrslimits = false) {
281 real gamma, k;
282 Reverse(zone, northp, x, y, lat, lon, gamma, k, mgrslimits);
283 }
284
285 /**
286 * Transfer UTM/UPS coordinated from one zone to another.
287 *
288 * @param[in] zonein the UTM zone for \e xin and \e yin (or zero for UPS).
289 * @param[in] northpin hemisphere for \e xin and \e yin (true means north,
290 * false means south).
291 * @param[in] xin easting of point (meters) in \e zonein.
292 * @param[in] yin northing of point (meters) in \e zonein.
293 * @param[in] zoneout the requested UTM zone for \e xout and \e yout (or
294 * zero for UPS).
295 * @param[in] northpout hemisphere for \e xout output and \e yout.
296 * @param[out] xout easting of point (meters) in \e zoneout.
297 * @param[out] yout northing of point (meters) in \e zoneout.
298 * @param[out] zone the actual UTM zone for \e xout and \e yout (or zero
299 * for UPS); this equals \e zoneout if \e zoneout &ge; 0.
300 * @exception GeographicErr if \e zonein is out of range (see below).
301 * @exception GeographicErr if \e zoneout is out of range (see below).
302 * @exception GeographicErr if \e xin or \e yin fall outside their allowed
303 * ranges (see UTMUPS::Reverse).
304 * @exception GeographicErr if \e xout or \e yout fall outside their
305 * allowed ranges (see UTMUPS::Reverse).
306 *
307 * \e zonein must be in the range [UTMUPS::MINZONE, UTMUPS::MAXZONE] = [0,
308 * 60] with \e zonein = UTMUPS::UPS, 0, indicating UPS. \e zonein may
309 * also be UTMUPS::INVALID.
310 *
311 * \e zoneout must be in the range [UTMUPS::MINPSEUDOZONE, UTMUPS::MAXZONE]
312 * = [-4, 60]. If \e zoneout &lt; UTMUPS::MINZONE then the rules give in
313 * the documentation of UTMUPS::zonespec are applied, and \e zone is set to
314 * the actual zone used for output.
315 *
316 * (\e xout, \e yout) can overlap with (\e xin, \e yin).
317 **********************************************************************/
318 static void Transfer(int zonein, bool northpin, real xin, real yin,
319 int zoneout, bool northpout, real& xout, real& yout,
320 int& zone);
321
322 /**
323 * Decode a UTM/UPS zone string.
324 *
325 * @param[in] zonestr string representation of zone and hemisphere.
326 * @param[out] zone the UTM zone (zero means UPS).
327 * @param[out] northp hemisphere (true means north, false means south).
328 * @exception GeographicErr if \e zonestr is malformed.
329 *
330 * For UTM, \e zonestr has the form of a zone number in the range
331 * [UTMUPS::MINUTMZONE, UTMUPS::MAXUTMZONE] = [1, 60] followed by a
332 * hemisphere letter, n or s (or "north" or "south" spelled out). For UPS,
333 * it consists just of the hemisphere letter (or the spelled out
334 * hemisphere). The returned value of \e zone is UTMUPS::UPS = 0 for UPS.
335 * Note well that "38s" indicates the southern hemisphere of zone 38 and
336 * not latitude band S, 32&deg; &le; \e lat &lt; 40&deg;. n, 01s, 2n, 38s,
337 * south, 3north are legal. 0n, 001s, +3n, 61n, 38P are illegal. INV is a
338 * special value for which the returned value of \e is UTMUPS::INVALID.
339 **********************************************************************/
340 static void DecodeZone(const std::string& zonestr,
341 int& zone, bool& northp);
342
343 /**
344 * Encode a UTM/UPS zone string.
345 *
346 * @param[in] zone the UTM zone (zero means UPS).
347 * @param[in] northp hemisphere (true means north, false means south).
348 * @param[in] abbrev if true (the default) use abbreviated (n/s) notation
349 * for hemisphere; otherwise spell out the hemisphere (north/south)
350 * @exception GeographicErr if \e zone is out of range (see below).
351 * @exception std::bad_alloc if memoy for the string can't be allocated.
352 * @return string representation of zone and hemisphere.
353 *
354 * \e zone must be in the range [UTMUPS::MINZONE, UTMUPS::MAXZONE] = [0,
355 * 60] with \e zone = UTMUPS::UPS, 0, indicating UPS (but the resulting
356 * string does not contain "0"). \e zone may also be UTMUPS::INVALID, in
357 * which case the returned string is "inv". This reverses
358 * UTMUPS::DecodeZone.
359 **********************************************************************/
360 static std::string EncodeZone(int zone, bool northp, bool abbrev = true);
361
362 /**
363 * Decode EPSG.
364 *
365 * @param[in] epsg the EPSG code.
366 * @param[out] zone the UTM zone (zero means UPS).
367 * @param[out] northp hemisphere (true means north, false means south).
368 *
369 * EPSG (European Petroleum Survery Group) codes are a way to refer to many
370 * different projections. DecodeEPSG decodes those referring to UTM or UPS
371 * projections for the WGS84 ellipsoid. If the code does not refer to one
372 * of these projections, \e zone is set to UTMUPS::INVALID. See
373 * https://www.spatialreference.org/ref/epsg/
374 **********************************************************************/
375 static void DecodeEPSG(int epsg, int& zone, bool& northp);
376
377 /**
378 * Encode zone as EPSG.
379 *
380 * @param[in] zone the UTM zone (zero means UPS).
381 * @param[in] northp hemisphere (true means north, false means south).
382 * @return EPSG code (or -1 if \e zone is not in the range
383 * [UTMUPS::MINZONE, UTMUPS::MAXZONE] = [0, 60])
384 *
385 * Convert \e zone and \e northp to the corresponding EPSG (European
386 * Petroleum Survery Group) codes
387 **********************************************************************/
388 static int EncodeEPSG(int zone, bool northp);
389
390 /**
391 * @return shift (meters) necessary to align north and south halves of a
392 * UTM zone (10<sup>7</sup>).
393 **********************************************************************/
394 static Math::real UTMShift();
395
396 /** \name Inspector functions
397 **********************************************************************/
398 ///@{
399 /**
400 * @return \e a the equatorial radius of the WGS84 ellipsoid (meters).
401 *
402 * (The WGS84 value is returned because the UTM and UPS projections are
403 * based on this ellipsoid.)
404 **********************************************************************/
406 { return Constants::WGS84_a(); }
407
408 /**
409 * @return \e f the flattening of the WGS84 ellipsoid.
410 *
411 * (The WGS84 value is returned because the UTM and UPS projections are
412 * based on this ellipsoid.)
413 **********************************************************************/
415 { return Constants::WGS84_f(); }
416 ///@}
417
418 };
419
420} // namespace GeographicLib
421
422#endif // GEOGRAPHICLIB_UTMUPS_HPP
Header for GeographicLib::Constants class.
#define GEOGRAPHICLIB_EXPORT
Definition: Constants.hpp:67
GeographicLib::Math::real real
Definition: GeodSolve.cpp:31
Convert between geographic coordinates and UTM/UPS.
Definition: UTMUPS.hpp:75
static Math::real Flattening()
Definition: UTMUPS.hpp:414
static Math::real EquatorialRadius()
Definition: UTMUPS.hpp:405
static void Forward(real lat, real lon, int &zone, bool &northp, real &x, real &y, int setzone=STANDARD, bool mgrslimits=false)
Definition: UTMUPS.hpp:269
static void Reverse(int zone, bool northp, real x, real y, real &lat, real &lon, bool mgrslimits=false)
Definition: UTMUPS.hpp:279
Namespace for GeographicLib.
Definition: Accumulator.cpp:12