strategy_transform.hpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504
  1. // Boost.Geometry (aka GGL, Generic Geometry Library)
  2. // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
  3. // Copyright (c) 2008-2012 Bruno Lalande, Paris, France.
  4. // Copyright (c) 2009-2012 Mateusz Loskot, London, UK.
  5. // Parts of Boost.Geometry are redesigned from Geodan's Geographic Library
  6. // (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands.
  7. // Use, modification and distribution is subject to the Boost Software License,
  8. // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
  9. // http://www.boost.org/LICENSE_1_0.txt)
  10. #ifndef BOOST_GEOMETRY_STRATEGIES_STRATEGY_TRANSFORM_HPP
  11. #define BOOST_GEOMETRY_STRATEGIES_STRATEGY_TRANSFORM_HPP
  12. #include <cstddef>
  13. #include <cmath>
  14. #include <functional>
  15. #include <boost/numeric/conversion/cast.hpp>
  16. #include <boost/geometry/algorithms/convert.hpp>
  17. #include <boost/geometry/arithmetic/arithmetic.hpp>
  18. #include <boost/geometry/core/access.hpp>
  19. #include <boost/geometry/core/radian_access.hpp>
  20. #include <boost/geometry/core/coordinate_dimension.hpp>
  21. #include <boost/geometry/strategies/transform.hpp>
  22. #include <boost/geometry/util/math.hpp>
  23. #include <boost/geometry/util/select_coordinate_type.hpp>
  24. namespace boost { namespace geometry
  25. {
  26. namespace strategy { namespace transform
  27. {
  28. #ifndef DOXYGEN_NO_DETAIL
  29. namespace detail
  30. {
  31. template
  32. <
  33. typename Src, typename Dst,
  34. std::size_t D, std::size_t N,
  35. template <typename> class F
  36. >
  37. struct transform_coordinates
  38. {
  39. template <typename T>
  40. static inline void transform(Src const& source, Dst& dest, T value)
  41. {
  42. typedef typename select_coordinate_type<Src, Dst>::type coordinate_type;
  43. F<coordinate_type> function;
  44. set<D>(dest, boost::numeric_cast<coordinate_type>(function(get<D>(source), value)));
  45. transform_coordinates<Src, Dst, D + 1, N, F>::transform(source, dest, value);
  46. }
  47. };
  48. template
  49. <
  50. typename Src, typename Dst,
  51. std::size_t N,
  52. template <typename> class F
  53. >
  54. struct transform_coordinates<Src, Dst, N, N, F>
  55. {
  56. template <typename T>
  57. static inline void transform(Src const& , Dst& , T )
  58. {
  59. }
  60. };
  61. } // namespace detail
  62. #endif // DOXYGEN_NO_DETAIL
  63. /*!
  64. \brief Transformation strategy to copy one point to another using assignment operator
  65. \ingroup transform
  66. \tparam P point type
  67. */
  68. template <typename P>
  69. struct copy_direct
  70. {
  71. inline bool apply(P const& p1, P& p2) const
  72. {
  73. p2 = p1;
  74. return true;
  75. }
  76. };
  77. /*!
  78. \brief Transformation strategy to do copy a point, copying per coordinate.
  79. \ingroup transform
  80. \tparam P1 first point type
  81. \tparam P2 second point type
  82. */
  83. template <typename P1, typename P2>
  84. struct copy_per_coordinate
  85. {
  86. inline bool apply(P1 const& p1, P2& p2) const
  87. {
  88. // Defensive check, dimensions are equal, selected by specialization
  89. assert_dimension_equal<P1, P2>();
  90. geometry::convert(p1, p2);
  91. return true;
  92. }
  93. };
  94. /*!
  95. \brief Transformation strategy to go from degree to radian and back
  96. \ingroup transform
  97. \tparam P1 first point type
  98. \tparam P2 second point type
  99. \tparam F additional functor to divide or multiply with d2r
  100. */
  101. template <typename P1, typename P2, template <typename> class F>
  102. struct degree_radian_vv
  103. {
  104. inline bool apply(P1 const& p1, P2& p2) const
  105. {
  106. // Spherical coordinates always have 2 coordinates measured in angles
  107. // The optional third one is distance/height, provided in another strategy
  108. // Polar coordinates having one angle, will be also in another strategy
  109. assert_dimension<P1, 2>();
  110. assert_dimension<P2, 2>();
  111. detail::transform_coordinates<P1, P2, 0, 2, F>::transform(p1, p2, math::d2r);
  112. return true;
  113. }
  114. };
  115. template <typename P1, typename P2, template <typename> class F>
  116. struct degree_radian_vv_3
  117. {
  118. inline bool apply(P1 const& p1, P2& p2) const
  119. {
  120. assert_dimension<P1, 3>();
  121. assert_dimension<P2, 3>();
  122. detail::transform_coordinates<P1, P2, 0, 2, F>::transform(p1, p2, math::d2r);
  123. // Copy height or other third dimension
  124. set<2>(p2, get<2>(p1));
  125. return true;
  126. }
  127. };
  128. #ifndef DOXYGEN_NO_DETAIL
  129. namespace detail
  130. {
  131. /// Helper function for conversion, phi/theta are in radians
  132. template <typename P, typename T, typename R>
  133. inline void spherical_polar_to_cartesian(T phi, T theta, R r, P& p)
  134. {
  135. assert_dimension<P, 3>();
  136. // http://en.wikipedia.org/wiki/List_of_canonical_coordinate_transformations#From_spherical_coordinates
  137. // http://www.vias.org/comp_geometry/math_coord_convert_3d.htm
  138. // https://moodle.polymtl.ca/file.php/1183/Autres_Documents/Derivation_for_Spherical_Co-ordinates.pdf
  139. // http://en.citizendium.org/wiki/Spherical_polar_coordinates
  140. // Phi = first, theta is second, r is third, see documentation on cs::spherical
  141. // (calculations are splitted to implement ttmath)
  142. T r_sin_theta = r;
  143. T r_cos_theta = r;
  144. r_sin_theta *= sin(theta);
  145. r_cos_theta *= cos(theta);
  146. set<0>(p, r_sin_theta * cos(phi));
  147. set<1>(p, r_sin_theta * sin(phi));
  148. set<2>(p, r_cos_theta);
  149. }
  150. /// Helper function for conversion, lambda/delta (lon lat) are in radians
  151. template <typename P, typename T, typename R>
  152. inline void spherical_equatorial_to_cartesian(T lambda, T delta, R r, P& p)
  153. {
  154. assert_dimension<P, 3>();
  155. // http://mathworld.wolfram.com/GreatCircle.html
  156. // http://www.spenvis.oma.be/help/background/coortran/coortran.html WRONG
  157. T r_cos_delta = r;
  158. T r_sin_delta = r;
  159. r_cos_delta *= cos(delta);
  160. r_sin_delta *= sin(delta);
  161. set<0>(p, r_cos_delta * cos(lambda));
  162. set<1>(p, r_cos_delta * sin(lambda));
  163. set<2>(p, r_sin_delta);
  164. }
  165. /// Helper function for conversion
  166. template <typename P, typename T>
  167. inline bool cartesian_to_spherical2(T x, T y, T z, P& p)
  168. {
  169. assert_dimension<P, 2>();
  170. // http://en.wikipedia.org/wiki/List_of_canonical_coordinate_transformations#From_Cartesian_coordinates
  171. #if defined(BOOST_GEOMETRY_TRANSFORM_CHECK_UNIT_SPHERE)
  172. // TODO: MAYBE ONLY IF TO BE CHECKED?
  173. T const r = /*sqrt not necessary, sqrt(1)=1*/ (x * x + y * y + z * z);
  174. // Unit sphere, so r should be 1
  175. if (geometry::math::abs(r - 1.0) > T(1e-6))
  176. {
  177. return false;
  178. }
  179. // end todo
  180. #endif
  181. set_from_radian<0>(p, atan2(y, x));
  182. set_from_radian<1>(p, acos(z));
  183. return true;
  184. }
  185. template <typename P, typename T>
  186. inline bool cartesian_to_spherical_equatorial2(T x, T y, T z, P& p)
  187. {
  188. assert_dimension<P, 2>();
  189. set_from_radian<0>(p, atan2(y, x));
  190. set_from_radian<1>(p, asin(z));
  191. return true;
  192. }
  193. template <typename P, typename T>
  194. inline bool cartesian_to_spherical3(T x, T y, T z, P& p)
  195. {
  196. assert_dimension<P, 3>();
  197. // http://en.wikipedia.org/wiki/List_of_canonical_coordinate_transformations#From_Cartesian_coordinates
  198. T const r = sqrt(x * x + y * y + z * z);
  199. set<2>(p, r);
  200. set_from_radian<0>(p, atan2(y, x));
  201. if (r > 0.0)
  202. {
  203. set_from_radian<1>(p, acos(z / r));
  204. return true;
  205. }
  206. return false;
  207. }
  208. template <typename P, typename T>
  209. inline bool cartesian_to_spherical_equatorial3(T x, T y, T z, P& p)
  210. {
  211. assert_dimension<P, 3>();
  212. // http://en.wikipedia.org/wiki/List_of_canonical_coordinate_transformations#From_Cartesian_coordinates
  213. T const r = sqrt(x * x + y * y + z * z);
  214. set<2>(p, r);
  215. set_from_radian<0>(p, atan2(y, x));
  216. if (r > 0.0)
  217. {
  218. set_from_radian<1>(p, asin(z / r));
  219. return true;
  220. }
  221. return false;
  222. }
  223. } // namespace detail
  224. #endif // DOXYGEN_NO_DETAIL
  225. /*!
  226. \brief Transformation strategy for 2D spherical (phi,theta) to 3D cartesian (x,y,z)
  227. \details on Unit sphere
  228. \ingroup transform
  229. \tparam P1 first point type
  230. \tparam P2 second point type
  231. */
  232. template <typename P1, typename P2>
  233. struct from_spherical_polar_2_to_cartesian_3
  234. {
  235. inline bool apply(P1 const& p1, P2& p2) const
  236. {
  237. assert_dimension<P1, 2>();
  238. detail::spherical_polar_to_cartesian(get_as_radian<0>(p1), get_as_radian<1>(p1), 1.0, p2);
  239. return true;
  240. }
  241. };
  242. template <typename P1, typename P2>
  243. struct from_spherical_equatorial_2_to_cartesian_3
  244. {
  245. inline bool apply(P1 const& p1, P2& p2) const
  246. {
  247. assert_dimension<P1, 2>();
  248. detail::spherical_equatorial_to_cartesian(get_as_radian<0>(p1), get_as_radian<1>(p1), 1.0, p2);
  249. return true;
  250. }
  251. };
  252. /*!
  253. \brief Transformation strategy for 3D spherical (phi,theta,r) to 3D cartesian (x,y,z)
  254. \ingroup transform
  255. \tparam P1 first point type
  256. \tparam P2 second point type
  257. */
  258. template <typename P1, typename P2>
  259. struct from_spherical_polar_3_to_cartesian_3
  260. {
  261. inline bool apply(P1 const& p1, P2& p2) const
  262. {
  263. assert_dimension<P1, 3>();
  264. detail::spherical_polar_to_cartesian(
  265. get_as_radian<0>(p1), get_as_radian<1>(p1), get<2>(p1), p2);
  266. return true;
  267. }
  268. };
  269. template <typename P1, typename P2>
  270. struct from_spherical_equatorial_3_to_cartesian_3
  271. {
  272. inline bool apply(P1 const& p1, P2& p2) const
  273. {
  274. assert_dimension<P1, 3>();
  275. detail::spherical_equatorial_to_cartesian(
  276. get_as_radian<0>(p1), get_as_radian<1>(p1), get<2>(p1), p2);
  277. return true;
  278. }
  279. };
  280. /*!
  281. \brief Transformation strategy for 3D cartesian (x,y,z) to 2D spherical (phi,theta)
  282. \details on Unit sphere
  283. \ingroup transform
  284. \tparam P1 first point type
  285. \tparam P2 second point type
  286. \note If x,y,z point is not lying on unit sphere, transformation will return false
  287. */
  288. template <typename P1, typename P2>
  289. struct from_cartesian_3_to_spherical_polar_2
  290. {
  291. inline bool apply(P1 const& p1, P2& p2) const
  292. {
  293. assert_dimension<P1, 3>();
  294. return detail::cartesian_to_spherical2(get<0>(p1), get<1>(p1), get<2>(p1), p2);
  295. }
  296. };
  297. template <typename P1, typename P2>
  298. struct from_cartesian_3_to_spherical_equatorial_2
  299. {
  300. inline bool apply(P1 const& p1, P2& p2) const
  301. {
  302. assert_dimension<P1, 3>();
  303. return detail::cartesian_to_spherical_equatorial2(get<0>(p1), get<1>(p1), get<2>(p1), p2);
  304. }
  305. };
  306. /*!
  307. \brief Transformation strategy for 3D cartesian (x,y,z) to 3D spherical (phi,theta,r)
  308. \ingroup transform
  309. \tparam P1 first point type
  310. \tparam P2 second point type
  311. */
  312. template <typename P1, typename P2>
  313. struct from_cartesian_3_to_spherical_polar_3
  314. {
  315. inline bool apply(P1 const& p1, P2& p2) const
  316. {
  317. assert_dimension<P1, 3>();
  318. return detail::cartesian_to_spherical3(get<0>(p1), get<1>(p1), get<2>(p1), p2);
  319. }
  320. };
  321. template <typename P1, typename P2>
  322. struct from_cartesian_3_to_spherical_equatorial_3
  323. {
  324. inline bool apply(P1 const& p1, P2& p2) const
  325. {
  326. assert_dimension<P1, 3>();
  327. return detail::cartesian_to_spherical_equatorial3(get<0>(p1), get<1>(p1), get<2>(p1), p2);
  328. }
  329. };
  330. #ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS
  331. namespace services
  332. {
  333. /// Specialization for same coordinate system family, same system, same dimension, same point type, can be copied
  334. template <typename CoordSysTag, typename CoordSys, std::size_t D, typename P>
  335. struct default_strategy<CoordSysTag, CoordSysTag, CoordSys, CoordSys, D, D, P, P>
  336. {
  337. typedef copy_direct<P> type;
  338. };
  339. /// Specialization for same coordinate system family and system, same dimension, different point type, copy per coordinate
  340. template <typename CoordSysTag, typename CoordSys, std::size_t D, typename P1, typename P2>
  341. struct default_strategy<CoordSysTag, CoordSysTag, CoordSys, CoordSys, D, D, P1, P2>
  342. {
  343. typedef copy_per_coordinate<P1, P2> type;
  344. };
  345. /// Specialization to transform from degree to radian for any coordinate system / point type combination
  346. template <typename CoordSysTag, template<typename> class CoordSys, typename P1, typename P2>
  347. struct default_strategy<CoordSysTag, CoordSysTag, CoordSys<degree>, CoordSys<radian>, 2, 2, P1, P2>
  348. {
  349. typedef degree_radian_vv<P1, P2, std::multiplies> type;
  350. };
  351. /// Specialization to transform from radian to degree for any coordinate system / point type combination
  352. template <typename CoordSysTag, template<typename> class CoordSys, typename P1, typename P2>
  353. struct default_strategy<CoordSysTag, CoordSysTag, CoordSys<radian>, CoordSys<degree>, 2, 2, P1, P2>
  354. {
  355. typedef degree_radian_vv<P1, P2, std::divides> type;
  356. };
  357. /// Specialization degree->radian in 3D
  358. template <typename CoordSysTag, template<typename> class CoordSys, typename P1, typename P2>
  359. struct default_strategy<CoordSysTag, CoordSysTag, CoordSys<degree>, CoordSys<radian>, 3, 3, P1, P2>
  360. {
  361. typedef degree_radian_vv_3<P1, P2, std::multiplies> type;
  362. };
  363. /// Specialization radian->degree in 3D
  364. template <typename CoordSysTag, template<typename> class CoordSys, typename P1, typename P2>
  365. struct default_strategy<CoordSysTag, CoordSysTag, CoordSys<radian>, CoordSys<degree>, 3, 3, P1, P2>
  366. {
  367. typedef degree_radian_vv_3<P1, P2, std::divides> type;
  368. };
  369. /// Specialization to transform from unit sphere(phi,theta) to XYZ
  370. template <typename CoordSys1, typename CoordSys2, typename P1, typename P2>
  371. struct default_strategy<spherical_polar_tag, cartesian_tag, CoordSys1, CoordSys2, 2, 3, P1, P2>
  372. {
  373. typedef from_spherical_polar_2_to_cartesian_3<P1, P2> type;
  374. };
  375. /// Specialization to transform from sphere(phi,theta,r) to XYZ
  376. template <typename CoordSys1, typename CoordSys2, typename P1, typename P2>
  377. struct default_strategy<spherical_polar_tag, cartesian_tag, CoordSys1, CoordSys2, 3, 3, P1, P2>
  378. {
  379. typedef from_spherical_polar_3_to_cartesian_3<P1, P2> type;
  380. };
  381. template <typename CoordSys1, typename CoordSys2, typename P1, typename P2>
  382. struct default_strategy<spherical_equatorial_tag, cartesian_tag, CoordSys1, CoordSys2, 2, 3, P1, P2>
  383. {
  384. typedef from_spherical_equatorial_2_to_cartesian_3<P1, P2> type;
  385. };
  386. template <typename CoordSys1, typename CoordSys2, typename P1, typename P2>
  387. struct default_strategy<spherical_equatorial_tag, cartesian_tag, CoordSys1, CoordSys2, 3, 3, P1, P2>
  388. {
  389. typedef from_spherical_equatorial_3_to_cartesian_3<P1, P2> type;
  390. };
  391. /// Specialization to transform from XYZ to unit sphere(phi,theta)
  392. template <typename CoordSys1, typename CoordSys2, typename P1, typename P2>
  393. struct default_strategy<cartesian_tag, spherical_polar_tag, CoordSys1, CoordSys2, 3, 2, P1, P2>
  394. {
  395. typedef from_cartesian_3_to_spherical_polar_2<P1, P2> type;
  396. };
  397. template <typename CoordSys1, typename CoordSys2, typename P1, typename P2>
  398. struct default_strategy<cartesian_tag, spherical_equatorial_tag, CoordSys1, CoordSys2, 3, 2, P1, P2>
  399. {
  400. typedef from_cartesian_3_to_spherical_equatorial_2<P1, P2> type;
  401. };
  402. /// Specialization to transform from XYZ to sphere(phi,theta,r)
  403. template <typename CoordSys1, typename CoordSys2, typename P1, typename P2>
  404. struct default_strategy<cartesian_tag, spherical_polar_tag, CoordSys1, CoordSys2, 3, 3, P1, P2>
  405. {
  406. typedef from_cartesian_3_to_spherical_polar_3<P1, P2> type;
  407. };
  408. template <typename CoordSys1, typename CoordSys2, typename P1, typename P2>
  409. struct default_strategy<cartesian_tag, spherical_equatorial_tag, CoordSys1, CoordSys2, 3, 3, P1, P2>
  410. {
  411. typedef from_cartesian_3_to_spherical_equatorial_3<P1, P2> type;
  412. };
  413. } // namespace services
  414. #endif // DOXYGEN_NO_STRATEGY_SPECIALIZATIONS
  415. }} // namespace strategy::transform
  416. }} // namespace boost::geometry
  417. #endif // BOOST_GEOMETRY_STRATEGIES_STRATEGY_TRANSFORM_HPP