1 // Boost.Geometry (aka GGL, Generic Geometry Library)
3 // Copyright (c) 2007-2014 Barend Gehrels, Amsterdam, the Netherlands.
5 // This file was modified by Oracle on 2014, 2017.
6 // Modifications copyright (c) 2014-2017 Oracle and/or its affiliates.
7 // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle
8 // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
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)
14 #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_FOLLOW_HPP
15 #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_FOLLOW_HPP
19 #include <boost/range.hpp>
20 #include <boost/mpl/assert.hpp>
22 #include <boost/geometry/algorithms/detail/point_on_border.hpp>
23 #include <boost/geometry/algorithms/detail/overlay/append_no_duplicates.hpp>
24 #include <boost/geometry/algorithms/detail/overlay/copy_segments.hpp>
25 #include <boost/geometry/algorithms/detail/overlay/turn_info.hpp>
27 #include <boost/geometry/algorithms/covered_by.hpp>
28 #include <boost/geometry/algorithms/clear.hpp>
29 #include <boost/geometry/algorithms/detail/relate/turns.hpp>
32 namespace boost { namespace geometry
36 #ifndef DOXYGEN_NO_DETAIL
37 namespace detail { namespace overlay
43 template <typename Turn, typename Operation>
44 static inline bool is_entering(Turn const& /* TODO remove this parameter */, Operation const& op)
46 // (Blocked means: blocked for polygon/polygon intersection, because
47 // they are reversed. But for polygon/line it is similar to continue)
48 return op.operation == operation_intersection
49 || op.operation == operation_continue
50 || op.operation == operation_blocked
60 typename PtInPolyStrategy
62 static inline bool last_covered_by(Turn const& turn, Operation const& op,
63 LineString const& linestring, Polygon const& polygon,
64 PtInPolyStrategy const& strategy)
66 return geometry::covered_by(range::at(linestring, op.seg_id.segment_index), polygon, strategy);
76 typename PtInPolyStrategy
78 static inline bool is_leaving(Turn const& turn, Operation const& op,
79 bool entered, bool first,
80 LineString const& linestring, Polygon const& polygon,
81 PtInPolyStrategy const& strategy)
83 if (op.operation == operation_union)
86 || turn.method == method_crosses
87 || (first && last_covered_by(turn, op, linestring, polygon, strategy))
100 typename PtInPolyStrategy
102 static inline bool is_staying_inside(Turn const& turn, Operation const& op,
103 bool entered, bool first,
104 LineString const& linestring, Polygon const& polygon,
105 PtInPolyStrategy const& strategy)
107 if (turn.method == method_crosses)
109 // The normal case, this is completely covered with entering/leaving
110 // so stay out of this time consuming "covered_by"
114 if (is_entering(turn, op))
116 return entered || (first && last_covered_by(turn, op, linestring, polygon, strategy));
128 typename PtInPolyStrategy
130 static inline bool was_entered(Turn const& turn, Operation const& op, bool first,
131 Linestring const& linestring, Polygon const& polygon,
132 PtInPolyStrategy const& strategy)
134 if (first && (turn.method == method_collinear || turn.method == method_equal))
136 return last_covered_by(turn, op, linestring, polygon, strategy);
142 // Template specialization structure to call the right actions for the right type
143 template <overlay_type OverlayType, bool RemoveSpikes = true>
144 struct action_selector
146 // If you get here the overlay type is not intersection or difference
147 // BOOST_MPL_ASSERT(false);
150 // Specialization for intersection, containing the implementation
151 template <bool RemoveSpikes>
152 struct action_selector<overlay_intersection, RemoveSpikes>
156 typename OutputIterator,
157 typename LineStringOut,
161 typename SideStrategy,
162 typename RobustPolicy
164 static inline void enter(LineStringOut& current_piece,
166 segment_identifier& segment_id,
167 signed_size_type , Point const& point,
168 Operation const& operation,
169 SideStrategy const& ,
170 RobustPolicy const& ,
173 // On enter, append the intersection point and remember starting point
174 // TODO: we don't check on spikes for linestrings (?). Consider this.
175 detail::overlay::append_no_duplicates(current_piece, point);
176 segment_id = operation.seg_id;
181 typename OutputIterator,
182 typename LineStringOut,
186 typename SideStrategy,
187 typename RobustPolicy
189 static inline void leave(LineStringOut& current_piece,
190 LineString const& linestring,
191 segment_identifier& segment_id,
192 signed_size_type index, Point const& point,
194 SideStrategy const& strategy,
195 RobustPolicy const& robust_policy,
198 // On leave, copy all segments from starting point, append the intersection point
199 // and add the output piece
200 detail::copy_segments::copy_segments_linestring
203 >::apply(linestring, segment_id, index, strategy, robust_policy, current_piece);
204 detail::overlay::append_no_duplicates(current_piece, point);
205 if (::boost::size(current_piece) > 1)
207 *out++ = current_piece;
210 geometry::clear(current_piece);
215 typename OutputIterator,
216 typename LineStringOut,
221 static inline void isolated_point(LineStringOut&,
224 signed_size_type, Point const& point,
225 Operation const& , OutputIterator& out)
227 LineStringOut isolated_point_ls;
228 geometry::append(isolated_point_ls, point);
230 #ifndef BOOST_GEOMETRY_ALLOW_ONE_POINT_LINESTRINGS
231 geometry::append(isolated_point_ls, point);
232 #endif // BOOST_GEOMETRY_ALLOW_ONE_POINT_LINESTRINGS
234 *out++ = isolated_point_ls;
237 static inline bool is_entered(bool entered)
242 static inline bool included(int inside_value)
244 return inside_value >= 0; // covered_by
249 // Specialization for difference, which reverses these actions
250 template <bool RemoveSpikes>
251 struct action_selector<overlay_difference, RemoveSpikes>
253 typedef action_selector<overlay_intersection, RemoveSpikes> normal_action;
257 typename OutputIterator,
258 typename LineStringOut,
262 typename SideStrategy,
263 typename RobustPolicy
265 static inline void enter(LineStringOut& current_piece,
266 LineString const& linestring,
267 segment_identifier& segment_id,
268 signed_size_type index, Point const& point,
269 Operation const& operation,
270 SideStrategy const& strategy,
271 RobustPolicy const& robust_policy,
274 normal_action::leave(current_piece, linestring, segment_id, index,
275 point, operation, strategy, robust_policy, out);
280 typename OutputIterator,
281 typename LineStringOut,
285 typename SideStrategy,
286 typename RobustPolicy
288 static inline void leave(LineStringOut& current_piece,
289 LineString const& linestring,
290 segment_identifier& segment_id,
291 signed_size_type index, Point const& point,
292 Operation const& operation,
293 SideStrategy const& strategy,
294 RobustPolicy const& robust_policy,
297 normal_action::enter(current_piece, linestring, segment_id, index,
298 point, operation, strategy, robust_policy, out);
303 typename OutputIterator,
304 typename LineStringOut,
309 static inline void isolated_point(LineStringOut&,
312 signed_size_type, Point const&,
313 Operation const&, OutputIterator&)
317 static inline bool is_entered(bool entered)
319 return ! normal_action::is_entered(entered);
322 static inline bool included(int inside_value)
324 return ! normal_action::included(inside_value);
332 \brief Follows a linestring from intersection point to intersection point, outputting which
333 is inside, or outside, a ring or polygon
338 typename LineStringOut,
341 overlay_type OverlayType,
342 bool RemoveSpikes = true
347 #ifdef BOOST_GEOMETRY_SETOPS_LA_OLD_BEHAVIOR
348 template <typename Turn>
349 struct sort_on_segment
351 // In case of turn point at the same location, we want to have continue/blocked LAST
352 // because that should be followed (intersection) or skipped (difference).
353 inline int operation_order(Turn const& turn) const
355 operation_type const& operation = turn.operations[0].operation;
358 case operation_opposite : return 0;
359 case operation_none : return 0;
360 case operation_union : return 1;
361 case operation_intersection : return 2;
362 case operation_blocked : return 3;
363 case operation_continue : return 4;
368 inline bool use_operation(Turn const& left, Turn const& right) const
370 // If they are the same, OK.
371 return operation_order(left) < operation_order(right);
374 inline bool use_distance(Turn const& left, Turn const& right) const
376 return left.operations[0].fraction == right.operations[0].fraction
377 ? use_operation(left, right)
378 : left.operations[0].fraction < right.operations[0].fraction
382 inline bool operator()(Turn const& left, Turn const& right) const
384 segment_identifier const& sl = left.operations[0].seg_id;
385 segment_identifier const& sr = right.operations[0].seg_id;
388 ? use_distance(left, right)
394 #endif // BOOST_GEOMETRY_SETOPS_LA_OLD_BEHAVIOR
399 static inline bool included(int inside_value)
401 return following::action_selector
403 OverlayType, RemoveSpikes
404 >::included(inside_value);
410 typename OutputIterator,
411 typename RobustPolicy,
414 static inline OutputIterator apply(LineString const& linestring, Polygon const& polygon,
415 detail::overlay::operation_type , // TODO: this parameter might be redundant
417 RobustPolicy const& robust_policy,
419 Strategy const& strategy)
421 typedef typename boost::range_iterator<Turns>::type turn_iterator;
422 typedef typename boost::range_value<Turns>::type turn_type;
423 typedef typename boost::range_iterator
425 typename turn_type::container_type
426 >::type turn_operation_iterator_type;
428 typedef following::action_selector<OverlayType, RemoveSpikes> action;
430 typename Strategy::template point_in_geometry_strategy
433 >::type const pt_in_poly_strategy
434 = strategy.template get_point_in_geometry_strategy<LineString, Polygon>();
436 // Sort intersection points on segments-along-linestring, and distance
437 // (like in enrich is done for poly/poly)
438 // sort turns by Linear seg_id, then by fraction, then
439 // for same ring id: x, u, i, c
440 // for different ring id: c, i, u, x
441 #ifdef BOOST_GEOMETRY_SETOPS_LA_OLD_BEHAVIOR
442 std::sort(boost::begin(turns), boost::end(turns), sort_on_segment<turn_type>());
444 typedef relate::turns::less<0, relate::turns::less_op_linear_areal_single<0> > turn_less;
445 std::sort(boost::begin(turns), boost::end(turns), turn_less());
448 LineStringOut current_piece;
449 geometry::segment_identifier current_segment_id(0, -1, -1, -1);
451 // Iterate through all intersection points (they are ordered along the line)
452 bool entered = false;
454 for (turn_iterator it = boost::begin(turns); it != boost::end(turns); ++it)
456 turn_operation_iterator_type iit = boost::begin(it->operations);
458 if (following::was_entered(*it, *iit, first, linestring, polygon, pt_in_poly_strategy))
460 debug_traverse(*it, *iit, "-> Was entered");
464 if (following::is_staying_inside(*it, *iit, entered, first, linestring, polygon, pt_in_poly_strategy))
466 debug_traverse(*it, *iit, "-> Staying inside");
470 else if (following::is_entering(*it, *iit))
472 debug_traverse(*it, *iit, "-> Entering");
475 action::enter(current_piece, linestring, current_segment_id,
476 iit->seg_id.segment_index, it->point, *iit,
477 strategy, robust_policy,
480 else if (following::is_leaving(*it, *iit, entered, first, linestring, polygon, pt_in_poly_strategy))
482 debug_traverse(*it, *iit, "-> Leaving");
485 action::leave(current_piece, linestring, current_segment_id,
486 iit->seg_id.segment_index, it->point, *iit,
487 strategy, robust_policy,
493 if (action::is_entered(entered))
495 detail::copy_segments::copy_segments_linestring
500 static_cast<signed_size_type>(boost::size(linestring) - 1),
501 strategy, robust_policy,
505 // Output the last one, if applicable
506 if (::boost::size(current_piece) > 1)
508 *out++ = current_piece;
516 }} // namespace detail::overlay
517 #endif // DOXYGEN_NO_DETAIL
520 }} // namespace boost::geometry
523 #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_FOLLOW_HPP