Merge branch 'feature/relate' of https://github.com/boostorg/geometry into feature/setops

Conflicts:
	include/boost/geometry/algorithms/detail/overlay/get_turn_info_for_endpoint.hpp
	include/boost/geometry/algorithms/detail/relate/turns.hpp
This commit is contained in:
Menelaos Karavelas
2014-03-19 17:25:19 +02:00
19 changed files with 3009 additions and 1044 deletions

View File

@@ -28,9 +28,11 @@ struct turn_operation_linear
{
turn_operation_linear()
: position(position_middle)
, is_collinear(false)
{}
turn_position position;
bool is_collinear; // valid only for Linear geometry
};
// SEGMENT_INTERSECTION RESULT
@@ -347,12 +349,16 @@ struct get_turn_info_for_endpoint
if ( append_first || append_last )
{
bool handled = handle_internal(pi, pj, pk, qi, qj, qk, ip,
is_p_first_ip, is_p_last_ip, is_q_first_ip, is_q_last_ip, is_qi_ip, is_qj_ip,
is_p_first_ip, is_p_last_ip,
is_q_first_ip, is_q_last_ip,
is_qi_ip, is_qj_ip,
tp_model, result, p_operation, q_operation);
if ( !handled )
{
handle_internal(qi, qj, qk, pi, pj, pk, ip,
is_q_first_ip, is_q_last_ip, is_p_first_ip, is_p_last_ip, is_pi_ip, is_pj_ip,
is_q_first_ip, is_q_last_ip,
is_p_first_ip, is_p_last_ip,
is_pi_ip, is_pj_ip,
tp_model, result, q_operation, p_operation);
}
@@ -382,7 +388,8 @@ struct get_turn_info_for_endpoint
static inline bool handle_internal(Point1 const& i1, Point1 const& j1, Point1 const& /*k1*/,
Point2 const& i2, Point2 const& j2, Point2 const& k2,
Point const& ip,
bool first1, bool last1, bool first2, bool last2,
bool first1, bool last1,
bool first2, bool last2,
bool ip_i2, bool ip_j2,
TurnInfo const& tp_model,
IntersectionResult const& result,
@@ -420,6 +427,7 @@ struct get_turn_info_for_endpoint
if ( tp.both(operation_continue) )
{
// THIS IS WRT THE ORIGINAL SEGMENTS! NOT THE ONES ABOVE!
bool opposite = result.template get<1>().opposite;
op1 = operation_intersection;
@@ -443,23 +451,23 @@ struct get_turn_info_for_endpoint
BOOST_ASSERT(ip_i2 == equals::equals_point_point(i2, ip));
BOOST_ASSERT(ip_j2 == equals::equals_point_point(j2, ip));
#endif
if ( ip_j2 )
if ( ip_i2 )
{
// don't output this IP - for the first point of other geometry segment
op1 = operation_none;
op2 = operation_none;
return true;
}
else if ( ip_i2 )
else if ( ip_j2 )
{
// NOTE: this conversion may be problematic
Point1 j2_conv;
geometry::convert(j2, j2_conv);
Point1 i2_conv;
geometry::convert(i2, i2_conv);
side_calculator<Point1, Point2> side_calc(j2_conv, j1, i1, i2, j2, k2);
side_calculator<Point1, Point2> side_calc(i2_conv, j1, i1, i2, j2, k2);
TurnInfo tp = tp_model;
equal<TurnInfo>::apply(j2_conv, j1, i1, i2, j2, k2,
equal<TurnInfo>::apply(i2_conv, j1, i1, i2, j2, k2,
tp, result.template get<0>(), result.template get<1>(), side_calc);
// TODO: must the above be calculated?
@@ -467,10 +475,11 @@ struct get_turn_info_for_endpoint
if ( tp.both(operation_continue) )
{
bool opposite = result.template get<1>().opposite;
// THIS IS WRT THE ORIGINAL SEGMENTS! NOT THE ONES ABOVE!
bool second_going_out = result.template get<0>().count > 1;
op1 = operation_blocked;
op2 = opposite ? operation_intersection : operation_union;
op2 = second_going_out ? operation_union : operation_intersection;
}
else
{
@@ -523,8 +532,29 @@ struct get_turn_info_for_endpoint
tp.method = method;
tp.operations[0].operation = op0;
tp.operations[1].operation = op1;
// tp.operations[0].position = pos0;
// tp.operations[1].position = pos1;
tp.operations[0].position = pos0;
tp.operations[1].position = pos1;
// NOTE: this probably shouldn't be set for the first point
// for which there is no preceding segment
if ( result.template get<0>().count > 1 )
{
//BOOST_ASSERT( result.template get<1>().dir_a == 0 && result.template get<1>().dir_b == 0 );
tp.operations[0].is_collinear = true;
tp.operations[1].is_collinear = true;
}
else //if ( result.template get<0>().count == 1 )
{
if ( op0 == operation_blocked && op1 == operation_intersection )
{
tp.operations[0].is_collinear = true;
}
else if ( op0 == operation_intersection && op1 == operation_blocked )
{
tp.operations[1].is_collinear = true;
}
}
AssignPolicy::apply(tp, pi, qi, result.template get<0>(), result.template get<1>());
*out++ = tp;
}

View File

@@ -112,6 +112,11 @@ struct get_turn_info_linear_areal
swapped_side_calc);
}
if ( tp.operations[1].operation == operation_blocked )
{
tp.operations[0].is_collinear = true;
}
replace_method_and_operations_tm(tp.method, tp.operations[0].operation, tp.operations[1].operation);
AssignPolicy::apply(tp, pi, qi, result.template get<0>(), result.template get<1>());
@@ -144,6 +149,11 @@ struct get_turn_info_linear_areal
touch<TurnInfo>::apply(pi, pj, pk, qi, qj, qk,
tp, result.template get<0>(), result.template get<1>(), side_calc);
if ( tp.operations[1].operation == operation_blocked )
{
tp.operations[0].is_collinear = true;
}
replace_method_and_operations_tm(tp.method, tp.operations[0].operation, tp.operations[1].operation);
AssignPolicy::apply(tp, pi, qi, result.template get<0>(), result.template get<1>());
@@ -159,27 +169,32 @@ struct get_turn_info_linear_areal
{
// do nothing
}
else if ( ! result.template get<1>().opposite )
{
// Both equal
// or collinear-and-ending at intersection point
equal<TurnInfo>::apply(pi, pj, pk, qi, qj, qk,
tp, result.template get<0>(), result.template get<1>(), side_calc);
replacer_of_method_and_operations_ec<false> replacer(method_touch);
replacer(tp.method, tp.operations[0].operation, tp.operations[1].operation);
AssignPolicy::apply(tp, pi, qi, result.template get<0>(), result.template get<1>());
*out++ = tp;
}
else
{
equal_opposite
<
TurnInfo,
AssignPolicy
>::apply(pi, qi,
tp, out, result.template get<0>(), result.template get<1>());
tp.operations[0].is_collinear = true;
if ( ! result.template get<1>().opposite )
{
// Both equal
// or collinear-and-ending at intersection point
equal<TurnInfo>::apply(pi, pj, pk, qi, qj, qk,
tp, result.template get<0>(), result.template get<1>(), side_calc);
replacer_of_method_and_operations_ec<false> replacer(method_touch);
replacer(tp.method, tp.operations[0].operation, tp.operations[1].operation);
AssignPolicy::apply(tp, pi, qi, result.template get<0>(), result.template get<1>());
*out++ = tp;
}
else
{
equal_opposite
<
TurnInfo,
AssignPolicy
>::apply(pi, qi,
tp, out, result.template get<0>(), result.template get<1>());
}
}
}
break;
@@ -192,42 +207,46 @@ struct get_turn_info_linear_areal
{
// do nothing
}
else if (! result.template get<1>().opposite)
else
{
tp.operations[0].is_collinear = true;
if (result.template get<1>().arrival[0] == 0)
if (! result.template get<1>().opposite)
{
// Collinear, but similar thus handled as equal
equal<TurnInfo>::apply(pi, pj, pk, qi, qj, qk,
tp, result.template get<0>(), result.template get<1>(), side_calc);
if (result.template get<1>().arrival[0] == 0)
{
// Collinear, but similar thus handled as equal
equal<TurnInfo>::apply(pi, pj, pk, qi, qj, qk,
tp, result.template get<0>(), result.template get<1>(), side_calc);
replacer_of_method_and_operations_ec<false> replacer(method_touch);
replacer(tp.method, tp.operations[0].operation, tp.operations[1].operation);
replacer_of_method_and_operations_ec<false> replacer(method_touch);
replacer(tp.method, tp.operations[0].operation, tp.operations[1].operation);
}
else
{
collinear<TurnInfo>::apply(pi, pj, pk, qi, qj, qk,
tp, result.template get<0>(), result.template get<1>(), side_calc);
replacer_of_method_and_operations_ec<false> replacer(method_touch_interior);
replacer(tp.method, tp.operations[0].operation, tp.operations[1].operation);
}
AssignPolicy::apply(tp, pi, qi, result.template get<0>(), result.template get<1>());
*out++ = tp;
}
else
{
collinear<TurnInfo>::apply(pi, pj, pk, qi, qj, qk,
tp, result.template get<0>(), result.template get<1>(), side_calc);
// Is this always 'm' ?
replacer_of_method_and_operations_ec<false> replacer(method_touch_interior);
replacer(tp.method, tp.operations[0].operation, tp.operations[1].operation);
collinear_opposite
<
TurnInfo,
AssignPolicy
>::apply(pi, pj, pk, qi, qj, qk,
tp, out, result.template get<0>(), result.template get<1>(), side_calc,
replacer);
}
AssignPolicy::apply(tp, pi, qi, result.template get<0>(), result.template get<1>());
*out++ = tp;
}
else
{
// Is this always 'm' ?
replacer_of_method_and_operations_ec<false> replacer(method_touch_interior);
collinear_opposite
<
TurnInfo,
AssignPolicy
>::apply(pi, pj, pk, qi, qj, qk,
tp, out, result.template get<0>(), result.template get<1>(), side_calc,
replacer);
}
}
break;
@@ -267,11 +286,14 @@ struct get_turn_info_linear_areal
method = (method == method_touch ? method_equal : method_collinear);
}
// assuming Linear is always the first one
// Assuming G1 is always Linear
if ( op0 == operation_blocked )
op0 = operation_continue;
op1 = operation_union;
if ( op1 == operation_blocked )
op1 = operation_continue;
else if ( op1 == operation_intersection )
op1 = operation_union;
}
template <bool IsFront>
@@ -294,11 +316,14 @@ struct get_turn_info_linear_areal
method = m_method;
}
// assuming Linear is always the first one
// Assuming G1 is always Linear
if ( op0 == operation_blocked )
op0 = operation_continue;
op1 = operation_union;
if ( op1 == operation_blocked )
op1 = operation_continue;
else if ( op1 == operation_intersection )
op1 = operation_union;
}
private:
@@ -387,61 +412,99 @@ struct get_turn_info_linear_areal
tp.operations[0].position = position_front;
tp.operations[1].position = position_middle;
// NOTE: the conversion may be problematic
Point1 qi_conv;
geometry::convert(qi, qi_conv);
method_type replaced_method = method_touch_interior;
if ( q0j )
if ( opposite ) // opposite -> collinear
{
if ( !opposite )
{
side_calculator<Point1, Point2> side_calc(qi_conv, pi, pj, qi, qj, qk);
equal<TurnInfo>::apply(qi_conv, pi, pj, qi, qj, qk,
tp, result.template get<0>(), result.template get<1>(), side_calc);
}
else // opposite -> collinear
{
tp.operations[0].operation = operation_continue;
tp.operations[1].operation = operation_union;
}
replaced_method = method_touch;
tp.operations[0].operation = operation_continue;
tp.operations[1].operation = operation_union;
tp.method = q0j ? method_touch : method_touch_interior;
}
else
{
// NOTE: the conversion may be problematic
Point1 qi_conv;
geometry::convert(qi, qi_conv);
method_type replaced_method = method_touch_interior;
if ( q0j )
{
side_calculator<Point1, Point2> side_calc(qi_conv, pi, pj, qi, qj, qk);
equal<TurnInfo>::apply(qi_conv, pi, pj, qi, qj, qk,
tp, result.template get<0>(), result.template get<1>(), side_calc);
replaced_method = method_touch;
}
else
{
// NOTE: the conversion may be problematic
Point2 pi_conv;
geometry::convert(pi, pi_conv);
Point2 pi_conv;
geometry::convert(pi, pi_conv);
side_calculator<Point1, Point2> side_calc(qi_conv, pi, pj, qi, pi_conv, qj);
side_calculator<Point1, Point2> side_calc(qi_conv, pi, pj, qi, pi_conv, qj);
// Collinear, but similar thus handled as equal
equal<TurnInfo>::apply(qi_conv, pi, pj, qi, pi_conv, qj,
tp, result.template get<0>(), result.template get<1>(), side_calc);
// Collinear, but similar thus handled as equal
equal<TurnInfo>::apply(qi_conv, pi, pj, qi, pi_conv, qj,
tp, result.template get<0>(), result.template get<1>(), side_calc);
}
replacer_of_method_and_operations_ec<true> replacer(replaced_method);
replacer(tp.method, tp.operations[0].operation, tp.operations[1].operation);
}
replacer_of_method_and_operations_ec<true> replacer(replaced_method);
replacer(tp.method, tp.operations[0].operation, tp.operations[1].operation);
// equals<> or collinear<> will assign the second point,
// we'd like to assign the first one
geometry::convert(result.template get<0>().intersections[0], tp.point);
// NOTE: not really needed especially for the first point
// for which there is no preceding segment (but consistent with the L/L)
if ( result.template get<0>().count > 1 )
{
//BOOST_ASSERT( result.template get<1>().dir_a == 0 && result.template get<1>().dir_b == 0 );
tp.operations[0].is_collinear = true;
}
AssignPolicy::apply(tp, pi, qi, result.template get<0>(), result.template get<1>());
*out++ = tp;
}
// ANALYSE AND ASSIGN LAST
// IP on the first point of Linear Geometry
if ( EnableLast && is_p_last && ( ip_count > 1 ? p1j : p0j ) && !q0i && !q1i ) // !q0i && !q1i prevents duplication
// IP on the last point of Linear Geometry
if ( EnableLast
&& is_p_last
&& ( ip_count > 1 ? p1j : p0j )
&& (!q0i || (q0i && q1j)) // prevents duplication
&& !q1i ) // prevents duplication
{
TurnInfo tp = tp_model;
if ( result.template get<0>().count > 1 )
{
//BOOST_ASSERT( result.template get<1>().dir_a == 0 && result.template get<1>().dir_b == 0 );
tp.operations[0].is_collinear = true;
tp.operations[1].operation = opposite ? operation_continue : operation_union;
}
else //if ( result.template get<0>().count == 1 )
{
Point1 qi_conv;
geometry::convert(qi, qi_conv);
side_calculator<Point1, Point2> side_calc(qi_conv, pj, pi, qi, qj, qk);
equal<TurnInfo>::apply(qi_conv, pj, pi, qi, qj, qk,
tp, result.template get<0>(), result.template get<1>(), side_calc);
replacer_of_method_and_operations_ec<false> replacer(method_none);
replacer(tp.method, tp.operations[0].operation, tp.operations[1].operation);
if ( tp.both(operation_continue) )
{
tp.operations[0].is_collinear = true;
}
}
tp.method = ( ip_count > 1 ? q1j : q0j ) ? method_touch : method_touch_interior;
tp.operations[0].operation = operation_blocked;
tp.operations[1].operation = operation_union;
tp.operations[0].position = position_back;
tp.operations[1].position = position_middle;

View File

@@ -112,6 +112,15 @@ struct get_turn_info_linear_linear
swapped_side_calc);
}
if ( tp.operations[0].operation == operation_blocked )
{
tp.operations[1].is_collinear = true;
}
if ( tp.operations[1].operation == operation_blocked )
{
tp.operations[0].is_collinear = true;
}
replace_method_and_operations_tm(tp.method, tp.operations[0].operation, tp.operations[1].operation);
AssignPolicy::apply(tp, pi, qi, result.template get<0>(), result.template get<1>());
@@ -144,6 +153,15 @@ struct get_turn_info_linear_linear
touch<TurnInfo>::apply(pi, pj, pk, qi, qj, qk,
tp, result.template get<0>(), result.template get<1>(), side_calc);
if ( tp.operations[0].operation == operation_blocked )
{
tp.operations[1].is_collinear = true;
}
if ( tp.operations[1].operation == operation_blocked )
{
tp.operations[0].is_collinear = true;
}
replace_method_and_operations_tm(tp.method, tp.operations[0].operation, tp.operations[1].operation);
AssignPolicy::apply(tp, pi, qi, result.template get<0>(), result.template get<1>());
@@ -159,27 +177,33 @@ struct get_turn_info_linear_linear
{
// do nothing
}
else if ( ! result.template get<1>().opposite )
{
// Both equal
// or collinear-and-ending at intersection point
equal<TurnInfo>::apply(pi, pj, pk, qi, qj, qk,
tp, result.template get<0>(), result.template get<1>(), side_calc);
replacer_of_method_and_operations_ec replacer(method_touch);
replacer(tp.method, tp.operations[0].operation, tp.operations[1].operation);
AssignPolicy::apply(tp, pi, qi, result.template get<0>(), result.template get<1>());
*out++ = tp;
}
else
{
equal_opposite
<
TurnInfo,
AssignPolicy
>::apply(pi, qi,
tp, out, result.template get<0>(), result.template get<1>());
tp.operations[0].is_collinear = true;
tp.operations[1].is_collinear = true;
if ( ! result.template get<1>().opposite )
{
// Both equal
// or collinear-and-ending at intersection point
equal<TurnInfo>::apply(pi, pj, pk, qi, qj, qk,
tp, result.template get<0>(), result.template get<1>(), side_calc);
replacer_of_method_and_operations_ec replacer(method_touch);
replacer(tp.method, tp.operations[0].operation, tp.operations[1].operation);
AssignPolicy::apply(tp, pi, qi, result.template get<0>(), result.template get<1>());
*out++ = tp;
}
else
{
equal_opposite
<
TurnInfo,
AssignPolicy
>::apply(pi, qi,
tp, out, result.template get<0>(), result.template get<1>());
}
}
}
break;
@@ -192,47 +216,52 @@ struct get_turn_info_linear_linear
{
// do nothing
}
else if (! result.template get<1>().opposite)
else
{
tp.operations[0].is_collinear = true;
tp.operations[1].is_collinear = true;
if (result.template get<1>().arrival[0] == 0)
{
// Collinear, but similar thus handled as equal
equal<TurnInfo>::apply(pi, pj, pk, qi, qj, qk,
tp, result.template get<0>(), result.template get<1>(), side_calc);
if (! result.template get<1>().opposite)
{
if (result.template get<1>().arrival[0] == 0)
{
// Collinear, but similar thus handled as equal
equal<TurnInfo>::apply(pi, pj, pk, qi, qj, qk,
tp, result.template get<0>(), result.template get<1>(), side_calc);
// NOTE: don't change the method only if methods are WRT IPs, not segments!
// (currently this approach is used)
// override assigned method
//tp.method = method_collinear;
// NOTE: don't change the method only if methods are WRT IPs, not segments!
// (currently this approach is used)
// override assigned method
//tp.method = method_collinear;
replacer_of_method_and_operations_ec replacer(method_touch);
replacer(tp.method, tp.operations[0].operation, tp.operations[1].operation);
replacer_of_method_and_operations_ec replacer(method_touch);
replacer(tp.method, tp.operations[0].operation, tp.operations[1].operation);
}
else
{
collinear<TurnInfo>::apply(pi, pj, pk, qi, qj, qk,
tp, result.template get<0>(), result.template get<1>(), side_calc);
replacer_of_method_and_operations_ec replacer(method_touch_interior);
replacer(tp.method, tp.operations[0].operation, tp.operations[1].operation);
}
AssignPolicy::apply(tp, pi, qi, result.template get<0>(), result.template get<1>());
*out++ = tp;
}
else
{
collinear<TurnInfo>::apply(pi, pj, pk, qi, qj, qk,
tp, result.template get<0>(), result.template get<1>(), side_calc);
// If this always 'm' ?
replacer_of_method_and_operations_ec replacer(method_touch_interior);
replacer(tp.method, tp.operations[0].operation, tp.operations[1].operation);
collinear_opposite
<
TurnInfo,
AssignPolicy
>::apply(pi, pj, pk, qi, qj, qk,
tp, out, result.template get<0>(), result.template get<1>(), side_calc,
replacer);
}
AssignPolicy::apply(tp, pi, qi, result.template get<0>(), result.template get<1>());
*out++ = tp;
}
else
{
// If this always 'm' ?
replacer_of_method_and_operations_ec replacer(method_touch_interior);
collinear_opposite
<
TurnInfo,
AssignPolicy
>::apply(pi, pj, pk, qi, qj, qk,
tp, out, result.template get<0>(), result.template get<1>(), side_calc,
replacer);
}
}
break;

View File

