follow.hpp 16 KB

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