centroid.hpp 18 KB

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