summaryrefslogtreecommitdiff
path: root/boost/geometry/strategies/agnostic/simplify_douglas_peucker.hpp
blob: 4a1a22d1cf8da23ce1567fcf60152eac24e75522 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
// Boost.Geometry (aka GGL, Generic Geometry Library)

// Copyright (c) 1995, 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
// Copyright (c) 1995 Maarten Hilferink, Amsterdam, the Netherlands

// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library
// (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands.

// Use, modification and distribution is subject to the Boost Software License,
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

#ifndef BOOST_GEOMETRY_STRATEGY_AGNOSTIC_SIMPLIFY_DOUGLAS_PEUCKER_HPP
#define BOOST_GEOMETRY_STRATEGY_AGNOSTIC_SIMPLIFY_DOUGLAS_PEUCKER_HPP


#include <cstddef>
#include <vector>

#include <boost/range.hpp>

#include <boost/geometry/core/cs.hpp>
#include <boost/geometry/strategies/distance.hpp>



//#define GL_DEBUG_DOUGLAS_PEUCKER

#ifdef GL_DEBUG_DOUGLAS_PEUCKER
#include <boost/geometry/io/dsv/write.hpp>
#endif


namespace boost { namespace geometry
{

namespace strategy { namespace simplify
{


#ifndef DOXYGEN_NO_DETAIL
namespace detail
{

    /*!
        \brief Small wrapper around a point, with an extra member "included"
        \details
            It has a const-reference to the original point (so no copy here)
        \tparam the enclosed point type
    */
    template<typename Point>
    struct douglas_peucker_point
    {
        Point const& p;
        bool included;

        inline douglas_peucker_point(Point const& ap)
            : p(ap)
            , included(false)
        {}

        // Necessary for proper compilation
        inline douglas_peucker_point<Point> operator=(douglas_peucker_point<Point> const& )
        {
            return douglas_peucker_point<Point>(*this);
        }
    };
}
#endif // DOXYGEN_NO_DETAIL


/*!
\brief Implements the simplify algorithm.
\ingroup strategies
\details The douglas_peucker strategy simplifies a linestring, ring or
    vector of points using the well-known Douglas-Peucker algorithm.
    For the algorithm, see for example:
\see http://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm
\see http://www2.dcs.hull.ac.uk/CISRG/projects/Royal-Inst/demos/dp.html
\tparam Point the point type
\tparam PointDistanceStrategy point-segment distance strategy to be used
\note This strategy uses itself a point-segment-distance strategy which
    can be specified
\author Barend and Maarten, 1995/1996
\author Barend, revised for Generic Geometry Library, 2008
*/
template
<
    typename Point,
    typename PointDistanceStrategy
>
class douglas_peucker
{
public :

	// See also ticket 5954 https://svn.boost.org/trac/boost/ticket/5954
	// Comparable is currently not possible here because it has to be compared to the squared of max_distance, and more.
	// For now we have to take the real distance.
	typedef PointDistanceStrategy distance_strategy_type;
    // typedef typename strategy::distance::services::comparable_type<PointDistanceStrategy>::type distance_strategy_type;

    typedef typename strategy::distance::services::return_type<distance_strategy_type>::type return_type;

private :
    typedef detail::douglas_peucker_point<Point> dp_point_type;
    typedef typename std::vector<dp_point_type>::iterator iterator_type;


    static inline void consider(iterator_type begin,
                iterator_type end,
                return_type const& max_dist, int& n,
                distance_strategy_type const& ps_distance_strategy)
    {
        std::size_t size = end - begin;

        // size must be at least 3
        // because we want to consider a candidate point in between
        if (size <= 2)
        {
#ifdef GL_DEBUG_DOUGLAS_PEUCKER
            if (begin != end)
            {
                std::cout << "ignore between " << dsv(begin->p)
                    << " and " << dsv((end - 1)->p)
                    << " size=" << size << std::endl;
            }
            std::cout << "return because size=" << size << std::endl;
#endif
            return;
        }

        iterator_type last = end - 1;

#ifdef GL_DEBUG_DOUGLAS_PEUCKER
        std::cout << "find between " << dsv(begin->p)
            << " and " << dsv(last->p)
            << " size=" << size << std::endl;
#endif


        // Find most far point, compare to the current segment
        //geometry::segment<Point const> s(begin->p, last->p);
        return_type md(-1.0); // any value < 0
        iterator_type candidate;
        for(iterator_type it = begin + 1; it != last; ++it)
        {
            return_type dist = ps_distance_strategy.apply(it->p, begin->p, last->p);

#ifdef GL_DEBUG_DOUGLAS_PEUCKER
            std::cout << "consider " << dsv(it->p)
                << " at " << double(dist)
                << ((dist > max_dist) ? " maybe" : " no") 
                << std::endl;

#endif
            if (dist > md)
            {
                md = dist;
                candidate = it;
            }
        }

        // If a point is found, set the include flag
        // and handle segments in between recursively
        if (md > max_dist)
        {
#ifdef GL_DEBUG_DOUGLAS_PEUCKER
            std::cout << "use " << dsv(candidate->p) << std::endl;
#endif

            candidate->included = true;
            n++;

            consider(begin, candidate + 1, max_dist, n, ps_distance_strategy);
            consider(candidate, end, max_dist, n, ps_distance_strategy);
        }
    }


public :

    template <typename Range, typename OutputIterator>
    static inline OutputIterator apply(Range const& range,
                    OutputIterator out, double max_distance)
    {
        distance_strategy_type strategy;

        // Copy coordinates, a vector of references to all points
        std::vector<dp_point_type> ref_candidates(boost::begin(range),
                        boost::end(range));

        // Include first and last point of line,
        // they are always part of the line
        int n = 2;
        ref_candidates.front().included = true;
        ref_candidates.back().included = true;

        // Get points, recursively, including them if they are further away
        // than the specified distance
        typedef typename strategy::distance::services::return_type<distance_strategy_type>::type return_type;

        consider(boost::begin(ref_candidates), boost::end(ref_candidates), max_distance, n, strategy);

        // Copy included elements to the output
        for(typename std::vector<dp_point_type>::const_iterator it
                        = boost::begin(ref_candidates);
            it != boost::end(ref_candidates);
            ++it)
        {
            if (it->included)
            {
                // copy-coordinates does not work because OutputIterator
                // does not model Point (??)
                //geometry::convert(it->p, *out);
                *out = it->p;
                out++;
            }
        }
        return out;
    }

};

}} // namespace strategy::simplify


}} // namespace boost::geometry

#endif // BOOST_GEOMETRY_STRATEGY_AGNOSTIC_SIMPLIFY_DOUGLAS_PEUCKER_HPP