~ubuntu-branches/ubuntu/saucy/merkaartor/saucy

« back to all changes in this revision

Viewing changes to include/ggl/algorithms/simplify.hpp

  • Committer: Bazaar Package Importer
  • Author(s): Bernd Zeimetz
  • Date: 2009-09-13 00:52:12 UTC
  • mto: (1.2.7 upstream) (0.1.3 upstream) (3.1.7 sid)
  • mto: This revision was merged to the branch mainline in revision 10.
  • Revision ID: james.westby@ubuntu.com-20090913005212-pjecal8zxm07x0fj
ImportĀ upstreamĀ versionĀ 0.14+svnfixes~20090912

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Generic Geometry Library
 
2
//
 
3
// Copyright Barend Gehrels 1995-2009, Geodan Holding B.V. Amsterdam, the Netherlands.
 
4
// Copyright Bruno Lalande 2008, 2009
 
5
// Use, modification and distribution is subject to the Boost Software License,
 
6
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
 
7
// http://www.boost.org/LICENSE_1_0.txt)
 
8
 
 
9
#ifndef GGL_ALGORITHMS_SIMPLIFY_HPP
 
10
#define GGL_ALGORITHMS_SIMPLIFY_HPP
 
11
 
 
12
#include <boost/range/functions.hpp>
 
13
#include <boost/range/metafunctions.hpp>
 
14
 
 
15
#include <ggl/algorithms/distance.hpp>
 
16
#include <ggl/core/cs.hpp>
 
17
#include <ggl/core/ring_type.hpp>
 
18
#include <ggl/core/exterior_ring.hpp>
 
19
#include <ggl/core/interior_rings.hpp>
 
20
#include <ggl/strategies/agnostic/agn_simplify.hpp>
 
21
 
 
22
 
 
23
/*!
 
24
\defgroup simplify simplification (generalization)
 
25
\par Source description:
 
26
- Wikipedia: given a 'curve' composed of line segments to find a curve not too dissimilar but that has fewer points
 
27
 
 
28
\see http://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm
 
29
 
 
30
\par Performance
 
31
Performance is measured on simplification of a collection of rings, such that 10% of the points is kept.
 
32
- 2776 counties of US are simplified in 0.8 seconds (2.5 seconds or 11.5 seconds in 2 other libraries)
 
33
- 3918 zipcode areas of the Netherlands are simplified in 0.03 seconds (0.1 seconds or 0.45 seconds in 2 other libraries)
 
34
 
 
35
 
 
36
\par Geometries
 
37
- LINESTRING:
 
38
\image html simplify_linestring.png
 
39
- POLYGON: simplifying a valid simple polygon (which never intersects itself) might result in an invalid polygon,
 
40
where the simplified rings intersect themselves or one of the other outer or inner rings.
 
41
Efficient simplification of a ring/polygon is still an "Open Problem"
 
42
(http://maven.smith.edu/~orourke/TOPP/P24.html#Problem.24)
 
43
 
 
44
*/
 
45
 
 
46
namespace ggl
 
