simplify_douglas_peucker.hpp 7.1 KB

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