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