47
{
 
48
 
 
49
#ifndef DOXYGEN_NO_DETAIL
 
50
namespace detail { namespace simplify {
 
51
 
 
52
template<typename R, typename OutputIterator, typename S>
 
53
inline void simplify_range_strategy(R const& range, OutputIterator out, double max_distance, S const& strategy)
 
54
{
 
55
    if (boost::begin(range) == boost::end(range) || max_distance < 0)
 
56
    {
 
57
        std::copy(boost::begin(range), boost::end(range), out);
 
58
    }
 
59
    else
 
60
    {
 
61
        typename boost::range_const_iterator<R>::type it = boost::begin(range) + 1;
 
62
        if (it == boost::end(range) || it + 1 == boost::end(range))
 
63
        {
 
64
            std::copy(boost::begin(range), boost::end(range), out);
 
65
        }
 
66
        else
 
67
        {
 
68
            strategy.simplify(range, out, max_distance);
 
69
        }
 
70
    }
 
71
}
 
72
 
 
73
template<typename R, typename OutputIterator>
 
74
inline void simplify_range(R const& range, OutputIterator out, double max_distance)
 
75
{
 
76
    // Define default strategy
 
77
    typedef typename point_type<R>::type point_type;
 
78
    typedef typename cs_tag<point_type>::type cs_tag;
 
79
    typedef typename strategy_distance_segment
 
80
        <
 
81
            cs_tag,
 
82
            cs_tag,
 
83
            point_type,
 
84
            segment<const point_type>
 
85
        >::type strategy_type;
 
86
 
 
87
    strategy::simplify::douglas_peucker<R, OutputIterator, strategy_type> douglas;
 
88
 
 
89
    simplify_range_strategy(range, out, max_distance, douglas);
 
90
}
 
91
 
 
92
template<typename R, typename OutputIterator, typename S>
 
93
inline void simplify_ring(R const& r_in, OutputIterator out, double max_distance, S const& strategy)
 
94
{
 
95
    // Call do_container for a ring
 
96
 
 
97
    // The first/last point (the closing point of the ring) should maybe be excluded because it
 
98
    // lies on a line with second/one but last. Here it is never excluded.
 
99
 
 
100
    // Note also that, especially if max_distance is too large, the output ring might be self intersecting
 
101
    // while the input ring is not, although chances are low in normal polygons
 
102
 
 
103
    // Finally the inputring might have 4 points (=correct), the output < 4(=wrong)
 
104
    if (boost::size(r_in) <= 4 || max_distance < 0)
 
105
    {
 
106
        std::copy(boost::begin(r_in), boost::end(r_in), out);
 
107
    }
 
108
    else
 
109
    {
 
110
        simplify_range_strategy(r_in, out, max_distance, strategy);
 
111
    }
 
112
}
 
113
 
 
114
template<typename Y, typename S>
 
115
inline void simplify_polygon(Y const& poly_in, Y& poly_out, double max_distance, S const& strategy)
 
116
{
 
117
    typedef typename boost::range_iterator
 
118
        <typename interior_type<Y>::type>::type iterator_type;
 
119
    typedef typename boost::range_const_iterator
 
120
        <typename interior_type<Y>::type>::type const_iterator_type;
 
121
 
 
122
    poly_out.clear();
 
123
 
 
124
    // Note that if there are inner rings, and distance is too large, they might intersect with the
 
125
    // outer ring in the output, while it didn't in the input.
 
126
    simplify_ring(exterior_ring(poly_in), std::back_inserter(exterior_ring(poly_out)),
 
127
                  max_distance, strategy);
 
128
 
 
129
    interior_rings(poly_out).resize(boost::size(interior_rings(poly_in)));
 
130
 
 
131
    const_iterator_type it_in = boost::begin(interior_rings(poly_in));
 
132
    iterator_type it_out = boost::begin(interior_rings(poly_out));
 
133
 
 
134
    for (; it_in != boost::end(interior_rings(poly_in)); it_in++, it_out++)
 
135
    {
 
136
        simplify_ring(*it_in, std::back_inserter(*it_out), max_distance, strategy);
 
137
    }
 
138
}
 
139
 
 
140
template<typename Y>
 
141
inline void simplify_polygon(Y const& poly_in, Y& poly_out, double max_distance)
 
142
{
 
143
    // Define default strategy
 
144
    typedef typename ring_type<Y>::type ring_type;
 
145
    typedef std::back_insert_iterator<ring_type> iterator_type;
 
146
 
 
147
    typedef typename point_type<Y>::type point_type;
 
148
    typedef typename cs_tag<point_type>::type cs_tag;
 
149
    typedef typename strategy_distance_segment
 
150
        <
 
151
            cs_tag,
 
152
            cs_tag,
 
153
            point_type,
 
154
            segment<const point_type>
 
155
        >::type strategy_type;
 
156
 
 
157
    strategy::simplify::douglas_peucker<ring_type, iterator_type, strategy_type> douglas;
 
158
 
 
159
    simplify_polygon(poly_in, poly_out, max_distance, douglas);
 
160
}
 
161
 
 
162
}} // namespace detail::simplify
 
163
#endif // DOXYGEN_NO_DETAIL
 
164
 
 
165
 
 
166
#ifndef DOXYGEN_NO_DISPATCH
 
167
namespace dispatch
 
168
{
 
169
 
 
170
template <typename Tag, typename G>
 
171
struct simplify
 
172
{
 
173
};
 
174
 
 
175
// Partial specializations
 
176
template <typename R>
 
177
struct simplify<linestring_tag, R>
 
178
{
 
179
    template<typename OutputIterator, typename S>
 
180
    static inline void apply(R const& range, OutputIterator out, double max_distance, S const& strategy)
 
181
    {
 
182
        strategy.simplify(range, out, max_distance);
 
183
    }
 
184
 
 
185
    template<typename OutputIterator>
 
186
    static inline void apply(R const& range, OutputIterator out, double max_distance)
 
187
    {
 
188
        // Define default strategy
 
189
        typedef typename point_type<R>::type point_type;
 
190
        typedef typename cs_tag<point_type>::type cs_tag;
 
191
        typedef typename strategy_distance_segment
 
192
            <
 
193
                cs_tag,
 
194
                cs_tag,
 
195
                point_type,
 
196
                segment<const point_type>
 
197
           >::type strategy_type;
 
198
 
 
199
        strategy::simplify::douglas_peucker<R, OutputIterator, strategy_type> douglas;
 
200
 
 
201
        detail::simplify::simplify_range_strategy(range, out, max_distance, douglas);
 
202
    }
 
203
};
 
204
 
 
205
template <typename R>
 
206
struct simplify<ring_tag, R>
 
207
{
 
208
    /// Simplify a ring, using a strategy
 
209
    template<typename S>
 
210
    static inline void apply(R const& ring_in, R& ring_out, double max_distance, S const& strategy)
 
211
    {
 
212
        using detail::simplify::simplify_ring;
 
213
        simplify_ring(ring_in, std::back_inserter(ring_out), max_distance, strategy);
 
214
    }
 
215
 
 
216
    /// Simplify a ring
 
217
    static inline void apply(R const& ring_in, R& ring_out, double max_distance)
 
218
    {
 
219
        // Define default strategy
 
220
        typedef typename point_type<R>::type point_type;
 
221
        typedef typename cs_tag<point_type>::type cs_tag;
 
222
        typedef typename strategy_distance_segment
 
223
            <
 
224
                cs_tag,
 
225
                cs_tag,
 
226
                point_type,
 
227
                segment<const point_type>
 
228
            >::type strategy_type;
 
229
        typedef std::back_insert_iterator<R> iterator_type;
 
230
 
 
231
        strategy::simplify::douglas_peucker<R, iterator_type, strategy_type> douglas;
 
232
 
 
233
        detail::simplify::simplify_ring(ring_in, std::back_inserter(ring_out), max_distance, douglas);
 
234
    }
 
235
};
 
236
 
 
237
template <typename P>
 
238
struct simplify<polygon_tag, P>
 
239
{
 
240
    /// Simplify a polygon, using a strategy
 
241
    template<typename S>
 
242
    static inline void apply(P const& poly_in, P& poly_out, double max_distance, S const& strategy)
 
243
    {
 
244
        detail::simplify::simplify_polygon(poly_in, poly_out, max_distance, strategy);
 
245
    }
 
246
 
 
247
    /// Simplify a polygon
 
248
    static inline void apply(P const& poly_in, P& poly_out, double max_distance)
 
249
    {
 
250
        detail::simplify::simplify_polygon(poly_in, poly_out, max_distance);
 
251
    }
 
252
};
 
253
 
 
254
} // namespace dispatch
 
255
#endif // DOXYGEN_NO_DISPATCH
 
256
 
 
257
 
 
258
// Model 1, using output iterator
 
259
 
 
260
/*!
 
261
    \brief Simplify a geometry
 
262
    \ingroup simplify
 
263
    \details The simplify algorithm removes points, keeping the shape as much as possible.
 
264
    This version of simplify uses an output iterator
 
265
    \param geometry the geometry to be simplified, being a ggl::linestring, vector, iterator pair, or any other boost compatible range
 
266
    \param out output iterator, outputs all simplified points
 
267
    \param max_distance distance (in units of input coordinates) of a vertex to other segments to be removed
 
268
    \par Example:
 
269
    The simplify algorithm can be used as following:
 
270
    \dontinclude doxygen_examples.cpp
 
271
    \skip example_simplify_linestring1
 
272
    \line {
 
273
    \until }
 
274
 */
 
275
template<typename G, typename O>
 
276
inline void simplify(const G& geometry, O out, double max_distance)
 
277
{
 
278
    typedef dispatch::simplify<typename tag<G>::type, G> simplify_type;
 
279
    simplify_type::apply(geometry, out, max_distance);
 
280
}
 
281
 
 
282
/*!
 
283
    \brief Simplify a geometry
 
284
    \ingroup simplify
 
285
    \details The simplify algorithm removes points, keeping the shape as much as possible.
 
286
    This version of simplify uses an output iterator and a simplify strategy
 
287
    \param geometry the geometry to be simplified, being a ggl::linestring, vector, iterator pair, or any other boost compatible range
 
288
    \param out output iterator, outputs all simplified points
 
289
    \param max_distance distance (in units of input coordinates) of a vertex to other segments to be removed
 
290
    \param strategy simplify strategy to be used for simplification, might include point-distance strategy
 
291
    \par Example:
 
292
    The simplify algorithm with strategy can be used as following:
 
293
    \dontinclude doxygen_examples.cpp
 
294
    \skip example_simplify_linestring2
 
295
    \line {
 
296
    \until }
 
297
 */
 
298
template<typename G, typename O, typename S>
 
299
inline void simplify(const G& geometry, O out, double max_distance, S const& strategy)
 
300
{
 
301
    typedef dispatch::simplify<typename tag<G>::type, G> simplify_type;
 
302
    simplify_type::apply(geometry, out, max_distance, strategy);
 
303
}
 
304
 
 
305
// Model 2, where output is same type as input
 
306
 
 
307
/*!
 
308
    \brief Simplify a geometry
 
309
    \ingroup simplify
 
310
    \details This version of simplify simplifies a geometry using the default strategy (Douglas Peucker),
 
311
    where the output is of the same geometry type as the input.
 
312
    \param geometry input geometry, to be simplified
 
313
    \param out output geometry, simplified version of the input geometry
 
314
    \param max_distance distance (in units of input coordinates) of a vertex to other segments to be removed
 
315
 */
 
316
template<typename G>
 
317
inline void simplify(const G& geometry, G& out, double max_distance)
 
318
{
 
319
    typedef dispatch::simplify<typename tag<G>::type, G> simplify_type;
 
320
    simplify_type::apply(geometry, out, max_distance);
 
321
}
 
322
 
 
323
/*!
 
324
    \brief Simplify a geometry
 
325
    \ingroup simplify
 
326
    \details This version of simplify simplifies a geometry using a specified strategy
 
327
    where the output is of the same geometry type as the input.
 
328
    \param geometry input geometry, to be simplified
 
329
    \param out output geometry, simplified version of the input geometry
 
330
    \param max_distance distance (in units of input coordinates) of a vertex to other segments to be removed
 
331
    \param strategy simplify strategy to be used for simplification, might include point-distance strategy
 
332
 */
 
333
template<typename G, typename S>
 
334
inline void simplify(const G& geometry, G& out, double max_distance, S const& strategy)
 
335
{
 
336
    typedef dispatch::simplify<typename tag<G>::type, G> simplify_type;
 
337
    simplify_type::apply(geometry, out, max_distance, strategy);
 
338
}
 
339
 
 
340
} // namespace ggl
 
341
 
 
342
#endif // GGL_ALGORITHMS_SIMPLIFY_HPP