centroid.hpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
  1. // Boost.Geometry (aka GGL, Generic Geometry Library)
  2. // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
  3. // Copyright (c) 2008-2012 Bruno Lalande, Paris, France.
  4. // Copyright (c) 2009-2012 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_CENTROID_HPP
  11. #define BOOST_GEOMETRY_ALGORITHMS_CENTROID_HPP
  12. #include <cstddef>
  13. #include <boost/range.hpp>
  14. #include <boost/typeof/typeof.hpp>
  15. #include <boost/geometry/core/closure.hpp>
  16. #include <boost/geometry/core/cs.hpp>
  17. #include <boost/geometry/core/coordinate_dimension.hpp>
  18. #include <boost/geometry/core/exception.hpp>
  19. #include <boost/geometry/core/exterior_ring.hpp>
  20. #include <boost/geometry/core/interior_rings.hpp>
  21. #include <boost/geometry/core/tag_cast.hpp>
  22. #include <boost/geometry/algorithms/convert.hpp>
  23. #include <boost/geometry/algorithms/distance.hpp>
  24. #include <boost/geometry/algorithms/not_implemented.hpp>
  25. #include <boost/geometry/geometries/concepts/check.hpp>
  26. #include <boost/geometry/strategies/centroid.hpp>
  27. #include <boost/geometry/strategies/concepts/centroid_concept.hpp>
  28. #include <boost/geometry/views/closeable_view.hpp>
  29. #include <boost/geometry/util/for_each_coordinate.hpp>
  30. #include <boost/geometry/util/select_coordinate_type.hpp>
  31. namespace boost { namespace geometry
  32. {
  33. #if ! defined(BOOST_GEOMETRY_CENTROID_NO_THROW)
  34. /*!
  35. \brief Centroid Exception
  36. \ingroup centroid
  37. \details The centroid_exception is thrown if the free centroid function is called with
  38. geometries for which the centroid cannot be calculated. For example: a linestring
  39. without points, a polygon without points, an empty multi-geometry.
  40. \qbk{
  41. [heading See also]
  42. \* [link geometry.reference.algorithms.centroid the centroid function]
  43. }
  44. */
  45. class centroid_exception : public geometry::exception
  46. {
  47. public:
  48. inline centroid_exception() {}
  49. virtual char const* what() const throw()
  50. {
  51. return "Boost.Geometry Centroid calculation exception";
  52. }
  53. };
  54. #endif
  55. #ifndef DOXYGEN_NO_DETAIL
  56. namespace detail { namespace centroid
  57. {
  58. struct centroid_point
  59. {
  60. template<typename Point, typename PointCentroid, typename Strategy>
  61. static inline void apply(Point const& point, PointCentroid& centroid,
  62. Strategy const&)
  63. {
  64. geometry::convert(point, centroid);
  65. }
  66. };
  67. template
  68. <
  69. typename Indexed,
  70. typename Point,
  71. std::size_t Dimension,
  72. std::size_t DimensionCount
  73. >
  74. struct centroid_indexed_calculator
  75. {
  76. typedef typename select_coordinate_type
  77. <
  78. Indexed, Point
  79. >::type coordinate_type;
  80. static inline void apply(Indexed const& indexed, Point& centroid)
  81. {
  82. coordinate_type const c1 = get<min_corner, Dimension>(indexed);
  83. coordinate_type const c2 = get<max_corner, Dimension>(indexed);
  84. coordinate_type m = c1 + c2;
  85. coordinate_type const two = 2;
  86. m /= two;
  87. set<Dimension>(centroid, m);
  88. centroid_indexed_calculator
  89. <
  90. Indexed, Point,
  91. Dimension + 1, DimensionCount
  92. >::apply(indexed, centroid);
  93. }
  94. };
  95. template<typename Indexed, typename Point, std::size_t DimensionCount>
  96. struct centroid_indexed_calculator<Indexed, Point, DimensionCount, DimensionCount>
  97. {
  98. static inline void apply(Indexed const& , Point& )
  99. {
  100. }
  101. };
  102. struct centroid_indexed
  103. {
  104. template<typename Indexed, typename Point, typename Strategy>
  105. static inline void apply(Indexed const& indexed, Point& centroid,
  106. Strategy const&)
  107. {
  108. centroid_indexed_calculator
  109. <
  110. Indexed, Point,
  111. 0, dimension<Indexed>::type::value
  112. >::apply(indexed, centroid);
  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. throw centroid_exception();
  130. #endif
  131. return false;
  132. }
  133. else // if (n == 1)
  134. {
  135. // Take over the first point in a "coordinate neutral way"
  136. geometry::convert(*boost::begin(range), centroid);
  137. return false;
  138. }
  139. return true;
  140. }
  141. /*!
  142. \brief Calculate the centroid of a ring.
  143. */
  144. template <closure_selector Closure>
  145. struct centroid_range_state
  146. {
  147. template<typename Ring, typename Strategy>
  148. static inline void apply(Ring const& ring,
  149. Strategy const& strategy, typename Strategy::state_type& state)
  150. {
  151. typedef typename closeable_view<Ring const, Closure>::type view_type;
  152. typedef typename boost::range_iterator<view_type const>::type iterator_type;
  153. view_type view(ring);
  154. iterator_type it = boost::begin(view);
  155. iterator_type end = boost::end(view);
  156. for (iterator_type previous = it++;
  157. it != end;
  158. ++previous, ++it)
  159. {
  160. strategy.apply(*previous, *it, state);
  161. }
  162. }
  163. };
  164. template <closure_selector Closure>
  165. struct centroid_range
  166. {
  167. template<typename Range, typename Point, typename Strategy>
  168. static inline void apply(Range const& range, Point& centroid,
  169. Strategy const& strategy)
  170. {
  171. if (range_ok(range, centroid))
  172. {
  173. typename Strategy::state_type state;
  174. centroid_range_state<Closure>::apply(range, strategy, state);
  175. strategy.result(state, centroid);
  176. }
  177. }
  178. };
  179. /*!
  180. \brief Centroid of a polygon.
  181. \note Because outer ring is clockwise, inners are counter clockwise,
  182. triangle approach is OK and works for polygons with rings.
  183. */
  184. struct centroid_polygon_state
  185. {
  186. template<typename Polygon, typename Strategy>
  187. static inline void apply(Polygon const& poly,
  188. Strategy const& strategy, typename Strategy::state_type& state)
  189. {
  190. typedef typename ring_type<Polygon>::type ring_type;
  191. typedef centroid_range_state<geometry::closure<ring_type>::value> per_ring;
  192. per_ring::apply(exterior_ring(poly), strategy, state);
  193. typename interior_return_type<Polygon const>::type rings
  194. = interior_rings(poly);
  195. for (BOOST_AUTO_TPL(it, boost::begin(rings)); it != boost::end(rings); ++it)
  196. {
  197. per_ring::apply(*it, strategy, state);
  198. }
  199. }
  200. };
  201. struct centroid_polygon
  202. {
  203. template<typename Polygon, typename Point, typename Strategy>
  204. static inline void apply(Polygon const& poly, Point& centroid,
  205. Strategy const& strategy)
  206. {
  207. if (range_ok(exterior_ring(poly), centroid))
  208. {
  209. typename Strategy::state_type state;
  210. centroid_polygon_state::apply(poly, strategy, state);
  211. strategy.result(state, centroid);
  212. }
  213. }
  214. };
  215. }} // namespace detail::centroid
  216. #endif // DOXYGEN_NO_DETAIL
  217. #ifndef DOXYGEN_NO_DISPATCH
  218. namespace dispatch
  219. {
  220. template
  221. <
  222. typename Geometry,
  223. typename Tag = typename tag<Geometry>::type
  224. >
  225. struct centroid: not_implemented<Tag>
  226. {};
  227. template <typename Geometry>
  228. struct centroid<Geometry, point_tag>
  229. : detail::centroid::centroid_point
  230. {};
  231. template <typename Box>
  232. struct centroid<Box, box_tag>
  233. : detail::centroid::centroid_indexed
  234. {};
  235. template <typename Segment>
  236. struct centroid<Segment, segment_tag>
  237. : detail::centroid::centroid_indexed
  238. {};
  239. template <typename Ring>
  240. struct centroid<Ring, ring_tag>
  241. : detail::centroid::centroid_range<geometry::closure<Ring>::value>
  242. {};
  243. template <typename Linestring>
  244. struct centroid<Linestring, linestring_tag>
  245. : detail::centroid::centroid_range<closed>
  246. {};
  247. template <typename Polygon>
  248. struct centroid<Polygon, polygon_tag>
  249. : detail::centroid::centroid_polygon
  250. {};
  251. } // namespace dispatch
  252. #endif // DOXYGEN_NO_DISPATCH
  253. /*!
  254. \brief \brief_calc{centroid} \brief_strategy
  255. \ingroup centroid
  256. \details \details_calc{centroid,geometric center (or: center of mass)}. \details_strategy_reasons
  257. \tparam Geometry \tparam_geometry
  258. \tparam Point \tparam_point
  259. \tparam Strategy \tparam_strategy{Centroid}
  260. \param geometry \param_geometry
  261. \param c \param_point \param_set{centroid}
  262. \param strategy \param_strategy{centroid}
  263. \qbk{distinguish,with strategy}
  264. \qbk{[include reference/algorithms/centroid.qbk]}
  265. \qbk{[include reference/algorithms/centroid_strategies.qbk]}
  266. }
  267. */
  268. template<typename Geometry, typename Point, typename Strategy>
  269. inline void centroid(Geometry const& geometry, Point& c,
  270. Strategy const& strategy)
  271. {
  272. //BOOST_CONCEPT_ASSERT( (geometry::concept::CentroidStrategy<Strategy>) );
  273. concept::check_concepts_and_equal_dimensions<Point, Geometry const>();
  274. typedef typename point_type<Geometry>::type point_type;
  275. // Call dispatch apply method. That one returns true if centroid
  276. // should be taken from state.
  277. dispatch::centroid<Geometry>::apply(geometry, c, strategy);
  278. }
  279. /*!
  280. \brief \brief_calc{centroid}
  281. \ingroup centroid
  282. \details \details_calc{centroid,geometric center (or: center of mass)}. \details_default_strategy
  283. \tparam Geometry \tparam_geometry
  284. \tparam Point \tparam_point
  285. \param geometry \param_geometry
  286. \param c The calculated centroid will be assigned to this point reference
  287. \qbk{[include reference/algorithms/centroid.qbk]}
  288. \qbk{
  289. [heading Example]
  290. [centroid]
  291. [centroid_output]
  292. }
  293. */
  294. template<typename Geometry, typename Point>
  295. inline void centroid(Geometry const& geometry, Point& c)
  296. {
  297. concept::check_concepts_and_equal_dimensions<Point, Geometry const>();
  298. typedef typename strategy::centroid::services::default_strategy
  299. <
  300. typename cs_tag<Geometry>::type,
  301. typename tag_cast
  302. <
  303. typename tag<Geometry>::type,
  304. pointlike_tag,
  305. linear_tag,
  306. areal_tag
  307. >::type,
  308. dimension<Geometry>::type::value,
  309. Point,
  310. Geometry
  311. >::type strategy_type;
  312. centroid(geometry, c, strategy_type());
  313. }
  314. /*!
  315. \brief \brief_calc{centroid}
  316. \ingroup centroid
  317. \details \details_calc{centroid,geometric center (or: center of mass)}. \details_return{centroid}.
  318. \tparam Point \tparam_point
  319. \tparam Geometry \tparam_geometry
  320. \param geometry \param_geometry
  321. \return \return_calc{centroid}
  322. \qbk{[include reference/algorithms/centroid.qbk]}
  323. */
  324. template<typename Point, typename Geometry>
  325. inline Point return_centroid(Geometry const& geometry)
  326. {
  327. concept::check_concepts_and_equal_dimensions<Point, Geometry const>();
  328. Point c;
  329. centroid(geometry, c);
  330. return c;
  331. }
  332. /*!
  333. \brief \brief_calc{centroid} \brief_strategy
  334. \ingroup centroid
  335. \details \details_calc{centroid,geometric center (or: center of mass)}. \details_return{centroid}. \details_strategy_reasons
  336. \tparam Point \tparam_point
  337. \tparam Geometry \tparam_geometry
  338. \tparam Strategy \tparam_strategy{centroid}
  339. \param geometry \param_geometry
  340. \param strategy \param_strategy{centroid}
  341. \return \return_calc{centroid}
  342. \qbk{distinguish,with strategy}
  343. \qbk{[include reference/algorithms/centroid.qbk]}
  344. \qbk{[include reference/algorithms/centroid_strategies.qbk]}
  345. */
  346. template<typename Point, typename Geometry, typename Strategy>
  347. inline Point return_centroid(Geometry const& geometry, Strategy const& strategy)
  348. {
  349. //BOOST_CONCEPT_ASSERT( (geometry::concept::CentroidStrategy<Strategy>) );
  350. concept::check_concepts_and_equal_dimensions<Point, Geometry const>();
  351. Point c;
  352. centroid(geometry, c, strategy);
  353. return c;
  354. }
  355. }} // namespace boost::geometry
  356. #endif // BOOST_GEOMETRY_ALGORITHMS_CENTROID_HPP