Geoid.hpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478
  1. /**
  2. * \file Geoid.hpp
  3. * \brief Header for GeographicLib::Geoid class
  4. *
  5. * Copyright (c) Charles Karney (2009-2021) <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_GEOID_HPP)
  10. #define GEOGRAPHICLIB_GEOID_HPP 1
  11. #include <vector>
  12. #include <fstream>
  13. #include <GeographicLib/Constants.hpp>
  14. #if defined(_MSC_VER)
  15. // Squelch warnings about dll vs vector and constant conditional expressions
  16. # pragma warning (push)
  17. # pragma warning (disable: 4251 4127)
  18. #endif
  19. #if !defined(GEOGRAPHICLIB_GEOID_PGM_PIXEL_WIDTH)
  20. /**
  21. * The size of the pixel data in the pgm data files for the geoids. 2 is the
  22. * standard size corresponding to a maxval 2<sup>16</sup>&minus;1. Setting it
  23. * to 4 uses a maxval of 2<sup>32</sup>&minus;1 and changes the extension for
  24. * the data files from .pgm to .pgm4. Note that the format of these pgm4 files
  25. * is a non-standard extension of the pgm format.
  26. **********************************************************************/
  27. # define GEOGRAPHICLIB_GEOID_PGM_PIXEL_WIDTH 2
  28. #endif
  29. namespace GeographicLib {
  30. /**
  31. * \brief Looking up the height of the geoid above the ellipsoid
  32. *
  33. * This class evaluates the height of one of the standard geoids, EGM84,
  34. * EGM96, or EGM2008 by bilinear or cubic interpolation into a rectangular
  35. * grid of data. These geoid models are documented in
  36. * - EGM84:
  37. * https://earth-info.nga.mil/index.php?dir=wgs84&action=wgs84#tab_egm84
  38. * - EGM96:
  39. * https://earth-info.nga.mil/index.php?dir=wgs84&action=wgs84#tab_egm96
  40. * - EGM2008:
  41. * https://earth-info.nga.mil/index.php?dir=wgs84&action=wgs84#tab_egm2008
  42. *
  43. * The geoids are defined in terms of spherical harmonics. However in order
  44. * to provide a quick and flexible method of evaluating the geoid heights,
  45. * this class evaluates the height by interpolation into a grid of
  46. * precomputed values.
  47. *
  48. * The height of the geoid above the ellipsoid, \e N, is sometimes called the
  49. * geoid undulation. It can be used to convert a height above the ellipsoid,
  50. * \e h, to the corresponding height above the geoid (the orthometric height,
  51. * roughly the height above mean sea level), \e H, using the relations
  52. *
  53. * &nbsp;&nbsp;&nbsp;\e h = \e N + \e H;
  54. * &nbsp;&nbsp;\e H = &minus;\e N + \e h.
  55. *
  56. * See \ref geoid for details of how to install the data sets, the data
  57. * format, estimates of the interpolation errors, and how to use caching.
  58. *
  59. * This class is typically \e not thread safe in that a single instantiation
  60. * cannot be safely used by multiple threads because of the way the object
  61. * reads the data set and because it maintains a single-cell cache. If
  62. * multiple threads need to calculate geoid heights they should all construct
  63. * thread-local instantiations. Alternatively, set the optional \e
  64. * threadsafe parameter to true in the constructor. This causes the
  65. * constructor to read all the data into memory and to turn off the
  66. * single-cell caching which results in a Geoid object which \e is thread
  67. * safe.
  68. *
  69. * Example of use:
  70. * \include example-Geoid.cpp
  71. *
  72. * <a href="GeoidEval.1.html">GeoidEval</a> is a command-line utility
  73. * providing access to the functionality of Geoid.
  74. **********************************************************************/
  75. class GEOGRAPHICLIB_EXPORT Geoid {
  76. private:
  77. typedef Math::real real;
  78. #if GEOGRAPHICLIB_GEOID_PGM_PIXEL_WIDTH != 4
  79. typedef unsigned short pixel_t;
  80. static const unsigned pixel_size_ = 2;
  81. static const unsigned pixel_max_ = 0xffffu;
  82. #else
  83. typedef unsigned pixel_t;
  84. static const unsigned pixel_size_ = 4;
  85. static const unsigned pixel_max_ = 0xffffffffu;
  86. #endif
  87. static const unsigned stencilsize_ = 12;
  88. static const unsigned nterms_ = ((3 + 1) * (3 + 2))/2; // for a cubic fit
  89. static const int c0_;
  90. static const int c0n_;
  91. static const int c0s_;
  92. static const int c3_[stencilsize_ * nterms_];
  93. static const int c3n_[stencilsize_ * nterms_];
  94. static const int c3s_[stencilsize_ * nterms_];
  95. std::string _name, _dir, _filename;
  96. const bool _cubic;
  97. const real _a, _e2, _degree, _eps;
  98. mutable std::ifstream _file;
  99. real _rlonres, _rlatres;
  100. std::string _description, _datetime;
  101. real _offset, _scale, _maxerror, _rmserror;
  102. int _width, _height;
  103. unsigned long long _datastart, _swidth;
  104. bool _threadsafe;
  105. // Area cache
  106. mutable std::vector< std::vector<pixel_t> > _data;
  107. mutable bool _cache;
  108. // NE corner and extent of cache
  109. mutable int _xoffset, _yoffset, _xsize, _ysize;
  110. // Cell cache
  111. mutable int _ix, _iy;
  112. mutable real _v00, _v01, _v10, _v11;
  113. mutable real _t[nterms_];
  114. void filepos(int ix, int iy) const {
  115. _file.seekg(std::streamoff
  116. (_datastart +
  117. pixel_size_ * (unsigned(iy)*_swidth + unsigned(ix))));
  118. }
  119. real rawval(int ix, int iy) const {
  120. if (ix < 0)
  121. ix += _width;
  122. else if (ix >= _width)
  123. ix -= _width;
  124. if (_cache && iy >= _yoffset && iy < _yoffset + _ysize &&
  125. ((ix >= _xoffset && ix < _xoffset + _xsize) ||
  126. (ix + _width >= _xoffset && ix + _width < _xoffset + _xsize))) {
  127. return real(_data[iy - _yoffset]
  128. [ix >= _xoffset ? ix - _xoffset : ix + _width - _xoffset]);
  129. } else {
  130. if (iy < 0 || iy >= _height) {
  131. iy = iy < 0 ? -iy : 2 * (_height - 1) - iy;
  132. ix += (ix < _width/2 ? 1 : -1) * _width/2;
  133. }
  134. try {
  135. filepos(ix, iy);
  136. // initial values to suppress warnings in case get fails
  137. char a = 0, b = 0;
  138. _file.get(a);
  139. _file.get(b);
  140. unsigned r = ((unsigned char)(a) << 8) | (unsigned char)(b);
  141. if (pixel_size_ == 4) {
  142. _file.get(a);
  143. _file.get(b);
  144. r = (r << 16) | ((unsigned char)(a) << 8) | (unsigned char)(b);
  145. }
  146. return real(r);
  147. }
  148. catch (const std::exception& e) {
  149. // throw GeographicErr("Error reading " + _filename + ": "
  150. // + e.what());
  151. // triggers complaints about the "binary '+'" under Visual Studio.
  152. // So use '+=' instead.
  153. std::string err("Error reading ");
  154. err += _filename;
  155. err += ": ";
  156. err += e.what();
  157. throw GeographicErr(err);
  158. }
  159. }
  160. }
  161. real height(real lat, real lon) const;
  162. Geoid(const Geoid&) = delete; // copy constructor not allowed
  163. Geoid& operator=(const Geoid&) = delete; // copy assignment not allowed
  164. public:
  165. /**
  166. * Flags indicating conversions between heights above the geoid and heights
  167. * above the ellipsoid.
  168. **********************************************************************/
  169. enum convertflag {
  170. /**
  171. * The multiplier for converting from heights above the geoid to heights
  172. * above the ellipsoid.
  173. **********************************************************************/
  174. ELLIPSOIDTOGEOID = -1,
  175. /**
  176. * No conversion.
  177. **********************************************************************/
  178. NONE = 0,
  179. /**
  180. * The multiplier for converting from heights above the ellipsoid to
  181. * heights above the geoid.
  182. **********************************************************************/
  183. GEOIDTOELLIPSOID = 1,
  184. };
  185. /** \name Setting up the geoid
  186. **********************************************************************/
  187. ///@{
  188. /**
  189. * Construct a geoid.
  190. *
  191. * @param[in] name the name of the geoid.
  192. * @param[in] path (optional) directory for data file.
  193. * @param[in] cubic (optional) interpolation method; false means bilinear,
  194. * true (the default) means cubic.
  195. * @param[in] threadsafe (optional), if true, construct a thread safe
  196. * object. The default is false
  197. * @exception GeographicErr if the data file cannot be found, is
  198. * unreadable, or is corrupt.
  199. * @exception GeographicErr if \e threadsafe is true but the memory
  200. * necessary for caching the data can't be allocated.
  201. *
  202. * The data file is formed by appending ".pgm" to the name. If \e path is
  203. * specified (and is non-empty), then the file is loaded from directory, \e
  204. * path. Otherwise the path is given by DefaultGeoidPath(). If the \e
  205. * threadsafe parameter is true, the data set is read into memory, the data
  206. * file is closed, and single-cell caching is turned off; this results in a
  207. * Geoid object which \e is thread safe.
  208. **********************************************************************/
  209. explicit Geoid(const std::string& name, const std::string& path = "",
  210. bool cubic = true, bool threadsafe = false);
  211. /**
  212. * Set up a cache.
  213. *
  214. * @param[in] south latitude (degrees) of the south edge of the cached
  215. * area.
  216. * @param[in] west longitude (degrees) of the west edge of the cached area.
  217. * @param[in] north latitude (degrees) of the north edge of the cached
  218. * area.
  219. * @param[in] east longitude (degrees) of the east edge of the cached area.
  220. * @exception GeographicErr if the memory necessary for caching the data
  221. * can't be allocated (in this case, you will have no cache and can try
  222. * again with a smaller area).
  223. * @exception GeographicErr if there's a problem reading the data.
  224. * @exception GeographicErr if this is called on a threadsafe Geoid.
  225. *
  226. * Cache the data for the specified "rectangular" area bounded by the
  227. * parallels \e south and \e north and the meridians \e west and \e east.
  228. * \e east is always interpreted as being east of \e west, if necessary by
  229. * adding 360&deg; to its value. \e south and \e north should be in
  230. * the range [&minus;90&deg;, 90&deg;].
  231. **********************************************************************/
  232. void CacheArea(real south, real west, real north, real east) const;
  233. /**
  234. * Cache all the data.
  235. *
  236. * @exception GeographicErr if the memory necessary for caching the data
  237. * can't be allocated (in this case, you will have no cache and can try
  238. * again with a smaller area).
  239. * @exception GeographicErr if there's a problem reading the data.
  240. * @exception GeographicErr if this is called on a threadsafe Geoid.
  241. *
  242. * On most computers, this is fast for data sets with grid resolution of 5'
  243. * or coarser. For a 1' grid, the required RAM is 450MB; a 2.5' grid needs
  244. * 72MB; and a 5' grid needs 18MB.
  245. **********************************************************************/
  246. void CacheAll() const { CacheArea(real(-90), real(0),
  247. real(90), real(360)); }
  248. /**
  249. * Clear the cache. This never throws an error. (This does nothing with a
  250. * thread safe Geoid.)
  251. **********************************************************************/
  252. void CacheClear() const;
  253. ///@}
  254. /** \name Compute geoid heights
  255. **********************************************************************/
  256. ///@{
  257. /**
  258. * Compute the geoid height at a point
  259. *
  260. * @param[in] lat latitude of the point (degrees).
  261. * @param[in] lon longitude of the point (degrees).
  262. * @exception GeographicErr if there's a problem reading the data; this
  263. * never happens if (\e lat, \e lon) is within a successfully cached
  264. * area.
  265. * @return the height of the geoid above the ellipsoid (meters).
  266. *
  267. * The latitude should be in [&minus;90&deg;, 90&deg;].
  268. **********************************************************************/
  269. Math::real operator()(real lat, real lon) const {
  270. return height(lat, lon);
  271. }
  272. /**
  273. * Convert a height above the geoid to a height above the ellipsoid and
  274. * vice versa.
  275. *
  276. * @param[in] lat latitude of the point (degrees).
  277. * @param[in] lon longitude of the point (degrees).
  278. * @param[in] h height of the point (degrees).
  279. * @param[in] d a Geoid::convertflag specifying the direction of the
  280. * conversion; Geoid::GEOIDTOELLIPSOID means convert a height above the
  281. * geoid to a height above the ellipsoid; Geoid::ELLIPSOIDTOGEOID means
  282. * convert a height above the ellipsoid to a height above the geoid.
  283. * @exception GeographicErr if there's a problem reading the data; this
  284. * never happens if (\e lat, \e lon) is within a successfully cached
  285. * area.
  286. * @return converted height (meters).
  287. **********************************************************************/
  288. Math::real ConvertHeight(real lat, real lon, real h,
  289. convertflag d) const {
  290. return h + real(d) * height(lat, lon);
  291. }
  292. ///@}
  293. /** \name Inspector functions
  294. **********************************************************************/
  295. ///@{
  296. /**
  297. * @return geoid description, if available, in the data file; if
  298. * absent, return "NONE".
  299. **********************************************************************/
  300. const std::string& Description() const { return _description; }
  301. /**
  302. * @return date of the data file; if absent, return "UNKNOWN".
  303. **********************************************************************/
  304. const std::string& DateTime() const { return _datetime; }
  305. /**
  306. * @return full file name used to load the geoid data.
  307. **********************************************************************/
  308. const std::string& GeoidFile() const { return _filename; }
  309. /**
  310. * @return "name" used to load the geoid data (from the first argument of
  311. * the constructor).
  312. **********************************************************************/
  313. const std::string& GeoidName() const { return _name; }
  314. /**
  315. * @return directory used to load the geoid data.
  316. **********************************************************************/
  317. const std::string& GeoidDirectory() const { return _dir; }
  318. /**
  319. * @return interpolation method ("cubic" or "bilinear").
  320. **********************************************************************/
  321. const std::string Interpolation() const
  322. { return std::string(_cubic ? "cubic" : "bilinear"); }
  323. /**
  324. * @return estimate of the maximum interpolation and quantization error
  325. * (meters).
  326. *
  327. * This relies on the value being stored in the data file. If the value is
  328. * absent, return &minus;1.
  329. **********************************************************************/
  330. Math::real MaxError() const { return _maxerror; }
  331. /**
  332. * @return estimate of the RMS interpolation and quantization error
  333. * (meters).
  334. *
  335. * This relies on the value being stored in the data file. If the value is
  336. * absent, return &minus;1.
  337. **********************************************************************/
  338. Math::real RMSError() const { return _rmserror; }
  339. /**
  340. * @return offset (meters).
  341. *
  342. * This in used in converting from the pixel values in the data file to
  343. * geoid heights.
  344. **********************************************************************/
  345. Math::real Offset() const { return _offset; }
  346. /**
  347. * @return scale (meters).
  348. *
  349. * This in used in converting from the pixel values in the data file to
  350. * geoid heights.
  351. **********************************************************************/
  352. Math::real Scale() const { return _scale; }
  353. /**
  354. * @return true if the object is constructed to be thread safe.
  355. **********************************************************************/
  356. bool ThreadSafe() const { return _threadsafe; }
  357. /**
  358. * @return true if a data cache is active.
  359. **********************************************************************/
  360. bool Cache() const { return _cache; }
  361. /**
  362. * @return west edge of the cached area; the cache includes this edge.
  363. **********************************************************************/
  364. Math::real CacheWest() const {
  365. return _cache ? ((_xoffset + (_xsize == _width ? 0 : _cubic)
  366. + _width/2) % _width - _width/2) / _rlonres :
  367. 0;
  368. }
  369. /**
  370. * @return east edge of the cached area; the cache excludes this edge.
  371. **********************************************************************/
  372. Math::real CacheEast() const {
  373. return _cache ?
  374. CacheWest() +
  375. (_xsize - (_xsize == _width ? 0 : 1 + 2 * _cubic)) / _rlonres :
  376. 0;
  377. }
  378. /**
  379. * @return north edge of the cached area; the cache includes this edge.
  380. **********************************************************************/
  381. Math::real CacheNorth() const {
  382. return _cache ? 90 - (_yoffset + _cubic) / _rlatres : 0;
  383. }
  384. /**
  385. * @return south edge of the cached area; the cache excludes this edge
  386. * unless it's the south pole.
  387. **********************************************************************/
  388. Math::real CacheSouth() const {
  389. return _cache ? 90 - ( _yoffset + _ysize - 1 - _cubic) / _rlatres : 0;
  390. }
  391. /**
  392. * @return \e a the equatorial radius of the WGS84 ellipsoid (meters).
  393. *
  394. * (The WGS84 value is returned because the supported geoid models are all
  395. * based on this ellipsoid.)
  396. **********************************************************************/
  397. Math::real EquatorialRadius() const
  398. { return Constants::WGS84_a(); }
  399. /**
  400. * @return \e f the flattening of the WGS84 ellipsoid.
  401. *
  402. * (The WGS84 value is returned because the supported geoid models are all
  403. * based on this ellipsoid.)
  404. **********************************************************************/
  405. Math::real Flattening() const { return Constants::WGS84_f(); }
  406. /**
  407. * \deprecated An old name for EquatorialRadius().
  408. **********************************************************************/
  409. GEOGRAPHICLIB_DEPRECATED("Use EquatorialRadius()")
  410. Math::real MajorRadius() const { return EquatorialRadius(); }
  411. ///@}
  412. /**
  413. * @return the default path for geoid data files.
  414. *
  415. * This is the value of the environment variable GEOGRAPHICLIB_GEOID_PATH,
  416. * if set; otherwise, it is $GEOGRAPHICLIB_DATA/geoids if the environment
  417. * variable GEOGRAPHICLIB_DATA is set; otherwise, it is a compile-time
  418. * default (/usr/local/share/GeographicLib/geoids on non-Windows systems
  419. * and C:/ProgramData/GeographicLib/geoids on Windows systems).
  420. **********************************************************************/
  421. static std::string DefaultGeoidPath();
  422. /**
  423. * @return the default name for the geoid.
  424. *
  425. * This is the value of the environment variable GEOGRAPHICLIB_GEOID_NAME,
  426. * if set; otherwise, it is "egm96-5". The Geoid class does not use this
  427. * function; it is just provided as a convenience for a calling program
  428. * when constructing a Geoid object.
  429. **********************************************************************/
  430. static std::string DefaultGeoidName();
  431. };
  432. } // namespace GeographicLib
  433. #if defined(_MSC_VER)
  434. # pragma warning (pop)
  435. #endif
  436. #endif // GEOGRAPHICLIB_GEOID_HPP