// Boost.Geometry (aka GGL, Generic Geometry Library) // Copyright (c) 2007-2015 Barend Gehrels, Amsterdam, the Netherlands. // Copyright (c) 2008-2015 Bruno Lalande, Paris, France. // Copyright (c) 2009-2015 Mateusz Loskot, London, UK. // Copyright (c) 2014-2017 Adam Wulkiewicz, Lodz, Poland. // This file was modified by Oracle on 2014-2020. // Modifications copyright (c) 2014-2020 Oracle and/or its affiliates. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle // 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 #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: /*! \brief The default constructor */ inline centroid_exception() {} /*! \brief Returns the explanatory string. \return Pointer to a null-terminated string with explanatory information. */ virtual char const* what() const throw() { return "Boost.Geometry Centroid calculation exception"; } }; #endif #ifndef DOXYGEN_NO_DETAIL namespace detail { namespace centroid { struct centroid_point { template static inline void apply(Point const& point, PointCentroid& centroid, Strategy const&) { geometry::convert(point, centroid); } }; template < typename Indexed, typename Point, std::size_t Dimension = 0, std::size_t DimensionCount = dimension::type::value > struct centroid_indexed_calculator { typedef typename select_coordinate_type < Indexed, Point >::type coordinate_type; static inline void apply(Indexed const& indexed, Point& centroid) { coordinate_type const c1 = get(indexed); coordinate_type const c2 = get(indexed); coordinate_type m = c1 + c2; coordinate_type const two = 2; m /= two; set(centroid, m); centroid_indexed_calculator < Indexed, Point, Dimension + 1 >::apply(indexed, centroid); } }; template struct centroid_indexed_calculator { static inline void apply(Indexed const& , Point& ) { } }; struct centroid_indexed { template static inline void apply(Indexed const& indexed, Point& centroid, Strategy const&) { centroid_indexed_calculator < Indexed, Point >::apply(indexed, 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) BOOST_THROW_EXCEPTION(centroid_exception()); #else return false; #endif } else // if (n == 1) { // Take over the first point in a "coordinate neutral way" geometry::convert(*boost::begin(range), centroid); return false; } //return true; // unreachable } /*! \brief Calculate the centroid of a Ring or a Linestring. */ template struct centroid_range_state { template static inline void apply(Ring const& ring, PointTransformer const& transformer, Strategy const& strategy, typename Strategy::state_type& state) { boost::ignore_unused(strategy); typedef typename geometry::point_type::type point_type; 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); if (it != end) { typename PointTransformer::result_type previous_pt = transformer.apply(*it); for ( ++it ; it != end ; ++it) { typename PointTransformer::result_type pt = transformer.apply(*it); strategy.apply(static_cast(previous_pt), static_cast(pt), state); previous_pt = pt; } } } }; template struct centroid_range { template static inline bool apply(Range const& range, Point& centroid, Strategy const& strategy) { if (range_ok(range, centroid)) { // prepare translation transformer translating_transformer transformer(*boost::begin(range)); typename Strategy::state_type state; centroid_range_state::apply(range, transformer, strategy, state); if ( strategy.result(state, centroid) ) { // translate the result back transformer.apply_reverse(centroid); return true; } } return false; } }; /*! \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. */ struct centroid_polygon_state { template static inline void apply(Polygon const& poly, PointTransformer const& transformer, Strategy const& strategy, typename Strategy::state_type& state) { typedef typename ring_type::type ring_type; typedef centroid_range_state::value> per_ring; per_ring::apply(exterior_ring(poly), transformer, strategy, state); typename interior_return_type::type rings = interior_rings(poly); for (typename detail::interior_iterator::type it = boost::begin(rings); it != boost::end(rings); ++it) { per_ring::apply(*it, transformer, strategy, state); } } }; struct centroid_polygon { template static inline bool apply(Polygon const& poly, Point& centroid, Strategy const& strategy) { if (range_ok(exterior_ring(poly), centroid)) { // prepare translation transformer translating_transformer transformer(*boost::begin(exterior_ring(poly))); typename Strategy::state_type state; centroid_polygon_state::apply(poly, transformer, strategy, state); if ( strategy.result(state, centroid) ) { // translate the result back transformer.apply_reverse(centroid); return true; } } return false; } }; /*! \brief Building block of a multi-point, to be used as Policy in the more generec centroid_multi */ struct centroid_multi_point_state { template static inline void apply(Point const& point, PointTransformer const& transformer, Strategy const& strategy, typename Strategy::state_type& state) { boost::ignore_unused(strategy); strategy.apply(static_cast(transformer.apply(point)), state); } }; /*! \brief Generic implementation which calls a policy to calculate the centroid of the total of its single-geometries \details The Policy is, in general, the single-version, with state. So detail::centroid::centroid_polygon_state is used as a policy for this detail::centroid::centroid_multi */ template struct centroid_multi { template static inline bool apply(Multi const& multi, Point& centroid, Strategy const& strategy) { #if ! defined(BOOST_GEOMETRY_CENTROID_NO_THROW) // If there is nothing in any of the ranges, it is not possible // to calculate the centroid if (geometry::is_empty(multi)) { BOOST_THROW_EXCEPTION(centroid_exception()); } #endif // prepare translation transformer translating_transformer transformer(multi); typename Strategy::state_type state; for (typename boost::range_iterator::type it = boost::begin(multi); it != boost::end(multi); ++it) { Policy::apply(*it, transformer, strategy, state); } if ( strategy.result(state, centroid) ) { // translate the result back transformer.apply_reverse(centroid); return true; } return false; } }; template struct centroid_linear_areal { template static inline void apply(Geometry const& geom, Point& centroid, Strategy const& strategy) { if ( ! Algorithm::apply(geom, centroid, strategy) ) { geometry::point_on_border(centroid, geom); } } }; }} // namespace detail::centroid #endif // DOXYGEN_NO_DETAIL #ifndef DOXYGEN_NO_DISPATCH namespace dispatch { template < typename Geometry, typename Tag = typename tag::type > struct centroid: not_implemented {}; template struct centroid : detail::centroid::centroid_point {}; template struct centroid : detail::centroid::centroid_indexed {}; template struct centroid : detail::centroid::centroid_indexed {}; template struct centroid : detail::centroid::centroid_linear_areal < detail::centroid::centroid_range::value> > {}; template struct centroid : detail::centroid::centroid_linear_areal < detail::centroid::centroid_range > {}; template struct centroid : detail::centroid::centroid_linear_areal < detail::centroid::centroid_polygon > {}; template struct centroid : detail::centroid::centroid_linear_areal < detail::centroid::centroid_multi < detail::centroid::centroid_range_state > > {}; template struct centroid : detail::centroid::centroid_linear_areal < detail::centroid::centroid_multi < detail::centroid::centroid_polygon_state > > {}; template struct centroid : detail::centroid::centroid_multi < detail::centroid::centroid_multi_point_state > {}; } // namespace dispatch #endif // DOXYGEN_NO_DISPATCH namespace resolve_strategy { template struct centroid { template static inline void apply(Geometry const& geometry, Point& out, Strategy const& strategy) { dispatch::centroid::apply(geometry, out, strategy); } template static inline void apply(Geometry const& geometry, Point& out, default_strategy) { 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; dispatch::centroid::apply(geometry, out, strategy_type()); } }; } // namespace resolve_strategy namespace resolve_variant { template struct centroid { template static inline void apply(Geometry const& geometry, Point& out, Strategy const& strategy) { concepts::check_concepts_and_equal_dimensions(); resolve_strategy::centroid::apply(geometry, out, strategy); } }; template struct centroid > { template struct visitor: boost::static_visitor { Point& m_out; Strategy const& m_strategy; visitor(Point& out, Strategy const& strategy) : m_out(out), m_strategy(strategy) {} template void operator()(Geometry const& geometry) const { centroid::apply(geometry, m_out, m_strategy); } }; template static inline void apply(boost::variant const& geometry, Point& out, Strategy const& strategy) { boost::apply_visitor(visitor(out, strategy), geometry); } }; } // namespace resolve_variant /*! \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) { resolve_variant::centroid::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) { geometry::centroid(geometry, c, default_strategy()); } /*! \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) { Point c; geometry::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) { Point c; geometry::centroid(geometry, c, strategy); return c; } }} // namespace boost::geometry #endif // BOOST_GEOMETRY_ALGORITHMS_CENTROID_HPP