GeographicLib  2.6
MGRS.hpp
Go to the documentation of this file.
1 /**
2  * \file MGRS.hpp
3  * \brief Header for GeographicLib::MGRS class
4  *
5  * Copyright (c) Charles Karney (2008-2024) <karney@alum.mit.edu> and licensed
6  * under the MIT/X11 License. For more information, see
7  * https://geographiclib.sourceforge.io/
8  **********************************************************************/
9 
10 #if !defined(GEOGRAPHICLIB_MGRS_HPP)
11 #define GEOGRAPHICLIB_MGRS_HPP 1
12 
14 #include <GeographicLib/UTMUPS.hpp>
15 
16 namespace GeographicLib {
17 
18  /**
19  * \brief Convert between UTM/UPS and %MGRS
20  *
21  * MGRS is defined in Chapter 3 of
22  * - J. W. Hager, L. L. Fry, S. S. Jacks, D. R. Hill,
23  * <a href="https://web.archive.org/web/20161214054445/http://earth-info.nga.mil/GandG/publications/tm8358.1/pdf/TM8358_1.pdf">
24  * Datums, Ellipsoids, Grids, and Grid Reference Systems</a>,
25  * Defense Mapping Agency, Technical Manual TM8358.1 (1990).
26  * .
27  * This document has been updated by the two NGA documents
28  * - <a href="https://earth-info.nga.mil/php/download.php?file=coord-grids">
29  * Universal Grids and Grid Reference Systems</a>,
30  * NGA.STND.0037 (2014).
31  * - <a href="https://earth-info.nga.mil/php/download.php?file=coord-utmups">
32  * The Universal Grids and the Transverse Mercator and Polar Stereographic
33  * Map Projections</a>, NGA.SIG.0012 (2014).
34  *
35  * This implementation has the following properties:
36  * - The conversions are closed, i.e., output from Forward is legal input for
37  * Reverse and vice versa. Conversion in both directions preserve the
38  * UTM/UPS selection and the UTM zone.
39  * - Forward followed by Reverse and vice versa is approximately the
40  * identity. (This is affected in predictable ways by errors in
41  * determining the latitude band and by loss of precision in the MGRS
42  * coordinates.)
43  * - The trailing digits produced by Forward are consistent as the precision
44  * is varied. Specifically, the digits are obtained by operating on the
45  * easting with &lfloor;10<sup>6</sup> <i>x</i>&rfloor; and extracting the
46  * required digits from the resulting number (and similarly for the
47  * northing).
48  * - All MGRS coordinates truncate to legal 100 km blocks. All MGRS
49  * coordinates with a legal 100 km block prefix are legal (even though the
50  * latitude band letter may now belong to a neighboring band).
51  * - The range of UTM/UPS coordinates allowed for conversion to MGRS
52  * coordinates is the maximum consistent with staying within the letter
53  * ranges of the MGRS scheme.
54  * - All the transformations are implemented as static methods in the MGRS
55  * class.
56  *
57  * The <a href="http://www.nga.mil">NGA</a> software package
58  * <a href="https://earth-info.nga.mil/index.php?dir=wgs84&action=wgs84#tab_geotrans">geotrans</a>
59  * also provides conversions to and from MGRS. Version 3.0 (and earlier)
60  * suffers from some drawbacks:
61  * - Inconsistent rules are used to determine the whether a particular MGRS
62  * coordinate is legal. A more systematic approach is taken here.
63  * - The underlying projections are not very accurately implemented.
64  *
65  * Example of use:
66  * \include example-MGRS.cpp
67  **********************************************************************/
69  private:
70  typedef Math::real real;
71  static const char* const hemispheres_;
72  static const char* const utmcols_[3];
73  static const char* const utmrow_;
74  static const char* const upscols_[4];
75  static const char* const upsrows_[2];
76  static const char* const latband_;
77  static const char* const upsband_;
78  static const char* const digits_;
79  static const char* const alpha_;
80 
81  static const int mineasting_[4];
82  static const int maxeasting_[4];
83  static const int minnorthing_[4];
84  static const int maxnorthing_[4];
85  static constexpr int base_ = 10;
86  // Top-level tiles are 10^5 m = 100 km on a side
87  static constexpr int tilelevel_ = 5;
88  // Period of UTM row letters
89  static constexpr int utmrowperiod_ = 20;
90  // Row letters are shifted by 5 for even zones
91  static constexpr int utmevenrowshift_ = 5;
92  // Maximum precision is um
93  static constexpr int maxprec_ = 5 + 6;
94  // For generating digits at maxprec
95  static constexpr int mult_ = 1000000;
96  static void CheckCoords(bool utmp, bool& northp, real& x, real& y);
97  static int UTMRow(int iband, int icol, int irow);
98 
99  friend class UTMUPS; // UTMUPS::StandardZone calls LatitudeBand
100  // Return latitude band number [-10, 10) for the given latitude (degrees).
101  // The bands are reckoned in include their southern edges.
102  static int LatitudeBand(real lat) {
103  using std::floor;
104  int ilat = int(floor(lat));
105  return (std::max)(-10, (std::min)(9, (ilat + 80)/8 - 10));
106  }
107  // Return approximate latitude band number [-10, 10) for the given northing
108  // (meters). With this rule, each 100km tile would have a unique band
109  // letter corresponding to the latitude at the center of the tile. This
110  // function isn't currently used.
111  static int ApproxLatitudeBand(real y) {
112  // northing at tile center in units of tile = 100km
113  using std::floor, std::fabs, std::fmin;
114  real ya = floor( fmin(real(88), fabs(y / real(tile_))) ) + real(0.5);
115  // convert to lat (mult by 90/100) and then to band (divide by 8)
116  // the +1 fine tunes the boundary between bands 3 and 4
117  int b = int(floor( ((ya * 9 + 1) / 10) / 8 ));
118  // For the northern hemisphere we have
119  // band rows num
120  // N 0 0:8 9
121  // P 1 9:17 9
122  // Q 2 18:26 9
123  // R 3 27:34 8
124  // S 4 35:43 9
125  // T 5 44:52 9
126  // U 6 53:61 9
127  // V 7 62:70 9
128  // W 8 71:79 9
129  // X 9 80:94 15
130  return y >= 0 ? b : -(b + 1);
131  }
132  // UTMUPS accesses these constants
133  static constexpr int tile_ = 100000; // Size MGRS blocks
134  static constexpr int minutmcol_ = 1;
135  static constexpr int maxutmcol_ = 9;
136  static constexpr int minutmSrow_ = 10;
137  static constexpr int maxutmSrow_ = 100; // Also used for UTM S false northing
138  static constexpr int minutmNrow_ = 0; // Also used for UTM N false northing
139  static constexpr int maxutmNrow_ = 95;
140  static constexpr int minupsSind_ = 8; // These 4 ind's apply to easting and northing
141  static constexpr int maxupsSind_ = 32;
142  static constexpr int minupsNind_ = 13;
143  static constexpr int maxupsNind_ = 27;
144  static constexpr int upseasting_ = 20; // Also used for UPS false northing
145  static constexpr int utmeasting_ = 5; // UTM false easting
146  // Difference between S hemisphere northing and N hemisphere northing
147  static constexpr int utmNshift_ = (maxutmSrow_ - minutmNrow_) * tile_;
148  MGRS() = delete; // Disable constructor
149 
150  public:
151 
152  /**
153  * Convert UTM or UPS coordinate to an MGRS coordinate.
154  *
155  * @param[in] zone UTM zone (zero means UPS).
156  * @param[in] northp hemisphere (true means north, false means south).
157  * @param[in] x easting of point (meters).
158  * @param[in] y northing of point (meters).
159  * @param[in] prec precision relative to 100 km.
160  * @param[out] mgrs MGRS string.
161  * @exception GeographicErr if \e zone, \e x, or \e y is outside its
162  * allowed range.
163  * @exception GeographicErr if the memory for the MGRS string can't be
164  * allocated.
165  *
166  * \e prec specifies the precision of the MGRS string as follows:
167  * - \e prec = &minus;1 (min), only the grid zone is returned
168  * - \e prec = 0, 100 km
169  * - \e prec = 1, 10 km
170  * - \e prec = 2, 1 km
171  * - \e prec = 3, 100 m
172  * - \e prec = 4, 10 m
173  * - \e prec = 5, 1 m
174  * - \e prec = 6, 0.1 m
175  * - &hellip;
176  * - \e prec = 11 (max), 1 &mu;m
177  *
178  * UTM eastings are allowed to be in the range [100 km, 900 km], northings
179  * are allowed to be in in [0 km, 9500 km] for the northern hemisphere and
180  * in [1000 km, 10000 km] for the southern hemisphere. (However UTM
181  * northings can be continued across the equator. So the actual limits on
182  * the northings are [&minus;9000 km, 9500 km] for the "northern"
183  * hemisphere and [1000 km, 19500 km] for the "southern" hemisphere.)
184  *
185  * UPS eastings/northings are allowed to be in the range [1300 km, 2700 km]
186  * in the northern hemisphere and in [800 km, 3200 km] in the southern
187  * hemisphere.
188  *
189  * The ranges are 100 km more restrictive than for the conversion between
190  * geographic coordinates and UTM and UPS given by UTMUPS. These
191  * restrictions are dictated by the allowed letters in MGRS coordinates.
192  * The choice of 9500 km for the maximum northing for northern hemisphere
193  * and of 1000 km as the minimum northing for southern hemisphere provide
194  * at least 0.5 degree extension into standard UPS zones. The upper ends
195  * of the ranges for the UPS coordinates is dictated by requiring symmetry
196  * about the meridians 0E and 90E.
197  *
198  * All allowed UTM and UPS coordinates may now be converted to legal MGRS
199  * coordinates with the proviso that eastings and northings on the upper
200  * boundaries are silently reduced by about 4 nm (4 nanometers) to place
201  * them \e within the allowed range. (This includes reducing a southern
202  * hemisphere northing of 10000 km by 4 nm so that it is placed in latitude
203  * band M.) The UTM or UPS coordinates are truncated to requested
204  * precision to determine the MGRS coordinate. Thus in UTM zone 38n, the
205  * square area with easting in [444 km, 445 km) and northing in [3688 km,
206  * 3689 km) maps to MGRS coordinate 38SMB4488 (at \e prec = 2, 1 km),
207  * Khulani Sq., Baghdad.
208  *
209  * The UTM/UPS selection and the UTM zone is preserved in the conversion to
210  * MGRS coordinate. Thus for \e zone > 0, the MGRS coordinate begins with
211  * the zone number followed by one of [C--M] for the southern
212  * hemisphere and [N--X] for the northern hemisphere. For \e zone =
213  * 0, the MGRS coordinates begins with one of [AB] for the southern
214  * hemisphere and [XY] for the northern hemisphere.
215  *
216  * The conversion to the MGRS is exact for prec in [0, 5] except that a
217  * neighboring latitude band letter may be given if the point is within 5nm
218  * of a band boundary. For prec in [6, 11], the conversion is accurate to
219  * roundoff.
220  *
221  * If \e prec = &minus;1, then the "grid zone designation", e.g., 18T, is
222  * returned. This consists of the UTM zone number (absent for UPS) and the
223  * first letter of the MGRS string which labels the latitude band for UTM
224  * and the hemisphere for UPS.
225  *
226  * If \e x or \e y is NaN or if \e zone is UTMUPS::INVALID, the returned
227  * MGRS string is "INVALID".
228  *
229  * Return the result via a reference argument to avoid the overhead of
230  * allocating a potentially large number of small strings. If an error is
231  * thrown, then \e mgrs is unchanged.
232  **********************************************************************/
233  static void Forward(int zone, bool northp, real x, real y,
234  int prec, std::string& mgrs);
235 
236  /**
237  * Convert UTM or UPS coordinate to an MGRS coordinate when the latitude is
238  * known.
239  *
240  * @param[in] zone UTM zone (zero means UPS).
241  * @param[in] northp hemisphere (true means north, false means south).
242  * @param[in] x easting of point (meters).
243  * @param[in] y northing of point (meters).
244  * @param[in] lat latitude (degrees).
245  * @param[in] prec precision relative to 100 km.
246  * @param[out] mgrs MGRS string.
247  * @exception GeographicErr if \e zone, \e x, or \e y is outside its
248  * allowed range.
249  * @exception GeographicErr if \e lat is inconsistent with the given UTM
250  * coordinates.
251  * @exception std::bad_alloc if the memory for \e mgrs can't be allocated.
252  *
253  * The latitude is ignored for \e zone = 0 (UPS); otherwise the latitude is
254  * used to determine the latitude band and this is checked for consistency
255  * using the same tests as Reverse.
256  **********************************************************************/
257  static void Forward(int zone, bool northp, real x, real y, real lat,
258  int prec, std::string& mgrs);
259 
260  /**
261  * Convert a MGRS coordinate to UTM or UPS coordinates.
262  *
263  * @param[in] mgrs MGRS string.
264  * @param[out] zone UTM zone (zero means UPS).
265  * @param[out] northp hemisphere (true means north, false means south).
266  * @param[out] x easting of point (meters).
267  * @param[out] y northing of point (meters).
268  * @param[out] prec precision relative to 100 km.
269  * @param[in] centerp if true (default), return center of the MGRS square,
270  * else return SW (lower left) corner.
271  * @exception GeographicErr if \e mgrs is illegal.
272  *
273  * All conversions from MGRS to UTM/UPS are permitted provided the MGRS
274  * coordinate is a possible result of a conversion in the other direction.
275  * (The leading 0 may be dropped from an input MGRS coordinate for UTM
276  * zones 1--9.) In addition, MGRS coordinates with a neighboring
277  * latitude band letter are permitted provided that some portion of the
278  * 100 km block is within the given latitude band. Thus
279  * - 38VLS and 38WLS are allowed (latitude 64N intersects the square
280  * 38[VW]LS); but 38VMS is not permitted (all of 38WMS is north of 64N)
281  * - 38MPE and 38NPF are permitted (they straddle the equator); but 38NPE
282  * and 38MPF are not permitted (the equator does not intersect either
283  * block).
284  * - Similarly ZAB and YZB are permitted (they straddle the prime
285  * meridian); but YAB and ZZB are not (the prime meridian does not
286  * intersect either block).
287  *
288  * The UTM/UPS selection and the UTM zone is preserved in the conversion
289  * from MGRS coordinate. The conversion is exact for prec in [0, 5]. With
290  * \e centerp = true, the conversion from MGRS to geographic and back is
291  * stable. This is not assured if \e centerp = false.
292  *
293  * If a "grid zone designation" (for example, 18T or A) is given, then some
294  * suitable (but essentially arbitrary) point within that grid zone is
295  * returned. The main utility of the conversion is to allow \e zone and \e
296  * northp to be determined. In this case, the \e centerp parameter is
297  * ignored and \e prec is set to &minus;1.
298  *
299  * If the first 3 characters of \e mgrs are "INV", then \e x and \e y are
300  * set to NaN, \e zone is set to UTMUPS::INVALID, and \e prec is set to
301  * &minus;2.
302  *
303  * If an exception is thrown, then the arguments are unchanged.
304  **********************************************************************/
305  static void Reverse(const std::string& mgrs,
306  int& zone, bool& northp, real& x, real& y,
307  int& prec, bool centerp = true);
308 
309  /**
310  * Split a MGRS grid reference into its components.
311  *
312  * @param[in] mgrs MGRS string, e.g., 38SMB4488.
313  * @param[out] gridzone the grid zone, e.g., 38S.
314  * @param[out] block the 100km block id, e.g., MB.
315  * @param[out] easting the leading digits of the block easting, e.g., 44.
316  * @param[out] northing the leading digits of the block easting, e.g., 88.
317  * @exception GeographicErr if \e mgrs is illegal.
318  *
319  * Only the most rudimentary checking of MGRS grid ref is done: it is
320  * expected to consist of 0-2 digits followed by 1 or 3 letters, followed
321  * (in the case of 3 letters) by an even number (possibly 0) of digits. In
322  * reporting errors, the letters I and O (illegal in MSRS) are regarded as
323  * non-alphabetic. The returned \e gridzone will always be non-empty. The
324  * other output arguments may be empty strings.
325  *
326  * If the first 3 characters of \e mgrs are "INV", then \e gridzone is set
327  * to those 3 characters and the other return arguments are set to empty
328  * strings..
329  *
330  * If an exception is thrown, then the arguments are unchanged.
331  **********************************************************************/
332  static void Decode(const std::string& mgrs,
333  std::string& gridzone, std::string& block,
334  std::string& easting, std::string& northing);
335 
336  /** \name Inspector functions
337  **********************************************************************/
338  ///@{
339  /**
340  * @return \e a the equatorial radius of the WGS84 ellipsoid (meters).
341  *
342  * (The WGS84 value is returned because the UTM and UPS projections are
343  * based on this ellipsoid.)
344  **********************************************************************/
346 
347  /**
348  * @return \e f the flattening of the WGS84 ellipsoid.
349  *
350  * (The WGS84 value is returned because the UTM and UPS projections are
351  * based on this ellipsoid.)
352  **********************************************************************/
353  static Math::real Flattening() { return UTMUPS::Flattening(); }
354  ///@}
355 
356  /**
357  * Perform some checks on the UTMUPS coordinates on this ellipsoid. Throw
358  * an error if any of the assumptions made in the MGRS class is not true.
359  * This check needs to be carried out if the ellipsoid parameters (or the
360  * UTM/UPS scales) are ever changed.
361  **********************************************************************/
362  static void Check();
363 
364  };
365 
366 } // namespace GeographicLib
367 
368 #endif // GEOGRAPHICLIB_MGRS_HPP
#define GEOGRAPHICLIB_EXPORT
Definition: Constants.hpp:59
static Math::real Flattening()
Definition: UTMUPS.hpp:414
Header for GeographicLib::UTMUPS class.
Convert between geographic coordinates and UTM/UPS.
Definition: UTMUPS.hpp:75
static Math::real EquatorialRadius()
Definition: UTMUPS.hpp:405
Namespace for GeographicLib.
Definition: Accumulator.cpp:12
static Math::real EquatorialRadius()
Definition: MGRS.hpp:345
GeographicLib::Math::real real
Definition: Geod3Solve.cpp:25
Header for GeographicLib::Constants class.
static Math::real Flattening()
Definition: MGRS.hpp:353
Convert between UTM/UPS and MGRS.
Definition: MGRS.hpp:68