[relate] Refactor relate utilities and implementation.

Implement boundary_checker and follow helpers for geometry collection.
They are commented-out for now because they are not used anywhere.
The code is there in case they were needed in the future.
This commit is contained in:
Adam Wulkiewicz 2022-05-03 14:16:45 +02:00
parent e2496e2fed
commit f80cacc4cb
11 changed files with 643 additions and 513 deletions

View File

@ -3,8 +3,8 @@
// Copyright (c) 2007-2014 Barend Gehrels, Amsterdam, the Netherlands.
// Copyright (c) 2017 Adam Wulkiewicz, Lodz, Poland.
// This file was modified by Oracle on 2014-2020.
// Modifications copyright (c) 2014-2020 Oracle and/or its affiliates.
// This file was modified by Oracle on 2014-2022.
// Modifications copyright (c) 2014-2022 Oracle and/or its affiliates.
// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle
// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
@ -438,8 +438,7 @@ public :
// for different ring id: c, i, u, x
typedef relate::turns::less
<
0, relate::turns::less_op_linear_areal_single<0>,
typename Strategy::cs_tag
0, relate::turns::less_op_linear_areal_single<0>, Strategy
> turn_less;
std::sort(boost::begin(turns), boost::end(turns), turn_less());

View File

@ -1,6 +1,6 @@
// Boost.Geometry (aka GGL, Generic Geometry Library)
// Copyright (c) 2014-2020, Oracle and/or its affiliates.
// Copyright (c) 2014-2022, Oracle and/or its affiliates.
// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle
// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
@ -160,14 +160,10 @@ protected:
detail::get_turns::no_interrupt_policy interrupt_policy;
using point_type = typename geometry::point_type<LinearGeometry1>::type;
using mutable_point_type = typename helper_geometry<point_type>::type;
geometry::detail::relate::turns::get_turns
<
LinearGeometry1,
LinearGeometry2,
mutable_point_type,
detail::get_turns::get_turn_info_type
<
LinearGeometry1,
@ -235,13 +231,10 @@ public:
OutputIterator oit,
Strategy const& strategy)
{
using point_type = typename geometry::point_type<Linear1>::type;
using mutable_point_type = typename helper_geometry<point_type>::type;
typedef typename detail::relate::turns::get_turns
<
Linear1,
Linear2,
mutable_point_type,
detail::get_turns::get_turn_info_type
<
Linear1,

View File

@ -208,9 +208,6 @@ struct areal_areal
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,
@ -225,23 +222,18 @@ struct areal_areal
return;
// get and analyse turns
using point_type = typename geometry::point_type<Geometry1>::type;
using mutable_point_type = typename helper_geometry<point_type>::type;
typedef typename turns::get_turns
using turn_type = typename turns::get_turns
<
Geometry1, Geometry2, mutable_point_type
>::template turn_info_type<Strategy>::type turn_type;
Geometry1, Geometry2
>::template turn_info_type<Strategy>::type;
std::vector<turn_type> turns;
interrupt_policy_areal_areal<Result> interrupt_policy(geometry1, geometry2, result);
turns::get_turns<Geometry1, Geometry2, mutable_point_type>::apply(turns, geometry1, geometry2, interrupt_policy, strategy);
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);
@ -264,8 +256,8 @@ struct areal_areal
|| 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());
using less_t = turns::less<0, turns::less_op_areal_areal<0>, Strategy>;
std::sort(turns.begin(), turns.end(), less_t());
/*if ( may_update<interior, exterior, '2'>(result)
|| may_update<boundary, exterior, '1'>(result)
@ -304,8 +296,8 @@ struct areal_areal
|| 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());
using less_t = turns::less<1, turns::less_op_areal_areal<1>, Strategy>;
std::sort(turns.begin(), turns.end(), less_t());
/*if ( may_update<interior, exterior, '2', true>(result)
|| may_update<boundary, exterior, '1', true>(result)
@ -357,9 +349,7 @@ struct areal_areal
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)
for (auto it = boost::begin(turns) ; it != boost::end(turns) ; ++it)
{
per_turn<0>(*it);
per_turn<1>(*it);
@ -657,10 +647,9 @@ struct areal_areal
return;
}
typename detail::sub_range_return_type<Geometry const>::type
range_ref = detail::sub_range(geometry, seg_id);
auto const& sub_range = detail::sub_range(geometry, seg_id);
if ( boost::empty(range_ref) )
if ( boost::empty(sub_range) )
{
// TODO: throw an exception?
return; // ignore
@ -673,7 +662,7 @@ struct areal_areal
// 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),
int const pig = point_in_geometry(range::front(sub_range),
other_geometry,
m_point_in_areal_strategy);

View File

@ -1,6 +1,6 @@
// Boost.Geometry (aka GGL, Generic Geometry Library)
// Copyright (c) 2014-2020 Oracle and/or its affiliates.
// Copyright (c) 2014-2022 Oracle and/or its affiliates.
// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
@ -15,6 +15,7 @@
#include <boost/geometry/algorithms/detail/equals/point_point.hpp>
#include <boost/geometry/algorithms/detail/sub_range.hpp>
//#include <boost/geometry/algorithms/detail/visit.hpp>
#include <boost/geometry/algorithms/num_points.hpp>
#include <boost/geometry/geometries/helper_geometry.hpp>
@ -28,9 +29,9 @@ namespace boost { namespace geometry
{
#ifndef DOXYGEN_NO_DETAIL
namespace detail { namespace relate {
namespace detail { namespace relate
{
enum boundary_query { boundary_front, boundary_back, boundary_any };
template
<
@ -43,31 +44,26 @@ class boundary_checker {};
template <typename Geometry, typename Strategy>
class boundary_checker<Geometry, Strategy, linestring_tag>
{
using mutable_point_type
= typename helper_geometry<typename point_type<Geometry>::type>::type;
public:
boundary_checker(Geometry const& g, Strategy const& s)
: m_has_boundary( boost::size(g) >= 2
&& ! detail::equals::equals_point_point(range::front(g),
range::back(g),
s) )
: m_has_boundary(
boost::size(g) >= 2
&& ! detail::equals::equals_point_point(range::front(g), range::back(g), s))
#ifdef BOOST_GEOMETRY_DEBUG_RELATE_BOUNDARY_CHECKER
, m_geometry(g)
#endif
, m_strategy(s)
{}
template <boundary_query BoundaryQuery, typename Point>
template <typename Point>
bool is_endpoint_boundary(Point const& pt) const
{
boost::ignore_unused(pt);
#ifdef BOOST_GEOMETRY_DEBUG_RELATE_BOUNDARY_CHECKER
// may give false positives for INT
BOOST_GEOMETRY_ASSERT( (BoundaryQuery == boundary_front || BoundaryQuery == boundary_any)
&& detail::equals::equals_point_point(pt, range::front(m_geometry), m_strategy)
|| (BoundaryQuery == boundary_back || BoundaryQuery == boundary_any)
&& detail::equals::equals_point_point(pt, range::back(m_geometry), m_strategy) );
BOOST_GEOMETRY_ASSERT(
detail::equals::equals_point_point(pt, range::front(m_geometry), m_strategy)
|| detail::equals::equals_point_point(pt, range::back(m_geometry), m_strategy));
#endif
return m_has_boundary;
}
@ -85,6 +81,67 @@ private:
Strategy const& m_strategy;
};
template <typename Point, typename Strategy, typename Out>
inline void copy_pts_boundary_points(Point const& front_pt, Point const& back_pt,
Strategy const& strategy, Out & boundary_points)
{
using mutable_point_type = typename Out::value_type;
// linear ring or point - no boundary
if (! equals::equals_point_point(front_pt, back_pt, strategy))
{
// do not add points containing NaN coordinates
// because they cannot be reasonably compared, e.g. with MSVC
// an assertion failure is reported in std::equal_range()
if (! geometry::has_nan_coordinate(front_pt))
{
mutable_point_type pt;
geometry::convert(front_pt, pt);
boundary_points.push_back(front_pt);
}
if (! geometry::has_nan_coordinate(back_pt))
{
mutable_point_type pt;
geometry::convert(back_pt, pt);
boundary_points.push_back(back_pt);
}
}
}
template <typename Segment, typename Strategy, typename Out>
inline void copy_seg_boundary_points(Segment const& seg, Strategy const& strategy,
Out & boundary_points)
{
typename Out::value_type front_pt, back_pt;
assign_point_from_index<0>(seg, front_pt);
assign_point_from_index<1>(seg, back_pt);
copy_pts_boundary_points(front_pt, back_pt, strategy, boundary_points);
}
template <typename Linestring, typename Strategy, typename Out>
inline void copy_ls_boundary_points(Linestring const& ls, Strategy const& strategy,
Out & boundary_points)
{
// empty or point - no boundary
if (boost::size(ls) >= 2)
{
auto const& front_pt = range::front(ls);
auto const& back_pt = range::back(ls);
copy_pts_boundary_points(front_pt, back_pt, strategy, boundary_points);
}
}
template <typename MultiLinestring, typename Strategy, typename Out>
inline void copy_mls_boundary_points(MultiLinestring const& mls, Strategy const& strategy,
Out & boundary_points)
{
for (auto it = boost::begin(mls); it != boost::end(mls); ++it)
{
copy_ls_boundary_points(*it, strategy, boundary_points);
}
}
template <typename Geometry, typename Strategy>
class boundary_checker<Geometry, Strategy, multi_linestring_tag>
{
@ -98,72 +155,36 @@ public:
// First call O(NlogN)
// Each next call O(logN)
template <boundary_query BoundaryQuery, typename Point>
template <typename Point>
bool is_endpoint_boundary(Point const& pt) const
{
using less_type = geometry::less<mutable_point_type, -1, typename Strategy::cs_tag>;
auto const multi_count = boost::size(m_geometry);
if ( multi_count < 1 )
if (multi_count < 1)
{
return false;
}
if ( ! m_is_filled )
if (! m_is_filled)
{
//boundary_points.clear();
m_boundary_points.reserve(multi_count * 2);
for (auto it = boost::begin(m_geometry); it != boost::end(m_geometry); ++it)
{
auto const& ls = *it;
copy_mls_boundary_points(m_geometry, m_strategy, m_boundary_points);
// empty or only one point - no boundary
if (boost::size(ls) < 2)
{
continue;
}
auto const& front_pt = range::front(ls);
auto const& back_pt = range::back(ls);
// linear ring or point - no boundary
if (! equals::equals_point_point(front_pt, back_pt, m_strategy))
{
// do not add points containing NaN coordinates
// because they cannot be reasonably compared, e.g. with MSVC
// an assertion failure is reported in std::equal_range()
if (! geometry::has_nan_coordinate(front_pt))
{
mutable_point_type pt;
geometry::convert(front_pt, pt);
m_boundary_points.push_back(pt);
}
if (! geometry::has_nan_coordinate(back_pt))
{
mutable_point_type pt;
geometry::convert(back_pt, pt);
m_boundary_points.push_back(pt);
}
}
}
std::sort(m_boundary_points.begin(),
m_boundary_points.end(),
less_type());
std::sort(m_boundary_points.begin(), m_boundary_points.end(), less_type());
m_is_filled = true;
}
auto const equal_points_count
= boost::size(
std::equal_range(m_boundary_points.begin(),
m_boundary_points.end(),
pt,
less_type())
);
auto const equal_range = std::equal_range(m_boundary_points.begin(),
m_boundary_points.end(),
pt,
less_type());
std::size_t const equal_points_count = boost::size(equal_range);
return equal_points_count % 2 != 0;// && equal_points_count > 0; // the number is odd and > 0
}
@ -181,6 +202,98 @@ private:
Strategy const& m_strategy;
};
// NOTE: In case it was needed in the future. Commented-out for now.
/*
template <typename Geometry, typename Strategy>
class boundary_checker<Geometry, Strategy, geometry_collection_tag>
{
using point_type = typename point_type<Geometry>::type;
using mutable_point_type = typename helper_geometry<point_type>::type;
public:
boundary_checker(Geometry const& g, Strategy const& s)
: m_is_filled(false), m_geometry(g), m_strategy(s)
{}
// First call O(NlogN)
// Each next call O(logN)
template <typename Point>
bool is_endpoint_boundary(Point const& pt) const
{
using less_t = geometry::less<mutable_point_type, -1, typename Strategy::cs_tag>;
auto multi_count = boost::size(m_geometry);
if (multi_count < 1)
{
return false;
}
if (! m_is_filled)
{
//boundary_points.clear();
m_boundary_points.reserve(multi_count * 2);
detail::visit_breadth_first([&](auto const& g)
{
add_boundary_points(g);
return true;
}, m_geometry);
std::sort(m_boundary_points.begin(),
m_boundary_points.end(),
less_t());
m_is_filled = true;
}
auto const equal_range = std::equal_range(m_boundary_points.begin(),
m_boundary_points.end(),
pt,
less_t());
std::size_t const equal_points_count = boost::size(equal_range);
return equal_points_count % 2 != 0;// && equal_points_count > 0; // the number is odd and > 0
}
Strategy const& strategy() const
{
return m_strategy;
}
private:
template <typename Geom, std::enable_if_t<util::is_multi_linestring<Geom>::value, int> = 0>
void add_boundary_points(Geom const& geom) const
{
copy_mls_boundary_points(geom, m_strategy, m_boundary_points);
}
template <typename Geom, std::enable_if_t<util::is_linestring<Geom>::value, int> = 0>
void add_boundary_points(Geom const& geom) const
{
copy_ls_boundary_points(geom, m_strategy, m_boundary_points);
}
template <typename Geom, std::enable_if_t<util::is_segment<Geom>::value, int> = 0>
void add_boundary_points(Geom const& geom) const
{
copy_seg_boundary_points(geom, m_strategy, m_boundary_points);
}
template <typename Geom, std::enable_if_t<! util::is_linear<Geom>::value, int> = 0>
void add_boundary_points(Geom const& ) const
{}
mutable bool m_is_filled;
// TODO: store references/pointers instead of converted points?
mutable std::vector<mutable_point_type> m_boundary_points;
Geometry const& m_geometry;
Strategy const& m_strategy;
};
*/
}} // namespace detail::relate
#endif // DOXYGEN_NO_DETAIL

