simplify_douglas_peucker.hpp 9.9 KB

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