1 // Boost.Geometry (aka GGL, Generic Geometry Library)
3 // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
5 // This file was modified by Oracle on 2013, 2014, 2015, 2017, 2018, 2019.
6 // Modifications copyright (c) 2013-2019 Oracle and/or its affiliates.
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_RELATE_LINEAR_AREAL_HPP
15 #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_LINEAR_AREAL_HPP
17 #include <boost/core/ignore_unused.hpp>
18 #include <boost/range/size.hpp>
20 #include <boost/geometry/core/assert.hpp>
21 #include <boost/geometry/core/topological_dimension.hpp>
23 #include <boost/geometry/util/condition.hpp>
24 #include <boost/geometry/util/range.hpp>
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>
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>
36 #include <boost/geometry/views/detail/normalized_view.hpp>
38 namespace boost { namespace geometry
41 #ifndef DOXYGEN_NO_DETAIL
42 namespace detail { namespace relate {
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!
48 // may be used to set IE and BE for a Linear geometry for which no turns were generated
53 typename PointInArealStrategy,
54 typename BoundaryChecker,
57 class no_turns_la_linestring_pred
60 no_turns_la_linestring_pred(Geometry2 const& geometry2,
62 PointInArealStrategy const& point_in_areal_strategy,
63 BoundaryChecker const& boundary_checker)
64 : m_geometry2(geometry2)
66 , m_point_in_areal_strategy(point_in_areal_strategy)
67 , m_boundary_checker(boundary_checker)
68 , m_interrupt_flags(0)
70 if ( ! may_update<interior, interior, '1', TransposeResult>(m_result) )
72 m_interrupt_flags |= 1;
75 if ( ! may_update<interior, exterior, '1', TransposeResult>(m_result) )
77 m_interrupt_flags |= 2;
80 if ( ! may_update<boundary, interior, '0', TransposeResult>(m_result) )
82 m_interrupt_flags |= 4;
85 if ( ! may_update<boundary, exterior, '0', TransposeResult>(m_result) )
87 m_interrupt_flags |= 8;
91 template <typename Linestring>
92 bool operator()(Linestring const& linestring)
94 std::size_t const count = boost::size(linestring);
100 // TODO: throw an exception?
104 // if those flags are set nothing will change
105 if ( m_interrupt_flags == 0xF )
110 int const pig = detail::within::point_in_geometry(range::front(linestring),
112 m_point_in_areal_strategy);
113 //BOOST_GEOMETRY_ASSERT_MSG(pig != 0, "There should be no IPs");
117 update<interior, interior, '1', TransposeResult>(m_result);
118 m_interrupt_flags |= 1;
122 update<interior, exterior, '1', TransposeResult>(m_result);
123 m_interrupt_flags |= 2;
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)) ) )
135 update<boundary, interior, '0', TransposeResult>(m_result);
136 m_interrupt_flags |= 4;
140 update<boundary, exterior, '0', TransposeResult>(m_result);
141 m_interrupt_flags |= 8;
145 return m_interrupt_flags != 0xF
146 && ! m_result.interrupt;
150 Geometry2 const& m_geometry2;
152 PointInArealStrategy const& m_point_in_areal_strategy;
153 BoundaryChecker const& m_boundary_checker;
154 unsigned m_interrupt_flags;
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
162 no_turns_la_areal_pred(Result & res)
164 , interrupt(! may_update<interior, exterior, '2', TransposeResult>(m_result)
165 && ! may_update<boundary, exterior, '1', TransposeResult>(m_result) )
168 template <typename Areal>
169 bool operator()(Areal const& areal)
177 // handle empty/invalid geometries in a different way than below?
179 typedef typename geometry::point_type<Areal>::type point_type;
181 bool const ok = boost::geometry::point_on_border(dummy, areal);
183 // TODO: for now ignore, later throw an exception?
189 update<interior, exterior, '2', TransposeResult>(m_result);
190 update<boundary, exterior, '1', TransposeResult>(m_result);
197 bool const interrupt;
200 // The implementation of an algorithm calculating relate() for L/A
201 template <typename Geometry1, typename Geometry2, bool TransposeResult = false>
204 // check Linear / Areal
205 BOOST_STATIC_ASSERT(topological_dimension<Geometry1>::value == 1
206 && topological_dimension<Geometry2>::value == 2);
208 static const bool interruption_enabled = true;
210 typedef typename geometry::point_type<Geometry1>::type point1_type;
211 typedef typename geometry::point_type<Geometry2>::type point2_type;
213 template <typename Geometry>
218 typename tag<Geometry>::type
222 template <typename Geom1, typename Geom2, typename Strategy>
223 struct multi_turn_info
224 : turns::get_turns<Geom1, Geom2>::template turn_info_type<Strategy>::type
226 multi_turn_info() : priority(0) {}
227 int priority; // single-geometry sorting priority
230 template <typename Geom1, typename Geom2, typename Strategy>
231 struct turn_info_type
234 is_multi<Geometry2>::value,
235 multi_turn_info<Geom1, Geom2, Strategy>,
236 typename turns::get_turns<Geom1, Geom2>::template turn_info_type<Strategy>::type
240 template <typename Result, typename IntersectionStrategy>
241 static inline void apply(Geometry1 const& geometry1, Geometry2 const& geometry2,
243 IntersectionStrategy const& intersection_strategy)
245 // TODO: If Areal geometry may have infinite size, change the following line:
247 // The result should be FFFFFFFFF
248 relate::set<exterior, exterior, result_dimension<Geometry2>::value, TransposeResult>(result);// FFFFFFFFd, d in [1,9] or T
250 if ( BOOST_GEOMETRY_CONDITION( result.interrupt ) )
253 // get and analyse turns
254 typedef typename turn_info_type<Geometry1, Geometry2, IntersectionStrategy>::type turn_type;
255 std::vector<turn_type> turns;
257 interrupt_policy_linear_areal<Geometry2, Result> interrupt_policy(geometry2, result);
259 turns::get_turns<Geometry1, Geometry2>::apply(turns, geometry1, geometry2, interrupt_policy, intersection_strategy);
260 if ( BOOST_GEOMETRY_CONDITION( result.interrupt ) )
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>();
266 typedef typename IntersectionStrategy::cs_tag cs_tag;
267 typedef typename within_strategy_type::equals_point_point_strategy_type eq_pp_strategy_type;
269 typedef boundary_checker
273 > boundary_checker1_type;
274 boundary_checker1_type boundary_checker1(geometry1);
276 no_turns_la_linestring_pred
280 within_strategy_type,
281 boundary_checker1_type,
287 for_each_disjoint_geometry_if<0, Geometry1>::apply(turns.begin(), turns.end(), geometry1, pred1);
288 if ( BOOST_GEOMETRY_CONDITION( result.interrupt ) )
291 no_turns_la_areal_pred<Result, !TransposeResult> pred2(result);
292 for_each_disjoint_geometry_if<1, Geometry2>::apply(turns.begin(), turns.end(), geometry2, pred2);
293 if ( BOOST_GEOMETRY_CONDITION( result.interrupt ) )
299 // This is set here because in the case if empty Areal geometry were passed
300 // those shouldn't be set
301 relate::set<exterior, interior, '2', TransposeResult>(result);// FFFFFF2Fd
302 if ( BOOST_GEOMETRY_CONDITION( result.interrupt ) )
306 sort_dispatch<cs_tag>(turns.begin(), turns.end(), is_multi<Geometry2>());
308 turns_analyser<turn_type> analyser;
309 analyse_each_turn(result, analyser,
310 turns.begin(), turns.end(),
311 geometry1, geometry2,
313 intersection_strategy.get_side_strategy());
315 if ( BOOST_GEOMETRY_CONDITION( result.interrupt ) )
319 // If 'c' (insersection_boundary) was not found we know that any Ls isn't equal to one of the Rings
320 if ( !interrupt_policy.is_boundary_found )
322 relate::set<exterior, boundary, '1', TransposeResult>(result);
324 // Don't calculate it if it's required
325 else if ( may_update<exterior, boundary, '1', TransposeResult>(result) )
327 // TODO: REVISE THIS CODE AND PROBABLY REWRITE SOME PARTS TO BE MORE HUMAN-READABLE
328 // IN GENERAL IT ANALYSES THE RINGS OF AREAL GEOMETRY AND DETECTS THE ONES THAT
329 // MAY OVERLAP THE INTERIOR OF LINEAR GEOMETRY (NO IPs OR NON-FAKE 'u' OPERATION)
330 // NOTE: For one case std::sort may be called again to sort data by operations for data already sorted by ring index
331 // In the worst case scenario the complexity will be O( NlogN + R*(N/R)log(N/R) )
332 // So always should remain O(NlogN) -> for R==1 <-> 1(N/1)log(N/1), for R==N <-> N(N/N)log(N/N)
333 // Some benchmarking should probably be done to check if only one std::sort should be used
335 // sort by multi_index and rind_index
336 std::sort(turns.begin(), turns.end(), less_ring());
338 typedef typename std::vector<turn_type>::iterator turn_iterator;
340 turn_iterator it = turns.begin();
341 segment_identifier * prev_seg_id_ptr = NULL;
343 for ( ; it != turns.end() ; )
345 // it's the next single geometry
346 if ( prev_seg_id_ptr == NULL
347 || prev_seg_id_ptr->multi_index != it->operations[1].seg_id.multi_index )
349 // if the first ring has no IPs
350 if ( it->operations[1].seg_id.ring_index > -1 )
352 // we can be sure that the exterior overlaps the boundary
353 relate::set<exterior, boundary, '1', TransposeResult>(result);
356 // if there was some previous ring
357 if ( prev_seg_id_ptr != NULL )
359 signed_size_type const next_ring_index = prev_seg_id_ptr->ring_index + 1;
360 BOOST_GEOMETRY_ASSERT(next_ring_index >= 0);
362 // if one of the last rings of previous single geometry was ommited
363 if ( static_cast<std::size_t>(next_ring_index)
364 < geometry::num_interior_rings(
365 single_geometry(geometry2, *prev_seg_id_ptr)) )
367 // we can be sure that the exterior overlaps the boundary
368 relate::set<exterior, boundary, '1', TransposeResult>(result);
373 // if it's the same single geometry
374 else /*if ( previous_multi_index == it->operations[1].seg_id.multi_index )*/
376 // and we jumped over one of the rings
377 if ( prev_seg_id_ptr != NULL // just in case
378 && prev_seg_id_ptr->ring_index + 1 < it->operations[1].seg_id.ring_index )
380 // we can be sure that the exterior overlaps the boundary
381 relate::set<exterior, boundary, '1', TransposeResult>(result);
386 prev_seg_id_ptr = boost::addressof(it->operations[1].seg_id);
388 // find the next ring first iterator and check if the analysis should be performed
389 has_boundary_intersection has_boundary_inters;
390 turn_iterator next = find_next_ring(it, turns.end(), has_boundary_inters);
392 // if there is no 1d overlap with the boundary
393 if ( !has_boundary_inters.result )
395 // we can be sure that the exterior overlaps the boundary
396 relate::set<exterior, boundary, '1', TransposeResult>(result);
399 // else there is 1d overlap with the boundary so we must analyse the boundary
403 typedef turns::less<1, turns::less_op_areal_linear<1>, cs_tag> less;
404 std::sort(it, next, less());
406 eq_pp_strategy_type const eq_pp_strategy = within_strategy.get_equals_point_point_strategy();
409 areal_boundary_analyser<turn_type> analyser;
410 for ( turn_iterator rit = it ; rit != next ; ++rit )
412 // if the analyser requests, break the search
413 if ( !analyser.apply(it, rit, next, eq_pp_strategy) )
417 // if the boundary of Areal goes out of the Linear
418 if ( analyser.is_union_detected )
420 // we can be sure that the boundary of Areal overlaps the exterior of Linear
421 relate::set<exterior, boundary, '1', TransposeResult>(result);
429 // if there was some previous ring
430 if ( prev_seg_id_ptr != NULL )
432 signed_size_type const next_ring_index = prev_seg_id_ptr->ring_index + 1;
433 BOOST_GEOMETRY_ASSERT(next_ring_index >= 0);
435 // if one of the last rings of previous single geometry was ommited
436 if ( static_cast<std::size_t>(next_ring_index)
437 < geometry::num_interior_rings(
438 single_geometry(geometry2, *prev_seg_id_ptr)) )
440 // we can be sure that the exterior overlaps the boundary
441 relate::set<exterior, boundary, '1', TransposeResult>(result);
447 template <typename It, typename Pred, typename Comp>
448 static void for_each_equal_range(It first, It last, Pred pred, Comp comp)
453 It first_equal = first;
455 for ( ++first ; ; ++first, ++prev )
457 if ( first == last || !comp(*prev, *first) )
459 pred(first_equal, first);
470 template <typename Turn>
471 bool operator()(Turn const& left, Turn const& right) const
473 return left.operations[0].seg_id == right.operations[0].seg_id
474 && left.operations[0].fraction == right.operations[0].fraction;
478 struct same_ip_and_multi_index
480 template <typename Turn>
481 bool operator()(Turn const& left, Turn const& right) const
483 return same_ip()(left, right)
484 && left.operations[1].seg_id.multi_index == right.operations[1].seg_id.multi_index;
488 template <typename OpToPriority>
489 struct set_turns_group_priority
491 template <typename TurnIt>
492 void operator()(TurnIt first, TurnIt last) const
494 BOOST_GEOMETRY_ASSERT(first != last);
495 static OpToPriority op_to_priority;
496 // find the operation with the least priority
497 int least_priority = op_to_priority(first->operations[0]);
498 for ( TurnIt it = first + 1 ; it != last ; ++it )
500 int priority = op_to_priority(it->operations[0]);
501 if ( priority < least_priority )
502 least_priority = priority;
504 // set the least priority for all turns of the group
505 for ( TurnIt it = first ; it != last ; ++it )
507 it->priority = least_priority;
512 template <typename SingleLess>
513 struct sort_turns_group
517 template <typename Turn>
518 bool operator()(Turn const& left, Turn const& right) const
520 return left.operations[1].seg_id.multi_index == right.operations[1].seg_id.multi_index ?
521 SingleLess()(left, right) :
522 left.priority < right.priority;
526 template <typename TurnIt>
527 void operator()(TurnIt first, TurnIt last) const
529 std::sort(first, last, less());
533 template <typename CSTag, typename TurnIt>
534 static void sort_dispatch(TurnIt first, TurnIt last, boost::true_type const& /*is_multi*/)
536 // sort turns by Linear seg_id, then by fraction, then by other multi_index
537 typedef turns::less<0, turns::less_other_multi_index<0>, CSTag> less;
538 std::sort(first, last, less());
540 // For the same IP and multi_index - the same other's single geometry
541 // set priorities as the least operation found for the whole single geometry
542 // so e.g. single geometries containing 'u' will always be before those only containing 'i'
543 typedef turns::op_to_int<0,2,3,1,4,0> op_to_int_xuic;
544 for_each_equal_range(first, last,
545 set_turns_group_priority<op_to_int_xuic>(), // least operation in xuic order
546 same_ip_and_multi_index()); // other's multi_index
548 // When priorities for single geometries are set now sort turns for the same IP
549 // if multi_index is the same sort them according to the single-less
550 // else use priority of the whole single-geometry set earlier
551 typedef turns::less<0, turns::less_op_linear_areal_single<0>, CSTag> single_less;
552 for_each_equal_range(first, last,
553 sort_turns_group<single_less>(),
557 template <typename CSTag, typename TurnIt>
558 static void sort_dispatch(TurnIt first, TurnIt last, boost::false_type const& /*is_multi*/)
560 // sort turns by Linear seg_id, then by fraction, then
561 // for same ring id: x, u, i, c
562 // for different ring id: c, i, u, x
563 typedef turns::less<0, turns::less_op_linear_areal_single<0>, CSTag> less;
564 std::sort(first, last, less());
568 // interrupt policy which may be passed to get_turns to interrupt the analysis
569 // based on the info in the passed result/mask
570 template <typename Areal, typename Result>
571 class interrupt_policy_linear_areal
574 static bool const enabled = true;
576 interrupt_policy_linear_areal(Areal const& areal, Result & result)
577 : m_result(result), m_areal(areal)
578 , is_boundary_found(false)
581 // TODO: since we update result for some operations here, we may not do it in the analyser!
583 template <typename Range>
584 inline bool apply(Range const& turns)
586 typedef typename boost::range_iterator<Range const>::type iterator;
588 for (iterator it = boost::begin(turns) ; it != boost::end(turns) ; ++it)
590 if ( it->operations[0].operation == overlay::operation_intersection )
592 bool const no_interior_rings
593 = geometry::num_interior_rings(
594 single_geometry(m_areal, it->operations[1].seg_id)) == 0;
596 // WARNING! THIS IS TRUE ONLY IF THE POLYGON IS SIMPLE!
597 // OR WITHOUT INTERIOR RINGS (AND OF COURSE VALID)
598 if ( no_interior_rings )
599 update<interior, interior, '1', TransposeResult>(m_result);
601 else if ( it->operations[0].operation == overlay::operation_continue )
603 update<interior, boundary, '1', TransposeResult>(m_result);
604 is_boundary_found = true;
606 else if ( ( it->operations[0].operation == overlay::operation_union
607 || it->operations[0].operation == overlay::operation_blocked )
608 && it->operations[0].position == overlay::position_middle )
610 // TODO: here we could also check the boundaries and set BB at this point
611 update<interior, boundary, '0', TransposeResult>(m_result);
615 return m_result.interrupt;
620 Areal const& m_areal;
623 bool is_boundary_found;
626 // This analyser should be used like Input or SinglePass Iterator
627 // IMPORTANT! It should be called also for the end iterator - last
628 template <typename TurnInfo>
631 typedef typename TurnInfo::point_type turn_point_type;
633 static const std::size_t op_id = 0;
634 static const std::size_t other_op_id = 1;
636 template <typename TurnPointCSTag, typename PointP, typename PointQ,
637 typename SideStrategy,
638 typename Pi = PointP, typename Pj = PointP, typename Pk = PointP,
639 typename Qi = PointQ, typename Qj = PointQ, typename Qk = PointQ
641 struct la_side_calculator
643 inline la_side_calculator(Pi const& pi, Pj const& pj, Pk const& pk,
644 Qi const& qi, Qj const& qj, Qk const& qk,
645 SideStrategy const& side_strategy)
646 : m_pi(pi), m_pj(pj), m_pk(pk)
647 , m_qi(qi), m_qj(qj), m_qk(qk)
648 , m_side_strategy(side_strategy)
651 inline int pk_wrt_p1() const { return m_side_strategy.apply(m_pi, m_pj, m_pk); }
652 inline int qk_wrt_p1() const { return m_side_strategy.apply(m_pi, m_pj, m_qk); }
653 inline int pk_wrt_q2() const { return m_side_strategy.apply(m_qj, m_qk, m_pk); }
663 SideStrategy m_side_strategy;
669 : m_previous_turn_ptr(NULL)
670 , m_previous_operation(overlay::operation_none)
671 , m_boundary_counter(0)
672 , m_interior_detected(false)
673 , m_first_interior_other_id_ptr(NULL)
674 , m_first_from_unknown(false)
675 , m_first_from_unknown_boundary_detected(false)
678 template <typename Result,
681 typename OtherGeometry,
682 typename BoundaryChecker,
683 typename SideStrategy>
684 void apply(Result & res, TurnIt it,
685 Geometry const& geometry,
686 OtherGeometry const& other_geometry,
687 BoundaryChecker const& boundary_checker,
688 SideStrategy const& side_strategy)
690 overlay::operation_type op = it->operations[op_id].operation;
692 if ( op != overlay::operation_union
693 && op != overlay::operation_intersection
694 && op != overlay::operation_blocked
695 && op != overlay::operation_continue ) // operation_boundary / operation_boundary_intersection
700 segment_identifier const& seg_id = it->operations[op_id].seg_id;
701 segment_identifier const& other_id = it->operations[other_op_id].seg_id;
703 const bool first_in_range = m_seg_watcher.update(seg_id);
705 // TODO: should apply() for the post-last ip be called if first_in_range ?
706 // this would unify how last points in ranges are handled
707 // possibly replacing parts of the code below
708 // e.g. for is_multi and m_interior_detected
710 // handle possible exit
711 bool fake_enter_detected = false;
712 if ( m_exit_watcher.get_exit_operation() == overlay::operation_union )
714 // real exit point - may be multiple
715 // we know that we entered and now we exit
716 if ( ! turn_on_the_same_ip<op_id>(m_exit_watcher.get_exit_turn(), *it,
717 side_strategy.get_equals_point_point_strategy()) )
719 m_exit_watcher.reset_detected_exit();
721 update<interior, exterior, '1', TransposeResult>(res);
723 // next single geometry
724 if ( first_in_range && m_previous_turn_ptr )
726 // NOTE: similar code is in the post-last-ip-apply()
727 segment_identifier const& prev_seg_id = m_previous_turn_ptr->operations[op_id].seg_id;
729 bool const prev_back_b = is_endpoint_on_boundary<boundary_back>(
730 range::back(sub_range(geometry, prev_seg_id)),
733 // if there is a boundary on the last point
736 update<boundary, exterior, '0', TransposeResult>(res);
740 // fake exit point, reset state
741 else if ( op == overlay::operation_intersection
742 || op == overlay::operation_continue ) // operation_boundary
744 m_exit_watcher.reset_detected_exit();
745 fake_enter_detected = true;
748 else if ( m_exit_watcher.get_exit_operation() == overlay::operation_blocked )
750 // ignore multiple BLOCKs for this same single geometry1
751 if ( op == overlay::operation_blocked
752 && seg_id.multi_index == m_previous_turn_ptr->operations[op_id].seg_id.multi_index )
757 if ( ( op == overlay::operation_intersection
758 || op == overlay::operation_continue )
759 && turn_on_the_same_ip<op_id>(m_exit_watcher.get_exit_turn(), *it,
760 side_strategy.get_equals_point_point_strategy()) )
762 fake_enter_detected = true;
765 m_exit_watcher.reset_detected_exit();
768 if ( BOOST_GEOMETRY_CONDITION( is_multi<OtherGeometry>::value )
769 && m_first_from_unknown )
771 // For MultiPolygon many x/u operations may be generated as a first IP
772 // if for all turns x/u was generated and any of the Polygons doesn't contain the LineString
773 // then we know that the LineString is outside
774 // Similar with the u/u turns, if it was the first one it doesn't mean that the
775 // Linestring came from the exterior
776 if ( ( m_previous_operation == overlay::operation_blocked
777 && ( op != overlay::operation_blocked // operation different than block
778 || seg_id.multi_index != m_previous_turn_ptr->operations[op_id].seg_id.multi_index ) ) // or the next single-geometry
779 || ( m_previous_operation == overlay::operation_union
780 && ! turn_on_the_same_ip<op_id>(*m_previous_turn_ptr, *it,
781 side_strategy.get_equals_point_point_strategy()) )
784 update<interior, exterior, '1', TransposeResult>(res);
785 if ( m_first_from_unknown_boundary_detected )
787 update<boundary, exterior, '0', TransposeResult>(res);
790 m_first_from_unknown = false;
791 m_first_from_unknown_boundary_detected = false;
795 // NOTE: THE WHOLE m_interior_detected HANDLING IS HERE BECAUSE WE CAN'T EFFICIENTLY SORT TURNS (CORRECTLY)
796 // BECAUSE THE SAME IP MAY BE REPRESENTED BY TWO SEGMENTS WITH DIFFERENT DISTANCES
797 // IT WOULD REQUIRE THE CALCULATION OF MAX DISTANCE
798 // TODO: WE COULD GET RID OF THE TEST IF THE DISTANCES WERE NORMALIZED
800 // UPDATE: THEY SHOULD BE NORMALIZED NOW
802 // TODO: THIS IS POTENTIALLY ERROREOUS!
803 // THIS ALGORITHM DEPENDS ON SOME SPECIFIC SEQUENCE OF OPERATIONS
804 // IT WOULD GIVE WRONG RESULTS E.G.
805 // IN THE CASE OF SELF-TOUCHING POINT WHEN 'i' WOULD BE BEFORE 'u'
807 // handle the interior overlap
808 if ( m_interior_detected )
810 BOOST_GEOMETRY_ASSERT_MSG(m_previous_turn_ptr, "non-NULL ptr expected");
812 // real interior overlap
813 if ( ! turn_on_the_same_ip<op_id>(*m_previous_turn_ptr, *it,
814 side_strategy.get_equals_point_point_strategy()) )
816 update<interior, interior, '1', TransposeResult>(res);
817 m_interior_detected = false;
819 // new range detected - reset previous state and check the boundary
820 if ( first_in_range )
822 segment_identifier const& prev_seg_id = m_previous_turn_ptr->operations[op_id].seg_id;
824 bool const prev_back_b = is_endpoint_on_boundary<boundary_back>(
825 range::back(sub_range(geometry, prev_seg_id)),
828 // if there is a boundary on the last point
831 update<boundary, interior, '0', TransposeResult>(res);
834 // The exit_watcher is reset below
835 // m_exit_watcher.reset();
838 // fake interior overlap
839 else if ( op == overlay::operation_continue )
841 m_interior_detected = false;
843 else if ( op == overlay::operation_union )
845 // TODO: this probably is not a good way of handling the interiors/enters
846 // the solution similar to exit_watcher would be more robust
847 // all enters should be kept and handled.
848 // maybe integrate it with the exit_watcher -> enter_exit_watcher
849 if ( m_first_interior_other_id_ptr
850 && m_first_interior_other_id_ptr->multi_index == other_id.multi_index )
852 m_interior_detected = false;
857 // NOTE: If post-last-ip apply() was called this wouldn't be needed
858 if ( first_in_range )
860 m_exit_watcher.reset();
861 m_boundary_counter = 0;
862 m_first_from_unknown = false;
863 m_first_from_unknown_boundary_detected = false;
867 if ( op == overlay::operation_intersection
868 || op == overlay::operation_continue ) // operation_boundary/operation_boundary_intersection
870 bool const first_point = first_in_range || m_first_from_unknown;
871 bool no_enters_detected = m_exit_watcher.is_outside();
872 m_exit_watcher.enter(*it);
874 if ( op == overlay::operation_intersection )
876 if ( m_boundary_counter > 0 && it->operations[op_id].is_collinear )
877 --m_boundary_counter;
879 if ( m_boundary_counter == 0 )
881 // interiors overlaps
882 //update<interior, interior, '1', TransposeResult>(res);
884 // TODO: think about the implementation of the more robust version
885 // this way only the first enter will be handled
886 if ( !m_interior_detected )
889 // we might enter a boundary of some other ring on the same IP
890 m_interior_detected = true;
891 m_first_interior_other_id_ptr = boost::addressof(other_id);
895 else // operation_boundary
897 // don't add to the count for all met boundaries
898 // only if this is the "new" boundary
899 if ( first_point || !it->operations[op_id].is_collinear )
900 ++m_boundary_counter;
902 update<interior, boundary, '1', TransposeResult>(res);
906 = is_ip_on_boundary<boundary_front>(it->point,
907 it->operations[op_id],
910 // going inside on boundary point
913 update<boundary, boundary, '0', TransposeResult>(res);
915 // going inside on non-boundary point
918 update<interior, boundary, '0', TransposeResult>(res);
920 // if we didn't enter in the past, we were outside
921 if ( no_enters_detected
922 && ! fake_enter_detected
923 && it->operations[op_id].position != overlay::position_front )
925 // TODO: calculate_from_inside() is only needed if the current Linestring is not closed
926 bool const from_inside = first_point
927 && calculate_from_inside(geometry,
933 update<interior, interior, '1', TransposeResult>(res);
935 update<interior, exterior, '1', TransposeResult>(res);
937 // if it's the first IP then the first point is outside
940 bool const front_b = is_endpoint_on_boundary<boundary_front>(
941 range::front(sub_range(geometry, seg_id)),
944 // if there is a boundary on the first point
948 update<boundary, interior, '0', TransposeResult>(res);
950 update<boundary, exterior, '0', TransposeResult>(res);
956 if ( BOOST_GEOMETRY_CONDITION( is_multi<OtherGeometry>::value ) )
958 m_first_from_unknown = false;
959 m_first_from_unknown_boundary_detected = false;
963 else if ( op == overlay::operation_union || op == overlay::operation_blocked )
965 bool const op_blocked = op == overlay::operation_blocked;
966 bool const no_enters_detected = m_exit_watcher.is_outside()
967 // TODO: is this condition ok?
968 // TODO: move it into the exit_watcher?
969 && m_exit_watcher.get_exit_operation() == overlay::operation_none;
971 if ( op == overlay::operation_union )
973 if ( m_boundary_counter > 0 && it->operations[op_id].is_collinear )
974 --m_boundary_counter;
976 else // overlay::operation_blocked
978 m_boundary_counter = 0;
981 // we're inside, possibly going out right now
982 if ( ! no_enters_detected )
985 && it->operations[op_id].position == overlay::position_back ) // ignore spikes!
987 // check if this is indeed the boundary point
988 // NOTE: is_ip_on_boundary<>() should be called here but the result will be the same
989 if ( is_endpoint_on_boundary<boundary_back>(it->point, boundary_checker) )
991 update<boundary, boundary, '0', TransposeResult>(res);
994 // union, inside, but no exit -> collinear on self-intersection point
995 // not needed since we're already inside the boundary
996 /*else if ( !exit_detected )
998 update<interior, boundary, '0', TransposeResult>(res);
1001 // we're outside or inside and this is the first turn
1004 bool const this_b = is_ip_on_boundary<boundary_any>(it->point,
1005 it->operations[op_id],
1008 // if current IP is on boundary of the geometry
1011 update<boundary, boundary, '0', TransposeResult>(res);
1013 // if current IP is not on boundary of the geometry
1016 update<interior, boundary, '0', TransposeResult>(res);
1019 // TODO: very similar code is used in the handling of intersection
1020 if ( it->operations[op_id].position != overlay::position_front )
1022 // TODO: calculate_from_inside() is only needed if the current Linestring is not closed
1023 // NOTE: this is not enough for MultiPolygon and operation_blocked
1024 // For LS/MultiPolygon multiple x/u turns may be generated
1025 // the first checked Polygon may be the one which LS is outside for.
1026 bool const first_point = first_in_range || m_first_from_unknown;
1027 bool const first_from_inside = first_point
1028 && calculate_from_inside(geometry,
1032 if ( first_from_inside )
1034 update<interior, interior, '1', TransposeResult>(res);
1036 // notify the exit_watcher that we started inside
1037 m_exit_watcher.enter(*it);
1038 // and reset unknown flags since we know that we started inside
1039 m_first_from_unknown = false;
1040 m_first_from_unknown_boundary_detected = false;
1044 if ( BOOST_GEOMETRY_CONDITION( is_multi<OtherGeometry>::value )
1045 /*&& ( op == overlay::operation_blocked
1046 || op == overlay::operation_union )*/ ) // if we're here it's u or x
1048 m_first_from_unknown = true;
1052 update<interior, exterior, '1', TransposeResult>(res);
1056 // first IP on the last segment point - this means that the first point is outside or inside
1057 if ( first_point && ( !this_b || op_blocked ) )
1059 bool const front_b = is_endpoint_on_boundary<boundary_front>(
1060 range::front(sub_range(geometry, seg_id)),
1063 // if there is a boundary on the first point
1066 if ( first_from_inside )
1068 update<boundary, interior, '0', TransposeResult>(res);
1072 if ( BOOST_GEOMETRY_CONDITION( is_multi<OtherGeometry>::value )
1073 /*&& ( op == overlay::operation_blocked
1074 || op == overlay::operation_union )*/ ) // if we're here it's u or x
1076 BOOST_GEOMETRY_ASSERT(m_first_from_unknown);
1077 m_first_from_unknown_boundary_detected = true;
1081 update<boundary, exterior, '0', TransposeResult>(res);
1089 // if we're going along a boundary, we exit only if the linestring was collinear
1090 if ( m_boundary_counter == 0
1091 || it->operations[op_id].is_collinear )
1093 // notify the exit watcher about the possible exit
1094 m_exit_watcher.exit(*it);
1098 // store ref to previously analysed (valid) turn
1099 m_previous_turn_ptr = boost::addressof(*it);
1100 // and previously analysed (valid) operation
1101 m_previous_operation = op;
1105 template <typename Result,
1108 typename OtherGeometry,
1109 typename BoundaryChecker>
1110 void apply(Result & res,
1111 TurnIt first, TurnIt last,
1112 Geometry const& geometry,
1113 OtherGeometry const& /*other_geometry*/,
1114 BoundaryChecker const& boundary_checker)
1116 boost::ignore_unused(first, last);
1117 //BOOST_GEOMETRY_ASSERT( first != last );
1119 // For MultiPolygon many x/u operations may be generated as a first IP
1120 // if for all turns x/u was generated and any of the Polygons doesn't contain the LineString
1121 // then we know that the LineString is outside
1122 if ( BOOST_GEOMETRY_CONDITION( is_multi<OtherGeometry>::value )
1123 && m_first_from_unknown )
1125 update<interior, exterior, '1', TransposeResult>(res);
1126 if ( m_first_from_unknown_boundary_detected )
1128 update<boundary, exterior, '0', TransposeResult>(res);
1132 //m_first_from_unknown = false;
1133 //m_first_from_unknown_boundary_detected = false;
1136 // here, the possible exit is the real one
1137 // we know that we entered and now we exit
1138 if ( /*m_exit_watcher.get_exit_operation() == overlay::operation_union // THIS CHECK IS REDUNDANT
1139 ||*/ m_previous_operation == overlay::operation_union
1140 && !m_interior_detected )
1143 update<interior, exterior, '1', TransposeResult>(res);
1145 BOOST_GEOMETRY_ASSERT(first != last);
1146 BOOST_GEOMETRY_ASSERT(m_previous_turn_ptr);
1148 segment_identifier const& prev_seg_id = m_previous_turn_ptr->operations[op_id].seg_id;
1150 bool const prev_back_b = is_endpoint_on_boundary<boundary_back>(
1151 range::back(sub_range(geometry, prev_seg_id)),
1154 // if there is a boundary on the last point
1157 update<boundary, exterior, '0', TransposeResult>(res);
1160 // we might enter some Areal and didn't go out,
1161 else if ( m_previous_operation == overlay::operation_intersection
1162 || m_interior_detected )
1165 update<interior, interior, '1', TransposeResult>(res);
1166 m_interior_detected = false;
1168 BOOST_GEOMETRY_ASSERT(first != last);
1169 BOOST_GEOMETRY_ASSERT(m_previous_turn_ptr);
1171 segment_identifier const& prev_seg_id = m_previous_turn_ptr->operations[op_id].seg_id;
1173 bool const prev_back_b = is_endpoint_on_boundary<boundary_back>(
1174 range::back(sub_range(geometry, prev_seg_id)),
1177 // if there is a boundary on the last point
1180 update<boundary, interior, '0', TransposeResult>(res);
1184 // This condition may be false if the Linestring is lying on the Polygon's collinear spike
1185 // if Polygon's spikes are not handled in get_turns() or relate() (they currently aren't)
1186 //BOOST_GEOMETRY_ASSERT_MSG(m_previous_operation != overlay::operation_continue,
1187 // "Unexpected operation! Probably the error in get_turns(L,A) or relate(L,A)");
1188 // Currently one c/c turn is generated for the exit
1189 // when a Linestring is going out on a collinear spike
1190 // When a Linestring is going in on a collinear spike
1191 // the turn is not generated for the entry
1192 // So assume it's the former
1193 if ( m_previous_operation == overlay::operation_continue )
1195 update<interior, exterior, '1', TransposeResult>(res);
1197 segment_identifier const& prev_seg_id = m_previous_turn_ptr->operations[op_id].seg_id;
1199 bool const prev_back_b = is_endpoint_on_boundary<boundary_back>(
1200 range::back(sub_range(geometry, prev_seg_id)),
1203 // if there is a boundary on the last point
1206 update<boundary, exterior, '0', TransposeResult>(res);
1210 // Reset exit watcher before the analysis of the next Linestring
1211 m_exit_watcher.reset();
1212 m_boundary_counter = 0;
1213 m_first_from_unknown = false;
1214 m_first_from_unknown_boundary_detected = false;
1217 // check if the passed turn's segment of Linear geometry arrived
1218 // from the inside or the outside of the Areal geometry
1219 template <typename Turn, typename SideStrategy>
1220 static inline bool calculate_from_inside(Geometry1 const& geometry1,
1221 Geometry2 const& geometry2,
1223 SideStrategy const& side_strategy)
1225 typedef typename cs_tag<typename Turn::point_type>::type cs_tag;
1227 if ( turn.operations[op_id].position == overlay::position_front )
1230 typename sub_range_return_type<Geometry1 const>::type
1231 range1 = sub_range(geometry1, turn.operations[op_id].seg_id);
1233 typedef detail::normalized_view<Geometry2 const> const range2_type;
1234 typedef typename boost::range_iterator<range2_type>::type range2_iterator;
1235 range2_type range2(sub_range(geometry2, turn.operations[other_op_id].seg_id));
1237 BOOST_GEOMETRY_ASSERT(boost::size(range1));
1238 std::size_t const s2 = boost::size(range2);
1239 BOOST_GEOMETRY_ASSERT(s2 > 2);
1240 std::size_t const seg_count2 = s2 - 1;
1242 std::size_t const p_seg_ij = static_cast<std::size_t>(turn.operations[op_id].seg_id.segment_index);
1243 std::size_t const q_seg_ij = static_cast<std::size_t>(turn.operations[other_op_id].seg_id.segment_index);
1245 BOOST_GEOMETRY_ASSERT(p_seg_ij + 1 < boost::size(range1));
1246 BOOST_GEOMETRY_ASSERT(q_seg_ij + 1 < s2);
1248 point1_type const& pi = range::at(range1, p_seg_ij);
1249 point2_type const& qi = range::at(range2, q_seg_ij);
1250 point2_type const& qj = range::at(range2, q_seg_ij + 1);
1251 point1_type qi_conv;
1252 geometry::convert(qi, qi_conv);
1253 bool const is_ip_qj = equals::equals_point_point(turn.point, qj, side_strategy.get_equals_point_point_strategy());
1255 // BOOST_GEOMETRY_ASSERT(!equals::equals_point_point(turn.point, pi));
1256 // BOOST_GEOMETRY_ASSERT(!equals::equals_point_point(turn.point, qi));
1258 geometry::convert(turn.point, new_pj);
1262 std::size_t const q_seg_jk = (q_seg_ij + 1) % seg_count2;
1263 // TODO: the following function should return immediately, however the worst case complexity is O(N)
1264 // It would be good to replace it with some O(1) mechanism
1265 range2_iterator qk_it = find_next_non_duplicated(boost::begin(range2),
1266 range::pos(range2, q_seg_jk),
1268 side_strategy.get_equals_point_point_strategy());
1270 // Will this sequence of points be always correct?
1271 la_side_calculator<cs_tag, point1_type, point2_type, SideStrategy>
1272 side_calc(qi_conv, new_pj, pi, qi, qj, *qk_it, side_strategy);
1274 return calculate_from_inside_sides(side_calc);
1279 geometry::convert(turn.point, new_qj);
1281 la_side_calculator<cs_tag, point1_type, point2_type, SideStrategy>
1282 side_calc(qi_conv, new_pj, pi, qi, new_qj, qj, side_strategy);
1284 return calculate_from_inside_sides(side_calc);
1288 template <typename It, typename EqPPStrategy>
1289 static inline It find_next_non_duplicated(It first, It current, It last,
1290 EqPPStrategy const& strategy)
1292 BOOST_GEOMETRY_ASSERT( current != last );
1296 for ( ++it ; it != last ; ++it )
1298 if ( !equals::equals_point_point(*current, *it, strategy) )
1302 // if not found start from the beginning
1303 for ( it = first ; it != current ; ++it )
1305 if ( !equals::equals_point_point(*current, *it, strategy) )
1312 // calculate inside or outside based on side_calc
1313 // this is simplified version of a check from equal<>
1314 template <typename SideCalc>
1315 static inline bool calculate_from_inside_sides(SideCalc const& side_calc)
1317 int const side_pk_p = side_calc.pk_wrt_p1();
1318 int const side_qk_p = side_calc.qk_wrt_p1();
1319 // If they turn to same side (not opposite sides)
1320 if (! overlay::base_turn_handler::opposite(side_pk_p, side_qk_p))
1322 int const side_pk_q2 = side_calc.pk_wrt_q2();
1323 return side_pk_q2 == -1;
1327 return side_pk_p == -1;
1332 exit_watcher<TurnInfo, op_id> m_exit_watcher;
1333 segment_watcher<same_single> m_seg_watcher;
1334 TurnInfo * m_previous_turn_ptr;
1335 overlay::operation_type m_previous_operation;
1336 unsigned m_boundary_counter;
1337 bool m_interior_detected;
1338 const segment_identifier * m_first_interior_other_id_ptr;
1339 bool m_first_from_unknown;
1340 bool m_first_from_unknown_boundary_detected;
1343 // call analyser.apply() for each turn in range
1344 // IMPORTANT! The analyser is also called for the end iterator - last
1345 template <typename Result,
1349 typename OtherGeometry,
1350 typename BoundaryChecker,
1351 typename SideStrategy>
1352 static inline void analyse_each_turn(Result & res,
1353 Analyser & analyser,
1354 TurnIt first, TurnIt last,
1355 Geometry const& geometry,
1356 OtherGeometry const& other_geometry,
1357 BoundaryChecker const& boundary_checker,
1358 SideStrategy const& side_strategy)
1360 if ( first == last )
1363 for ( TurnIt it = first ; it != last ; ++it )
1365 analyser.apply(res, it,
1366 geometry, other_geometry,
1370 if ( BOOST_GEOMETRY_CONDITION( res.interrupt ) )
1374 analyser.apply(res, first, last,
1375 geometry, other_geometry,
1379 // less comparator comparing multi_index then ring_index
1380 // may be used to sort turns by ring
1383 template <typename Turn>
1384 inline bool operator()(Turn const& left, Turn const& right) const
1386 return left.operations[1].seg_id.multi_index < right.operations[1].seg_id.multi_index
1387 || ( left.operations[1].seg_id.multi_index == right.operations[1].seg_id.multi_index
1388 && left.operations[1].seg_id.ring_index < right.operations[1].seg_id.ring_index );
1392 // policy/functor checking if a turn's operation is operation_continue
1393 struct has_boundary_intersection
1395 has_boundary_intersection()
1398 template <typename Turn>
1399 inline void operator()(Turn const& turn)
1401 if ( turn.operations[1].operation == overlay::operation_continue )
1408 // iterate through the range and search for the different multi_index or ring_index
1409 // also call fun for each turn in the current range
1410 template <typename It, typename Fun>
1411 static inline It find_next_ring(It first, It last, Fun & fun)
1413 if ( first == last )
1416 signed_size_type const multi_index = first->operations[1].seg_id.multi_index;
1417 signed_size_type const ring_index = first->operations[1].seg_id.ring_index;
1422 for ( ; first != last ; ++first )
1424 if ( multi_index != first->operations[1].seg_id.multi_index
1425 || ring_index != first->operations[1].seg_id.ring_index )
1436 // analyser which called for turns sorted by seg/distance/operation
1437 // checks if the boundary of Areal geometry really got out
1438 // into the exterior of Linear geometry
1439 template <typename TurnInfo>
1440 class areal_boundary_analyser
1443 areal_boundary_analyser()
1444 : is_union_detected(false)
1445 , m_previous_turn_ptr(NULL)
1448 template <typename TurnIt, typename EqPPStrategy>
1449 bool apply(TurnIt /*first*/, TurnIt it, TurnIt last,
1450 EqPPStrategy const& strategy)
1452 overlay::operation_type op = it->operations[1].operation;
1456 if ( op != overlay::operation_union
1457 && op != overlay::operation_continue )
1462 if ( is_union_detected )
1464 BOOST_GEOMETRY_ASSERT(m_previous_turn_ptr != NULL);
1465 if ( !detail::equals::equals_point_point(it->point, m_previous_turn_ptr->point, strategy) )
1470 else if ( op == overlay::operation_continue ) // operation_boundary
1472 is_union_detected = false;
1476 if ( op == overlay::operation_union )
1478 is_union_detected = true;
1479 m_previous_turn_ptr = boost::addressof(*it);
1490 bool is_union_detected;
1493 const TurnInfo * m_previous_turn_ptr;
1497 template <typename Geometry1, typename Geometry2>
1500 typedef linear_areal<Geometry2, Geometry1, true> linear_areal_type;
1502 static const bool interruption_enabled = linear_areal_type::interruption_enabled;
1504 template <typename Result, typename IntersectionStrategy>
1505 static inline void apply(Geometry1 const& geometry1, Geometry2 const& geometry2,
1507 IntersectionStrategy const& intersection_strategy)
1509 linear_areal_type::apply(geometry2, geometry1, result, intersection_strategy);
1513 }} // namespace detail::relate
1514 #endif // DOXYGEN_NO_DETAIL
1516 }} // namespace boost::geometry
1518 #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_LINEAR_AREAL_HPP