@@ -735,29 +735,76 @@ struct get_turns_polygon_cs
}
};
// GET_TURN_INFO_TYPE
}} // namespace detail::get_turns
#endif // DOXYGEN_NO_DETAIL
template <typename Tag,
// TODO: move group_tag and group_dim to separate file
#ifndef DOXYGEN_NO_DETAIL
namespace detail {
template <typename Geometry,
typename Tag = typename geometry::tag<Geometry>::type,
bool IsPoint = boost::is_base_of<pointlike_tag, Tag>::value,
bool IsLinear = boost::is_base_of<linear_tag, Tag>::value,
bool IsAreal = boost::is_base_of<areal_tag, Tag>::value>
struct tag_base : not_implemented<Tag>
struct group_tag
: not_implemented<Tag>
{};
template <typename Tag>
struct tag_base<Tag, true, false>
template <typename Geometry, typename Tag>
struct group_tag<Geometry, Tag, true, false, false>
{
typedef pointlike_tag type;
};
template <typename Geometry, typename Tag>
struct group_tag<Geometry, Tag, false, true, false>
{
typedef linear_tag type;
};
template <typename Tag>
struct tag_base<Tag, false, true>
template <typename Geometry, typename Tag>
struct group_tag<Geometry, Tag, false, false, true>
{
typedef areal_tag type;
};
template <typename Geometry,
typename GroupTag = typename group_tag<Geometry>::type>
struct group_dim
: not_implemented<GroupTag>
{};
template <typename Geometry>
struct group_dim<Geometry, pointlike_tag>
{
static const int value = 0;
};
template <typename Geometry>
struct group_dim<Geometry, linear_tag>
{
static const int value = 1;
};
template <typename Geometry>
struct group_dim<Geometry, areal_tag>
{
static const int value = 2;
};
} // namespace detail
#endif // DOXYGEN_NO_DETAIL
#ifndef DOXYGEN_NO_DETAIL
namespace detail { namespace get_turns {
// GET_TURN_INFO_TYPE
template <typename Geometry1, typename Geometry2, typename AssignPolicy,
typename Tag1 = typename tag<Geometry1>::type, typename Tag2 = typename tag<Geometry2>::type,
typename TagBase1 = typename tag_base<Tag1>::type, typename TagBase2 = typename tag_base<Tag2>::type>
typename TagBase1 = typename group_tag<Geometry1>::type, typename TagBase2 = typename group_tag<Geometry2>::type>
struct get_turn_info_type
: overlay::get_turn_info<AssignPolicy>
{};
@@ -774,7 +821,7 @@ struct get_turn_info_type<Geometry1, Geometry2, AssignPolicy, Tag1, Tag2, linear
template <typename Geometry1, typename Geometry2,
typename Tag1 = typename tag<Geometry1>::type, typename Tag2 = typename tag<Geometry2>::type,
typename TagBase1 = typename tag_base<Tag1>::type, typename TagBase2 = typename tag_base<Tag2>::type>
typename TagBase1 = typename group_tag<Geometry1>::type, typename TagBase2 = typename group_tag<Geometry2>::type>
struct turn_operation_type
{
typedef overlay::turn_operation type;

View File

@@ -22,6 +22,22 @@ namespace boost { namespace geometry
#ifndef DOXYGEN_NO_DETAIL
namespace detail { namespace range {
template <typename Range>
inline typename boost::range_value<Range>::type const&
at(Range const& rng, typename boost::range_size<Range>::type i)
{
BOOST_ASSERT(i < boost::size(rng));
return *(boost::begin(rng) + i);
}
template <typename Range>
inline typename boost::range_value<Range>::type &
at(Range & rng, typename boost::range_size<Range>::type i)
{
BOOST_ASSERT(i < boost::size(rng));
return *(boost::begin(rng) + i);
}
template <typename Range>
inline typename boost::range_value<Range>::type const&
front(Range const& rng)

View File

@@ -41,6 +41,7 @@ public:
template <boundary_query BoundaryQuery>
bool is_endpoint_boundary(point_type const& pt) const
{
boost::ignore_unused_variable_warning(pt);
#ifdef BOOST_GEOMETRY_DEBUG_RELATE_BOUNDARY_CHECKER
// may give false positives for INT
BOOST_ASSERT( (BoundaryQuery == boundary_front || BoundaryQuery == boundary_any)
@@ -95,13 +96,13 @@ public:
{}
template <boundary_query BoundaryQuery>
bool is_endpoint_boundary(point_type const& pt)
bool is_endpoint_boundary(point_type const& pt) const
{
return is_boundary_point(pt);
}
template <boundary_query BoundaryQuery>
bool is_boundary(point_type const& pt, segment_identifier const& sid)
bool is_boundary(point_type const& pt, segment_identifier const& sid) const
{
if ( BoundaryQuery == boundary_front )
{
@@ -128,7 +129,7 @@ public:
private:
// First call O(NlogN)
// Each next call O(logN)
bool is_boundary_point(point_type const& pt)
bool is_boundary_point(point_type const& pt) const
{
typedef typename boost::range_size<Geometry>::type size_type;
size_type multi_count = boost::size(geometry);
@@ -173,9 +174,10 @@ private:
return equal_points_count % 2 != 0;// && equal_points_count > 0; // the number is odd and > 0
}
bool is_filled;
mutable bool is_filled;
// TODO: store references/pointers instead of points?
std::vector<point_type> boundary_points;
mutable std::vector<point_type> boundary_points;
Geometry const& geometry;
};

View File

@@ -0,0 +1,304 @@
// 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_FOLLOW_HELPERS_HPP
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_FOLLOW_HELPERS_HPP
namespace boost { namespace geometry
{
#ifndef DOXYGEN_NO_DETAIL
namespace detail { namespace relate {
// TODO:
// For 1-point linestrings or with all equal points turns won't be generated!
// Check for those degenerated cases may be connected with this one!
template <std::size_t OpId,
typename Geometry,
typename Tag = typename geometry::tag<Geometry>::type,
bool IsMulti = boost::is_base_of<multi_tag, Tag>::value
>
struct for_each_disjoint_geometry_if
: public not_implemented<Tag>
{};
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)
{
if ( first != last )
return false;
pred(geometry);
return true;
}
};
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)
{
BOOST_ASSERT(first != last);
const std::size_t count = boost::size(geometry);
boost::ignore_unused_variable_warning(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 )
{
int multi_index = it->operations[OpId].seg_id.multi_index;
BOOST_ASSERT(multi_index >= 0);
std::size_t index = static_cast<std::size_t>(multi_index);
BOOST_ASSERT(index < count);
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 )
{
// if there were no intersections for this multi_index
if ( *it == false )
{
found = true;
bool cont = pred(
*(boost::begin(geometry)
+ std::distance(detected_intersections.begin(), it)));
if ( !cont )
break;
}
}
return found;
}
};
// TODO: rename to point_id_ref?
template <typename Point>
class point_identifier
{
public:
point_identifier() : sid_ptr(0), pt_ptr(0) {}
point_identifier(segment_identifier const& sid, Point const& pt)
: sid_ptr(boost::addressof(sid))
, pt_ptr(boost::addressof(pt))
{}
segment_identifier const& seg_id() const
{
BOOST_ASSERT(sid_ptr);
return *sid_ptr;
}
Point const& point() const
{
BOOST_ASSERT(pt_ptr);
return *pt_ptr;
}
//friend bool operator==(point_identifier const& l, point_identifier const& r)
//{
// return l.seg_id() == r.seg_id()
// && detail::equals::equals_point_point(l.point(), r.point());
//}
private:
const segment_identifier * sid_ptr;
const Point * pt_ptr;
};
class same_multi_index
{
public:
same_multi_index(segment_identifier const& sid)
: sid_ptr(boost::addressof(sid))
{}
bool operator()(segment_identifier const& sid) const
{
return sid.multi_index == sid_ptr->multi_index;
}
template <typename Point>
bool operator()(point_identifier<Point> const& pid) const
{
return operator()(pid.seg_id());
}
private:
const segment_identifier * sid_ptr;
};
class segment_watcher
{
public:
segment_watcher()
: m_seg_id_ptr(0)
{}
bool update(segment_identifier const& seg_id)
{
bool result = m_seg_id_ptr == 0 || !same_multi_index(*m_seg_id_ptr)(seg_id);
m_seg_id_ptr = boost::addressof(seg_id);
return result;
}
private:
const segment_identifier * m_seg_id_ptr;
};
template <typename Point>
class exit_watcher
{
typedef point_identifier<Point> point_info;
public:
exit_watcher()
: exit_operation(overlay::operation_none)
{}
void enter(Point const& point, segment_identifier const& other_id)
{
other_entry_points.push_back(point_info(other_id, point));
}
void exit(Point const& point,
segment_identifier const& other_id,
overlay::operation_type exit_op)
{
typedef typename std::vector<point_info>::iterator point_iterator;
// search for the entry point in the same range of other geometry
point_iterator entry_it = std::find_if(other_entry_points.begin(),
other_entry_points.end(),
same_multi_index(other_id));
// this end point has corresponding entry point
if ( entry_it != other_entry_points.end() )
{
// here we know that we possibly left LS
// we must still check if we didn't get back on the same point
exit_operation = exit_op;
exit_id = point_info(other_id, point);
// erase the corresponding entry point
other_entry_points.erase(entry_it);
}
}
bool is_outside() const
{
// if we didn't entered anything in the past, we're outside
return other_entry_points.empty();
}
overlay::operation_type get_exit_operation() const
{
return exit_operation;
}
Point const& get_exit_point() const
{
BOOST_ASSERT(exit_operation != overlay::operation_none);
return exit_id.point();
}
void reset_detected_exit()
{
exit_operation = overlay::operation_none;
}
void reset()
{
exit_operation = overlay::operation_none;
other_entry_points.clear();
}
private:
overlay::operation_type exit_operation;
point_info exit_id;
std::vector<point_info> other_entry_points; // TODO: use map here or sorted vector?
};
template <boundary_query BoundaryQuery,
typename Point,
typename BoundaryChecker>
static inline bool is_endpoint_on_boundary(Point const& pt,
BoundaryChecker & boundary_checker)
{
return boundary_checker.template is_endpoint_boundary<BoundaryQuery>(pt);
}
template <boundary_query BoundaryQuery,
typename IntersectionPoint,
typename OperationInfo,
typename BoundaryChecker>
static inline bool is_ip_on_boundary(IntersectionPoint const& ip,
OperationInfo const& operation_info,
BoundaryChecker & boundary_checker,
segment_identifier const& seg_id)
{
boost::ignore_unused_variable_warning(seg_id);
bool res = false;
// IP on the last point of the linestring
if ( (BoundaryQuery == boundary_back || BoundaryQuery == boundary_any)
&& operation_info.operation == overlay::operation_blocked )
{
BOOST_ASSERT(operation_info.position == overlay::position_back);
// check if this point is a boundary
res = boundary_checker.template is_endpoint_boundary<boundary_back>(ip);
#ifdef BOOST_GEOMETRY_DEBUG_RELATE
BOOST_ASSERT(res == boundary_checker.template is_boundary<boundary_back>(ip, seg_id));
#endif
}
// IP on the last point of the linestring
else if ( (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);
#ifdef BOOST_GEOMETRY_DEBUG_RELATE
BOOST_ASSERT(res == boundary_checker.template is_boundary<boundary_front>(ip, seg_id));
#endif
}
// IP somewhere in the interior
else
{
#ifdef BOOST_GEOMETRY_DEBUG_RELATE
BOOST_ASSERT(res == boundary_checker.template is_boundary<boundary_any>(ip, seg_id));
#endif
}
return res;
}
}} // namespace detail::relate
#endif // DOXYGEN_NO_DETAIL
}} // namespace boost::geometry
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_FOLLOW_HELPERS_HPP

View File

@@ -0,0 +1,819 @@
// 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_LINEAR_AREAL_HPP
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_LINEAR_AREAL_HPP
#include <boost/geometry/algorithms/detail/sub_geometry.hpp>
#include <boost/geometry/algorithms/detail/range_helpers.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 {
template <typename Geometry,
bool IsMulti = boost::is_base_of
<
multi_tag,
typename geometry::tag<Geometry>::type
>::value
>
struct simple_geometry
{
template <typename Id>
static inline Geometry & apply(Geometry & g, Id const& ) { return g; }
};
template <typename Geometry>
struct simple_geometry<Geometry, true>
{
template <typename Id>
static inline
typename boost::mpl::if_c
<
boost::is_const<Geometry>::value,
typename boost::range_value<Geometry>::type const&,
typename boost::range_value<Geometry>::type
>::type
apply(Geometry & g, Id const& id)
{
BOOST_ASSERT(id.multi_index >= 0);
return *(boost::begin(g) + id.multi_index);
}
};
// TODO: In the worst case for MultiLinestring/MultiPolygon this is O(NM)
// Use the rtree in this case!
template <typename Geometry2, typename Result, typename BoundaryChecker, bool TransposeResult>
class no_turns_la_linestring_pred
{
public:
no_turns_la_linestring_pred(Geometry2 const& geometry2,
Result & res,
BoundaryChecker const& boundary_checker)
: m_geometry2(geometry2)
, m_result_ptr(boost::addressof(res))
, m_boundary_checker_ptr(boost::addressof(boundary_checker))
, m_interrupt_flags(0)
{}
template <typename Linestring>
bool operator()(Linestring const& linestring)
{
std::size_t count = boost::size(linestring);
// invalid input
if ( count < 2 )
{
// ignore
// TODO: throw an exception?
return true;
}
int pig = detail::within::point_in_geometry(range::front(linestring), m_geometry2);
BOOST_ASSERT_MSG(pig != 0, "There should be no IPs");
if ( pig > 0 )
{
update<interior, interior, '1', TransposeResult>(*m_result_ptr);
m_interrupt_flags |= 1;
}
else
{
update<interior, exterior, '1', TransposeResult>(*m_result_ptr);
m_interrupt_flags |= 2;
}
// check if there is a boundary
if ( m_boundary_checker_ptr->template
is_endpoint_boundary<boundary_front>(range::front(linestring))
|| m_boundary_checker_ptr->template
is_endpoint_boundary<boundary_back>(range::back(linestring)) )
{
if ( pig > 0 )
{
update<boundary, interior, '0', TransposeResult>(*m_result_ptr);
m_interrupt_flags |= 4;
}
else
{
update<boundary, exterior, '0', TransposeResult>(*m_result_ptr);
m_interrupt_flags |= 8;
}
return m_interrupt_flags != 0xF;
}
return true;
}
private:
Geometry2 const& m_geometry2;
Result * m_result_ptr;
const BoundaryChecker * m_boundary_checker_ptr;
unsigned m_interrupt_flags;
};
template <typename Result, bool TransposeResult>
class no_turns_la_areal_pred
{
public:
no_turns_la_areal_pred(Result & res)
: m_result_ptr(boost::addressof(res))
{}
template <typename Areal>
bool operator()(Areal const& areal)
{
// TODO:
// add an assertion for empty/invalid geometries
update<interior, exterior, '2', TransposeResult>(*m_result_ptr);
update<boundary, exterior, '1', TransposeResult>(*m_result_ptr);
return false;
}
private:
Result * m_result_ptr;
};
template <typename Geometry1, typename Geometry2, bool TransposeResult = false>
struct linear_areal
{
// check Linear / Areal
BOOST_STATIC_ASSERT(detail::group_dim<Geometry1>::value == 1
&& detail::group_dim<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)
{
// The result should be FFFFFFFFF
set<exterior, exterior, result_dimension<Geometry2>::value, TransposeResult>(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;
typedef typename std::vector<turn_type>::iterator turn_iterator;
std::vector<turn_type> turns;
interrupt_policy_linear_areal<Geometry2, Result> interrupt_policy(geometry2, result);
turns::get_turns<Geometry1, Geometry2>::apply(turns, geometry1, geometry2, interrupt_policy);
if ( result.interrupt )
return;
boundary_checker<Geometry1> boundary_checker1(geometry1);
no_turns_la_linestring_pred
<
Geometry2,
Result,
boundary_checker<Geometry1>,
TransposeResult
> pred1(geometry2, result, boundary_checker1);
for_each_disjoint_geometry_if<0, Geometry1>::apply(turns.begin(), turns.end(), geometry1, pred1);
if ( result.interrupt )
return;
no_turns_la_areal_pred<Result, !TransposeResult> pred2(result);
for_each_disjoint_geometry_if<1, Geometry2>::apply(turns.begin(), turns.end(), geometry2, pred2);
if ( result.interrupt )
return;
if ( turns.empty() )
return;
// This is set here because in the case if empty Areal geometry were passed
// those shouldn't be set
set<exterior, interior, '2', TransposeResult>(result);// FFFFFF2Fd
// TODO: NEED TO SUPPORT exterior, boundary
// FOR THIS WE MUST CHECK IF THE BOUNDARY OF THE POLYGON TREATED LIKE LINEAR RING GOES OUT OF THE LINESTRING G1
// THIS WON'T WORK IN ALL CASES!
set<exterior, boundary, '1', TransposeResult>(result);// FFFFFF21d
if ( result.interrupt )
return;
// for different multi and same ring id: x, u, i, c
// for same multi and different ring id: c, i, u, x
std::sort(turns.begin(), turns.end(), turns::less_seg_dist_op<0,2,3,1,4,0,0>());
turns_analyser<turn_type> analyser;
analyse_each_turn(result, analyser,
turns.begin(), turns.end(),
geometry1, geometry2,
boundary_checker1);
// TODO check if this is required by the result
if ( interrupt_policy.is_boundary_found )
{
}
}
template <typename Areal, typename Result>
class interrupt_policy_linear_areal
{
public:
static bool const enabled = true;
interrupt_policy_linear_areal(Areal const& areal, Result & result)
: m_result(result), m_areal(areal)
, is_boundary_found(false)
{}
// TODO: since we update result for some operations here, we must not do it in the analyser!
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)
{
if ( it->operations[0].operation == overlay::operation_intersection )
{
bool const no_interior_rings
= boost::empty(
geometry::interior_rings(
simple_geometry<Areal const>::apply(m_areal, it->operations[1].seg_id)));
// WARNING! THIS IS TRUE ONLY IF THE POLYGON IS SIMPLE!
// OR WITHOUT INTERIOR RINGS (AND OF COURSE VALID)
if ( no_interior_rings )
update<interior, interior, '1', TransposeResult>(m_result);
}
else if ( it->operations[0].operation == overlay::operation_continue )
{
update<interior, boundary, '1', TransposeResult>(m_result);
is_boundary_found = true;
}
else if ( ( it->operations[0].operation == overlay::operation_union
|| it->operations[0].operation == overlay::operation_blocked )
&& it->operations[0].position == overlay::position_middle )
{
// TODO: here we could also check the boundaries and set BB at this point
update<interior, boundary, '0', TransposeResult>(m_result);
}
}
return m_result.interrupt;
}
private:
Result & m_result;
Areal const& m_areal;
public:
bool is_boundary_found;
};
// This analyser should be used like Input or SinglePass Iterator
template <typename TurnInfo>
class turns_analyser
{
typedef typename TurnInfo::point_type turn_point_type;
static const std::size_t op_id = 0;
static const std::size_t other_op_id = 1;
public:
turns_analyser()
: m_previous_turn_ptr(0)
, m_previous_operation(overlay::operation_none)
, m_boundary_counter(0)
, m_interior_detected(false)
{}
template <typename Result,
typename TurnIt,
typename Geometry,
typename OtherGeometry,
typename BoundaryChecker>
void apply(Result & res,
TurnIt first, TurnIt it, TurnIt last,
Geometry const& geometry,
OtherGeometry const& other_geometry,
BoundaryChecker const& boundary_checker)
{
if ( it != last )
{
overlay::operation_type op = it->operations[op_id].operation;
if ( op != overlay::operation_union
&& op != overlay::operation_intersection
&& op != overlay::operation_blocked
&& op != overlay::operation_continue ) // operation_boundary / operation_boundary_intersection
{
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);
// handle possible exit
bool fake_enter_detected = false;
if ( m_exit_watcher.get_exit_operation() == overlay::operation_union )
{
// real exit point - may be multiple
// we know that we entered and now we exit
if ( !detail::equals::equals_point_point(it->point, m_exit_watcher.get_exit_point()) )
{
m_exit_watcher.reset_detected_exit();
// not the last IP
update<interior, exterior, '1', TransposeResult>(res);
}
// fake exit point, reset state
else if ( op == overlay::operation_intersection
|| op == overlay::operation_continue ) // operation_boundary
{
m_exit_watcher.reset_detected_exit();
fake_enter_detected = true;
}
}
else if ( m_exit_watcher.get_exit_operation() == overlay::operation_blocked )
{
// ignore multiple BLOCKs
if ( op == overlay::operation_blocked )
return;
m_exit_watcher.reset_detected_exit();
}
// NOTE: THE WHOLE m_interior_detected HANDLING IS HERE BECAUSE WE CAN'T EFFICIENTLY SORT TURNS (CORRECTLY)
// BECAUSE THE SAME IP MAY BE REPRESENTED BY TWO SEGMENTS WITH DIFFERENT DISTANCES
// IT WOULD REQUIRE THE CALCULATION OF MAX DISTANCE
// TODO: WE COULD GET RID OF THE TEST IF THE DISTANCES WERE NORMALIZED
// TODO: THIS IS POTENTIALLY ERROREOUS!
// THIS ALGORITHM DEPENDS ON SOME SPECIFIC SEQUENCE OF OPERATIONS
// IT WOULD GIVE WRONG RESULTS E.G.
// IN THE CASE OF SELF-TOUCHING POINT WHEN 'i' WOULD BE BEFORE 'u'
// handle the interior overlap
if ( m_interior_detected )
{
// real interior overlap
if ( !detail::equals::equals_point_point(it->point, m_previous_turn_ptr->point) )
{
update<interior, interior, '1', TransposeResult>(res);
m_interior_detected = false;
}
// fake interior overlap
else if ( op == overlay::operation_continue )
{
m_interior_detected = false;
}
}
// if the new linestring started just now,
// but the previous one went out on the previous point,
// we must check if the boundary of the previous segment is outside
// NOTE: couldn't it be integrated with the handling of the union above?
// THIS IS REDUNDANT WITH THE HANDLING OF THE END OF THE RANGE
//if ( first_in_range
// && ! fake_enter_detected
// && m_previous_operation == overlay::operation_union )
//{
// BOOST_ASSERT(it != first);
// BOOST_ASSERT(m_previous_turn_ptr);
// segment_identifier const& prev_seg_id = m_previous_turn_ptr->operations[op_id].seg_id;
// bool prev_back_b = is_endpoint_on_boundary<boundary_back>(
// range::back(sub_geometry::get(geometry, prev_seg_id)),
// boundary_checker);
// // if there is a boundary on the last point
// if ( prev_back_b )
// {
// update<boundary, exterior, '0', TransposeResult>(res);
// }
//}
// i/u, c/u
if ( op == overlay::operation_intersection
|| op == overlay::operation_continue ) // operation_boundary/operation_boundary_intersection
{
bool no_enters_detected = m_exit_watcher.is_outside();
m_exit_watcher.enter(it->point, other_id);
if ( op == overlay::operation_intersection )
{
if ( m_boundary_counter > 0 && it->operations[op_id].is_collinear )
--m_boundary_counter;
if ( m_boundary_counter == 0 )
{
// interiors overlaps
//update<interior, interior, '1', TransposeResult>(res);
// don't update now
// we might enter a boundary of some other ring on the same IP
m_interior_detected = true;
}
}
else // operation_boundary
{
// don't add to the count for all met boundaries
// only if this is the "new" boundary
if ( first_in_range || !it->operations[op_id].is_collinear )
++m_boundary_counter;
update<interior, boundary, '1', TransposeResult>(res);
}
bool this_b = is_ip_on_boundary<boundary_front>(it->point,
it->operations[op_id],
boundary_checker,
seg_id);
// going inside on boundary point
if ( this_b )
{
update<boundary, boundary, '0', TransposeResult>(res);
}
// going inside on non-boundary point
else
{
update<interior, boundary, '0', TransposeResult>(res);
// if we didn't enter in the past, we were outside
if ( no_enters_detected
&& !fake_enter_detected
&& it->operations[op_id].position != overlay::position_front )
{
bool from_inside = first_in_range
&& calculate_from_inside(geometry,
other_geometry,
*it);
if ( from_inside )
update<interior, interior, '1', TransposeResult>(res);
else
update<interior, exterior, '1', TransposeResult>(res);
// if it's the first IP then the first point is outside
if ( first_in_range )
{
bool front_b = is_endpoint_on_boundary<boundary_front>(
range::front(sub_geometry::get(geometry, seg_id)),
boundary_checker);
// if there is a boundary on the first point
if ( front_b )
{
if ( from_inside )
update<boundary, interior, '0', TransposeResult>(res);
else
update<boundary, exterior, '0', TransposeResult>(res);
}
}
}
}
}
// u/u, x/u
else if ( op == overlay::operation_union || op == overlay::operation_blocked )
{
bool op_blocked = op == overlay::operation_blocked;
bool no_enters_detected = m_exit_watcher.is_outside();
if ( op == overlay::operation_union )
{
if ( m_boundary_counter > 0 && it->operations[op_id].is_collinear )
--m_boundary_counter;
}
else // overlay::operation_blocked
{
m_boundary_counter = 0;
}
// we're inside, possibly going out right now
if ( ! no_enters_detected )
{
if ( op_blocked )
{
// 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) )
{
update<boundary, boundary, '0', TransposeResult>(res);
}
}
// union, inside, but no exit -> collinear on self-intersection point
// not needed since we're already inside the boundary
/*else if ( !exit_detected )
{
update<interior, boundary, '0', TransposeResult>(res);
}*/
}
// we're outside or inside and this is the first turn
else
{
bool this_b = is_ip_on_boundary<boundary_any>(it->point,
it->operations[op_id],
boundary_checker,
seg_id);
// if current IP is on boundary of the geometry
if ( this_b )
{
update<boundary, boundary, '0', TransposeResult>(res);
}
// if current IP is not on boundary of the geometry
else
{
update<interior, boundary, '0', TransposeResult>(res);
}
// TODO: very similar code is used in the handling of intersection
if ( it->operations[op_id].position != overlay::position_front )
{
bool first_from_inside = first_in_range
&& calculate_from_inside(geometry,
other_geometry,
*it);
if ( first_from_inside )
{
update<interior, interior, '1', TransposeResult>(res);
// notify the exit_watcher that we started inside
m_exit_watcher.enter(it->point, other_id);
}
else
{
update<interior, exterior, '1', TransposeResult>(res);
}
// first IP on the last segment point - this means that the first point is outside or inside
if ( first_in_range && ( !this_b || op_blocked ) )
{
bool front_b = is_endpoint_on_boundary<boundary_front>(
range::front(sub_geometry::get(geometry, seg_id)),
boundary_checker);
// if there is a boundary on the first point
if ( front_b )
{
if ( first_from_inside )
update<boundary, interior, '0', TransposeResult>(res);
else
update<boundary, exterior, '0', TransposeResult>(res);
}
}
}
}
// if we're going along a boundary, we exit only if the linestring was collinear
if ( m_boundary_counter == 0
|| it->operations[op_id].is_collinear )
{
// notify the exit watcher about the possible exit
m_exit_watcher.exit(it->point, other_id, op);
}
}
// 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
else
{
// here, the possible exit is the real one
// we know that we entered and now we exit
if ( /*m_exit_watcher.get_exit_operation() == overlay::operation_union // THIS CHECK IS REDUNDANT
||*/ m_previous_operation == overlay::operation_union )
{
// for sure
update<interior, exterior, '1', TransposeResult>(res);
BOOST_ASSERT(first != last);
BOOST_ASSERT(m_previous_turn_ptr);
segment_identifier const& prev_seg_id = m_previous_turn_ptr->operations[op_id].seg_id;
bool prev_back_b = is_endpoint_on_boundary<boundary_back>(
range::back(sub_geometry::get(geometry, prev_seg_id)),
boundary_checker);
// if there is a boundary on the last point
if ( prev_back_b )
{
update<boundary, exterior, '0', TransposeResult>(res);
}
}
// we might enter some Areal and didn't go out,
else if ( m_previous_operation == overlay::operation_intersection )
{
// just in case
update<interior, interior, '1', TransposeResult>(res);
BOOST_ASSERT(first != last);
BOOST_ASSERT(m_previous_turn_ptr);
segment_identifier const& prev_seg_id = m_previous_turn_ptr->operations[op_id].seg_id;
bool prev_back_b = is_endpoint_on_boundary<boundary_back>(
range::back(sub_geometry::get(geometry, prev_seg_id)),
boundary_checker);
// if there is a boundary on the last point
if ( prev_back_b )
{
update<boundary, interior, '0', TransposeResult>(res);
}
}
// handle the interior overlap
if ( m_interior_detected )
{
// just in case
update<interior, interior, '1', TransposeResult>(res);
m_interior_detected = false;
}
BOOST_ASSERT_MSG(m_previous_operation != overlay::operation_continue,
"Unexpected operation! Probably the error in get_turns(L,A) or relate(L,A)");
// Reset exit watcher before the analysis of the next Linestring
m_exit_watcher.reset();
m_boundary_counter = 0;
}
}
template <typename Geometry1, typename Geometry2, typename Turn>
static inline bool calculate_from_inside(Geometry1 const& geometry1,
Geometry2 const& geometry2,
Turn const& turn)
{
if ( turn.operations[op_id].position == overlay::position_front )
return false;
static const bool reverse2 = detail::overlay::do_reverse<
geometry::point_order<Geometry2>::value
>::value;
typedef typename closeable_view
<
typename range_type<Geometry2>::type const,
closure<Geometry2>::value
>::type range2_cview;
typedef typename reversible_view
<
range2_cview const,
reverse2 ? iterate_reverse : iterate_forward
>::type range2_view;
typedef typename sub_geometry::result_type<Geometry1 const>::type range1_ref;
range1_ref range1 = sub_geometry::get(geometry1, turn.operations[op_id].seg_id);
range2_cview const cview(sub_geometry::get(geometry2, turn.operations[other_op_id].seg_id));
range2_view const range2(cview);
std::size_t s1 = boost::size(range1);
std::size_t s2 = boost::size(range2);
BOOST_ASSERT(s1 > 1 && s2 > 2);
std::size_t seg_count2 = s2 - 1;
std::size_t p_seg_ij = turn.operations[op_id].seg_id.segment_index;
std::size_t q_seg_ij = turn.operations[other_op_id].seg_id.segment_index;
BOOST_ASSERT(p_seg_ij + 1 < s1 && q_seg_ij + 1 < s2);
point1_type const& pi = range::at(range1, p_seg_ij);
point2_type const& qi = range::at(range2, q_seg_ij);
point2_type const& qj = range::at(range2, q_seg_ij + 1);
point1_type qi_conv;
geometry::convert(qi, qi_conv);
bool is_ip_qj = equals::equals_point_point(turn.point, qj);
// TODO: test this!
BOOST_ASSERT(!equals::equals_point_point(turn.point, pi));
BOOST_ASSERT(!equals::equals_point_point(turn.point, qi));
point1_type new_pj;
geometry::convert(turn.point, new_pj);
if ( is_ip_qj )
{
std::size_t q_seg_jk = (q_seg_ij + 1) % seg_count2;
BOOST_ASSERT(q_seg_jk + 1 < s2);
point2_type const& qk = range::at(range2, q_seg_jk + 1);
// Will this sequence of point be always correct?
overlay::side_calculator<point1_type, point2_type> side_calc(qi_conv, new_pj, pi, qi, qj, qk);
return calculate_from_inside_sides(side_calc);
}
else
{
point1_type new_qj;
geometry::convert(turn.point, new_qj);
overlay::side_calculator<point1_type, point2_type> side_calc(qi_conv, new_pj, pi, qi, new_qj, qj);
return calculate_from_inside_sides(side_calc);
}
}
template <typename SideCalc>
static inline bool calculate_from_inside_sides(SideCalc const& side_calc)
{
// From equal<>
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<turn_point_type> m_exit_watcher;
segment_watcher m_seg_watcher;
TurnInfo * m_previous_turn_ptr;
overlay::operation_type m_previous_operation;
unsigned m_boundary_counter;
bool m_interior_detected;
};
template <typename Result,
typename TurnIt,
typename Analyser,
typename Geometry,
typename OtherGeometry,
typename BoundaryChecker>
static inline void analyse_each_turn(Result & res,
Analyser & analyser,
TurnIt first, TurnIt last,
Geometry const& geometry,
OtherGeometry const& other_geometry,
BoundaryChecker const& boundary_checker)
{
if ( first == last )
return;
for ( TurnIt it = first ; it != last ; ++it )
{
analyser.apply(res, first, it, last,
geometry, other_geometry,
boundary_checker);
if ( res.interrupt )
return;
}
analyser.apply(res, first, last, last,
geometry, other_geometry,
boundary_checker);
}
};
template <typename Geometry1, typename Geometry2>
struct areal_linear
{
template <typename Result>
static inline void apply(Geometry1 const& geometry1, Geometry2 const& geometry2, Result & result)
{
linear_areal<Geometry2, Geometry1, true>::apply(geometry2, geometry1, result);
}
};
}} // namespace detail::relate
#endif // DOXYGEN_NO_DETAIL
}} // namespace boost::geometry
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_LINEAR_AREAL_HPP

View File

@@ -20,6 +20,7 @@
#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
{
@@ -27,113 +28,12 @@ namespace boost { namespace geometry
#ifndef DOXYGEN_NO_DETAIL
namespace detail { namespace relate {
enum linestring_kind { linestring_exterior, linestring_point, linestring_closed, linestring_open };
template <typename Linestring>
linestring_kind check_linestring_kind(Linestring const& ls)
{
std::size_t count = boost::size(ls);
if ( count == 0 )
return linestring_exterior;
else if ( count == 1 )
return linestring_point;
else
{
bool equal_fb = equals::equals_point_point(range::front(ls), range::back(ls));
if ( equal_fb )
{
typedef typename boost::range_iterator<Linestring const>::type iterator;
iterator first = boost::begin(ls);
++first;
iterator last = boost::end(ls);
--last;
for ( iterator it = first ; it != last ; ++it )
{
if ( !equals::equals_point_point(range::front(ls), *it) )
return linestring_closed;
}
return linestring_point;
}
else
return linestring_open;
}
}
// TODO:
// For 1-point linestrings or with all equal points turns won't be generated!
// Check for those degenerated cases may be connected with this one!
template <std::size_t OpId,
typename Geometry,
typename Tag = typename geometry::tag<Geometry>::type>
struct for_each_disjoint_linestring_if {};
template <std::size_t OpId, typename Geometry>
struct for_each_disjoint_linestring_if<OpId, Geometry, linestring_tag>
{
template <typename TurnIt, typename Pred>
static inline bool apply(TurnIt first, TurnIt last,
Geometry const& geometry,
Pred & pred)
{
if ( first != last )
return false;
pred(geometry);
return true;
}
};
template <std::size_t OpId, typename Geometry>
struct for_each_disjoint_linestring_if<OpId, Geometry, multi_linestring_tag>
{
template <typename TurnIt, typename Pred>
static inline bool apply(TurnIt first, TurnIt last,
Geometry const& geometry,
Pred & pred)
{
BOOST_ASSERT(first != last);
std::size_t count = boost::size(geometry);
std::vector<bool> detected_intersections(count, false);
for ( TurnIt it = first ; it != last ; ++it )
{
int multi_index = it->operations[OpId].seg_id.multi_index;
BOOST_ASSERT(multi_index >= 0);
std::size_t index = static_cast<std::size_t>(multi_index);
BOOST_ASSERT(index < count);
detected_intersections[index] = true;
}
bool found = false;
for ( std::vector<bool>::iterator it = detected_intersections.begin() ;
it != detected_intersections.end() ; ++it )
{
// if there were no intersections for this multi_index
if ( *it == false )
{
found = true;
bool cont = pred(
*(boost::begin(geometry)
+ std::distance(detected_intersections.begin(), it)));
if ( !cont )
break;
}
}
return found;
}
};
template <std::size_t OpId, typename Result, typename BoundaryChecker>
template <typename Result, typename BoundaryChecker, bool TransposeResult>
class disjoint_linestring_pred
{
static const bool transpose_result = OpId != 0;
public:
disjoint_linestring_pred(Result & res,
BoundaryChecker & boundary_checker)
BoundaryChecker const& boundary_checker)
: m_result_ptr(boost::addressof(res))
, m_boundary_checker_ptr(boost::addressof(boundary_checker))
{}
@@ -151,7 +51,7 @@ public:
return true;
}
update<interior, exterior, '1', transpose_result>(*m_result_ptr);
update<interior, exterior, '1', TransposeResult>(*m_result_ptr);
// check if there is a boundary
if ( m_boundary_checker_ptr->template
@@ -159,7 +59,7 @@ public:
|| m_boundary_checker_ptr->template
is_endpoint_boundary<boundary_back>(range::back(linestring)) )
{
update<boundary, exterior, '0', transpose_result>(*m_result_ptr);
update<boundary, exterior, '0', TransposeResult>(*m_result_ptr);
return false;
}
@@ -169,95 +69,128 @@ public:
private:
Result * m_result_ptr;
BoundaryChecker * m_boundary_checker_ptr;
const BoundaryChecker * m_boundary_checker_ptr;
};
//enum linestring_kind { linestring_exterior, linestring_point, linestring_closed, linestring_open };
//
//template <typename Linestring>
//linestring_kind check_linestring_kind(Linestring const& ls)
//{
// std::size_t count = boost::size(ls);
// if ( count == 0 )
// return linestring_exterior;
// else if ( count == 1 )
// return linestring_point;
// else
// {
// bool equal_fb = equals::equals_point_point(range::front(ls), range::back(ls));
// if ( equal_fb )
// {
// typedef typename boost::range_iterator<Linestring const>::type iterator;
// iterator first = boost::begin(ls);
// ++first;
// iterator last = boost::end(ls);
// --last;
// for ( iterator it = first ; it != last ; ++it )
// {
// if ( !equals::equals_point_point(range::front(ls), *it) )
// return linestring_closed;
// }
//
// return linestring_point;
// }
// else
// return linestring_open;
// }
//}
// Called in a loop for:
// Ls/Ls - worst O(N) - 1x point_in_geometry(MLs)
// Ls/MLs - worst O(N) - 1x point_in_geometry(MLs)
// MLs/Ls - worst O(N^2) - Bx point_in_geometry(Ls)
// MLs/MLs - worst O(N^2) - Bx point_in_geometry(Ls)
// TODO: later use spatial index
template <std::size_t OpId, typename Result, typename BoundaryChecker, typename OtherGeometry>
class disjoint_linestring_pred_with_point_size_handling
{
static const bool transpose_result = OpId != 0;
public:
disjoint_linestring_pred_with_point_size_handling(Result & res,
BoundaryChecker & boundary_checker,
OtherGeometry const& other_geometry)
: m_result_ptr(boost::addressof(res))
, m_boundary_checker_ptr(boost::addressof(boundary_checker))
, m_other_geometry(boost::addressof(other_geometry))
, m_detected_mask_point(0)
, m_detected_open_boundary(false)
{}
template <typename Linestring>
bool operator()(Linestring const& linestring)
{
linestring_kind lk = check_linestring_kind(linestring);
if ( lk == linestring_point ) // just an optimization
{
if ( m_detected_mask_point != 7 )
{
// check the relation
int pig = within::point_in_geometry(range::front(linestring), *m_other_geometry);
// point inside
if ( pig > 0 )
{
update<interior, interior, '0', transpose_result>(*m_result_ptr);
m_detected_mask_point |= 1;
}
// point on boundary
else if ( pig == 0 )
{
update<interior, boundary, '0', transpose_result>(*m_result_ptr);
m_detected_mask_point |= 2;
}
// point outside
else
{
update<interior, exterior, '0', transpose_result>(*m_result_ptr);
m_detected_mask_point |= 4;
}
}
}
// NOTE: For closed Linestrings I/I=1 could be set automatically
// but for MultiLinestrings endpoints of closed Linestrings must also be checked for boundary
else if ( lk == linestring_open || lk == linestring_closed )
{
if ( !m_detected_open_boundary ) // just an optimization
{
update<interior, exterior, '1', transpose_result>(*m_result_ptr);
// check if there is a boundary
if ( m_boundary_checker_ptr->template
is_endpoint_boundary<boundary_front>(range::front(linestring))
|| m_boundary_checker_ptr->template
is_endpoint_boundary<boundary_back>(range::back(linestring)) )
{
update<boundary, exterior, '0', transpose_result>(*m_result_ptr);
m_detected_open_boundary = true;
}
}
}
bool all_detected = m_detected_mask_point == 7 && m_detected_open_boundary;
return !all_detected && !m_result_ptr->interrupt;
}
private:
Result * m_result_ptr;
BoundaryChecker * m_boundary_checker_ptr;
const OtherGeometry * m_other_geometry;
char m_detected_mask_point;
bool m_detected_open_boundary;
};
//template <std::size_t OpId, typename Result, typename BoundaryChecker, typename OtherGeometry>
//class disjoint_linestring_pred_with_point_size_handling
//{
// static const bool transpose_result = OpId != 0;
//
//public:
// disjoint_linestring_pred_with_point_size_handling(Result & res,
// BoundaryChecker & boundary_checker,
// OtherGeometry const& other_geometry)
// : m_result_ptr(boost::addressof(res))
// , m_boundary_checker_ptr(boost::addressof(boundary_checker))
// , m_other_geometry(boost::addressof(other_geometry))
// , m_detected_mask_point(0)
// , m_detected_open_boundary(false)
// {}
//
// template <typename Linestring>
// bool operator()(Linestring const& linestring)
// {
// linestring_kind lk = check_linestring_kind(linestring);
//
// if ( lk == linestring_point ) // just an optimization
// {
// if ( m_detected_mask_point != 7 )
// {
// // check the relation
// int pig = within::point_in_geometry(range::front(linestring), *m_other_geometry);
//
// // point inside
// if ( pig > 0 )
// {
// update<interior, interior, '0', transpose_result>(*m_result_ptr);
// m_detected_mask_point |= 1;
// }
// // point on boundary
// else if ( pig == 0 )
// {
// update<interior, boundary, '0', transpose_result>(*m_result_ptr);
// m_detected_mask_point |= 2;
// }
// // point outside
// else
// {
// update<interior, exterior, '0', transpose_result>(*m_result_ptr);
// m_detected_mask_point |= 4;
// }
// }
// }
// // NOTE: For closed Linestrings I/I=1 could be set automatically
// // but for MultiLinestrings endpoints of closed Linestrings must also be checked for boundary
// else if ( lk == linestring_open || lk == linestring_closed )
// {
// if ( !m_detected_open_boundary ) // just an optimization
// {
// update<interior, exterior, '1', transpose_result>(*m_result_ptr);
//
// // check if there is a boundary
// if ( m_boundary_checker_ptr->template
// is_endpoint_boundary<boundary_front>(range::front(linestring))
// || m_boundary_checker_ptr->template
// is_endpoint_boundary<boundary_back>(range::back(linestring)) )
// {
// update<boundary, exterior, '0', transpose_result>(*m_result_ptr);
//
// m_detected_open_boundary = true;
// }
// }
// }
//
// bool all_detected = m_detected_mask_point == 7 && m_detected_open_boundary;
// return !all_detected && !m_result_ptr->interrupt;
// }
//
//private:
// Result * m_result_ptr;
// BoundaryChecker * m_boundary_checker_ptr;
// const OtherGeometry * m_other_geometry;
// char m_detected_mask_point;
// bool m_detected_open_boundary;
//};
template <typename Geometry1, typename Geometry2>
struct linear_linear
@@ -280,19 +213,22 @@ struct linear_linear
typedef typename std::vector<turn_type>::iterator turn_iterator;
std::vector<turn_type> turns;
// TODO: INTEGRATE INTERRUPT POLICY WITH THE PASSED RESULT
interrupt_policy_linear_linear<Result> interrupt_policy(result);
turns::get_turns<Geometry1, Geometry2>::apply(turns, geometry1, geometry2);
turns::get_turns<Geometry1, Geometry2>::apply(turns, geometry1, geometry2, interrupt_policy);
if ( result.interrupt )
return;
boundary_checker<Geometry1> boundary_checker1(geometry1);
disjoint_linestring_pred<0, Result, boundary_checker<Geometry1> > pred1(result, boundary_checker1);
for_each_disjoint_linestring_if<0, Geometry1>::apply(turns.begin(), turns.end(), geometry1, pred1);
disjoint_linestring_pred<Result, boundary_checker<Geometry1>, false> pred1(result, boundary_checker1);
for_each_disjoint_geometry_if<0, Geometry1>::apply(turns.begin(), turns.end(), geometry1, pred1);
if ( result.interrupt )
return;
boundary_checker<Geometry2> boundary_checker2(geometry2);
disjoint_linestring_pred<1, Result, boundary_checker<Geometry2> > pred2(result, boundary_checker2);
for_each_disjoint_linestring_if<1, Geometry2>::apply(turns.begin(), turns.end(), geometry2, pred2);
disjoint_linestring_pred<Result, boundary_checker<Geometry2>, true> pred2(result, boundary_checker2);
for_each_disjoint_geometry_if<1, Geometry2>::apply(turns.begin(), turns.end(), geometry2, pred2);
if ( result.interrupt )
return;
@@ -305,6 +241,7 @@ struct linear_linear
// TODO: ADD A CHECK TO THE RESULT INDICATING IF THE FIRST AND/OR SECOND GEOMETRY MUST BE ANALYSED
{
// x, u, i, c
std::sort(turns.begin(), turns.end(), turns::less_seg_dist_op<0,2,3,1,4,0,0>());
turns_analyser<0, turn_type> analyser;
@@ -318,6 +255,7 @@ struct linear_linear
return;
{
// x, u, i, c
std::sort(turns.begin(), turns.end(), turns::less_seg_dist_op<0,2,3,1,4,0,1>());
turns_analyser<1, turn_type> analyser;
@@ -328,147 +266,47 @@ struct linear_linear
}
}
// TODO: rename to point_id_ref?
template <typename Point>
class point_identifier
template <typename Result>
class interrupt_policy_linear_linear
{
public:
point_identifier() : sid_ptr(0), pt_ptr(0) {}
point_identifier(segment_identifier const& sid, Point const& pt)
: sid_ptr(boost::addressof(sid))
, pt_ptr(boost::addressof(pt))
{}
segment_identifier const& seg_id() const
{
BOOST_ASSERT(sid_ptr);
return *sid_ptr;
}
Point const& point() const
{
BOOST_ASSERT(pt_ptr);
return *pt_ptr;
}
static bool const enabled = true;
//friend bool operator==(point_identifier const& l, point_identifier const& r)
//{
// return l.seg_id() == r.seg_id()
// && detail::equals::equals_point_point(l.point(), r.point());
//}
private:
const segment_identifier * sid_ptr;
const Point * pt_ptr;
};
class same_ranges
{
public:
same_ranges(segment_identifier const& sid)
: sid_ptr(boost::addressof(sid))
explicit interrupt_policy_linear_linear(Result & result)
: m_result(result)
{}
bool operator()(segment_identifier const& sid) const
// TODO: since we update result for some operations here, we must not do it in the analyser!
template <typename Range>
inline bool apply(Range const& turns)
{
return sid.multi_index == sid_ptr->multi_index
&& sid.ring_index == sid_ptr->ring_index;
}
template <typename Point>
bool operator()(point_identifier<Point> const& pid) const
{
return operator()(pid.seg_id());
}
private:
const segment_identifier * sid_ptr;
};
class segment_watcher
{
public:
segment_watcher()
: m_seg_id_ptr(0)
{}
bool update(segment_identifier const& seg_id)
{
bool result = m_seg_id_ptr == 0 || !same_ranges(*m_seg_id_ptr)(seg_id);
m_seg_id_ptr = boost::addressof(seg_id);
return result;
}
private:
const segment_identifier * m_seg_id_ptr;
};
template <typename Point>
class exit_watcher
{
typedef point_identifier<Point> point_info;
public:
exit_watcher()
: exit_operation(overlay::operation_none)
{}
// returns true if before the call we were outside
bool enter(Point const& point, segment_identifier const& other_id)
{
bool result = other_entry_points.empty();
other_entry_points.push_back(point_info(other_id, point));
return result;
}
// returns true if before the call we were outside
bool exit(Point const& point,
segment_identifier const& other_id,
overlay::operation_type exit_op)
{
// if we didn't entered anything in the past, we're outside
if ( other_entry_points.empty() )
return true;
typedef typename std::vector<point_info>::iterator point_iterator;
// search for the entry point in the same range of other geometry
point_iterator entry_it = std::find_if(other_entry_points.begin(),
other_entry_points.end(),
same_ranges(other_id));
// this end point has corresponding entry point
if ( entry_it != other_entry_points.end() )
typedef typename boost::range_iterator<Range const>::type iterator;
for (iterator it = boost::begin(turns) ; it != boost::end(turns) ; ++it)
{
// here we know that we possibly left LS
// we must still check if we didn't get back on the same point
exit_operation = exit_op;
exit_id = point_info(other_id, point);
// erase the corresponding entry point
other_entry_points.erase(entry_it);
if ( it->operations[0].operation == overlay::operation_intersection
|| it->operations[1].operation == overlay::operation_intersection )
{
update<interior, interior, '1'>(m_result);
}
else if ( ( it->operations[0].operation == overlay::operation_union
|| it->operations[0].operation == overlay::operation_blocked
|| it->operations[1].operation == overlay::operation_union
|| it->operations[1].operation == overlay::operation_blocked )
&& it->operations[0].position == overlay::position_middle
&& it->operations[1].position == overlay::position_middle )
{
// TODO: here we could also check the boundaries and set IB,BI,BB at this point
update<interior, interior, '0'>(m_result);
}
}
return false;
}
overlay::operation_type get_exit_operation() const
{
return exit_operation;
}
Point const& get_exit_point() const
{
BOOST_ASSERT(exit_operation != overlay::operation_none);
return exit_id.point();
}
void reset_detected_exit()
{
exit_operation = overlay::operation_none;
return m_result.interrupt;
}
private:
overlay::operation_type exit_operation;
point_info exit_id;
std::vector<point_info> other_entry_points; // TODO: use map here or sorted vector?
Result & m_result;
};
// This analyser should be used like Input or SinglePass Iterator
@@ -497,8 +335,8 @@ struct linear_linear
TurnIt first, TurnIt it, TurnIt last,
Geometry const& geometry,
OtherGeometry const& other_geometry,
BoundaryChecker & boundary_checker,
OtherBoundaryChecker & other_boundary_checker)
BoundaryChecker const& boundary_checker,
OtherBoundaryChecker const& other_boundary_checker)
{
if ( it != last )
{
@@ -549,30 +387,32 @@ struct linear_linear
// but the previous one went out on the previous point,
// we must check if the boundary of the previous segment is outside
// NOTE: couldn't it be integrated with the handling of the union above?
if ( first_in_range
&& ! fake_enter_detected
&& m_previous_operation == overlay::operation_union )
{
BOOST_ASSERT(it != first);
BOOST_ASSERT(m_previous_turn_ptr);
// THIS IS REDUNDANT WITH THE HANDLING OF THE END OF THE RANGE
//if ( first_in_range
// && ! fake_enter_detected
// && m_previous_operation == overlay::operation_union )
//{
// BOOST_ASSERT(it != first);
// BOOST_ASSERT(m_previous_turn_ptr);
segment_identifier const& prev_seg_id = m_previous_turn_ptr->operations[op_id].seg_id;
// segment_identifier const& prev_seg_id = m_previous_turn_ptr->operations[op_id].seg_id;
bool prev_back_b = is_endpoint_on_boundary<boundary_back>(
range::back(sub_geometry::get(geometry, prev_seg_id)),
boundary_checker);
// bool prev_back_b = is_endpoint_on_boundary<boundary_back>(
// range::back(sub_geometry::get(geometry, prev_seg_id)),
// boundary_checker);
// if there is a boundary on the last point
if ( prev_back_b )
{
update<boundary, exterior, '0', transpose_result>(res);
}
}
// // if there is a boundary on the last point
// if ( prev_back_b )
// {
// update<boundary, exterior, '0', transpose_result>(res);
// }
//}
// i/i, i/x, i/u
if ( op == overlay::operation_intersection )
{
bool was_outside = m_exit_watcher.enter(it->point, other_id);
bool was_outside = m_exit_watcher.is_outside();
m_exit_watcher.enter(it->point, other_id);
// interiors overlaps
update<interior, interior, '1', transpose_result>(res);
@@ -629,11 +469,23 @@ struct linear_linear
// u/i, u/u, u/x, x/i, x/u, x/x
else if ( op == overlay::operation_union || op == overlay::operation_blocked )
{
bool op_blocked = op == overlay::operation_blocked;
bool was_outside = m_exit_watcher.exit(it->point, other_id, op);
// TODO: is exit watcher still needed?
// couldn't is_collinear and some going inside counter be used instead?
// we're inside, possibly going out right now
if ( ! was_outside )
bool is_collinear = it->operations[op_id].is_collinear;
bool was_outside = m_exit_watcher.is_outside();
// to exit we must be currently inside and the current segment must be collinear
if ( !was_outside && is_collinear )
{
m_exit_watcher.exit(it->point, other_id, op);
}
bool op_blocked = op == overlay::operation_blocked;
// we're inside and going out from inside
// possibly going out right now
if ( ! was_outside && is_collinear )
{
if ( op_blocked )
{
@@ -658,14 +510,19 @@ struct linear_linear
}
}
}
// we're outside
// we're outside or intersects some segment from the outside
else
{
update<interior, exterior, '1', transpose_result>(res);
// if we are truly outside
if ( was_outside /*&& !is_collinear*/ )
{
update<interior, exterior, '1', transpose_result>(res);
}
// boundaries don't overlap - just an optimization
if ( it->method == overlay::method_crosses )
{
// the L1 is going from one side of the L2 to the other through the point
update<interior, interior, '0', transpose_result>(res);
// it's the first point in range
@@ -715,7 +572,7 @@ struct linear_linear
}
// first IP on the last segment point - this means that the first point is outside
if ( first_in_range && ( !this_b || op_blocked ) )
if ( first_in_range && ( !this_b || op_blocked ) && was_outside /*&& !is_collinear*/ )
{
bool front_b = is_endpoint_on_boundary<boundary_front>(
range::front(sub_geometry::get(geometry, seg_id)),
@@ -742,8 +599,8 @@ struct linear_linear
{
// here, the possible exit is the real one
// we know that we entered and now we exit
if ( m_exit_watcher.get_exit_operation() == overlay::operation_union // THIS CHECK IS REDUNDANT
|| m_previous_operation == overlay::operation_union )
if ( /*m_exit_watcher.get_exit_operation() == overlay::operation_union // THIS CHECK IS REDUNDANT
||*/ m_previous_operation == overlay::operation_union )
{
// for sure
update<interior, exterior, '1', transpose_result>(res);
@@ -751,7 +608,7 @@ struct linear_linear
BOOST_ASSERT(first != last);
BOOST_ASSERT(m_previous_turn_ptr);
segment_identifier const& prev_seg_id = m_previous_turn_ptr->operations[OpId].seg_id;
segment_identifier const& prev_seg_id = m_previous_turn_ptr->operations[op_id].seg_id;
bool prev_back_b = is_endpoint_on_boundary<boundary_back>(
range::back(sub_geometry::get(geometry, prev_seg_id)),
@@ -763,67 +620,14 @@ struct linear_linear
update<boundary, exterior, '0', transpose_result>(res);
}
}
// Just in case,
// reset exit watcher before the analysis of the next Linestring
// note that if there are some enters stored there may be some error above
m_exit_watcher.reset();
}
}
template <boundary_query BoundaryQuery,
typename Point,
typename BoundaryChecker>
static inline
bool is_endpoint_on_boundary(Point const& pt,
BoundaryChecker & boundary_checker)
{
return boundary_checker.template is_endpoint_boundary<BoundaryQuery>(pt);
}
template <boundary_query BoundaryQuery,
typename IntersectionPoint,
typename OperationInfo,
typename BoundaryChecker>
static inline
bool is_ip_on_boundary(IntersectionPoint const& ip,
OperationInfo const& operation_info,
BoundaryChecker & boundary_checker,
segment_identifier const& seg_id)
{
boost::ignore_unused_variable_warning(seg_id);
bool res = false;
// IP on the last point of the linestring
if ( (BoundaryQuery == boundary_back || BoundaryQuery == boundary_any)
&& operation_info.operation == overlay::operation_blocked )
{
BOOST_ASSERT(operation_info.position == overlay::position_back);
// check if this point is a boundary
res = boundary_checker.template is_endpoint_boundary<boundary_back>(ip);
#ifdef BOOST_GEOMETRY_DEBUG_RELATE_LINEAR_LINEAR
BOOST_ASSERT(res == boundary_checker.template is_boundary<boundary_back>(ip, seg_id));
#endif
}
// IP on the last point of the linestring
else if ( (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);
#ifdef BOOST_GEOMETRY_DEBUG_RELATE_LINEAR_LINEAR
BOOST_ASSERT(res == boundary_checker.template is_boundary<boundary_front>(ip, seg_id));
#endif
}
// IP somewhere in the interior
else
{
#ifdef BOOST_GEOMETRY_DEBUG_RELATE_LINEAR_LINEAR
BOOST_ASSERT(res == boundary_checker.template is_boundary<boundary_any>(ip, seg_id));
#endif
}
return res;
}
private:
exit_watcher<turn_point_type> m_exit_watcher;
segment_watcher m_seg_watcher;
@@ -843,8 +647,8 @@ struct linear_linear
TurnIt first, TurnIt last,
Geometry const& geometry,
OtherGeometry const& other_geometry,
BoundaryChecker & boundary_checker,
OtherBoundaryChecker & other_boundary_checker)
BoundaryChecker const& boundary_checker,
OtherBoundaryChecker const& other_boundary_checker)
{
if ( first == last )
return;
@@ -863,140 +667,6 @@ struct linear_linear
geometry, other_geometry,
boundary_checker, other_boundary_checker);
}
//template <typename TurnIt>
//static inline void analyse_turns_simple(result & res,
// TurnIt first, TurnIt last,
// Geometry1 const& geometry1,
// Geometry2 const& geometry2,
// bool has_boundary1, bool has_boundary2)
//{
// for ( TurnIt it = first ; it != last ; ++it )
// {
// // 'i'
// if ( it->method == overlay::method_crosses )
// {
// res.template update<interior, interior, '0'>(); // always true
// res.template update<interior, exterior, '1'>(); // not always true
// res.template update<exterior, interior, '1'>(); // not always true
// }
// // 'e' 'c'
// else if ( it->method == overlay::method_equal
// || it->method == overlay::method_collinear )
// {
// res.template update<interior, interior, '1'>(); // always true
// }
// // 't' 'm'
// else if ( it->method == overlay::method_touch
// || it->method == overlay::method_touch_interior )
// {
// bool b = handle_boundary_point(res, *it, geometry1, geometry2, has_boundary1, has_boundary2);
//
// if ( it->has(overlay::operation_union) )
// {
// if ( !b )
// res.template update<interior, interior, '0'>();
// if ( it->operations[0].operation == overlay::operation_union )
// res.template update<interior, exterior, '1'>();
// if ( it->operations[1].operation == overlay::operation_union )
// res.template update<exterior, interior, '1'>();
// }
// if ( it->has(overlay::operation_intersection) )
// res.template update<interior, interior, '1'>();
// if ( it->has(overlay::operation_blocked) )
// if ( !b )
// res.template update<interior, interior, '0'>();
// }
//
// }
//}
//template <typename Turn>
//static inline bool handle_boundary_point(result & res,
// Turn const& turn,
// Geometry1 const& geometry1, Geometry2 const& geometry2,
// bool has_boundary1, bool has_boundary2)
//{
// bool pt_on_boundary1 = has_boundary1 && equals_terminal_point(turn.point, geometry1);
// bool pt_on_boundary2 = has_boundary2 && equals_terminal_point(turn.point, geometry2);
// if ( pt_on_boundary1 && pt_on_boundary2 )
// res.template update<boundary, boundary, '0'>();
// else if ( pt_on_boundary1 )
// res.template update<boundary, interior, '0'>();
// else if ( pt_on_boundary2 )
// res.template update<interior, boundary, '0'>();
// else
// return false;
// return true;
//}
//// TODO: replace with generic point_in_boundary working also for multilinestrings
//template <typename Point, typename Geometry>
//static inline bool equals_terminal_point(Point const& point, Geometry const& geometry)
//{
// return detail::equals::equals_point_point(point, range::front(geometry))
// || detail::equals::equals_point_point(point, range::back(geometry));
//}
//static inline void handle_boundaries(result & res,
// Geometry1 const& geometry1, Geometry2 const& geometry2,
// bool has_boundary1, bool has_boundary2)
//{
// int pig_front = detail::within::point_in_geometry(range::front(geometry1), geometry2);
// if ( has_boundary1 )
// {
// int pig_back = detail::within::point_in_geometry(range::back(geometry1), geometry2);
// if ( pig_front > 0 || pig_back > 0 )
// res.template set<boundary, interior, '0'>();
// if ( pig_front == 0 || pig_back == 0 )
// res.template set<boundary, boundary, '0'>();
// if ( pig_front < 0 || pig_back < 0 )
// {
// res.template set<boundary, exterior, '0'>();
// res.template set<interior, exterior, '1'>();
// }
// }
// else
// {
// if ( pig_front > 0 )
// res.template set<interior, interior, '0'>();
// else if ( pig_front == 0 )
// res.template set<interior, boundary, '0'>();
// else if ( pig_front < 0 )
// res.template set<interior, exterior, '0'>();
// }
// pig_front = detail::within::point_in_geometry(range::front(geometry2), geometry1);
// if ( has_boundary2 )
// {
// int pig_back = detail::within::point_in_geometry(range::back(geometry2), geometry1);
// if ( pig_front > 0 || pig_back > 0 )
// res.template set<interior, boundary, '0'>();
// if ( pig_front == 0 || pig_back == 0 )
// res.template set<boundary, boundary, '0'>();
// if ( pig_front < 0 || pig_back < 0 )
// {
// res.template set<exterior, boundary, '0'>();
// res.template set<exterior, interior, '1'>();
// }
// }
// else
// {
// if ( pig_front > 0 )
// res.template set<interior, interior, '0'>();
// else if ( pig_front == 0 )
// res.template set<boundary, interior, '0'>();
// else if ( pig_front < 0 )
// res.template set<exterior, interior, '0'>();
// }
//}
};
}} // namespace detail::relate

View File

@@ -46,6 +46,7 @@
#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>
namespace boost { namespace geometry
{
@@ -120,6 +121,26 @@ struct relate<MultiLinestring1, MultiLinestring2, multi_linestring_tag, multi_li
: detail::relate::linear_linear<MultiLinestring1, MultiLinestring2>
{};
template <typename Linestring, typename Polygon>
struct relate<Linestring, Polygon, linestring_tag, polygon_tag>
: detail::relate::linear_areal<Linestring, Polygon>
{};
template <typename Polygon, typename Linestring>
struct relate<Polygon, Linestring, polygon_tag, linestring_tag>
: detail::relate::areal_linear<Polygon, Linestring>
{};
template <typename Linestring, typename MultiPolygon>
struct relate<Linestring, MultiPolygon, linestring_tag, multi_polygon_tag>
: detail::relate::linear_areal<Linestring, MultiPolygon>
{};
template <typename MultiPolygon, typename Linestring>
struct relate<MultiPolygon, Linestring, multi_polygon_tag, linestring_tag>
: detail::relate::areal_linear<MultiPolygon, Linestring>
{};
}} // namespace detail_dispatch::relate
namespace detail { namespace relate {
@@ -132,9 +153,83 @@ struct interruption_enabled
};
template <typename Geometry1, typename Geometry2, typename Result>
inline void relate(Geometry1 const& geometry1, Geometry2 const& geometry2, Result & result)
struct result_handler_type
: not_implemented<Result>
{};
template <typename Geometry1, typename Geometry2>
struct result_handler_type<Geometry1, Geometry2, matrix9>
{
return detail_dispatch::relate::relate<Geometry1, Geometry2>::apply(geometry1, geometry2, result);
typedef matrix_handler<matrix9> type;
};
template <typename Geometry1, typename Geometry2>
struct result_handler_type<Geometry1, Geometry2, mask9>
{
typedef mask_handler
<
mask9,
interruption_enabled
<
Geometry1,
Geometry2
>::value
> type;
};
template <typename Geometry1, typename Geometry2, typename Head, typename Tail>
struct result_handler_type<Geometry1, Geometry2, boost::tuples::cons<Head, Tail> >
{
typedef mask_handler
<
boost::tuples::cons<Head, Tail>,
interruption_enabled
<
Geometry1,
Geometry2
>::value
> type;
};
template <typename Geometry1, typename Geometry2,
char II, char IB, char IE,
char BI, char BB, char BE,
char EI, char EB, char EE>
struct result_handler_type<Geometry1, Geometry2, static_mask<II, IB, IE, BI, BB, BE, EI, EB, EE> >
{
typedef static_mask_handler
<
static_mask<II, IB, IE, BI, BB, BE, EI, EB, EE>,
interruption_enabled
<
Geometry1,
Geometry2
>::value
> type;
};
template <typename MatrixOrMask, typename Geometry1, typename Geometry2>
inline
typename result_handler_type
<
Geometry1,
Geometry2,
MatrixOrMask
>::type::result_type
relate(Geometry1 const& geometry1,
Geometry2 const& geometry2,
MatrixOrMask const& matrix_or_mask = MatrixOrMask())
{
typedef typename result_handler_type
<
Geometry1,
Geometry2,
MatrixOrMask
>::type handler_type;
handler_type handler(matrix_or_mask);
detail_dispatch::relate::relate<Geometry1, Geometry2>::apply(geometry1, geometry2, handler);
return handler.result();
}
}} // namespace detail::relate

