[buffer] remove close_to_offset and min_distance, add edge_min_fraction

This commit is contained in:
Barend Gehrels 2022-06-08 16:29:43 +02:00
parent 9f33365496
commit 2462357198
9 changed files with 206 additions and 288 deletions

View File

@ -158,7 +158,6 @@ struct buffer_turn_info
// or (for deflate) if there are not enough points to traverse it.
bool is_turn_traversable;
bool close_to_offset;
bool is_linear_end_point;
bool within_original;
signed_size_type count_in_original; // increased by +1 for in ext.ring, -1 for int.ring
@ -166,7 +165,6 @@ struct buffer_turn_info
inline buffer_turn_info()
: turn_index(0)
, is_turn_traversable(true)
, close_to_offset(false)
, is_linear_end_point(false)
, within_original(false)
, count_in_original(0)

View File

@ -289,65 +289,6 @@ struct buffered_piece_collection
, m_robust_policy(robust_policy)
{}
inline bool is_following(buffer_turn_info_type const& turn,
buffer_turn_operation_type const& op)
{
return turn.operations[0].seg_id.segment_index == op.seg_id.segment_index
|| turn.operations[1].seg_id.segment_index == op.seg_id.segment_index;
}
// Verify if turns which are classified as OK (outside or on border of
// offsetted ring) do not traverse through other turns which are classified
// as WITHIN (inside a piece). This can happen if turns are nearly colocated
// and due to floating point precision just classified as within, while
// they should not be within.
// In those cases the turns are fine to travel through (and should),
// but they are not made startable.
template <typename Vector>
inline void pretraverse(Vector const& indexed_operations)
{
// Verify if the turns which are OK don't skip segments
typedef typename boost::range_value<Vector>::type indexed_type;
buffer_turn_operation_type last_traversable_operation;
buffer_turn_info_type last_traversable_turn;
bool first = true;
for (std::size_t i = 0; i < indexed_operations.size(); i++)
{
indexed_type const & itop = indexed_operations[i];
buffer_turn_info_type const& turn = m_turns[itop.turn_index];
if (turn.is_turn_traversable && ! first)
{
// Check previous and next turns. The first is handled
BOOST_GEOMETRY_ASSERT(i > 0);
indexed_type const& previous_itop = indexed_operations[i - 1];
std::size_t const next_index = i + 1 < indexed_operations.size() ? i + 1 : 0;
indexed_type const& next_itop = indexed_operations[next_index];
buffer_turn_info_type& previous_turn = m_turns[previous_itop.turn_index];
buffer_turn_info_type& next_turn = m_turns[next_itop.turn_index];
if (previous_turn.close_to_offset
&& is_following(previous_turn, last_traversable_operation))
{
previous_turn.is_turn_traversable = true;
}
else if (next_turn.close_to_offset
&& is_following(next_turn, last_traversable_operation))
{
next_turn.is_turn_traversable = true;
}
}
if (turn.is_turn_traversable)
{
first = false;
last_traversable_operation = *itop.subject;
last_traversable_turn = turn;
}
}
}
inline void check_linear_endpoints(buffer_turn_info_type& turn) const
{
// TODO this is quadratic. But the #endpoints, expected, is low,
@ -390,14 +331,6 @@ struct buffered_piece_collection
{
std::sort(mit->second.begin(), mit->second.end(), buffer_less());
}
for (typename mapped_vector_type::iterator mit
= mapped_vector.begin();
mit != mapped_vector.end();
++mit)
{
pretraverse(mit->second);
}
}
inline void deflate_check_turns()

View File

