centroid.hpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681
  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. // Copyright (c) 2014-2017 Adam Wulkiewicz, Lodz, Poland.
  6. // This file was modified by Oracle on 2014, 2015.
  7. // Modifications copyright (c) 2014-2015 Oracle and/or its affiliates.
  8. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
  9. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle
  10. // Parts of Boost.Geometry are redesigned from Geodan's Geographic Library
  11. // (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands.
  12. // Use, modification and distribution is subject to the Boost Software License,
  13. // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
  14. // http://www.boost.org/LICENSE_1_0.txt)
  15. #ifndef BOOST_GEOMETRY_ALGORITHMS_CENTROID_HPP
  16. #define BOOST_GEOMETRY_ALGORITHMS_CENTROID_HPP
  17. #include <cstddef>
  18. #include <boost/core/ignore_unused.hpp>
  19. #include <boost/range.hpp>
  20. #include <boost/throw_exception.hpp>
  21. #include <boost/variant/apply_visitor.hpp>
  22. #include <boost/variant/static_visitor.hpp>
  23. #include <boost/variant/variant_fwd.hpp>
  24. #include <boost/geometry/core/closure.hpp>
  25. #include <boost/geometry/core/cs.hpp>
  26. #include <boost/geometry/core/coordinate_dimension.hpp>
  27. #include <boost/geometry/core/exception.hpp>
  28. #include <boost/geometry/core/exterior_ring.hpp>
  29. #include <boost/geometry/core/interior_rings.hpp>
  30. #include <boost/geometry/core/tag_cast.hpp>
  31. #include <boost/geometry/core/tags.hpp>
  32. #include <boost/geometry/core/point_type.hpp>
  33. #include <boost/geometry/geometries/concepts/check.hpp>
  34. #include <boost/geometry/algorithms/assign.hpp>
  35. #include <boost/geometry/algorithms/convert.hpp>
  36. #include <boost/geometry/algorithms/detail/interior_iterator.hpp>
  37. #include <boost/geometry/algorithms/detail/point_on_border.hpp>
  38. #include <boost/geometry/algorithms/not_implemented.hpp>
  39. #include <boost/geometry/strategies/centroid.hpp>
  40. #include <boost/geometry/strategies/concepts/centroid_concept.hpp>
  41. #include <boost/geometry/strategies/default_strategy.hpp>
  42. #include <boost/geometry/views/closeable_view.hpp>
  43. #include <boost/geometry/util/for_each_coordinate.hpp>
  44. #include <boost/geometry/util/select_coordinate_type.hpp>
  45. #include <boost/geometry/algorithms/is_empty.hpp>
  46. #include <boost/geometry/algorithms/detail/centroid/translating_transformer.hpp>
  47. namespace boost { namespace geometry
  48. {
  49. #if ! defined(BOOST_GEOMETRY_CENTROID_NO_THROW)
  50. /*!
  51. \brief Centroid Exception
  52. \ingroup centroid
  53. \details The centroid_exception is thrown if the free centroid function is called with
  54. geometries for which the centroid cannot be calculated. For example: a linestring
  55. without points, a polygon without points, an empty multi-geometry.
  56. \qbk{
  57. [heading See also]
  58. \* [link geometry.reference.algorithms.centroid the centroid function]
  59. }
  60. */
  61. class centroid_exception : public geometry::exception
  62. {
  63. public:
  64. /*!
  65. \brief The default constructor
  66. */
  67. inline centroid_exception() {}
  68. /*!
  69. \brief Returns the explanatory string.
  70. \return Pointer to a null-terminated string with explanatory information.
  71. */
  72. virtual char const* what() const throw()
  73. {
  74. return "Boost.Geometry Centroid calculation exception";
  75. }
  76. };
  77. #endif
  78. #ifndef DOXYGEN_NO_DETAIL
  79. namespace detail { namespace centroid
  80. {
  81. struct centroid_point
  82. {
  83. template<typename Point, typename PointCentroid, typename Strategy>
  84. static inline void apply(Point const& point, PointCentroid& centroid,
  85. Strategy const&)
  86. {
  87. geometry::convert(point, centroid);
  88. }
  89. };
  90. template
  91. <
  92. typename Indexed,
  93. typename Point,
  94. std::size_t Dimension = 0,
  95. std::size_t DimensionCount = dimension<Indexed>::type::value
  96. >
  97. struct centroid_indexed_calculator
  98. {
  99. typedef typename select_coordinate_type
  100. <
  101. Indexed, Point
  102. >::type coordinate_type;
  103. static inline void apply(Indexed const& indexed, Point& centroid)
  104. {
  105. coordinate_type const c1 = get<min_corner, Dimension>(indexed);
  106. coordinate_type const c2 = get<max_corner, Dimension>(indexed);
  107. coordinate_type m = c1 + c2;
  108. coordinate_type const two = 2;
  109. m /= two;
  110. set<Dimension>(centroid, m);
  111. centroid_indexed_calculator
  112. <
  113. Indexed, Point, Dimension + 1
  114. >::apply(indexed, centroid);
  115. }
  116. };
  117. template<typename Indexed, typename Point, std::size_t DimensionCount>
  118. struct centroid_indexed_calculator<Indexed, Point, DimensionCount, DimensionCount>
  119. {
  120. static inline void apply(Indexed const& , Point& )
  121. {
  122. }
  123. };
  124. struct centroid_indexed
  125. {
  126. template<typename Indexed, typename Point, typename Strategy>
  127. static inline void apply(Indexed const& indexed, Point& centroid,
  128. Strategy const&)
  129. {
  130. centroid_indexed_calculator
  131. <
  132. Indexed, Point
  133. >::apply(indexed, centroid);
  134. }
  135. };
  136. // There is one thing where centroid is different from e.g. within.
  137. // If the ring has only one point, it might make sense that
  138. // that point is the centroid.
  139. template<typename Point, typename Range>
  140. inline bool range_ok(Range const& range, Point& centroid)
  141. {
  142. std::size_t const n = boost::size(range);
  143. if (n > 1)
  144. {
  145. return true;
  146. }
  147. else if (n <= 0)
  148. {
  149. #if ! defined(BOOST_GEOMETRY_CENTROID_NO_THROW)
  150. BOOST_THROW_EXCEPTION(centroid_exception());
  151. #else
  152. return false;
  153. #endif
  154. }
  155. else // if (n == 1)
  156. {
  157. // Take over the first point in a "coordinate neutral way"
  158. geometry::convert(*boost::begin(range), centroid);
  159. return false;
  160. }
  161. //return true; // unreachable
  162. }
  163. /*!
  164. \brief Calculate the centroid of a Ring or a Linestring.
  165. */
  166. template <closure_selector Closure>
  167. struct centroid_range_state
  168. {
  169. template<typename Ring, typename PointTransformer, typename Strategy>
  170. static inline void apply(Ring const& ring,
  171. PointTransformer const& transformer,
  172. Strategy const& strategy,
  173. typename Strategy::state_type& state)
  174. {
  175. boost::ignore_unused(strategy);
  176. typedef typename geometry::point_type<Ring const>::type point_type;
  177. typedef typename closeable_view<Ring const, Closure>::type view_type;
  178. typedef typename boost::range_iterator<view_type const>::type iterator_type;
  179. view_type view(ring);
  180. iterator_type it = boost::begin(view);
  181. iterator_type end = boost::end(view);
  182. if (it != end)
  183. {
  184. typename PointTransformer::result_type
  185. previous_pt = transformer.apply(*it);
  186. for ( ++it ; it != end ; ++it)
  187. {
  188. typename PointTransformer::result_type
  189. pt = transformer.apply(*it);
  190. strategy.apply(static_cast<point_type const&>(previous_pt),
  191. static_cast<point_type const&>(pt),
  192. state);
  193. previous_pt = pt;
  194. }
  195. }
  196. }
  197. };
  198. template <closure_selector Closure>
  199. struct centroid_range
  200. {
  201. template<typename Range, typename Point, typename Strategy>
  202. static inline bool apply(Range const& range, Point& centroid,
  203. Strategy const& strategy)
  204. {
  205. if (range_ok(range, centroid))
  206. {
  207. // prepare translation transformer
  208. translating_transformer<Range> transformer(*boost::begin(range));
  209. typename Strategy::state_type state;
  210. centroid_range_state<Closure>::apply(range, transformer,
  211. strategy, state);
  212. if ( strategy.result(state, centroid) )
  213. {
  214. // translate the result back
  215. transformer.apply_reverse(centroid);
  216. return true;
  217. }
  218. }
  219. return false;
  220. }
  221. };
  222. /*!
  223. \brief Centroid of a polygon.
  224. \note Because outer ring is clockwise, inners are counter clockwise,
  225. triangle approach is OK and works for polygons with rings.
  226. */
  227. struct centroid_polygon_state
  228. {
  229. template<typename Polygon, typename PointTransformer, typename Strategy>
  230. static inline void apply(Polygon const& poly,
  231. PointTransformer const& transformer,
  232. Strategy const& strategy,
  233. typename Strategy::state_type& state)
  234. {
  235. typedef typename ring_type<Polygon>::type ring_type;
  236. typedef centroid_range_state<geometry::closure<ring_type>::value> per_ring;
  237. per_ring::apply(exterior_ring(poly), transformer, strategy, state);
  238. typename interior_return_type<Polygon const>::type
  239. rings = interior_rings(poly);
  240. for (typename detail::interior_iterator<Polygon const>::type
  241. it = boost::begin(rings); it != boost::end(rings); ++it)
  242. {
  243. per_ring::apply(*it, transformer, strategy, state);
  244. }
  245. }
  246. };
  247. struct centroid_polygon
  248. {
  249. template<typename Polygon, typename Point, typename Strategy>
  250. static inline bool apply(Polygon const& poly, Point& centroid,
  251. Strategy const& strategy)
  252. {
  253. if (range_ok(exterior_ring(poly), centroid))
  254. {
  255. // prepare translation transformer
  256. translating_transformer<Polygon>
  257. transformer(*boost::begin(exterior_ring(poly)));
  258. typename Strategy::state_type state;
  259. centroid_polygon_state::apply(poly, transformer, strategy, state);
  260. if ( strategy.result(state, centroid) )
  261. {
  262. // translate the result back
  263. transformer.apply_reverse(centroid);
  264. return true;
  265. }
  266. }
  267. return false;
  268. }
  269. };
  270. /*!
  271. \brief Building block of a multi-point, to be used as Policy in the
  272. more generec centroid_multi
  273. */
  274. struct centroid_multi_point_state
  275. {
  276. template <typename Point, typename PointTransformer, typename Strategy>
  277. static inline void apply(Point const& point,
  278. PointTransformer const& transformer,
  279. Strategy const& strategy,
  280. typename Strategy::state_type& state)
  281. {
  282. boost::ignore_unused(strategy);
  283. strategy.apply(static_cast<Point const&>(transformer.apply(point)),
  284. state);
  285. }
  286. };
  287. /*!
  288. \brief Generic implementation which calls a policy to calculate the
  289. centroid of the total of its single-geometries
  290. \details The Policy is, in general, the single-version, with state. So
  291. detail::centroid::centroid_polygon_state is used as a policy for this
  292. detail::centroid::centroid_multi
  293. */
  294. template <typename Policy>
  295. struct centroid_multi
  296. {
  297. template <typename Multi, typename Point, typename Strategy>
  298. static inline bool apply(Multi const& multi,
  299. Point& centroid,
  300. Strategy const& strategy)
  301. {
  302. #if ! defined(BOOST_GEOMETRY_CENTROID_NO_THROW)
  303. // If there is nothing in any of the ranges, it is not possible
  304. // to calculate the centroid
  305. if (geometry::is_empty(multi))
  306. {
  307. BOOST_THROW_EXCEPTION(centroid_exception());
  308. }
  309. #endif
  310. // prepare translation transformer
  311. translating_transformer<Multi> transformer(multi);
  312. typename Strategy::state_type state;
  313. for (typename boost::range_iterator<Multi const>::type
  314. it = boost::begin(multi);
  315. it != boost::end(multi);
  316. ++it)
  317. {
  318. Policy::apply(*it, transformer, strategy, state);
  319. }
  320. if ( strategy.result(state, centroid) )
  321. {
  322. // translate the result back
  323. transformer.apply_reverse(centroid);
  324. return true;
  325. }
  326. return false;
  327. }
  328. };
  329. template <typename Algorithm>
  330. struct centroid_linear_areal
  331. {
  332. template <typename Geometry, typename Point, typename Strategy>
  333. static inline void apply(Geometry const& geom,
  334. Point& centroid,
  335. Strategy const& strategy)
  336. {
  337. if ( ! Algorithm::apply(geom, centroid, strategy) )
  338. {
  339. geometry::point_on_border(centroid, geom);
  340. }
  341. }
  342. };
  343. }} // namespace detail::centroid
  344. #endif // DOXYGEN_NO_DETAIL
  345. #ifndef DOXYGEN_NO_DISPATCH
  346. namespace dispatch
  347. {
  348. template
  349. <
  350. typename Geometry,
  351. typename Tag = typename tag<Geometry>::type
  352. >
  353. struct centroid: not_implemented<Tag>
  354. {};
  355. template <typename Geometry>
  356. struct centroid<Geometry, point_tag>
  357. : detail::centroid::centroid_point
  358. {};
  359. template <typename Box>
  360. struct centroid<Box, box_tag>
  361. : detail::centroid::centroid_indexed
  362. {};
  363. template <typename Segment>
  364. struct centroid<Segment, segment_tag>
  365. : detail::centroid::centroid_indexed
  366. {};
  367. template <typename Ring>
  368. struct centroid<Ring, ring_tag>
  369. : detail::centroid::centroid_linear_areal
  370. <
  371. detail::centroid::centroid_range<geometry::closure<Ring>::value>
  372. >
  373. {};
  374. template <typename Linestring>
  375. struct centroid<Linestring, linestring_tag>
  376. : detail::centroid::centroid_linear_areal
  377. <
  378. detail::centroid::centroid_range<closed>
  379. >
  380. {};
  381. template <typename Polygon>
  382. struct centroid<Polygon, polygon_tag>
  383. : detail::centroid::centroid_linear_areal
  384. <
  385. detail::centroid::centroid_polygon
  386. >
  387. {};
  388. template <typename MultiLinestring>
  389. struct centroid<MultiLinestring, multi_linestring_tag>
  390. : detail::centroid::centroid_linear_areal
  391. <
  392. detail::centroid::centroid_multi
  393. <
  394. detail::centroid::centroid_range_state<closed>
  395. >
  396. >
  397. {};
  398. template <typename MultiPolygon>
  399. struct centroid<MultiPolygon, multi_polygon_tag>
  400. : detail::centroid::centroid_linear_areal
  401. <
  402. detail::centroid::centroid_multi
  403. <
  404. detail::centroid::centroid_polygon_state
  405. >
  406. >
  407. {};
  408. template <typename MultiPoint>
  409. struct centroid<MultiPoint, multi_point_tag>
  410. : detail::centroid::centroid_multi
  411. <
  412. detail::centroid::centroid_multi_point_state
  413. >
  414. {};
  415. } // namespace dispatch
  416. #endif // DOXYGEN_NO_DISPATCH
  417. namespace resolve_strategy {
  418. template <typename Geometry>
  419. struct centroid
  420. {
  421. template <typename Point, typename Strategy>
  422. static inline void apply(Geometry const& geometry, Point& out, Strategy const& strategy)
  423. {
  424. dispatch::centroid<Geometry>::apply(geometry, out, strategy);
  425. }
  426. template <typename Point>
  427. static inline void apply(Geometry const& geometry, Point& out, default_strategy)
  428. {
  429. typedef typename strategy::centroid::services::default_strategy
  430. <
  431. typename cs_tag<Geometry>::type,
  432. typename tag_cast
  433. <
  434. typename tag<Geometry>::type,
  435. pointlike_tag,
  436. linear_tag,
  437. areal_tag
  438. >::type,
  439. dimension<Geometry>::type::value,
  440. Point,
  441. Geometry
  442. >::type strategy_type;
  443. dispatch::centroid<Geometry>::apply(geometry, out, strategy_type());
  444. }
  445. };
  446. } // namespace resolve_strategy
  447. namespace resolve_variant {
  448. template <typename Geometry>
  449. struct centroid
  450. {
  451. template <typename Point, typename Strategy>
  452. static inline void apply(Geometry const& geometry, Point& out, Strategy const& strategy)
  453. {
  454. concepts::check_concepts_and_equal_dimensions<Point, Geometry const>();
  455. resolve_strategy::centroid<Geometry>::apply(geometry, out, strategy);
  456. }
  457. };
  458. template <BOOST_VARIANT_ENUM_PARAMS(typename T)>
  459. struct centroid<boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)> >
  460. {
  461. template <typename Point, typename Strategy>
  462. struct visitor: boost::static_visitor<void>
  463. {
  464. Point& m_out;
  465. Strategy const& m_strategy;
  466. visitor(Point& out, Strategy const& strategy)
  467. : m_out(out), m_strategy(strategy)
  468. {}
  469. template <typename Geometry>
  470. void operator()(Geometry const& geometry) const
  471. {
  472. centroid<Geometry>::apply(geometry, m_out, m_strategy);
  473. }
  474. };
  475. template <typename Point, typename Strategy>
  476. static inline void
  477. apply(boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)> const& geometry,
  478. Point& out,
  479. Strategy const& strategy)
  480. {
  481. boost::apply_visitor(visitor<Point, Strategy>(out, strategy), geometry);
  482. }
  483. };
  484. } // namespace resolve_variant
  485. /*!
  486. \brief \brief_calc{centroid} \brief_strategy
  487. \ingroup centroid
  488. \details \details_calc{centroid,geometric center (or: center of mass)}. \details_strategy_reasons
  489. \tparam Geometry \tparam_geometry
  490. \tparam Point \tparam_point
  491. \tparam Strategy \tparam_strategy{Centroid}
  492. \param geometry \param_geometry
  493. \param c \param_point \param_set{centroid}
  494. \param strategy \param_strategy{centroid}
  495. \qbk{distinguish,with strategy}
  496. \qbk{[include reference/algorithms/centroid.qbk]}
  497. \qbk{[include reference/algorithms/centroid_strategies.qbk]}
  498. }
  499. */
  500. template<typename Geometry, typename Point, typename Strategy>
  501. inline void centroid(Geometry const& geometry, Point& c,
  502. Strategy const& strategy)
  503. {
  504. resolve_variant::centroid<Geometry>::apply(geometry, c, strategy);
  505. }
  506. /*!
  507. \brief \brief_calc{centroid}
  508. \ingroup centroid
  509. \details \details_calc{centroid,geometric center (or: center of mass)}. \details_default_strategy
  510. \tparam Geometry \tparam_geometry
  511. \tparam Point \tparam_point
  512. \param geometry \param_geometry
  513. \param c The calculated centroid will be assigned to this point reference
  514. \qbk{[include reference/algorithms/centroid.qbk]}
  515. \qbk{
  516. [heading Example]
  517. [centroid]
  518. [centroid_output]
  519. }
  520. */
  521. template<typename Geometry, typename Point>
  522. inline void centroid(Geometry const& geometry, Point& c)
  523. {
  524. geometry::centroid(geometry, c, default_strategy());
  525. }
  526. /*!
  527. \brief \brief_calc{centroid}
  528. \ingroup centroid
  529. \details \details_calc{centroid,geometric center (or: center of mass)}. \details_return{centroid}.
  530. \tparam Point \tparam_point
  531. \tparam Geometry \tparam_geometry
  532. \param geometry \param_geometry
  533. \return \return_calc{centroid}
  534. \qbk{[include reference/algorithms/centroid.qbk]}
  535. */
  536. template<typename Point, typename Geometry>
  537. inline Point return_centroid(Geometry const& geometry)
  538. {
  539. Point c;
  540. geometry::centroid(geometry, c);
  541. return c;
  542. }
  543. /*!
  544. \brief \brief_calc{centroid} \brief_strategy
  545. \ingroup centroid
  546. \details \details_calc{centroid,geometric center (or: center of mass)}. \details_return{centroid}. \details_strategy_reasons
  547. \tparam Point \tparam_point
  548. \tparam Geometry \tparam_geometry
  549. \tparam Strategy \tparam_strategy{centroid}
  550. \param geometry \param_geometry
  551. \param strategy \param_strategy{centroid}
  552. \return \return_calc{centroid}
  553. \qbk{distinguish,with strategy}
  554. \qbk{[include reference/algorithms/centroid.qbk]}
  555. \qbk{[include reference/algorithms/centroid_strategies.qbk]}
  556. */
  557. template<typename Point, typename Geometry, typename Strategy>
  558. inline Point return_centroid(Geometry const& geometry, Strategy const& strategy)
  559. {
  560. Point c;
  561. geometry::centroid(geometry, c, strategy);
  562. return c;
  563. }
  564. }} // namespace boost::geometry
  565. #endif // BOOST_GEOMETRY_ALGORITHMS_CENTROID_HPP