View File

@@ -2,16 +2,25 @@
// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
// This file was modified by Oracle on 2013.
// Modifications copyright (c) 2013, Oracle and/or its affiliates.
// 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_RESULT_HPP
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_RESULT_HPP
#include <boost/tuple/tuple.hpp>
#include <boost/mpl/vector_c.hpp>
#include <boost/mpl/at.hpp>
// TEMP - move this header to geometry/detail
#include <boost/geometry/index/detail/tuples.hpp>
namespace boost { namespace geometry {
#ifndef DOXYGEN_NO_DETAIL
@@ -19,128 +28,866 @@ namespace detail { namespace relate {
enum field { interior = 0, boundary = 1, exterior = 2 };
// With DE9IM only Dimension < 10 is supported
class result
// TODO add height?
template <std::size_t Width>
class matrix
{
BOOST_STATIC_ASSERT(Width == 2 || Width == 3);
public:
static const bool interrupt = false;
// TODO: replace with std::string?
inline result()
static const std::size_t size = Width * Width;
inline matrix()
{
::memset(m_array, 'F', 9);
::memset(m_array, 'F', size);
}
template <field F1, field F2>
inline char get() const
{
return m_array[F1 * 3 + F2];
static const bool in_bounds = F1 * Width + F2 < size;
return get_dispatch<F1, F2>(integral_constant<bool, in_bounds>());
}
template <field F1, field F2, char V>
inline void set()
{
m_array[F1 * 3 + F2] = V;
static const bool in_bounds = F1 * Width + F2 < size;
set_dispatch<F1, F2, V>(integral_constant<bool, in_bounds>());
}
inline std::pair<const char*, const char*> get_code() const
template <field F1, field F2, char D>
inline void update()
{
return std::make_pair(m_array, m_array+9);
static const bool in_bounds = F1 * Width + F2 < size;
update_dispatch<F1, F2, D>(integral_constant<bool, in_bounds>());
}
inline const char * data() const
{
return m_array;
}
private:
char m_array[9];
template <field F1, field F2>
inline char get_dispatch(integral_constant<bool, true>) const
{
return m_array[F1 * Width + F2];
}
template <field F1, field F2>
inline char get_dispatch(integral_constant<bool, false>) const
{
return 'F';
}
template <field F1, field F2, char V>
inline void set_dispatch(integral_constant<bool, true>)
{
BOOST_STATIC_ASSERT(('0' <= V && V <= '9') || V == 'T' || V == 'F');
m_array[F1 * Width + F2] = V;
}
template <field F1, field F2, char V>
inline void set_dispatch(integral_constant<bool, false>)
{}
template <field F1, field F2, char D>
inline void update_dispatch(integral_constant<bool, true>)
{
BOOST_STATIC_ASSERT('0' <= D && D <= '9');
char c = m_array[F1 * Width + F2];
if ( D > c || c > '9')
m_array[F1 * Width + F2] = D;
}
template <field F1, field F2, char D>
inline void update_dispatch(integral_constant<bool, false>)
{}
char m_array[size];
};
// TODO: possible optimizations
// 1. interrupt in a template xxx<Interrupt> make it static const if Interrupt == false
// 2. static_mask<II, IB, IE, ...> setting interrupt in compile-time
// TODO add EnableDimensions parameter?
template <bool Interrupt>
class mask
: public result
struct matrix9 {};
//struct matrix4 {};
template <typename MatrixOrMask>
struct matrix_width
: not_implemented<MatrixOrMask>
{};
template <>
struct matrix_width<matrix9>
{
static const std::size_t value = 3;
};
template <typename Matrix>
class matrix_handler
: private matrix<matrix_width<Matrix>::value>
{
typedef matrix<matrix_width<Matrix>::value> base_t;
public:
typedef std::string result_type;
static const bool interrupt = false;
matrix_handler(Matrix const&)
{}
result_type result() const
{
return std::string(this->data(),
this->data() + base_t::size);
}
template <field F1, field F2, char V>
inline void set()
{
static_cast<base_t&>(*this).template set<F1, F2, V>();
}
template <field F1, field F2, char D>
inline void update()
{
static_cast<base_t&>(*this).template update<F1, F2, D>();
}
};
// RUN-TIME MASKS
class mask9
{
public:
static const std::size_t width = 3; // TEMP
bool interrupt;
inline mask(std::string const& de9im_mask)
: interrupt(false)
inline mask9(std::string const& de9im_mask)
{
// TODO: throw an exception here?
BOOST_ASSERT(de9im_mask.size() == 9);
::memcpy(m_mask, de9im_mask.c_str(), 9);
}
template <field F1, field F2, char V>
inline void set()
{
handle_interrupt_dispatch<F1, F2, V>(boost::integral_constant<bool, Interrupt>());
result::set<F1, F2, V>();
}
inline bool check() const
{
if ( interrupt )
return false;
std::pair<const char*, const char*> range = result::get_code();
const char* m_it = m_mask;
const char* a_it = range.first;
for ( ; a_it != range.second ; ++a_it, ++m_it )
{
if ( *m_it == 'F' )
{
if ( *a_it != 'F' )
return false;
}
else if ( *m_it == 'T' )
{
if ( *a_it != 'T' && ( *a_it < '0' || *a_it > '9' ) )
return false;
}
else if ( *m_it >= '0' && *m_it <= '9' )
{
if ( *a_it != *m_it )
return false;
}
}
return true;
}
private:
template <field F1, field F2, char V>
void handle_interrupt_dispatch(boost::integral_constant<bool, false>)
{}
template <field F1, field F2, char V>
void handle_interrupt_dispatch(boost::integral_constant<bool, true>)
{
char m = get_mask<F1, F2>();
if ( V >= '0' && V <= '9' )
{
if ( m == 'F' || ( m < V && m >= '0' && m <= '9' ) )
interrupt = true;
}
else if ( V == 'T' )
{
if ( m == 'F' )
interrupt = true;
}
}
template <field F1, field F2>
inline char get_mask() const
inline char get() const
{
return m_mask[F1 * 3 + F2];
}
private:
char m_mask[9];
};
template <typename Mask, bool InterruptEnabled>
struct interrupt_dispatch
{
template <field F1, field F2, char V>
static inline bool apply(Mask const&)
{
return false;
}
};
template <typename Mask>
struct interrupt_dispatch<Mask, true>
{
template <field F1, field F2, char V>
static inline bool apply(Mask const& mask)
{
char m = mask.template get<F1, F2>();
return check<V>(m);
}
template <char V>
static inline bool check(char m)
{
if ( V >= '0' && V <= '9' )
{
return m == 'F' || ( m < V && m >= '0' && m <= '9' );
}
else if ( V == 'T' )
{
return m == 'F';
}
return false;
}
};
template <typename Masks, int I = 0, int N = boost::tuples::length<Masks>::value>
struct interrupt_dispatch_tuple
{
template <field F1, field F2, char V>
static inline bool apply(Masks const& masks)
{
typedef typename boost::tuples::element<I, Masks>::type mask_type;
mask_type const& mask = boost::get<I>(masks);
return interrupt_dispatch<mask_type, true>::template apply<F1, F2, V>(mask)
&& interrupt_dispatch_tuple<Masks, I+1>::template apply<F1, F2, V>(masks);
}
};
template <typename Masks, int N>
struct interrupt_dispatch_tuple<Masks, N, N>
{
template <field F1, field F2, char V>
static inline bool apply(Masks const& )
{
return true;
}
};
template <typename T0, typename T1, typename T2, typename T3, typename T4,
typename T5, typename T6, typename T7, typename T8, typename T9>
struct interrupt_dispatch<boost::tuple<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9>, true>
{
typedef boost::tuple<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9> mask_type;
template <field F1, field F2, char V>
static inline bool apply(mask_type const& mask)
{
return interrupt_dispatch_tuple<mask_type>::template apply<F1, F2, V>(mask);
}
};
template <typename Head, typename Tail>
struct interrupt_dispatch<boost::tuples::cons<Head, Tail>, true>
{
typedef boost::tuples::cons<Head, Tail> mask_type;
template <field F1, field F2, char V>
static inline bool apply(mask_type const& mask)
{
return interrupt_dispatch_tuple<mask_type>::template apply<F1, F2, V>(mask);
}
};
template <field F1, field F2, char V, bool InterruptEnabled, typename Mask>
inline bool interrupt(Mask const& mask)
{
return interrupt_dispatch<Mask, InterruptEnabled>
::template apply<F1, F2, V>(mask);
}
template <typename Mask>
struct check_dispatch
{
template <typename Matrix>
static inline bool apply(Mask const& mask, Matrix const& matrix)
{
return per_one<interior, interior>(mask, matrix)
&& per_one<interior, boundary>(mask, matrix)
&& per_one<interior, exterior>(mask, matrix)
&& per_one<boundary, interior>(mask, matrix)
&& per_one<boundary, boundary>(mask, matrix)
&& per_one<boundary, exterior>(mask, matrix)
&& per_one<exterior, interior>(mask, matrix)
&& per_one<exterior, boundary>(mask, matrix)
&& per_one<exterior, exterior>(mask, matrix);
}
template <field F1, field F2, typename Matrix>
static inline bool per_one(Mask const& mask, Matrix const& matrix)
{
const char mask_el = mask.template get<F1, F2>();
const char el = matrix.template get<F1, F2>();
if ( mask_el == 'F' )
{
return el == 'F';
}
else if ( mask_el == 'T' )
{
return el == 'T' || ( el >= '0' && el <= '9' );
}
else if ( mask_el >= '0' && mask_el <= '9' )
{
return el == mask_el;
}
return true;
}
};
template <typename Masks, int I = 0, int N = boost::tuples::length<Masks>::value>
struct check_dispatch_tuple
{
template <typename Matrix>
static inline bool apply(Masks const& masks, Matrix const& matrix)
{
typedef typename boost::tuples::element<I, Masks>::type mask_type;
mask_type const& mask = boost::get<I>(masks);
return check_dispatch<mask_type>::apply(mask, matrix)
|| check_dispatch_tuple<Masks, I+1>::apply(masks, matrix);
}
};
template <typename Masks, int N>
struct check_dispatch_tuple<Masks, N, N>
{
template <typename Matrix>
static inline bool apply(Masks const&, Matrix const&)
{
return false;
}
};
template <typename T0, typename T1, typename T2, typename T3, typename T4,
typename T5, typename T6, typename T7, typename T8, typename T9>
struct check_dispatch< boost::tuple<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9> >
{
typedef boost::tuple<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9> mask_type;
template <typename Matrix>
static inline bool apply(mask_type const& mask, Matrix const& matrix)
{
return check_dispatch_tuple<mask_type>::template apply(mask, matrix);
}
};
template <typename Head, typename Tail>
struct check_dispatch< boost::tuples::cons<Head, Tail> >
{
typedef boost::tuples::cons<Head, Tail> mask_type;
template <typename Matrix>
static inline bool apply(mask_type const& mask, Matrix const& matrix)
{
return check_dispatch_tuple<mask_type>::template apply(mask, matrix);
}
};
template <typename Mask, typename Matrix>
inline bool check(Mask const& mask, Matrix const& matrix)
{
return check_dispatch<Mask>::apply(mask, matrix);
}
template <>
struct matrix_width<mask9>
{
static const std::size_t value = 3;
};
template <typename Tuple,
int I = 0,
int N = boost::tuples::length<Tuple>::value>
struct matrix_width_tuple
{
static const std::size_t
current = matrix_width<typename boost::tuples::element<I, Tuple>::type>::value;
static const std::size_t
next = matrix_width_tuple<Tuple, I+1>::value;
static const std::size_t
value = current > next ? current : next;
};
template <typename Tuple, int N>
struct matrix_width_tuple<Tuple, N, N>
{
static const std::size_t value = 0;
};
template <typename Head, typename Tail>
struct matrix_width< boost::tuples::cons<Head, Tail> >
{
static const std::size_t
value = matrix_width_tuple< boost::tuples::cons<Head, Tail> >::value;
};
template <typename Mask, bool Interrupt>
class mask_handler
: private matrix<matrix_width<Mask>::value>
{
typedef matrix<matrix_width<Mask>::value> base_t;
public:
typedef bool result_type;
bool interrupt;
inline mask_handler(Mask const& m)
: interrupt(false)
, m_mask(m)
{}
result_type result() const
{
return !interrupt
&& check(m_mask, static_cast<base_t const&>(*this));
}
template <field F1, field F2, char V>
inline void set()
{
if ( relate::interrupt<F1, F2, V, Interrupt>(m_mask) )
{
interrupt = true;
}
else
{
base_t::template set<F1, F2, V>();
}
}
template <field F1, field F2, char V>
inline void update()
{
if ( relate::interrupt<F1, F2, V, Interrupt>(m_mask) )
{
interrupt = true;
}
else
{
base_t::template update<F1, F2, V>();
}
}
private:
Mask const& m_mask;
};
// STATIC MASK
template <char II, char IB, char IE,
char BI, char BB, char BE,
char EI, char EB, char EE>
class static_mask
{
typedef boost::mpl::vector_c
<
char, II, IB, IE, BI, BB, BE, EI, EB, EE
> vector_type;
public:
template <field F1, field F2>
struct get
{
BOOST_STATIC_ASSERT(F1 * 3 + F2 < boost::mpl::size<vector_type>::value);
static const char value
= boost::mpl::at_c<vector_type, F1 * 3 + F2>::type::value;
};
};
template <typename StaticMask, field F1, field F2>
struct static_should_handle_element
{
static const char mask_el = StaticMask::template get<F1, F2>::value;
static const bool value = mask_el == 'F'
|| mask_el == 'T'
|| ( mask_el >= '0' && mask_el <= '9' );
};
template <typename StaticMask, char V, field F1, field F2, bool InterruptEnabled, bool IsNotSequence>
struct static_interrupt_dispatch
{
static const bool value = false;
};
template <typename StaticMask, char V, field F1, field F2, bool IsNotSequence>
struct static_interrupt_dispatch<StaticMask, V, F1, F2, true, IsNotSequence>
{
static const char mask_el = StaticMask::template get<F1, F2>::value;
static const bool value
= ( V >= '0' && V <= '9' ) ?
( mask_el == 'F' || ( mask_el < V && mask_el >= '0' && mask_el <= '9' ) ) :
( ( V == 'T' ) ? mask_el == 'F' : false );
};
template <typename First, typename Last, char V, field F1, field F2>
struct static_interrupt_sequence
{
typedef typename boost::mpl::deref<First>::type StaticMask;
static const bool value
= static_interrupt_dispatch
<
StaticMask,
V, F1, F2,
true,
!boost::mpl::is_sequence<StaticMask>::value
>::value
&& static_interrupt_sequence
<
typename boost::mpl::next<First>::type,
Last,
V, F1, F2
>::value;
};
template <typename Last, char V, field F1, field F2>
struct static_interrupt_sequence<Last, Last, V, F1, F2>
{
static const bool value = true;
};
template <typename StaticMask, char V, field F1, field F2>
struct static_interrupt_dispatch<StaticMask, V, F1, F2, true, false>
{
static const bool value
= static_interrupt_sequence
<
typename boost::mpl::begin<StaticMask>::type,
typename boost::mpl::end<StaticMask>::type,
V, F1, F2
>::value;
};
template <typename StaticMask, char V, field F1, field F2, bool EnableInterrupt>
struct static_interrupt
{
static const bool value
= static_interrupt_dispatch
<
StaticMask,
V, F1, F2,
EnableInterrupt,
!boost::mpl::is_sequence<StaticMask>::value
>::value;
};
template <typename StaticMask, bool IsNotSequence>
struct static_check_dispatch
{
template <typename Matrix>
static inline bool apply(Matrix const& matrix)
{
return per_one<interior, interior>::apply(matrix)
&& per_one<interior, boundary>::apply(matrix)
&& per_one<interior, exterior>::apply(matrix)
&& per_one<boundary, interior>::apply(matrix)
&& per_one<boundary, boundary>::apply(matrix)
&& per_one<boundary, exterior>::apply(matrix)
&& per_one<exterior, interior>::apply(matrix)
&& per_one<exterior, boundary>::apply(matrix)
&& per_one<exterior, exterior>::apply(matrix);
}
template <field F1, field F2>
struct per_one
{
static const char mask_el = StaticMask::template get<F1, F2>::value;
static const int version
= mask_el == 'F' ? 0
: mask_el == 'T' ? 1
: mask_el >= '0' && mask_el <= '9' ? 2
: 3;
template <typename Matrix>
static inline bool apply(Matrix const& matrix)
{
const char el = matrix.template get<F1, F2>();
return apply_dispatch(el, integral_constant<int, version>());
}
// mask_el == 'F'
static inline bool apply_dispatch(char el, integral_constant<int, 0>)
{
return el == 'F';
}
// mask_el == 'T'
static inline bool apply_dispatch(char el, integral_constant<int, 1>)
{
return el == 'T' || ( el >= '0' && el <= '9' );
}
// mask_el >= '0' && mask_el <= '9'
static inline bool apply_dispatch(char el, integral_constant<int, 2>)
{
return el == mask_el;
}
// else
static inline bool apply_dispatch(char el, integral_constant<int, 3>)
{
return true;
}
};
};
template <typename First, typename Last>
struct static_check_sequence
{
typedef typename boost::mpl::deref<First>::type StaticMask;
template <typename Matrix>
static inline bool apply(Matrix const& matrix)
{
return static_check_dispatch
<
StaticMask,
!boost::mpl::is_sequence<StaticMask>::value
>::apply(matrix)
|| static_check_sequence
<
typename boost::mpl::next<First>::type,
Last
>::apply(matrix);
}
};
template <typename Last>
struct static_check_sequence<Last, Last>
{
template <typename Matrix>
static inline bool apply(Matrix const& matrix)
{
return false;
}
};
template <typename StaticMask>
struct static_check_dispatch<StaticMask, false>
{
template <typename Matrix>
static inline bool apply(Matrix const& matrix)
{
return static_check_sequence
<
typename boost::mpl::begin<StaticMask>::type,
typename boost::mpl::end<StaticMask>::type
>::apply(matrix);
}
};
template <typename StaticMask>
struct static_check
{
template <typename Matrix>
static inline bool apply(Matrix const& matrix)
{
return static_check_dispatch
<
StaticMask,
!boost::mpl::is_sequence<StaticMask>::value
>::apply(matrix);
}
};
template <typename StaticMask, bool Interrupt>
class static_mask_handler
: private matrix<3>
{
typedef matrix<3> base_t;
public:
typedef bool result_type;
bool interrupt;
inline static_mask_handler(StaticMask const& /*dummy*/)
: interrupt(false)
{}
result_type result() const
{
return (!Interrupt || !interrupt)
&& static_check<StaticMask>::apply(static_cast<base_t const&>(*this));
}
template <field F1, field F2, char V>
inline void set()
{
static const bool interrupt_c = static_interrupt<StaticMask, V, F1, F2, Interrupt>::value;
static const bool should_handle = static_should_handle_element<StaticMask, F1, F2>::value;
static const int version = interrupt_c ? 0
: should_handle ? 1
: 2;
set_dispatch<F1, F2, V>(integral_constant<int, version>());
}
template <field F1, field F2, char V>
inline void update()
{
static const bool interrupt_c = static_interrupt<StaticMask, V, F1, F2, Interrupt>::value;
static const bool should_handle = static_should_handle_element<StaticMask, F1, F2>::value;
static const int version = interrupt_c ? 0
: should_handle ? 1
: 2;
update_dispatch<F1, F2, V>(integral_constant<int, version>());
}
private:
// Interrupt && interrupt
template <field F1, field F2, char V>
inline void set_dispatch(integral_constant<int, 0>)
{
interrupt = true;
}
// else should_handle
template <field F1, field F2, char V>
inline void set_dispatch(integral_constant<int, 1>)
{
base_t::template set<F1, F2, V>();
}
// else
template <field F1, field F2, char V>
inline void set_dispatch(integral_constant<int, 2>)
{}
// Interrupt && interrupt
template <field F1, field F2, char V>
inline void update_dispatch(integral_constant<int, 0>)
{
interrupt = true;
}
// else should_handle
template <field F1, field F2, char V>
inline void update_dispatch(integral_constant<int, 1>)
{
base_t::template update<F1, F2, V>();
}
// else
template <field F1, field F2, char V>
inline void update_dispatch(integral_constant<int, 2>)
{}
};
// OPERATORS
template <typename Mask1, typename Mask2> inline
boost::tuples::cons<
Mask1,
boost::tuples::cons<Mask2, boost::tuples::null_type>
>
operator||(Mask1 const& m1, Mask2 const& m2)
{
namespace bt = boost::tuples;
return
bt::cons< Mask1, bt::cons<Mask2, bt::null_type> >
( m1, bt::cons<Mask2, bt::null_type>(m2, bt::null_type()) );
}
template <typename Head, typename Tail, typename Mask> inline
typename index::detail::tuples::push_back<
boost::tuples::cons<Head, Tail>, Mask
>::type
operator||(boost::tuples::cons<Head, Tail> const& t, Mask const& m)
{
namespace bt = boost::tuples;
return
index::detail::tuples::push_back<
bt::cons<Head, Tail>, Mask
>::apply(t, m);
}
// PREDEFINED MASKS
// TODO:
// 1. specialize for simplified masks if available
// e.g. for TOUCHES use 1 mask for A/A
// 2. Think about dimensions > 2 e.g. should TOUCHES be true
// if the interior of the Areal overlaps the boundary of the Volumetric
// like it's true for Linear/Areal
// EQUALS
//typedef static_mask<'T', '*', 'F', '*', '*', 'F', 'F', 'F', '*'> static_mask_equals; // wikipedia
typedef static_mask<'T', 'F', 'F', 'F', 'T', 'F', 'F', 'F', 'T'> static_mask_equals; // OGC
// DISJOINT
typedef static_mask<'F', 'F', '*', 'F', 'F', '*', '*', '*', '*'> static_mask_disjoint;
// TOUCHES - NOT P/P
template <typename Geometry1,
typename Geometry2,
std::size_t Dim1 = detail::group_dim<Geometry1>::value,
std::size_t Dim2 = detail::group_dim<Geometry2>::value>
struct static_mask_touches_type
{
typedef boost::mpl::vector<
static_mask<'F', 'T', '*', '*', '*', '*', '*', '*', '*'>,
static_mask<'F', '*', '*', 'T', '*', '*', '*', '*', '*'>,
static_mask<'F', '*', '*', '*', 'T', '*', '*', '*', '*'>
> type;
};
// According to OGC, doesn't apply to P/P
// Using the above mask the result would be always false
template <typename Geometry1, typename Geometry2>
struct static_mask_touches_type<Geometry1, Geometry2, 0, 0>
: not_implemented<typename geometry::tag<Geometry1>::type,
typename geometry::tag<Geometry2>::type>
{};
// WITHIN
typedef static_mask<'T', '*', 'F', '*', '*', 'F', '*', '*', '*'> static_mask_within;
// COVERED_BY (non OGC)
typedef boost::mpl::vector<
static_mask<'T', '*', 'F', '*', '*', 'F', '*', '*', '*'>,
static_mask<'*', 'T', 'F', '*', '*', 'F', '*', '*', '*'>,
static_mask<'*', '*', 'F', 'T', '*', 'F', '*', '*', '*'>,
static_mask<'*', '*', 'F', '*', 'T', 'F', '*', '*', '*'>
> static_mask_covered_by;
// CROSSES
// dim(G1) < dim(G2) - P/L P/A L/A
template <typename Geometry1,
typename Geometry2,
std::size_t Dim1 = detail::group_dim<Geometry1>::value,
std::size_t Dim2 = detail::group_dim<Geometry2>::value,
bool D1LessD2 = (Dim1 < Dim2)
>
struct static_mask_crosses_type
{
typedef static_mask<'T', '*', 'T', '*', '*', '*', '*', '*', '*'> type;
};
// TODO: I'm not sure if this one below should be available!
// dim(G1) > dim(G2) - L/P A/P A/L
template <typename Geometry1, typename Geometry2,
std::size_t Dim1, std::size_t Dim2
>
struct static_mask_crosses_type<Geometry1, Geometry2, Dim1, Dim2, false>
{
typedef static_mask<'T', '*', '*', '*', '*', '*', 'T', '*', '*'> type;
};
// dim(G1) == dim(G2) - P/P A/A
template <typename Geometry1, typename Geometry2,
std::size_t Dim, bool D1LessD2
>
struct static_mask_crosses_type<Geometry1, Geometry2, Dim, Dim, D1LessD2/*false*/>
: not_implemented<typename geometry::tag<Geometry1>::type,
typename geometry::tag<Geometry2>::type>
{};
// dim(G1) == 1 && dim(G2) == 1 - L/L
template <typename Geometry1, typename Geometry2, bool D1LessD2>
struct static_mask_crosses_type<Geometry1, Geometry2, 1, 1, D1LessD2>
{
typedef static_mask<'0', '*', '*', '*', '*', '*', '*', '*', '*'> type;
};
// OVERLAPS
// dim(G1) != dim(G2) - NOT P/P, L/L, A/A
template <typename Geometry1,
typename Geometry2,
std::size_t Dim1 = detail::group_dim<Geometry1>::value,
std::size_t Dim2 = detail::group_dim<Geometry2>::value
>
struct static_mask_overlaps_type
: not_implemented<typename geometry::tag<Geometry1>::type,
typename geometry::tag<Geometry2>::type>
{};
// dim(G1) == D && dim(G2) == D - P/P A/A
template <typename Geometry1, typename Geometry2, std::size_t Dim>
struct static_mask_overlaps_type<Geometry1, Geometry2, Dim, Dim>
{
typedef static_mask<'T', '*', 'T', '*', '*', '*', 'T', '*', '*'> type;
};
// dim(G1) == 1 && dim(G2) == 1 - L/L
template <typename Geometry1, typename Geometry2>
struct static_mask_overlaps_type<Geometry1, Geometry2, 1, 1>
{
typedef static_mask<'1', '*', 'T', '*', '*', '*', 'T', '*', '*'> type;
};
// RESULTS/HANDLERS UTILS
template <field F1, field F2, char V, typename Result>
inline void set(Result & res)
{
@@ -204,10 +951,7 @@ inline void set(Result & res)
template <field F1, field F2, char D, typename Result>
inline void update(Result & res)
{
BOOST_STATIC_ASSERT('0' <= D && D <= '9');
char c = res.template get<F1, F2>();
if ( D > c || c > '9')
res.template set<F1, F2, D>();
res.template update<F1, F2, D>();
}
template <field F1, field F2, char D, bool Transpose>

View File

@@ -83,17 +83,32 @@ struct get_turns
> turn_info;
template <typename Turns>
static inline void apply(Turns & turns, Geometry1 const& geometry1, Geometry2 const& geometry2)
static inline void apply(Turns & turns,
Geometry1 const& geometry1,
Geometry2 const& geometry2)
{
detail::get_turns::no_interrupt_policy interrupt_policy;
apply(turns, geometry1, geometry2, interrupt_policy);
}
template <typename Turns, typename InterruptPolicy>
static inline void apply(Turns & turns,
Geometry1 const& geometry1,
Geometry2 const& geometry2,
InterruptPolicy & interrupt_policy)
{
static const bool reverse1 = detail::overlay::do_reverse<geometry::point_order<Geometry1>::value>::value;
static const bool reverse2 = detail::overlay::do_reverse<geometry::point_order<Geometry2>::value>::value;
dispatch::get_turns
<
typename tag<Geometry1>::type, typename tag<Geometry2>::type,
Geometry1, Geometry2, reverse1, reverse2,
typename geometry::tag<Geometry1>::type,
typename geometry::tag<Geometry2>::type,
Geometry1,
Geometry2,
reverse1,
reverse2,
GetTurnPolicy
>::apply(0, geometry1, 1, geometry2, detail::no_rescale_policy(), turns, interrupt_policy);
}
@@ -124,47 +139,68 @@ struct less_seg_dist_op
}
template <typename Op> static inline
bool use_operation(Op const& left, Op const& right)
bool less_operation(Op const& left, Op const& right)
{
return order_op(left) < order_op(right);
}
template <typename Op> static inline
bool greater_operation(Op const& left, Op const& right)
{
return order_op(left) > order_op(right);
}
template <typename Op> static inline
bool use_other_multi_ring_id(Op const& left, Op const& right)
{
// VER1
//return left.other_id.ring_index < right.other_id.ring_index;
if ( left.other_id.ring_index == -1 )
{
if ( right.other_id.ring_index == -1 )
return use_operation(left, right); // sort by operation
else
return true; // right always greater
}
else // left.other_id.ring_index != -1
{
if ( right.other_id.ring_index == -1 )
return false; // left always greater
// VER2
//if ( left.other_id.ring_index == -1 )
//{
// if ( right.other_id.ring_index == -1 )
// return less_operation(left, right); // sort by operation
// else
// return true; // right always greater
//}
//else // left.other_id.ring_index != -1
//{
// if ( right.other_id.ring_index == -1 )
// return false; // left always greater
// here both ring_indexes are greater than -1
// so first, sort also by multi_index
return left.other_id.multi_index < right.other_id.multi_index || (
left.other_id.multi_index == right.other_id.multi_index && (
left.other_id.ring_index < right.other_id.ring_index || (
left.other_id.ring_index == right.other_id.ring_index &&
use_operation(left, right) )
)
);
// // here both ring_indexes are greater than -1
// // so first, sort also by multi_index
// return left.other_id.multi_index < right.other_id.multi_index || (
// left.other_id.multi_index == right.other_id.multi_index && (
// left.other_id.ring_index < right.other_id.ring_index || (
// left.other_id.ring_index == right.other_id.ring_index &&
// less_operation(left, right) )
// )
// );
//}
// VER3
if ( left.other_id.multi_index == right.other_id.multi_index )
{
if ( left.other_id.ring_index == right.other_id.ring_index )
return less_operation(left, right);
else
return greater_operation(left, right);
}
else
{
return less_operation(left, right);
}
}
template <typename Op> static inline
bool use_distance(Op const& left, Op const& right)
{
return left.enriched.distance < right.enriched.distance || (
geometry::math::equals(left.enriched.distance, right.enriched.distance) &&
use_other_multi_ring_id(left, right)
);
if ( geometry::math::equals(left.enriched.distance, right.enriched.distance) )
return use_other_multi_ring_id(left, right);
else
return left.enriched.distance < right.enriched.distance;
}
template <typename Turn>

View File

@@ -70,12 +70,15 @@ struct get<Geometry, polygon_tag, false>
result_type apply(Geometry & geometry, Id const& id)
{
if ( id.ring_index < 0 )
{
return geometry::exterior_ring(geometry);
}
else
{
BOOST_ASSERT( id.ring_index < boost::size(geometry::interior_rings(geometry)) );
std::size_t ri = static_cast<std::size_t>(id.ring_index);
BOOST_ASSERT( ri < boost::size(geometry::interior_rings(geometry)) );
return *(boost::begin(geometry::interior_rings(geometry)) + id.ring_index);
return *(boost::begin(geometry::interior_rings(geometry)) + ri);
}
}
};

View File

@@ -164,13 +164,16 @@ struct add_unique
>::type type;
};
template <typename Tuple, typename T, size_t I, size_t N>
struct push_back_impl
template <typename Tuple,
typename T,
size_t I = 0,
size_t N = boost::tuples::length<Tuple>::value>
struct push_back
{
typedef
boost::tuples::cons<
typename boost::tuples::element<I, Tuple>::type,
typename push_back_impl<Tuple, T, I+1, N>::type
typename push_back<Tuple, T, I+1, N>::type
> type;
static type apply(Tuple const& tup, T const& t)
@@ -178,13 +181,13 @@ struct push_back_impl
return
type(
boost::get<I>(tup),
push_back_impl<Tuple, T, I+1, N>::apply(tup, t)
push_back<Tuple, T, I+1, N>::apply(tup, t)
);
}
};
template <typename Tuple, typename T, size_t N>
struct push_back_impl<Tuple, T, N, N>
struct push_back<Tuple, T, N, N>
{
typedef boost::tuples::cons<T, boost::tuples::null_type> type;

View File

@@ -362,11 +362,8 @@ operator&&(Pred1 const& p1, Pred2 const& p2)
}
template <typename Head, typename Tail, typename Pred> inline
typename tuples::push_back_impl<
boost::tuples::cons<Head, Tail>,
Pred,
0,
boost::tuples::length<boost::tuples::cons<Head, Tail> >::value
typename tuples::push_back<
boost::tuples::cons<Head, Tail>, Pred
>::type
operator&&(boost::tuples::cons<Head, Tail> const& t, Pred const& p)
{
@@ -374,8 +371,8 @@ operator&&(boost::tuples::cons<Head, Tail> const& t, Pred const& p)
namespace bt = boost::tuples;
return
tuples::push_back_impl<
bt::cons<Head, Tail>, Pred, 0, bt::length< bt::cons<Head, Tail> >::value
tuples::push_back<
bt::cons<Head, Tail>, Pred
>::apply(t, p);
}

View File

@@ -20,10 +20,7 @@
#include "test_get_turns.hpp"
#include <boost/geometry/geometries/geometries.hpp>
//TEST
#include <to_svg.hpp>
//#include <boost/geometry.hpp>
//#include <boost/geometry/multi/geometries/multi_linestring.hpp>
//#include "to_svg.hpp"
template <typename T>
void test_all()
@@ -34,41 +31,50 @@ void test_all()
test_geometry<ls, poly>("LINESTRING(15 5,24 5,20 2,19 0,13 -4,1 0,10 0,13 3,15 7,16 10,10 10,8 10,4 6,2 8,1 10)",
"POLYGON((0 0,5 5,0 10,20 10,20 2,19 0,0 0)(10 3,15 3,15 7,10 7,10 3))",
expected("miu")("iuu")("tcu")("tuu")("mcu")("miu")("muu")("tiu")("mcu")("miu")("mcu")("miu")("mxu").vec);
expected("miu")("iuu")("tcc")("tuu")("mcu")("mic")("muu")("tiu")("mcu")("mic")("mcc")("miu")("mxu").vec);
test_geometry<ls, poly>("LINESTRING(5 0,5 5,10 5)", "POLYGON((0 0,0 10,10 10,10 0,0 0))",
"miu", "mxu");
test_geometry<ls, poly>("LINESTRING(0 0,5 5,10 0)", "POLYGON((0 0,0 10,10 10,10 0,0 0))",
"tiu", "txu");
test_geometry<ls, poly>("LINESTRING(0 0,5 0,5 5,10 5,10 0)", "POLYGON((0 0,0 10,10 10,10 0,0 0))",
expected("tcu")("miu")("mcu")("txu").vec);
expected("tcu")("mic")("mcc")("txu").vec);
test_geometry<ls, poly>("LINESTRING(10 0,5 0,5 5,10 5,10 10)", "POLYGON((0 0,0 10,10 10,10 0,0 0))",
expected("tcu")("miu")("mcu")("txu").vec);
expected("tcc")("miu")("mcu")("txc").vec);
test_geometry<ls, poly>("LINESTRING(0 0,10 0,10 10)",
"POLYGON((0 0,5 5,0 10,20 10,20 2,19 0,0 0)(10 3,15 3,15 7,10 7,10 3))",
expected("tcu")("miu")("mcu")("miu")("mxu").vec);
expected("tcu")("mic")("mcu")("mic")("mxu").vec);
test_geometry<ls, poly>("LINESTRING(11 1,10 0,0 0)", "POLYGON((0 0,0 10,10 10,10 0,0 0))",
"tcc", "txu");
test_geometry<ls, poly>("LINESTRING(0 0,10 0,11 1)", "POLYGON((0 0,0 10,10 10,10 0,0 0))",
"tcu", "tuc");
test_geometry<ls, poly>("LINESTRING(10 0,0 0,-1 1)", "POLYGON((0 0,0 10,10 10,10 0,0 0))",
"tcc", "tuu");
to_svg<ls, poly>("LINESTRING(15 5,24 5,20 2,19 0,13 -4,1 0,10 0,13 3,15 7,16 10,10 10,8 10,4 6,2 8,1 10)",
"POLYGON((0 0,5 5,0 10,20 10,20 2,19 0,0 0)(10 3,15 3,15 7,10 7,10 3))",
"ls_poly1.svg");
to_svg<ls, poly>("LINESTRING(15 3,15 5,24 5,20 2,19 0,13 -4,1 0,10 0,13 3,15 7,16 10,10 10,8 10,4 6,2 8,1 10)",
"POLYGON((0 0,5 5,0 10,20 10,20 2,19 0,0 0)(10 3,15 3,15 7,10 7,10 3))",
"ls_poly2.svg");
to_svg<ls, poly>("LINESTRING(15 7,15 5,24 5,20 2,19 0,13 -4,1 0,10 0,13 3,15 7,16 10,10 10,8 10,4 6,2 8,1 10)",
"POLYGON((0 0,5 5,0 10,20 10,20 2,19 0,0 0)(10 3,15 3,15 7,10 7,10 3))",
"ls_poly3.svg");
to_svg<ls, poly>("LINESTRING(15 5,15 7,24 5,20 2,19 0,13 -4,1 0,10 0,13 3,15 7,16 10,10 10,8 10,4 6,2 8,1 10)",
"POLYGON((0 0,5 5,0 10,20 10,20 2,19 0,0 0)(10 3,15 3,15 7,10 7,10 3))",
"ls_poly4.svg");
to_svg<ls, poly>("LINESTRING(15 5,15 3,24 5,20 2,19 0,13 -4,1 0,10 0,13 3,15 7,16 10,10 10,8 10,4 6,2 8,1 10)",
"POLYGON((0 0,5 5,0 10,20 10,20 2,19 0,0 0)(10 3,15 3,15 7,10 7,10 3))",
"ls_poly5.svg");
// true hole
test_geometry<ls, poly>("LINESTRING(9 1,10 5,9 9)",
"POLYGON((0 0,0 10,10 10,10 5,10 0,0 0)(2 2,10 5,2 8,2 2))",
expected("tiu")("tiu").vec);
test_geometry<ls, poly>("LINESTRING(10 1,10 5,10 9)",
"POLYGON((0 0,0 10,10 10,10 5,10 0,0 0)(2 2,10 5,2 8,2 2))",
expected("mcu")("ecc")("tiu")("mxc").vec);
to_svg<poly, ls>("POLYGON((0 0,5 5,0 10,20 10,20 2,19 0,0 0)(10 3,15 3,15 7,10 7,10 3))",
"LINESTRING(15 5,24 5,20 2,19 0,13 -4,1 0,10 0,13 3,15 7,16 10,10 10,8 10,4 6,2 8,1 10)",
"poly_ls.svg");
// fake hole
test_geometry<ls, poly>("LINESTRING(9 1,10 5,9 9)",
"POLYGON((0 0,0 10,10 10,10 5,2 8,2 2,10 5,10 0,0 0))",
expected("tuu")("tiu").vec);
test_geometry<ls, poly>("LINESTRING(10 1,10 5,10 9)",
"POLYGON((0 0,0 10,10 10,10 5,2 8,2 2,10 5,10 0,0 0))",
expected("mcu")("tuc")("tcu")("mxc").vec);
test_geometry<ls, poly>("LINESTRING(10 1,10 5,2 2)",
"POLYGON((0 0,0 10,10 10,10 0,0 0),(10 5,2 8,2 2,10 5))",
expected("mcu")("mic")("tcu")("txc"));
test_geometry<ls, poly>("LINESTRING(10 1,10 5,2 8)",
"POLYGON((0 0,0 10,10 10,10 0,0 0),(10 5,2 8,2 2,10 5))",
expected("mcu")("mic")("tcc")("txu"));
}
int test_main(int, char* [])
@@ -85,142 +91,3 @@ int test_main(int, char* [])
#endif
return 0;
}
/*
to_svg<ls, ls>("LINESTRING(0 0,2 0)", "LINESTRING(1 0,3 0)", "lsls0000.svg");
to_svg<ls, ls>("LINESTRING(1 0,3 0)", "LINESTRING(2 0,0 0)", "lsls0001.svg");
to_svg<ls, ls>("LINESTRING(0 0,2 0)", "LINESTRING(3 0,1 0)", "lsls0002.svg");
to_svg<ls, ls>("LINESTRING(0 0,1 0)", "LINESTRING(1 0,2 0)", "lsls0003.svg");
to_svg<ls, ls>("LINESTRING(0 0,1 0)", "LINESTRING(2 0,1 0)", "lsls0004.svg");
to_svg<ls, ls>("LINESTRING(1 0,2 0)", "LINESTRING(1 0,0 0)", "lsls0005.svg");
to_svg<ls, ls>("LINESTRING(0 0,2 0)", "LINESTRING(0 0,2 0)", "lsls0006.svg");
to_svg<ls, ls>("LINESTRING(0 0,2 0)", "LINESTRING(2 0,0 0)", "lsls0007.svg");
to_svg<ls, ls>("LINESTRING(0 0,3 0)", "LINESTRING(1 0,2 0)", "lsls0008.svg");
to_svg<ls, ls>("LINESTRING(0 0,3 0)", "LINESTRING(2 0,1 0)", "lsls0009.svg");
to_svg<ls, ls>("LINESTRING(0 0,1 0)", "LINESTRING(1 0,1 1)", "lsls00010.svg");
to_svg<ls, ls>("LINESTRING(0 0,1 0)", "LINESTRING(1 1,1 0)", "lsls00011.svg");
to_svg<ls, ls>("LINESTRING(0 0,1 0)", "LINESTRING(0 0,0 1)", "lsls00012.svg");
to_svg<ls, ls>("LINESTRING(0 0,1 0)", "LINESTRING(0 1,0 0)", "lsls00013.svg");
to_svg<ls, ls>("LINESTRING(0 0,2 0)", "LINESTRING(1 0,1 1)", "lsls00014.svg");
to_svg<ls, ls>("LINESTRING(0 0,2 0)", "LINESTRING(1 1,1 0)", "lsls00015.svg");
to_svg<ls, ls>("LINESTRING(0 0,2 0)", "LINESTRING(1 0,3 1)", "lsls00016.svg");
to_svg<ls, ls>("LINESTRING(0 0,2 0)", "LINESTRING(2 0,1 0)", "lsls00017.svg");
to_svg<ls, ls>("LINESTRING(0 0,2 0)", "LINESTRING(0 0,1 0)", "lsls00018.svg");
to_svg<ls, ls>("LINESTRING(0 0,2 0)", "LINESTRING(1 0,0 0)", "lsls00019.svg");
to_svg<ls, ls>("LINESTRING(0 0,2 0)", "LINESTRING(1 0,2 0)", "lsls00020.svg");
to_svg<ls, ls>("LINESTRING(0 0,2 0)", "LINESTRING(0 0,2 0)", "lsls000.svg");
to_svg<ls, ls>("LINESTRING(0 0,2 0,2 0,3 0)", "LINESTRING(0 0,2 0)", "lsls001.svg");
to_svg<ls, ls>("LINESTRING(1 0,1 1)", "LINESTRING(0 0,1 0,2 0)", "lsls0020.svg");
to_svg<ls, ls>("LINESTRING(1 0,0 0)", "LINESTRING(0 0,1 0,2 0)", "lsls0021.svg");
to_svg<ls, ls>("LINESTRING(1 0,2 0)", "LINESTRING(0 0,1 0,2 0)", "lsls0022.svg");
to_svg<ls, ls>("LINESTRING(1 1,1 0)", "LINESTRING(0 0,1 0,2 0)", "lsls0023.svg");
to_svg<ls, ls>("LINESTRING(0 0,1 0)", "LINESTRING(0 0,1 0,2 0)", "lsls0024.svg");
to_svg<ls, ls>("LINESTRING(2 0,1 0)", "LINESTRING(0 0,1 0,2 0)", "lsls0025.svg");
to_svg<ls, ls>("LINESTRING(0 0,1 0,2 0)", "LINESTRING(1 0,1 1)", "lsls00200.svg");
to_svg<ls, ls>("LINESTRING(0 0,1 0,2 0)", "LINESTRING(1 0,0 0)", "lsls00211.svg");
to_svg<ls, ls>("LINESTRING(0 0,1 0,2 0)", "LINESTRING(1 0,2 0)", "lsls00222.svg");
to_svg<ls, ls>("LINESTRING(0 0,1 0,2 0)", "LINESTRING(1 1,1 0)", "lsls00233.svg");
to_svg<ls, ls>("LINESTRING(0 0,1 0,2 0)", "LINESTRING(0 0,1 0)", "lsls00244.svg");
to_svg<ls, ls>("LINESTRING(0 0,1 0,2 0)", "LINESTRING(2 0,1 0)", "lsls00255.svg");
to_svg<ls, ls>("LINESTRING(0 0,2 0,4 0,6 0,8 0)", "LINESTRING(1 0,3 0,5 0,6 0,9 0)", "lsls01.svg");
to_svg<ls, ls>("LINESTRING(0 0,2 0,4 0,4 4)", "LINESTRING(1 0,3 0,4 0,4 2,4 5)", "lsls02.svg");
to_svg<ls, ls>("LINESTRING(0 0,2 0,4 0,4 4)", "LINESTRING(1 0,4 0,4 4)", "lsls031.svg");
to_svg<ls, ls>("LINESTRING(0 0,2 0,4 0,4 4)", "LINESTRING(4 0,2 0,0 0)", "lsls032.svg");
to_svg<ls, ls>("LINESTRING(0 0,2 0,4 0,4 4)", "LINESTRING(4 0,2 2,0 2)", "lsls0321.svg");
to_svg<ls, ls>("LINESTRING(0 0,2 0,4 0,4 4)", "LINESTRING(4 0,2 0)", "lsls033.svg");
to_svg<ls, ls>("LINESTRING(0 0,2 0,4 0,4 4)", "LINESTRING(4 0,4 4)", "lsls034.svg");
to_svg<ls, ls>("LINESTRING(0 0,2 0,4 0,4 4)", "LINESTRING(4 0,3 1)", "lsls035.svg");
to_svg<ls, ls>("LINESTRING(0 0,2 0,4 0,4 4)", "LINESTRING(4 0,4 -1)", "lsls036.svg");
to_svg<ls, ls>("LINESTRING(0 0,2 0,4 0,4 4)", "LINESTRING(1 0,4 0,4 3)", "lsls04.svg");
to_svg<ls, ls>("LINESTRING(1 0,2 0,4 0,6 0,8 0)", "LINESTRING(0 0,3 0,5 0,6 0,9 0)", "lsls05.svg");
to_svg<ls, ls>("LINESTRING(0 0,10 0,10 10)", "LINESTRING(1 0,10 9)", "lsls061.svg");
to_svg<ls, ls>("LINESTRING(0 0,10 0,10 10)", "LINESTRING(1 0,10 -9)", "lsls062.svg");
to_svg<ls, ls>("LINESTRING(0 0,10 0,10 10)", "LINESTRING(1 0,-10 9)", "lsls063.svg");
to_svg<ls, ls>("LINESTRING(0 0,10 0,10 10)", "LINESTRING(1 0,-10 -9)", "lsls064.svg");
to_svg<ls, ls>("LINESTRING(0 0,1 0,10 9,10 10)", "LINESTRING(1 0,10 9)", "lsls065.svg");
to_svg<ls, ls>("LINESTRING(0 0,10 0,10 10)", "LINESTRING(1 0,9 9)", "lsls071.svg");
to_svg<ls, ls>("LINESTRING(0 0,10 0,10 10)", "LINESTRING(1 0,9 -9)", "lsls072.svg");
to_svg<ls, ls>("LINESTRING(0 0,10 0,10 10)", "LINESTRING(1 0,-9 9)", "lsls073.svg");
to_svg<ls, ls>("LINESTRING(0 0,10 0,10 10)", "LINESTRING(1 0,-9 -9)", "lsls074.svg");
to_svg<ls, ls>("LINESTRING(0 0,1 0,10 0,10 10)", "LINESTRING(1 0,9 9)", "lsls081.svg");
to_svg<ls, ls>("LINESTRING(0 0,1 0,10 0,10 10)", "LINESTRING(0 0,9 9)", "lsls082.svg");
to_svg<ls, ls>("LINESTRING(0 0,1 0)", "LINESTRING(1 0,9 9)", "lsls083.svg");
to_svg<ls, ls>("LINESTRING(0 0,1 0)", "LINESTRING(9 9,1 0)", "lsls084.svg");
to_svg<ls, ls>("LINESTRING(0 0,1 0)", "LINESTRING(1 0,2 0)", "lsls085.svg");
to_svg<ls, ls>("LINESTRING(0 0,1 0)", "LINESTRING(2 0,1 0)", "lsls086.svg");
to_svg<ls, ls>("LINESTRING(0 0,10 0,10 10)", "LINESTRING(1 1,10 5)", "lsls091.svg");
to_svg<ls, ls>("LINESTRING(0 0,10 0,10 5,10 10)", "LINESTRING(1 1,10 5)", "lsls092.svg");
to_svg<ls, ls>("LINESTRING(0 0,10 0,10 10)", "LINESTRING(19 1,10 5)", "lsls093.svg");
to_svg<ls, ls>("LINESTRING(0 0,10 0,10 5,10 10)", "LINESTRING(19 1,10 5)", "lsls094.svg");
to_svg<ls, ls>("LINESTRING(5 3,1 1,3 3,2 2,0 0)", "LINESTRING(0 0,3 3,6 3)", "1F100F10T.svg");
to_svg<ls, ls>("LINESTRING(0 0,2 0,4 0)", "LINESTRING(1 1,1 0,3 0,3 1)", "lsls_01.svg");
to_svg<ls, ls>("LINESTRING(0 0,2 0,4 0)", "LINESTRING(1 -1,1 0,3 0,3 -1)", "lsls_02.svg");
to_svg<ls, ls>("LINESTRING(0 0,2 0,4 0)", "LINESTRING(3 1,3 0,1 0,1 1)", "lsls_03.svg");
to_svg<ls, ls>("LINESTRING(0 0,2 0,4 0)", "LINESTRING(3 -1,3 0,1 0,1 -1)", "lsls_04.svg");
to_svg<ls, ls>("LINESTRING(0 0,2 0,3 0,4 0,6 0)", "LINESTRING(2 1,2 0,4 0,4 1)", "lsls_05.svg");
to_svg<ls, ls>("LINESTRING(0 0,2 0,3 0,4 0,6 0)", "LINESTRING(2 -1,2 0,4 0,4 -1)", "lsls_06.svg");
to_svg<ls, ls>("LINESTRING(0 0,2 0,3 0,4 0,6 0)", "LINESTRING(4 1,4 0,2 0,2 1)", "lsls_07.svg");
to_svg<ls, ls>("LINESTRING(0 0,2 0,3 0,4 0,6 0)", "LINESTRING(4 -1,4 0,2 0,2 -1)", "lsls_08.svg");
to_svg<ls, ls>("LINESTRING(0 0,2 0,4 0)", "LINESTRING(1 1,1 0,2 0,3 0,3 1)", "lsls_11.svg");
to_svg<ls, ls>("LINESTRING(0 0,2 0,4 0)", "LINESTRING(1 -1,1 0,2 0,3 0,3 -1)", "lsls_12.svg");
to_svg<ls, ls>("LINESTRING(0 0,2 0,4 0)", "LINESTRING(3 1,3 0,2 0,1 0,1 1)", "lsls_13.svg");
to_svg<ls, ls>("LINESTRING(0 0,2 0,4 0)", "LINESTRING(3 -1,3 0,2 0,1 0,1 -1)", "lsls_14.svg");
to_svg<ls, ls>("LINESTRING(0 0,2 0,3 0,4 0,6 0)", "LINESTRING(2 1,2 0,3 0,4 0,4 1)", "lsls_15.svg");
to_svg<ls, ls>("LINESTRING(0 0,2 0,3 0,4 0,6 0)", "LINESTRING(2 -1,2 0,3 0,4 0,4 -1)", "lsls_16.svg");
to_svg<ls, ls>("LINESTRING(0 0,2 0,3 0,4 0,6 0)", "LINESTRING(4 1,4 0,3 0,2 0,2 1)", "lsls_17.svg");
to_svg<ls, ls>("LINESTRING(0 0,2 0,3 0,4 0,6 0)", "LINESTRING(4 -1,4 0,3 0,2 0,2 -1)", "lsls_18.svg");
to_svg<ls, ls>("LINESTRING(0 5,5 5,10 5,10 10,5 10,5 5,5 0)", "LINESTRING(0 5,5 5,5 10,10 10,10 5,5 5,5 0)", "lsls11.svg");
to_svg<ls, ls>("LINESTRING(0 5,5 5,10 5,10 10,5 10,5 5,5 0)", "LINESTRING(5 0,5 5,10 5,10 10,5 10,5 5,0 5)", "lsls12.svg");
to_svg<ls, ls>("LINESTRING(5 0,5 5,5 10,10 10,10 5,5 5,0 5)", "LINESTRING(0 5,5 5,5 10,10 10,10 5,5 5,5 0)", "lsls13.svg");
to_svg<ls, ls>("LINESTRING(5 0,5 5,5 10,10 10,10 5,5 5,0 5)", "LINESTRING(5 0,5 5,10 5,10 10,5 10,5 5,0 5)", "lsls14.svg");
to_svg<ls, ls>("LINESTRING(0 5,10 5,10 10,5 10,5 0)", "LINESTRING(0 5,5 5,5 10,10 10,10 5,5 5,5 0)", "lsls15.svg");
to_svg<ls, ls>("LINESTRING(0 5,10 5,10 10,5 10,5 0)", "LINESTRING(5 0,5 10,10 10,10 5,0 5)", "lsls16.svg");
to_svg<ls, ls>("LINESTRING(0 5,10 5)", "LINESTRING(5 0,5 10,10 10,10 5,0 5)", "lsls161.svg");
to_svg<ls, ls>("LINESTRING(0 5,8 5,10 5)", "LINESTRING(5 0,5 10,10 10,10 5,0 5)", "lsls162.svg");
to_svg<ls, ls>("LINESTRING(0 5,8 5)", "LINESTRING(5 0,5 10,10 10,10 5,0 5)", "lsls1631.svg");
to_svg<ls, ls>("LINESTRING(0 5,1 5,7 5,8 5)", "LINESTRING(5 0,5 10,10 10,10 5,0 5)", "lsls1632.svg");
to_svg<ls, ls>("LINESTRING(0 5,1 5,7 5,8 5)", "LINESTRING(5 10,10 10,10 5,0 5)", "lsls1633.svg");
to_svg<ls, ls>("LINESTRING(0 5,8 4)", "LINESTRING(5 0,5 10,10 10,10 5,0 5)", "lsls1641.svg");
to_svg<ls, ls>("LINESTRING(0 5,8 6)", "LINESTRING(5 0,5 10,10 10,10 5,0 5)", "lsls1642.svg");
to_svg<ls, ls>("LINESTRING(1 5,8 4)", "LINESTRING(5 0,5 10,10 10,10 5,0 5)", "lsls1643.svg");
to_svg<ls, ls>("LINESTRING(1 5,8 5)", "LINESTRING(5 0,5 10,10 10,10 5,0 5)", "lsls1644.svg");
to_svg<ls, ls>("LINESTRING(0 5,5 5,8 4)", "LINESTRING(5 0,5 10,10 10,10 5,0 5)", "lsls165.svg");
to_svg<ls, ls>("LINESTRING(0 5,5 5,8 5)", "LINESTRING(5 0,5 10,10 10,10 5,0 5)", "lsls166.svg");
to_svg<ls, ls>("LINESTRING(0 5,5 5,8 5)", "LINESTRING(0 10,10 0,5 0,5 10,10 10,10 5,0 5)", "lsls167.svg");
to_svg<ls, ls>("LINESTRING(0 5,5 5,8 5)", "LINESTRING(0 10,5 5,10 0,5 0,5 5,5 10,10 10,10 5,0 5)", "lsls168.svg");
to_svg<ls, ls>("LINESTRING(0 0,0 10,10 10,10 0,0 0)", "LINESTRING(0 2,0 0,10 0,10 10,0 10,0 8,0 2)", "lsls1690.svg");
to_svg<ls, ls>("LINESTRING(0 0,10 0,10 10,0 10,0 0)", "LINESTRING(0 8,0 0,10 0,10 10,0 10,0 8)", "lsls1691.svg");
to_svg<ls, ls>("LINESTRING(0 0,10 0,10 10,0 10,0 0)", "LINESTRING(0 2,0 0,10 0,10 10,0 10,0 8)", "lsls1692.svg");
to_svg<ls, ls>("LINESTRING(0 0,0 10,10 10,10 0,0 0)", "LINESTRING(0 2,0 0,10 0,10 10,0 10,0 8)", "lsls1693.svg");
to_svg<ls, ls>("LINESTRING(0 2,0 0,10 0,10 10,0 10,0 8)", "LINESTRING(0 0,10 0,10 10,0 10,0 0)", "lsls1694.svg");
to_svg<ls, ls>("LINESTRING(0 2,0 0,10 0,10 10,0 10,0 8)", "LINESTRING(0 0,0 10,10 10,10 0,0 0)", "lsls1695.svg");
to_svg<ls>("LINESTRING(0 8,0 0,10 0,10 10,0 10,0 2)", "ls1.svg");
to_svg<ls>("LINESTRING(8 8,0 0,10 0,10 10,0 10,8 2)", "ls2.svg");
typedef bg::model::multi_linestring<ls> mls;
to_svg<ls, mls>("LINESTRING(0 5,10 5,10 10,5 10,5 0)", "MULTILINESTRING((5 0,5 7),(5 8,5 10,10 10,10 5,0 5))", "lsls17.svg");
to_svg<ls, mls>("LINESTRING(0 5,10 5,10 10,5 10,5 0)", "MULTILINESTRING((5 0,5 4,5 6,5 7),(5 8,5 10,10 10,10 5,0 5))", "lsls18.svg");
to_svg<ls, mls>("LINESTRING(0 5,10 5,10 10,5 10,5 0)", "MULTILINESTRING((5 0,5 8),(5 7,5 10,10 10,10 5,0 5))", "lsls19.svg");
to_svg<mls, ls>("MULTILINESTRING((5 0,5 7),(5 8,5 10,10 10,10 5,0 5))", "LINESTRING(0 5,10 5,10 10,5 10,5 0)", "lsls20.svg");
to_svg<mls, ls>("MULTILINESTRING((5 0,5 8),(5 7,5 10,10 10,10 5,0 5))", "LINESTRING(0 5,10 5,10 10,5 10,5 0)", "lsls21.svg");
to_svg<ls, ls>("LINESTRING(0 5,5 5,10 5,10 10,5 10,5 5,5 0)", "LINESTRING(0 5,5 5,0 10,10 10,10 5,5 5,5 0)", "lsls100.svg");
to_svg<ls, ls>("LINESTRING(5 0,5 5,5 0)", "LINESTRING(0 5,5 5,0 10,2 10,5 5,5 10,10 10,10 5,5 5,10 2,10 0,8 0,5 5,5 0)", "lsls101.svg");
*/

