aea.hpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  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. // Author: Gerald Evenden (1995)
  14. // Thomas Knudsen (2016) - revise/add regression tests
  15. // Last updated version of proj: 5.0.0
  16. // Original copyright notice:
  17. // Purpose: Implementation of the aea (Albers Equal Area) projection.
  18. // Author: Gerald Evenden
  19. // Copyright (c) 1995, Gerald Evenden
  20. // Permission is hereby granted, free of charge, to any person obtaining a
  21. // copy of this software and associated documentation files (the "Software"),
  22. // to deal in the Software without restriction, including without limitation
  23. // the rights to use, copy, modify, merge, publish, distribute, sublicense,
  24. // and/or sell copies of the Software, and to permit persons to whom the
  25. // Software is furnished to do so, subject to the following conditions:
  26. // The above copyright notice and this permission notice shall be included
  27. // in all copies or substantial portions of the Software.
  28. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  29. // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  30. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  31. // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  32. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  33. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  34. // DEALINGS IN THE SOFTWARE.
  35. #ifndef BOOST_GEOMETRY_PROJECTIONS_AEA_HPP
  36. #define BOOST_GEOMETRY_PROJECTIONS_AEA_HPP
  37. #include <boost/core/ignore_unused.hpp>
  38. #include <boost/geometry/util/math.hpp>
  39. #include <boost/math/special_functions/hypot.hpp>
  40. #include <boost/geometry/srs/projections/impl/base_static.hpp>
  41. #include <boost/geometry/srs/projections/impl/base_dynamic.hpp>
  42. #include <boost/geometry/srs/projections/impl/projects.hpp>
  43. #include <boost/geometry/srs/projections/impl/factory_entry.hpp>
  44. #include <boost/geometry/srs/projections/impl/pj_mlfn.hpp>
  45. #include <boost/geometry/srs/projections/impl/pj_msfn.hpp>
  46. #include <boost/geometry/srs/projections/impl/pj_param.hpp>
  47. #include <boost/geometry/srs/projections/impl/pj_qsfn.hpp>
  48. namespace boost { namespace geometry
  49. {
  50. namespace projections
  51. {
  52. #ifndef DOXYGEN_NO_DETAIL
  53. namespace detail { namespace aea
  54. {
  55. static const double epsilon10 = 1.e-10;
  56. static const double tolerance7 = 1.e-7;
  57. static const double epsilon = 1.0e-7;
  58. static const double tolerance = 1.0e-10;
  59. static const int n_iter = 15;
  60. template <typename T>
  61. struct par_aea
  62. {
  63. T ec;
  64. T n;
  65. T c;
  66. T dd;
  67. T n2;
  68. T rho0;
  69. T phi1;
  70. T phi2;
  71. detail::en<T> en;
  72. int ellips;
  73. };
  74. /* determine latitude angle phi-1 */
  75. template <typename T>
  76. inline T phi1_(T const& qs, T const& Te, T const& Tone_es)
  77. {
  78. int i;
  79. T Phi, sinpi, cospi, con, com, dphi;
  80. Phi = asin (.5 * qs);
  81. if (Te < epsilon)
  82. return( Phi );
  83. i = n_iter;
  84. do {
  85. sinpi = sin (Phi);
  86. cospi = cos (Phi);
  87. con = Te * sinpi;
  88. com = 1. - con * con;
  89. dphi = .5 * com * com / cospi * (qs / Tone_es -
  90. sinpi / com + .5 / Te * log ((1. - con) /
  91. (1. + con)));
  92. Phi += dphi;
  93. } while (fabs(dphi) > tolerance && --i);
  94. return( i ? Phi : HUGE_VAL );
  95. }
  96. // template class, using CRTP to implement forward/inverse
  97. template <typename T, typename Parameters>
  98. struct base_aea_ellipsoid
  99. : public base_t_fi<base_aea_ellipsoid<T, Parameters>, T, Parameters>
  100. {
  101. par_aea<T> m_proj_parm;
  102. inline base_aea_ellipsoid(const Parameters& par)
  103. : base_t_fi<base_aea_ellipsoid<T, Parameters>, T, Parameters>(*this, par)
  104. {}
  105. // FORWARD(e_forward) ellipsoid & spheroid
  106. // Project coordinates from geographic (lon, lat) to cartesian (x, y)
  107. inline void fwd(T lp_lon, T const& lp_lat, T& xy_x, T& xy_y) const
  108. {
  109. T rho = this->m_proj_parm.c - (this->m_proj_parm.ellips
  110. ? this->m_proj_parm.n * pj_qsfn(sin(lp_lat), this->m_par.e, this->m_par.one_es)
  111. : this->m_proj_parm.n2 * sin(lp_lat));
  112. if (rho < 0.)
  113. BOOST_THROW_EXCEPTION( projection_exception(error_tolerance_condition) );
  114. rho = this->m_proj_parm.dd * sqrt(rho);
  115. xy_x = rho * sin( lp_lon *= this->m_proj_parm.n );
  116. xy_y = this->m_proj_parm.rho0 - rho * cos(lp_lon);
  117. }
  118. // INVERSE(e_inverse) ellipsoid & spheroid
  119. // Project coordinates from cartesian (x, y) to geographic (lon, lat)
  120. inline void inv(T xy_x, T xy_y, T& lp_lon, T& lp_lat) const
  121. {
  122. static const T half_pi = detail::half_pi<T>();
  123. T rho = 0.0;
  124. if( (rho = boost::math::hypot(xy_x, xy_y = this->m_proj_parm.rho0 - xy_y)) != 0.0 ) {
  125. if (this->m_proj_parm.n < 0.) {
  126. rho = -rho;
  127. xy_x = -xy_x;
  128. xy_y = -xy_y;
  129. }
  130. lp_lat = rho / this->m_proj_parm.dd;
  131. if (this->m_proj_parm.ellips) {
  132. lp_lat = (this->m_proj_parm.c - lp_lat * lp_lat) / this->m_proj_parm.n;
  133. if (fabs(this->m_proj_parm.ec - fabs(lp_lat)) > tolerance7) {
  134. if ((lp_lat = phi1_(lp_lat, this->m_par.e, this->m_par.one_es)) == HUGE_VAL)
  135. BOOST_THROW_EXCEPTION( projection_exception(error_tolerance_condition) );
  136. } else
  137. lp_lat = lp_lat < 0. ? -half_pi : half_pi;
  138. } else if (fabs(lp_lat = (this->m_proj_parm.c - lp_lat * lp_lat) / this->m_proj_parm.n2) <= 1.)
  139. lp_lat = asin(lp_lat);
  140. else
  141. lp_lat = lp_lat < 0. ? -half_pi : half_pi;
  142. lp_lon = atan2(xy_x, xy_y) / this->m_proj_parm.n;
  143. } else {
  144. lp_lon = 0.;
  145. lp_lat = this->m_proj_parm.n > 0. ? half_pi : - half_pi;
  146. }
  147. }
  148. static inline std::string get_name()
  149. {
  150. return "aea_ellipsoid";
  151. }
  152. };
  153. template <typename Parameters, typename T>
  154. inline void setup(Parameters const& par, par_aea<T>& proj_parm)
  155. {
  156. T cosphi, sinphi;
  157. int secant;
  158. if (fabs(proj_parm.phi1 + proj_parm.phi2) < epsilon10)
  159. BOOST_THROW_EXCEPTION( projection_exception(error_conic_lat_equal) );
  160. proj_parm.n = sinphi = sin(proj_parm.phi1);
  161. cosphi = cos(proj_parm.phi1);
  162. secant = fabs(proj_parm.phi1 - proj_parm.phi2) >= epsilon10;
  163. if( (proj_parm.ellips = (par.es > 0.))) {
  164. T ml1, m1;
  165. proj_parm.en = pj_enfn<T>(par.es);
  166. m1 = pj_msfn(sinphi, cosphi, par.es);
  167. ml1 = pj_qsfn(sinphi, par.e, par.one_es);
  168. if (secant) { /* secant cone */
  169. T ml2, m2;
  170. sinphi = sin(proj_parm.phi2);
  171. cosphi = cos(proj_parm.phi2);
  172. m2 = pj_msfn(sinphi, cosphi, par.es);
  173. ml2 = pj_qsfn(sinphi, par.e, par.one_es);
  174. if (ml2 == ml1)
  175. BOOST_THROW_EXCEPTION( projection_exception(0) );
  176. proj_parm.n = (m1 * m1 - m2 * m2) / (ml2 - ml1);
  177. }
  178. proj_parm.ec = 1. - .5 * par.one_es * log((1. - par.e) /
  179. (1. + par.e)) / par.e;
  180. proj_parm.c = m1 * m1 + proj_parm.n * ml1;
  181. proj_parm.dd = 1. / proj_parm.n;
  182. proj_parm.rho0 = proj_parm.dd * sqrt(proj_parm.c - proj_parm.n * pj_qsfn(sin(par.phi0),
  183. par.e, par.one_es));
  184. } else {
  185. if (secant) proj_parm.n = .5 * (proj_parm.n + sin(proj_parm.phi2));
  186. proj_parm.n2 = proj_parm.n + proj_parm.n;
  187. proj_parm.c = cosphi * cosphi + proj_parm.n2 * sinphi;
  188. proj_parm.dd = 1. / proj_parm.n;
  189. proj_parm.rho0 = proj_parm.dd * sqrt(proj_parm.c - proj_parm.n2 * sin(par.phi0));
  190. }
  191. }
  192. // Albers Equal Area
  193. template <typename Params, typename Parameters, typename T>
  194. inline void setup_aea(Params const& params, Parameters const& par, par_aea<T>& proj_parm)
  195. {
  196. proj_parm.phi1 = 0.0;
  197. proj_parm.phi2 = 0.0;
  198. bool is_phi1_set = pj_param_r<srs::spar::lat_1>(params, "lat_1", srs::dpar::lat_1, proj_parm.phi1);
  199. bool is_phi2_set = pj_param_r<srs::spar::lat_2>(params, "lat_2", srs::dpar::lat_2, proj_parm.phi2);
  200. // Boost.Geometry specific, set default parameters manually
  201. if (! is_phi1_set || ! is_phi2_set) {
  202. bool const use_defaults = ! pj_get_param_b<srs::spar::no_defs>(params, "no_defs", srs::dpar::no_defs);
  203. if (use_defaults) {
  204. if (!is_phi1_set)
  205. proj_parm.phi1 = 29.5;
  206. if (!is_phi2_set)
  207. proj_parm.phi2 = 45.5;
  208. }
  209. }
  210. setup(par, proj_parm);
  211. }
  212. // Lambert Equal Area Conic
  213. template <typename Params, typename Parameters, typename T>
  214. inline void setup_leac(Params const& params, Parameters const& par, par_aea<T>& proj_parm)
  215. {
  216. static const T half_pi = detail::half_pi<T>();
  217. proj_parm.phi2 = pj_get_param_r<T, srs::spar::lat_1>(params, "lat_1", srs::dpar::lat_1);
  218. proj_parm.phi1 = pj_get_param_b<srs::spar::south>(params, "south", srs::dpar::south) ? -half_pi : half_pi;
  219. setup(par, proj_parm);
  220. }
  221. }} // namespace detail::aea
  222. #endif // doxygen
  223. /*!
  224. \brief Albers Equal Area projection
  225. \ingroup projections
  226. \tparam Geographic latlong point type
  227. \tparam Cartesian xy point type
  228. \tparam Parameters parameter type
  229. \par Projection characteristics
  230. - Conic
  231. - Spheroid
  232. - Ellipsoid
  233. \par Projection parameters
  234. - lat_1: Latitude of first standard parallel (degrees)
  235. - lat_2: Latitude of second standard parallel (degrees)
  236. \par Example
  237. \image html ex_aea.gif
  238. */
  239. template <typename T, typename Parameters>
  240. struct aea_ellipsoid : public detail::aea::base_aea_ellipsoid<T, Parameters>
  241. {
  242. template <typename Params>
  243. inline aea_ellipsoid(Params const& params, Parameters const& par)
  244. : detail::aea::base_aea_ellipsoid<T, Parameters>(par)
  245. {
  246. detail::aea::setup_aea(params, this->m_par, this->m_proj_parm);
  247. }
  248. };
  249. /*!
  250. \brief Lambert Equal Area Conic projection
  251. \ingroup projections
  252. \tparam Geographic latlong point type
  253. \tparam Cartesian xy point type
  254. \tparam Parameters parameter type
  255. \par Projection characteristics
  256. - Conic
  257. - Spheroid
  258. - Ellipsoid
  259. \par Projection parameters
  260. - lat_1: Latitude of first standard parallel (degrees)
  261. - south: Denotes southern hemisphere UTM zone (boolean)
  262. \par Example
  263. \image html ex_leac.gif
  264. */
  265. template <typename T, typename Parameters>
  266. struct leac_ellipsoid : public detail::aea::base_aea_ellipsoid<T, Parameters>
  267. {
  268. template <typename Params>
  269. inline leac_ellipsoid(Params const& params, Parameters const& par)
  270. : detail::aea::base_aea_ellipsoid<T, Parameters>(par)
  271. {
  272. detail::aea::setup_leac(params, this->m_par, this->m_proj_parm);
  273. }
  274. };
  275. #ifndef DOXYGEN_NO_DETAIL
  276. namespace detail
  277. {
  278. // Static projection
  279. BOOST_GEOMETRY_PROJECTIONS_DETAIL_STATIC_PROJECTION(srs::spar::proj_aea, aea_ellipsoid, aea_ellipsoid)
  280. BOOST_GEOMETRY_PROJECTIONS_DETAIL_STATIC_PROJECTION(srs::spar::proj_leac, leac_ellipsoid, leac_ellipsoid)
  281. // Factory entry(s)
  282. BOOST_GEOMETRY_PROJECTIONS_DETAIL_FACTORY_ENTRY_FI(aea_entry, aea_ellipsoid)
  283. BOOST_GEOMETRY_PROJECTIONS_DETAIL_FACTORY_ENTRY_FI(leac_entry, leac_ellipsoid)
  284. BOOST_GEOMETRY_PROJECTIONS_DETAIL_FACTORY_INIT_BEGIN(aea_init)
  285. {
  286. BOOST_GEOMETRY_PROJECTIONS_DETAIL_FACTORY_INIT_ENTRY(aea, aea_entry)
  287. BOOST_GEOMETRY_PROJECTIONS_DETAIL_FACTORY_INIT_ENTRY(leac, leac_entry)
  288. }
  289. } // namespace detail
  290. #endif // doxygen
  291. } // namespace projections
  292. }} // namespace boost::geometry
  293. #endif // BOOST_GEOMETRY_PROJECTIONS_AEA_HPP