@ -225,9 +225,8 @@ struct piece_border
// Whatever the return value, the state should be checked.
template <typename TurnPoint, typename UmbrellaStrategy, typename State>
template <typename TurnPoint, typename State>
bool point_on_piece(TurnPoint const& point,
UmbrellaStrategy const& umbrella_strategy,
bool one_sided, bool is_linear_end_point,
State& state) const
{
@ -263,20 +262,20 @@ struct piece_border
{
// One point. Walk from last offsetted to point, and from point to first offsetted
continue_processing = step(point, offsetted_back, m_originals[0],
tir, umbrella_strategy, por_from_offsetted, state)
tir, por_from_offsetted, state)
&& step(point, m_originals[0], offsetted_front,
tir, umbrella_strategy, por_to_offsetted, state);
tir, por_to_offsetted, state);
}
else if (m_original_size == 2)
{
// Two original points. Walk from last offsetted point to first original point,
// then along original, then from second oginal to first offsetted point
continue_processing = step(point, offsetted_back, m_originals[0],
tir, umbrella_strategy, por_from_offsetted, state)
tir, por_from_offsetted, state)
&& step(point, m_originals[0], m_originals[1],
tir, umbrella_strategy, por_original, state)
tir, por_original, state)
&& step(point, m_originals[1], offsetted_front,
tir, umbrella_strategy, por_to_offsetted, state);
tir, por_to_offsetted, state);
}
if (continue_processing)
@ -284,7 +283,7 @@ struct piece_border
// Check the offsetted ring (in rounded joins, these might be
// several segments)
walk_offsetted(point, m_ring->begin() + m_begin, m_ring->begin() + m_end,
tir, umbrella_strategy, state);
tir, state);
}
return true;
@ -310,11 +309,11 @@ private :
template
<
typename TurnPoint, typename Iterator,
typename TiRStrategy, typename UmbrellaStrategy,
typename TiRStrategy,
typename State
>
bool walk_offsetted(TurnPoint const& point, Iterator begin, Iterator end,
TiRStrategy const & strategy, UmbrellaStrategy const& umbrella_strategy,
TiRStrategy const & strategy,
State& state) const
{
Iterator it = begin;
@ -338,7 +337,7 @@ private :
for (Iterator previous = it++ ; it != beyond ; ++previous, ++it )
{
if (! step(point, *previous, *it, strategy, umbrella_strategy,
if (! step(point, *previous, *it, strategy,
geometry::strategy::buffer::place_on_ring_offsetted, state))
{
return false;
@ -347,27 +346,12 @@ private :
return true;
}
template <typename TurnPoint, typename TiRStrategy, typename UmbrellaStrategy, typename State>
template <typename TurnPoint, typename TiRStrategy, typename State>
bool step(TurnPoint const& point, Point const& p1, Point const& p2,
TiRStrategy const& strategy, UmbrellaStrategy const& umbrella_strategy,
TiRStrategy const& strategy,
geometry::strategy::buffer::place_on_ring_type place_on_ring, State& state) const
{
// A step between original/offsetted ring is always convex
// (unless the join strategy generates points left of it -
// future: convexity might be added to the buffer-join-strategy)
// Therefore, if the state count > 0, it means the point is left of it,
// and because it is convex, we can stop
auto const dm = geometry::detail::get_distance_measure(point, p1, p2, umbrella_strategy);
if (m_is_convex && dm.measure > 0)
{
// The point is left of this segment of a convex piece
state.m_count = 0;
return false;
}
// Call strategy, and if it is on the border, return false
// to stop further processing.
return strategy.apply(point, p1, p2, dm, place_on_ring, state);
return strategy.apply(point, p1, p2, place_on_ring, m_is_convex, state, get_full_ring());
}
template <typename It, typename Box, typename Strategy>

View File

@ -102,7 +102,7 @@ public:
: m_turns(turns)
, m_pieces(pieces)
, m_distance_strategy(distance_strategy)
, m_umbrella_strategy(umbrella_strategy)
, m_umbrella_strategy(umbrella_strategy)
{}
template <typename Turn, typename Piece>
@ -164,44 +164,20 @@ public:
}
// Check if buffer is one-sided (at this point), because then a point
// on the original is not considered as within.
// on the original border is not considered as within.
bool const one_sided = has_zero_distance_at(turn.point);
typename Border::state_type state;
if (! border.point_on_piece(turn.point, m_umbrella_strategy, one_sided,
if (! border.point_on_piece(turn.point, one_sided,
turn.is_linear_end_point, state))
{
return true;
}
if (state.code() == 1)
if (state.is_inside() && ! state.is_on_boundary())
{
// It is WITHIN a piece, or on the piece border, but not
// on the offsetted part of it.
// TODO - at further removing rescaling, this threshold can be
// adapted, or ideally, go.
// This threshold is minimized to the point where fragile
// unit tests of hard cases start to fail (5 in multi_polygon)
// But it is acknowlegded that such a threshold depends on the
// scale of the input.
#if defined(BOOST_GEOMETRY_USE_RESCALING)
if (state.m_min_distance > 1.0e-4 || ! state.m_close_to_offset)
#else
constexpr double zero = 0;
if (math::larger(state.m_min_distance, zero) || ! state.m_close_to_offset)
#endif
{
Turn& mutable_turn = m_turns[turn.turn_index];
mutable_turn.is_turn_traversable = false;
// Keep track of the information if this turn was close
// to an offset (without threshold). Because if it was,
// it might have been classified incorrectly and in the
// pretraversal step, it can in hindsight be classified
// as "outside".
mutable_turn.close_to_offset = state.m_close_to_offset;
}
Turn& mutable_turn = m_turns[turn.turn_index];
mutable_turn.is_turn_traversable = false;
}
return true;

View File

@ -14,6 +14,8 @@
#include <boost/geometry/algorithms/detail/make/make.hpp>
#include <boost/geometry/util/math.hpp>
#include <boost/geometry/strategies/cartesian/side_rounded_input.hpp>
namespace boost { namespace geometry
{
@ -60,34 +62,41 @@ public:
struct counter
{
inline counter()
: m_count(0)
, m_min_distance(0)
, m_close_to_offset(false)
{}
//! Returns -1 for outside, 1 for inside
inline int code() const
{
return m_count == 0 ? -1 : 1;
}
//! Counter, is increased if point is left of a segment (outside),
//! and decreased if point is right of a segment (inside)
int m_count;
int count{0};
int count_on_offsetted{0};
int count_on_origin{0};
int count_on_edge{0};
CalculationType edge_min_fraction{(std::numeric_limits<CalculationType>::max)()};
#if defined(BOOST_GEOMETRY_USE_RESCALING)
CalculationType inside_min_measure{(std::numeric_limits<CalculationType>::max)()};
#endif
inline bool is_inside() const
{
return count < 0 || count_on_origin > 0;
}
inline bool is_on_boundary() const
{
return count_on_origin == 0
&& (count_on_offsetted > 0
|| (count_on_edge > 0 && edge_min_fraction < 1.0e-3)
#if defined(BOOST_GEOMETRY_USE_RESCALING)
|| (count < 0 && inside_min_measure < 1.0e-5)
#endif
);
}
//! Indicate an indication of distance. It is always set, unless
//! the point is located on the border-part of the original.
//! It is not guaranteed to be the minimum distance, because it is only
//! calculated for a selection of the offsetted ring.
CalculationType m_min_distance;
bool m_close_to_offset;
};
typedef counter state_type;
using state_type = counter;
template <typename Point, typename PointOfSegment>
static inline bool in_vertical_range(Point const& point,
static inline bool is_in_vertical_range(Point const& point,
PointOfSegment const& s1,
PointOfSegment const& s2)
{
@ -95,108 +104,111 @@ public:
CalculationType const s1y = get<1>(s1);
CalculationType const s2y = get<1>(s2);
return (s1y >= py && s2y <= py)
|| (s2y >= py && s1y <= py);
return s1y < s2y ? (py >= s1y && py <= s2y) : (py >= s2y && py <= s1y);
}
template <typename Dm, typename Point, typename PointOfSegment>
template <typename Point, typename PointOfSegment, typename Ring>
static inline void apply_on_boundary(Point const& point,
PointOfSegment const& s1,
PointOfSegment const& s2,
place_on_ring_type place_on_ring,
counter& the_state)
counter& the_state,
Ring const& full_ring)
{
if (place_on_ring == place_on_ring_offsetted)
{
// Consider the point as "outside"
the_state.m_count = 0;
the_state.m_close_to_offset = true;
the_state.m_min_distance = 0;
the_state.count_on_offsetted++;
}
else if (place_on_ring == place_on_ring_to_offsetted
|| place_on_ring == place_on_ring_from_offsetted)
{
// Check distance from "point" to either s1 or s2
// on a line perpendicular to s1-s2
typedef model::infinite_line<CalculationType> line_type;
the_state.count_on_edge++;
line_type const line
= detail::make::make_perpendicular_line<CalculationType>(s1, s2,
place_on_ring == place_on_ring_to_offsetted ? s2 : s1);
auto const line1 = detail::make::make_perpendicular_line<CalculationType>(s1, s2, s1);
auto const line2 = detail::make::make_perpendicular_line<CalculationType>(s2, s1, s2);
Dm perp;
perp.measure = arithmetic::side_value(line, point);
// If it is to the utmost point s1 or s2, it is "outside"
the_state.m_count = perp.is_zero() ? 0 : 1;
the_state.m_close_to_offset = true;
the_state.m_min_distance = geometry::math::abs(perp.measure);
auto const value1 = arithmetic::side_value(line1, point);
auto const value2 = arithmetic::side_value(line2, point);
if (value1 >= 0 && value2 >= 0)
{
auto const length_value = value1 + value2;
if (length_value > 0)
{
// If it is to the utmost point s1 or s2, it is "outside"
auto const fraction = (place_on_ring == place_on_ring_to_offsetted ? value2 : value1) / length_value;
if (fraction < the_state.edge_min_fraction)
{
the_state.edge_min_fraction = fraction;
}
}
}
}
else
{
// It is on the border, the part of the original
// Consider it as "inside".
the_state.m_count = 1;
the_state.count_on_origin++;
}
}
template <typename Dm, typename Point, typename PointOfSegment>
template <typename Point, typename PointOfSegment, typename Ring>
static inline bool apply(Point const& point,
PointOfSegment const& s1,
PointOfSegment const& s2,
Dm const& dm,
place_on_ring_type place_on_ring,
counter& the_state)
bool is_convex,
counter& the_state, Ring const& full_ring)
{
int const side = strategy::side::side_rounded_input<CalculationType>::apply(s1, s2, point);
if (is_convex && side > 0)
{
// If the point is left of this segment of a convex piece, it can never be inside.
// Stop further processing
the_state.count = 1;
return false;
}
CalculationType const px = get<0>(point);
CalculationType const s1x = get<0>(s1);
CalculationType const s2x = get<0>(s2);
bool const in_horizontal_range
= (s1x >= px && s2x <= px)
|| (s2x >= px && s1x <= px);
bool const in_horizontal_range = s1x < s2x ? (px >= s1x && px <= s2x) : (px >= s2x && px <= s1x);
bool const vertical = s1x == s2x;
bool const measured_on_boundary = dm.is_zero();
if (measured_on_boundary
&& (in_horizontal_range
|| (vertical && in_vertical_range(point, s1, s2))))
if (in_horizontal_range || (vertical && is_in_vertical_range(point, s1, s2)))
{
apply_on_boundary<Dm>(point, s1, s2, place_on_ring, the_state);
// Indicate that no further processing is necessary.
return false;
}
if (side == 0)
{
apply_on_boundary(point, s1, s2, place_on_ring, the_state, full_ring);
}
#if defined(BOOST_GEOMETRY_USE_RESCALING)
else if (side == -1)
{
auto const line = detail::make::make_infinite_line<CalculationType>(s1, s2);
auto const value = -arithmetic::side_value(line, point);
if (value > 0 && value < the_state.inside_min_measure) { the_state.inside_min_measure = value; }
bool const is_on_right_side = dm.is_negative();
if (place_on_ring == place_on_ring_offsetted
&& is_on_right_side
&& (! the_state.m_close_to_offset
|| -dm.measure < the_state.m_min_distance))
{
// This part of the ring was the offsetted part,
// keep track of the distance WITHIN the ring
// with respect to the offsetted part
// NOTE: this is also done if it is NOT in the horizontal range.
the_state.m_min_distance = -dm.measure;
the_state.m_close_to_offset = true;
}
#endif
}
if (in_horizontal_range)
{
// Use only absolute comparisons, because the ring is continuous -
// what was missed is there earlier or later, and turns should
// not be counted twice (which can happen if an epsilon is used).
bool const eq1 = s1x == px;
bool const eq2 = s2x == px;
auto const on_boundary = the_state.count_on_offsetted + the_state.count_on_edge + the_state.count_on_origin;
if (on_boundary == 0)
{
// Use only absolute comparisons, because the ring is continuous -
// what was missed is there earlier or later, and turns should
// not be counted twice (which can happen if an epsilon is used).
bool const eq1 = s1x == px;
bool const eq2 = s2x == px;
// Account for 1 or 2 for left side (outside)
// and for -1 or -2 for right side (inside)
int const side = is_on_right_side ? -1 : 1;
int const multiplier = eq1 || eq2 ? 1 : 2;
// Account for 1 or 2 for left side (outside)
// and for -1 or -2 for right side (inside)
int const multiplier = eq1 || eq2 ? 1 : 2;
the_state.m_count += side * multiplier;
the_state.count += side * multiplier;
}
}
return true;

View File

@ -205,10 +205,8 @@ void test_all()
test_one<linestring, polygon>("curve", curve, join_round, end_flat, 58.1944, 5.0, settings, 3.0);
test_one<linestring, polygon>("curve", curve, join_miter, end_flat, 58.7371, 5.0, settings, 3.0);
#if defined(BOOST_GEOMETRY_USE_RESCALING)
test_one<linestring, polygon>("tripod", tripod, join_miter, end_flat, 74.25, 3.0);
test_one<linestring, polygon>("tripod", tripod, join_miter, end_round, 116.6336, 3.0);
#endif
test_one<linestring, polygon>("chained2", chained2, join_round, end_flat, 11.3137, 2.5, settings, 1.5);
test_one<linestring, polygon>("chained3", chained3, join_round, end_flat, 16.9706, 2.5, settings, 1.5);
@ -313,7 +311,6 @@ void test_all()
#if ! defined(BOOST_GEOMETRY_USE_RESCALING)
{
// Cases sensible to the value of state.m_min_distance and "close_to_offset"
using bg::strategy::buffer::join_round;
using bg::strategy::buffer::end_round;
test_one<linestring, polygon>("issue_988_a", issue_988, join_round(8), end_round(8), 0.0029235, 0.010068);
@ -337,19 +334,22 @@ void test_all()
test_one<linestring, polygon>("mysql_23023665_1", mysql_23023665, join_round32, end_flat, 459.1051, 10);
test_one<linestring, polygon>("mysql_23023665_2", mysql_23023665, join_round32, end_flat, 6877.7097, 50);
#if ! defined(BOOST_GEOMETRY_USE_RESCALING) || defined(BOOST_GEOMETRY_TEST_FAILURES)
test_one<linestring, polygon>("mysql_25662426", mysql_25662426, join_round32, end_round32, 1, 0, 1660.6673, 10);
#endif
// Test behaviour with different buffer sizes, generating internally turns on different locations
test_one<linestring, polygon>("mysql_25662426a_05", mysql_25662426a, join_round32, end_round32, 27.6156, 0.5);
#if defined(BOOST_GEOMETRY_USE_RESCALING) || defined(BOOST_GEOMETRY_TEST_FAILURES)
// Fails without rescaling
test_one<linestring, polygon>("mysql_25662426a_1", mysql_25662426a, join_round32, end_round32, 54.9018, 1.0);
#endif
test_one<linestring, polygon>("mysql_25662426a_2", mysql_25662426a, join_round32, end_round32, 103.6072, 2.0);
#if ! defined(BOOST_GEOMETRY_USE_RESCALING) || defined(BOOST_GEOMETRY_TEST_FAILURES)
test_one<linestring, polygon>("mysql_25662426a_3", mysql_25662426a, join_round32, end_round32, 152.1163, 3.0);
#endif
test_one<linestring, polygon>("mysql_25662426a_4", mysql_25662426a, join_round32, end_round32, 206.4831, 4.0);
test_one<linestring, polygon>("mysql_25662426a_5", mysql_25662426a, join_round32, end_round32, 266.8505, 5.0);
#if ! defined(BOOST_GEOMETRY_USE_RESCALING) || defined(BOOST_GEOMETRY_TEST_FAILURES)
test_one<linestring, polygon>("mysql_25662426a_10", mysql_25662426a, join_round32, end_round32, 660.7355, 10.0);
#endif
test_one<linestring, polygon>("mysql_25662426a_05", mysql_25662426a, join_round32, end_flat, 26.8352, 0.5);
test_one<linestring, polygon>("mysql_25662426a_1", mysql_25662426a, join_round32, end_flat, 53.3411, 1.0);
@ -371,10 +371,7 @@ void test_all()
// Mostly right
test_one<linestring, polygon>("mysql_25662426a_mostly_right_05", mysql_25662426a, join_round32, end_round32, 14.3419, 0.05, settings, 0.5);
#if defined(BOOST_GEOMETRY_USE_RESCALING) || defined(BOOST_GEOMETRY_TEST_FAILURES)
// Fails without rescaling
test_one<linestring, polygon>("mysql_25662426a_mostly_right_1", mysql_25662426a, join_round32, end_round32, 27.1955, 0.1, settings, 1.0);
#endif
test_one<linestring, polygon>("mysql_25662426a_mostly_right_2", mysql_25662426a, join_round32, end_round32, 43.1821, 0.2, settings, 2.0);
test_one<linestring, polygon>("mysql_25662426a_mostly_right_3", mysql_25662426a, join_round32, end_round32, 54.4337, 0.3, settings, 3.0);
test_one<linestring, polygon>("mysql_25662426a_mostly_right_4", mysql_25662426a, join_round32, end_round32, 75.6376, 0.4, settings, 4.0);
@ -386,7 +383,9 @@ void test_all()
test_one<linestring, polygon>("mysql_25662426a_left_1", mysql_25662426a, join_round32, end_round32, 30.1214, 1.0, settings, 0.0);
test_one<linestring, polygon>("mysql_25662426a_left_2", mysql_25662426a, join_round32, end_round32, 66.4858, 2.0, ut_settings(0.01, false), 0.0); // It has a self touching point
test_one<linestring, polygon>("mysql_25662426a_left_3", mysql_25662426a, join_round32, end_round32, 108.3305, 3.0, settings, 0.0);
//#if ! defined(BOOST_GEOMETRY_USE_RESCALING) || defined(BOOST_GEOMETRY_TEST_FAILURES)
test_one<linestring, polygon>("mysql_25662426a_left_4", mysql_25662426a, join_round32, end_round32, 155.5128, 4.0, specific_settings, 0.0);
//#endif
test_one<linestring, polygon>("mysql_25662426a_left_5", mysql_25662426a, join_round32, end_round32, 208.1289, 5.0, specific_settings, 0.0);
test_one<linestring, polygon>("mysql_25662426a_left_10", mysql_25662426a, join_round32, end_round32, 554.8818, 10.0, specific_settings, 0.0);

View File

@ -386,6 +386,9 @@ static std::string const nores_495d
static std::string const nores_e402
= "MULTIPOLYGON(((3 1,4 2,4 1,3 1)),((3 1,4 0,3 0,3 1)))";
static std::string const nores_b03e
= "MULTIPOLYGON(((3 1,3 2,4 2,4 1,3 1)),((5 0,6 1,6 0,5 0)))";
// rescaled
static std::string const res_ebc4
= "MULTIPOLYGON(((3 9,3 10,4 9,3 9)),((9 5,9 6,10 6,10 5,9 5)),((8 8,8 9,9 9,8 8)),((4 8,3 7,3 8,4 8)),((4 8,5 9,6 9,6 8,4 8)),((4 5,3 4,3 5,4 5)),((4 5,5 6,5 5,4 5)))";
@ -575,6 +578,8 @@ void test_all()
test_one<multi_polygon_type, polygon_type>("rt_u8", rt_u8, join_miter, end_flat, 70.9142, 1.0);
test_one<multi_polygon_type, polygon_type>("rt_u9", rt_u9, join_miter, end_flat, 59.3063, 1.0);
#if ! defined(BOOST_GEOMETRY_USE_RESCALING) || defined(BOOST_GEOMETRY_TEST_FAILURES)
// Fails with rescaling after removing pretraversal
test_one<multi_polygon_type, polygon_type>("rt_u10", rt_u10, join_miter, end_flat, 144.0858, 1.0);
test_one<multi_polygon_type, polygon_type>("rt_u10_51", rt_u10, join_miter, end_flat, 0.16738, -0.51);
test_one<multi_polygon_type, polygon_type>("rt_u10_c_51", rt_u10_c, join_miter, end_flat, 0.066952, -0.51);
@ -582,6 +587,7 @@ void test_all()
// TODO: invalid - making a bow-tie
test_one<multi_polygon_type, polygon_type>("rt_u10_50", rt_u10, join_miter, end_flat, 0.214466, -0.50, ut_settings::ignore_validity());
test_one<multi_polygon_type, polygon_type>("rt_u10_45", rt_u10, join_miter, end_flat, 1.3000, -0.45);
#endif
test_one<multi_polygon_type, polygon_type>("rt_u10_25", rt_u10, join_miter, end_flat, 9.6682, -0.25);
test_one<multi_polygon_type, polygon_type>("rt_u11", rt_u11, join_miter, end_flat, 131.3995, 1.0);
@ -635,13 +641,14 @@ void test_all()
test_one<multi_polygon_type, polygon_type>("nores_5318", nores_5318, join_round32, end_flat, 22.7311, 1.0);
test_one<multi_polygon_type, polygon_type>("nores_495d", nores_495d, join_round32, end_flat, 23.4376, 1.0);
test_one<multi_polygon_type, polygon_type>("nores_e402", nores_e402, join_round32, end_flat, {9.9888, 9.9898}, 1.0);
test_one<multi_polygon_type, polygon_type>("nores_b03e", nores_b03e, join_round32, end_flat, 14.4877, 1.0);
#if ! defined(BOOST_GEOMETRY_USE_RESCALING) || defined(BOOST_GEOMETRY_TEST_FAILURES)
// Erroneous cases with rescaling (out of 8)
test_one<multi_polygon_type, polygon_type>("res_ebc4", res_ebc4, join_round32, end_flat, 43.8877, 1.0);
#if ! defined(BOOST_GEOMETRY_USE_RESCALING) || defined(BOOST_GEOMETRY_TEST_FAILURES)
// Erroneous case with rescaling
test_one<multi_polygon_type, polygon_type>("res_8618", res_8618, join_round32, end_flat, 48.1085, 1.0);
#endif
test_one<multi_polygon_type, polygon_type>("res_3b4d", res_3b4d, join_round32, end_flat, 48.4739, 1.0);
#endif
test_one<multi_polygon_type, polygon_type>("neighbouring_small",
neighbouring,

View File

@ -152,27 +152,29 @@ void test_rectangle_properties()
}
template <typename Point, typename Border, typename Mapper>
void test_point(std::string const& wkt, bool expected, Border const& border,
void test_point(std::string const& wkt, bool expected_outside,
bool expected_on_offsetted, bool expected_on_edge, bool expected_on_origin,
Border const& border,
Mapper& mapper, std::string const& color)
{
bg::strategies::buffer::cartesian<> strategy;
typename Border::state_type state;
Point point;
bg::read_wkt(wkt, point);
border.point_on_piece(point, strategy, false, false, state);
bool const on_piece = state.code() == 1;
BOOST_CHECK_MESSAGE(on_piece == expected,
" expected: " << expected
<< " detected: " << on_piece
<< " wkt: " << wkt);
border.point_on_piece(point, false, false, state);
BOOST_CHECK(expected_outside == state.count > 0);
BOOST_CHECK(expected_on_offsetted == state.count_on_offsetted > 0);
BOOST_CHECK(expected_on_edge == state.count_on_edge > 0);
BOOST_CHECK(expected_on_origin == state.count_on_origin > 0);
#ifdef TEST_WITH_SVG
std::string style = "fill:" + color + ";stroke:rgb(0,0,0);stroke-width:1";
mapper.map(point, style);
// Mark on-piece as T or F
std::ostringstream out;
out << (on_piece ? "T" : "F");
out << (state.is_inside() ? "I" : "")
<< (state.is_on_boundary() ? "B" : "")
<< " " << state.count
<< " " << state.count_on_offsetted << ":" << state.count_on_edge << ":" << state.count_on_origin;
mapper.text(point, out.str(), "fill:rgb(0,0,0);font-family='Arial';font-size:9px;", 10, -10);
#else
boost::ignore_unused(mapper, color);
@ -219,27 +221,27 @@ void test_rectangle_point_on_piece_a()
#endif
// Points inside
test_point<Point>("POINT(1.5 2.5)", true, border, mapper, "red");
test_point<Point>("POINT(1.5 2.5)", false, false, false, false, border, mapper, "red");
// Points outside
test_point<Point>("POINT(0.5 2.5)", false, border, mapper, "green");
test_point<Point>("POINT(2.5 2.5)", false, border, mapper, "green");
test_point<Point>("POINT(1.5 1.5)", false, border, mapper, "green");
test_point<Point>("POINT(1.5 3.5)", false, border, mapper, "green");
test_point<Point>("POINT(0.5 2.5)", true, false, false, false, border, mapper, "green");
test_point<Point>("POINT(2.5 2.5)", true, false, false, false, border, mapper, "green");
test_point<Point>("POINT(1.5 1.5)", true, false, false, false, border, mapper, "green");
test_point<Point>("POINT(1.5 3.5)", true, false, false, false, border, mapper, "green");
// Points on the original (should be INSIDE)
test_point<Point>("POINT(1.0 2.0)", true, border, mapper, "orange");
test_point<Point>("POINT(1.5 2.0)", true, border, mapper, "orange");
test_point<Point>("POINT(2.0 2.0)", true, border, mapper, "orange");
test_point<Point>("POINT(1.0 2.0)", false, false, true, true, border, mapper, "orange");
test_point<Point>("POINT(1.5 2.0)", false, false, false, true, border, mapper, "orange");
test_point<Point>("POINT(2.0 2.0)", false, false, true, true, border, mapper, "orange");
// Points on the offsetted ring (should be OUTSIDE)
test_point<Point>("POINT(1.0 3.0)", false, border, mapper, "blue");
test_point<Point>("POINT(1.5 3.0)", false, border, mapper, "blue");
test_point<Point>("POINT(2.0 3.0)", false, border, mapper, "blue");
test_point<Point>("POINT(1.0 3.0)", false, true, true, false, border, mapper, "blue");
test_point<Point>("POINT(1.5 3.0)", false, true, false, false, border, mapper, "blue");
test_point<Point>("POINT(2.0 3.0)", false, true, true, false, border, mapper, "blue");
// Points on between original and offsetted ring (should be INSIDE)
test_point<Point>("POINT(1.0 2.5)", true, border, mapper, "cyan");
test_point<Point>("POINT(2.0 2.5)", true, border, mapper, "cyan");
// Points on the edge, that is between original and offsetted ring (should be INSIDE)
test_point<Point>("POINT(1.0 2.5)", false, false, true, false, border, mapper, "cyan");
test_point<Point>("POINT(2.0 2.5)", false, false, true, false, border, mapper, "cyan");
}
template <typename Point>
@ -263,27 +265,27 @@ void test_rectangle_point_on_piece_c()
// Piece labeled as 'c' : from (2,1) to (3,2)
// Points inside
test_point<Point>("POINT(2.5 1.5)", true, border, mapper, "red");
test_point<Point>("POINT(2.5 1.5)", false, false, false, false, border, mapper, "red");
// Points outside
test_point<Point>("POINT(1.5 1.5)", false, border, mapper, "green");
test_point<Point>("POINT(3.5 1.5)", false, border, mapper, "green");
test_point<Point>("POINT(2.5 0.5)", false, border, mapper, "green");
test_point<Point>("POINT(2.5 2.5)", false, border, mapper, "green");
test_point<Point>("POINT(1.5 1.5)", true, false, false, false, border, mapper, "green");
test_point<Point>("POINT(3.5 1.5)", true, false, false, false, border, mapper, "green");
test_point<Point>("POINT(2.5 0.5)", true, false, false, false, border, mapper, "green");
test_point<Point>("POINT(2.5 2.5)", true, false, false, false, border, mapper, "green");
// Points on the original (should be INSIDE)
test_point<Point>("POINT(2.0 1.0)", true, border, mapper, "orange");
test_point<Point>("POINT(2.0 1.5)", true, border, mapper, "orange");
test_point<Point>("POINT(2.0 2.0)", true, border, mapper, "orange");
test_point<Point>("POINT(2.0 1.0)", false, false, true, true, border, mapper, "orange");
test_point<Point>("POINT(2.0 1.5)", false, false, false, true, border, mapper, "orange");
test_point<Point>("POINT(2.0 2.0)", false, false, true, true, border, mapper, "orange");
// Points on the offsetted ring (should be OUTSIDE)
test_point<Point>("POINT(3.0 1.0)", false, border, mapper, "blue");
test_point<Point>("POINT(3.0 1.5)", false, border, mapper, "blue");
test_point<Point>("POINT(3.0 2.0)", false, border, mapper, "blue");
test_point<Point>("POINT(3.0 1.0)", false, true, true, false, border, mapper, "blue");
test_point<Point>("POINT(3.0 1.5)", false, true, false, false, border, mapper, "blue");
test_point<Point>("POINT(3.0 2.0)", false, true, true, false, border, mapper, "blue");
// Points on between original and offsetted ring (should be INSIDE)
test_point<Point>("POINT(2.5 2.0)", true, border, mapper, "cyan");
test_point<Point>("POINT(2.5 1.0)", true, border, mapper, "cyan");
test_point<Point>("POINT(2.5 2.0)", false, false, true, false, border, mapper, "cyan");
test_point<Point>("POINT(2.5 1.0)", false, false, true, false, border, mapper, "cyan");
}
template <typename Point>
@ -305,27 +307,27 @@ void test_diamond_point_on_piece_a()
#endif
// Points inside
test_point<Point>("POINT(2 4)", true, border, mapper, "red");
test_point<Point>("POINT(2 4)", false, false, false, false, border, mapper, "red");
// Points outside
test_point<Point>("POINT(1 3)", false, border, mapper, "green");
test_point<Point>("POINT(1 5)", false, border, mapper, "green");
test_point<Point>("POINT(3 3)", false, border, mapper, "green");
test_point<Point>("POINT(3 5)", false, border, mapper, "green");
test_point<Point>("POINT(1 3)", true, false, false, false, border, mapper, "green");
test_point<Point>("POINT(1 5)", true, false, false, false, border, mapper, "green");
test_point<Point>("POINT(3 3)", true, false, false, false, border, mapper, "green");
test_point<Point>("POINT(3 5)", true, false, false, false, border, mapper, "green");
// Points on the original (should be INSIDE)
test_point<Point>("POINT(2 3)", true, border, mapper, "orange");
test_point<Point>("POINT(2.5 3.5)", true, border, mapper, "orange");
test_point<Point>("POINT(3 4)", true, border, mapper, "orange");
test_point<Point>("POINT(2 3)", false, false, true, true, border, mapper, "orange");
test_point<Point>("POINT(2.5 3.5)", false, false, false, true, border, mapper, "orange");
test_point<Point>("POINT(3 4)", false, false, true, true, border, mapper, "orange");
// Points on the offsetted ring (should be OUTSIDE)
test_point<Point>("POINT(1 4)", false, border, mapper, "blue");
test_point<Point>("POINT(1.5 4.5)", false, border, mapper, "blue");
test_point<Point>("POINT(2 5)", false, border, mapper, "blue");
test_point<Point>("POINT(1 4)", false, true, true, false, border, mapper, "blue");
test_point<Point>("POINT(1.5 4.5)", false, true, false, false, border, mapper, "blue");
test_point<Point>("POINT(2 5)", false, true, true, false, border, mapper, "blue");
// Points on between original and offsetted ring (should be INSIDE)
test_point<Point>("POINT(1.5 3.5)", true, border, mapper, "cyan");
test_point<Point>("POINT(2.5 4.5)", true, border, mapper, "cyan");
test_point<Point>("POINT(1.5 3.5)", false, false, true, false, border, mapper, "cyan");
test_point<Point>("POINT(2.5 4.5)", false, false, true, false, border, mapper, "cyan");
}
template <typename Point>
@ -347,27 +349,27 @@ void test_diamond_point_on_piece_c()
#endif
// Points inside
test_point<Point>("POINT(4 4)", true, border, mapper, "red");
test_point<Point>("POINT(4 4)", false, false, false, false, border, mapper, "red");
// Points outside
test_point<Point>("POINT(3 3)", false, border, mapper, "green");
test_point<Point>("POINT(3 5)", false, border, mapper, "green");
test_point<Point>("POINT(5 3)", false, border, mapper, "green");
test_point<Point>("POINT(5 5)", false, border, mapper, "green");
test_point<Point>("POINT(3 3)", true, false, false, false, border, mapper, "green");
test_point<Point>("POINT(3 5)", true, false, false, false, border, mapper, "green");
test_point<Point>("POINT(5 3)", true, false, false, false, border, mapper, "green");
test_point<Point>("POINT(5 5)", true, false, false, false, border, mapper, "green");
// Points on the original (should be INSIDE)
test_point<Point>("POINT(3 4)", true, border, mapper, "orange");
test_point<Point>("POINT(3.5 3.5)", true, border, mapper, "orange");
test_point<Point>("POINT(4 3)", true, border, mapper, "orange");
test_point<Point>("POINT(3 4)", false, false, true, true, border, mapper, "orange");
test_point<Point>("POINT(3.5 3.5)", false, false, false, true, border, mapper, "orange");
test_point<Point>("POINT(4 3)", false, false, true, true, border, mapper, "orange");
// Points on the offsetted ring (should be OUTSIDE)
test_point<Point>("POINT(4 5)", false, border, mapper, "blue");
test_point<Point>("POINT(4.5 4.5)", false, border, mapper, "blue");
test_point<Point>("POINT(5 4)", false, border, mapper, "blue");
test_point<Point>("POINT(4 5)", false, true, true, false, border, mapper, "blue");
test_point<Point>("POINT(4.5 4.5)", false, true, false, false, border, mapper, "blue");
test_point<Point>("POINT(5 4)", false, true, true, false, border, mapper, "blue");
// Points on between original and offsetted ring (should be INSIDE)
test_point<Point>("POINT(3.5 4.5)", true, border, mapper, "cyan");
test_point<Point>("POINT(4.5 3.5)", true, border, mapper, "cyan");
test_point<Point>("POINT(3.5 4.5)", false, false, true, false, border, mapper, "cyan");
test_point<Point>("POINT(4.5 3.5)", false, false, true, false, border, mapper, "cyan");
}
int test_main(int, char* [])

View File

@ -110,7 +110,6 @@ static std::string const parcel3_bend // of parcel_3 - clipped
static std::string const italy_part1
= "POLYGON ((1660000 5190000 , 1651702.9375 5167014.5, 1650311.34375 5167763.53125, 1643318.59375 5172188.15625, 1642488.03125 5172636.75, 1640818.25 5173802.75, 1640107.03125 5174511.21875, 1638931.9375 5176054.03125, 1638684.5625 5177095.75, 1660000 5190000))";
// Did have a self-intersection without pretraverse
static std::string const italy_part2
= "POLYGON ((1792367.255086994031444 4724456.568442338146269,1909252.60910044144839 4777690.547005645930767,1817506.869651311077178 4621340.239452145062387,1740727.367822392610833 4604255.192098897881806,1783739.994550514500588 4682197.288425728678703,1786152.065277023706585 4682158.049781472422183,1788625.584362450055778 4681761.819279043003917,1790914.090454180026427 4681245.758200471289456,1792150.627357912017033 4680453.383482984267175,1792707.3361313669011 4680334.832171658985317,1795490.546040181070566 4681008.495863274671137,1800252.571217337390408 4684615.251035280525684,1800994.404303982388228 4685209.840326445177197,1801272.925669946707785 4685567.095846899785101,1802819.153397066518664 4688660.170577370561659,1804117.69525716942735 4692190.08131886087358,1804426.829483100911602 4693301.616071742959321,1805323.730620422400534 4697309.691134516149759,1805601.80670842458494 4699214.415976728312671,1805725.371343206381425 4702868.090166873298585,1805880.439393880078569 4708906.485807630233467,1805725.371343206381425 4710575.432937441393733,1805509.300211574882269 4710973.931955102831125,1805200.165985643398017 4711211.894124387763441,1802726.090302763739601 4711926.958936070092022,1799170.100488863186911 4713835.571500253863633,1798026.070081980666146 4714671.060250979848206,1792367.255086994031444 4724456.568442338146269))";
@ -150,6 +149,9 @@ static std::string const issue_369
static std::string const issue_555
= "POLYGON((100.0637755102041 100.0770239202989,100.0360264929713 100.1,100 100.1,0 100.1,-0.0999999999999943 100.1,-0.09999999999999432 100,-0.1000000000000014 0,-0.1000000000000014 -0.1000000000000014,-1.836970198721056e-17 -0.1000000000000014,100 -0.1000000000000014,100.0360264929713 -0.1000000000000012,100.0637755102041 -0.07702392029888726,101.1041649480706 0.7844145387331185,103.4026139046043 2.203948968996805,105.8748306243106 3.293026604107826,108.4735936784235 4.030845140790255,111.1492644962378 4.403311621418428,113.8507355037622 4.403311621418428,116.5264063215765 4.030845140790252,119.1251693756894 3.293026604107823,121.5973860953957 2.203948968996801,123.8958350519294 0.7844145387331167,124.9362244897959 -0.07702392029888117,124.9639735070287 -0.09999999999999432,125 -0.09999999999999432,135 -0.1000000000000014,135.1 -0.0999999999999943,135.1 0,135.1 100,135.1 100.1,135 100.1,125 100.1,124.9639735070287 100.1,124.9362244897959 100.0770239202989,123.8958350519294 99.21558546126688,121.5973860953958 97.7960510310032,119.1251693756894 96.70697339589218,116.5264063215765 95.96915485920975,113.8507355037622 95.59668837858158,111.1492644962378 95.59668837858158,108.4735936784235 95.96915485920975,105.8748306243106 96.70697339589218,103.4026139046043 97.7960510310032,101.1041649480706 99.21558546126688,100.0637755102041 100.0770239202989),(124.9 71.15855631858324,124.9 28.84144368141676,124.9 27.45166011926875,124.5057975806765 24.70018817664119,123.7253617444602 22.03243382063001,122.574469398775 19.50232706258395,121.0763864178284 17.16101529763987,119.2613973111302 15.0558293341122,117.1661930067267 13.22932657712651,114.8331291254269 11.71843070902665,112.3093697403817 10.55368525835367,109.6459339313539 9.758636146879443,106.8966644080684 9.349355696820595,104.1171390524456 9.33411772066918,101.3635473834475 9.7132302618644,100.0275510204082 10.0961298147011,100.014047339233 10.09999999999999,100 10.09999999999999,29.6 10.1,28.20533247460344 10.1,25.44438883273841 10.49696376664363,22.76804145044581 11.28281026239098,20.2307730424806 12.44154191878281,17.88423507675108 13.94957030080114,15.77619629938279 15.77619629938281,13.94957030080113 17.88423507675109,12.44154191878281 20.23077304248061,11.28281026239098 22.76804145044582,10.49696376664362 25.44438883273843,10.1 28.20533247460346,10.1 29.6,10.1 70.40000000000001,10.1 71.79466752539656,10.49696376664363 74.5556111672616,11.28281026239098 77.23195854955421,12.44154191878281 79.76922695751941,13.94957030080114 82.11576492324893,15.77619629938281 84.22380370061721,17.88423507675109 86.05042969919887,20.23077304248062 87.5584580812172,22.76804145044583 88.71718973760903,25.44438883273843 89.50303623335638,28.20533247460346 89.90000000000001,29.6 89.90000000000001,100 89.90000000000001,100.014047339233 89.90000000000001,100.0275510204082 89.9038701852989,101.3635473834475 90.2867697381356,104.1171390524456 90.66588227933082,106.8966644080684 90.6506443031794,109.6459339313539 90.24136385312056,112.3093697403817 89.44631474164633,114.8331291254269 88.28156929097335,117.1661930067267 86.77067342287347,119.2613973111302 84.94417066588778,121.0763864178284 82.83898470236012,122.574469398775 80.49767293741604,123.7253617444602 77.96756617936998,124.5057975806765 75.29981182335879,124.9 72.54833988073125,124.9 71.15855631858324))";
static std::string const issue_1019
= "POLYGON((577255 928582,577255 928582,577228 928786,577245 932654,577619 933122,580589 933287,580929 933297,583237 932957,583504 932546,583652 929953,582964 928631,577255 928582))";
// CCW Polygons not working in 1.56
static std::string const mysql_report_2014_10_24
= "POLYGON((0 0, 0 8, 8 8, 8 10, -10 10, -10 0, 0 0))";
@ -483,8 +485,8 @@ void test_all()
test_one<polygon_type, polygon_type>("county1", county1, join_round, end_flat, {0.00114, 0.00115}, 0.01);
test_one<polygon_type, polygon_type>("county1", county1, join_miter, end_flat, {0.00132, 0.00133}, 0.01);
test_one<polygon_type, polygon_type>("county1", county1, join_round, end_flat, {3.944e-05, 3.947e-5}, -0.003);
test_one<polygon_type, polygon_type>("county1", county1, join_miter, end_flat, {3.943e-05, 3.946e-5}, -0.003);
test_one<polygon_type, polygon_type>("county1", county1, join_round, end_flat, {3.94e-05, 4.24e-5}, -0.003);
test_one<polygon_type, polygon_type>("county1", county1, join_miter, end_flat, {3.94e-05, 4.24e-5}, -0.003);
test_one<polygon_type, polygon_type>("parcel1_10", parcel1, join_round, end_flat, 7571.405, 10.0);
test_one<polygon_type, polygon_type>("parcel1_10", parcel1, join_miter, end_flat, {8207, 8217}, 10.0);
@ -532,8 +534,11 @@ void test_all()
test_one<polygon_type, polygon_type>("italy_part1_60", italy_part1,
join_round, end_flat, 15479097108.720, 60.0 * 1000.0);
#if ! defined(BOOST_GEOMETRY_USE_RESCALING) || defined(BOOST_GEOMETRY_TEST_FAILURES)
// Fails with rescaling after removing pretraverse
test_one<polygon_type, polygon_type>("italy_part2_5", italy_part2,
join_round, end_flat, {12496082120, 12496082124}, 5 * 1000.0);
#endif
if (! BOOST_GEOMETRY_CONDITION((std::is_same<coor_type, float>::value)))
{
@ -604,6 +609,8 @@ void test_all()
test_one<polygon_type, polygon_type>("issue_555_1000", issue_555, jr, er, 4519.9627, -0.001);
}
test_one<polygon_type, polygon_type>("issue_1019", issue_1019, join_miter, end_flat, 34835787.44782, 300.0);
{
bg::strategy::buffer::join_round join_round32(32);
bg::strategy::buffer::end_round end_round32(32);