summaryrefslogtreecommitdiff
path: root/boost/geometry/algorithms/detail/relate/areal_areal.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'boost/geometry/algorithms/detail/relate/areal_areal.hpp')
-rw-r--r--boost/geometry/algorithms/detail/relate/areal_areal.hpp823
1 files changed, 823 insertions, 0 deletions
diff --git a/boost/geometry/algorithms/detail/relate/areal_areal.hpp b/boost/geometry/algorithms/detail/relate/areal_areal.hpp
new file mode 100644
index 0000000000..31d206ac99
--- /dev/null
+++ b/boost/geometry/algorithms/detail/relate/areal_areal.hpp
@@ -0,0 +1,823 @@
+// Boost.Geometry (aka GGL, Generic Geometry Library)
+
+// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
+
+// This file was modified by Oracle on 2013, 2014.
+// Modifications copyright (c) 2013-2014 Oracle and/or its affiliates.
+
+// Use, modification and distribution is subject to the Boost Software License,
+// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
+
+#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_AREAL_AREAL_HPP
+#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_AREAL_AREAL_HPP
+
+#include <boost/geometry/core/topological_dimension.hpp>
+#include <boost/geometry/util/range.hpp>
+
+#include <boost/geometry/algorithms/num_interior_rings.hpp>
+#include <boost/geometry/algorithms/detail/point_on_border.hpp>
+#include <boost/geometry/algorithms/detail/sub_range.hpp>
+#include <boost/geometry/algorithms/detail/single_geometry.hpp>
+
+#include <boost/geometry/algorithms/detail/relate/point_geometry.hpp>
+#include <boost/geometry/algorithms/detail/relate/turns.hpp>
+#include <boost/geometry/algorithms/detail/relate/boundary_checker.hpp>
+#include <boost/geometry/algorithms/detail/relate/follow_helpers.hpp>
+
+namespace boost { namespace geometry
+{
+
+#ifndef DOXYGEN_NO_DETAIL
+namespace detail { namespace relate {
+
+// WARNING!
+// TODO: In the worst case calling this Pred in a loop for MultiPolygon/MultiPolygon may take O(NM)
+// Use the rtree in this case!
+
+// may be used to set EI and EB for an Areal geometry for which no turns were generated
+template <typename OtherAreal, typename Result, bool TransposeResult>
+class no_turns_aa_pred
+{
+public:
+ no_turns_aa_pred(OtherAreal const& other_areal, Result & res)
+ : m_result(res)
+ , m_other_areal(other_areal)
+ , m_flags(0)
+ {
+ // check which relations must be analysed
+
+ if ( ! may_update<interior, interior, '2', TransposeResult>(m_result)
+ && ! may_update<boundary, interior, '1', TransposeResult>(m_result)
+ && ! may_update<exterior, interior, '2', TransposeResult>(m_result) )
+ {
+ m_flags |= 1;
+ }
+
+ if ( ! may_update<interior, exterior, '2', TransposeResult>(m_result)
+ && ! may_update<boundary, exterior, '1', TransposeResult>(m_result) )
+ {
+ m_flags |= 2;
+ }
+ }
+
+ template <typename Areal>
+ bool operator()(Areal const& areal)
+ {
+ // if those flags are set nothing will change
+ if ( m_flags == 3 )
+ {
+ return false;
+ }
+
+ typedef typename geometry::point_type<Areal>::type point_type;
+ point_type pt;
+ bool const ok = boost::geometry::point_on_border(pt, areal);
+
+ // TODO: for now ignore, later throw an exception?
+ if ( !ok )
+ {
+ return true;
+ }
+
+ // check if the areal is inside the other_areal
+ // TODO: This is O(N)
+ // Run in a loop O(NM) - optimize!
+ int const pig = detail::within::point_in_geometry(pt, m_other_areal);
+ //BOOST_ASSERT( pig != 0 );
+
+ // inside
+ if ( pig > 0 )
+ {
+ update<interior, interior, '2', TransposeResult>(m_result);
+ update<boundary, interior, '1', TransposeResult>(m_result);
+ update<exterior, interior, '2', TransposeResult>(m_result);
+ m_flags |= 1;
+
+ // TODO: OPTIMIZE!
+ // Only the interior rings of other ONE single geometry must be checked
+ // NOT all geometries
+
+ // Check if any interior ring is outside
+ ring_identifier ring_id(0, -1, 0);
+ int const irings_count = boost::numeric_cast<int>(
+ geometry::num_interior_rings(areal) );
+ for ( ; ring_id.ring_index < irings_count ; ++ring_id.ring_index )
+ {
+ typename detail::sub_range_return_type<Areal const>::type
+ range_ref = detail::sub_range(areal, ring_id);
+
+ if ( boost::empty(range_ref) )
+ {
+ // TODO: throw exception?
+ continue; // ignore
+ }
+
+ // TODO: O(N)
+ // Optimize!
+ int const hpig = detail::within::point_in_geometry(range::front(range_ref), m_other_areal);
+
+ // hole outside
+ if ( hpig < 0 )
+ {
+ update<interior, exterior, '2', TransposeResult>(m_result);
+ update<boundary, exterior, '1', TransposeResult>(m_result);
+ m_flags |= 2;
+ break;
+ }
+ }
+ }
+ // outside
+ else
+ {
+ update<interior, exterior, '2', TransposeResult>(m_result);
+ update<boundary, exterior, '1', TransposeResult>(m_result);
+ m_flags |= 2;
+
+ // Check if any interior ring is inside
+ ring_identifier ring_id(0, -1, 0);
+ int const irings_count = boost::numeric_cast<int>(
+ geometry::num_interior_rings(areal) );
+ for ( ; ring_id.ring_index < irings_count ; ++ring_id.ring_index )
+ {
+ typename detail::sub_range_return_type<Areal const>::type
+ range_ref = detail::sub_range(areal, ring_id);
+
+ if ( boost::empty(range_ref) )
+ {
+ // TODO: throw exception?
+ continue; // ignore
+ }
+
+ // TODO: O(N)
+ // Optimize!
+ int const hpig = detail::within::point_in_geometry(range::front(range_ref), m_other_areal);
+
+ // hole inside
+ if ( hpig > 0 )
+ {
+ update<interior, interior, '2', TransposeResult>(m_result);
+ update<boundary, interior, '1', TransposeResult>(m_result);
+ update<exterior, interior, '2', TransposeResult>(m_result);
+ m_flags |= 1;
+ break;
+ }
+ }
+ }
+
+ return m_flags != 3 && !m_result.interrupt;
+ }
+
+private:
+ Result & m_result;
+ OtherAreal const& m_other_areal;
+ int m_flags;
+};
+
+// The implementation of an algorithm calculating relate() for A/A
+template <typename Geometry1, typename Geometry2>
+struct areal_areal
+{
+ // check Linear / Areal
+ BOOST_STATIC_ASSERT(topological_dimension<Geometry1>::value == 2
+ && topological_dimension<Geometry2>::value == 2);
+
+ static const bool interruption_enabled = true;
+
+ typedef typename geometry::point_type<Geometry1>::type point1_type;
+ typedef typename geometry::point_type<Geometry2>::type point2_type;
+
+ template <typename Result>
+ static inline void apply(Geometry1 const& geometry1, Geometry2 const& geometry2, Result & result)
+ {
+// TODO: If Areal geometry may have infinite size, change the following line:
+
+ // The result should be FFFFFFFFF
+ relate::set<exterior, exterior, result_dimension<Geometry2>::value>(result);// FFFFFFFFd, d in [1,9] or T
+
+ if ( result.interrupt )
+ return;
+
+ // get and analyse turns
+ typedef typename turns::get_turns<Geometry1, Geometry2>::turn_info turn_type;
+ std::vector<turn_type> turns;
+
+ interrupt_policy_areal_areal<Result> interrupt_policy(geometry1, geometry2, result);
+
+ turns::get_turns<Geometry1, Geometry2>::apply(turns, geometry1, geometry2, interrupt_policy);
+ if ( result.interrupt )
+ return;
+
+ no_turns_aa_pred<Geometry2, Result, false> pred1(geometry2, result);
+ for_each_disjoint_geometry_if<0, Geometry1>::apply(turns.begin(), turns.end(), geometry1, pred1);
+ if ( result.interrupt )
+ return;
+
+ no_turns_aa_pred<Geometry1, Result, true> pred2(geometry1, result);
+ for_each_disjoint_geometry_if<1, Geometry2>::apply(turns.begin(), turns.end(), geometry2, pred2);
+ if ( result.interrupt )
+ return;
+
+ if ( turns.empty() )
+ return;
+
+ if ( may_update<interior, interior, '2'>(result)
+ || may_update<interior, exterior, '2'>(result)
+ || may_update<boundary, interior, '1'>(result)
+ || may_update<boundary, exterior, '1'>(result)
+ || may_update<exterior, interior, '2'>(result) )
+ {
+ // sort turns
+ typedef turns::less<0, turns::less_op_areal_areal<0> > less;
+ std::sort(turns.begin(), turns.end(), less());
+
+ /*if ( may_update<interior, exterior, '2'>(result)
+ || may_update<boundary, exterior, '1'>(result)
+ || may_update<boundary, interior, '1'>(result)
+ || may_update<exterior, interior, '2'>(result) )*/
+ {
+ // analyse sorted turns
+ turns_analyser<turn_type, 0> analyser;
+ analyse_each_turn(result, analyser, turns.begin(), turns.end());
+
+ if ( result.interrupt )
+ return;
+ }
+
+ if ( may_update<interior, interior, '2'>(result)
+ || may_update<interior, exterior, '2'>(result)
+ || may_update<boundary, interior, '1'>(result)
+ || may_update<boundary, exterior, '1'>(result)
+ || may_update<exterior, interior, '2'>(result) )
+ {
+ // analyse rings for which turns were not generated
+ // or only i/i or u/u was generated
+ uncertain_rings_analyser<0, Result, Geometry1, Geometry2> rings_analyser(result, geometry1, geometry2);
+ analyse_uncertain_rings<0>::apply(rings_analyser, turns.begin(), turns.end());
+
+ if ( result.interrupt )
+ return;
+ }
+ }
+
+ if ( may_update<interior, interior, '2', true>(result)
+ || may_update<interior, exterior, '2', true>(result)
+ || may_update<boundary, interior, '1', true>(result)
+ || may_update<boundary, exterior, '1', true>(result)
+ || may_update<exterior, interior, '2', true>(result) )
+ {
+ // sort turns
+ typedef turns::less<1, turns::less_op_areal_areal<1> > less;
+ std::sort(turns.begin(), turns.end(), less());
+
+ /*if ( may_update<interior, exterior, '2', true>(result)
+ || may_update<boundary, exterior, '1', true>(result)
+ || may_update<boundary, interior, '1', true>(result)
+ || may_update<exterior, interior, '2', true>(result) )*/
+ {
+ // analyse sorted turns
+ turns_analyser<turn_type, 1> analyser;
+ analyse_each_turn(result, analyser, turns.begin(), turns.end());
+
+ if ( result.interrupt )
+ return;
+ }
+
+ if ( may_update<interior, interior, '2', true>(result)
+ || may_update<interior, exterior, '2', true>(result)
+ || may_update<boundary, interior, '1', true>(result)
+ || may_update<boundary, exterior, '1', true>(result)
+ || may_update<exterior, interior, '2', true>(result) )
+ {
+ // analyse rings for which turns were not generated
+ // or only i/i or u/u was generated
+ uncertain_rings_analyser<1, Result, Geometry2, Geometry1> rings_analyser(result, geometry2, geometry1);
+ analyse_uncertain_rings<1>::apply(rings_analyser, turns.begin(), turns.end());
+
+ //if ( result.interrupt )
+ // return;
+ }
+ }
+ }
+
+ // interrupt policy which may be passed to get_turns to interrupt the analysis
+ // based on the info in the passed result/mask
+ template <typename Result>
+ class interrupt_policy_areal_areal
+ {
+ public:
+ static bool const enabled = true;
+
+ interrupt_policy_areal_areal(Geometry1 const& geometry1,
+ Geometry2 const& geometry2,
+ Result & result)
+ : m_result(result)
+ , m_geometry1(geometry1)
+ , m_geometry2(geometry2)
+ {}
+
+ template <typename Range>
+ inline bool apply(Range const& turns)
+ {
+ typedef typename boost::range_iterator<Range const>::type iterator;
+
+ for (iterator it = boost::begin(turns) ; it != boost::end(turns) ; ++it)
+ {
+ per_turn<0>(*it);
+ per_turn<1>(*it);
+ }
+
+ return m_result.interrupt;
+ }
+
+ private:
+ template <std::size_t OpId, typename Turn>
+ inline void per_turn(Turn const& turn)
+ {
+ static const std::size_t other_op_id = (OpId + 1) % 2;
+ static const bool transpose_result = OpId != 0;
+
+ overlay::operation_type const op = turn.operations[OpId].operation;
+
+ if ( op == overlay::operation_union )
+ {
+ // ignore u/u
+ /*if ( turn.operations[other_op_id].operation != overlay::operation_union )
+ {
+ update<interior, exterior, '2', transpose_result>(m_result);
+ update<boundary, exterior, '1', transpose_result>(m_result);
+ }*/
+
+ update<boundary, boundary, '0', transpose_result>(m_result);
+ }
+ else if ( op == overlay::operation_intersection )
+ {
+ // ignore i/i
+ if ( turn.operations[other_op_id].operation != overlay::operation_intersection )
+ {
+ update<interior, interior, '2', transpose_result>(m_result);
+ //update<boundary, interior, '1', transpose_result>(m_result);
+ }
+
+ update<boundary, boundary, '0', transpose_result>(m_result);
+ }
+ else if ( op == overlay::operation_continue )
+ {
+ update<boundary, boundary, '1', transpose_result>(m_result);
+ update<interior, interior, '2', transpose_result>(m_result);
+ }
+ else if ( op == overlay::operation_blocked )
+ {
+ update<boundary, boundary, '1', transpose_result>(m_result);
+ update<interior, exterior, '2', transpose_result>(m_result);
+ }
+ }
+
+ Result & m_result;
+ Geometry1 const& m_geometry1;
+ Geometry2 const& m_geometry2;
+ };
+
+ // This analyser should be used like Input or SinglePass Iterator
+ // IMPORTANT! It should be called also for the end iterator - last
+ template <typename TurnInfo, std::size_t OpId>
+ class turns_analyser
+ {
+ typedef typename TurnInfo::point_type turn_point_type;
+
+ static const std::size_t op_id = OpId;
+ static const std::size_t other_op_id = (OpId + 1) % 2;
+ static const bool transpose_result = OpId != 0;
+
+ public:
+ turns_analyser()
+ : m_previous_turn_ptr(0)
+ , m_previous_operation(overlay::operation_none)
+ , m_enter_detected(false)
+ , m_exit_detected(false)
+ {}
+
+ template <typename Result,
+ typename TurnIt>
+ void apply(Result & result, TurnIt it)
+ {
+ //BOOST_ASSERT( it != last );
+
+ overlay::operation_type const op = it->operations[op_id].operation;
+
+ if ( op != overlay::operation_union
+ && op != overlay::operation_intersection
+ && op != overlay::operation_blocked
+ && op != overlay::operation_continue )
+ {
+ return;
+ }
+
+ segment_identifier const& seg_id = it->operations[op_id].seg_id;
+ //segment_identifier const& other_id = it->operations[other_op_id].seg_id;
+
+ const bool first_in_range = m_seg_watcher.update(seg_id);
+
+ if ( m_previous_turn_ptr )
+ {
+ if ( m_exit_detected /*m_previous_operation == overlay::operation_union*/ )
+ {
+ // real exit point - may be multiple
+ if ( first_in_range
+ || ! turn_on_the_same_ip<op_id>(*m_previous_turn_ptr, *it) )
+ {
+ update_exit(result);
+ m_exit_detected = false;
+ }
+ // fake exit point, reset state
+ else if ( op != overlay::operation_union )
+ {
+ m_exit_detected = false;
+ }
+ }
+ /*else*/
+ if ( m_enter_detected /*m_previous_operation == overlay::operation_intersection*/ )
+ {
+ // real entry point
+ if ( first_in_range
+ || ! turn_on_the_same_ip<op_id>(*m_previous_turn_ptr, *it) )
+ {
+ update_enter(result);
+ m_enter_detected = false;
+ }
+ // fake entry point, reset state
+ else if ( op != overlay::operation_intersection )
+ {
+ m_enter_detected = false;
+ }
+ }
+ }
+
+ if ( op == overlay::operation_union )
+ {
+ // already set in interrupt policy
+ //update<boundary, boundary, '0', transpose_result>(m_result);
+
+ // ignore u/u
+ //if ( it->operations[other_op_id].operation != overlay::operation_union )
+ {
+ m_exit_detected = true;
+ }
+ }
+ else if ( op == overlay::operation_intersection )
+ {
+ // ignore i/i
+ if ( it->operations[other_op_id].operation != overlay::operation_intersection )
+ {
+ // already set in interrupt policy
+ //update<interior, interior, '2', transpose_result>(result);
+ //update<boundary, boundary, '0', transpose_result>(result);
+ m_enter_detected = true;
+ }
+ }
+ else if ( op == overlay::operation_blocked )
+ {
+ // already set in interrupt policy
+ }
+ else // if ( op == overlay::operation_continue )
+ {
+ // already set in interrupt policy
+ }
+
+ // store ref to previously analysed (valid) turn
+ m_previous_turn_ptr = boost::addressof(*it);
+ // and previously analysed (valid) operation
+ m_previous_operation = op;
+ }
+
+ // it == last
+ template <typename Result>
+ void apply(Result & result)
+ {
+ //BOOST_ASSERT( first != last );
+
+ if ( m_exit_detected /*m_previous_operation == overlay::operation_union*/ )
+ {
+ update_exit(result);
+ m_exit_detected = false;
+ }
+
+ if ( m_enter_detected /*m_previous_operation == overlay::operation_intersection*/ )
+ {
+ update_enter(result);
+ m_enter_detected = false;
+ }
+ }
+
+ template <typename Result>
+ static inline void update_exit(Result & result)
+ {
+ update<interior, exterior, '2', transpose_result>(result);
+ update<boundary, exterior, '1', transpose_result>(result);
+ }
+
+ template <typename Result>
+ static inline void update_enter(Result & result)
+ {
+ update<boundary, interior, '1', transpose_result>(result);
+ update<exterior, interior, '2', transpose_result>(result);
+ }
+
+ private:
+ segment_watcher<same_ring> m_seg_watcher;
+ TurnInfo * m_previous_turn_ptr;
+ overlay::operation_type m_previous_operation;
+ bool m_enter_detected;
+ bool m_exit_detected;
+ };
+
+ // call analyser.apply() for each turn in range
+ // IMPORTANT! The analyser is also called for the end iterator - last
+ template <typename Result,
+ typename Analyser,
+ typename TurnIt>
+ static inline void analyse_each_turn(Result & res,
+ Analyser & analyser,
+ TurnIt first, TurnIt last)
+ {
+ if ( first == last )
+ return;
+
+ for ( TurnIt it = first ; it != last ; ++it )
+ {
+ analyser.apply(res, it);
+
+ if ( res.interrupt )
+ return;
+ }
+
+ analyser.apply(res);
+ }
+
+ template <std::size_t OpId, typename Result, typename Geometry, typename OtherGeometry>
+ class uncertain_rings_analyser
+ {
+ static const bool transpose_result = OpId != 0;
+ static const int other_id = (OpId + 1) % 2;
+
+ public:
+ inline uncertain_rings_analyser(Result & result,
+ Geometry const& geom,
+ OtherGeometry const& other_geom)
+ : geometry(geom), other_geometry(other_geom)
+ , interrupt(result.interrupt) // just in case, could be false as well
+ , m_result(result)
+ , m_flags(0)
+ {
+ // check which relations must be analysed
+
+ if ( ! may_update<interior, interior, '2', transpose_result>(m_result)
+ && ! may_update<boundary, interior, '1', transpose_result>(m_result) )
+ {
+ m_flags |= 1;
+ }
+
+ if ( ! may_update<interior, exterior, '2', transpose_result>(m_result)
+ && ! may_update<boundary, exterior, '1', transpose_result>(m_result) )
+ {
+ m_flags |= 2;
+ }
+
+ if ( ! may_update<boundary, interior, '1', transpose_result>(m_result)
+ && ! may_update<exterior, interior, '2', transpose_result>(m_result) )
+ {
+ m_flags |= 4;
+ }
+ }
+
+ inline void no_turns(segment_identifier const& seg_id)
+ {
+ // if those flags are set nothing will change
+ if ( (m_flags & 3) == 3 )
+ {
+ return;
+ }
+
+ typename detail::sub_range_return_type<Geometry const>::type
+ range_ref = detail::sub_range(geometry, seg_id);
+
+ if ( boost::empty(range_ref) )
+ {
+ // TODO: throw an exception?
+ return; // ignore
+ }
+
+ // TODO: possible optimization
+ // if the range is an interior ring we may use other IPs generated for this single geometry
+ // to know which other single geometries should be checked
+
+ // TODO: optimize! e.g. use spatial index
+ // O(N) - running it in a loop would gives O(NM)
+ int const pig = detail::within::point_in_geometry(range::front(range_ref), other_geometry);
+
+ //BOOST_ASSERT(pig != 0);
+ if ( pig > 0 )
+ {
+ update<boundary, interior, '1', transpose_result>(m_result);
+ update<interior, interior, '2', transpose_result>(m_result);
+ m_flags |= 1;
+ }
+ else
+ {
+ update<boundary, exterior, '1', transpose_result>(m_result);
+ update<interior, exterior, '2', transpose_result>(m_result);
+ m_flags |= 2;
+ }
+
+// TODO: break if all things are set
+// also some of them could be checked outside, before the analysis
+// In this case we shouldn't relay just on dummy flags
+// Flags should be initialized with proper values
+// or the result should be checked directly
+// THIS IS ALSO TRUE FOR OTHER ANALYSERS! in L/L and L/A
+
+ interrupt = m_flags == 7 || m_result.interrupt;
+ }
+
+ template <typename TurnIt>
+ inline void turns(TurnIt first, TurnIt last)
+ {
+ // if those flags are set nothing will change
+ if ( (m_flags & 6) == 6 )
+ {
+ return;
+ }
+
+ bool found_ii = false;
+ bool found_uu = false;
+
+ for ( TurnIt it = first ; it != last ; ++it )
+ {
+ if ( it->operations[0].operation == overlay::operation_intersection
+ && it->operations[1].operation == overlay::operation_intersection )
+ {
+ // ignore exterior ring
+ if ( it->operations[OpId].seg_id.ring_index >= 0 )
+ {
+ found_ii = true;
+ }
+ }
+ else if ( it->operations[0].operation == overlay::operation_union
+ && it->operations[1].operation == overlay::operation_union )
+ {
+ // ignore if u/u is for holes
+ //if ( it->operations[OpId].seg_id.ring_index >= 0
+ // && it->operations[other_id].seg_id.ring_index >= 0 )
+ {
+ found_uu = true;
+ }
+ }
+ else // ignore
+ {
+ return; // don't interrupt
+ }
+ }
+
+ // only i/i was generated for this ring
+ if ( found_ii )
+ {
+ //update<interior, interior, '0', transpose_result>(m_result);
+ //update<boundary, boundary, '0', transpose_result>(m_result);
+ update<boundary, interior, '1', transpose_result>(m_result);
+ update<exterior, interior, '2', transpose_result>(m_result);
+ m_flags |= 4;
+ }
+
+ // only u/u was generated for this ring
+ if ( found_uu )
+ {
+ update<boundary, exterior, '1', transpose_result>(m_result);
+ update<interior, exterior, '2', transpose_result>(m_result);
+ m_flags |= 2;
+
+ // not necessary since this will be checked in the next iteration
+ // but increases the pruning strength
+ // WARNING: this is not reflected in flags
+ update<exterior, boundary, '1', transpose_result>(m_result);
+ update<exterior, interior, '2', transpose_result>(m_result);
+ }
+
+ interrupt = m_flags == 7 || m_result.interrupt; // interrupt if the result won't be changed in the future
+ }
+
+ Geometry const& geometry;
+ OtherGeometry const& other_geometry;
+ bool interrupt;
+
+ private:
+ Result & m_result;
+ int m_flags;
+ };
+
+ template <std::size_t OpId>
+ class analyse_uncertain_rings
+ {
+ public:
+ template <typename Analyser, typename TurnIt>
+ static inline void apply(Analyser & analyser, TurnIt first, TurnIt last)
+ {
+ if ( first == last )
+ return;
+
+ for_preceding_rings(analyser, *first);
+ //analyser.per_turn(*first);
+
+ TurnIt prev = first;
+ for ( ++first ; first != last ; ++first, ++prev )
+ {
+ // same multi
+ if ( prev->operations[OpId].seg_id.multi_index
+ == first->operations[OpId].seg_id.multi_index )
+ {
+ // same ring
+ if ( prev->operations[OpId].seg_id.ring_index
+ == first->operations[OpId].seg_id.ring_index )
+ {
+ //analyser.per_turn(*first);
+ }
+ // same multi, next ring
+ else
+ {
+ //analyser.end_ring(*prev);
+ analyser.turns(prev, first);
+
+ //if ( prev->operations[OpId].seg_id.ring_index + 1
+ // < first->operations[OpId].seg_id.ring_index)
+ {
+ for_no_turns_rings(analyser,
+ *first,
+ prev->operations[OpId].seg_id.ring_index + 1,
+ first->operations[OpId].seg_id.ring_index);
+ }
+
+ //analyser.per_turn(*first);
+ }
+ }
+ // next multi
+ else
+ {
+ //analyser.end_ring(*prev);
+ analyser.turns(prev, first);
+ for_following_rings(analyser, *prev);
+ for_preceding_rings(analyser, *first);
+ //analyser.per_turn(*first);
+ }
+
+ if ( analyser.interrupt )
+ {
+ return;
+ }
+ }
+
+ //analyser.end_ring(*prev);
+ analyser.turns(prev, first); // first == last
+ for_following_rings(analyser, *prev);
+ }
+
+ private:
+ template <typename Analyser, typename Turn>
+ static inline void for_preceding_rings(Analyser & analyser, Turn const& turn)
+ {
+ segment_identifier const& seg_id = turn.operations[OpId].seg_id;
+
+ for_no_turns_rings(analyser, turn, -1, seg_id.ring_index);
+ }
+
+ template <typename Analyser, typename Turn>
+ static inline void for_following_rings(Analyser & analyser, Turn const& turn)
+ {
+ segment_identifier const& seg_id = turn.operations[OpId].seg_id;
+
+ int count = boost::numeric_cast<int>(
+ geometry::num_interior_rings(
+ detail::single_geometry(analyser.geometry, seg_id)));
+
+ for_no_turns_rings(analyser, turn, seg_id.ring_index + 1, count);
+ }
+
+ template <typename Analyser, typename Turn>
+ static inline void for_no_turns_rings(Analyser & analyser, Turn const& turn, int first, int last)
+ {
+ segment_identifier seg_id = turn.operations[OpId].seg_id;
+
+ for ( seg_id.ring_index = first ; seg_id.ring_index < last ; ++seg_id.ring_index )
+ {
+ analyser.no_turns(seg_id);
+ }
+ }
+ };
+};
+
+}} // namespace detail::relate
+#endif // DOXYGEN_NO_DETAIL
+
+}} // namespace boost::geometry
+
+#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_AREAL_AREAL_HPP