• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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