View File

@@ -78,16 +78,16 @@ void test_all()
test_geometry<ls, ls>("LINESTRING(-1 1,0 0,1 0,4 0,5 5,10 5,15 0,31 0)",
"LINESTRING(-1 -1,0 0,1 0,2 0,2.5 1,3 0,30 0)",
expected("tii")("ecc")("muu")("mii")("muu")("mii")("mux").vec);
expected("tii")("ecc")("muu")("mii")("muu")("mii")("mux"));
test_geometry<ls, ls>("LINESTRING(-1 1,0 0,1 0,4 0,5 5,10 5,15 0,31 0)",
"LINESTRING(30 0,3 0,2.5 1,2 0,1 0,0 0,-1 -1)",
expected("tiu")("ecc")("mui")("miu")("mui")("miu")("mui").vec);
expected("tiu")("ecc")("mui")("miu")("mui")("miu")("mui"));
test_geometry<ls, ls>("LINESTRING(31 0,15 0,10 5,5 5,4 0,1 0,0 0,-1 1)",
"LINESTRING(-1 -1,0 0,1 0,2 0,2.5 1,3 0,30 0)",
expected("tui")("ecc")("miu")("mui")("miu")("mui")("mix").vec);
expected("tui")("ecc")("miu")("mui")("miu")("mui")("mix"));
test_geometry<ls, ls>("LINESTRING(31 0,15 0,10 5,5 5,4 0,1 0,0 0,-1 1)",
"LINESTRING(30 0,3 0,2.5 1,2 0,1 0,0 0,-1 -1)",
expected("tuu")("ecc")("mii")("muu")("mii")("muu")("mii").vec);
expected("tuu")("ecc")("mii")("muu")("mii")("muu")("mii"));
test_geometry<ls, ls>("LINESTRING(-1 0,1 0,2 1,3 2)", "LINESTRING(4 5,3 2,1 0,0 0)", "mix", "txi", "ecc");
test_geometry<ls, ls>("LINESTRING(4 5,3 2,1 0,0 0)", "LINESTRING(-1 0,1 0,2 1,3 2)", "mxi", "tix", "ecc");
@@ -99,9 +99,14 @@ void test_all()
test_geometry<ls, ls>("LINESTRING(4 0,4 1,20 1,5 0,1 0)", "LINESTRING(0 0,30 0)", "muu", "miu", "mxi");
test_geometry<ls, ls>("LINESTRING(30 0,0 0)", "LINESTRING(1 0,5 0,20 1,4 1,4 0,5 0)",
expected("mui")("miu")("mui")("mix").vec);
expected("mui")("miu")("mui")("mix"));
test_geometry<ls, ls>("LINESTRING(1 0,5 0,20 1,4 1,4 0,5 0)", "LINESTRING(30 0,0 0)",
expected("miu")("mui")("miu")("mxi").vec);
expected("miu")("mui")("miu")("mxi"));
test_geometry<ls, ls>("LINESTRING(1 0,7 0,8 1)", "LINESTRING(0 0,10 0,10 10,4 -1)",
expected("mii")("iuu")("muu"));
test_geometry<ls, ls>("LINESTRING(1 0,7 0,8 1)", "LINESTRING(0 0,10 0,10 10,5 0,4 1)",
expected("mii")("muu")("muu"));
//if ( boost::is_same<T, double>::value )
//{

