follow.hpp 16 KB

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