MGRS.hpp 16 KB

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