View File

@ -2,8 +2,8 @@
// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
// This file was modified by Oracle on 2013, 2014, 2018.
// Modifications copyright (c) 2013-2018 Oracle and/or its affiliates.
// This file was modified by Oracle on 2013-2022.
// Modifications copyright (c) 2013-2022 Oracle and/or its affiliates.
// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
@ -52,14 +52,12 @@ template <std::size_t OpId, typename Geometry, typename Tag>
struct for_each_disjoint_geometry_if<OpId, Geometry, Tag, false>
{
template <typename TurnIt, typename Pred>
static inline bool apply(TurnIt first, TurnIt last,
Geometry const& geometry,
Pred & pred)
static void apply(TurnIt first, TurnIt last, Geometry const& geometry, Pred & pred)
{
if ( first != last )
return false;
pred(geometry);
return true;
if (first == last)
{
pred(geometry);
}
}
};
@ -67,49 +65,43 @@ template <std::size_t OpId, typename Geometry, typename Tag>
struct for_each_disjoint_geometry_if<OpId, Geometry, Tag, true>
{
template <typename TurnIt, typename Pred>
static inline bool apply(TurnIt first, TurnIt last,
Geometry const& geometry,
Pred & pred)
static void apply(TurnIt first, TurnIt last, Geometry const& geometry, Pred & pred)
{
if ( first != last )
return for_turns(first, last, geometry, pred);
if (first == last)
{
for_empty(geometry, pred);
}
else
return for_empty(geometry, pred);
{
for_turns(first, last, geometry, pred);
}
}
template <typename Pred>
static inline bool for_empty(Geometry const& geometry,
Pred & pred)
static void for_empty(Geometry const& geometry, Pred & pred)
{
typedef typename boost::range_iterator<Geometry const>::type iterator;
// O(N)
// check predicate for each contained geometry without generated turn
for ( iterator it = boost::begin(geometry) ;
it != boost::end(geometry) ; ++it )
for (auto it = boost::begin(geometry); it != boost::end(geometry) ; ++it)
{
bool cont = pred(*it);
if ( !cont )
if (! pred(*it))
{
break;
}
}
return !boost::empty(geometry);
}
template <typename TurnIt, typename Pred>
static inline bool for_turns(TurnIt first, TurnIt last,
Geometry const& geometry,
Pred & pred)
static void for_turns(TurnIt first, TurnIt last, Geometry const& geometry, Pred & pred)
{
BOOST_GEOMETRY_ASSERT(first != last);
const std::size_t count = boost::size(geometry);
boost::ignore_unused(count);
// O(I)
// gather info about turns generated for contained geometries
std::vector<bool> detected_intersections(count, false);
for ( TurnIt it = first ; it != last ; ++it )
for (TurnIt it = first; it != last; ++it)
{
signed_size_type multi_index = it->operations[OpId].seg_id.multi_index;
BOOST_GEOMETRY_ASSERT(multi_index >= 0);
@ -118,28 +110,167 @@ struct for_each_disjoint_geometry_if<OpId, Geometry, Tag, true>
detected_intersections[index] = true;
}
bool found = false;
// O(N)
// check predicate for each contained geometry without generated turn
for ( std::vector<bool>::iterator it = detected_intersections.begin() ;
it != detected_intersections.end() ; ++it )
for (std::size_t index = 0; index < detected_intersections.size(); ++index)
{
// if there were no intersections for this multi_index
if ( *it == false )
if (detected_intersections[index] == false)
{
found = true;
std::size_t const index = std::size_t(std::distance(detected_intersections.begin(), it));
bool cont = pred(range::at(geometry, index));
if ( !cont )
if (! pred(range::at(geometry, index)))
{
break;
}
}
}
return found;
}
};
// In case it was needed in the future. Commented-out for now.
/*
// GC is expected to support random access, i.e. random_access_view has to be passed.
// Predicate is called for each segmental single geometry.
// Point-like geometries and boxes are ignored.
template <std::size_t OpId, typename RandomGC>
struct for_each_disjoint_geometry_if<OpId, RandomGC, geometry_collection_tag, true>
{
struct detected_data
{
void set_multi_index(std::size_t multi_index)
{
if (multi_index >= multi.size())
{
multi.resize(multi_index + 1, false);
}
multi[multi_index] = true;
}
std::vector<bool> multi;
bool is_detected = false;
};
template <typename TurnIt, typename Pred>
static void apply(TurnIt first, TurnIt last, RandomGC const& random_gc, Pred & pred)
{
return first != last
? for_turns(first, last, random_gc, pred)
: for_empty(random_gc, pred);
}
template <typename Pred>
static void for_empty(RandomGC const& random_gc, Pred & pred)
{
auto const end = boost::end(random_gc);
for (auto it = boost::begin(random_gc); it != end; ++it)
{
bool ok = true;
traits::iter_visit<RandomGC>::apply([&](auto const& g)
{
ok = call_pred(pred, g);
}, it);
if (! ok)
{
break;
}
}
}
template <typename TurnIt, typename Pred>
static void for_turns(TurnIt first, TurnIt last, RandomGC const& random_gc, Pred & pred)
{
BOOST_GEOMETRY_ASSERT(first != last);
const std::size_t count = boost::size(random_gc);
std::vector<detected_data> detected_intersections(count);
for (TurnIt it = first; it != last; ++it)
{
auto const& op = it->operations[OpId];
auto const& seg_id = op.seg_id;
BOOST_GEOMETRY_ASSERT(op.gc_index >= 0);
detected_intersections[op.gc_index].is_detected = true;
if (seg_id.multi_index >= 0)
{
detected_intersections[op.gc_index].set_multi_index(std::size_t(seg_id.multi_index));
}
}
auto const begin = boost::begin(random_gc);
auto const end = boost::end(random_gc);
for (auto it = begin; it != end; ++it)
{
bool ok = true;
traits::iter_visit<RandomGC>::apply([&](auto const& g)
{
std::size_t gc_index = it - begin;
// either single or the whole multi was not detected
if (detected_intersections[gc_index].is_detected == false)
{
ok = call_pred(pred, g);
}
else
{
ok = check_multi_elements(pred, g, detected_intersections[gc_index].multi);
}
}, it);
if (! ok)
{
break;
}
}
}
template <typename Pred, typename Geometry, std::enable_if_t<util::is_segmental<Geometry>::value && util::is_multi<Geometry>::value, int> = 0>
static bool call_pred(Pred & pred, Geometry const& geometry)
{
for (auto it = boost::begin(geometry); it != boost::end(geometry); ++it)
{
if (! pred(*it))
{
return false;
}
}
return true;
}
template <typename Pred, typename Geometry, std::enable_if_t<util::is_segmental<Geometry>::value && util::is_single<Geometry>::value, int> = 0>
static bool call_pred(Pred & pred, Geometry const& geometry)
{
return pred(geometry);
}
template <typename Pred, typename Geometry, std::enable_if_t<! util::is_segmental<Geometry>::value, int> = 0>
static bool call_pred(Pred & , Geometry const&)
{
return true;
}
template <typename Pred, typename Geometry, std::enable_if_t<util::is_segmental<Geometry>::value && util::is_multi<Geometry>::value, int> = 0>
static bool check_multi_elements(Pred & pred, Geometry const& geometry, std::vector<bool> const& detected_multi)
{
std::size_t size = boost::size(geometry);
for (std::size_t multi_index = 0; multi_index < size; ++multi_index)
{
if (multi_index >= detected_multi.size() || detected_multi[multi_index] == false)
{
if (! pred(range::at(geometry, multi_index)))
{
return false;
}
}
}
return true;
}
template <typename Pred, typename Geometry, std::enable_if_t<! util::is_segmental<Geometry>::value || ! util::is_multi<Geometry>::value, int> = 0>
static bool check_multi_elements(Pred & , Geometry const& , std::vector<bool> const& )
{
return true;
}
};
*/
// WARNING! This class stores pointers!
// Passing a reference to local variable will result in undefined behavior!
template <typename Point>
@ -365,44 +496,17 @@ inline bool turn_on_the_same_ip(Turn const& prev_turn, Turn const& curr_turn,
return detail::equals::equals_point_point(prev_turn.point, curr_turn.point, strategy);
}
template <boundary_query BoundaryQuery,
typename Point,
typename BoundaryChecker>
inline bool is_endpoint_on_boundary(Point const& pt,
BoundaryChecker & boundary_checker)
template <typename IntersectionPoint, typename OperationInfo, typename BoundaryChecker>
static inline bool is_ip_on_boundary(IntersectionPoint const& ip,
OperationInfo const& operation_info,
BoundaryChecker const& boundary_checker)
{
return boundary_checker.template is_endpoint_boundary<BoundaryQuery>(pt);
}
template <boundary_query BoundaryQuery,
typename IntersectionPoint,
typename OperationInfo,
typename BoundaryChecker>
inline bool is_ip_on_boundary(IntersectionPoint const& ip,
OperationInfo const& operation_info,
BoundaryChecker & boundary_checker,
segment_identifier const& seg_id)
{
boost::ignore_unused(seg_id);
bool res = false;
// IP on the last point of the linestring
if ( BOOST_GEOMETRY_CONDITION(BoundaryQuery == boundary_back || BoundaryQuery == boundary_any)
&& operation_info.position == overlay::position_back )
{
// check if this point is a boundary
res = boundary_checker.template is_endpoint_boundary<boundary_back>(ip);
}
// IP on the last point of the linestring
else if ( BOOST_GEOMETRY_CONDITION(BoundaryQuery == boundary_front || BoundaryQuery == boundary_any)
&& operation_info.position == overlay::position_front )
{
// check if this point is a boundary
res = boundary_checker.template is_endpoint_boundary<boundary_front>(ip);
}
return res;
// IP on the first or the last point of the linestring
return (operation_info.position == overlay::position_back
|| operation_info.position == overlay::position_front)
// check if this point is a boundary
? boundary_checker.is_endpoint_boundary(ip)
: false;
}

