[buffer] propagate error information from strategy to buffer inserter

This commit is contained in:
barendgehrels
2015-05-27 10:56:56 +02:00
parent ac70187c37
commit cdf014e35d
7 changed files with 96 additions and 51 deletions

View File

@@ -12,9 +12,9 @@
#include <cstddef>
#include <iterator>
#include <boost/assert.hpp>
#include <boost/core/ignore_unused.hpp>
#include <boost/numeric/conversion/cast.hpp>
#include <boost/range.hpp>
#include <boost/geometry/core/closure.hpp>
@@ -227,7 +227,7 @@ struct buffer_range
typename EndStrategy,
typename RobustPolicy
>
static inline bool iterate(Collection& collection,
static inline strategy::buffer::result_code iterate(Collection& collection,
Iterator begin, Iterator end,
strategy::buffer::buffer_side_selector side,
DistanceStrategy const& distance_strategy,
@@ -266,7 +266,7 @@ struct buffer_range
* pup: penultimate_point
*/
bool result = false;
strategy::buffer::result_code result = strategy::buffer::result_no_output;
bool first = true;
Iterator it = begin;
@@ -277,18 +277,25 @@ struct buffer_range
for (Iterator prev = it++; it != end; ++it)
{
generated_side.clear();
side_strategy.apply(*prev, *it, side,
strategy::buffer::result_code error_code
= side_strategy.apply(*prev, *it, side,
distance_strategy, generated_side);
if (generated_side.empty())
if (error_code == strategy::buffer::result_no_output)
{
// Because input is simplified, this is improbable,
// but it can happen for degenerate geometries
// Further handling of this side is skipped
continue;
}
else if (error_code == strategy::buffer::result_error_numerical)
{
return error_code;
}
result = true;
BOOST_ASSERT(! generated_side.empty());
result = strategy::buffer::result_normal;
if (! first)
{
@@ -459,7 +466,7 @@ struct buffer_inserter<ring_tag, RingInput, RingOutput>
typename EndStrategy,
typename RobustPolicy
>
static inline bool iterate(Collection& collection,
static inline strategy::buffer::result_code iterate(Collection& collection,
Iterator begin, Iterator end,
strategy::buffer::buffer_side_selector side,
DistanceStrategy const& distance_strategy,
@@ -472,13 +479,14 @@ struct buffer_inserter<ring_tag, RingInput, RingOutput>
typedef detail::buffer::buffer_range<RingOutput> buffer_range;
bool result = buffer_range::iterate(collection, begin, end,
strategy::buffer::result_code result
= buffer_range::iterate(collection, begin, end,
side,
distance_strategy, side_strategy, join_strategy, end_strategy, robust_policy,
first_p1, first_p2, last_p1, last_p2);
// Generate closing join
if (result)
if (result == strategy::buffer::result_normal)
{
buffer_range::add_join(collection,
*(end - 2),
@@ -515,7 +523,7 @@ struct buffer_inserter<ring_tag, RingInput, RingOutput>
RingInput simplified;
detail::buffer::simplify_input(ring, distance, simplified);
bool has_output = false;
strategy::buffer::result_code code = strategy::buffer::result_no_output;
std::size_t n = boost::size(simplified);
std::size_t const min_points = core_detail::closure::minimum_ring_size
@@ -529,21 +537,19 @@ struct buffer_inserter<ring_tag, RingInput, RingOutput>
if (distance.negative())
{
// Walk backwards (rings will be reversed afterwards)
// It might be that this will be changed later.
// TODO: decide this.
has_output = iterate(collection, boost::rbegin(view), boost::rend(view),
code = iterate(collection, boost::rbegin(view), boost::rend(view),
strategy::buffer::buffer_side_right,
distance, side_strategy, join_strategy, end_strategy, robust_policy);
}
else
{
has_output = iterate(collection, boost::begin(view), boost::end(view),
code = iterate(collection, boost::begin(view), boost::end(view),
strategy::buffer::buffer_side_left,
distance, side_strategy, join_strategy, end_strategy, robust_policy);
}
}
if (! has_output && n >= 1)
if (code == strategy::buffer::result_no_output && n >= 1)
{
// Use point_strategy to buffer degenerated ring
detail::buffer::buffer_point<output_point_type>
@@ -577,7 +583,7 @@ struct buffer_inserter<linestring_tag, Linestring, Polygon>
typename EndStrategy,
typename RobustPolicy
>
static inline bool iterate(Collection& collection,
static inline strategy::buffer::result_code iterate(Collection& collection,
Iterator begin, Iterator end,
strategy::buffer::buffer_side_selector side,
DistanceStrategy const& distance_strategy,
@@ -602,24 +608,27 @@ struct buffer_inserter<linestring_tag, Linestring, Polygon>
else
{
std::vector<output_point_type> generated_side;
side_strategy.apply(ultimate_point, penultimate_point,
strategy::buffer::result_code code
= side_strategy.apply(ultimate_point, penultimate_point,
strategy::buffer::buffer_side_right,
distance_strategy, generated_side);
if (generated_side.empty())
if (code != strategy::buffer::result_normal)
{
return false;
// No output or numerical error
return code;
}
reverse_p1 = generated_side.front();
}
output_point_type first_p2, last_p1, last_p2;
bool result = detail::buffer::buffer_range<output_ring_type>::iterate(collection,
strategy::buffer::result_code result
= detail::buffer::buffer_range<output_ring_type>::iterate(collection,
begin, end, side,
distance_strategy, side_strategy, join_strategy, end_strategy, robust_policy,
first_p1, first_p2, last_p1, last_p2);
if (result)
if (result == strategy::buffer::result_normal)
{
std::vector<output_point_type> range_out;
end_strategy.apply(penultimate_point, last_p2, ultimate_point, reverse_p1, side, distance_strategy, range_out);
@@ -649,28 +658,29 @@ struct buffer_inserter<linestring_tag, Linestring, Polygon>
Linestring simplified;
detail::buffer::simplify_input(linestring, distance, simplified);
bool has_output = false;
strategy::buffer::result_code code = strategy::buffer::result_no_output;
std::size_t n = boost::size(simplified);
if (n > 1)
{
collection.start_new_ring();
output_point_type first_p1;
has_output = iterate(collection,
code = iterate(collection,
boost::begin(simplified), boost::end(simplified),
strategy::buffer::buffer_side_left,
distance, side_strategy, join_strategy, end_strategy, robust_policy,
first_p1);
if (has_output)
if (code == strategy::buffer::result_normal)
{
iterate(collection, boost::rbegin(simplified), boost::rend(simplified),
code = iterate(collection,
boost::rbegin(simplified), boost::rend(simplified),
strategy::buffer::buffer_side_right,
distance, side_strategy, join_strategy, end_strategy, robust_policy,
first_p1);
}
collection.finish_ring();
}
if (! has_output && n >= 1)
if (code == strategy::buffer::result_no_output && n >= 1)
{
// Use point_strategy to buffer degenerated linestring
detail::buffer::buffer_point<output_point_type>

View File

@@ -83,6 +83,17 @@ enum join_selector
join_spike // collinear, with overlap, next segment goes back
};
/*!
\brief Enumerates types of result codes from buffer strategies
\ingroup enum
*/
enum result_code
{
result_normal,
result_error_numerical,
result_no_output
};
}} // namespace strategy::buffer

View File

@@ -9,6 +9,8 @@
#include <cstddef>
#include <boost/math/special_functions/fpclassify.hpp>
#include <boost/geometry/core/coordinate_type.hpp>
#include <boost/geometry/core/access.hpp>
#include <boost/geometry/util/math.hpp>
@@ -51,9 +53,9 @@ public :
typename OutputRange,
typename DistanceStrategy
>
static inline void apply(
static inline result_code apply(
Point const& input_p1, Point const& input_p2,
strategy::buffer::buffer_side_selector side,
buffer_side_selector side,
DistanceStrategy const& distance,
OutputRange& output_range)
{
@@ -78,10 +80,11 @@ public :
// In case of coordinates differences of e.g. 1e300, length
// will overflow and we should not generate output
#ifdef BOOST_GEOMETRY_DEBUG_BUFFER_WARN
std::cout << "Length not calculated for points " << geometry::wkt(input_p1)
<< " " << geometry::wkt(input_p2) << " " << length << std::endl;
std::cout << "Error in length calculation for points "
<< geometry::wkt(input_p1) << " " << geometry::wkt(input_p2)
<< " length: " << length << std::endl;
#endif
return;
return result_error_numerical;
}
if (geometry::math::equals(length, 0))
@@ -89,14 +92,29 @@ public :
// Coordinates are simplified and therefore most often not equal.
// But if simplify is skipped, or for lines with two
// equal points, length is 0 and we cannot generate output.
return;
return result_no_output;
}
promoted_type const d = distance.apply(input_p1, input_p2, side);
// Generate the normalized perpendicular p, to the left (ccw)
promoted_type const px = -dy / length;
promoted_type const py = dx / length;
promoted_type const d = distance.apply(input_p1, input_p2, side);
if (geometry::math::equals(px, 0)
&& geometry::math::equals(py, 0))
{
// This basically should not occur - because of the checks above.
// There are no unit tests triggering this condition
#ifdef BOOST_GEOMETRY_DEBUG_BUFFER_WARN
std::cout << "Error in perpendicular calculation for points "
<< geometry::wkt(input_p1) << " " << geometry::wkt(input_p2)
<< " length: " << length
<< " distance: " << d
<< std::endl;
#endif
return result_no_output;
}
output_range.resize(2);
@@ -104,6 +122,8 @@ public :
set<1>(output_range.front(), get<1>(input_p1) + py * d);
set<0>(output_range.back(), get<0>(input_p2) + px * d);
set<1>(output_range.back(), get<1>(input_p2) + py * d);
return result_normal;
}
#endif // DOXYGEN_SHOULD_SKIP_THIS
};