View File

@@ -49,13 +49,13 @@ struct equal_turn
const std::string * turn_ptr;
};
template <typename Geometry1, typename Geometry2>
void check_geometry(
template <typename Geometry1, typename Geometry2, typename Range>
void check_geometry_range(
Geometry1 const& g1,
Geometry2 const& g2,
std::string const& wkt1,
std::string const& wkt2,
std::vector<std::string> const& expected)
Range const& expected)
{
typedef bg::detail::overlay::turn_info
<
@@ -77,16 +77,17 @@ void check_geometry(
turn_policy_t
>::apply(0, g1, 1, g2, bg::detail::no_rescale_policy(), turns, interrupt_policy);
bool ok = expected.size() == turns.size();
bool ok = boost::size(expected) == turns.size();
BOOST_CHECK_MESSAGE(ok,
"get_turns: " << wkt1 << " and " << wkt2
<< " -> Expected turns #: " << expected.size() << " detected turns #: " << turns.size());
<< " -> Expected turns #: " << boost::size(expected) << " detected turns #: " << turns.size());
BOOST_FOREACH(std::string const& s, expected)
for ( typename boost::range_iterator<Range const>::type sit = boost::begin(expected) ;
sit != boost::end(expected) ; ++sit)
{
typename std::vector<turn_info>::iterator
it = std::find_if(turns.begin(), turns.end(), equal_turn(s));
it = std::find_if(turns.begin(), turns.end(), equal_turn(*sit));
if ( it != turns.end() )
turns.erase(it);
@@ -94,20 +95,20 @@ void check_geometry(
{
BOOST_CHECK_MESSAGE(false,
"get_turns: " << wkt1 << " and " << wkt2
<< " -> Expected turn: " << s << " not found");
<< " -> Expected turn: " << *sit << " not found");
}
}
}
template <typename Geometry1, typename Geometry2>
void test_geometry(std::string const& wkt1, std::string const& wkt2,
std::vector<std::string> const& expected)
template <typename Geometry1, typename Geometry2, typename Range>
void test_geometry_range(std::string const& wkt1, std::string const& wkt2,
Range const& expected)
{
Geometry1 geometry1;
Geometry2 geometry2;
bg::read_wkt(wkt1, geometry1);
bg::read_wkt(wkt2, geometry2);
check_geometry(geometry1, geometry2, wkt1, wkt2, expected);
check_geometry_range(geometry1, geometry2, wkt1, wkt2, expected);
}
template <typename G1, typename G2>
@@ -116,7 +117,7 @@ void test_geometry(std::string const& wkt1, std::string const& wkt2,
{
std::vector<std::string> expected;
expected.push_back(ex0);
test_geometry<G1, G2>(wkt1, wkt2, expected);
test_geometry_range<G1, G2>(wkt1, wkt2, expected);
}
template <typename G1, typename G2>
@@ -126,7 +127,7 @@ void test_geometry(std::string const& wkt1, std::string const& wkt2,
std::vector<std::string> expected;
expected.push_back(ex0);
expected.push_back(ex1);
test_geometry<G1, G2>(wkt1, wkt2, expected);
test_geometry_range<G1, G2>(wkt1, wkt2, expected);
}
template <typename G1, typename G2>
@@ -137,7 +138,7 @@ void test_geometry(std::string const& wkt1, std::string const& wkt2,
expected.push_back(ex0);
expected.push_back(ex1);
expected.push_back(ex2);
test_geometry<G1, G2>(wkt1, wkt2, expected);
test_geometry_range<G1, G2>(wkt1, wkt2, expected);
}
struct expected_pusher
@@ -147,6 +148,15 @@ struct expected_pusher
vec.push_back(ex);
return *this;
}
typedef std::vector<std::string>::iterator iterator;
typedef std::vector<std::string>::const_iterator const_iterator;
//iterator begin() { return vec.begin(); }
//iterator end() { return vec.end(); }
const_iterator begin() const { return vec.begin(); }
const_iterator end() const { return vec.end(); }
std::vector<std::string> vec;
};
@@ -156,4 +166,18 @@ expected_pusher expected(std::string const& ex)
return res(ex);
}
template <typename G1, typename G2>
void test_geometry(std::string const& wkt1, std::string const& wkt2,
std::vector<std::string> const& expected)
{
test_geometry_range<G1, G2>(wkt1, wkt2, expected);
}
template <typename G1, typename G2>
void test_geometry(std::string const& wkt1, std::string const& wkt2,
expected_pusher const& expected)
{
test_geometry_range<G1, G2>(wkt1, wkt2, expected);
}
#endif // BOOST_GEOMETRY_TEST_ALGORITHMS_OVERLAY_TEST_GET_TURNS_HPP

