direction_code.hpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. // Boost.Geometry (aka GGL, Generic Geometry Library)
  2. // Copyright (c) 2015-2020 Barend Gehrels, Amsterdam, the Netherlands.
  3. // This file was modified by Oracle on 2015-2020.
  4. // Modifications copyright (c) 2015-2020 Oracle and/or its affiliates.
  5. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle
  6. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
  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_ALGORITHMS_DETAIL_DIRECTION_CODE_HPP
  11. #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_DIRECTION_CODE_HPP
  12. #include <type_traits>
  13. #include <boost/geometry/core/access.hpp>
  14. #include <boost/geometry/core/static_assert.hpp>
  15. #include <boost/geometry/arithmetic/infinite_line_functions.hpp>
  16. #include <boost/geometry/algorithms/detail/make/make.hpp>
  17. #include <boost/geometry/util/math.hpp>
  18. #include <boost/geometry/util/select_coordinate_type.hpp>
  19. #include <boost/geometry/util/normalize_spheroidal_coordinates.hpp>
  20. namespace boost { namespace geometry
  21. {
  22. #ifndef DOXYGEN_NO_DETAIL
  23. namespace detail
  24. {
  25. template <typename CSTag>
  26. struct direction_code_impl
  27. {
  28. BOOST_GEOMETRY_STATIC_ASSERT_FALSE(
  29. "Not implemented for this coordinate system.",
  30. CSTag);
  31. };
  32. template <>
  33. struct direction_code_impl<cartesian_tag>
  34. {
  35. template <typename PointSegmentA, typename PointSegmentB, typename Point2>
  36. static inline int apply(PointSegmentA const& segment_a, PointSegmentB const& segment_b,
  37. Point2 const& point)
  38. {
  39. using calc_t = typename geometry::select_coordinate_type
  40. <
  41. PointSegmentA, PointSegmentB, Point2
  42. >::type;
  43. using line_type = model::infinite_line<calc_t>;
  44. // Situation and construction of perpendicular line
  45. //
  46. // P1 a--------------->b P2
  47. // |
  48. // |
  49. // v
  50. //
  51. // P1 is located right of the (directional) perpendicular line
  52. // and therefore gets a negative side_value, and returns -1.
  53. // P2 is to the left of the perpendicular line and returns 1.
  54. // If the specified point is located on top of b, it returns 0.
  55. line_type const line
  56. = detail::make::make_perpendicular_line<calc_t>(segment_a,
  57. segment_b, segment_b);
  58. if (arithmetic::is_degenerate(line))
  59. {
  60. return 0;
  61. }
  62. calc_t const sv = arithmetic::side_value(line, point);
  63. static calc_t const zero = 0;
  64. return sv == zero ? 0 : sv > zero ? 1 : -1;
  65. }
  66. };
  67. template <>
  68. struct direction_code_impl<spherical_equatorial_tag>
  69. {
  70. template <typename PointSegmentA, typename PointSegmentB, typename Point2>
  71. static inline int apply(PointSegmentA const& segment_a, PointSegmentB const& segment_b,
  72. Point2 const& p)
  73. {
  74. {
  75. using units_sa_t = typename cs_angular_units<PointSegmentA>::type;
  76. using units_sb_t = typename cs_angular_units<PointSegmentB>::type;
  77. using units_p_t = typename cs_angular_units<Point2>::type;
  78. BOOST_GEOMETRY_STATIC_ASSERT(
  79. (std::is_same<units_sa_t, units_sb_t>::value),
  80. "Not implemented for different units.",
  81. units_sa_t, units_sb_t);
  82. BOOST_GEOMETRY_STATIC_ASSERT(
  83. (std::is_same<units_sa_t, units_p_t>::value),
  84. "Not implemented for different units.",
  85. units_sa_t, units_p_t);
  86. }
  87. using coor_sa_t = typename coordinate_type<PointSegmentA>::type;
  88. using coor_sb_t = typename coordinate_type<PointSegmentB>::type;
  89. using coor_p_t = typename coordinate_type<Point2>::type;
  90. // Declare unit type (equal for all types) and calc type (coerced to most precise)
  91. using units_t = typename cs_angular_units<Point2>::type;
  92. using calc_t = typename geometry::select_coordinate_type
  93. <
  94. PointSegmentA, PointSegmentB, Point2
  95. >::type;
  96. using constants_sa_t = math::detail::constants_on_spheroid<coor_sa_t, units_t>;
  97. using constants_sb_t = math::detail::constants_on_spheroid<coor_sb_t, units_t>;
  98. using constants_p_t = math::detail::constants_on_spheroid<coor_p_t, units_t>;
  99. static coor_sa_t const pi_half_sa = constants_sa_t::max_latitude();
  100. static coor_sb_t const pi_half_sb = constants_sb_t::max_latitude();
  101. static coor_p_t const pi_half_p = constants_p_t::max_latitude();
  102. static calc_t const c0 = 0;
  103. coor_sa_t const a0 = geometry::get<0>(segment_a);
  104. coor_sa_t const a1 = geometry::get<1>(segment_a);
  105. coor_sb_t const b0 = geometry::get<0>(segment_b);
  106. coor_sb_t const b1 = geometry::get<1>(segment_b);
  107. coor_p_t const p0 = geometry::get<0>(p);
  108. coor_p_t const p1 = geometry::get<1>(p);
  109. if ( (math::equals(b0, a0) && math::equals(b1, a1))
  110. || (math::equals(b0, p0) && math::equals(b1, p1)) )
  111. {
  112. return 0;
  113. }
  114. bool const is_a_pole = math::equals(pi_half_sa, math::abs(a1));
  115. bool const is_b_pole = math::equals(pi_half_sb, math::abs(b1));
  116. bool const is_p_pole = math::equals(pi_half_p, math::abs(p1));
  117. if ( is_b_pole && ((is_a_pole && math::sign(b1) == math::sign(a1))
  118. || (is_p_pole && math::sign(b1) == math::sign(p1))) )
  119. {
  120. return 0;
  121. }
  122. // NOTE: as opposed to the implementation for cartesian CS
  123. // here point b is the origin
  124. calc_t const dlon1 = math::longitude_distance_signed<units_t, calc_t>(b0, a0);
  125. calc_t const dlon2 = math::longitude_distance_signed<units_t, calc_t>(b0, p0);
  126. bool is_antilon1 = false, is_antilon2 = false;
  127. calc_t const dlat1 = latitude_distance_signed<units_t, calc_t>(b1, a1, dlon1, is_antilon1);
  128. calc_t const dlat2 = latitude_distance_signed<units_t, calc_t>(b1, p1, dlon2, is_antilon2);
  129. calc_t const mx = is_a_pole || is_b_pole || is_p_pole
  130. ? c0
  131. : (std::min)(is_antilon1 ? c0 : math::abs(dlon1),
  132. is_antilon2 ? c0 : math::abs(dlon2));
  133. calc_t const my = (std::min)(math::abs(dlat1),
  134. math::abs(dlat2));
  135. int s1 = 0, s2 = 0;
  136. if (mx >= my)
  137. {
  138. s1 = dlon1 > 0 ? 1 : -1;
  139. s2 = dlon2 > 0 ? 1 : -1;
  140. }
  141. else
  142. {
  143. s1 = dlat1 > 0 ? 1 : -1;
  144. s2 = dlat2 > 0 ? 1 : -1;
  145. }
  146. return s1 == s2 ? -1 : 1;
  147. }
  148. template <typename Units, typename T>
  149. static inline T latitude_distance_signed(T const& lat1, T const& lat2, T const& lon_ds, bool & is_antilon)
  150. {
  151. using constants = math::detail::constants_on_spheroid<T, Units>;
  152. static T const pi = constants::half_period();
  153. static T const c0 = 0;
  154. T res = lat2 - lat1;
  155. is_antilon = math::equals(math::abs(lon_ds), pi);
  156. if (is_antilon)
  157. {
  158. res = lat2 + lat1;
  159. if (res >= c0)
  160. res = pi - res;
  161. else
  162. res = -pi - res;
  163. }
  164. return res;
  165. }
  166. };
  167. template <>
  168. struct direction_code_impl<spherical_polar_tag>
  169. {
  170. template <typename PointSegmentA, typename PointSegmentB, typename Point2>
  171. static inline int apply(PointSegmentA segment_a, PointSegmentB segment_b,
  172. Point2 p)
  173. {
  174. using constants_sa_t = math::detail::constants_on_spheroid
  175. <
  176. typename coordinate_type<PointSegmentA>::type,
  177. typename cs_angular_units<PointSegmentA>::type
  178. >;
  179. using constants_p_t = math::detail::constants_on_spheroid
  180. <
  181. typename coordinate_type<Point2>::type,
  182. typename cs_angular_units<Point2>::type
  183. >;
  184. geometry::set<1>(segment_a,
  185. constants_sa_t::max_latitude() - geometry::get<1>(segment_a));
  186. geometry::set<1>(segment_b,
  187. constants_sa_t::max_latitude() - geometry::get<1>(segment_b));
  188. geometry::set<1>(p,
  189. constants_p_t::max_latitude() - geometry::get<1>(p));
  190. return direction_code_impl
  191. <
  192. spherical_equatorial_tag
  193. >::apply(segment_a, segment_b, p);
  194. }
  195. };
  196. // if spherical_tag is passed then pick cs_tag based on PointSegmentA type
  197. // with spherical_equatorial_tag as the default
  198. template <>
  199. struct direction_code_impl<spherical_tag>
  200. {
  201. template <typename PointSegmentA, typename PointSegmentB, typename Point2>
  202. static inline int apply(PointSegmentA segment_a, PointSegmentB segment_b,
  203. Point2 p)
  204. {
  205. return direction_code_impl
  206. <
  207. std::conditional_t
  208. <
  209. std::is_same
  210. <
  211. typename geometry::cs_tag<PointSegmentA>::type,
  212. spherical_polar_tag
  213. >::value,
  214. spherical_polar_tag,
  215. spherical_equatorial_tag
  216. >
  217. >::apply(segment_a, segment_b, p);
  218. }
  219. };
  220. template <>
  221. struct direction_code_impl<geographic_tag>
  222. : direction_code_impl<spherical_equatorial_tag>
  223. {};
  224. // Gives sense of direction for point p, collinear w.r.t. segment (a,b)
  225. // Returns -1 if p goes backward w.r.t (a,b), so goes from b in direction of a
  226. // Returns 1 if p goes forward, so extends (a,b)
  227. // Returns 0 if p is equal with b, or if (a,b) is degenerate
  228. // Note that it does not do any collinearity test, that should be done before
  229. // In some cases the "segment" consists of different source points, and therefore
  230. // their types might differ.
  231. template <typename CSTag, typename PointSegmentA, typename PointSegmentB, typename Point2>
  232. inline int direction_code(PointSegmentA const& segment_a, PointSegmentB const& segment_b,
  233. Point2 const& p)
  234. {
  235. return direction_code_impl<CSTag>::apply(segment_a, segment_b, p);
  236. }
  237. } // namespace detail
  238. #endif //DOXYGEN_NO_DETAIL
  239. }} // namespace boost::geometry
  240. #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_DIRECTION_CODE_HPP