You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
875 lines
32 KiB
C++
875 lines
32 KiB
C++
// 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, 2015, 2017, 2018, 2019.
|
|
// Modifications copyright (c) 2013-2019 Oracle and/or its affiliates.
|
|
|
|
// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
|
|
|
|
// 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)
|
|
|
|
#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/condition.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,
|
|
typename PointInArealStrategy,
|
|
bool TransposeResult
|
|
>
|
|
class no_turns_aa_pred
|
|
{
|
|
public:
|
|
no_turns_aa_pred(OtherAreal const& other_areal,
|
|
Result & res,
|
|
PointInArealStrategy const& point_in_areal_strategy)
|
|
: m_result(res)
|
|
, m_point_in_areal_strategy(point_in_areal_strategy)
|
|
, 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)
|
|
{
|
|
using detail::within::point_in_geometry;
|
|
|
|
// 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 = point_in_geometry(pt,
|
|
m_other_areal,
|
|
m_point_in_areal_strategy);
|
|
//BOOST_GEOMETRY_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);
|
|
std::size_t const irings_count = geometry::num_interior_rings(areal);
|
|
for ( ; static_cast<std::size_t>(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 = point_in_geometry(range::front(range_ref),
|
|
m_other_areal,
|
|
m_point_in_areal_strategy);
|
|
|
|
// 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);
|
|
std::size_t const irings_count = geometry::num_interior_rings(areal);
|
|
for ( ; static_cast<std::size_t>(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 = point_in_geometry(range::front(range_ref),
|
|
m_other_areal,
|
|
m_point_in_areal_strategy);
|
|
|
|
// 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;
|
|
PointInArealStrategy const& m_point_in_areal_strategy;
|
|
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, typename Strategy>
|
|
static inline void apply(Geometry1 const& geometry1, Geometry2 const& geometry2,
|
|
Result & result,
|
|
Strategy const& strategy)
|
|
{
|
|
// 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 ( BOOST_GEOMETRY_CONDITION(result.interrupt) )
|
|
return;
|
|
|
|
// get and analyse turns
|
|
typedef typename turns::get_turns
|
|
<
|
|
Geometry1, Geometry2
|
|
>::template turn_info_type<Strategy>::type 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, strategy);
|
|
if ( BOOST_GEOMETRY_CONDITION(result.interrupt) )
|
|
return;
|
|
|
|
typedef typename Strategy::cs_tag cs_tag;
|
|
|
|
no_turns_aa_pred<Geometry2, Result, Strategy, false>
|
|
pred1(geometry2, result, strategy);
|
|
for_each_disjoint_geometry_if<0, Geometry1>::apply(turns.begin(), turns.end(), geometry1, pred1);
|
|
if ( BOOST_GEOMETRY_CONDITION(result.interrupt) )
|
|
return;
|
|
|
|
no_turns_aa_pred<Geometry1, Result, Strategy, true>
|
|
pred2(geometry1, result, strategy);
|
|
for_each_disjoint_geometry_if<1, Geometry2>::apply(turns.begin(), turns.end(), geometry2, pred2);
|
|
if ( BOOST_GEOMETRY_CONDITION(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>, cs_tag> 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(), strategy);
|
|
|
|
if ( BOOST_GEOMETRY_CONDITION(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, Strategy>
|
|
rings_analyser(result, geometry1, geometry2, strategy);
|
|
analyse_uncertain_rings<0>::apply(rings_analyser, turns.begin(), turns.end());
|
|
|
|
if ( BOOST_GEOMETRY_CONDITION(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>, cs_tag> 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(), strategy);
|
|
|
|
if ( BOOST_GEOMETRY_CONDITION(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, Strategy>
|
|
rings_analyser(result, geometry2, geometry1, strategy);
|
|
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 )
|
|
{
|
|
// not correct e.g. for G1 touching G2 in a point where a hole is touching the exterior ring
|
|
// in this case 2 turns i/... and u/u will be generated for this IP
|
|
//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, typename EqPPStrategy>
|
|
void apply(Result & result, TurnIt it, EqPPStrategy const& strategy)
|
|
{
|
|
//BOOST_GEOMETRY_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, strategy) )
|
|
{
|
|
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, strategy) )
|
|
{
|
|
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 )
|
|
{
|
|
// this was set in the interrupt policy but it was wrong
|
|
// also here it's wrong since it may be a fake entry point
|
|
//update<interior, interior, '2', transpose_result>(result);
|
|
|
|
// already set in interrupt policy
|
|
//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_GEOMETRY_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<interior, interior, '2', transpose_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,
|
|
typename EqPPStrategy
|
|
>
|
|
static inline void analyse_each_turn(Result & res,
|
|
Analyser & analyser,
|
|
TurnIt first, TurnIt last,
|
|
EqPPStrategy const& strategy)
|
|
{
|
|
if ( first == last )
|
|
return;
|
|
|
|
for ( TurnIt it = first ; it != last ; ++it )
|
|
{
|
|
analyser.apply(res, it, strategy);
|
|
|
|
if ( BOOST_GEOMETRY_CONDITION(res.interrupt) )
|
|
return;
|
|
}
|
|
|
|
analyser.apply(res);
|
|
}
|
|
|
|
template
|
|
<
|
|
std::size_t OpId,
|
|
typename Result,
|
|
typename Geometry,
|
|
typename OtherGeometry,
|
|
typename PointInArealStrategy
|
|
>
|
|
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,
|
|
PointInArealStrategy const& point_in_areal_strategy)
|
|
: geometry(geom)
|
|
, other_geometry(other_geom)
|
|
, interrupt(result.interrupt) // just in case, could be false as well
|
|
, m_result(result)
|
|
, m_point_in_areal_strategy(point_in_areal_strategy)
|
|
, m_flags(0)
|
|
{
|
|
// check which relations must be analysed
|
|
// NOTE: 1 and 4 could probably be connected
|
|
|
|
if ( ! may_update<interior, interior, '2', 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 == 7 )
|
|
{
|
|
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 gives O(NM)
|
|
using detail::within::point_in_geometry;
|
|
int const pig = point_in_geometry(range::front(range_ref),
|
|
other_geometry,
|
|
m_point_in_areal_strategy);
|
|
|
|
//BOOST_GEOMETRY_ASSERT(pig != 0);
|
|
if ( pig > 0 )
|
|
{
|
|
update<interior, interior, '2', transpose_result>(m_result);
|
|
m_flags |= 1;
|
|
|
|
update<boundary, interior, '1', transpose_result>(m_result);
|
|
update<exterior, interior, '2', transpose_result>(m_result);
|
|
m_flags |= 4;
|
|
}
|
|
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 )
|
|
{
|
|
found_ii = true;
|
|
}
|
|
else if ( it->operations[0].operation == overlay::operation_union
|
|
&& it->operations[1].operation == overlay::operation_union )
|
|
{
|
|
found_uu = true;
|
|
}
|
|
else // ignore
|
|
{
|
|
return; // don't interrupt
|
|
}
|
|
}
|
|
|
|
// only i/i was generated for this ring
|
|
if ( found_ii )
|
|
{
|
|
update<interior, interior, '2', transpose_result>(m_result);
|
|
m_flags |= 1;
|
|
|
|
//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;
|
|
}
|
|
|
|
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;
|
|
PointInArealStrategy const& m_point_in_areal_strategy;
|
|
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;
|
|
|
|
signed_size_type
|
|
count = boost::numeric_cast<signed_size_type>(
|
|
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,
|
|
signed_size_type first,
|
|
signed_size_type 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
|