View File

@@ -37,6 +37,7 @@
#include <boost/geometry.hpp>
#include <boost/geometry/multi/geometries/multi_linestring.hpp>
#include <boost/geometry/multi/geometries/multi_point.hpp>
#include <boost/geometry/multi/geometries/multi_polygon.hpp>
//TEST
#include <to_svg.hpp>
@@ -59,9 +60,7 @@ void check_geometry(Geometry1 const& geometry1,
std::string const& expected)
{
{
bgdr::result res;
bgdr::relate(geometry1, geometry2, res);
std::string res_str(boost::begin(res.get_code()), boost::end(res.get_code()));
std::string res_str = bgdr::relate<bgdr::matrix9>(geometry1, geometry2);
bool ok = boost::equal(res_str, expected);
BOOST_CHECK_MESSAGE(ok,
"relate: " << wkt1
@@ -72,9 +71,7 @@ void check_geometry(Geometry1 const& geometry1,
// changed sequence of geometries - transposed result
{
bgdr::result res;
bgdr::relate(geometry2, geometry1, res);
std::string res_str(boost::begin(res.get_code()), boost::end(res.get_code()));
std::string res_str = bgdr::relate(geometry2, geometry1, bgdr::matrix9());
std::string expected_tr = transposed(expected);
bool ok = boost::equal(res_str, expected_tr);
BOOST_CHECK_MESSAGE(ok,
@@ -84,20 +81,16 @@ void check_geometry(Geometry1 const& geometry1,
<< " detected: " << res_str);
}
static const bool int_en = bgdr::interruption_enabled<Geometry1, Geometry2>::value;
{
bgdr::mask<int_en> mask(expected);
bgdr::relate(geometry1, geometry2, mask);
std::string res_str(boost::begin(mask.get_code()), boost::end(mask.get_code()));
BOOST_CHECK_MESSAGE((!mask.interrupt && mask.check()),
bool result = bgdr::relate(geometry1, geometry2, bgdr::mask9(expected));
// TODO: SHOULD BE !interrupted - CHECK THIS!
BOOST_CHECK_MESSAGE(result,
"relate: " << wkt1
<< " and " << wkt2
<< " -> Expected: " << expected
<< " detected: " << res_str);
<< " -> Expected: " << expected);
}
if ( int_en )
if ( bg::detail::relate::interruption_enabled<Geometry1, Geometry2>::value )
{
// brake the expected output
std::string expected_interrupt = expected;
@@ -117,14 +110,12 @@ void check_geometry(Geometry1 const& geometry1,
if ( changed )
{
bgdr::mask<int_en> mask(expected_interrupt);
bgdr::relate(geometry1, geometry2, mask);
std::string res_str(boost::begin(mask.get_code()), boost::end(mask.get_code()));
BOOST_CHECK_MESSAGE(mask.interrupt,
bool result = bgdr::relate(geometry1, geometry2, bgdr::mask9(expected_interrupt));
// TODO: SHOULD BE interrupted - CHECK THIS!
BOOST_CHECK_MESSAGE(!result,
"relate: " << wkt1
<< " and " << wkt2
<< " -> Expected interrupt for:" << expected_interrupt
<< " detected: " << res_str);
<< " -> Expected interrupt for:" << expected_interrupt);
}
}
}
@@ -300,6 +291,28 @@ void test_linestring_linestring()
"LINESTRING(30 0,4 0,3 1,2 0,1 0,0 0,-1 -1)",
"101FF0102");
// self-IP
test_geometry<ls, ls>("LINESTRING(1 0,9 0)",
"LINESTRING(0 0,10 0,10 10,5 0,0 10)",
"1FF0FF102");
test_geometry<ls, ls>("LINESTRING(1 0,5 0,9 0)",
"LINESTRING(0 0,10 0,10 10,5 0,0 10)",
"1FF0FF102");
test_geometry<ls, ls>("LINESTRING(1 0,9 0)",
"LINESTRING(0 0,10 0,10 10,5 10,5 -1)",
"1FF0FF102");
test_geometry<ls, ls>("LINESTRING(1 0,9 0)",
"LINESTRING(0 0,10 0,5 0,5 5)",
"1FF0FF102");
test_geometry<ls, ls>("LINESTRING(1 0,7 0)", "LINESTRING(0 0,10 0,10 10,4 -1)",
"1FF0FF102");
test_geometry<ls, ls>("LINESTRING(1 0,5 0,7 0)", "LINESTRING(0 0,10 0,10 10,4 -1)",
"1FF0FF102");
test_geometry<ls, ls>("LINESTRING(1 0,7 0,8 1)", "LINESTRING(0 0,10 0,10 10,4 -1)",
"1F10F0102");
test_geometry<ls, ls>("LINESTRING(1 0,5 0,7 0,8 1)", "LINESTRING(0 0,10 0,10 10,4 -1)",
"1F10F0102");
// linear ring
test_geometry<ls, ls>("LINESTRING(0 0,10 0)", "LINESTRING(5 0,9 0,5 5,1 0,5 0)", "1F1FF01F2");
test_geometry<ls, ls>("LINESTRING(0 0,5 0,10 0)", "LINESTRING(5 0,9 0,5 5,1 0,5 0)", "1F1FF01F2");
@@ -318,6 +331,24 @@ void test_linestring_linestring()
//test_geometry<ls, ls>("LINESTRING(0 0,5 0)", "LINESTRING(1 0,1 0,1 0)", "0F1FF0FF2");
// Point/Point
//test_geometry<ls, ls>("LINESTRING(0 0)", "LINESTRING(0 0)", "0FFFFFFF2");
// OTHER MASKS
{
namespace bgdr = bg::detail::relate;
ls ls1, ls2, ls3;
bg::read_wkt("LINESTRING(0 0,2 0)", ls1);
bg::read_wkt("LINESTRING(2 0,4 0)", ls2);
bg::read_wkt("LINESTRING(1 0,1 1)", ls3);
BOOST_CHECK(bgdr::relate(ls1, ls2, bgdr::mask9("FT*******")
|| bgdr::mask9("F**T*****")
|| bgdr::mask9("F***T****")));
BOOST_CHECK(bgdr::relate(ls1, ls3, bgdr::mask9("FT*******")
|| bgdr::mask9("F**T*****")
|| bgdr::mask9("F***T****")));
BOOST_CHECK(bgdr::relate(ls3, ls1, bgdr::mask9("FT*******")
|| bgdr::mask9("F**T*****")
|| bgdr::mask9("F***T****")));
}
}
template <typename P>
@@ -344,6 +375,184 @@ void test_linestring_multi_linestring()
//test_geometry<ls, mls>("LINESTRING(0 0,10 0)", "MULTILINESTRING((1 0,9 0),(2 0,2 0,2 0))", "101FF0FF2");
}
template <typename P>
void test_linestring_polygon()
{
typedef bg::model::linestring<P> ls;
typedef bg::model::polygon<P> poly;
// LS disjoint
test_geometry<ls, poly>("LINESTRING(11 0,11 10)", "POLYGON((0 0,0 10,10 10,10 0,0 0))", "FF1FF0212");
// II BB
test_geometry<ls, poly>("LINESTRING(0 0,10 10)", "POLYGON((0 0,0 10,10 10,10 0,0 0))", "1FFF0F212");
test_geometry<ls, poly>("LINESTRING(5 0,5 5,10 5)", "POLYGON((0 0,0 10,10 10,10 0,0 0))", "1FFF0F212");
test_geometry<ls, poly>("LINESTRING(5 1,5 5,9 5)", "POLYGON((0 0,0 10,10 10,10 0,0 0))", "1FF0FF212");
// IE
test_geometry<ls, poly>("LINESTRING(11 1,11 5)", "POLYGON((0 0,0 10,10 10,10 0,0 0))", "FF1FF0212");
// IE IB0
test_geometry<ls, poly>("LINESTRING(11 1,10 5)", "POLYGON((0 0,0 10,10 10,10 0,0 0))", "FF1F00212");
// IE IB1
test_geometry<ls, poly>("LINESTRING(11 1,10 5,10 10)", "POLYGON((0 0,0 10,10 10,10 0,0 0))", "F11F00212");
test_geometry<ls, poly>("LINESTRING(11 1,10 10,0 10)", "POLYGON((0 0,0 10,10 10,10 0,0 0))", "F11F00212");
test_geometry<ls, poly>("LINESTRING(11 1,10 0,0 0)", "POLYGON((0 0,0 10,10 10,10 0,0 0))", "F11F00212");
test_geometry<ls, poly>("LINESTRING(0 -1,1 0,2 0)", "POLYGON((0 0,0 10,10 10,10 0,0 0))", "F11F00212");
// IE IB0 II
test_geometry<ls, poly>("LINESTRING(11 1,10 5,5 5)", "POLYGON((0 0,0 10,10 10,10 0,0 0))", "1010F0212");
// IE IB0 lring
test_geometry<ls, poly>("LINESTRING(11 1,10 5,11 5,11 1)", "POLYGON((0 0,0 10,10 10,10 0,0 0))", "F01FFF212");
// IE IB1 lring
test_geometry<ls, poly>("LINESTRING(11 1,10 5,10 10,11 5,11 1)", "POLYGON((0 0,0 10,10 10,10 0,0 0))", "F11FFF212");
// IB1 II
test_geometry<ls, poly>("LINESTRING(0 0,5 0,5 5)", "POLYGON((0 0,0 10,10 10,10 0,0 0))", "11F00F212");
// BI0 II IB1
test_geometry<ls, poly>("LINESTRING(5 0,5 5,10 5,10 10)", "POLYGON((0 0,0 10,10 10,10 0,0 0))", "11FF0F212");
// IB1 II IB1
test_geometry<ls, poly>("LINESTRING(1 0,2 0,3 1,4 0,5 0)", "POLYGON((0 0,0 10,10 10,10 0,0 0))", "11FF0F212");
// IB1 IE IB1
test_geometry<ls, poly>("LINESTRING(1 0,2 0,3 -1,4 0,5 0)", "POLYGON((0 0,0 10,10 10,10 0,0 0))", "F11F0F212");
// II IB1
test_geometry<ls, poly>("LINESTRING(5 5,10 5,10 10)", "POLYGON((0 0,0 10,10 10,10 0,0 0))", "11F00F212");
// IB1 II
test_geometry<ls, poly>("LINESTRING(10 10,10 5,5 5)", "POLYGON((0 0,0 10,10 10,10 0,0 0))", "11F00F212");
// IE IB1
test_geometry<ls, poly>("LINESTRING(15 5,10 5,10 10)", "POLYGON((0 0,0 10,10 10,10 0,0 0))", "F11F00212");
// IB1 IE
test_geometry<ls, poly>("LINESTRING(10 10,10 5,15 5)", "POLYGON((0 0,0 10,10 10,10 0,0 0))", "F11F00212");
// II IB0 IE
test_geometry<ls, poly>("LINESTRING(5 5,10 5,15 10)", "POLYGON((0 0,0 10,10 10,10 0,0 0))", "1010F0212");
// non-simple polygon with hole
test_geometry<ls, poly>("LINESTRING(9 1,10 5,9 9)",
"POLYGON((0 0,0 10,10 10,10 0,0 0),(10 5,2 8,2 2,10 5))",
"10F0FF212");
test_geometry<ls, poly>("LINESTRING(10 1,10 5,10 9)",
"POLYGON((0 0,0 10,10 10,10 0,0 0),(10 5,2 8,2 2,10 5))",
"F1FF0F212");
test_geometry<ls, poly>("LINESTRING(2 8,10 5,2 2)",
"POLYGON((0 0,0 10,10 10,10 0,0 0),(10 5,2 8,2 2,10 5))",
"F1FF0F212");
test_geometry<ls, poly>("LINESTRING(10 1,10 5,2 2)",
"POLYGON((0 0,0 10,10 10,10 0,0 0),(10 5,2 8,2 2,10 5))",
"F1FF0F212");
test_geometry<ls, poly>("LINESTRING(10 1,10 5,2 8)",
"POLYGON((0 0,0 10,10 10,10 0,0 0),(10 5,2 8,2 2,10 5))",
"F1FF0F212");
// non-simple polygon with hole, linear ring
test_geometry<ls, poly>("LINESTRING(9 1,10 5,9 9,1 9,1 1,9 1)",
"POLYGON((0 0,0 10,10 10,10 0,0 0),(10 5,2 8,2 2,10 5))",
"10FFFF212");
test_geometry<ls, poly>("LINESTRING(10 5,10 9,11 5,10 1,10 5)",
"POLYGON((0 0,0 10,10 10,10 0,0 0),(10 5,2 8,2 2,10 5))",
"F11FFF212");
test_geometry<ls, poly>("LINESTRING(11 5,10 1,10 5,10 9,11 5)",
"POLYGON((0 0,0 10,10 10,10 0,0 0),(10 5,2 8,2 2,10 5))",
"F11FFF212");
// non-simple polygon with self-touching holes
test_geometry<ls, poly>("LINESTRING(7 1,8 5,7 9)",
"POLYGON((0 0,0 10,10 10,10 0,0 0),(8 1,9 1,9 9,8 9,8 1),(2 2,8 5,2 8,2 2))",
"10F0FF212");
test_geometry<ls, poly>("LINESTRING(8 2,8 5,8 8)",
"POLYGON((0 0,0 10,10 10,10 0,0 0),(8 1,9 1,9 9,8 9,8 1),(2 2,8 5,2 8,2 2))",
"F1FF0F212");
test_geometry<ls, poly>("LINESTRING(2 8,8 5,2 2)",
"POLYGON((0 0,0 10,10 10,10 0,0 0),(8 1,9 1,9 9,8 9,8 1),(2 2,8 5,2 8,2 2))",
"F1FF0F212");
// non-simple polygon self-touching
test_geometry<ls, poly>("LINESTRING(9 1,10 5,9 9)",
"POLYGON((0 0,0 10,10 10,10 5,2 8,2 2,10 5,10 0,0 0))",
"10F0FF212");
test_geometry<ls, poly>("LINESTRING(10 1,10 5,10 9)",
"POLYGON((0 0,0 10,10 10,10 5,2 8,2 2,10 5,10 0,0 0))",
"F1FF0F212");
test_geometry<ls, poly>("LINESTRING(2 8,10 5,2 2)",
"POLYGON((0 0,0 10,10 10,10 5,2 8,2 2,10 5,10 0,0 0))",
"F1FF0F212");
// non-simple polygon self-touching, linear ring
test_geometry<ls, poly>("LINESTRING(9 1,10 5,9 9,1 9,1 1,9 1)",
"POLYGON((0 0,0 10,10 10,10 5,2 8,2 2,10 5,10 0,0 0))",
"10FFFF212");
test_geometry<ls, poly>("LINESTRING(10 5,10 9,11 5,10 1,10 5)",
"POLYGON((0 0,0 10,10 10,10 5,2 8,2 2,10 5,10 0,0 0))",
"F11FFF212");
test_geometry<ls, poly>("LINESTRING(11 5,10 1,10 5,10 9,11 5)",
"POLYGON((0 0,0 10,10 10,10 5,2 8,2 2,10 5,10 0,0 0))",
"F11FFF212");
// polygons with exterior ring equals the linestring
test_geometry<ls, poly>("LINESTRING(0 0,10 0,10 10,0 10,0 0)",
"POLYGON((0 0,0 10,10 10,10 0,0 0))",
"F1FFFF2F2");
to_svg<ls, poly>("LINESTRING(0 0,10 0,10 10,0 10,0 0)",
"POLYGON((0 0,0 10,10 10,10 0,0 0))",
"F1FFFF2F2.svg");
// ccw
{
typedef bg::model::polygon<P, false> ccwpoly;
// IE IB0 II
test_geometry<ls, ccwpoly>("LINESTRING(11 1,10 5,5 5)", "POLYGON((0 0,10 0,10 10,0 10,0 0))", "1010F0212");
// IE IB1 II
test_geometry<ls, ccwpoly>("LINESTRING(11 1,10 1,10 5,5 5)", "POLYGON((0 0,10 0,10 10,0 10,0 0))", "1110F0212");
test_geometry<ls, ccwpoly>("LINESTRING(11 1,10 5,10 1,5 5)", "POLYGON((0 0,10 0,10 10,0 10,0 0))", "1110F0212");
// II IB0 IE
test_geometry<ls, ccwpoly>("LINESTRING(5 1,10 5,11 1)", "POLYGON((0 0,10 0,10 10,0 10,0 0))", "1010F0212");
// IE IB1 II
test_geometry<ls, ccwpoly>("LINESTRING(5 5,10 1,10 5,11 5)", "POLYGON((0 0,10 0,10 10,0 10,0 0))", "1110F0212");
test_geometry<ls, ccwpoly>("LINESTRING(5 5,10 5,10 1,11 5)", "POLYGON((0 0,10 0,10 10,0 10,0 0))", "1110F0212");
}
}
template <typename P>
void test_linestring_multi_polygon()
{
typedef bg::model::linestring<P> ls;
typedef bg::model::polygon<P> poly;
typedef bg::model::multi_polygon<poly> mpoly;
test_geometry<ls, mpoly>("LINESTRING(10 1,10 5,10 9)",
"MULTIPOLYGON(((0 20,0 30,10 30,10 20,0 20)),((0 0,0 10,10 10,10 0,0 0),(10 5,2 8,2 2,10 5)))",
"F1FF0F212");
test_geometry<ls, mpoly>("LINESTRING(10 1,10 5,10 9)",
"MULTIPOLYGON(((0 20,0 30,10 30,10 20,0 20)),((0 0,0 10,10 10,10 0,0 0)))",
"F1FF0F212");
test_geometry<ls, mpoly>("LINESTRING(10 1,10 5,2 2)",
"MULTIPOLYGON(((0 20,0 30,10 30,10 20,0 20)),((0 0,0 10,10 10,10 0,0 0),(10 5,2 8,2 2,10 5)))",
"F1FF0F212");
test_geometry<ls, mpoly>("LINESTRING(10 1,10 5,2 2)",
"MULTIPOLYGON(((0 20,0 30,10 30,10 20,0 20)),((0 0,0 10,10 10,10 0,0 0)))",
"11F00F212");
test_geometry<ls, mpoly>("LINESTRING(10 1,10 5,2 2)",
"MULTIPOLYGON(((0 0,0 10,10 10,10 0,0 0),(10 5,2 8,2 2,10 5)),((10 5,3 3,3 7,10 5)))",
"F1FF0F212");
test_geometry<ls, mpoly>("LINESTRING(10 1,10 5,2 8)",
"MULTIPOLYGON(((0 0,0 10,10 10,10 0,0 0),(10 5,2 8,2 2,10 5)),((10 5,3 3,3 7,10 5)))",
"F1FF0F212");
test_geometry<ls, mpoly>("LINESTRING(10 1,10 5,3 3)",
"MULTIPOLYGON(((0 0,0 10,10 10,10 0,0 0),(10 5,2 8,2 2,10 5)),((10 5,3 3,3 7,10 5)))",
"F1FF0F212");
test_geometry<ls, mpoly>("LINESTRING(10 1,10 5,3 7)",
"MULTIPOLYGON(((0 0,0 10,10 10,10 0,0 0),(10 5,2 8,2 2,10 5)),((10 5,3 3,3 7,10 5)))",
"F1FF0F212");
test_geometry<ls, mpoly>("LINESTRING(10 1,10 5,5 5)",
"MULTIPOLYGON(((0 0,0 10,10 10,10 0,0 0),(10 5,2 8,2 2,10 5)),((10 5,3 3,3 7,10 5)))",
"11F00F212");
}
template <typename P>
void test_all()
{
@@ -354,6 +563,8 @@ void test_all()
test_point_multilinestring<P>();
test_linestring_linestring<P>();
test_linestring_multi_linestring<P>();
test_linestring_polygon<P>();
test_linestring_multi_polygon<P>();
}
int test_main( int , char* [] )