simplify.hpp 22 KB

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