summaryrefslogtreecommitdiff
path: root/boost/geometry/algorithms/simplify.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'boost/geometry/algorithms/simplify.hpp')
-rw-r--r--boost/geometry/algorithms/simplify.hpp250
1 files changed, 192 insertions, 58 deletions
diff --git a/boost/geometry/algorithms/simplify.hpp b/boost/geometry/algorithms/simplify.hpp
index cfeb542220..298f9c640a 100644
--- a/boost/geometry/algorithms/simplify.hpp
+++ b/boost/geometry/algorithms/simplify.hpp
@@ -36,9 +36,13 @@
#include <boost/geometry/strategies/default_strategy.hpp>
#include <boost/geometry/strategies/distance.hpp>
+#include <boost/geometry/algorithms/area.hpp>
#include <boost/geometry/algorithms/clear.hpp>
#include <boost/geometry/algorithms/convert.hpp>
+#include <boost/geometry/algorithms/detail/equals/point_point.hpp>
#include <boost/geometry/algorithms/not_implemented.hpp>
+#include <boost/geometry/algorithms/is_empty.hpp>
+#include <boost/geometry/algorithms/perimeter.hpp>
#include <boost/geometry/algorithms/detail/distance/default_strategies.hpp>
@@ -49,6 +53,14 @@ namespace boost { namespace geometry
namespace detail { namespace simplify
{
+template <typename Range>
+inline bool is_degenerate(Range const& range)
+{
+ return boost::size(range) == 2
+ && detail::equals::equals_point_point(geometry::range::front(range),
+ geometry::range::back(range));
+}
+
struct simplify_range_insert
{
template<typename Range, typename Strategy, typename OutputIterator, typename Distance>
@@ -57,7 +69,11 @@ struct simplify_range_insert
{
boost::ignore_unused(strategy);
- if (boost::size(range) <= 2 || max_distance < 0)
+ if (is_degenerate(range))
+ {
+ std::copy(boost::begin(range), boost::begin(range) + 1, out);
+ }
+ else if (boost::size(range) <= 2 || max_distance < 0)
{
std::copy(boost::begin(range), boost::end(range), out);
}
@@ -71,41 +87,32 @@ struct simplify_range_insert
struct simplify_copy
{
- template <typename Range, typename Strategy, typename Distance>
- static inline void apply(Range const& range, Range& out,
+ template <typename RangeIn, typename RangeOut, typename Strategy, typename Distance>
+ static inline void apply(RangeIn const& range, RangeOut& out,
Distance const& , Strategy const& )
{
std::copy
(
- boost::begin(range), boost::end(range), geometry::range::back_inserter(out)
+ boost::begin(range), boost::end(range),
+ geometry::range::back_inserter(out)
);
}
};
-template<std::size_t Minimum>
+template <std::size_t MinimumToUseStrategy>
struct simplify_range
{
- template <typename Range, typename Strategy, typename Distance>
- static inline void apply(Range const& range, Range& out,
+ template <typename RangeIn, typename RangeOut, typename Strategy, typename Distance>
+ static inline void apply(RangeIn const& range, RangeOut& out,
Distance const& max_distance, Strategy const& strategy)
{
- // Call do_container for a linestring / ring
-
- /* For a RING:
- The first/last point (the closing point of the ring) should maybe
- be excluded because it lies on a line with second/one but last.
- Here it is never excluded.
+ // For a RING:
+ // Note that, especially if max_distance is too large,
+ // the output ring might be self intersecting while the input ring is
+ // not, although chances are low in normal polygons
- Note also that, especially if max_distance is too large,
- the output ring might be self intersecting while the input ring is
- not, although chances are low in normal polygons
-
- Finally the inputring might have 3 (open) or 4 (closed) points (=correct),
- the output < 3 or 4(=wrong)
- */
-
- if (boost::size(range) <= int(Minimum) || max_distance < 0.0)
+ if (boost::size(range) <= MinimumToUseStrategy || max_distance < 0)
{
simplify_copy::apply(range, out, max_distance, strategy);
}
@@ -116,34 +123,172 @@ struct simplify_range
range, geometry::range::back_inserter(out), max_distance, strategy
);
}
+
+ // Verify the two remaining points are equal. If so, remove one of them.
+ // This can cause the output being under the minimum size
+ if (is_degenerate(out))
+ {
+ range::resize(out, 1);
+ }
+ }
+};
+
+struct simplify_ring
+{
+private :
+ template <typename Area>
+ static inline int area_sign(Area const& area)
+ {
+ return area > 0 ? 1 : area < 0 ? -1 : 0;
+ }
+
+ template <typename Strategy, typename Ring>
+ static std::size_t get_opposite(std::size_t index, Ring const& ring)
+ {
+ typename Strategy::distance_strategy_type distance_strategy;
+
+ // Verify if it is NOT the case that all points are less than the
+ // simplifying distance. If so, output is empty.
+ typename Strategy::distance_type max_distance(-1);
+
+ typename geometry::point_type<Ring>::type point = range::at(ring, index);
+ std::size_t i = 0;
+ for (typename boost::range_iterator<Ring const>::type
+ it = boost::begin(ring); it != boost::end(ring); ++it, ++i)
+ {
+ // This actually is point-segment distance but will result
+ // in point-point distance
+ typename Strategy::distance_type dist = distance_strategy.apply(*it, point, point);
+ if (dist > max_distance)
+ {
+ max_distance = dist;
+ index = i;
+ }
+ }
+ return index;
+ }
+
+public :
+ template <typename Ring, typename Strategy, typename Distance>
+ static inline void apply(Ring const& ring, Ring& out,
+ Distance const& max_distance, Strategy const& strategy)
+ {
+ std::size_t const size = boost::size(ring);
+ if (size == 0)
+ {
+ return;
+ }
+
+ int const input_sign = area_sign(geometry::area(ring));
+
+ std::set<std::size_t> visited_indexes;
+
+ // Rotate it into a copied vector
+ // (vector, because source type might not support rotation)
+ // (duplicate end point will be simplified away)
+ typedef typename geometry::point_type<Ring>::type point_type;
+
+ std::vector<point_type> rotated(size);
+
+ // Closing point (but it will not start here)
+ std::size_t index = 0;
+
+ // Iterate (usually one iteration is enough)
+ for (std::size_t iteration = 0; iteration < 4u; iteration++)
+ {
+ // Always take the opposite. Opposite guarantees that no point
+ // "halfway" is chosen, creating an artefact (very narrow triangle)
+ // Iteration 0: opposite to closing point (1/2, = on convex hull)
+ // (this will start simplification with that point
+ // and its opposite ~0)
+ // Iteration 1: move a quarter on that ring, then opposite to 1/4
+ // (with its opposite 3/4)
+ // Iteration 2: move an eight on that ring, then opposite (1/8)
+ // Iteration 3: again move a quarter, then opposite (7/8)
+ // So finally 8 "sides" of the ring have been examined (if it were
+ // a semi-circle). Most probably, there are only 0 or 1 iterations.
+ switch (iteration)
+ {
+ case 1 : index = (index + size / 4) % size; break;
+ case 2 : index = (index + size / 8) % size; break;
+ case 3 : index = (index + size / 4) % size; break;
+ }
+ index = get_opposite<Strategy>(index, ring);
+
+ if (visited_indexes.count(index) > 0)
+ {
+ // Avoid trying the same starting point more than once
+ continue;
+ }
+
+ std::rotate_copy(boost::begin(ring), range::pos(ring, index),
+ boost::end(ring), rotated.begin());
+
+ // Close the rotated copy
+ rotated.push_back(range::at(ring, index));
+
+ simplify_range<0>::apply(rotated, out, max_distance, strategy);
+
+ // Verify that what was positive, stays positive (or goes to 0)
+ // and what was negative stays negative (or goes to 0)
+ int const output_sign = area_sign(geometry::area(out));
+ if (output_sign == input_sign)
+ {
+ // Result is considered as satisfactory (usually this is the
+ // first iteration - only for small rings, having a scale
+ // similar to simplify_distance, next iterations are tried
+ return;
+ }
+
+ // Original is simplified away. Possibly there is a solution
+ // when another starting point is used
+ geometry::clear(out);
+
+ if (iteration == 0
+ && geometry::perimeter(ring) < 3 * max_distance)
+ {
+ // Check if it is useful to iterate. A minimal triangle has a
+ // perimeter of a bit more than 3 times the simplify distance
+ return;
+ }
+
+ // Prepare next try
+ visited_indexes.insert(index);
+ rotated.resize(size);
+ }
}
};
+
struct simplify_polygon
{
private:
template
<
- std::size_t Minimum,
typename IteratorIn,
- typename IteratorOut,
+ typename InteriorRingsOut,
typename Distance,
typename Strategy
>
static inline void iterate(IteratorIn begin, IteratorIn end,
- IteratorOut it_out,
+ InteriorRingsOut& interior_rings_out,
Distance const& max_distance, Strategy const& strategy)
{
- for (IteratorIn it_in = begin; it_in != end; ++it_in, ++it_out)
+ typedef typename boost::range_value<InteriorRingsOut>::type single_type;
+ for (IteratorIn it = begin; it != end; ++it)
{
- simplify_range<Minimum>::apply(*it_in, *it_out, max_distance, strategy);
+ single_type out;
+ simplify_ring::apply(*it, out, max_distance, strategy);
+ if (! geometry::is_empty(out))
+ {
+ range::push_back(interior_rings_out, out);
+ }
}
}
template
<
- std::size_t Minimum,
typename InteriorRingsIn,
typename InteriorRingsOut,
typename Distance,
@@ -154,12 +299,11 @@ private:
InteriorRingsOut& interior_rings_out,
Distance const& max_distance, Strategy const& strategy)
{
- traits::resize<InteriorRingsOut>::apply(interior_rings_out,
- boost::size(interior_rings_in));
+ range::clear(interior_rings_out);
- iterate<Minimum>(
+ iterate(
boost::begin(interior_rings_in), boost::end(interior_rings_in),
- boost::begin(interior_rings_out),
+ interior_rings_out,
max_distance, strategy);
}
@@ -168,21 +312,14 @@ public:
static inline void apply(Polygon const& poly_in, Polygon& poly_out,
Distance const& max_distance, Strategy const& strategy)
{
- std::size_t const minimum = core_detail::closure::minimum_ring_size
- <
- geometry::closure<Polygon>::value
- >::value;
-
// Note that if there are inner rings, and distance is too large,
// they might intersect with the outer ring in the output,
// while it didn't in the input.
- simplify_range<minimum>::apply(exterior_ring(poly_in),
- exterior_ring(poly_out),
- max_distance, strategy);
+ simplify_ring::apply(exterior_ring(poly_in), exterior_ring(poly_out),
+ max_distance, strategy);
- apply_interior_rings<minimum>(interior_rings(poly_in),
- interior_rings(poly_out),
- max_distance, strategy);
+ apply_interior_rings(interior_rings(poly_in),
+ interior_rings(poly_out), max_distance, strategy);
}
};
@@ -194,16 +331,19 @@ struct simplify_multi
static inline void apply(MultiGeometry const& multi, MultiGeometry& out,
Distance const& max_distance, Strategy const& strategy)
{
- traits::resize<MultiGeometry>::apply(out, boost::size(multi));
+ range::clear(out);
+
+ typedef typename boost::range_value<MultiGeometry>::type single_type;
- typename boost::range_iterator<MultiGeometry>::type it_out
- = boost::begin(out);
for (typename boost::range_iterator<MultiGeometry const>::type
- it_in = boost::begin(multi);
- it_in != boost::end(multi);
- ++it_in, ++it_out)
+ it = boost::begin(multi); it != boost::end(multi); ++it)
{
- Policy::apply(*it_in, *it_out, max_distance, strategy);
+ single_type single_out;
+ Policy::apply(*it, single_out, max_distance, strategy);
+ if (! geometry::is_empty(single_out))
+ {
+ range::push_back(out, single_out);
+ }
}
}
};
@@ -236,7 +376,7 @@ struct simplify<Point, point_tag>
}
};
-
+// Linestring, keep 2 points (unless those points are the same)
template <typename Linestring>
struct simplify<Linestring, linestring_tag>
: detail::simplify::simplify_range<2>
@@ -244,13 +384,7 @@ struct simplify<Linestring, linestring_tag>
template <typename Ring>
struct simplify<Ring, ring_tag>
- : detail::simplify::simplify_range
- <
- core_detail::closure::minimum_ring_size
- <
- geometry::closure<Ring>::value
- >::value
- >
+ : detail::simplify::simplify_ring
{};
template <typename Polygon>