Chris@16: // Boost.Geometry (aka GGL, Generic Geometry Library) Chris@16: Chris@16: // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. Chris@16: // Copyright (c) 2008-2012 Bruno Lalande, Paris, France. Chris@16: // Copyright (c) 2009-2012 Mateusz Loskot, London, UK. Chris@101: // Copyright (c) 2014 Adam Wulkiewicz, Lodz, Poland. Chris@101: Chris@101: // This file was modified by Oracle on 2014, 2015. Chris@101: // Modifications copyright (c) 2014-2015 Oracle and/or its affiliates. Chris@101: Chris@101: // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle Chris@16: Chris@16: // Parts of Boost.Geometry are redesigned from Geodan's Geographic Library Chris@16: // (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. Chris@16: Chris@16: // Use, modification and distribution is subject to the Boost Software License, Chris@16: // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at Chris@16: // http://www.boost.org/LICENSE_1_0.txt) Chris@16: Chris@16: #ifndef BOOST_GEOMETRY_ALGORITHMS_CENTROID_HPP Chris@16: #define BOOST_GEOMETRY_ALGORITHMS_CENTROID_HPP Chris@16: Chris@16: Chris@16: #include Chris@16: Chris@101: #include Chris@16: #include Chris@101: Chris@101: #include Chris@101: #include Chris@101: #include Chris@16: Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@101: #include Chris@101: #include Chris@16: Chris@101: #include Chris@101: Chris@101: #include Chris@16: #include Chris@101: #include Chris@101: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@101: #include Chris@16: #include Chris@16: Chris@16: #include Chris@16: #include Chris@16: Chris@101: #include Chris@101: #include Chris@101: Chris@101: #include Chris@16: Chris@16: Chris@16: namespace boost { namespace geometry Chris@16: { Chris@16: Chris@16: Chris@16: #if ! defined(BOOST_GEOMETRY_CENTROID_NO_THROW) Chris@16: Chris@16: /*! Chris@16: \brief Centroid Exception Chris@16: \ingroup centroid Chris@16: \details The centroid_exception is thrown if the free centroid function is called with Chris@16: geometries for which the centroid cannot be calculated. For example: a linestring Chris@16: without points, a polygon without points, an empty multi-geometry. Chris@16: \qbk{ Chris@16: [heading See also] Chris@16: \* [link geometry.reference.algorithms.centroid the centroid function] Chris@16: } Chris@16: Chris@16: */ Chris@16: class centroid_exception : public geometry::exception Chris@16: { Chris@16: public: Chris@16: Chris@101: /*! Chris@101: \brief The default constructor Chris@101: */ Chris@16: inline centroid_exception() {} Chris@16: Chris@101: /*! Chris@101: \brief Returns the explanatory string. Chris@101: \return Pointer to a null-terminated string with explanatory information. Chris@101: */ Chris@16: virtual char const* what() const throw() Chris@16: { Chris@16: return "Boost.Geometry Centroid calculation exception"; Chris@16: } Chris@16: }; Chris@16: Chris@16: #endif Chris@16: Chris@16: Chris@16: #ifndef DOXYGEN_NO_DETAIL Chris@16: namespace detail { namespace centroid Chris@16: { Chris@16: Chris@16: struct centroid_point Chris@16: { Chris@16: template Chris@16: static inline void apply(Point const& point, PointCentroid& centroid, Chris@16: Strategy const&) Chris@16: { Chris@16: geometry::convert(point, centroid); Chris@16: } Chris@16: }; Chris@16: Chris@16: template Chris@16: < Chris@16: typename Indexed, Chris@16: typename Point, Chris@101: std::size_t Dimension = 0, Chris@101: std::size_t DimensionCount = dimension::type::value Chris@16: > Chris@16: struct centroid_indexed_calculator Chris@16: { Chris@16: typedef typename select_coordinate_type Chris@16: < Chris@16: Indexed, Point Chris@16: >::type coordinate_type; Chris@16: static inline void apply(Indexed const& indexed, Point& centroid) Chris@16: { Chris@16: coordinate_type const c1 = get(indexed); Chris@16: coordinate_type const c2 = get(indexed); Chris@16: coordinate_type m = c1 + c2; Chris@16: coordinate_type const two = 2; Chris@16: m /= two; Chris@16: Chris@16: set(centroid, m); Chris@16: Chris@16: centroid_indexed_calculator Chris@16: < Chris@101: Indexed, Point, Dimension + 1 Chris@16: >::apply(indexed, centroid); Chris@16: } Chris@16: }; Chris@16: Chris@16: Chris@16: template Chris@16: struct centroid_indexed_calculator Chris@16: { Chris@16: static inline void apply(Indexed const& , Point& ) Chris@16: { Chris@16: } Chris@16: }; Chris@16: Chris@16: Chris@16: struct centroid_indexed Chris@16: { Chris@16: template Chris@16: static inline void apply(Indexed const& indexed, Point& centroid, Chris@16: Strategy const&) Chris@16: { Chris@16: centroid_indexed_calculator Chris@16: < Chris@101: Indexed, Point Chris@16: >::apply(indexed, centroid); Chris@16: } Chris@16: }; Chris@16: Chris@16: Chris@16: // There is one thing where centroid is different from e.g. within. Chris@16: // If the ring has only one point, it might make sense that Chris@16: // that point is the centroid. Chris@16: template Chris@16: inline bool range_ok(Range const& range, Point& centroid) Chris@16: { Chris@16: std::size_t const n = boost::size(range); Chris@16: if (n > 1) Chris@16: { Chris@16: return true; Chris@16: } Chris@16: else if (n <= 0) Chris@16: { Chris@16: #if ! defined(BOOST_GEOMETRY_CENTROID_NO_THROW) Chris@16: throw centroid_exception(); Chris@101: #else Chris@101: return false; Chris@16: #endif Chris@16: } Chris@16: else // if (n == 1) Chris@16: { Chris@16: // Take over the first point in a "coordinate neutral way" Chris@16: geometry::convert(*boost::begin(range), centroid); Chris@16: return false; Chris@16: } Chris@101: //return true; // unreachable Chris@16: } Chris@16: Chris@16: /*! Chris@101: \brief Calculate the centroid of a Ring or a Linestring. Chris@16: */ Chris@16: template Chris@16: struct centroid_range_state Chris@16: { Chris@101: template Chris@16: static inline void apply(Ring const& ring, Chris@101: PointTransformer const& transformer, Chris@101: Strategy const& strategy, Chris@101: typename Strategy::state_type& state) Chris@16: { Chris@101: boost::ignore_unused(strategy); Chris@101: Chris@101: typedef typename geometry::point_type::type point_type; Chris@16: typedef typename closeable_view::type view_type; Chris@16: Chris@16: typedef typename boost::range_iterator::type iterator_type; Chris@16: Chris@16: view_type view(ring); Chris@16: iterator_type it = boost::begin(view); Chris@16: iterator_type end = boost::end(view); Chris@16: Chris@101: typename PointTransformer::result_type Chris@101: previous_pt = transformer.apply(*it); Chris@101: Chris@101: for ( ++it ; it != end ; ++it) Chris@16: { Chris@101: typename PointTransformer::result_type Chris@101: pt = transformer.apply(*it); Chris@101: Chris@101: strategy.apply(static_cast(previous_pt), Chris@101: static_cast(pt), Chris@101: state); Chris@101: Chris@101: previous_pt = pt; Chris@16: } Chris@16: } Chris@16: }; Chris@16: Chris@16: template Chris@16: struct centroid_range Chris@16: { Chris@16: template Chris@101: static inline bool apply(Range const& range, Point& centroid, Chris@101: Strategy const& strategy) Chris@16: { Chris@16: if (range_ok(range, centroid)) Chris@16: { Chris@101: // prepare translation transformer Chris@101: translating_transformer transformer(*boost::begin(range)); Chris@101: Chris@16: typename Strategy::state_type state; Chris@101: centroid_range_state::apply(range, transformer, Chris@101: strategy, state); Chris@101: Chris@101: if ( strategy.result(state, centroid) ) Chris@101: { Chris@101: // translate the result back Chris@101: transformer.apply_reverse(centroid); Chris@101: return true; Chris@101: } Chris@16: } Chris@101: Chris@101: return false; Chris@16: } Chris@16: }; Chris@16: Chris@16: Chris@16: /*! Chris@16: \brief Centroid of a polygon. Chris@16: \note Because outer ring is clockwise, inners are counter clockwise, Chris@16: triangle approach is OK and works for polygons with rings. Chris@16: */ Chris@16: struct centroid_polygon_state Chris@16: { Chris@101: template Chris@16: static inline void apply(Polygon const& poly, Chris@101: PointTransformer const& transformer, Chris@101: Strategy const& strategy, Chris@101: typename Strategy::state_type& state) Chris@16: { Chris@16: typedef typename ring_type::type ring_type; Chris@16: typedef centroid_range_state::value> per_ring; Chris@16: Chris@101: per_ring::apply(exterior_ring(poly), transformer, strategy, state); Chris@16: Chris@101: typename interior_return_type::type Chris@101: rings = interior_rings(poly); Chris@101: Chris@101: for (typename detail::interior_iterator::type Chris@101: it = boost::begin(rings); it != boost::end(rings); ++it) Chris@16: { Chris@101: per_ring::apply(*it, transformer, strategy, state); Chris@16: } Chris@16: } Chris@16: }; Chris@16: Chris@16: struct centroid_polygon Chris@16: { Chris@16: template Chris@101: static inline bool apply(Polygon const& poly, Point& centroid, Chris@101: Strategy const& strategy) Chris@16: { Chris@16: if (range_ok(exterior_ring(poly), centroid)) Chris@16: { Chris@101: // prepare translation transformer Chris@101: translating_transformer Chris@101: transformer(*boost::begin(exterior_ring(poly))); Chris@101: Chris@16: typename Strategy::state_type state; Chris@101: centroid_polygon_state::apply(poly, transformer, strategy, state); Chris@101: Chris@101: if ( strategy.result(state, centroid) ) Chris@101: { Chris@101: // translate the result back Chris@101: transformer.apply_reverse(centroid); Chris@101: return true; Chris@101: } Chris@101: } Chris@101: Chris@101: return false; Chris@101: } Chris@101: }; Chris@101: Chris@101: Chris@101: /*! Chris@101: \brief Building block of a multi-point, to be used as Policy in the Chris@101: more generec centroid_multi Chris@101: */ Chris@101: struct centroid_multi_point_state Chris@101: { Chris@101: template Chris@101: static inline void apply(Point const& point, Chris@101: PointTransformer const& transformer, Chris@101: Strategy const& strategy, Chris@101: typename Strategy::state_type& state) Chris@101: { Chris@101: boost::ignore_unused(strategy); Chris@101: strategy.apply(static_cast(transformer.apply(point)), Chris@101: state); Chris@101: } Chris@101: }; Chris@101: Chris@101: Chris@101: /*! Chris@101: \brief Generic implementation which calls a policy to calculate the Chris@101: centroid of the total of its single-geometries Chris@101: \details The Policy is, in general, the single-version, with state. So Chris@101: detail::centroid::centroid_polygon_state is used as a policy for this Chris@101: detail::centroid::centroid_multi Chris@101: Chris@101: */ Chris@101: template Chris@101: struct centroid_multi Chris@101: { Chris@101: template Chris@101: static inline bool apply(Multi const& multi, Chris@101: Point& centroid, Chris@101: Strategy const& strategy) Chris@101: { Chris@101: #if ! defined(BOOST_GEOMETRY_CENTROID_NO_THROW) Chris@101: // If there is nothing in any of the ranges, it is not possible Chris@101: // to calculate the centroid Chris@101: if (geometry::num_points(multi) == 0) Chris@101: { Chris@101: throw centroid_exception(); Chris@101: } Chris@101: #endif Chris@101: Chris@101: // prepare translation transformer Chris@101: translating_transformer transformer(multi); Chris@101: Chris@101: typename Strategy::state_type state; Chris@101: Chris@101: for (typename boost::range_iterator::type Chris@101: it = boost::begin(multi); Chris@101: it != boost::end(multi); Chris@101: ++it) Chris@101: { Chris@101: Policy::apply(*it, transformer, strategy, state); Chris@101: } Chris@101: Chris@101: if ( strategy.result(state, centroid) ) Chris@101: { Chris@101: // translate the result back Chris@101: transformer.apply_reverse(centroid); Chris@101: return true; Chris@101: } Chris@101: Chris@101: return false; Chris@101: } Chris@101: }; Chris@101: Chris@101: Chris@101: template Chris@101: struct centroid_linear_areal Chris@101: { Chris@101: template Chris@101: static inline void apply(Geometry const& geom, Chris@101: Point& centroid, Chris@101: Strategy const& strategy) Chris@101: { Chris@101: if ( ! Algorithm::apply(geom, centroid, strategy) ) Chris@101: { Chris@101: geometry::point_on_border(centroid, geom); Chris@16: } Chris@16: } Chris@16: }; Chris@16: Chris@16: Chris@16: }} // namespace detail::centroid Chris@16: #endif // DOXYGEN_NO_DETAIL Chris@16: Chris@16: Chris@16: #ifndef DOXYGEN_NO_DISPATCH Chris@16: namespace dispatch Chris@16: { Chris@16: Chris@16: template Chris@16: < Chris@16: typename Geometry, Chris@16: typename Tag = typename tag::type Chris@16: > Chris@16: struct centroid: not_implemented Chris@16: {}; Chris@16: Chris@16: template Chris@16: struct centroid Chris@16: : detail::centroid::centroid_point Chris@16: {}; Chris@16: Chris@16: template Chris@16: struct centroid Chris@16: : detail::centroid::centroid_indexed Chris@16: {}; Chris@16: Chris@16: template Chris@16: struct centroid Chris@16: : detail::centroid::centroid_indexed Chris@16: {}; Chris@16: Chris@16: template Chris@16: struct centroid Chris@101: : detail::centroid::centroid_linear_areal Chris@101: < Chris@101: detail::centroid::centroid_range::value> Chris@101: > Chris@16: {}; Chris@16: Chris@16: template Chris@16: struct centroid Chris@101: : detail::centroid::centroid_linear_areal Chris@101: < Chris@101: detail::centroid::centroid_range Chris@101: > Chris@101: {}; Chris@16: Chris@16: template Chris@16: struct centroid Chris@101: : detail::centroid::centroid_linear_areal Chris@101: < Chris@101: detail::centroid::centroid_polygon Chris@101: > Chris@101: {}; Chris@101: Chris@101: template Chris@101: struct centroid Chris@101: : detail::centroid::centroid_linear_areal Chris@101: < Chris@101: detail::centroid::centroid_multi Chris@101: < Chris@101: detail::centroid::centroid_range_state Chris@101: > Chris@101: > Chris@101: {}; Chris@101: Chris@101: template Chris@101: struct centroid Chris@101: : detail::centroid::centroid_linear_areal Chris@101: < Chris@101: detail::centroid::centroid_multi Chris@101: < Chris@101: detail::centroid::centroid_polygon_state Chris@101: > Chris@101: > Chris@101: {}; Chris@101: Chris@101: template Chris@101: struct centroid Chris@101: : detail::centroid::centroid_multi Chris@101: < Chris@101: detail::centroid::centroid_multi_point_state Chris@101: > Chris@101: {}; Chris@101: Chris@16: Chris@16: } // namespace dispatch Chris@16: #endif // DOXYGEN_NO_DISPATCH Chris@16: Chris@16: Chris@101: namespace resolve_strategy { Chris@101: Chris@101: template Chris@101: struct centroid Chris@101: { Chris@101: template Chris@101: static inline void apply(Geometry const& geometry, Point& out, Strategy const& strategy) Chris@101: { Chris@101: dispatch::centroid::apply(geometry, out, strategy); Chris@101: } Chris@101: Chris@101: template Chris@101: static inline void apply(Geometry const& geometry, Point& out, default_strategy) Chris@101: { Chris@101: typedef typename strategy::centroid::services::default_strategy Chris@101: < Chris@101: typename cs_tag::type, Chris@101: typename tag_cast Chris@101: < Chris@101: typename tag::type, Chris@101: pointlike_tag, Chris@101: linear_tag, Chris@101: areal_tag Chris@101: >::type, Chris@101: dimension::type::value, Chris@101: Point, Chris@101: Geometry Chris@101: >::type strategy_type; Chris@101: Chris@101: dispatch::centroid::apply(geometry, out, strategy_type()); Chris@101: } Chris@101: }; Chris@101: Chris@101: } // namespace resolve_strategy Chris@101: Chris@101: Chris@101: namespace resolve_variant { Chris@101: Chris@101: template Chris@101: struct centroid Chris@101: { Chris@101: template Chris@101: static inline void apply(Geometry const& geometry, Point& out, Strategy const& strategy) Chris@101: { Chris@101: concept::check_concepts_and_equal_dimensions(); Chris@101: resolve_strategy::centroid::apply(geometry, out, strategy); Chris@101: } Chris@101: }; Chris@101: Chris@101: template Chris@101: struct centroid > Chris@101: { Chris@101: template Chris@101: struct visitor: boost::static_visitor Chris@101: { Chris@101: Point& m_out; Chris@101: Strategy const& m_strategy; Chris@101: Chris@101: visitor(Point& out, Strategy const& strategy) Chris@101: : m_out(out), m_strategy(strategy) Chris@101: {} Chris@101: Chris@101: template Chris@101: void operator()(Geometry const& geometry) const Chris@101: { Chris@101: centroid::apply(geometry, m_out, m_strategy); Chris@101: } Chris@101: }; Chris@101: Chris@101: template Chris@101: static inline void Chris@101: apply(boost::variant const& geometry, Chris@101: Point& out, Chris@101: Strategy const& strategy) Chris@101: { Chris@101: boost::apply_visitor(visitor(out, strategy), geometry); Chris@101: } Chris@101: }; Chris@101: Chris@101: } // namespace resolve_variant Chris@101: Chris@101: Chris@16: /*! Chris@16: \brief \brief_calc{centroid} \brief_strategy Chris@16: \ingroup centroid Chris@16: \details \details_calc{centroid,geometric center (or: center of mass)}. \details_strategy_reasons Chris@16: \tparam Geometry \tparam_geometry Chris@16: \tparam Point \tparam_point Chris@16: \tparam Strategy \tparam_strategy{Centroid} Chris@16: \param geometry \param_geometry Chris@16: \param c \param_point \param_set{centroid} Chris@16: \param strategy \param_strategy{centroid} Chris@16: Chris@16: \qbk{distinguish,with strategy} Chris@16: \qbk{[include reference/algorithms/centroid.qbk]} Chris@16: \qbk{[include reference/algorithms/centroid_strategies.qbk]} Chris@16: } Chris@16: Chris@16: */ Chris@16: template Chris@16: inline void centroid(Geometry const& geometry, Point& c, Chris@16: Strategy const& strategy) Chris@16: { Chris@101: resolve_variant::centroid::apply(geometry, c, strategy); Chris@16: } Chris@16: Chris@16: Chris@16: /*! Chris@16: \brief \brief_calc{centroid} Chris@16: \ingroup centroid Chris@16: \details \details_calc{centroid,geometric center (or: center of mass)}. \details_default_strategy Chris@16: \tparam Geometry \tparam_geometry Chris@16: \tparam Point \tparam_point Chris@16: \param geometry \param_geometry Chris@16: \param c The calculated centroid will be assigned to this point reference Chris@16: Chris@16: \qbk{[include reference/algorithms/centroid.qbk]} Chris@16: \qbk{ Chris@16: [heading Example] Chris@16: [centroid] Chris@16: [centroid_output] Chris@16: } Chris@16: */ Chris@16: template Chris@16: inline void centroid(Geometry const& geometry, Point& c) Chris@16: { Chris@101: centroid(geometry, c, default_strategy()); Chris@16: } Chris@16: Chris@16: Chris@16: /*! Chris@16: \brief \brief_calc{centroid} Chris@16: \ingroup centroid Chris@16: \details \details_calc{centroid,geometric center (or: center of mass)}. \details_return{centroid}. Chris@16: \tparam Point \tparam_point Chris@16: \tparam Geometry \tparam_geometry Chris@16: \param geometry \param_geometry Chris@16: \return \return_calc{centroid} Chris@16: Chris@16: \qbk{[include reference/algorithms/centroid.qbk]} Chris@16: */ Chris@16: template Chris@16: inline Point return_centroid(Geometry const& geometry) Chris@16: { Chris@16: Point c; Chris@16: centroid(geometry, c); Chris@16: return c; Chris@16: } Chris@16: Chris@16: /*! Chris@16: \brief \brief_calc{centroid} \brief_strategy Chris@16: \ingroup centroid Chris@16: \details \details_calc{centroid,geometric center (or: center of mass)}. \details_return{centroid}. \details_strategy_reasons Chris@16: \tparam Point \tparam_point Chris@16: \tparam Geometry \tparam_geometry Chris@16: \tparam Strategy \tparam_strategy{centroid} Chris@16: \param geometry \param_geometry Chris@16: \param strategy \param_strategy{centroid} Chris@16: \return \return_calc{centroid} Chris@16: Chris@16: \qbk{distinguish,with strategy} Chris@16: \qbk{[include reference/algorithms/centroid.qbk]} Chris@16: \qbk{[include reference/algorithms/centroid_strategies.qbk]} Chris@16: */ Chris@16: template Chris@16: inline Point return_centroid(Geometry const& geometry, Strategy const& strategy) Chris@16: { Chris@16: Point c; Chris@16: centroid(geometry, c, strategy); Chris@16: return c; Chris@16: } Chris@16: Chris@16: Chris@16: }} // namespace boost::geometry Chris@16: Chris@16: Chris@16: #endif // BOOST_GEOMETRY_ALGORITHMS_CENTROID_HPP