centroid.hpp 19 KB

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