View File

@ -2,9 +2,8 @@
// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
// This file was modified by Oracle on 2013-2021.
// Modifications copyright (c) 2013-2021 Oracle and/or its affiliates.
// This file was modified by Oracle on 2013-2022.
// Modifications copyright (c) 2013-2022 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,
@ -15,16 +14,15 @@
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_IMPLEMENTATION_HPP
#include <boost/geometry/core/tags.hpp>
#include <boost/geometry/algorithms/detail/relate/interface.hpp>
#include <boost/geometry/algorithms/detail/relate/point_point.hpp>
#include <boost/geometry/algorithms/detail/relate/point_geometry.hpp>
#include <boost/geometry/algorithms/detail/relate/linear_linear.hpp>
#include <boost/geometry/algorithms/detail/relate/linear_areal.hpp>
#include <boost/geometry/algorithms/detail/relate/multi_point_geometry.hpp>
#include <boost/geometry/algorithms/detail/relate/areal_areal.hpp>
#include <boost/geometry/algorithms/detail/relate/interface.hpp>
#include <boost/geometry/algorithms/detail/relate/linear_areal.hpp>
#include <boost/geometry/algorithms/detail/relate/linear_linear.hpp>
#include <boost/geometry/algorithms/detail/relate/multi_point_geometry.hpp>
#include <boost/geometry/algorithms/detail/relate/point_geometry.hpp>
#include <boost/geometry/algorithms/detail/relate/point_point.hpp>
#include <boost/geometry/core/tags.hpp>
#include <boost/geometry/strategies/relate/cartesian.hpp>
#include <boost/geometry/strategies/relate/geographic.hpp>
@ -116,6 +114,7 @@ struct relate<Areal1, Areal2, Tag1, Tag2, 2, 2, true>
: detail::relate::areal_areal<Areal1, Areal2>
{};
} // namespace dispatch
#endif // DOXYGEN_NO_DISPATCH

