simplify.hpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691
  1. // Boost.Geometry (aka GGL, Generic Geometry Library)
  2. // Copyright (c) 2007-2015 Barend Gehrels, Amsterdam, the Netherlands.
  3. // Copyright (c) 2008-2015 Bruno Lalande, Paris, France.
  4. // Copyright (c) 2009-2015 Mateusz Loskot, London, UK.
  5. // Parts of Boost.Geometry are redesigned from Geodan's Geographic Library
  6. // (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands.
  7. // Use, modification and distribution is subject to the Boost Software License,
  8. // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
  9. // http://www.boost.org/LICENSE_1_0.txt)
  10. #ifndef BOOST_GEOMETRY_ALGORITHMS_SIMPLIFY_HPP
  11. #define BOOST_GEOMETRY_ALGORITHMS_SIMPLIFY_HPP
  12. #include <cstddef>
  13. #include <boost/core/ignore_unused.hpp>
  14. #include <boost/range.hpp>
  15. #include <boost/variant/apply_visitor.hpp>
  16. #include <boost/variant/static_visitor.hpp>
  17. #include <boost/variant/variant_fwd.hpp>
  18. #include <boost/geometry/core/cs.hpp>
  19. #include <boost/geometry/core/closure.hpp>
  20. #include <boost/geometry/core/exterior_ring.hpp>
  21. #include <boost/geometry/core/interior_rings.hpp>
  22. #include <boost/geometry/core/mutable_range.hpp>
  23. #include <boost/geometry/core/tags.hpp>
  24. #include <boost/geometry/geometries/concepts/check.hpp>
  25. #include <boost/geometry/strategies/agnostic/simplify_douglas_peucker.hpp>
  26. #include <boost/geometry/strategies/concepts/simplify_concept.hpp>
  27. #include <boost/geometry/strategies/default_strategy.hpp>
  28. #include <boost/geometry/strategies/distance.hpp>
  29. #include <boost/geometry/algorithms/area.hpp>
  30. #include <boost/geometry/algorithms/clear.hpp>
  31. #include <boost/geometry/algorithms/convert.hpp>
  32. #include <boost/geometry/algorithms/detail/equals/point_point.hpp>
  33. #include <boost/geometry/algorithms/not_implemented.hpp>
  34. #include <boost/geometry/algorithms/is_empty.hpp>
  35. #include <boost/geometry/algorithms/perimeter.hpp>
  36. #include <boost/geometry/algorithms/detail/distance/default_strategies.hpp>
  37. namespace boost { namespace geometry
  38. {
  39. #ifndef DOXYGEN_NO_DETAIL
  40. namespace detail { namespace simplify
  41. {
  42. template <typename Range>
  43. inline bool is_degenerate(Range const& range)
  44. {
  45. return boost::size(range) == 2
  46. && detail::equals::equals_point_point(geometry::range::front(range),
  47. geometry::range::back(range));
  48. }
  49. struct simplify_range_insert
  50. {
  51. template<typename Range, typename Strategy, typename OutputIterator, typename Distance>
  52. static inline void apply(Range const& range, OutputIterator out,
  53. Distance const& max_distance, Strategy const& strategy)
  54. {
  55. boost::ignore_unused(strategy);
  56. if (is_degenerate(range))
  57. {
  58. std::copy(boost::begin(range), boost::begin(range) + 1, out);
  59. }
  60. else if (boost::size(range) <= 2 || max_distance < 0)
  61. {
  62. std::copy(boost::begin(range), boost::end(range), out);
  63. }
  64. else
  65. {
  66. strategy.apply(range, out, max_distance);
  67. }
  68. }
  69. };
  70. struct simplify_copy
  71. {
  72. template <typename RangeIn, typename RangeOut, typename Strategy, typename Distance>
  73. static inline void apply(RangeIn const& range, RangeOut& out,
  74. Distance const& , Strategy const& )
  75. {
  76. std::copy
  77. (
  78. boost::begin(range), boost::end(range),
  79. geometry::range::back_inserter(out)
  80. );
  81. }
  82. };
  83. template <std::size_t MinimumToUseStrategy>
  84. struct simplify_range
  85. {
  86. template <typename RangeIn, typename RangeOut, typename Strategy, typename Distance>
  87. static inline void apply(RangeIn const& range, RangeOut& out,
  88. Distance const& max_distance, Strategy const& strategy)
  89. {
  90. // For a RING:
  91. // Note that, especially if max_distance is too large,
  92. // the output ring might be self intersecting while the input ring is
  93. // not, although chances are low in normal polygons
  94. if (boost::size(range) <= MinimumToUseStrategy || max_distance < 0)
  95. {
  96. simplify_copy::apply(range, out, max_distance, strategy);
  97. }
  98. else
  99. {
  100. simplify_range_insert::apply
  101. (
  102. range, geometry::range::back_inserter(out), max_distance, strategy
  103. );
  104. }
  105. // Verify the two remaining points are equal. If so, remove one of them.
  106. // This can cause the output being under the minimum size
  107. if (is_degenerate(out))
  108. {
  109. range::resize(out, 1);
  110. }
  111. }
  112. };
  113. struct simplify_ring
  114. {
  115. private :
  116. template <typename Area>
  117. static inline int area_sign(Area const& area)
  118. {
  119. return area > 0 ? 1 : area < 0 ? -1 : 0;
  120. }
  121. template <typename Strategy, typename Ring>
  122. static std::size_t get_opposite(std::size_t index, Ring const& ring)
  123. {
  124. typename Strategy::distance_strategy_type distance_strategy;
  125. // Verify if it is NOT the case that all points are less than the
  126. // simplifying distance. If so, output is empty.
  127. typename Strategy::distance_type max_distance(-1);
  128. typename geometry::point_type<Ring>::type point = range::at(ring, index);
  129. std::size_t i = 0;
  130. for (typename boost::range_iterator<Ring const>::type
  131. it = boost::begin(ring); it != boost::end(ring); ++it, ++i)
  132. {
  133. // This actually is point-segment distance but will result
  134. // in point-point distance
  135. typename Strategy::distance_type dist = distance_strategy.apply(*it, point, point);
  136. if (dist > max_distance)
  137. {
  138. max_distance = dist;
  139. index = i;
  140. }
  141. }
  142. return index;
  143. }
  144. public :
  145. template <typename Ring, typename Strategy, typename Distance>
  146. static inline void apply(Ring const& ring, Ring& out,
  147. Distance const& max_distance, Strategy const& strategy)
  148. {
  149. std::size_t const size = boost::size(ring);
  150. if (size == 0)
  151. {
  152. return;
  153. }
  154. int const input_sign = area_sign(geometry::area(ring));
  155. std::set<std::size_t> visited_indexes;
  156. // Rotate it into a copied vector
  157. // (vector, because source type might not support rotation)
  158. // (duplicate end point will be simplified away)
  159. typedef typename geometry::point_type<Ring>::type point_type;
  160. std::vector<point_type> rotated(size);
  161. // Closing point (but it will not start here)
  162. std::size_t index = 0;
  163. // Iterate (usually one iteration is enough)
  164. for (std::size_t iteration = 0; iteration < 4u; iteration++)
  165. {
  166. // Always take the opposite. Opposite guarantees that no point
  167. // "halfway" is chosen, creating an artefact (very narrow triangle)
  168. // Iteration 0: opposite to closing point (1/2, = on convex hull)
  169. // (this will start simplification with that point
  170. // and its opposite ~0)
  171. // Iteration 1: move a quarter on that ring, then opposite to 1/4
  172. // (with its opposite 3/4)
  173. // Iteration 2: move an eight on that ring, then opposite (1/8)
  174. // Iteration 3: again move a quarter, then opposite (7/8)
  175. // So finally 8 "sides" of the ring have been examined (if it were
  176. // a semi-circle). Most probably, there are only 0 or 1 iterations.
  177. switch (iteration)
  178. {
  179. case 1 : index = (index + size / 4) % size; break;
  180. case 2 : index = (index + size / 8) % size; break;
  181. case 3 : index = (index + size / 4) % size; break;
  182. }
  183. index = get_opposite<Strategy>(index, ring);
  184. if (visited_indexes.count(index) > 0)
  185. {
  186. // Avoid trying the same starting point more than once
  187. continue;
  188. }
  189. std::rotate_copy(boost::begin(ring), range::pos(ring, index),
  190. boost::end(ring), rotated.begin());
  191. // Close the rotated copy
  192. rotated.push_back(range::at(ring, index));
  193. simplify_range<0>::apply(rotated, out, max_distance, strategy);
  194. // Verify that what was positive, stays positive (or goes to 0)
  195. // and what was negative stays negative (or goes to 0)
  196. int const output_sign = area_sign(geometry::area(out));
  197. if (output_sign == input_sign)
  198. {
  199. // Result is considered as satisfactory (usually this is the
  200. // first iteration - only for small rings, having a scale
  201. // similar to simplify_distance, next iterations are tried
  202. return;
  203. }
  204. // Original is simplified away. Possibly there is a solution
  205. // when another starting point is used
  206. geometry::clear(out);
  207. if (iteration == 0
  208. && geometry::perimeter(ring) < 3 * max_distance)
  209. {
  210. // Check if it is useful to iterate. A minimal triangle has a
  211. // perimeter of a bit more than 3 times the simplify distance
  212. return;
  213. }
  214. // Prepare next try
  215. visited_indexes.insert(index);
  216. rotated.resize(size);
  217. }
  218. }
  219. };
  220. struct simplify_polygon
  221. {
  222. private:
  223. template
  224. <
  225. typename IteratorIn,
  226. typename InteriorRingsOut,
  227. typename Distance,
  228. typename Strategy
  229. >
  230. static inline void iterate(IteratorIn begin, IteratorIn end,
  231. InteriorRingsOut& interior_rings_out,
  232. Distance const& max_distance, Strategy const& strategy)
  233. {
  234. typedef typename boost::range_value<InteriorRingsOut>::type single_type;
  235. for (IteratorIn it = begin; it != end; ++it)
  236. {
  237. single_type out;
  238. simplify_ring::apply(*it, out, max_distance, strategy);
  239. if (! geometry::is_empty(out))
  240. {
  241. range::push_back(interior_rings_out, out);
  242. }
  243. }
  244. }
  245. template
  246. <
  247. typename InteriorRingsIn,
  248. typename InteriorRingsOut,
  249. typename Distance,
  250. typename Strategy
  251. >
  252. static inline void apply_interior_rings(
  253. InteriorRingsIn const& interior_rings_in,
  254. InteriorRingsOut& interior_rings_out,
  255. Distance const& max_distance, Strategy const& strategy)
  256. {
  257. range::clear(interior_rings_out);
  258. iterate(
  259. boost::begin(interior_rings_in), boost::end(interior_rings_in),
  260. interior_rings_out,
  261. max_distance, strategy);
  262. }
  263. public:
  264. template <typename Polygon, typename Strategy, typename Distance>
  265. static inline void apply(Polygon const& poly_in, Polygon& poly_out,
  266. Distance const& max_distance, Strategy const& strategy)
  267. {
  268. // Note that if there are inner rings, and distance is too large,
  269. // they might intersect with the outer ring in the output,
  270. // while it didn't in the input.
  271. simplify_ring::apply(exterior_ring(poly_in), exterior_ring(poly_out),
  272. max_distance, strategy);
  273. apply_interior_rings(interior_rings(poly_in),
  274. interior_rings(poly_out), max_distance, strategy);
  275. }
  276. };
  277. template<typename Policy>
  278. struct simplify_multi
  279. {
  280. template <typename MultiGeometry, typename Strategy, typename Distance>
  281. static inline void apply(MultiGeometry const& multi, MultiGeometry& out,
  282. Distance const& max_distance, Strategy const& strategy)
  283. {
  284. range::clear(out);
  285. typedef typename boost::range_value<MultiGeometry>::type single_type;
  286. for (typename boost::range_iterator<MultiGeometry const>::type
  287. it = boost::begin(multi); it != boost::end(multi); ++it)
  288. {
  289. single_type single_out;
  290. Policy::apply(*it, single_out, max_distance, strategy);
  291. if (! geometry::is_empty(single_out))
  292. {
  293. range::push_back(out, single_out);
  294. }
  295. }
  296. }
  297. };
  298. }} // namespace detail::simplify
  299. #endif // DOXYGEN_NO_DETAIL
  300. #ifndef DOXYGEN_NO_DISPATCH
  301. namespace dispatch
  302. {
  303. template
  304. <
  305. typename Geometry,
  306. typename Tag = typename tag<Geometry>::type
  307. >
  308. struct simplify: not_implemented<Tag>
  309. {};
  310. template <typename Point>
  311. struct simplify<Point, point_tag>
  312. {
  313. template <typename Distance, typename Strategy>
  314. static inline void apply(Point const& point, Point& out,
  315. Distance const& , Strategy const& )
  316. {
  317. geometry::convert(point, out);
  318. }
  319. };
  320. // Linestring, keep 2 points (unless those points are the same)
  321. template <typename Linestring>
  322. struct simplify<Linestring, linestring_tag>
  323. : detail::simplify::simplify_range<2>
  324. {};
  325. template <typename Ring>
  326. struct simplify<Ring, ring_tag>
  327. : detail::simplify::simplify_ring
  328. {};
  329. template <typename Polygon>
  330. struct simplify<Polygon, polygon_tag>
  331. : detail::simplify::simplify_polygon
  332. {};
  333. template
  334. <
  335. typename Geometry,
  336. typename Tag = typename tag<Geometry>::type
  337. >
  338. struct simplify_insert: not_implemented<Tag>
  339. {};
  340. template <typename Linestring>
  341. struct simplify_insert<Linestring, linestring_tag>
  342. : detail::simplify::simplify_range_insert
  343. {};
  344. template <typename Ring>
  345. struct simplify_insert<Ring, ring_tag>
  346. : detail::simplify::simplify_range_insert
  347. {};
  348. template <typename MultiPoint>
  349. struct simplify<MultiPoint, multi_point_tag>
  350. : detail::simplify::simplify_copy
  351. {};
  352. template <typename MultiLinestring>
  353. struct simplify<MultiLinestring, multi_linestring_tag>
  354. : detail::simplify::simplify_multi<detail::simplify::simplify_range<2> >
  355. {};
  356. template <typename MultiPolygon>
  357. struct simplify<MultiPolygon, multi_polygon_tag>
  358. : detail::simplify::simplify_multi<detail::simplify::simplify_polygon>
  359. {};
  360. } // namespace dispatch
  361. #endif // DOXYGEN_NO_DISPATCH
  362. namespace resolve_strategy
  363. {
  364. struct simplify
  365. {
  366. template <typename Geometry, typename Distance, typename Strategy>
  367. static inline void apply(Geometry const& geometry,
  368. Geometry& out,
  369. Distance const& max_distance,
  370. Strategy const& strategy)
  371. {
  372. dispatch::simplify<Geometry>::apply(geometry, out, max_distance, strategy);
  373. }
  374. template <typename Geometry, typename Distance>
  375. static inline void apply(Geometry const& geometry,
  376. Geometry& out,
  377. Distance const& max_distance,
  378. default_strategy)
  379. {
  380. typedef typename point_type<Geometry>::type point_type;
  381. typedef typename strategy::distance::services::default_strategy
  382. <
  383. point_tag, segment_tag, point_type
  384. >::type ds_strategy_type;
  385. typedef strategy::simplify::douglas_peucker
  386. <
  387. point_type, ds_strategy_type
  388. > strategy_type;
  389. BOOST_CONCEPT_ASSERT(
  390. (concepts::SimplifyStrategy<strategy_type, point_type>)
  391. );
  392. apply(geometry, out, max_distance, strategy_type());
  393. }
  394. };
  395. struct simplify_insert
  396. {
  397. template
  398. <
  399. typename Geometry,
  400. typename OutputIterator,
  401. typename Distance,
  402. typename Strategy
  403. >
  404. static inline void apply(Geometry const& geometry,
  405. OutputIterator& out,
  406. Distance const& max_distance,
  407. Strategy const& strategy)
  408. {
  409. dispatch::simplify_insert<Geometry>::apply(geometry, out, max_distance, strategy);
  410. }
  411. template <typename Geometry, typename OutputIterator, typename Distance>
  412. static inline void apply(Geometry const& geometry,
  413. OutputIterator& out,
  414. Distance const& max_distance,
  415. default_strategy)
  416. {
  417. typedef typename point_type<Geometry>::type point_type;
  418. typedef typename strategy::distance::services::default_strategy
  419. <
  420. point_tag, segment_tag, point_type
  421. >::type ds_strategy_type;
  422. typedef strategy::simplify::douglas_peucker
  423. <
  424. point_type, ds_strategy_type
  425. > strategy_type;
  426. BOOST_CONCEPT_ASSERT(
  427. (concepts::SimplifyStrategy<strategy_type, point_type>)
  428. );
  429. apply(geometry, out, max_distance, strategy_type());
  430. }
  431. };
  432. } // namespace resolve_strategy
  433. namespace resolve_variant {
  434. template <typename Geometry>
  435. struct simplify
  436. {
  437. template <typename Distance, typename Strategy>
  438. static inline void apply(Geometry const& geometry,
  439. Geometry& out,
  440. Distance const& max_distance,
  441. Strategy const& strategy)
  442. {
  443. resolve_strategy::simplify::apply(geometry, out, max_distance, strategy);
  444. }
  445. };
  446. template <BOOST_VARIANT_ENUM_PARAMS(typename T)>
  447. struct simplify<boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)> >
  448. {
  449. template <typename Distance, typename Strategy>
  450. struct visitor: boost::static_visitor<void>
  451. {
  452. Distance const& m_max_distance;
  453. Strategy const& m_strategy;
  454. visitor(Distance const& max_distance, Strategy const& strategy)
  455. : m_max_distance(max_distance)
  456. , m_strategy(strategy)
  457. {}
  458. template <typename Geometry>
  459. void operator()(Geometry const& geometry, Geometry& out) const
  460. {
  461. simplify<Geometry>::apply(geometry, out, m_max_distance, m_strategy);
  462. }
  463. };
  464. template <typename Distance, typename Strategy>
  465. static inline void
  466. apply(boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)> const& geometry,
  467. boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)>& out,
  468. Distance const& max_distance,
  469. Strategy const& strategy)
  470. {
  471. boost::apply_visitor(
  472. visitor<Distance, Strategy>(max_distance, strategy),
  473. geometry,
  474. out
  475. );
  476. }
  477. };
  478. } // namespace resolve_variant
  479. /*!
  480. \brief Simplify a geometry using a specified strategy
  481. \ingroup simplify
  482. \tparam Geometry \tparam_geometry
  483. \tparam Distance A numerical distance measure
  484. \tparam Strategy A type fulfilling a SimplifyStrategy concept
  485. \param strategy A strategy to calculate simplification
  486. \param geometry input geometry, to be simplified
  487. \param out output geometry, simplified version of the input geometry
  488. \param max_distance distance (in units of input coordinates) of a vertex
  489. to other segments to be removed
  490. \param strategy simplify strategy to be used for simplification, might
  491. include point-distance strategy
  492. \image html svg_simplify_country.png "The image below presents the simplified country"
  493. \qbk{distinguish,with strategy}
  494. */
  495. template<typename Geometry, typename Distance, typename Strategy>
  496. inline void simplify(Geometry const& geometry, Geometry& out,
  497. Distance const& max_distance, Strategy const& strategy)
  498. {
  499. concepts::check<Geometry>();
  500. geometry::clear(out);
  501. resolve_variant::simplify<Geometry>::apply(geometry, out, max_distance, strategy);
  502. }
  503. /*!
  504. \brief Simplify a geometry
  505. \ingroup simplify
  506. \tparam Geometry \tparam_geometry
  507. \tparam Distance \tparam_numeric
  508. \note This version of simplify simplifies a geometry using the default
  509. strategy (Douglas Peucker),
  510. \param geometry input geometry, to be simplified
  511. \param out output geometry, simplified version of the input geometry
  512. \param max_distance distance (in units of input coordinates) of a vertex
  513. to other segments to be removed
  514. \qbk{[include reference/algorithms/simplify.qbk]}
  515. */
  516. template<typename Geometry, typename Distance>
  517. inline void simplify(Geometry const& geometry, Geometry& out,
  518. Distance const& max_distance)
  519. {
  520. concepts::check<Geometry>();
  521. geometry::simplify(geometry, out, max_distance, default_strategy());
  522. }
  523. #ifndef DOXYGEN_NO_DETAIL
  524. namespace detail { namespace simplify
  525. {
  526. /*!
  527. \brief Simplify a geometry, using an output iterator
  528. and a specified strategy
  529. \ingroup simplify
  530. \tparam Geometry \tparam_geometry
  531. \param geometry input geometry, to be simplified
  532. \param out output iterator, outputs all simplified points
  533. \param max_distance distance (in units of input coordinates) of a vertex
  534. to other segments to be removed
  535. \param strategy simplify strategy to be used for simplification,
  536. might include point-distance strategy
  537. \qbk{distinguish,with strategy}
  538. \qbk{[include reference/algorithms/simplify.qbk]}
  539. */
  540. template<typename Geometry, typename OutputIterator, typename Distance, typename Strategy>
  541. inline void simplify_insert(Geometry const& geometry, OutputIterator out,
  542. Distance const& max_distance, Strategy const& strategy)
  543. {
  544. concepts::check<Geometry const>();
  545. resolve_strategy::simplify_insert::apply(geometry, out, max_distance, strategy);
  546. }
  547. /*!
  548. \brief Simplify a geometry, using an output iterator
  549. \ingroup simplify
  550. \tparam Geometry \tparam_geometry
  551. \param geometry input geometry, to be simplified
  552. \param out output iterator, outputs all simplified points
  553. \param max_distance distance (in units of input coordinates) of a vertex
  554. to other segments to be removed
  555. \qbk{[include reference/algorithms/simplify_insert.qbk]}
  556. */
  557. template<typename Geometry, typename OutputIterator, typename Distance>
  558. inline void simplify_insert(Geometry const& geometry, OutputIterator out,
  559. Distance const& max_distance)
  560. {
  561. // Concept: output point type = point type of input geometry
  562. concepts::check<Geometry const>();
  563. concepts::check<typename point_type<Geometry>::type>();
  564. simplify_insert(geometry, out, max_distance, default_strategy());
  565. }
  566. }} // namespace detail::simplify
  567. #endif // DOXYGEN_NO_DETAIL
  568. }} // namespace boost::geometry
  569. #endif // BOOST_GEOMETRY_ALGORITHMS_SIMPLIFY_HPP