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