View File

@ -2,8 +2,8 @@
// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
// This file was modified by Oracle on 2013-2021.
// Modifications copyright (c) 2013-2021 Oracle and/or its affiliates.
// This file was modified by Oracle on 2013-2022.
// Modifications copyright (c) 2013-2022 Oracle and/or its affiliates.
// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
@ -53,7 +53,7 @@ template
<
typename Geometry2,
typename Result,
typename PointInArealStrategy,
typename Strategy,
typename BoundaryChecker,
bool TransposeResult
>
@ -62,11 +62,11 @@ class no_turns_la_linestring_pred
public:
no_turns_la_linestring_pred(Geometry2 const& geometry2,
Result & res,
PointInArealStrategy const& point_in_areal_strategy,
Strategy const& strategy,
BoundaryChecker const& boundary_checker)
: m_geometry2(geometry2)
, m_result(res)
, m_point_in_areal_strategy(point_in_areal_strategy)
, m_strategy(strategy)
, m_boundary_checker(boundary_checker)
, m_interrupt_flags(0)
{
@ -112,7 +112,7 @@ public:
int const pig = detail::within::point_in_geometry(range::front(linestring),
m_geometry2,
m_point_in_areal_strategy);
m_strategy);
//BOOST_GEOMETRY_ASSERT_MSG(pig != 0, "There should be no IPs");
if ( pig > 0 )
@ -127,11 +127,9 @@ public:
}
// check if there is a boundary
if ( ( m_interrupt_flags & 0xC ) != 0xC // if wasn't already set
&& ( m_boundary_checker.template
is_endpoint_boundary<boundary_front>(range::front(linestring))
|| m_boundary_checker.template
is_endpoint_boundary<boundary_back>(range::back(linestring)) ) )
if ((m_interrupt_flags & 0xC) != 0xC // if wasn't already set
&& (m_boundary_checker.is_endpoint_boundary(range::front(linestring))
|| m_boundary_checker.is_endpoint_boundary(range::back(linestring))))
{
if ( pig > 0 )
{
@ -152,7 +150,7 @@ public:
private:
Geometry2 const& m_geometry2;
Result & m_result;
PointInArealStrategy const& m_point_in_areal_strategy;
Strategy const& m_strategy;
BoundaryChecker const& m_boundary_checker;
unsigned m_interrupt_flags;
};
@ -200,13 +198,134 @@ private:
bool const interrupt;
};
template <typename It, typename Strategy>
inline It find_next_non_duplicated(It first, It current, It last, Strategy const& strategy)
{
BOOST_GEOMETRY_ASSERT(current != last);
It it = current;
for (++it ; it != last ; ++it)
{
if (! equals::equals_point_point(*current, *it, strategy))
{
return it;
}
}
// if not found start from the beginning
for (it = first ; it != current ; ++it)
{
if (! equals::equals_point_point(*current, *it, strategy))
{
return it;
}
}
return current;
}
// calculate inside or outside based on side_calc
// this is simplified version of a check from equal<>
template
<
typename Pi, typename Pj, typename Pk,
typename Qi, typename Qj, typename Qk,
typename Strategy
>
inline bool calculate_from_inside_sides(Pi const& pi, Pj const& pj, Pk const& pk,
Qi const& qi, Qj const& qj, Qk const& qk,
Strategy const& strategy)
{
auto const side_strategy = strategy.side();
int const side_pk_p = side_strategy.apply(pi, pj, pk);
int const side_qk_p = side_strategy.apply(pi, pj, qk);
// If they turn to same side (not opposite sides)
if (! overlay::base_turn_handler::opposite(side_pk_p, side_qk_p))
{
int const side_pk_q2 = side_strategy.apply(qj, qk, pk);
return side_pk_q2 == -1;
}
else
{
return side_pk_p == -1;
}
}
// check if the passed turn's segment of Linear geometry arrived
// from the inside or the outside of the Areal geometry
template
<
std::size_t OpId,
typename Geometry1, typename Geometry2,
typename Turn, typename Strategy
>
inline bool calculate_from_inside(Geometry1 const& geometry1,
Geometry2 const& geometry2,
Turn const& turn,
Strategy const& strategy)
{
static const std::size_t op_id = OpId;
static const std::size_t other_op_id = (OpId + 1) % 2;
if (turn.operations[op_id].position == overlay::position_front)
{
return false;
}
auto const& range1 = sub_range(geometry1, turn.operations[op_id].seg_id);
using range2_view = detail::closed_clockwise_view<typename ring_type<Geometry2>::type const>;
using range2_iterator = typename boost::range_iterator<range2_view const>::type;
range2_view const range2(sub_range(geometry2, turn.operations[other_op_id].seg_id));
BOOST_GEOMETRY_ASSERT(boost::size(range1));
std::size_t const s2 = boost::size(range2);
BOOST_GEOMETRY_ASSERT(s2 > 2);
std::size_t const seg_count2 = s2 - 1;
std::size_t const p_seg_ij = static_cast<std::size_t>(turn.operations[op_id].seg_id.segment_index);
std::size_t const q_seg_ij = static_cast<std::size_t>(turn.operations[other_op_id].seg_id.segment_index);
BOOST_GEOMETRY_ASSERT(p_seg_ij + 1 < boost::size(range1));
BOOST_GEOMETRY_ASSERT(q_seg_ij + 1 < s2);
auto const& pi = range::at(range1, p_seg_ij);
auto const& qi = range::at(range2, q_seg_ij);
auto const& qj = range::at(range2, q_seg_ij + 1);
bool const is_ip_qj = equals::equals_point_point(turn.point, qj, strategy);
// TODO: test this!
// BOOST_GEOMETRY_ASSERT(!equals::equals_point_point(turn.point, pi));
// BOOST_GEOMETRY_ASSERT(!equals::equals_point_point(turn.point, qi));
if (is_ip_qj)
{
std::size_t const q_seg_jk = (q_seg_ij + 1) % seg_count2;
// TODO: the following function should return immediately, however the worst case complexity is O(N)
// It would be good to replace it with some O(1) mechanism
range2_iterator qk_it = find_next_non_duplicated(boost::begin(range2),
range::pos(range2, q_seg_jk),
boost::end(range2),
strategy);
// Calculate sides in a different point order for P and Q
// Will this sequence of points be always correct?
return calculate_from_inside_sides(qi, turn.point, pi, qi, qj, *qk_it, strategy);
}
else
{
// Calculate sides with different points for P and Q
return calculate_from_inside_sides(qi, turn.point, pi, qi, turn.point, qj, strategy);
}
}
// The implementation of an algorithm calculating relate() for L/A
template <typename Geometry1, typename Geometry2, bool TransposeResult = false>
struct linear_areal
{
using point_type = typename geometry::point_type<Geometry1>::type;
using mutable_point_type = typename helper_geometry<point_type>::type;
// check Linear / Areal
BOOST_STATIC_ASSERT(topological_dimension<Geometry1>::value == 1
&& topological_dimension<Geometry2>::value == 2);
@ -215,7 +334,7 @@ struct linear_areal
template <typename Geom1, typename Geom2, typename Strategy>
struct multi_turn_info
: turns::get_turns<Geom1, Geom2, mutable_point_type>::template turn_info_type<Strategy>::type
: turns::get_turns<Geom1, Geom2>::template turn_info_type<Strategy>::type
{
multi_turn_info() : priority(0) {}
int priority; // single-geometry sorting priority
@ -227,7 +346,7 @@ struct linear_areal
<
util::is_multi<Geometry2>::value,
multi_turn_info<Geom1, Geom2, Strategy>,
typename turns::get_turns<Geom1, Geom2, mutable_point_type>::template turn_info_type<Strategy>::type
typename turns::get_turns<Geom1, Geom2>::template turn_info_type<Strategy>::type
>
{};
@ -236,7 +355,7 @@ struct linear_areal
Result & result,
Strategy const& strategy)
{
// TODO: If Areal geometry may have infinite size, change the following line:
// 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, TransposeResult>(result);// FFFFFFFFd, d in [1,9] or T
@ -245,12 +364,12 @@ struct linear_areal
return;
// get and analyse turns
typedef typename turn_info_type<Geometry1, Geometry2, Strategy>::type turn_type;
using turn_type = typename turn_info_type<Geometry1, Geometry2, Strategy>::type;
std::vector<turn_type> turns;
interrupt_policy_linear_areal<Geometry2, Result> interrupt_policy(geometry2, result);
turns::get_turns<Geometry1, Geometry2, mutable_point_type>::apply(turns, geometry1, geometry2, interrupt_policy, strategy);
turns::get_turns<Geometry1, Geometry2>::apply(turns, geometry1, geometry2, interrupt_policy, strategy);
if ( BOOST_GEOMETRY_CONDITION( result.interrupt ) )
return;
@ -293,7 +412,7 @@ struct linear_areal
return;
{
sort_dispatch<cs_tag>(turns.begin(), turns.end(), util::is_multi<Geometry2>());
sort_dispatch(turns.begin(), turns.end(), strategy, util::is_multi<Geometry2>());
turns_analyser<turn_type> analyser;
analyse_each_turn(result, analyser,
@ -325,12 +444,9 @@ struct linear_areal
// sort by multi_index and rind_index
std::sort(turns.begin(), turns.end(), less_ring());
typedef typename std::vector<turn_type>::iterator turn_iterator;
turn_iterator it = turns.begin();
segment_identifier * prev_seg_id_ptr = NULL;
// for each ring
for ( ; it != turns.end() ; )
for (auto it = turns.begin() ; it != turns.end() ; )
{
// it's the next single geometry
if ( prev_seg_id_ptr == NULL
@ -377,7 +493,7 @@ struct linear_areal
// find the next ring first iterator and check if the analysis should be performed
has_boundary_intersection has_boundary_inters;
turn_iterator next = find_next_ring(it, turns.end(), has_boundary_inters);
auto next = find_next_ring(it, turns.end(), has_boundary_inters);
// if there is no 1d overlap with the boundary
if ( !has_boundary_inters.result )
@ -390,12 +506,12 @@ struct linear_areal
else
{
// u, c
typedef turns::less<1, turns::less_op_areal_linear<1>, cs_tag> less;
std::sort(it, next, less());
using less_t = turns::less<1, turns::less_op_areal_linear<1>, Strategy>;
std::sort(it, next, less_t());
// analyse
areal_boundary_analyser<turn_type> analyser;
for ( turn_iterator rit = it ; rit != next ; ++rit )
for (auto rit = it ; rit != next ; ++rit)
{
// if the analyser requests, break the search
if ( !analyser.apply(it, rit, next, strategy) )
@ -518,12 +634,13 @@ struct linear_areal
}
};
template <typename CSTag, typename TurnIt>
static void sort_dispatch(TurnIt first, TurnIt last, std::true_type const& /*is_multi*/)
template <typename TurnIt, typename Strategy>
static void sort_dispatch(TurnIt first, TurnIt last, Strategy const& ,
std::true_type const& /*is_multi*/)
{
// sort turns by Linear seg_id, then by fraction, then by other multi_index
typedef turns::less<0, turns::less_other_multi_index<0>, CSTag> less;
std::sort(first, last, less());
using less_t = turns::less<0, turns::less_other_multi_index<0>, Strategy>;
std::sort(first, last, less_t());
// For the same IP and multi_index - the same other's single geometry
// set priorities as the least operation found for the whole single geometry
@ -536,20 +653,21 @@ struct linear_areal
// When priorities for single geometries are set now sort turns for the same IP
// if multi_index is the same sort them according to the single-less
// else use priority of the whole single-geometry set earlier
typedef turns::less<0, turns::less_op_linear_areal_single<0>, CSTag> single_less;
using single_less_t = turns::less<0, turns::less_op_linear_areal_single<0>, Strategy>;
for_each_equal_range(first, last,
sort_turns_group<single_less>(),
sort_turns_group<single_less_t>(),
same_ip());
}
template <typename CSTag, typename TurnIt>
static void sort_dispatch(TurnIt first, TurnIt last, std::false_type const& /*is_multi*/)
template <typename TurnIt, typename Strategy>
static void sort_dispatch(TurnIt first, TurnIt last, Strategy const& ,
std::false_type const& /*is_multi*/)
{
// sort turns by Linear seg_id, then by fraction, then
// for same ring id: x, u, i, c
// for different ring id: c, i, u, x
typedef turns::less<0, turns::less_op_linear_areal_single<0>, CSTag> less;
std::sort(first, last, less());
using less_t = turns::less<0, turns::less_op_linear_areal_single<0>, Strategy>;
std::sort(first, last, less_t());
}
@ -571,9 +689,7 @@ struct linear_areal
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)
for (auto it = boost::begin(turns) ; it != boost::end(turns) ; ++it)
{
if ( it->operations[0].operation == overlay::operation_intersection )
{
@ -621,37 +737,6 @@ struct linear_areal
static const std::size_t op_id = 0;
static const std::size_t other_op_id = 1;
template <typename Strategy,
typename Pi, typename Pj, typename Pk,
typename Qi, typename Qj, typename Qk>
struct la_side_calculator
{
typedef decltype(std::declval<Strategy>().side()) side_strategy_type;
inline la_side_calculator(Pi const& pi, Pj const& pj, Pk const& pk,
Qi const& qi, Qj const& qj, Qk const& qk,
Strategy const& strategy)
: m_pi(pi), m_pj(pj), m_pk(pk)
, m_qi(qi), m_qj(qj), m_qk(qk)
, m_side_strategy(strategy.side())
{}
inline int pk_wrt_p1() const { return m_side_strategy.apply(m_pi, m_pj, m_pk); }
inline int qk_wrt_p1() const { return m_side_strategy.apply(m_pi, m_pj, m_qk); }
inline int pk_wrt_q2() const { return m_side_strategy.apply(m_qj, m_qk, m_pk); }
private :
Pi const& m_pi;
Pj const& m_pj;
Pk const& m_pk;
Qi const& m_qi;
Qj const& m_qj;
Qk const& m_qk;
side_strategy_type m_side_strategy;
};
public:
turns_analyser()
: m_previous_turn_ptr(NULL)
@ -714,9 +799,8 @@ struct linear_areal
// NOTE: similar code is in the post-last-ip-apply()
segment_identifier const& prev_seg_id = m_previous_turn_ptr->operations[op_id].seg_id;
bool const prev_back_b = is_endpoint_on_boundary<boundary_back>(
range::back(sub_range(geometry, prev_seg_id)),
boundary_checker);
bool const prev_back_b = boundary_checker.is_endpoint_boundary(
range::back(sub_range(geometry, prev_seg_id)));
// if there is a boundary on the last point
if ( prev_back_b )
@ -809,9 +893,8 @@ struct linear_areal
{
segment_identifier const& prev_seg_id = m_previous_turn_ptr->operations[op_id].seg_id;
bool const prev_back_b = is_endpoint_on_boundary<boundary_back>(
range::back(sub_range(geometry, prev_seg_id)),
boundary_checker);
bool const prev_back_b = boundary_checker.is_endpoint_boundary(
range::back(sub_range(geometry, prev_seg_id)));
// if there is a boundary on the last point
if ( prev_back_b )
@ -890,11 +973,8 @@ struct linear_areal
update<interior, boundary, '1', TransposeResult>(res);
}
bool const this_b
= is_ip_on_boundary<boundary_front>(it->point,
it->operations[op_id],
boundary_checker,
seg_id);
bool const this_b = is_ip_on_boundary(it->point, it->operations[op_id],
boundary_checker);
// going inside on boundary point
if ( this_b )
{
@ -911,11 +991,11 @@ struct linear_areal
&& it->operations[op_id].position != overlay::position_front )
{
// TODO: calculate_from_inside() is only needed if the current Linestring is not closed
bool const from_inside = first_point
&& calculate_from_inside(geometry,
other_geometry,
*it,
strategy);
bool const from_inside =
first_point && calculate_from_inside<op_id>(geometry,
other_geometry,
*it,
strategy);
if ( from_inside )
update<interior, interior, '1', TransposeResult>(res);
@ -925,9 +1005,8 @@ struct linear_areal
// if it's the first IP then the first point is outside
if ( first_point )
{
bool const front_b = is_endpoint_on_boundary<boundary_front>(
range::front(sub_range(geometry, seg_id)),
boundary_checker);
bool const front_b = boundary_checker.is_endpoint_boundary(
range::front(sub_range(geometry, seg_id)));
// if there is a boundary on the first point
if ( front_b )
@ -974,7 +1053,7 @@ struct linear_areal
{
// check if this is indeed the boundary point
// NOTE: is_ip_on_boundary<>() should be called here but the result will be the same
if ( is_endpoint_on_boundary<boundary_back>(it->point, boundary_checker) )
if (boundary_checker.is_endpoint_boundary(it->point))
{
update<boundary, boundary, '0', TransposeResult>(res);
}
@ -989,10 +1068,8 @@ struct linear_areal
// we're outside or inside and this is the first turn
else
{
bool const this_b = is_ip_on_boundary<boundary_any>(it->point,
it->operations[op_id],
boundary_checker,
seg_id);
bool const this_b = is_ip_on_boundary(it->point, it->operations[op_id],
boundary_checker);
// if current IP is on boundary of the geometry
if ( this_b )
{
@ -1012,11 +1089,11 @@ struct linear_areal
// For LS/MultiPolygon multiple x/u turns may be generated
// the first checked Polygon may be the one which LS is outside for.
bool const first_point = first_in_range || m_first_from_unknown;
bool const first_from_inside = first_point
&& calculate_from_inside(geometry,
other_geometry,
*it,
strategy);
bool const first_from_inside =
first_point && calculate_from_inside<op_id>(geometry,
other_geometry,
*it,
strategy);
if ( first_from_inside )
{
update<interior, interior, '1', TransposeResult>(res);
@ -1044,9 +1121,8 @@ struct linear_areal
// first IP on the last segment point - this means that the first point is outside or inside
if ( first_point && ( !this_b || op_blocked ) )
{
bool const front_b = is_endpoint_on_boundary<boundary_front>(
range::front(sub_range(geometry, seg_id)),
boundary_checker);
bool const front_b = boundary_checker.is_endpoint_boundary(
range::front(sub_range(geometry, seg_id)));
// if there is a boundary on the first point
if ( front_b )
@ -1135,9 +1211,8 @@ struct linear_areal
segment_identifier const& prev_seg_id = m_previous_turn_ptr->operations[op_id].seg_id;
bool const prev_back_b = is_endpoint_on_boundary<boundary_back>(
range::back(sub_range(geometry, prev_seg_id)),
boundary_checker);
bool const prev_back_b = boundary_checker.is_endpoint_boundary(
range::back(sub_range(geometry, prev_seg_id)));
// if there is a boundary on the last point
if ( prev_back_b )
@ -1158,9 +1233,8 @@ struct linear_areal
segment_identifier const& prev_seg_id = m_previous_turn_ptr->operations[op_id].seg_id;
bool const prev_back_b = is_endpoint_on_boundary<boundary_back>(
range::back(sub_range(geometry, prev_seg_id)),
boundary_checker);
bool const prev_back_b = boundary_checker.is_endpoint_boundary(
range::back(sub_range(geometry, prev_seg_id)));
// if there is a boundary on the last point
if ( prev_back_b )
@ -1184,9 +1258,8 @@ struct linear_areal
segment_identifier const& prev_seg_id = m_previous_turn_ptr->operations[op_id].seg_id;
bool const prev_back_b = is_endpoint_on_boundary<boundary_back>(
range::back(sub_range(geometry, prev_seg_id)),
boundary_checker);
bool const prev_back_b = boundary_checker.is_endpoint_boundary(
range::back(sub_range(geometry, prev_seg_id)));
// if there is a boundary on the last point
if ( prev_back_b )
@ -1202,122 +1275,6 @@ struct linear_areal
m_first_from_unknown_boundary_detected = false;
}
// check if the passed turn's segment of Linear geometry arrived
// from the inside or the outside of the Areal geometry
template <typename Turn, typename Strategy>
static inline bool calculate_from_inside(Geometry1 const& geometry1,
Geometry2 const& geometry2,
Turn const& turn,
Strategy const& strategy)
{
if ( turn.operations[op_id].position == overlay::position_front )
{
return false;
}
typename sub_range_return_type<Geometry1 const>::type
range1 = sub_range(geometry1, turn.operations[op_id].seg_id);
using range2_view = detail::closed_clockwise_view<typename ring_type<Geometry2>::type const>;
using range2_iterator = typename boost::range_iterator<range2_view const>::type;
range2_view const range2(sub_range(geometry2, turn.operations[other_op_id].seg_id));
BOOST_GEOMETRY_ASSERT(boost::size(range1));
std::size_t const s2 = boost::size(range2);
BOOST_GEOMETRY_ASSERT(s2 > 2);
std::size_t const seg_count2 = s2 - 1;
std::size_t const p_seg_ij = static_cast<std::size_t>(turn.operations[op_id].seg_id.segment_index);
std::size_t const q_seg_ij = static_cast<std::size_t>(turn.operations[other_op_id].seg_id.segment_index);
BOOST_GEOMETRY_ASSERT(p_seg_ij + 1 < boost::size(range1));
BOOST_GEOMETRY_ASSERT(q_seg_ij + 1 < s2);
auto const& pi = range::at(range1, p_seg_ij);
auto const& qi = range::at(range2, q_seg_ij);
auto const& qj = range::at(range2, q_seg_ij + 1);
bool const is_ip_qj = equals::equals_point_point(turn.point, qj, strategy);
// TODO: test this!
// BOOST_GEOMETRY_ASSERT(!equals::equals_point_point(turn.point, pi));
// BOOST_GEOMETRY_ASSERT(!equals::equals_point_point(turn.point, qi));
if (is_ip_qj)
{
std::size_t const q_seg_jk = (q_seg_ij + 1) % seg_count2;
// TODO: the following function should return immediately, however the worst case complexity is O(N)
// It would be good to replace it with some O(1) mechanism
range2_iterator qk_it = find_next_non_duplicated(boost::begin(range2),
range::pos(range2, q_seg_jk),
boost::end(range2),
strategy);
// Calculate sides in a different point order for P and Q
// Will this sequence of points be always correct?
return calculate_from_inside_sides(qi, turn.point, pi, qi, qj, *qk_it, strategy);
}
else
{
// Calculate sides with different points for P and Q
return calculate_from_inside_sides(qi, turn.point, pi, qi, turn.point, qj, strategy);
}
}
template <typename It, typename Strategy>
static inline It find_next_non_duplicated(It first, It current, It last,
Strategy const& strategy)
{
BOOST_GEOMETRY_ASSERT( current != last );
It it = current;
for ( ++it ; it != last ; ++it )
{
if ( !equals::equals_point_point(*current, *it, strategy) )
return it;
}
// if not found start from the beginning
for ( it = first ; it != current ; ++it )
{
if ( !equals::equals_point_point(*current, *it, strategy) )
return it;
}
return current;
}
// calculate inside or outside based on side_calc
// this is simplified version of a check from equal<>
template <typename Strategy,
typename Pi, typename Pj, typename Pk,
typename Qi, typename Qj, typename Qk>
static inline
bool calculate_from_inside_sides(Pi const& pi, Pj const& pj, Pk const& pk,
Qi const& qi, Qj const& qj, Qk const& qk,
Strategy const& strategy)
{
la_side_calculator
<
Strategy,
Pi, Pj, Pk,
Qi, Qj, Qk
> side_calc(pi, pj, pk, qi, qj, qk, strategy);
int const side_pk_p = side_calc.pk_wrt_p1();
int const side_qk_p = side_calc.qk_wrt_p1();
// If they turn to same side (not opposite sides)
if (! overlay::base_turn_handler::opposite(side_pk_p, side_qk_p))
{
int const side_pk_q2 = side_calc.pk_wrt_q2();
return side_pk_q2 == -1;
}
else
{
return side_pk_p == -1;
}
}
private:
exit_watcher<TurnInfo, op_id> m_exit_watcher;
segment_watcher<same_single> m_seg_watcher;

View File

@ -2,8 +2,8 @@
// 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.
// This file was modified by Oracle on 2013-2022.
// Modifications copyright (c) 2013-2022 Oracle and/or its affiliates.
// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
@ -93,11 +93,9 @@ public:
m_flags |= 1;
// check if there is a boundary
if ( m_flags < 2
&& ( m_boundary_checker.template
is_endpoint_boundary<boundary_front>(range::front(linestring))
|| m_boundary_checker.template
is_endpoint_boundary<boundary_back>(range::back(linestring)) ) )
if (m_flags < 2
&& (m_boundary_checker.is_endpoint_boundary(range::front(linestring))
|| m_boundary_checker.is_endpoint_boundary(range::back(linestring))))
{
update<boundary, exterior, '0', TransposeResult>(m_result);
m_flags |= 2;
@ -124,20 +122,16 @@ struct linear_linear
Result & result,
Strategy const& strategy)
{
typedef typename Strategy::cs_tag cs_tag;
// The result should be FFFFFFFFF
relate::set<exterior, exterior, result_dimension<Geometry1>::value>(result);// FFFFFFFFd, d in [1,9] or T
if ( BOOST_GEOMETRY_CONDITION( result.interrupt ) )
return;
// get and analyse turns
using point_type = typename geometry::point_type<Geometry1>::type;
using mutable_point_type = typename helper_geometry<point_type>::type;
typedef typename turns::get_turns
using turn_type = typename turns::get_turns
<
Geometry1, Geometry2, mutable_point_type
>::template turn_info_type<Strategy>::type turn_type;
Geometry1, Geometry2
>::template turn_info_type<Strategy>::type;
std::vector<turn_type> turns;
interrupt_policy_linear_linear<Result> interrupt_policy(result);
@ -146,7 +140,6 @@ struct linear_linear
<
Geometry1,
Geometry2,
mutable_point_type,
detail::get_turns::get_turn_info_type<Geometry1, Geometry2, turns::assign_policy<true> >
>::apply(turns, geometry1, geometry2, interrupt_policy, strategy);
@ -180,8 +173,8 @@ struct linear_linear
|| may_update<boundary, boundary, '0'>(result)
|| may_update<boundary, exterior, '0'>(result) )
{
typedef turns::less<0, turns::less_op_linear_linear<0>, cs_tag> less;
std::sort(turns.begin(), turns.end(), less());
using less_t = turns::less<0, turns::less_op_linear_linear<0>, Strategy>;
std::sort(turns.begin(), turns.end(), less_t());
turns_analyser<turn_type, 0> analyser;
analyse_each_turn(result, analyser,
@ -200,8 +193,8 @@ struct linear_linear
|| may_update<boundary, boundary, '0', true>(result)
|| may_update<boundary, exterior, '0', true>(result) )
{
typedef turns::less<1, turns::less_op_linear_linear<1>, cs_tag> less;
std::sort(turns.begin(), turns.end(), less());
using less_t = turns::less<1, turns::less_op_linear_linear<1>, Strategy>;
std::sort(turns.begin(), turns.end(), less_t());
turns_analyser<turn_type, 1> analyser;
analyse_each_turn(result, analyser,
@ -226,9 +219,7 @@ struct linear_linear
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)
for (auto it = boost::begin(turns) ; it != boost::end(turns) ; ++it)
{
if ( it->operations[0].operation == overlay::operation_intersection
|| it->operations[1].operation == overlay::operation_intersection )
@ -376,20 +367,16 @@ struct linear_linear
update<interior, interior, '1', transpose_result>(res);
bool const this_b = it->operations[op_id].position == overlay::position_front // ignore spikes!
&& is_ip_on_boundary<boundary_front>(it->point,
it->operations[op_id],
boundary_checker,
seg_id);
&& is_ip_on_boundary(it->point, it->operations[op_id],
boundary_checker);
// going inside on boundary point
// may be front only
if ( this_b )
{
// may be front and back
bool const other_b = is_ip_on_boundary<boundary_any>(it->point,
it->operations[other_op_id],
other_boundary_checker,
other_id);
bool const other_b = is_ip_on_boundary(it->point, it->operations[other_op_id],
other_boundary_checker);
// it's also the boundary of the other geometry
if ( other_b )
@ -415,9 +402,8 @@ struct linear_linear
// if it's the first IP then the first point is outside
if ( first_in_range )
{
bool const front_b = is_endpoint_on_boundary<boundary_front>(
range::front(sub_range(geometry, seg_id)),
boundary_checker);
bool const front_b = boundary_checker.is_endpoint_boundary(
range::front(sub_range(geometry, seg_id)));
// if there is a boundary on the first point
if ( front_b )
@ -463,13 +449,12 @@ struct linear_linear
{
// check if this is indeed the boundary point
// NOTE: is_ip_on_boundary<>() should be called here but the result will be the same
if ( is_endpoint_on_boundary<boundary_back>(it->point, boundary_checker) )
if (boundary_checker.is_endpoint_boundary(it->point))
{
// may be front and back
bool const other_b = is_ip_on_boundary<boundary_any>(it->point,
it->operations[other_op_id],
other_boundary_checker,
other_id);
bool const other_b = is_ip_on_boundary(it->point,
it->operations[other_op_id],
other_boundary_checker);
// it's also the boundary of the other geometry
if ( other_b )
{
@ -503,9 +488,8 @@ struct linear_linear
// it's the first point in range
if ( first_in_range )
{
bool const front_b = is_endpoint_on_boundary<boundary_front>(
range::front(sub_range(geometry, seg_id)),
boundary_checker);
bool const front_b = boundary_checker.is_endpoint_boundary(
range::front(sub_range(geometry, seg_id)));
// if there is a boundary on the first point
if ( front_b )
@ -517,16 +501,14 @@ struct linear_linear
// method other than crosses, check more conditions
else
{
bool const this_b = is_ip_on_boundary<boundary_any>(it->point,
it->operations[op_id],
boundary_checker,
seg_id);
bool const other_b = is_ip_on_boundary<boundary_any>(it->point,
it->operations[other_op_id],
other_boundary_checker,
other_id);
bool const this_b = is_ip_on_boundary(it->point,
it->operations[op_id],
boundary_checker);
bool const other_b = is_ip_on_boundary(it->point,
it->operations[other_op_id],
other_boundary_checker);
// if current IP is on boundary of the geometry
if ( this_b )
{
@ -562,9 +544,8 @@ struct linear_linear
&& ! m_collinear_spike_exit
/*&& !is_collinear*/ )
{
bool const front_b = is_endpoint_on_boundary<boundary_front>(
range::front(sub_range(geometry, seg_id)),
boundary_checker);
bool const front_b = boundary_checker.is_endpoint_boundary(
range::front(sub_range(geometry, seg_id)));
// if there is a boundary on the first point
if ( front_b )
@ -621,9 +602,8 @@ struct linear_linear
segment_identifier const& prev_seg_id = turn_ptr->operations[op_id].seg_id;
//BOOST_GEOMETRY_ASSERT(!boost::empty(sub_range(geometry, prev_seg_id)));
bool const prev_back_b = is_endpoint_on_boundary<boundary_back>(
range::back(sub_range(geometry, prev_seg_id)),
boundary_checker);
bool const prev_back_b = boundary_checker.is_endpoint_boundary(
range::back(sub_range(geometry, prev_seg_id)));
// if there is a boundary on the last point
if ( prev_back_b )
@ -661,19 +641,17 @@ struct linear_linear
OtherBoundaryChecker const& other_boundary_checker,
bool first_in_range)
{
typename detail::single_geometry_return_type<Geometry const>::type
ls1_ref = detail::single_geometry(geometry, turn.operations[op_id].seg_id);
typename detail::single_geometry_return_type<OtherGeometry const>::type
ls2_ref = detail::single_geometry(other_geometry, turn.operations[other_op_id].seg_id);
auto const& ls1 = detail::single_geometry(geometry, turn.operations[op_id].seg_id);
auto const& ls2 = detail::single_geometry(other_geometry, turn.operations[other_op_id].seg_id);
// only one of those should be true:
if ( turn.operations[op_id].position == overlay::position_front )
{
// valid, point-sized
if ( boost::size(ls2_ref) == 2 )
if ( boost::size(ls2) == 2 )
{
bool const front_b = is_endpoint_on_boundary<boundary_front>(turn.point, boundary_checker);
bool const front_b = boundary_checker.is_endpoint_boundary(turn.point);
if ( front_b )
{
@ -693,11 +671,11 @@ struct linear_linear
else if ( turn.operations[op_id].position == overlay::position_back )
{
// valid, point-sized
if ( boost::size(ls2_ref) == 2 )
if ( boost::size(ls2) == 2 )
{
update<interior, exterior, '1', transpose_result>(res);
bool const back_b = is_endpoint_on_boundary<boundary_back>(turn.point, boundary_checker);
bool const back_b = boundary_checker.is_endpoint_boundary(turn.point);
if ( back_b )
{
@ -710,9 +688,9 @@ struct linear_linear
if ( first_in_range )
{
//BOOST_GEOMETRY_ASSERT(!boost::empty(ls1_ref));
bool const front_b = is_endpoint_on_boundary<boundary_front>(
range::front(ls1_ref), boundary_checker);
//BOOST_GEOMETRY_ASSERT(!boost::empty(ls1));
bool const front_b = boundary_checker.is_endpoint_boundary(
range::front(ls1));
if ( front_b )
{
update<boundary, exterior, '0', transpose_result>(res);
@ -727,13 +705,13 @@ struct linear_linear
// here we don't know which one is degenerated
bool const is_point1 = boost::size(ls1_ref) == 2
&& equals::equals_point_point(range::front(ls1_ref),
range::back(ls1_ref),
bool const is_point1 = boost::size(ls1) == 2
&& equals::equals_point_point(range::front(ls1),
range::back(ls1),
boundary_checker.strategy());
bool const is_point2 = boost::size(ls2_ref) == 2
&& equals::equals_point_point(range::front(ls2_ref),
range::back(ls2_ref),
bool const is_point2 = boost::size(ls2) == 2
&& equals::equals_point_point(range::front(ls2),
range::back(ls2),
other_boundary_checker.strategy());
// if the second one is degenerated
@ -743,9 +721,9 @@ struct linear_linear
if ( first_in_range )
{
//BOOST_GEOMETRY_ASSERT(!boost::empty(ls1_ref));
bool const front_b = is_endpoint_on_boundary<boundary_front>(
range::front(ls1_ref), boundary_checker);
//BOOST_GEOMETRY_ASSERT(!boost::empty(ls1));
bool const front_b = boundary_checker.is_endpoint_boundary(
range::front(ls1));
if ( front_b )
{
update<boundary, exterior, '0', transpose_result>(res);

View File

@ -2,8 +2,8 @@
// Copyright (c) 2007-2015 Barend Gehrels, Amsterdam, the Netherlands.
// This file was modified by Oracle on 2013-2020.
// Modifications copyright (c) 2013-2020 Oracle and/or its affiliates.
// This file was modified by Oracle on 2013-2022.
// Modifications copyright (c) 2013-2022 Oracle and/or its affiliates.
// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle
@ -20,6 +20,8 @@
#include <boost/geometry/algorithms/detail/overlay/get_turns.hpp>
#include <boost/geometry/algorithms/detail/overlay/get_turn_info.hpp>
#include <boost/geometry/geometries/helper_geometry.hpp>
#include <boost/geometry/policies/robustness/get_rescale_policy.hpp>
#include <boost/geometry/policies/robustness/segment_ratio_type.hpp>
@ -46,7 +48,6 @@ template
<
typename Geometry1,
typename Geometry2,
typename Point,
typename GetTurnPolicy = detail::get_turns::get_turn_info_type
<
Geometry1, Geometry2, assign_policy<>
@ -54,6 +55,11 @@ template
>
struct get_turns
{
using turn_point_type = typename helper_geometry
<
typename geometry::point_type<Geometry1>::type
>::type;
template <typename Strategy>
struct robust_policy_type
: geometry::rescale_overlay_policy_type
@ -71,14 +77,14 @@ struct get_turns
>
struct turn_info_type
{
using ratio_type = typename segment_ratio_type<Point, RobustPolicy>::type;
using ratio_type = typename segment_ratio_type<turn_point_type, RobustPolicy>::type;
using type = overlay::turn_info
<
Point,
turn_point_type,
ratio_type,
typename detail::get_turns::turn_operation_type
<
Geometry1, Geometry2, Point, ratio_type
Geometry1, Geometry2, turn_point_type, ratio_type
>::type
>;
};
@ -166,7 +172,7 @@ struct less_op_xxx_linear
template <std::size_t OpId>
struct less_op_linear_linear
: less_op_xxx_linear< OpId, op_to_int<0,2,3,1,4,0> >
: less_op_xxx_linear< OpId, op_to_int<0,2,3,1,4,0> > // xuic
{};
template <std::size_t OpId>
@ -269,7 +275,7 @@ struct less_other_multi_index
// sort turns by G1 - source_index == 0 by:
// seg_id -> distance and coordinates -> operation
template <std::size_t OpId, typename LessOp, typename CSTag>
template <std::size_t OpId, typename LessOp, typename Strategy>
struct less
{
BOOST_STATIC_ASSERT(OpId < 2);
@ -277,14 +283,8 @@ struct less
template <typename Turn>
static inline bool use_fraction(Turn const& left, Turn const& right)
{
typedef typename geometry::strategy::within::services::default_strategy
<
typename Turn::point_type, typename Turn::point_type,
point_tag, point_tag,
pointlike_tag, pointlike_tag,
typename tag_cast<CSTag, spherical_tag>::type,
typename tag_cast<CSTag, spherical_tag>::type
>::type eq_pp_strategy_type;
using eq_pp_strategy_type = decltype(std::declval<Strategy>().relate(
detail::dummy_point(), detail::dummy_point()));
static LessOp less_op;

View File

@ -1,6 +1,6 @@
// Boost.Geometry (aka GGL, Generic Geometry Library)
// Copyright (c) 2015-2020, Oracle and/or its affiliates.
// Copyright (c) 2015-2022, Oracle and/or its affiliates.
// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle
// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
@ -87,7 +87,6 @@ struct check_turn_less
<
Geometry1,
Geometry2,
typename bg::point_type<Geometry1>::type,
bg::detail::get_turns::get_turn_info_type
<
Geometry1, Geometry2, assign_policy<>

View File

@ -1,6 +1,6 @@
// Boost.Geometry (aka GGL, Generic Geometry Library)
// Copyright (c) 2014-2021, Oracle and/or its affiliates.
// Copyright (c) 2014-2022, Oracle and/or its affiliates.
// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle
// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
@ -84,7 +84,6 @@ private:
<
LinearGeometry1,
LinearGeometry2,
typename bg::point_type<LinearGeometry1>::type,
bg_detail::get_turns::get_turn_info_type
<
LinearGeometry1,
@ -110,7 +109,7 @@ public:
typedef typename bg_detail::relate::turns::get_turns
<
Linear1, Linear2, typename bg::point_type<Linear1>::type
Linear1, Linear2
>::template turn_info_type<strategy_type>::type turn_info;
typedef std::vector<turn_info> turns_container;