]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/boost/geometry/algorithms/detail/relate/linear_areal.hpp
update sources to v12.2.3
[ceph.git] / ceph / src / boost / boost / geometry / algorithms / detail / relate / linear_areal.hpp
1 // Boost.Geometry (aka GGL, Generic Geometry Library)
2
3 // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
4
5 // This file was modified by Oracle on 2013, 2014, 2015, 2017.
6 // Modifications copyright (c) 2013-2017 Oracle and/or its affiliates.
7
8 // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
9
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)
13
14 #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_LINEAR_AREAL_HPP
15 #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_LINEAR_AREAL_HPP
16
17 #include <boost/core/ignore_unused.hpp>
18 #include <boost/range/size.hpp>
19
20 #include <boost/geometry/core/assert.hpp>
21 #include <boost/geometry/core/topological_dimension.hpp>
22
23 #include <boost/geometry/util/condition.hpp>
24 #include <boost/geometry/util/range.hpp>
25
26 #include <boost/geometry/algorithms/num_interior_rings.hpp>
27 #include <boost/geometry/algorithms/detail/point_on_border.hpp>
28 #include <boost/geometry/algorithms/detail/sub_range.hpp>
29 #include <boost/geometry/algorithms/detail/single_geometry.hpp>
30
31 #include <boost/geometry/algorithms/detail/relate/point_geometry.hpp>
32 #include <boost/geometry/algorithms/detail/relate/turns.hpp>
33 #include <boost/geometry/algorithms/detail/relate/boundary_checker.hpp>
34 #include <boost/geometry/algorithms/detail/relate/follow_helpers.hpp>
35
36 #include <boost/geometry/views/detail/normalized_view.hpp>
37
38 namespace boost { namespace geometry
39 {
40
41 #ifndef DOXYGEN_NO_DETAIL
42 namespace detail { namespace relate {
43
44 // WARNING!
45 // TODO: In the worst case calling this Pred in a loop for MultiLinestring/MultiPolygon may take O(NM)
46 // Use the rtree in this case!
47
48 // may be used to set IE and BE for a Linear geometry for which no turns were generated
49 template
50 <
51 typename Geometry2,
52 typename Result,
53 typename PointInArealStrategy,
54 typename BoundaryChecker,
55 bool TransposeResult
56 >
57 class no_turns_la_linestring_pred
58 {
59 public:
60 no_turns_la_linestring_pred(Geometry2 const& geometry2,
61 Result & res,
62 PointInArealStrategy const& point_in_areal_strategy,
63 BoundaryChecker const& boundary_checker)
64 : m_geometry2(geometry2)
65 , m_result(res)
66 , m_point_in_areal_strategy(point_in_areal_strategy)
67 , m_boundary_checker(boundary_checker)
68 , m_interrupt_flags(0)
69 {
70 if ( ! may_update<interior, interior, '1', TransposeResult>(m_result) )
71 {
72 m_interrupt_flags |= 1;
73 }
74
75 if ( ! may_update<interior, exterior, '1', TransposeResult>(m_result) )
76 {
77 m_interrupt_flags |= 2;
78 }
79
80 if ( ! may_update<boundary, interior, '0', TransposeResult>(m_result) )
81 {
82 m_interrupt_flags |= 4;
83 }
84
85 if ( ! may_update<boundary, exterior, '0', TransposeResult>(m_result) )
86 {
87 m_interrupt_flags |= 8;
88 }
89 }
90
91 template <typename Linestring>
92 bool operator()(Linestring const& linestring)
93 {
94 std::size_t const count = boost::size(linestring);
95
96 // invalid input
97 if ( count < 2 )
98 {
99 // ignore
100 // TODO: throw an exception?
101 return true;
102 }
103
104 // if those flags are set nothing will change
105 if ( m_interrupt_flags == 0xF )
106 {
107 return false;
108 }
109
110 int const pig = detail::within::point_in_geometry(range::front(linestring),
111 m_geometry2,
112 m_point_in_areal_strategy);
113 //BOOST_GEOMETRY_ASSERT_MSG(pig != 0, "There should be no IPs");
114
115 if ( pig > 0 )
116 {
117 update<interior, interior, '1', TransposeResult>(m_result);
118 m_interrupt_flags |= 1;
119 }
120 else
121 {
122 update<interior, exterior, '1', TransposeResult>(m_result);
123 m_interrupt_flags |= 2;
124 }
125
126 // check if there is a boundary
127 if ( ( m_interrupt_flags & 0xC ) != 0xC // if wasn't already set
128 && ( m_boundary_checker.template
129 is_endpoint_boundary<boundary_front>(range::front(linestring))
130 || m_boundary_checker.template
131 is_endpoint_boundary<boundary_back>(range::back(linestring)) ) )
132 {
133 if ( pig > 0 )
134 {
135 update<boundary, interior, '0', TransposeResult>(m_result);
136 m_interrupt_flags |= 4;
137 }
138 else
139 {
140 update<boundary, exterior, '0', TransposeResult>(m_result);
141 m_interrupt_flags |= 8;
142 }
143 }
144
145 return m_interrupt_flags != 0xF
146 && ! m_result.interrupt;
147 }
148
149 private:
150 Geometry2 const& m_geometry2;
151 Result & m_result;
152 PointInArealStrategy const& m_point_in_areal_strategy;
153 BoundaryChecker const& m_boundary_checker;
154 unsigned m_interrupt_flags;
155 };
156
157 // may be used to set EI and EB for an Areal geometry for which no turns were generated
158 template <typename Result, bool TransposeResult>
159 class no_turns_la_areal_pred
160 {
161 public:
162 no_turns_la_areal_pred(Result & res)
163 : m_result(res)
164 , interrupt(! may_update<interior, exterior, '2', TransposeResult>(m_result)
165 && ! may_update<boundary, exterior, '1', TransposeResult>(m_result) )
166 {}
167
168 template <typename Areal>
169 bool operator()(Areal const& areal)
170 {
171 if ( interrupt )
172 {
173 return false;
174 }
175
176 // TODO:
177 // handle empty/invalid geometries in a different way than below?
178
179 typedef typename geometry::point_type<Areal>::type point_type;
180 point_type dummy;
181 bool const ok = boost::geometry::point_on_border(dummy, areal);
182
183 // TODO: for now ignore, later throw an exception?
184 if ( !ok )
185 {
186 return true;
187 }
188
189 update<interior, exterior, '2', TransposeResult>(m_result);
190 update<boundary, exterior, '1', TransposeResult>(m_result);
191
192 return false;
193 }
194
195 private:
196 Result & m_result;
197 bool const interrupt;
198 };
199
200 // The implementation of an algorithm calculating relate() for L/A
201 template <typename Geometry1, typename Geometry2, bool TransposeResult = false>
202 struct linear_areal
203 {
204 // check Linear / Areal
205 BOOST_STATIC_ASSERT(topological_dimension<Geometry1>::value == 1
206 && topological_dimension<Geometry2>::value == 2);
207
208 static const bool interruption_enabled = true;
209
210 typedef typename geometry::point_type<Geometry1>::type point1_type;
211 typedef typename geometry::point_type<Geometry2>::type point2_type;
212
213 template <typename Geometry>
214 struct is_multi
215 : boost::is_base_of
216 <
217 multi_tag,
218 typename tag<Geometry>::type
219 >
220 {};
221
222 template <typename Geom1, typename Geom2>
223 struct multi_turn_info
224 : turns::get_turns<Geom1, Geom2>::turn_info
225 {
226 multi_turn_info() : priority(0) {}
227 int priority; // single-geometry sorting priority
228 };
229
230 template <typename Geom1, typename Geom2>
231 struct turn_info_type
232 : boost::mpl::if_c
233 <
234 is_multi<Geometry2>::value,
235 multi_turn_info<Geom1, Geom2>,
236 typename turns::get_turns<Geom1, Geom2>::turn_info
237 >
238 {};
239
240 template <typename Result, typename IntersectionStrategy>
241 static inline void apply(Geometry1 const& geometry1, Geometry2 const& geometry2,
242 Result & result,
243 IntersectionStrategy const& intersection_strategy)
244 {
245 // TODO: If Areal geometry may have infinite size, change the following line:
246
247 // The result should be FFFFFFFFF
248 relate::set<exterior, exterior, result_dimension<Geometry2>::value, TransposeResult>(result);// FFFFFFFFd, d in [1,9] or T
249
250 if ( BOOST_GEOMETRY_CONDITION( result.interrupt ) )
251 return;
252
253 // get and analyse turns
254 typedef typename turn_info_type<Geometry1, Geometry2>::type turn_type;
255 std::vector<turn_type> turns;
256
257 interrupt_policy_linear_areal<Geometry2, Result> interrupt_policy(geometry2, result);
258
259 turns::get_turns<Geometry1, Geometry2>::apply(turns, geometry1, geometry2, interrupt_policy, intersection_strategy);
260 if ( BOOST_GEOMETRY_CONDITION( result.interrupt ) )
261 return;
262
263 typedef typename IntersectionStrategy::template point_in_geometry_strategy<Geometry1, Geometry2>::type within_strategy_type;
264 within_strategy_type const within_strategy = intersection_strategy.template get_point_in_geometry_strategy<Geometry1, Geometry2>();
265 boundary_checker<Geometry1> boundary_checker1(geometry1);
266 no_turns_la_linestring_pred
267 <
268 Geometry2,
269 Result,
270 within_strategy_type,
271 boundary_checker<Geometry1>,
272 TransposeResult
273 > pred1(geometry2,
274 result,
275 within_strategy,
276 boundary_checker1);
277 for_each_disjoint_geometry_if<0, Geometry1>::apply(turns.begin(), turns.end(), geometry1, pred1);
278 if ( BOOST_GEOMETRY_CONDITION( result.interrupt ) )
279 return;
280
281 no_turns_la_areal_pred<Result, !TransposeResult> pred2(result);
282 for_each_disjoint_geometry_if<1, Geometry2>::apply(turns.begin(), turns.end(), geometry2, pred2);
283 if ( BOOST_GEOMETRY_CONDITION( result.interrupt ) )
284 return;
285
286 if ( turns.empty() )
287 return;
288
289 // This is set here because in the case if empty Areal geometry were passed
290 // those shouldn't be set
291 relate::set<exterior, interior, '2', TransposeResult>(result);// FFFFFF2Fd
292 if ( BOOST_GEOMETRY_CONDITION( result.interrupt ) )
293 return;
294
295 {
296 sort_dispatch(turns.begin(), turns.end(), is_multi<Geometry2>());
297
298 turns_analyser<turn_type> analyser;
299 analyse_each_turn(result, analyser,
300 turns.begin(), turns.end(),
301 geometry1, geometry2,
302 boundary_checker1,
303 intersection_strategy.get_side_strategy());
304
305 if ( BOOST_GEOMETRY_CONDITION( result.interrupt ) )
306 return;
307 }
308
309 // If 'c' (insersection_boundary) was not found we know that any Ls isn't equal to one of the Rings
310 if ( !interrupt_policy.is_boundary_found )
311 {
312 relate::set<exterior, boundary, '1', TransposeResult>(result);
313 }
314 // Don't calculate it if it's required
315 else if ( may_update<exterior, boundary, '1', TransposeResult>(result) )
316 {
317 // TODO: REVISE THIS CODE AND PROBABLY REWRITE SOME PARTS TO BE MORE HUMAN-READABLE
318 // IN GENERAL IT ANALYSES THE RINGS OF AREAL GEOMETRY AND DETECTS THE ONES THAT
319 // MAY OVERLAP THE INTERIOR OF LINEAR GEOMETRY (NO IPs OR NON-FAKE 'u' OPERATION)
320 // NOTE: For one case std::sort may be called again to sort data by operations for data already sorted by ring index
321 // In the worst case scenario the complexity will be O( NlogN + R*(N/R)log(N/R) )
322 // So always should remain O(NlogN) -> for R==1 <-> 1(N/1)log(N/1), for R==N <-> N(N/N)log(N/N)
323 // Some benchmarking should probably be done to check if only one std::sort should be used
324
325 // sort by multi_index and rind_index
326 std::sort(turns.begin(), turns.end(), less_ring());
327
328 typedef typename std::vector<turn_type>::iterator turn_iterator;
329
330 turn_iterator it = turns.begin();
331 segment_identifier * prev_seg_id_ptr = NULL;
332 // for each ring
333 for ( ; it != turns.end() ; )
334 {
335 // it's the next single geometry
336 if ( prev_seg_id_ptr == NULL
337 || prev_seg_id_ptr->multi_index != it->operations[1].seg_id.multi_index )
338 {
339 // if the first ring has no IPs
340 if ( it->operations[1].seg_id.ring_index > -1 )
341 {
342 // we can be sure that the exterior overlaps the boundary
343 relate::set<exterior, boundary, '1', TransposeResult>(result);
344 break;
345 }
346 // if there was some previous ring
347 if ( prev_seg_id_ptr != NULL )
348 {
349 signed_size_type const next_ring_index = prev_seg_id_ptr->ring_index + 1;
350 BOOST_GEOMETRY_ASSERT(next_ring_index >= 0);
351
352 // if one of the last rings of previous single geometry was ommited
353 if ( static_cast<std::size_t>(next_ring_index)
354 < geometry::num_interior_rings(
355 single_geometry(geometry2, *prev_seg_id_ptr)) )
356 {
357 // we can be sure that the exterior overlaps the boundary
358 relate::set<exterior, boundary, '1', TransposeResult>(result);
359 break;
360 }
361 }
362 }
363 // if it's the same single geometry
364 else /*if ( previous_multi_index == it->operations[1].seg_id.multi_index )*/
365 {
366 // and we jumped over one of the rings
367 if ( prev_seg_id_ptr != NULL // just in case
368 && prev_seg_id_ptr->ring_index + 1 < it->operations[1].seg_id.ring_index )
369 {
370 // we can be sure that the exterior overlaps the boundary
371 relate::set<exterior, boundary, '1', TransposeResult>(result);
372 break;
373 }
374 }
375
376 prev_seg_id_ptr = boost::addressof(it->operations[1].seg_id);
377
378 // find the next ring first iterator and check if the analysis should be performed
379 has_boundary_intersection has_boundary_inters;
380 turn_iterator next = find_next_ring(it, turns.end(), has_boundary_inters);
381
382 // if there is no 1d overlap with the boundary
383 if ( !has_boundary_inters.result )
384 {
385 // we can be sure that the exterior overlaps the boundary
386 relate::set<exterior, boundary, '1', TransposeResult>(result);
387 break;
388 }
389 // else there is 1d overlap with the boundary so we must analyse the boundary
390 else
391 {
392 // u, c
393 typedef turns::less<1, turns::less_op_areal_linear<1> > less;
394 std::sort(it, next, less());
395
396 // analyse
397 areal_boundary_analyser<turn_type> analyser;
398 for ( turn_iterator rit = it ; rit != next ; ++rit )
399 {
400 // if the analyser requests, break the search
401 if ( !analyser.apply(it, rit, next) )
402 break;
403 }
404
405 // if the boundary of Areal goes out of the Linear
406 if ( analyser.is_union_detected )
407 {
408 // we can be sure that the boundary of Areal overlaps the exterior of Linear
409 relate::set<exterior, boundary, '1', TransposeResult>(result);
410 break;
411 }
412 }
413
414 it = next;
415 }
416
417 // if there was some previous ring
418 if ( prev_seg_id_ptr != NULL )
419 {
420 signed_size_type const next_ring_index = prev_seg_id_ptr->ring_index + 1;
421 BOOST_GEOMETRY_ASSERT(next_ring_index >= 0);
422
423 // if one of the last rings of previous single geometry was ommited
424 if ( static_cast<std::size_t>(next_ring_index)
425 < geometry::num_interior_rings(
426 single_geometry(geometry2, *prev_seg_id_ptr)) )
427 {
428 // we can be sure that the exterior overlaps the boundary
429 relate::set<exterior, boundary, '1', TransposeResult>(result);
430 }
431 }
432 }
433 }
434
435 template <typename It, typename Pred, typename Comp>
436 static void for_each_equal_range(It first, It last, Pred pred, Comp comp)
437 {
438 if ( first == last )
439 return;
440
441 It first_equal = first;
442 It prev = first;
443 for ( ++first ; ; ++first, ++prev )
444 {
445 if ( first == last || !comp(*prev, *first) )
446 {
447 pred(first_equal, first);
448 first_equal = first;
449 }
450
451 if ( first == last )
452 break;
453 }
454 }
455
456 struct same_ip
457 {
458 template <typename Turn>
459 bool operator()(Turn const& left, Turn const& right) const
460 {
461 return left.operations[0].seg_id == right.operations[0].seg_id
462 && left.operations[0].fraction == right.operations[0].fraction;
463 }
464 };
465
466 struct same_ip_and_multi_index
467 {
468 template <typename Turn>
469 bool operator()(Turn const& left, Turn const& right) const
470 {
471 return same_ip()(left, right)
472 && left.operations[1].seg_id.multi_index == right.operations[1].seg_id.multi_index;
473 }
474 };
475
476 template <typename OpToPriority>
477 struct set_turns_group_priority
478 {
479 template <typename TurnIt>
480 void operator()(TurnIt first, TurnIt last) const
481 {
482 BOOST_GEOMETRY_ASSERT(first != last);
483 static OpToPriority op_to_priority;
484 // find the operation with the least priority
485 int least_priority = op_to_priority(first->operations[0]);
486 for ( TurnIt it = first + 1 ; it != last ; ++it )
487 {
488 int priority = op_to_priority(it->operations[0]);
489 if ( priority < least_priority )
490 least_priority = priority;
491 }
492 // set the least priority for all turns of the group
493 for ( TurnIt it = first ; it != last ; ++it )
494 {
495 it->priority = least_priority;
496 }
497 }
498 };
499
500 template <typename SingleLess>
501 struct sort_turns_group
502 {
503 struct less
504 {
505 template <typename Turn>
506 bool operator()(Turn const& left, Turn const& right) const
507 {
508 return left.operations[1].seg_id.multi_index == right.operations[1].seg_id.multi_index ?
509 SingleLess()(left, right) :
510 left.priority < right.priority;
511 }
512 };
513
514 template <typename TurnIt>
515 void operator()(TurnIt first, TurnIt last) const
516 {
517 std::sort(first, last, less());
518 }
519 };
520
521 template <typename TurnIt>
522 static void sort_dispatch(TurnIt first, TurnIt last, boost::true_type const& /*is_multi*/)
523 {
524 // sort turns by Linear seg_id, then by fraction, then by other multi_index
525 typedef turns::less<0, turns::less_other_multi_index<0> > less;
526 std::sort(first, last, less());
527
528 // For the same IP and multi_index - the same other's single geometry
529 // set priorities as the least operation found for the whole single geometry
530 // so e.g. single geometries containing 'u' will always be before those only containing 'i'
531 typedef turns::op_to_int<0,2,3,1,4,0> op_to_int_xuic;
532 for_each_equal_range(first, last,
533 set_turns_group_priority<op_to_int_xuic>(), // least operation in xuic order
534 same_ip_and_multi_index()); // other's multi_index
535
536 // When priorities for single geometries are set now sort turns for the same IP
537 // if multi_index is the same sort them according to the single-less
538 // else use priority of the whole single-geometry set earlier
539 typedef turns::less<0, turns::less_op_linear_areal_single<0> > single_less;
540 for_each_equal_range(first, last,
541 sort_turns_group<single_less>(),
542 same_ip());
543 }
544
545 template <typename TurnIt>
546 static void sort_dispatch(TurnIt first, TurnIt last, boost::false_type const& /*is_multi*/)
547 {
548 // sort turns by Linear seg_id, then by fraction, then
549 // for same ring id: x, u, i, c
550 // for different ring id: c, i, u, x
551 typedef turns::less<0, turns::less_op_linear_areal_single<0> > less;
552 std::sort(first, last, less());
553 }
554
555
556 // interrupt policy which may be passed to get_turns to interrupt the analysis
557 // based on the info in the passed result/mask
558 template <typename Areal, typename Result>
559 class interrupt_policy_linear_areal
560 {
561 public:
562 static bool const enabled = true;
563
564 interrupt_policy_linear_areal(Areal const& areal, Result & result)
565 : m_result(result), m_areal(areal)
566 , is_boundary_found(false)
567 {}
568
569 // TODO: since we update result for some operations here, we may not do it in the analyser!
570
571 template <typename Range>
572 inline bool apply(Range const& turns)
573 {
574 typedef typename boost::range_iterator<Range const>::type iterator;
575
576 for (iterator it = boost::begin(turns) ; it != boost::end(turns) ; ++it)
577 {
578 if ( it->operations[0].operation == overlay::operation_intersection )
579 {
580 bool const no_interior_rings
581 = geometry::num_interior_rings(
582 single_geometry(m_areal, it->operations[1].seg_id)) == 0;
583
584 // WARNING! THIS IS TRUE ONLY IF THE POLYGON IS SIMPLE!
585 // OR WITHOUT INTERIOR RINGS (AND OF COURSE VALID)
586 if ( no_interior_rings )
587 update<interior, interior, '1', TransposeResult>(m_result);
588 }
589 else if ( it->operations[0].operation == overlay::operation_continue )
590 {
591 update<interior, boundary, '1', TransposeResult>(m_result);
592 is_boundary_found = true;
593 }
594 else if ( ( it->operations[0].operation == overlay::operation_union
595 || it->operations[0].operation == overlay::operation_blocked )
596 && it->operations[0].position == overlay::position_middle )
597 {
598 // TODO: here we could also check the boundaries and set BB at this point
599 update<interior, boundary, '0', TransposeResult>(m_result);
600 }
601 }
602
603 return m_result.interrupt;
604 }
605
606 private:
607 Result & m_result;
608 Areal const& m_areal;
609
610 public:
611 bool is_boundary_found;
612 };
613
614 // This analyser should be used like Input or SinglePass Iterator
615 // IMPORTANT! It should be called also for the end iterator - last
616 template <typename TurnInfo>
617 class turns_analyser
618 {
619 typedef typename TurnInfo::point_type turn_point_type;
620
621 static const std::size_t op_id = 0;
622 static const std::size_t other_op_id = 1;
623
624 public:
625 turns_analyser()
626 : m_previous_turn_ptr(NULL)
627 , m_previous_operation(overlay::operation_none)
628 , m_boundary_counter(0)
629 , m_interior_detected(false)
630 , m_first_interior_other_id_ptr(NULL)
631 , m_first_from_unknown(false)
632 , m_first_from_unknown_boundary_detected(false)
633 {}
634
635 template <typename Result,
636 typename TurnIt,
637 typename Geometry,
638 typename OtherGeometry,
639 typename BoundaryChecker,
640 typename SideStrategy>
641 void apply(Result & res, TurnIt it,
642 Geometry const& geometry,
643 OtherGeometry const& other_geometry,
644 BoundaryChecker const& boundary_checker,
645 SideStrategy const& side_strategy)
646 {
647 overlay::operation_type op = it->operations[op_id].operation;
648
649 if ( op != overlay::operation_union
650 && op != overlay::operation_intersection
651 && op != overlay::operation_blocked
652 && op != overlay::operation_continue ) // operation_boundary / operation_boundary_intersection
653 {
654 return;
655 }
656
657 segment_identifier const& seg_id = it->operations[op_id].seg_id;
658 segment_identifier const& other_id = it->operations[other_op_id].seg_id;
659
660 const bool first_in_range = m_seg_watcher.update(seg_id);
661
662 // TODO: should apply() for the post-last ip be called if first_in_range ?
663 // this would unify how last points in ranges are handled
664 // possibly replacing parts of the code below
665 // e.g. for is_multi and m_interior_detected
666
667 // handle possible exit
668 bool fake_enter_detected = false;
669 if ( m_exit_watcher.get_exit_operation() == overlay::operation_union )
670 {
671 // real exit point - may be multiple
672 // we know that we entered and now we exit
673 if ( ! turn_on_the_same_ip<op_id>(m_exit_watcher.get_exit_turn(), *it) )
674 {
675 m_exit_watcher.reset_detected_exit();
676
677 update<interior, exterior, '1', TransposeResult>(res);
678
679 // next single geometry
680 if ( first_in_range && m_previous_turn_ptr )
681 {
682 // NOTE: similar code is in the post-last-ip-apply()
683 segment_identifier const& prev_seg_id = m_previous_turn_ptr->operations[op_id].seg_id;
684
685 bool const prev_back_b = is_endpoint_on_boundary<boundary_back>(
686 range::back(sub_range(geometry, prev_seg_id)),
687 boundary_checker);
688
689 // if there is a boundary on the last point
690 if ( prev_back_b )
691 {
692 update<boundary, exterior, '0', TransposeResult>(res);
693 }
694 }
695 }
696 // fake exit point, reset state
697 else if ( op == overlay::operation_intersection
698 || op == overlay::operation_continue ) // operation_boundary
699 {
700 m_exit_watcher.reset_detected_exit();
701 fake_enter_detected = true;
702 }
703 }
704 else if ( m_exit_watcher.get_exit_operation() == overlay::operation_blocked )
705 {
706 // ignore multiple BLOCKs for this same single geometry1
707 if ( op == overlay::operation_blocked
708 && seg_id.multi_index == m_previous_turn_ptr->operations[op_id].seg_id.multi_index )
709 {
710 return;
711 }
712
713 if ( ( op == overlay::operation_intersection
714 || op == overlay::operation_continue )
715 && turn_on_the_same_ip<op_id>(m_exit_watcher.get_exit_turn(), *it) )
716 {
717 fake_enter_detected = true;
718 }
719
720 m_exit_watcher.reset_detected_exit();
721 }
722
723 if ( BOOST_GEOMETRY_CONDITION( is_multi<OtherGeometry>::value )
724 && m_first_from_unknown )
725 {
726 // For MultiPolygon many x/u operations may be generated as a first IP
727 // if for all turns x/u was generated and any of the Polygons doesn't contain the LineString
728 // then we know that the LineString is outside
729 // Similar with the u/u turns, if it was the first one it doesn't mean that the
730 // Linestring came from the exterior
731 if ( ( m_previous_operation == overlay::operation_blocked
732 && ( op != overlay::operation_blocked // operation different than block
733 || seg_id.multi_index != m_previous_turn_ptr->operations[op_id].seg_id.multi_index ) ) // or the next single-geometry
734 || ( m_previous_operation == overlay::operation_union
735 && ! turn_on_the_same_ip<op_id>(*m_previous_turn_ptr, *it) )
736 )
737 {
738 update<interior, exterior, '1', TransposeResult>(res);
739 if ( m_first_from_unknown_boundary_detected )
740 {
741 update<boundary, exterior, '0', TransposeResult>(res);
742 }
743
744 m_first_from_unknown = false;
745 m_first_from_unknown_boundary_detected = false;
746 }
747 }
748
749 // NOTE: THE WHOLE m_interior_detected HANDLING IS HERE BECAUSE WE CAN'T EFFICIENTLY SORT TURNS (CORRECTLY)
750 // BECAUSE THE SAME IP MAY BE REPRESENTED BY TWO SEGMENTS WITH DIFFERENT DISTANCES
751 // IT WOULD REQUIRE THE CALCULATION OF MAX DISTANCE
752 // TODO: WE COULD GET RID OF THE TEST IF THE DISTANCES WERE NORMALIZED
753
754 // UPDATE: THEY SHOULD BE NORMALIZED NOW
755
756 // TODO: THIS IS POTENTIALLY ERROREOUS!
757 // THIS ALGORITHM DEPENDS ON SOME SPECIFIC SEQUENCE OF OPERATIONS
758 // IT WOULD GIVE WRONG RESULTS E.G.
759 // IN THE CASE OF SELF-TOUCHING POINT WHEN 'i' WOULD BE BEFORE 'u'
760
761 // handle the interior overlap
762 if ( m_interior_detected )
763 {
764 BOOST_GEOMETRY_ASSERT_MSG(m_previous_turn_ptr, "non-NULL ptr expected");
765
766 // real interior overlap
767 if ( ! turn_on_the_same_ip<op_id>(*m_previous_turn_ptr, *it) )
768 {
769 update<interior, interior, '1', TransposeResult>(res);
770 m_interior_detected = false;
771
772 // new range detected - reset previous state and check the boundary
773 if ( first_in_range )
774 {
775 segment_identifier const& prev_seg_id = m_previous_turn_ptr->operations[op_id].seg_id;
776
777 bool const prev_back_b = is_endpoint_on_boundary<boundary_back>(
778 range::back(sub_range(geometry, prev_seg_id)),
779 boundary_checker);
780
781 // if there is a boundary on the last point
782 if ( prev_back_b )
783 {
784 update<boundary, interior, '0', TransposeResult>(res);
785 }
786
787 // The exit_watcher is reset below
788 // m_exit_watcher.reset();
789 }
790 }
791 // fake interior overlap
792 else if ( op == overlay::operation_continue )
793 {
794 m_interior_detected = false;
795 }
796 else if ( op == overlay::operation_union )
797 {
798 // TODO: this probably is not a good way of handling the interiors/enters
799 // the solution similar to exit_watcher would be more robust
800 // all enters should be kept and handled.
801 // maybe integrate it with the exit_watcher -> enter_exit_watcher
802 if ( m_first_interior_other_id_ptr
803 && m_first_interior_other_id_ptr->multi_index == other_id.multi_index )
804 {
805 m_interior_detected = false;
806 }
807 }
808 }
809
810 // NOTE: If post-last-ip apply() was called this wouldn't be needed
811 if ( first_in_range )
812 {
813 m_exit_watcher.reset();
814 m_boundary_counter = 0;
815 m_first_from_unknown = false;
816 m_first_from_unknown_boundary_detected = false;
817 }
818
819 // i/u, c/u
820 if ( op == overlay::operation_intersection
821 || op == overlay::operation_continue ) // operation_boundary/operation_boundary_intersection
822 {
823 bool const first_point = first_in_range || m_first_from_unknown;
824 bool no_enters_detected = m_exit_watcher.is_outside();
825 m_exit_watcher.enter(*it);
826
827 if ( op == overlay::operation_intersection )
828 {
829 if ( m_boundary_counter > 0 && it->operations[op_id].is_collinear )
830 --m_boundary_counter;
831
832 if ( m_boundary_counter == 0 )
833 {
834 // interiors overlaps
835 //update<interior, interior, '1', TransposeResult>(res);
836
837 // TODO: think about the implementation of the more robust version
838 // this way only the first enter will be handled
839 if ( !m_interior_detected )
840 {
841 // don't update now
842 // we might enter a boundary of some other ring on the same IP
843 m_interior_detected = true;
844 m_first_interior_other_id_ptr = boost::addressof(other_id);
845 }
846 }
847 }
848 else // operation_boundary
849 {
850 // don't add to the count for all met boundaries
851 // only if this is the "new" boundary
852 if ( first_point || !it->operations[op_id].is_collinear )
853 ++m_boundary_counter;
854
855 update<interior, boundary, '1', TransposeResult>(res);
856 }
857
858 bool const this_b
859 = is_ip_on_boundary<boundary_front>(it->point,
860 it->operations[op_id],
861 boundary_checker,
862 seg_id);
863 // going inside on boundary point
864 if ( this_b )
865 {
866 update<boundary, boundary, '0', TransposeResult>(res);
867 }
868 // going inside on non-boundary point
869 else
870 {
871 update<interior, boundary, '0', TransposeResult>(res);
872
873 // if we didn't enter in the past, we were outside
874 if ( no_enters_detected
875 && ! fake_enter_detected
876 && it->operations[op_id].position != overlay::position_front )
877 {
878 // TODO: calculate_from_inside() is only needed if the current Linestring is not closed
879 bool const from_inside = first_point
880 && calculate_from_inside(geometry,
881 other_geometry,
882 *it,
883 side_strategy);
884
885 if ( from_inside )
886 update<interior, interior, '1', TransposeResult>(res);
887 else
888 update<interior, exterior, '1', TransposeResult>(res);
889
890 // if it's the first IP then the first point is outside
891 if ( first_point )
892 {
893 bool const front_b = is_endpoint_on_boundary<boundary_front>(
894 range::front(sub_range(geometry, seg_id)),
895 boundary_checker);
896
897 // if there is a boundary on the first point
898 if ( front_b )
899 {
900 if ( from_inside )
901 update<boundary, interior, '0', TransposeResult>(res);
902 else
903 update<boundary, exterior, '0', TransposeResult>(res);
904 }
905 }
906 }
907 }
908
909 if ( BOOST_GEOMETRY_CONDITION( is_multi<OtherGeometry>::value ) )
910 {
911 m_first_from_unknown = false;
912 m_first_from_unknown_boundary_detected = false;
913 }
914 }
915 // u/u, x/u
916 else if ( op == overlay::operation_union || op == overlay::operation_blocked )
917 {
918 bool const op_blocked = op == overlay::operation_blocked;
919 bool const no_enters_detected = m_exit_watcher.is_outside()
920 // TODO: is this condition ok?
921 // TODO: move it into the exit_watcher?
922 && m_exit_watcher.get_exit_operation() == overlay::operation_none;
923
924 if ( op == overlay::operation_union )
925 {
926 if ( m_boundary_counter > 0 && it->operations[op_id].is_collinear )
927 --m_boundary_counter;
928 }
929 else // overlay::operation_blocked
930 {
931 m_boundary_counter = 0;
932 }
933
934 // we're inside, possibly going out right now
935 if ( ! no_enters_detected )
936 {
937 if ( op_blocked
938 && it->operations[op_id].position == overlay::position_back ) // ignore spikes!
939 {
940 // check if this is indeed the boundary point
941 // NOTE: is_ip_on_boundary<>() should be called here but the result will be the same
942 if ( is_endpoint_on_boundary<boundary_back>(it->point, boundary_checker) )
943 {
944 update<boundary, boundary, '0', TransposeResult>(res);
945 }
946 }
947 // union, inside, but no exit -> collinear on self-intersection point
948 // not needed since we're already inside the boundary
949 /*else if ( !exit_detected )
950 {
951 update<interior, boundary, '0', TransposeResult>(res);
952 }*/
953 }
954 // we're outside or inside and this is the first turn
955 else
956 {
957 bool const this_b = is_ip_on_boundary<boundary_any>(it->point,
958 it->operations[op_id],
959 boundary_checker,
960 seg_id);
961 // if current IP is on boundary of the geometry
962 if ( this_b )
963 {
964 update<boundary, boundary, '0', TransposeResult>(res);
965 }
966 // if current IP is not on boundary of the geometry
967 else
968 {
969 update<interior, boundary, '0', TransposeResult>(res);
970 }
971
972 // TODO: very similar code is used in the handling of intersection
973 if ( it->operations[op_id].position != overlay::position_front )
974 {
975 // TODO: calculate_from_inside() is only needed if the current Linestring is not closed
976 // NOTE: this is not enough for MultiPolygon and operation_blocked
977 // For LS/MultiPolygon multiple x/u turns may be generated
978 // the first checked Polygon may be the one which LS is outside for.
979 bool const first_point = first_in_range || m_first_from_unknown;
980 bool const first_from_inside = first_point
981 && calculate_from_inside(geometry,
982 other_geometry,
983 *it,
984 side_strategy);
985 if ( first_from_inside )
986 {
987 update<interior, interior, '1', TransposeResult>(res);
988
989 // notify the exit_watcher that we started inside
990 m_exit_watcher.enter(*it);
991 // and reset unknown flags since we know that we started inside
992 m_first_from_unknown = false;
993 m_first_from_unknown_boundary_detected = false;
994 }
995 else
996 {
997 if ( BOOST_GEOMETRY_CONDITION( is_multi<OtherGeometry>::value )
998 /*&& ( op == overlay::operation_blocked
999 || op == overlay::operation_union )*/ ) // if we're here it's u or x
1000 {
1001 m_first_from_unknown = true;
1002 }
1003 else
1004 {
1005 update<interior, exterior, '1', TransposeResult>(res);
1006 }
1007 }
1008
1009 // first IP on the last segment point - this means that the first point is outside or inside
1010 if ( first_point && ( !this_b || op_blocked ) )
1011 {
1012 bool const front_b = is_endpoint_on_boundary<boundary_front>(
1013 range::front(sub_range(geometry, seg_id)),
1014 boundary_checker);
1015
1016 // if there is a boundary on the first point
1017 if ( front_b )
1018 {
1019 if ( first_from_inside )
1020 {
1021 update<boundary, interior, '0', TransposeResult>(res);
1022 }
1023 else
1024 {
1025 if ( BOOST_GEOMETRY_CONDITION( is_multi<OtherGeometry>::value )
1026 /*&& ( op == overlay::operation_blocked
1027 || op == overlay::operation_union )*/ ) // if we're here it's u or x
1028 {
1029 BOOST_GEOMETRY_ASSERT(m_first_from_unknown);
1030 m_first_from_unknown_boundary_detected = true;
1031 }
1032 else
1033 {
1034 update<boundary, exterior, '0', TransposeResult>(res);
1035 }
1036 }
1037 }
1038 }
1039 }
1040 }
1041
1042 // if we're going along a boundary, we exit only if the linestring was collinear
1043 if ( m_boundary_counter == 0
1044 || it->operations[op_id].is_collinear )
1045 {
1046 // notify the exit watcher about the possible exit
1047 m_exit_watcher.exit(*it);
1048 }
1049 }
1050
1051 // store ref to previously analysed (valid) turn
1052 m_previous_turn_ptr = boost::addressof(*it);
1053 // and previously analysed (valid) operation
1054 m_previous_operation = op;
1055 }
1056
1057 // it == last
1058 template <typename Result,
1059 typename TurnIt,
1060 typename Geometry,
1061 typename OtherGeometry,
1062 typename BoundaryChecker>
1063 void apply(Result & res,
1064 TurnIt first, TurnIt last,
1065 Geometry const& geometry,
1066 OtherGeometry const& /*other_geometry*/,
1067 BoundaryChecker const& boundary_checker)
1068 {
1069 boost::ignore_unused(first, last);
1070 //BOOST_GEOMETRY_ASSERT( first != last );
1071
1072 // For MultiPolygon many x/u operations may be generated as a first IP
1073 // if for all turns x/u was generated and any of the Polygons doesn't contain the LineString
1074 // then we know that the LineString is outside
1075 if ( BOOST_GEOMETRY_CONDITION( is_multi<OtherGeometry>::value )
1076 && m_first_from_unknown )
1077 {
1078 update<interior, exterior, '1', TransposeResult>(res);
1079 if ( m_first_from_unknown_boundary_detected )
1080 {
1081 update<boundary, exterior, '0', TransposeResult>(res);
1082 }
1083
1084 // done below
1085 //m_first_from_unknown = false;
1086 //m_first_from_unknown_boundary_detected = false;
1087 }
1088
1089 // here, the possible exit is the real one
1090 // we know that we entered and now we exit
1091 if ( /*m_exit_watcher.get_exit_operation() == overlay::operation_union // THIS CHECK IS REDUNDANT
1092 ||*/ m_previous_operation == overlay::operation_union
1093 && !m_interior_detected )
1094 {
1095 // for sure
1096 update<interior, exterior, '1', TransposeResult>(res);
1097
1098 BOOST_GEOMETRY_ASSERT(first != last);
1099 BOOST_GEOMETRY_ASSERT(m_previous_turn_ptr);
1100
1101 segment_identifier const& prev_seg_id = m_previous_turn_ptr->operations[op_id].seg_id;
1102
1103 bool const prev_back_b = is_endpoint_on_boundary<boundary_back>(
1104 range::back(sub_range(geometry, prev_seg_id)),
1105 boundary_checker);
1106
1107 // if there is a boundary on the last point
1108 if ( prev_back_b )
1109 {
1110 update<boundary, exterior, '0', TransposeResult>(res);
1111 }
1112 }
1113 // we might enter some Areal and didn't go out,
1114 else if ( m_previous_operation == overlay::operation_intersection
1115 || m_interior_detected )
1116 {
1117 // just in case
1118 update<interior, interior, '1', TransposeResult>(res);
1119 m_interior_detected = false;
1120
1121 BOOST_GEOMETRY_ASSERT(first != last);
1122 BOOST_GEOMETRY_ASSERT(m_previous_turn_ptr);
1123
1124 segment_identifier const& prev_seg_id = m_previous_turn_ptr->operations[op_id].seg_id;
1125
1126 bool const prev_back_b = is_endpoint_on_boundary<boundary_back>(
1127 range::back(sub_range(geometry, prev_seg_id)),
1128 boundary_checker);
1129
1130 // if there is a boundary on the last point
1131 if ( prev_back_b )
1132 {
1133 update<boundary, interior, '0', TransposeResult>(res);
1134 }
1135 }
1136
1137 // This condition may be false if the Linestring is lying on the Polygon's collinear spike
1138 // if Polygon's spikes are not handled in get_turns() or relate() (they currently aren't)
1139 //BOOST_GEOMETRY_ASSERT_MSG(m_previous_operation != overlay::operation_continue,
1140 // "Unexpected operation! Probably the error in get_turns(L,A) or relate(L,A)");
1141 // Currently one c/c turn is generated for the exit
1142 // when a Linestring is going out on a collinear spike
1143 // When a Linestring is going in on a collinear spike
1144 // the turn is not generated for the entry
1145 // So assume it's the former
1146 if ( m_previous_operation == overlay::operation_continue )
1147 {
1148 update<interior, exterior, '1', TransposeResult>(res);
1149
1150 segment_identifier const& prev_seg_id = m_previous_turn_ptr->operations[op_id].seg_id;
1151
1152 bool const prev_back_b = is_endpoint_on_boundary<boundary_back>(
1153 range::back(sub_range(geometry, prev_seg_id)),
1154 boundary_checker);
1155
1156 // if there is a boundary on the last point
1157 if ( prev_back_b )
1158 {
1159 update<boundary, exterior, '0', TransposeResult>(res);
1160 }
1161 }
1162
1163 // Reset exit watcher before the analysis of the next Linestring
1164 m_exit_watcher.reset();
1165 m_boundary_counter = 0;
1166 m_first_from_unknown = false;
1167 m_first_from_unknown_boundary_detected = false;
1168 }
1169
1170 // check if the passed turn's segment of Linear geometry arrived
1171 // from the inside or the outside of the Areal geometry
1172 template <typename Turn, typename SideStrategy>
1173 static inline bool calculate_from_inside(Geometry1 const& geometry1,
1174 Geometry2 const& geometry2,
1175 Turn const& turn,
1176 SideStrategy const& side_strategy)
1177 {
1178 typedef typename cs_tag<typename Turn::point_type>::type cs_tag;
1179
1180 if ( turn.operations[op_id].position == overlay::position_front )
1181 return false;
1182
1183 typename sub_range_return_type<Geometry1 const>::type
1184 range1 = sub_range(geometry1, turn.operations[op_id].seg_id);
1185
1186 typedef detail::normalized_view<Geometry2 const> const range2_type;
1187 typedef typename boost::range_iterator<range2_type>::type range2_iterator;
1188 range2_type range2(sub_range(geometry2, turn.operations[other_op_id].seg_id));
1189
1190 BOOST_GEOMETRY_ASSERT(boost::size(range1));
1191 std::size_t const s2 = boost::size(range2);
1192 BOOST_GEOMETRY_ASSERT(s2 > 2);
1193 std::size_t const seg_count2 = s2 - 1;
1194
1195 std::size_t const p_seg_ij = static_cast<std::size_t>(turn.operations[op_id].seg_id.segment_index);
1196 std::size_t const q_seg_ij = static_cast<std::size_t>(turn.operations[other_op_id].seg_id.segment_index);
1197
1198 BOOST_GEOMETRY_ASSERT(p_seg_ij + 1 < boost::size(range1));
1199 BOOST_GEOMETRY_ASSERT(q_seg_ij + 1 < s2);
1200
1201 point1_type const& pi = range::at(range1, p_seg_ij);
1202 point2_type const& qi = range::at(range2, q_seg_ij);
1203 point2_type const& qj = range::at(range2, q_seg_ij + 1);
1204 point1_type qi_conv;
1205 geometry::convert(qi, qi_conv);
1206 bool const is_ip_qj = equals::equals_point_point(turn.point, qj);
1207 // TODO: test this!
1208 // BOOST_GEOMETRY_ASSERT(!equals::equals_point_point(turn.point, pi));
1209 // BOOST_GEOMETRY_ASSERT(!equals::equals_point_point(turn.point, qi));
1210 point1_type new_pj;
1211 geometry::convert(turn.point, new_pj);
1212
1213 if ( is_ip_qj )
1214 {
1215 std::size_t const q_seg_jk = (q_seg_ij + 1) % seg_count2;
1216 // TODO: the following function should return immediately, however the worst case complexity is O(N)
1217 // It would be good to replace it with some O(1) mechanism
1218 range2_iterator qk_it = find_next_non_duplicated(boost::begin(range2),
1219 range::pos(range2, q_seg_jk),
1220 boost::end(range2));
1221
1222 // Will this sequence of points be always correct?
1223 overlay::side_calculator<cs_tag, point1_type, point2_type, SideStrategy>
1224 side_calc(qi_conv, new_pj, pi, qi, qj, *qk_it, side_strategy);
1225
1226 return calculate_from_inside_sides(side_calc);
1227 }
1228 else
1229 {
1230 point2_type new_qj;
1231 geometry::convert(turn.point, new_qj);
1232
1233 overlay::side_calculator<cs_tag, point1_type, point2_type, SideStrategy>
1234 side_calc(qi_conv, new_pj, pi, qi, new_qj, qj, side_strategy);
1235
1236 return calculate_from_inside_sides(side_calc);
1237 }
1238 }
1239
1240 template <typename It>
1241 static inline It find_next_non_duplicated(It first, It current, It last)
1242 {
1243 BOOST_GEOMETRY_ASSERT( current != last );
1244
1245 It it = current;
1246
1247 for ( ++it ; it != last ; ++it )
1248 {
1249 if ( !equals::equals_point_point(*current, *it) )
1250 return it;
1251 }
1252
1253 // if not found start from the beginning
1254 for ( it = first ; it != current ; ++it )
1255 {
1256 if ( !equals::equals_point_point(*current, *it) )
1257 return it;
1258 }
1259
1260 return current;
1261 }
1262
1263 // calculate inside or outside based on side_calc
1264 // this is simplified version of a check from equal<>
1265 template <typename SideCalc>
1266 static inline bool calculate_from_inside_sides(SideCalc const& side_calc)
1267 {
1268 int const side_pk_p = side_calc.pk_wrt_p1();
1269 int const side_qk_p = side_calc.qk_wrt_p1();
1270 // If they turn to same side (not opposite sides)
1271 if (! overlay::base_turn_handler::opposite(side_pk_p, side_qk_p))
1272 {
1273 int const side_pk_q2 = side_calc.pk_wrt_q2();
1274 return side_pk_q2 == -1;
1275 }
1276 else
1277 {
1278 return side_pk_p == -1;
1279 }
1280 }
1281
1282 private:
1283 exit_watcher<TurnInfo, op_id> m_exit_watcher;
1284 segment_watcher<same_single> m_seg_watcher;
1285 TurnInfo * m_previous_turn_ptr;
1286 overlay::operation_type m_previous_operation;
1287 unsigned m_boundary_counter;
1288 bool m_interior_detected;
1289 const segment_identifier * m_first_interior_other_id_ptr;
1290 bool m_first_from_unknown;
1291 bool m_first_from_unknown_boundary_detected;
1292 };
1293
1294 // call analyser.apply() for each turn in range
1295 // IMPORTANT! The analyser is also called for the end iterator - last
1296 template <typename Result,
1297 typename TurnIt,
1298 typename Analyser,
1299 typename Geometry,
1300 typename OtherGeometry,
1301 typename BoundaryChecker,
1302 typename SideStrategy>
1303 static inline void analyse_each_turn(Result & res,
1304 Analyser & analyser,
1305 TurnIt first, TurnIt last,
1306 Geometry const& geometry,
1307 OtherGeometry const& other_geometry,
1308 BoundaryChecker const& boundary_checker,
1309 SideStrategy const& side_strategy)
1310 {
1311 if ( first == last )
1312 return;
1313
1314 for ( TurnIt it = first ; it != last ; ++it )
1315 {
1316 analyser.apply(res, it,
1317 geometry, other_geometry,
1318 boundary_checker,
1319 side_strategy);
1320
1321 if ( BOOST_GEOMETRY_CONDITION( res.interrupt ) )
1322 return;
1323 }
1324
1325 analyser.apply(res, first, last,
1326 geometry, other_geometry,
1327 boundary_checker);
1328 }
1329
1330 // less comparator comparing multi_index then ring_index
1331 // may be used to sort turns by ring
1332 struct less_ring
1333 {
1334 template <typename Turn>
1335 inline bool operator()(Turn const& left, Turn const& right) const
1336 {
1337 return left.operations[1].seg_id.multi_index < right.operations[1].seg_id.multi_index
1338 || ( left.operations[1].seg_id.multi_index == right.operations[1].seg_id.multi_index
1339 && left.operations[1].seg_id.ring_index < right.operations[1].seg_id.ring_index );
1340 }
1341 };
1342
1343 // policy/functor checking if a turn's operation is operation_continue
1344 struct has_boundary_intersection
1345 {
1346 has_boundary_intersection()
1347 : result(false) {}
1348
1349 template <typename Turn>
1350 inline void operator()(Turn const& turn)
1351 {
1352 if ( turn.operations[1].operation == overlay::operation_continue )
1353 result = true;
1354 }
1355
1356 bool result;
1357 };
1358
1359 // iterate through the range and search for the different multi_index or ring_index
1360 // also call fun for each turn in the current range
1361 template <typename It, typename Fun>
1362 static inline It find_next_ring(It first, It last, Fun & fun)
1363 {
1364 if ( first == last )
1365 return last;
1366
1367 signed_size_type const multi_index = first->operations[1].seg_id.multi_index;
1368 signed_size_type const ring_index = first->operations[1].seg_id.ring_index;
1369
1370 fun(*first);
1371 ++first;
1372
1373 for ( ; first != last ; ++first )
1374 {
1375 if ( multi_index != first->operations[1].seg_id.multi_index
1376 || ring_index != first->operations[1].seg_id.ring_index )
1377 {
1378 return first;
1379 }
1380
1381 fun(*first);
1382 }
1383
1384 return last;
1385 }
1386
1387 // analyser which called for turns sorted by seg/distance/operation
1388 // checks if the boundary of Areal geometry really got out
1389 // into the exterior of Linear geometry
1390 template <typename TurnInfo>
1391 class areal_boundary_analyser
1392 {
1393 public:
1394 areal_boundary_analyser()
1395 : is_union_detected(false)
1396 , m_previous_turn_ptr(NULL)
1397 {}
1398
1399 template <typename TurnIt>
1400 bool apply(TurnIt /*first*/, TurnIt it, TurnIt last)
1401 {
1402 overlay::operation_type op = it->operations[1].operation;
1403
1404 if ( it != last )
1405 {
1406 if ( op != overlay::operation_union
1407 && op != overlay::operation_continue )
1408 {
1409 return true;
1410 }
1411
1412 if ( is_union_detected )
1413 {
1414 BOOST_GEOMETRY_ASSERT(m_previous_turn_ptr != NULL);
1415 if ( !detail::equals::equals_point_point(it->point, m_previous_turn_ptr->point) )
1416 {
1417 // break
1418 return false;
1419 }
1420 else if ( op == overlay::operation_continue ) // operation_boundary
1421 {
1422 is_union_detected = false;
1423 }
1424 }
1425
1426 if ( op == overlay::operation_union )
1427 {
1428 is_union_detected = true;
1429 m_previous_turn_ptr = boost::addressof(*it);
1430 }
1431
1432 return true;
1433 }
1434 else
1435 {
1436 return false;
1437 }
1438 }
1439
1440 bool is_union_detected;
1441
1442 private:
1443 const TurnInfo * m_previous_turn_ptr;
1444 };
1445 };
1446
1447 template <typename Geometry1, typename Geometry2>
1448 struct areal_linear
1449 {
1450 typedef linear_areal<Geometry2, Geometry1, true> linear_areal_type;
1451
1452 static const bool interruption_enabled = linear_areal_type::interruption_enabled;
1453
1454 template <typename Result, typename IntersectionStrategy>
1455 static inline void apply(Geometry1 const& geometry1, Geometry2 const& geometry2,
1456 Result & result,
1457 IntersectionStrategy const& intersection_strategy)
1458 {
1459 linear_areal_type::apply(geometry2, geometry1, result, intersection_strategy);
1460 }
1461 };
1462
1463 }} // namespace detail::relate
1464 #endif // DOXYGEN_NO_DETAIL
1465
1466 }} // namespace boost::geometry
1467
1468 #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_LINEAR_AREAL_HPP