point_in_poly_winding.hpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593
  1. // Boost.Geometry (aka GGL, Generic Geometry Library)
  2. // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
  3. // Copyright (c) 2013-2017 Adam Wulkiewicz, Lodz, Poland.
  4. // This file was modified by Oracle on 2013-2023.
  5. // Modifications copyright (c) 2013-2023 Oracle and/or its affiliates.
  6. // Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle
  7. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
  8. // Parts of Boost.Geometry are redesigned from Geodan's Geographic Library
  9. // (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands.
  10. // Use, modification and distribution is subject to the Boost Software License,
  11. // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
  12. // http://www.boost.org/LICENSE_1_0.txt)
  13. #ifndef BOOST_GEOMETRY_STRATEGY_SPHERICAL_POINT_IN_POLY_WINDING_HPP
  14. #define BOOST_GEOMETRY_STRATEGY_SPHERICAL_POINT_IN_POLY_WINDING_HPP
  15. #include <boost/geometry/core/access.hpp>
  16. #include <boost/geometry/core/coordinate_system.hpp>
  17. #include <boost/geometry/core/cs.hpp>
  18. #include <boost/geometry/core/tags.hpp>
  19. #include <boost/geometry/util/math.hpp>
  20. #include <boost/geometry/util/select_calculation_type.hpp>
  21. #include <boost/geometry/util/normalize_spheroidal_coordinates.hpp>
  22. #include <boost/geometry/strategy/spherical/expand_point.hpp>
  23. #include <boost/geometry/strategies/cartesian/point_in_box.hpp>
  24. #include <boost/geometry/strategies/covered_by.hpp>
  25. #include <boost/geometry/strategies/side.hpp>
  26. #include <boost/geometry/strategies/spherical/disjoint_box_box.hpp>
  27. #include <boost/geometry/strategies/spherical/ssf.hpp>
  28. #include <boost/geometry/strategies/within.hpp>
  29. namespace boost { namespace geometry
  30. {
  31. namespace strategy { namespace within
  32. {
  33. #ifndef DOXYGEN_NO_DETAIL
  34. namespace detail
  35. {
  36. template <typename SideStrategy, typename CalculationType>
  37. class spherical_winding_base
  38. {
  39. template <typename Point, typename PointOfSegment>
  40. struct calculation_type
  41. : select_calculation_type
  42. <
  43. Point,
  44. PointOfSegment,
  45. CalculationType
  46. >
  47. {};
  48. /*! subclass to keep state */
  49. class counter
  50. {
  51. int m_count;
  52. //int m_count_n;
  53. int m_count_s;
  54. int m_raw_count;
  55. int m_raw_count_anti;
  56. bool m_touches;
  57. inline int code() const
  58. {
  59. if (m_touches)
  60. {
  61. return 0;
  62. }
  63. if (m_raw_count != 0 && m_raw_count_anti != 0)
  64. {
  65. if (m_raw_count > 0) // right, wrap around south pole
  66. {
  67. return (m_count + m_count_s) == 0 ? -1 : 1;
  68. }
  69. else // left, wrap around north pole
  70. {
  71. //return (m_count + m_count_n) == 0 ? -1 : 1;
  72. // m_count_n is 0
  73. return m_count == 0 ? -1 : 1;
  74. }
  75. }
  76. return m_count == 0 ? -1 : 1;
  77. }
  78. public :
  79. friend class spherical_winding_base;
  80. inline counter()
  81. : m_count(0)
  82. //, m_count_n(0)
  83. , m_count_s(0)
  84. , m_raw_count(0)
  85. , m_raw_count_anti(0)
  86. , m_touches(false)
  87. {}
  88. };
  89. struct count_info
  90. {
  91. explicit count_info(int c = 0, bool ia = false)
  92. : count(c)
  93. , is_anti(ia)
  94. {}
  95. int count;
  96. bool is_anti;
  97. };
  98. public:
  99. typedef typename SideStrategy::cs_tag cs_tag;
  100. spherical_winding_base() = default;
  101. template <typename Model>
  102. explicit spherical_winding_base(Model const& model)
  103. : m_side_strategy(model)
  104. {}
  105. // Typedefs and static methods to fulfill the concept
  106. typedef counter state_type;
  107. template <typename Point, typename PointOfSegment>
  108. inline bool apply(Point const& point,
  109. PointOfSegment const& s1, PointOfSegment const& s2,
  110. counter& state) const
  111. {
  112. typedef typename calculation_type<Point, PointOfSegment>::type calc_t;
  113. typedef typename geometry::detail::cs_angular_units<Point>::type units_t;
  114. typedef math::detail::constants_on_spheroid<calc_t, units_t> constants;
  115. bool eq1 = false;
  116. bool eq2 = false;
  117. bool s_antipodal = false;
  118. count_info ci = check_segment(point, s1, s2, state, eq1, eq2, s_antipodal);
  119. if (ci.count != 0)
  120. {
  121. if (! ci.is_anti)
  122. {
  123. int side = 0;
  124. if (ci.count == 1 || ci.count == -1)
  125. {
  126. side = side_equal(point, eq1 ? s1 : s2, ci);
  127. }
  128. else // count == 2 || count == -2
  129. {
  130. if (! s_antipodal)
  131. {
  132. // 1 left, -1 right
  133. side = m_side_strategy.apply(s1, s2, point);
  134. }
  135. else
  136. {
  137. calc_t const pi = constants::half_period();
  138. calc_t const s1_lat = get<1>(s1);
  139. calc_t const s2_lat = get<1>(s2);
  140. side = math::sign(ci.count)
  141. * (pi - s1_lat - s2_lat <= pi // segment goes through north pole
  142. ? -1 // going right all points will be on right side
  143. : 1); // going right all points will be on left side
  144. }
  145. }
  146. if (side == 0)
  147. {
  148. // Point is lying on segment
  149. state.m_touches = true;
  150. state.m_count = 0;
  151. return false;
  152. }
  153. // Side is NEG for right, POS for left.
  154. // The count is -2 for left, 2 for right (or -1/1)
  155. // Side positive thus means RIGHT and LEFTSIDE or LEFT and RIGHTSIDE
  156. // See accompagnying figure (TODO)
  157. if (side * ci.count > 0)
  158. {
  159. state.m_count += ci.count;
  160. }
  161. state.m_raw_count += ci.count;
  162. }
  163. else
  164. {
  165. // Count negated because the segment is on the other side of the globe
  166. // so it is reversed to match this side of the globe
  167. // Assuming geometry wraps around north pole, for segments on the other side of the globe
  168. // the point will always be RIGHT+RIGHTSIDE or LEFT+LEFTSIDE, so side*-count always < 0
  169. //state.m_count_n -= 0;
  170. // Assuming geometry wraps around south pole, for segments on the other side of the globe
  171. // the point will always be RIGHT+LEFTSIDE or LEFT+RIGHTSIDE, so side*-count always > 0
  172. state.m_count_s -= ci.count;
  173. state.m_raw_count_anti -= ci.count;
  174. }
  175. }
  176. return ! state.m_touches;
  177. }
  178. static inline int result(counter const& state)
  179. {
  180. return state.code();
  181. }
  182. protected:
  183. template <typename Point, typename PointOfSegment>
  184. static inline count_info check_segment(Point const& point,
  185. PointOfSegment const& seg1,
  186. PointOfSegment const& seg2,
  187. counter& state,
  188. bool& eq1, bool& eq2, bool& s_antipodal)
  189. {
  190. if (check_touch(point, seg1, seg2, state, eq1, eq2, s_antipodal))
  191. {
  192. return count_info(0, false);
  193. }
  194. return calculate_count(point, seg1, seg2, eq1, eq2, s_antipodal);
  195. }
  196. template <typename Point, typename PointOfSegment>
  197. static inline int check_touch(Point const& point,
  198. PointOfSegment const& seg1,
  199. PointOfSegment const& seg2,
  200. counter& state,
  201. bool& eq1,
  202. bool& eq2,
  203. bool& s_antipodal)
  204. {
  205. typedef typename calculation_type<Point, PointOfSegment>::type calc_t;
  206. typedef typename geometry::detail::cs_angular_units<Point>::type units_t;
  207. typedef math::detail::constants_on_spheroid<calc_t, units_t> constants;
  208. calc_t const c0 = 0;
  209. calc_t const c2 = 2;
  210. calc_t const pi = constants::half_period();
  211. calc_t const half_pi = pi / c2;
  212. calc_t const p_lon = get<0>(point);
  213. calc_t const s1_lon = get<0>(seg1);
  214. calc_t const s2_lon = get<0>(seg2);
  215. calc_t const p_lat = get<1>(point);
  216. calc_t const s1_lat = get<1>(seg1);
  217. calc_t const s2_lat = get<1>(seg2);
  218. // NOTE: lat in {-90, 90} and arbitrary lon
  219. // it doesn't matter what lon it is if it's a pole
  220. // so e.g. if one of the segment endpoints is a pole
  221. // then only the other lon matters
  222. bool eq1_strict = longitudes_equal<units_t>(s1_lon, p_lon);
  223. bool eq2_strict = longitudes_equal<units_t>(s2_lon, p_lon);
  224. bool eq1_anti = false;
  225. bool eq2_anti = false;
  226. calc_t const anti_p_lon = p_lon + (p_lon <= c0 ? pi : -pi);
  227. eq1 = eq1_strict // lon strictly equal to s1
  228. || (eq1_anti = longitudes_equal<units_t>(s1_lon, anti_p_lon)); // anti-lon strictly equal to s1
  229. eq2 = eq2_strict // lon strictly equal to s2
  230. || (eq2_anti = longitudes_equal<units_t>(s2_lon, anti_p_lon)); // anti-lon strictly equal to s2
  231. // segment overlapping pole
  232. calc_t const s_lon_diff = math::longitude_distance_signed<units_t>(s1_lon, s2_lon);
  233. s_antipodal = math::equals(s_lon_diff, pi);
  234. if (s_antipodal)
  235. {
  236. eq1 = eq2 = eq1 || eq2;
  237. // segment overlapping pole and point is pole
  238. if (math::equals(math::abs(p_lat), half_pi))
  239. {
  240. eq1 = eq2 = true;
  241. }
  242. }
  243. // check whether point is on a segment with a pole endpoint
  244. if (math::longitude_distance_signed<units_t>(s2_lon, p_lon) == c0)
  245. {
  246. bool const s1_north = math::equals(get<1>(seg1), half_pi);
  247. bool const s1_south = math::equals(get<1>(seg1), -half_pi);
  248. if (s1_north || s1_south)
  249. {
  250. state.m_touches = s1_south ? s2_lat > p_lat : s2_lat < p_lat;
  251. return state.m_touches;
  252. }
  253. }
  254. if (math::longitude_distance_signed<units_t>(s1_lon, p_lon) == c0)
  255. {
  256. bool const s2_north = math::equals(get<1>(seg2), half_pi);
  257. bool const s2_south = math::equals(get<1>(seg2), -half_pi);
  258. if (s2_north || s2_south)
  259. {
  260. state.m_touches = s2_south ? s1_lat > p_lat : s1_lat < p_lat;
  261. return state.m_touches;
  262. }
  263. }
  264. // Both equal p -> segment vertical
  265. // The only thing which has to be done is check if point is ON segment
  266. if (eq1 && eq2)
  267. {
  268. // segment endpoints on the same sides of the globe
  269. if (! s_antipodal)
  270. {
  271. // p's lat between segment endpoints' lats
  272. if ( (s1_lat <= p_lat && s2_lat >= p_lat) || (s2_lat <= p_lat && s1_lat >= p_lat) )
  273. {
  274. if (!eq1_anti || !eq2_anti)
  275. {
  276. state.m_touches = true;
  277. }
  278. }
  279. }
  280. else
  281. {
  282. // going through north or south pole?
  283. if (pi - s1_lat - s2_lat <= pi)
  284. {
  285. if ( (eq1_strict && s1_lat <= p_lat) || (eq2_strict && s2_lat <= p_lat) // north
  286. || math::equals(p_lat, half_pi) ) // point on north pole
  287. {
  288. state.m_touches = true;
  289. }
  290. else if (! eq1_strict && ! eq2_strict && math::equals(p_lat, -half_pi) ) // point on south pole
  291. {
  292. return false;
  293. }
  294. }
  295. else // south pole
  296. {
  297. if ( (eq1_strict && s1_lat >= p_lat) || (eq2_strict && s2_lat >= p_lat) // south
  298. || math::equals(p_lat, -half_pi) ) // point on south pole
  299. {
  300. state.m_touches = true;
  301. }
  302. else if (! eq1_strict && ! eq2_strict && math::equals(p_lat, half_pi) ) // point on north pole
  303. {
  304. return false;
  305. }
  306. }
  307. }
  308. return true;
  309. }
  310. return false;
  311. }
  312. // Called if point is not aligned with a vertical segment
  313. template <typename Point, typename PointOfSegment>
  314. static inline count_info calculate_count(Point const& point,
  315. PointOfSegment const& seg1,
  316. PointOfSegment const& seg2,
  317. bool eq1, bool eq2, bool s_antipodal)
  318. {
  319. typedef typename calculation_type<Point, PointOfSegment>::type calc_t;
  320. typedef typename geometry::detail::cs_angular_units<Point>::type units_t;
  321. typedef math::detail::constants_on_spheroid<calc_t, units_t> constants;
  322. // If both segment endpoints were poles below checks wouldn't be enough
  323. // but this means that either both are the same or that they are N/S poles
  324. // and therefore the segment is not valid.
  325. // If needed (eq1 && eq2 ? 0) could be returned
  326. calc_t const c0 = 0;
  327. calc_t const c2 = 2;
  328. calc_t const pi = constants::half_period();
  329. calc_t const half_pi = pi / c2;
  330. bool const s1_is_pole = math::equals(std::abs(get<1>(seg1)), half_pi);
  331. bool const s2_is_pole = math::equals(std::abs(get<1>(seg2)), half_pi);
  332. if (s1_is_pole && s2_is_pole)
  333. {
  334. return count_info(0, false);
  335. }
  336. calc_t const p = get<0>(point);
  337. calc_t const s1 = get<0>(seg1);
  338. calc_t const s2 = get<0>(seg2);
  339. calc_t const s1_p = math::longitude_distance_signed<units_t>(s1, p);
  340. if (s_antipodal)
  341. {
  342. return count_info(s1_p < c0 ? -2 : 2, false); // choose W/E
  343. }
  344. calc_t const s1_s2 = math::longitude_distance_signed<units_t>(s1, s2);
  345. if (eq1 || eq2) // Point on level s1 or s2
  346. {
  347. return count_info(s1_s2 < c0 ? -1 : 1, // choose W/E
  348. longitudes_equal<units_t>(p + pi, (eq1 ? s1 : s2)));
  349. }
  350. // Point between s1 and s2
  351. if ( math::sign(s1_p) == math::sign(s1_s2)
  352. && math::abs(s1_p) < math::abs(s1_s2) )
  353. {
  354. return count_info(s1_s2 < c0 ? -2 : 2, false); // choose W/E
  355. }
  356. calc_t const s1_p_anti = math::longitude_distance_signed<units_t>(s1, p + pi);
  357. // Anti-Point between s1 and s2
  358. if ( math::sign(s1_p_anti) == math::sign(s1_s2)
  359. && math::abs(s1_p_anti) < math::abs(s1_s2) )
  360. {
  361. return count_info(s1_s2 < c0 ? -2 : 2, true); // choose W/E
  362. }
  363. return count_info(0, false);
  364. }
  365. // Fix for https://svn.boost.org/trac/boost/ticket/9628
  366. // For floating point coordinates, the <D> coordinate of a point is compared
  367. // with the segment's points using some EPS. If the coordinates are "equal"
  368. // the sides are calculated. Therefore we can treat a segment as a long areal
  369. // geometry having some width. There is a small ~triangular area somewhere
  370. // between the segment's effective area and a segment's line used in sides
  371. // calculation where the segment is on the one side of the line but on the
  372. // other side of a segment (due to the width).
  373. // Below picture assuming D = 1, if D = 0 horiz<->vert, E<->N, RIGHT<->UP.
  374. // For the s1 of a segment going NE the real side is RIGHT but the point may
  375. // be detected as LEFT, like this:
  376. // RIGHT
  377. // ___----->
  378. // ^ O Pt __ __
  379. // EPS __ __
  380. // v__ __ BUT DETECTED AS LEFT OF THIS LINE
  381. // _____7
  382. // _____/
  383. // _____/
  384. // In the code below actually D = 0, so segments are nearly-vertical
  385. // Called when the point is on the same level as one of the segment's points
  386. // but the point is not aligned with a vertical segment
  387. template <typename Point, typename PointOfSegment>
  388. inline int side_equal(Point const& point,
  389. PointOfSegment const& se,
  390. count_info const& ci) const
  391. {
  392. typedef typename coordinate_type<PointOfSegment>::type scoord_t;
  393. typedef typename geometry::detail::cs_angular_units<Point>::type units_t;
  394. if (math::equals(get<1>(point), get<1>(se)))
  395. {
  396. return 0;
  397. }
  398. // Create a horizontal segment intersecting the original segment's endpoint
  399. // equal to the point, with the derived direction (E/W).
  400. PointOfSegment ss1, ss2;
  401. set<1>(ss1, get<1>(se));
  402. set<0>(ss1, get<0>(se));
  403. set<1>(ss2, get<1>(se));
  404. scoord_t ss20 = get<0>(se);
  405. if (ci.count > 0)
  406. {
  407. ss20 += small_angle<scoord_t, units_t>();
  408. }
  409. else
  410. {
  411. ss20 -= small_angle<scoord_t, units_t>();
  412. }
  413. math::normalize_longitude<units_t>(ss20);
  414. set<0>(ss2, ss20);
  415. // Check the side using this vertical segment
  416. return m_side_strategy.apply(ss1, ss2, point);
  417. }
  418. // 1 deg or pi/180 rad
  419. template <typename CalcT, typename Units>
  420. static inline CalcT small_angle()
  421. {
  422. typedef math::detail::constants_on_spheroid<CalcT, Units> constants;
  423. return constants::half_period() / CalcT(180);
  424. }
  425. template <typename Units, typename CalcT>
  426. static inline bool longitudes_equal(CalcT const& lon1, CalcT const& lon2)
  427. {
  428. return math::equals(
  429. math::longitude_distance_signed<Units>(lon1, lon2),
  430. CalcT(0));
  431. }
  432. SideStrategy m_side_strategy;
  433. };
  434. } // namespace detail
  435. #endif // DOXYGEN_NO_DETAIL
  436. /*!
  437. \brief Within detection using winding rule in spherical coordinate system.
  438. \ingroup strategies
  439. \tparam Point \tparam_point
  440. \tparam PointOfSegment \tparam_segment_point
  441. \tparam CalculationType \tparam_calculation
  442. \qbk{
  443. [heading See also]
  444. [link geometry.reference.algorithms.within.within_3_with_strategy within (with strategy)]
  445. }
  446. */
  447. template
  448. <
  449. typename Point = void, // for backward compatibility
  450. typename PointOfSegment = Point, // for backward compatibility
  451. typename CalculationType = void
  452. >
  453. class spherical_winding
  454. : public within::detail::spherical_winding_base
  455. <
  456. side::spherical_side_formula<CalculationType>,
  457. CalculationType
  458. >
  459. {};
  460. #ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS
  461. namespace services
  462. {
  463. template <typename PointLike, typename Geometry, typename AnyTag1, typename AnyTag2>
  464. struct default_strategy<PointLike, Geometry, AnyTag1, AnyTag2, pointlike_tag, polygonal_tag, spherical_tag, spherical_tag>
  465. {
  466. typedef within::spherical_winding<> type;
  467. };
  468. template <typename PointLike, typename Geometry, typename AnyTag1, typename AnyTag2>
  469. struct default_strategy<PointLike, Geometry, AnyTag1, AnyTag2, pointlike_tag, linear_tag, spherical_tag, spherical_tag>
  470. {
  471. typedef within::spherical_winding<> type;
  472. };
  473. } // namespace services
  474. #endif
  475. }} // namespace strategy::within
  476. #ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS
  477. namespace strategy { namespace covered_by { namespace services
  478. {
  479. template <typename PointLike, typename Geometry, typename AnyTag1, typename AnyTag2>
  480. struct default_strategy<PointLike, Geometry, AnyTag1, AnyTag2, pointlike_tag, polygonal_tag, spherical_tag, spherical_tag>
  481. {
  482. typedef within::spherical_winding<> type;
  483. };
  484. template <typename PointLike, typename Geometry, typename AnyTag1, typename AnyTag2>
  485. struct default_strategy<PointLike, Geometry, AnyTag1, AnyTag2, pointlike_tag, linear_tag, spherical_tag, spherical_tag>
  486. {
  487. typedef within::spherical_winding<> type;
  488. };
  489. }}} // namespace strategy::covered_by::services
  490. #endif
  491. }} // namespace boost::geometry
  492. #endif // BOOST_GEOMETRY_STRATEGY_SPHERICAL_POINT_IN_POLY_WINDING_HPP