follow.hpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527
  1. // Boost.Geometry (aka GGL, Generic Geometry Library)
  2. // Copyright (c) 2007-2014 Barend Gehrels, Amsterdam, the Netherlands.
  3. // Copyright (c) 2017 Adam Wulkiewicz, Lodz, Poland.
  4. // This file was modified by Oracle on 2014-2024.
  5. // Modifications copyright (c) 2014-2024 Oracle and/or its affiliates.
  6. // Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle
  7. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle
  8. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
  9. // Use, modification and distribution is subject to the Boost Software License,
  10. // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
  11. // http://www.boost.org/LICENSE_1_0.txt)
  12. #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_FOLLOW_HPP
  13. #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_FOLLOW_HPP
  14. #include <cstddef>
  15. #include <type_traits>
  16. #include <boost/range/begin.hpp>
  17. #include <boost/range/end.hpp>
  18. #include <boost/range/size.hpp>
  19. #include <boost/range/value_type.hpp>
  20. #include <boost/geometry/algorithms/clear.hpp>
  21. #include <boost/geometry/algorithms/detail/covered_by/implementation.hpp>
  22. #include <boost/geometry/algorithms/detail/overlay/append_no_duplicates.hpp>
  23. #include <boost/geometry/algorithms/detail/overlay/copy_segments.hpp>
  24. #include <boost/geometry/algorithms/detail/overlay/debug_traverse.hpp>
  25. #include <boost/geometry/algorithms/detail/overlay/turn_info.hpp>
  26. #include <boost/geometry/algorithms/detail/point_on_border.hpp>
  27. #include <boost/geometry/algorithms/detail/relate/turns.hpp>
  28. #include <boost/geometry/algorithms/detail/tupled_output.hpp>
  29. #include <boost/geometry/core/static_assert.hpp>
  30. #include <boost/geometry/util/condition.hpp>
  31. namespace boost { namespace geometry
  32. {
  33. #ifndef DOXYGEN_NO_DETAIL
  34. namespace detail { namespace overlay
  35. {
  36. namespace following
  37. {
  38. template <typename Turn, typename Operation>
  39. inline bool is_entering(Turn const& /* TODO remove this parameter */, Operation const& op)
  40. {
  41. // (Blocked means: blocked for polygon/polygon intersection, because
  42. // they are reversed. But for polygon/line it is similar to continue)
  43. return op.operation == operation_intersection
  44. || op.operation == operation_continue
  45. || op.operation == operation_blocked
  46. ;
  47. }
  48. template
  49. <
  50. typename Turn,
  51. typename Operation,
  52. typename LineString,
  53. typename Polygon,
  54. typename Strategy
  55. >
  56. inline bool last_covered_by(Turn const& /*turn*/, Operation const& op,
  57. LineString const& linestring, Polygon const& polygon,
  58. Strategy const& strategy)
  59. {
  60. return geometry::covered_by(range::at(linestring, op.seg_id.segment_index), polygon, strategy);
  61. }
  62. template
  63. <
  64. typename Turn,
  65. typename Operation,
  66. typename LineString,
  67. typename Polygon,
  68. typename Strategy
  69. >
  70. inline bool is_leaving(Turn const& turn, Operation const& op,
  71. bool entered, bool first,
  72. LineString const& linestring, Polygon const& polygon,
  73. Strategy const& strategy)
  74. {
  75. if (op.operation == operation_union)
  76. {
  77. return entered
  78. || turn.method == method_crosses
  79. || (first
  80. && op.position != position_front
  81. && last_covered_by(turn, op, linestring, polygon, strategy))
  82. ;
  83. }
  84. return false;
  85. }
  86. template
  87. <
  88. typename Turn,
  89. typename Operation,
  90. typename LineString,
  91. typename Polygon,
  92. typename Strategy
  93. >
  94. inline bool is_staying_inside(Turn const& turn, Operation const& op,
  95. bool entered, bool first,
  96. LineString const& linestring, Polygon const& polygon,
  97. Strategy const& strategy)
  98. {
  99. if (turn.method == method_crosses)
  100. {
  101. // The normal case, this is completely covered with entering/leaving
  102. // so stay out of this time consuming "covered_by"
  103. return false;
  104. }
  105. if (is_entering(turn, op))
  106. {
  107. return entered || (first && last_covered_by(turn, op, linestring, polygon, strategy));
  108. }
  109. return false;
  110. }
  111. template
  112. <
  113. typename Turn,
  114. typename Operation,
  115. typename Linestring,
  116. typename Polygon,
  117. typename Strategy
  118. >
  119. inline bool was_entered(Turn const& turn, Operation const& op, bool first,
  120. Linestring const& linestring, Polygon const& polygon,
  121. Strategy const& strategy)
  122. {
  123. if (first && (turn.method == method_collinear || turn.method == method_equal))
  124. {
  125. return last_covered_by(turn, op, linestring, polygon, strategy);
  126. }
  127. return false;
  128. }
  129. template
  130. <
  131. typename Turn,
  132. typename Operation
  133. >
  134. inline bool is_touching(Turn const& turn, Operation const& op,
  135. bool entered)
  136. {
  137. return (op.operation == operation_union || op.operation == operation_blocked)
  138. && (turn.method == method_touch || turn.method == method_touch_interior)
  139. && !entered
  140. && !op.is_collinear;
  141. }
  142. template
  143. <
  144. typename GeometryOut,
  145. typename Tag = geometry::tag_t<GeometryOut>
  146. >
  147. struct add_isolated_point
  148. {};
  149. template <typename LineStringOut>
  150. struct add_isolated_point<LineStringOut, linestring_tag>
  151. {
  152. template <typename Point, typename OutputIterator>
  153. static inline void apply(Point const& point, OutputIterator& out)
  154. {
  155. LineStringOut isolated_point_ls;
  156. geometry::append(isolated_point_ls, point);
  157. #ifndef BOOST_GEOMETRY_ALLOW_ONE_POINT_LINESTRINGS
  158. geometry::append(isolated_point_ls, point);
  159. #endif // BOOST_GEOMETRY_ALLOW_ONE_POINT_LINESTRINGS
  160. *out++ = isolated_point_ls;
  161. }
  162. };
  163. template <typename PointOut>
  164. struct add_isolated_point<PointOut, point_tag>
  165. {
  166. template <typename Point, typename OutputIterator>
  167. static inline void apply(Point const& point, OutputIterator& out)
  168. {
  169. PointOut isolated_point;
  170. geometry::detail::conversion::convert_point_to_point(point, isolated_point);
  171. *out++ = isolated_point;
  172. }
  173. };
  174. // Template specialization structure to call the right actions for the right type
  175. template <overlay_type OverlayType, bool RemoveSpikes = true>
  176. struct action_selector
  177. {
  178. BOOST_GEOMETRY_STATIC_ASSERT_FALSE(
  179. "If you get here the overlay type is not intersection or difference.",
  180. std::integral_constant<overlay_type, OverlayType>);
  181. };
  182. // Specialization for intersection, containing the implementation
  183. template <bool RemoveSpikes>
  184. struct action_selector<overlay_intersection, RemoveSpikes>
  185. {
  186. template
  187. <
  188. typename OutputIterator,
  189. typename LineStringOut,
  190. typename LineString,
  191. typename Point,
  192. typename Operation,
  193. typename Strategy
  194. >
  195. static inline void enter(LineStringOut& current_piece,
  196. LineString const& ,
  197. segment_identifier& segment_id,
  198. signed_size_type , Point const& point,
  199. Operation const& operation,
  200. Strategy const& strategy,
  201. OutputIterator& )
  202. {
  203. // On enter, append the intersection point and remember starting point
  204. // TODO: we don't check on spikes for linestrings (?). Consider this.
  205. detail::overlay::append_no_duplicates(current_piece, point, strategy);
  206. segment_id = operation.seg_id;
  207. }
  208. template
  209. <
  210. typename OutputIterator,
  211. typename LineStringOut,
  212. typename LineString,
  213. typename Point,
  214. typename Operation,
  215. typename Strategy
  216. >
  217. static inline void leave(LineStringOut& current_piece,
  218. LineString const& linestring,
  219. segment_identifier& segment_id,
  220. signed_size_type index, Point const& point,
  221. Operation const& ,
  222. Strategy const& strategy,
  223. OutputIterator& out)
  224. {
  225. // On leave, copy all segments from starting point, append the intersection point
  226. // and add the output piece
  227. detail::copy_segments::copy_segments_linestring
  228. <
  229. false, RemoveSpikes
  230. >::apply(linestring, segment_id, index, strategy, current_piece);
  231. detail::overlay::append_no_duplicates(current_piece, point, strategy);
  232. if (::boost::size(current_piece) > 1)
  233. {
  234. *out++ = current_piece;
  235. }
  236. geometry::clear(current_piece);
  237. }
  238. template
  239. <
  240. typename LineStringOrPointOut,
  241. typename Point,
  242. typename OutputIterator
  243. >
  244. static inline void isolated_point(Point const& point,
  245. OutputIterator& out)
  246. {
  247. add_isolated_point<LineStringOrPointOut>::apply(point, out);
  248. }
  249. static inline bool is_entered(bool entered)
  250. {
  251. return entered;
  252. }
  253. static inline bool included(int inside_value)
  254. {
  255. return inside_value >= 0; // covered_by
  256. }
  257. };
  258. // Specialization for difference, which reverses these actions
  259. template <bool RemoveSpikes>
  260. struct action_selector<overlay_difference, RemoveSpikes>
  261. {
  262. using normal_action = action_selector<overlay_intersection, RemoveSpikes>;
  263. template
  264. <
  265. typename OutputIterator,
  266. typename LineStringOut,
  267. typename LineString,
  268. typename Point,
  269. typename Operation,
  270. typename Strategy
  271. >
  272. static inline void enter(LineStringOut& current_piece,
  273. LineString const& linestring,
  274. segment_identifier& segment_id,
  275. signed_size_type index, Point const& point,
  276. Operation const& operation,
  277. Strategy const& strategy,
  278. OutputIterator& out)
  279. {
  280. normal_action::leave(current_piece, linestring, segment_id, index,
  281. point, operation, strategy, out);
  282. }
  283. template
  284. <
  285. typename OutputIterator,
  286. typename LineStringOut,
  287. typename LineString,
  288. typename Point,
  289. typename Operation,
  290. typename Strategy
  291. >
  292. static inline void leave(LineStringOut& current_piece,
  293. LineString const& linestring,
  294. segment_identifier& segment_id,
  295. signed_size_type index, Point const& point,
  296. Operation const& operation,
  297. Strategy const& strategy,
  298. OutputIterator& out)
  299. {
  300. normal_action::enter(current_piece, linestring, segment_id, index,
  301. point, operation, strategy, out);
  302. }
  303. template
  304. <
  305. typename LineStringOrPointOut,
  306. typename Point,
  307. typename OutputIterator
  308. >
  309. static inline void isolated_point(Point const&,
  310. OutputIterator const&)
  311. {
  312. }
  313. static inline bool is_entered(bool entered)
  314. {
  315. return ! normal_action::is_entered(entered);
  316. }
  317. static inline bool included(int inside_value)
  318. {
  319. return ! normal_action::included(inside_value);
  320. }
  321. };
  322. } // namespace following
  323. /*!
  324. \brief Follows a linestring from intersection point to intersection point, outputting which
  325. is inside, or outside, a ring or polygon
  326. \ingroup overlay
  327. */
  328. template
  329. <
  330. typename GeometryOut,
  331. typename LineString,
  332. typename Polygon,
  333. overlay_type OverlayType,
  334. bool RemoveSpikes,
  335. bool FollowIsolatedPoints
  336. >
  337. class follow
  338. {
  339. using linear = geometry::detail::output_geometry_access
  340. <
  341. GeometryOut, linestring_tag, linestring_tag
  342. >;
  343. using pointlike = geometry::detail::output_geometry_access
  344. <
  345. GeometryOut, point_tag, linestring_tag
  346. >;
  347. public :
  348. static inline bool included(int inside_value)
  349. {
  350. return following::action_selector
  351. <
  352. OverlayType, RemoveSpikes
  353. >::included(inside_value);
  354. }
  355. template
  356. <
  357. typename Turns,
  358. typename OutputIterator,
  359. typename Strategy
  360. >
  361. static inline OutputIterator apply(LineString const& linestring, Polygon const& polygon,
  362. detail::overlay::operation_type , // TODO: this parameter might be redundant
  363. Turns& turns,
  364. OutputIterator out,
  365. Strategy const& strategy)
  366. {
  367. using action = following::action_selector<OverlayType, RemoveSpikes>;
  368. // Sort intersection points on segments-along-linestring, and distance
  369. // (like in enrich is done for poly/poly)
  370. // sort turns by Linear seg_id, then by fraction, then
  371. // for same ring id: x, u, i, c
  372. // for different ring id: c, i, u, x
  373. using turn_less = relate::turns::less
  374. <
  375. 0, relate::turns::less_op_linear_areal_single<0>, Strategy
  376. >;
  377. std::sort(boost::begin(turns), boost::end(turns), turn_less());
  378. typename linear::type current_piece;
  379. geometry::segment_identifier current_segment_id(0, -1, -1, -1);
  380. // Iterate through all intersection points (they are ordered along the line)
  381. bool entered = false;
  382. bool first = true;
  383. for (auto const& turn : turns)
  384. {
  385. auto const& op = turn.operations[0];
  386. if (following::was_entered(turn, op, first, linestring, polygon, strategy))
  387. {
  388. debug_traverse(turn, op, "-> Was entered");
  389. entered = true;
  390. }
  391. if (following::is_staying_inside(turn, op, entered, first, linestring, polygon, strategy))
  392. {
  393. debug_traverse(turn, op, "-> Staying inside");
  394. entered = true;
  395. }
  396. else if (following::is_entering(turn, op))
  397. {
  398. debug_traverse(turn, op, "-> Entering");
  399. entered = true;
  400. action::enter(current_piece, linestring, current_segment_id,
  401. op.seg_id.segment_index, turn.point, op,
  402. strategy,
  403. linear::get(out));
  404. }
  405. else if (following::is_leaving(turn, op, entered, first, linestring, polygon, strategy))
  406. {
  407. debug_traverse(turn, op, "-> Leaving");
  408. entered = false;
  409. action::leave(current_piece, linestring, current_segment_id,
  410. op.seg_id.segment_index, turn.point, op,
  411. strategy,
  412. linear::get(out));
  413. }
  414. else if (BOOST_GEOMETRY_CONDITION(FollowIsolatedPoints)
  415. && following::is_touching(turn, op, entered))
  416. {
  417. debug_traverse(turn, op, "-> Isolated point");
  418. action::template isolated_point
  419. <
  420. typename pointlike::type
  421. >(turn.point, pointlike::get(out));
  422. }
  423. first = false;
  424. }
  425. if (action::is_entered(entered))
  426. {
  427. detail::copy_segments::copy_segments_linestring
  428. <
  429. false, RemoveSpikes
  430. >::apply(linestring,
  431. current_segment_id,
  432. static_cast<signed_size_type>(boost::size(linestring) - 1),
  433. strategy,
  434. current_piece);
  435. }
  436. // Output the last one, if applicable
  437. std::size_t current_piece_size = ::boost::size(current_piece);
  438. if (current_piece_size > 1)
  439. {
  440. *linear::get(out)++ = current_piece;
  441. }
  442. else if (BOOST_GEOMETRY_CONDITION(FollowIsolatedPoints)
  443. && current_piece_size == 1)
  444. {
  445. action::template isolated_point
  446. <
  447. typename pointlike::type
  448. >(range::front(current_piece), pointlike::get(out));
  449. }
  450. return out;
  451. }
  452. };
  453. }} // namespace detail::overlay
  454. #endif // DOXYGEN_NO_DETAIL
  455. }} // namespace boost::geometry
  456. #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_FOLLOW_HPP