geos.hpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  1. // Boost.Geometry - gis-projections (based on PROJ4)
  2. // Copyright (c) 2008-2015 Barend Gehrels, Amsterdam, the Netherlands.
  3. // This file was modified by Oracle on 2017, 2018.
  4. // Modifications copyright (c) 2017-2018, Oracle and/or its affiliates.
  5. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle.
  6. // Use, modification and distribution is subject to the Boost Software License,
  7. // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
  8. // http://www.boost.org/LICENSE_1_0.txt)
  9. // This file is converted from PROJ4, http://trac.osgeo.org/proj
  10. // PROJ4 is originally written by Gerald Evenden (then of the USGS)
  11. // PROJ4 is maintained by Frank Warmerdam
  12. // PROJ4 is converted to Boost.Geometry by Barend Gehrels
  13. // Last updated version of proj: 5.0.0
  14. // Original copyright notice:
  15. // Copyright (c) 2004 Gerald I. Evenden
  16. // Copyright (c) 2012 Martin Raspaud
  17. // See also (section 4.4.3.2):
  18. // http://www.eumetsat.int/en/area4/msg/news/us_doc/cgms_03_26.pdf
  19. // Permission is hereby granted, free of charge, to any person obtaining a
  20. // copy of this software and associated documentation files (the "Software"),
  21. // to deal in the Software without restriction, including without limitation
  22. // the rights to use, copy, modify, merge, publish, distribute, sublicense,
  23. // and/or sell copies of the Software, and to permit persons to whom the
  24. // Software is furnished to do so, subject to the following conditions:
  25. // The above copyright notice and this permission notice shall be included
  26. // in all copies or substantial portions of the Software.
  27. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  28. // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  29. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  30. // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  31. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  32. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  33. // DEALINGS IN THE SOFTWARE.
  34. #ifndef BOOST_GEOMETRY_PROJECTIONS_GEOS_HPP
  35. #define BOOST_GEOMETRY_PROJECTIONS_GEOS_HPP
  36. #include <boost/math/special_functions/hypot.hpp>
  37. #include <boost/geometry/srs/projections/impl/base_static.hpp>
  38. #include <boost/geometry/srs/projections/impl/base_dynamic.hpp>
  39. #include <boost/geometry/srs/projections/impl/projects.hpp>
  40. #include <boost/geometry/srs/projections/impl/factory_entry.hpp>
  41. namespace boost { namespace geometry
  42. {
  43. namespace srs { namespace par4
  44. {
  45. struct geos {}; // Geostationary Satellite View
  46. }} //namespace srs::par4
  47. namespace projections
  48. {
  49. #ifndef DOXYGEN_NO_DETAIL
  50. namespace detail { namespace geos
  51. {
  52. template <typename T>
  53. struct par_geos
  54. {
  55. T h;
  56. T radius_p;
  57. T radius_p2;
  58. T radius_p_inv2;
  59. T radius_g;
  60. T radius_g_1;
  61. T C;
  62. int flip_axis;
  63. };
  64. // template class, using CRTP to implement forward/inverse
  65. template <typename T, typename Parameters>
  66. struct base_geos_ellipsoid
  67. : public base_t_fi<base_geos_ellipsoid<T, Parameters>, T, Parameters>
  68. {
  69. par_geos<T> m_proj_parm;
  70. inline base_geos_ellipsoid(const Parameters& par)
  71. : base_t_fi<base_geos_ellipsoid<T, Parameters>, T, Parameters>(*this, par)
  72. {}
  73. // FORWARD(e_forward) ellipsoid
  74. // Project coordinates from geographic (lon, lat) to cartesian (x, y)
  75. inline void fwd(T& lp_lon, T& lp_lat, T& xy_x, T& xy_y) const
  76. {
  77. T r, Vx, Vy, Vz, tmp;
  78. /* Calculation of geocentric latitude. */
  79. lp_lat = atan (this->m_proj_parm.radius_p2 * tan (lp_lat));
  80. /* Calculation of the three components of the vector from satellite to
  81. ** position on earth surface (lon,lat).*/
  82. r = (this->m_proj_parm.radius_p) / boost::math::hypot(this->m_proj_parm.radius_p * cos (lp_lat), sin (lp_lat));
  83. Vx = r * cos (lp_lon) * cos (lp_lat);
  84. Vy = r * sin (lp_lon) * cos (lp_lat);
  85. Vz = r * sin (lp_lat);
  86. /* Check visibility. */
  87. if (((this->m_proj_parm.radius_g - Vx) * Vx - Vy * Vy - Vz * Vz * this->m_proj_parm.radius_p_inv2) < 0.) {
  88. BOOST_THROW_EXCEPTION( projection_exception(error_tolerance_condition) );
  89. }
  90. /* Calculation based on view angles from satellite. */
  91. tmp = this->m_proj_parm.radius_g - Vx;
  92. if(this->m_proj_parm.flip_axis) {
  93. xy_x = this->m_proj_parm.radius_g_1 * atan (Vy / boost::math::hypot (Vz, tmp));
  94. xy_y = this->m_proj_parm.radius_g_1 * atan (Vz / tmp);
  95. } else {
  96. xy_x = this->m_proj_parm.radius_g_1 * atan (Vy / tmp);
  97. xy_y = this->m_proj_parm.radius_g_1 * atan (Vz / boost::math::hypot (Vy, tmp));
  98. }
  99. }
  100. // INVERSE(e_inverse) ellipsoid
  101. // Project coordinates from cartesian (x, y) to geographic (lon, lat)
  102. inline void inv(T& xy_x, T& xy_y, T& lp_lon, T& lp_lat) const
  103. {
  104. T Vx, Vy, Vz, a, b, det, k;
  105. /* Setting three components of vector from satellite to position.*/
  106. Vx = -1.0;
  107. if(this->m_proj_parm.flip_axis) {
  108. Vz = tan (xy_y / this->m_proj_parm.radius_g_1);
  109. Vy = tan (xy_x / this->m_proj_parm.radius_g_1) * boost::math::hypot(1.0, Vz);
  110. } else {
  111. Vy = tan (xy_x / this->m_proj_parm.radius_g_1);
  112. Vz = tan (xy_y / this->m_proj_parm.radius_g_1) * boost::math::hypot(1.0, Vy);
  113. }
  114. /* Calculation of terms in cubic equation and determinant.*/
  115. a = Vz / this->m_proj_parm.radius_p;
  116. a = Vy * Vy + a * a + Vx * Vx;
  117. b = 2 * this->m_proj_parm.radius_g * Vx;
  118. if ((det = (b * b) - 4 * a * this->m_proj_parm.C) < 0.) {
  119. BOOST_THROW_EXCEPTION( projection_exception(error_tolerance_condition) );
  120. }
  121. /* Calculation of three components of vector from satellite to position.*/
  122. k = (-b - sqrt(det)) / (2. * a);
  123. Vx = this->m_proj_parm.radius_g + k * Vx;
  124. Vy *= k;
  125. Vz *= k;
  126. /* Calculation of longitude and latitude.*/
  127. lp_lon = atan2 (Vy, Vx);
  128. lp_lat = atan (Vz * cos (lp_lon) / Vx);
  129. lp_lat = atan (this->m_proj_parm.radius_p_inv2 * tan (lp_lat));
  130. }
  131. static inline std::string get_name()
  132. {
  133. return "geos_ellipsoid";
  134. }
  135. };
  136. // template class, using CRTP to implement forward/inverse
  137. template <typename T, typename Parameters>
  138. struct base_geos_spheroid
  139. : public base_t_fi<base_geos_spheroid<T, Parameters>, T, Parameters>
  140. {
  141. par_geos<T> m_proj_parm;
  142. inline base_geos_spheroid(const Parameters& par)
  143. : base_t_fi<base_geos_spheroid<T, Parameters>, T, Parameters>(*this, par)
  144. {}
  145. // FORWARD(s_forward) spheroid
  146. // Project coordinates from geographic (lon, lat) to cartesian (x, y)
  147. inline void fwd(T& lp_lon, T& lp_lat, T& xy_x, T& xy_y) const
  148. {
  149. T Vx, Vy, Vz, tmp;
  150. /* Calculation of the three components of the vector from satellite to
  151. ** position on earth surface (lon,lat).*/
  152. tmp = cos(lp_lat);
  153. Vx = cos (lp_lon) * tmp;
  154. Vy = sin (lp_lon) * tmp;
  155. Vz = sin (lp_lat);
  156. /* Check visibility.*/
  157. // TODO: in proj4 5.0.0 this check is not present
  158. if (((this->m_proj_parm.radius_g - Vx) * Vx - Vy * Vy - Vz * Vz) < 0.)
  159. BOOST_THROW_EXCEPTION( projection_exception(error_tolerance_condition) );
  160. /* Calculation based on view angles from satellite.*/
  161. tmp = this->m_proj_parm.radius_g - Vx;
  162. if(this->m_proj_parm.flip_axis) {
  163. xy_x = this->m_proj_parm.radius_g_1 * atan(Vy / boost::math::hypot(Vz, tmp));
  164. xy_y = this->m_proj_parm.radius_g_1 * atan(Vz / tmp);
  165. } else {
  166. xy_x = this->m_proj_parm.radius_g_1 * atan(Vy / tmp);
  167. xy_y = this->m_proj_parm.radius_g_1 * atan(Vz / boost::math::hypot(Vy, tmp));
  168. }
  169. }
  170. // INVERSE(s_inverse) spheroid
  171. // Project coordinates from cartesian (x, y) to geographic (lon, lat)
  172. inline void inv(T& xy_x, T& xy_y, T& lp_lon, T& lp_lat) const
  173. {
  174. T Vx, Vy, Vz, a, b, det, k;
  175. /* Setting three components of vector from satellite to position.*/
  176. Vx = -1.0;
  177. if(this->m_proj_parm.flip_axis) {
  178. Vz = tan (xy_y / (this->m_proj_parm.radius_g - 1.0));
  179. Vy = tan (xy_x / (this->m_proj_parm.radius_g - 1.0)) * sqrt (1.0 + Vz * Vz);
  180. } else {
  181. Vy = tan (xy_x / (this->m_proj_parm.radius_g - 1.0));
  182. Vz = tan (xy_y / (this->m_proj_parm.radius_g - 1.0)) * sqrt (1.0 + Vy * Vy);
  183. }
  184. /* Calculation of terms in cubic equation and determinant.*/
  185. a = Vy * Vy + Vz * Vz + Vx * Vx;
  186. b = 2 * this->m_proj_parm.radius_g * Vx;
  187. if ((det = (b * b) - 4 * a * this->m_proj_parm.C) < 0.) {
  188. BOOST_THROW_EXCEPTION( projection_exception(error_tolerance_condition) );
  189. }
  190. /* Calculation of three components of vector from satellite to position.*/
  191. k = (-b - sqrt(det)) / (2 * a);
  192. Vx = this->m_proj_parm.radius_g + k * Vx;
  193. Vy *= k;
  194. Vz *= k;
  195. /* Calculation of longitude and latitude.*/
  196. lp_lon = atan2 (Vy, Vx);
  197. lp_lat = atan (Vz * cos (lp_lon) / Vx);
  198. }
  199. static inline std::string get_name()
  200. {
  201. return "geos_spheroid";
  202. }
  203. };
  204. // Geostationary Satellite View
  205. template <typename Parameters, typename T>
  206. inline void setup_geos(Parameters& par, par_geos<T>& proj_parm)
  207. {
  208. std::string sweep_axis;
  209. if ((proj_parm.h = pj_get_param_f(par.params, "h")) <= 0.)
  210. BOOST_THROW_EXCEPTION( projection_exception(error_h_less_than_zero) );
  211. if (par.phi0 != 0.0)
  212. BOOST_THROW_EXCEPTION( projection_exception(error_unknown_prime_meridian) );
  213. sweep_axis = pj_get_param_s(par.params, "sweep");
  214. if (sweep_axis.empty())
  215. proj_parm.flip_axis = 0;
  216. else {
  217. if (sweep_axis[1] != '\0' || (sweep_axis[0] != 'x' && sweep_axis[0] != 'y'))
  218. BOOST_THROW_EXCEPTION( projection_exception(error_invalid_sweep_axis) );
  219. if (sweep_axis[0] == 'x')
  220. proj_parm.flip_axis = 1;
  221. else
  222. proj_parm.flip_axis = 0;
  223. }
  224. proj_parm.radius_g_1 = proj_parm.h / par.a;
  225. proj_parm.radius_g = 1. + proj_parm.radius_g_1;
  226. proj_parm.C = proj_parm.radius_g * proj_parm.radius_g - 1.0;
  227. if (par.es != 0.0) {
  228. proj_parm.radius_p = sqrt (par.one_es);
  229. proj_parm.radius_p2 = par.one_es;
  230. proj_parm.radius_p_inv2 = par.rone_es;
  231. } else {
  232. proj_parm.radius_p = proj_parm.radius_p2 = proj_parm.radius_p_inv2 = 1.0;
  233. }
  234. }
  235. }} // namespace detail::geos
  236. #endif // doxygen
  237. /*!
  238. \brief Geostationary Satellite View projection
  239. \ingroup projections
  240. \tparam Geographic latlong point type
  241. \tparam Cartesian xy point type
  242. \tparam Parameters parameter type
  243. \par Projection characteristics
  244. - Azimuthal
  245. - Spheroid
  246. - Ellipsoid
  247. \par Projection parameters
  248. - h: Height (real)
  249. - sweep: Sweep axis ('x' or 'y') (string)
  250. \par Example
  251. \image html ex_geos.gif
  252. */
  253. template <typename T, typename Parameters>
  254. struct geos_ellipsoid : public detail::geos::base_geos_ellipsoid<T, Parameters>
  255. {
  256. inline geos_ellipsoid(const Parameters& par) : detail::geos::base_geos_ellipsoid<T, Parameters>(par)
  257. {
  258. detail::geos::setup_geos(this->m_par, this->m_proj_parm);
  259. }
  260. };
  261. /*!
  262. \brief Geostationary Satellite View projection
  263. \ingroup projections
  264. \tparam Geographic latlong point type
  265. \tparam Cartesian xy point type
  266. \tparam Parameters parameter type
  267. \par Projection characteristics
  268. - Azimuthal
  269. - Spheroid
  270. - Ellipsoid
  271. \par Projection parameters
  272. - h: Height (real)
  273. - sweep: Sweep axis ('x' or 'y') (string)
  274. \par Example
  275. \image html ex_geos.gif
  276. */
  277. template <typename T, typename Parameters>
  278. struct geos_spheroid : public detail::geos::base_geos_spheroid<T, Parameters>
  279. {
  280. inline geos_spheroid(const Parameters& par) : detail::geos::base_geos_spheroid<T, Parameters>(par)
  281. {
  282. detail::geos::setup_geos(this->m_par, this->m_proj_parm);
  283. }
  284. };
  285. #ifndef DOXYGEN_NO_DETAIL
  286. namespace detail
  287. {
  288. // Static projection
  289. BOOST_GEOMETRY_PROJECTIONS_DETAIL_STATIC_PROJECTION(srs::par4::geos, geos_spheroid, geos_ellipsoid)
  290. // Factory entry(s)
  291. template <typename T, typename Parameters>
  292. class geos_entry : public detail::factory_entry<T, Parameters>
  293. {
  294. public :
  295. virtual base_v<T, Parameters>* create_new(const Parameters& par) const
  296. {
  297. if (par.es)
  298. return new base_v_fi<geos_ellipsoid<T, Parameters>, T, Parameters>(par);
  299. else
  300. return new base_v_fi<geos_spheroid<T, Parameters>, T, Parameters>(par);
  301. }
  302. };
  303. template <typename T, typename Parameters>
  304. inline void geos_init(detail::base_factory<T, Parameters>& factory)
  305. {
  306. factory.add_to_factory("geos", new geos_entry<T, Parameters>);
  307. }
  308. } // namespace detail
  309. #endif // doxygen
  310. } // namespace projections
  311. }} // namespace boost::geometry
  312. #endif // BOOST_GEOMETRY_PROJECTIONS_GEOS_HPP