Chris@16
|
1 // Boost.Geometry (aka GGL, Generic Geometry Library)
|
Chris@16
|
2
|
Chris@16
|
3 // Copyright (c) 1995, 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
|
Chris@16
|
4 // Copyright (c) 1995 Maarten Hilferink, Amsterdam, the Netherlands
|
Chris@16
|
5
|
Chris@16
|
6 // Parts of Boost.Geometry are redesigned from Geodan's Geographic Library
|
Chris@16
|
7 // (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands.
|
Chris@16
|
8
|
Chris@16
|
9 // Use, modification and distribution is subject to the Boost Software License,
|
Chris@16
|
10 // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
|
Chris@16
|
11 // http://www.boost.org/LICENSE_1_0.txt)
|
Chris@16
|
12
|
Chris@16
|
13 #ifndef BOOST_GEOMETRY_STRATEGY_AGNOSTIC_SIMPLIFY_DOUGLAS_PEUCKER_HPP
|
Chris@16
|
14 #define BOOST_GEOMETRY_STRATEGY_AGNOSTIC_SIMPLIFY_DOUGLAS_PEUCKER_HPP
|
Chris@16
|
15
|
Chris@16
|
16
|
Chris@16
|
17 #include <cstddef>
|
Chris@16
|
18 #include <vector>
|
Chris@16
|
19
|
Chris@16
|
20 #include <boost/range.hpp>
|
Chris@16
|
21
|
Chris@16
|
22 #include <boost/geometry/core/cs.hpp>
|
Chris@16
|
23 #include <boost/geometry/strategies/distance.hpp>
|
Chris@16
|
24
|
Chris@16
|
25
|
Chris@16
|
26
|
Chris@16
|
27 //#define GL_DEBUG_DOUGLAS_PEUCKER
|
Chris@16
|
28
|
Chris@16
|
29 #ifdef GL_DEBUG_DOUGLAS_PEUCKER
|
Chris@16
|
30 #include <boost/geometry/io/dsv/write.hpp>
|
Chris@16
|
31 #endif
|
Chris@16
|
32
|
Chris@16
|
33
|
Chris@16
|
34 namespace boost { namespace geometry
|
Chris@16
|
35 {
|
Chris@16
|
36
|
Chris@16
|
37 namespace strategy { namespace simplify
|
Chris@16
|
38 {
|
Chris@16
|
39
|
Chris@16
|
40
|
Chris@16
|
41 #ifndef DOXYGEN_NO_DETAIL
|
Chris@16
|
42 namespace detail
|
Chris@16
|
43 {
|
Chris@16
|
44
|
Chris@16
|
45 /*!
|
Chris@16
|
46 \brief Small wrapper around a point, with an extra member "included"
|
Chris@16
|
47 \details
|
Chris@16
|
48 It has a const-reference to the original point (so no copy here)
|
Chris@16
|
49 \tparam the enclosed point type
|
Chris@16
|
50 */
|
Chris@16
|
51 template<typename Point>
|
Chris@16
|
52 struct douglas_peucker_point
|
Chris@16
|
53 {
|
Chris@16
|
54 Point const& p;
|
Chris@16
|
55 bool included;
|
Chris@16
|
56
|
Chris@16
|
57 inline douglas_peucker_point(Point const& ap)
|
Chris@16
|
58 : p(ap)
|
Chris@16
|
59 , included(false)
|
Chris@16
|
60 {}
|
Chris@16
|
61
|
Chris@16
|
62 // Necessary for proper compilation
|
Chris@16
|
63 inline douglas_peucker_point<Point> operator=(douglas_peucker_point<Point> const& )
|
Chris@16
|
64 {
|
Chris@16
|
65 return douglas_peucker_point<Point>(*this);
|
Chris@16
|
66 }
|
Chris@16
|
67 };
|
Chris@16
|
68 }
|
Chris@16
|
69 #endif // DOXYGEN_NO_DETAIL
|
Chris@16
|
70
|
Chris@16
|
71
|
Chris@16
|
72 /*!
|
Chris@16
|
73 \brief Implements the simplify algorithm.
|
Chris@16
|
74 \ingroup strategies
|
Chris@16
|
75 \details The douglas_peucker strategy simplifies a linestring, ring or
|
Chris@16
|
76 vector of points using the well-known Douglas-Peucker algorithm.
|
Chris@16
|
77 \tparam Point the point type
|
Chris@16
|
78 \tparam PointDistanceStrategy point-segment distance strategy to be used
|
Chris@16
|
79 \note This strategy uses itself a point-segment-distance strategy which
|
Chris@16
|
80 can be specified
|
Chris@16
|
81 \author Barend and Maarten, 1995/1996
|
Chris@16
|
82 \author Barend, revised for Generic Geometry Library, 2008
|
Chris@16
|
83 */
|
Chris@16
|
84
|
Chris@16
|
85 /*
|
Chris@16
|
86 For the algorithm, see for example:
|
Chris@16
|
87 - http://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm
|
Chris@16
|
88 - http://www2.dcs.hull.ac.uk/CISRG/projects/Royal-Inst/demos/dp.html
|
Chris@16
|
89 */
|
Chris@16
|
90 template
|
Chris@16
|
91 <
|
Chris@16
|
92 typename Point,
|
Chris@16
|
93 typename PointDistanceStrategy
|
Chris@16
|
94 >
|
Chris@16
|
95 class douglas_peucker
|
Chris@16
|
96 {
|
Chris@16
|
97 public :
|
Chris@16
|
98
|
Chris@16
|
99 // See also ticket 5954 https://svn.boost.org/trac/boost/ticket/5954
|
Chris@16
|
100 // Comparable is currently not possible here because it has to be compared to the squared of max_distance, and more.
|
Chris@16
|
101 // For now we have to take the real distance.
|
Chris@16
|
102 typedef PointDistanceStrategy distance_strategy_type;
|
Chris@16
|
103 // typedef typename strategy::distance::services::comparable_type<PointDistanceStrategy>::type distance_strategy_type;
|
Chris@16
|
104
|
Chris@16
|
105 typedef typename strategy::distance::services::return_type
|
Chris@16
|
106 <
|
Chris@16
|
107 distance_strategy_type,
|
Chris@16
|
108 Point, Point
|
Chris@16
|
109 >::type return_type;
|
Chris@16
|
110
|
Chris@16
|
111 private :
|
Chris@16
|
112 typedef detail::douglas_peucker_point<Point> dp_point_type;
|
Chris@16
|
113 typedef typename std::vector<dp_point_type>::iterator iterator_type;
|
Chris@16
|
114
|
Chris@16
|
115
|
Chris@16
|
116 static inline void consider(iterator_type begin,
|
Chris@16
|
117 iterator_type end,
|
Chris@16
|
118 return_type const& max_dist, int& n,
|
Chris@16
|
119 distance_strategy_type const& ps_distance_strategy)
|
Chris@16
|
120 {
|
Chris@16
|
121 std::size_t size = end - begin;
|
Chris@16
|
122
|
Chris@16
|
123 // size must be at least 3
|
Chris@16
|
124 // because we want to consider a candidate point in between
|
Chris@16
|
125 if (size <= 2)
|
Chris@16
|
126 {
|
Chris@16
|
127 #ifdef GL_DEBUG_DOUGLAS_PEUCKER
|
Chris@16
|
128 if (begin != end)
|
Chris@16
|
129 {
|
Chris@16
|
130 std::cout << "ignore between " << dsv(begin->p)
|
Chris@16
|
131 << " and " << dsv((end - 1)->p)
|
Chris@16
|
132 << " size=" << size << std::endl;
|
Chris@16
|
133 }
|
Chris@16
|
134 std::cout << "return because size=" << size << std::endl;
|
Chris@16
|
135 #endif
|
Chris@16
|
136 return;
|
Chris@16
|
137 }
|
Chris@16
|
138
|
Chris@16
|
139 iterator_type last = end - 1;
|
Chris@16
|
140
|
Chris@16
|
141 #ifdef GL_DEBUG_DOUGLAS_PEUCKER
|
Chris@16
|
142 std::cout << "find between " << dsv(begin->p)
|
Chris@16
|
143 << " and " << dsv(last->p)
|
Chris@16
|
144 << " size=" << size << std::endl;
|
Chris@16
|
145 #endif
|
Chris@16
|
146
|
Chris@16
|
147
|
Chris@16
|
148 // Find most far point, compare to the current segment
|
Chris@16
|
149 //geometry::segment<Point const> s(begin->p, last->p);
|
Chris@16
|
150 return_type md(-1.0); // any value < 0
|
Chris@16
|
151 iterator_type candidate;
|
Chris@16
|
152 for(iterator_type it = begin + 1; it != last; ++it)
|
Chris@16
|
153 {
|
Chris@16
|
154 return_type dist = ps_distance_strategy.apply(it->p, begin->p, last->p);
|
Chris@16
|
155
|
Chris@16
|
156 #ifdef GL_DEBUG_DOUGLAS_PEUCKER
|
Chris@16
|
157 std::cout << "consider " << dsv(it->p)
|
Chris@16
|
158 << " at " << double(dist)
|
Chris@16
|
159 << ((dist > max_dist) ? " maybe" : " no")
|
Chris@16
|
160 << std::endl;
|
Chris@16
|
161
|
Chris@16
|
162 #endif
|
Chris@16
|
163 if (dist > md)
|
Chris@16
|
164 {
|
Chris@16
|
165 md = dist;
|
Chris@16
|
166 candidate = it;
|
Chris@16
|
167 }
|
Chris@16
|
168 }
|
Chris@16
|
169
|
Chris@16
|
170 // If a point is found, set the include flag
|
Chris@16
|
171 // and handle segments in between recursively
|
Chris@16
|
172 if (md > max_dist)
|
Chris@16
|
173 {
|
Chris@16
|
174 #ifdef GL_DEBUG_DOUGLAS_PEUCKER
|
Chris@16
|
175 std::cout << "use " << dsv(candidate->p) << std::endl;
|
Chris@16
|
176 #endif
|
Chris@16
|
177
|
Chris@16
|
178 candidate->included = true;
|
Chris@16
|
179 n++;
|
Chris@16
|
180
|
Chris@16
|
181 consider(begin, candidate + 1, max_dist, n, ps_distance_strategy);
|
Chris@16
|
182 consider(candidate, end, max_dist, n, ps_distance_strategy);
|
Chris@16
|
183 }
|
Chris@16
|
184 }
|
Chris@16
|
185
|
Chris@16
|
186
|
Chris@16
|
187 public :
|
Chris@16
|
188
|
Chris@16
|
189 template <typename Range, typename OutputIterator>
|
Chris@16
|
190 static inline OutputIterator apply(Range const& range,
|
Chris@16
|
191 OutputIterator out, double max_distance)
|
Chris@16
|
192 {
|
Chris@16
|
193 distance_strategy_type strategy;
|
Chris@16
|
194
|
Chris@16
|
195 // Copy coordinates, a vector of references to all points
|
Chris@16
|
196 std::vector<dp_point_type> ref_candidates(boost::begin(range),
|
Chris@16
|
197 boost::end(range));
|
Chris@16
|
198
|
Chris@16
|
199 // Include first and last point of line,
|
Chris@16
|
200 // they are always part of the line
|
Chris@16
|
201 int n = 2;
|
Chris@16
|
202 ref_candidates.front().included = true;
|
Chris@16
|
203 ref_candidates.back().included = true;
|
Chris@16
|
204
|
Chris@16
|
205 // Get points, recursively, including them if they are further away
|
Chris@16
|
206 // than the specified distance
|
Chris@16
|
207 consider(boost::begin(ref_candidates), boost::end(ref_candidates), max_distance, n, strategy);
|
Chris@16
|
208
|
Chris@16
|
209 // Copy included elements to the output
|
Chris@16
|
210 for(typename std::vector<dp_point_type>::const_iterator it
|
Chris@16
|
211 = boost::begin(ref_candidates);
|
Chris@16
|
212 it != boost::end(ref_candidates);
|
Chris@16
|
213 ++it)
|
Chris@16
|
214 {
|
Chris@16
|
215 if (it->included)
|
Chris@16
|
216 {
|
Chris@16
|
217 // copy-coordinates does not work because OutputIterator
|
Chris@16
|
218 // does not model Point (??)
|
Chris@16
|
219 //geometry::convert(it->p, *out);
|
Chris@16
|
220 *out = it->p;
|
Chris@16
|
221 out++;
|
Chris@16
|
222 }
|
Chris@16
|
223 }
|
Chris@16
|
224 return out;
|
Chris@16
|
225 }
|
Chris@16
|
226
|
Chris@16
|
227 };
|
Chris@16
|
228
|
Chris@16
|
229 }} // namespace strategy::simplify
|
Chris@16
|
230
|
Chris@16
|
231
|
Chris@16
|
232 namespace traits {
|
Chris@16
|
233
|
Chris@16
|
234 template <typename P>
|
Chris@16
|
235 struct point_type<strategy::simplify::detail::douglas_peucker_point<P> >
|
Chris@16
|
236 {
|
Chris@16
|
237 typedef P type;
|
Chris@16
|
238 };
|
Chris@16
|
239
|
Chris@16
|
240 } // namespace traits
|
Chris@16
|
241
|
Chris@16
|
242
|
Chris@16
|
243 }} // namespace boost::geometry
|
Chris@16
|
244
|
Chris@16
|
245 #endif // BOOST_GEOMETRY_STRATEGY_AGNOSTIC_SIMPLIFY_DOUGLAS_PEUCKER_HPP
|