// Boost.Geometry (aka GGL, Generic Geometry Library) // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. // Copyright (c) 2008-2012 Bruno Lalande, Paris, France. // Copyright (c) 2009-2012 Mateusz Loskot, London, UK. // Parts of Boost.Geometry are redesigned from Geodan's Geographic Library // (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. // Use, modification and distribution is subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #ifndef BOOST_GEOMETRY_ALGORITHMS_CENTROID_HPP #define BOOST_GEOMETRY_ALGORITHMS_CENTROID_HPP #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace boost { namespace geometry { #if ! defined(BOOST_GEOMETRY_CENTROID_NO_THROW) /*! \brief Centroid Exception \ingroup centroid \details The centroid_exception is thrown if the free centroid function is called with geometries for which the centroid cannot be calculated. For example: a linestring without points, a polygon without points, an empty multi-geometry. \qbk{ [heading See also] \* [link geometry.reference.algorithms.centroid the centroid function] } */ class centroid_exception : public geometry::exception { public: inline centroid_exception() {} virtual char const* what() const throw() { return "Boost.Geometry Centroid calculation exception"; } }; #endif #ifndef DOXYGEN_NO_DETAIL namespace detail { namespace centroid { template struct centroid_point { static inline void apply(Point const& point, PointCentroid& centroid, Strategy const&) { geometry::convert(point, centroid); } }; template < typename Box, typename Point, std::size_t Dimension, std::size_t DimensionCount > struct centroid_box_calculator { typedef typename select_coordinate_type < Box, Point >::type coordinate_type; static inline void apply(Box const& box, Point& centroid) { coordinate_type const c1 = get(box); coordinate_type const c2 = get(box); coordinate_type m = c1 + c2; m /= 2.0; set(centroid, m); centroid_box_calculator < Box, Point, Dimension + 1, DimensionCount >::apply(box, centroid); } }; template struct centroid_box_calculator { static inline void apply(Box const& , Point& ) { } }; template struct centroid_box { static inline void apply(Box const& box, Point& centroid, Strategy const&) { centroid_box_calculator < Box, Point, 0, dimension::type::value >::apply(box, centroid); } }; // There is one thing where centroid is different from e.g. within. // If the ring has only one point, it might make sense that // that point is the centroid. template inline bool range_ok(Range const& range, Point& centroid) { std::size_t const n = boost::size(range); if (n > 1) { return true; } else if (n <= 0) { #if ! defined(BOOST_GEOMETRY_CENTROID_NO_THROW) throw centroid_exception(); #endif return false; } else // if (n == 1) { // Take over the first point in a "coordinate neutral way" geometry::convert(*boost::begin(range), centroid); return false; } return true; } /*! \brief Calculate the centroid of a ring. */ template struct centroid_range_state { static inline void apply(Ring const& ring, Strategy const& strategy, typename Strategy::state_type& state) { typedef typename closeable_view::type view_type; typedef typename boost::range_iterator::type iterator_type; view_type view(ring); iterator_type it = boost::begin(view); iterator_type end = boost::end(view); for (iterator_type previous = it++; it != end; ++previous, ++it) { strategy.apply(*previous, *it, state); } } }; template struct centroid_range { static inline void apply(Range const& range, Point& centroid, Strategy const& strategy) { if (range_ok(range, centroid)) { typename Strategy::state_type state; centroid_range_state < Range, Closure, Strategy >::apply(range, strategy, state); strategy.result(state, centroid); } } }; /*! \brief Centroid of a polygon. \note Because outer ring is clockwise, inners are counter clockwise, triangle approach is OK and works for polygons with rings. */ template struct centroid_polygon_state { typedef typename ring_type::type ring_type; static inline void apply(Polygon const& poly, Strategy const& strategy, typename Strategy::state_type& state) { typedef centroid_range_state < ring_type, geometry::closure::value, Strategy > per_ring; per_ring::apply(exterior_ring(poly), strategy, state); typename interior_return_type::type rings = interior_rings(poly); for (BOOST_AUTO_TPL(it, boost::begin(rings)); it != boost::end(rings); ++it) { per_ring::apply(*it, strategy, state); } } }; template struct centroid_polygon { static inline void apply(Polygon const& poly, Point& centroid, Strategy const& strategy) { if (range_ok(exterior_ring(poly), centroid)) { typename Strategy::state_type state; centroid_polygon_state < Polygon, Strategy >::apply(poly, strategy, state); strategy.result(state, centroid); } } }; }} // namespace detail::centroid #endif // DOXYGEN_NO_DETAIL #ifndef DOXYGEN_NO_DISPATCH namespace dispatch { template < typename Tag, typename Geometry, typename Point, typename Strategy > struct centroid {}; template < typename Geometry, typename Point, typename Strategy > struct centroid : detail::centroid::centroid_point {}; template < typename Box, typename Point, typename Strategy > struct centroid : detail::centroid::centroid_box {}; template struct centroid : detail::centroid::centroid_range < Ring, Point, geometry::closure::value, Strategy > {}; template struct centroid : detail::centroid::centroid_range < Linestring, Point, closed, Strategy > {}; template struct centroid : detail::centroid::centroid_polygon {}; } // namespace dispatch #endif // DOXYGEN_NO_DISPATCH /*! \brief \brief_calc{centroid} \brief_strategy \ingroup centroid \details \details_calc{centroid,geometric center (or: center of mass)}. \details_strategy_reasons \tparam Geometry \tparam_geometry \tparam Point \tparam_point \tparam Strategy \tparam_strategy{Centroid} \param geometry \param_geometry \param c \param_point \param_set{centroid} \param strategy \param_strategy{centroid} \qbk{distinguish,with strategy} \qbk{[include reference/algorithms/centroid.qbk]} \qbk{[include reference/algorithms/centroid_strategies.qbk]} } */ template inline void centroid(Geometry const& geometry, Point& c, Strategy const& strategy) { //BOOST_CONCEPT_ASSERT( (geometry::concept::CentroidStrategy) ); concept::check_concepts_and_equal_dimensions(); typedef typename point_type::type point_type; // Call dispatch apply method. That one returns true if centroid // should be taken from state. dispatch::centroid < typename tag::type, Geometry, Point, Strategy >::apply(geometry, c, strategy); } /*! \brief \brief_calc{centroid} \ingroup centroid \details \details_calc{centroid,geometric center (or: center of mass)}. \details_default_strategy \tparam Geometry \tparam_geometry \tparam Point \tparam_point \param geometry \param_geometry \param c The calculated centroid will be assigned to this point reference \qbk{[include reference/algorithms/centroid.qbk]} \qbk{ [heading Example] [centroid] [centroid_output] } */ template inline void centroid(Geometry const& geometry, Point& c) { concept::check_concepts_and_equal_dimensions(); typedef typename strategy::centroid::services::default_strategy < typename cs_tag::type, typename tag_cast < typename tag::type, pointlike_tag, linear_tag, areal_tag >::type, dimension::type::value, Point, Geometry >::type strategy_type; centroid(geometry, c, strategy_type()); } /*! \brief \brief_calc{centroid} \ingroup centroid \details \details_calc{centroid,geometric center (or: center of mass)}. \details_return{centroid}. \tparam Point \tparam_point \tparam Geometry \tparam_geometry \param geometry \param_geometry \return \return_calc{centroid} \qbk{[include reference/algorithms/centroid.qbk]} */ template inline Point return_centroid(Geometry const& geometry) { concept::check_concepts_and_equal_dimensions(); Point c; centroid(geometry, c); return c; } /*! \brief \brief_calc{centroid} \brief_strategy \ingroup centroid \details \details_calc{centroid,geometric center (or: center of mass)}. \details_return{centroid}. \details_strategy_reasons \tparam Point \tparam_point \tparam Geometry \tparam_geometry \tparam Strategy \tparam_strategy{centroid} \param geometry \param_geometry \param strategy \param_strategy{centroid} \return \return_calc{centroid} \qbk{distinguish,with strategy} \qbk{[include reference/algorithms/centroid.qbk]} \qbk{[include reference/algorithms/centroid_strategies.qbk]} */ template inline Point return_centroid(Geometry const& geometry, Strategy const& strategy) { //BOOST_CONCEPT_ASSERT( (geometry::concept::CentroidStrategy) ); concept::check_concepts_and_equal_dimensions(); Point c; centroid(geometry, c, strategy); return c; } }} // namespace boost::geometry #endif // BOOST_GEOMETRY_ALGORITHMS_CENTROID_HPP