1 // Boost.Geometry (aka GGL, Generic Geometry Library)
3 // Copyright (c) 2007-2015 Barend Gehrels, Amsterdam, the Netherlands.
4 // Copyright (c) 2008-2015 Bruno Lalande, Paris, France.
5 // Copyright (c) 2009-2015 Mateusz Loskot, London, UK.
6 // Copyright (c) 2014-2017 Adam Wulkiewicz, Lodz, Poland.
8 // This file was modified by Oracle on 2014, 2015.
9 // Modifications copyright (c) 2014-2015 Oracle and/or its affiliates.
11 // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
12 // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle
14 // Parts of Boost.Geometry are redesigned from Geodan's Geographic Library
15 // (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands.
17 // Use, modification and distribution is subject to the Boost Software License,
18 // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
19 // http://www.boost.org/LICENSE_1_0.txt)
21 #ifndef BOOST_GEOMETRY_ALGORITHMS_CENTROID_HPP
22 #define BOOST_GEOMETRY_ALGORITHMS_CENTROID_HPP
27 #include <boost/core/ignore_unused.hpp>
28 #include <boost/range.hpp>
29 #include <boost/throw_exception.hpp>
31 #include <boost/variant/apply_visitor.hpp>
32 #include <boost/variant/static_visitor.hpp>
33 #include <boost/variant/variant_fwd.hpp>
35 #include <boost/geometry/core/closure.hpp>
36 #include <boost/geometry/core/cs.hpp>
37 #include <boost/geometry/core/coordinate_dimension.hpp>
38 #include <boost/geometry/core/exception.hpp>
39 #include <boost/geometry/core/exterior_ring.hpp>
40 #include <boost/geometry/core/interior_rings.hpp>
41 #include <boost/geometry/core/tag_cast.hpp>
42 #include <boost/geometry/core/tags.hpp>
43 #include <boost/geometry/core/point_type.hpp>
45 #include <boost/geometry/geometries/concepts/check.hpp>
47 #include <boost/geometry/algorithms/assign.hpp>
48 #include <boost/geometry/algorithms/convert.hpp>
49 #include <boost/geometry/algorithms/detail/interior_iterator.hpp>
50 #include <boost/geometry/algorithms/detail/point_on_border.hpp>
51 #include <boost/geometry/algorithms/not_implemented.hpp>
52 #include <boost/geometry/strategies/centroid.hpp>
53 #include <boost/geometry/strategies/concepts/centroid_concept.hpp>
54 #include <boost/geometry/strategies/default_strategy.hpp>
55 #include <boost/geometry/views/closeable_view.hpp>
57 #include <boost/geometry/util/for_each_coordinate.hpp>
58 #include <boost/geometry/util/select_coordinate_type.hpp>
60 #include <boost/geometry/algorithms/is_empty.hpp>
62 #include <boost/geometry/algorithms/detail/centroid/translating_transformer.hpp>
65 namespace boost { namespace geometry
69 #if ! defined(BOOST_GEOMETRY_CENTROID_NO_THROW)
72 \brief Centroid Exception
74 \details The centroid_exception is thrown if the free centroid function is called with
75 geometries for which the centroid cannot be calculated. For example: a linestring
76 without points, a polygon without points, an empty multi-geometry.
79 \* [link geometry.reference.algorithms.centroid the centroid function]
83 class centroid_exception : public geometry::exception
88 \brief The default constructor
90 inline centroid_exception() {}
93 \brief Returns the explanatory string.
94 \return Pointer to a null-terminated string with explanatory information.
96 virtual char const* what() const throw()
98 return "Boost.Geometry Centroid calculation exception";
105 #ifndef DOXYGEN_NO_DETAIL
106 namespace detail { namespace centroid
109 struct centroid_point
111 template<typename Point, typename PointCentroid, typename Strategy>
112 static inline void apply(Point const& point, PointCentroid& centroid,
115 geometry::convert(point, centroid);
123 std::size_t Dimension = 0,
124 std::size_t DimensionCount = dimension<Indexed>::type::value
126 struct centroid_indexed_calculator
128 typedef typename select_coordinate_type
131 >::type coordinate_type;
132 static inline void apply(Indexed const& indexed, Point& centroid)
134 coordinate_type const c1 = get<min_corner, Dimension>(indexed);
135 coordinate_type const c2 = get<max_corner, Dimension>(indexed);
136 coordinate_type m = c1 + c2;
137 coordinate_type const two = 2;
140 set<Dimension>(centroid, m);
142 centroid_indexed_calculator
144 Indexed, Point, Dimension + 1
145 >::apply(indexed, centroid);
150 template<typename Indexed, typename Point, std::size_t DimensionCount>
151 struct centroid_indexed_calculator<Indexed, Point, DimensionCount, DimensionCount>
153 static inline void apply(Indexed const& , Point& )
159 struct centroid_indexed
161 template<typename Indexed, typename Point, typename Strategy>
162 static inline void apply(Indexed const& indexed, Point& centroid,
165 centroid_indexed_calculator
168 >::apply(indexed, centroid);
173 // There is one thing where centroid is different from e.g. within.
174 // If the ring has only one point, it might make sense that
175 // that point is the centroid.
176 template<typename Point, typename Range>
177 inline bool range_ok(Range const& range, Point& centroid)
179 std::size_t const n = boost::size(range);
186 #if ! defined(BOOST_GEOMETRY_CENTROID_NO_THROW)
187 BOOST_THROW_EXCEPTION(centroid_exception());
194 // Take over the first point in a "coordinate neutral way"
195 geometry::convert(*boost::begin(range), centroid);
198 //return true; // unreachable
202 \brief Calculate the centroid of a Ring or a Linestring.
204 template <closure_selector Closure>
205 struct centroid_range_state
207 template<typename Ring, typename PointTransformer, typename Strategy>
208 static inline void apply(Ring const& ring,
209 PointTransformer const& transformer,
210 Strategy const& strategy,
211 typename Strategy::state_type& state)
213 boost::ignore_unused(strategy);
215 typedef typename geometry::point_type<Ring const>::type point_type;
216 typedef typename closeable_view<Ring const, Closure>::type view_type;
218 typedef typename boost::range_iterator<view_type const>::type iterator_type;
220 view_type view(ring);
221 iterator_type it = boost::begin(view);
222 iterator_type end = boost::end(view);
226 typename PointTransformer::result_type
227 previous_pt = transformer.apply(*it);
229 for ( ++it ; it != end ; ++it)
231 typename PointTransformer::result_type
232 pt = transformer.apply(*it);
234 strategy.apply(static_cast<point_type const&>(previous_pt),
235 static_cast<point_type const&>(pt),
244 template <closure_selector Closure>
245 struct centroid_range
247 template<typename Range, typename Point, typename Strategy>
248 static inline bool apply(Range const& range, Point& centroid,
249 Strategy const& strategy)
251 if (range_ok(range, centroid))
253 // prepare translation transformer
254 translating_transformer<Range> transformer(*boost::begin(range));
256 typename Strategy::state_type state;
257 centroid_range_state<Closure>::apply(range, transformer,
260 if ( strategy.result(state, centroid) )
262 // translate the result back
263 transformer.apply_reverse(centroid);
274 \brief Centroid of a polygon.
275 \note Because outer ring is clockwise, inners are counter clockwise,
276 triangle approach is OK and works for polygons with rings.
278 struct centroid_polygon_state
280 template<typename Polygon, typename PointTransformer, typename Strategy>
281 static inline void apply(Polygon const& poly,
282 PointTransformer const& transformer,
283 Strategy const& strategy,
284 typename Strategy::state_type& state)
286 typedef typename ring_type<Polygon>::type ring_type;
287 typedef centroid_range_state<geometry::closure<ring_type>::value> per_ring;
289 per_ring::apply(exterior_ring(poly), transformer, strategy, state);
291 typename interior_return_type<Polygon const>::type
292 rings = interior_rings(poly);
294 for (typename detail::interior_iterator<Polygon const>::type
295 it = boost::begin(rings); it != boost::end(rings); ++it)
297 per_ring::apply(*it, transformer, strategy, state);
302 struct centroid_polygon
304 template<typename Polygon, typename Point, typename Strategy>
305 static inline bool apply(Polygon const& poly, Point& centroid,
306 Strategy const& strategy)
308 if (range_ok(exterior_ring(poly), centroid))
310 // prepare translation transformer
311 translating_transformer<Polygon>
312 transformer(*boost::begin(exterior_ring(poly)));
314 typename Strategy::state_type state;
315 centroid_polygon_state::apply(poly, transformer, strategy, state);
317 if ( strategy.result(state, centroid) )
319 // translate the result back
320 transformer.apply_reverse(centroid);
331 \brief Building block of a multi-point, to be used as Policy in the
332 more generec centroid_multi
334 struct centroid_multi_point_state
336 template <typename Point, typename PointTransformer, typename Strategy>
337 static inline void apply(Point const& point,
338 PointTransformer const& transformer,
339 Strategy const& strategy,
340 typename Strategy::state_type& state)
342 boost::ignore_unused(strategy);
343 strategy.apply(static_cast<Point const&>(transformer.apply(point)),
350 \brief Generic implementation which calls a policy to calculate the
351 centroid of the total of its single-geometries
352 \details The Policy is, in general, the single-version, with state. So
353 detail::centroid::centroid_polygon_state is used as a policy for this
354 detail::centroid::centroid_multi
357 template <typename Policy>
358 struct centroid_multi
360 template <typename Multi, typename Point, typename Strategy>
361 static inline bool apply(Multi const& multi,
363 Strategy const& strategy)
365 #if ! defined(BOOST_GEOMETRY_CENTROID_NO_THROW)
366 // If there is nothing in any of the ranges, it is not possible
367 // to calculate the centroid
368 if (geometry::is_empty(multi))
370 BOOST_THROW_EXCEPTION(centroid_exception());
374 // prepare translation transformer
375 translating_transformer<Multi> transformer(multi);
377 typename Strategy::state_type state;
379 for (typename boost::range_iterator<Multi const>::type
380 it = boost::begin(multi);
381 it != boost::end(multi);
384 Policy::apply(*it, transformer, strategy, state);
387 if ( strategy.result(state, centroid) )
389 // translate the result back
390 transformer.apply_reverse(centroid);
399 template <typename Algorithm>
400 struct centroid_linear_areal
402 template <typename Geometry, typename Point, typename Strategy>
403 static inline void apply(Geometry const& geom,
405 Strategy const& strategy)
407 if ( ! Algorithm::apply(geom, centroid, strategy) )
409 geometry::point_on_border(centroid, geom);
415 }} // namespace detail::centroid
416 #endif // DOXYGEN_NO_DETAIL
419 #ifndef DOXYGEN_NO_DISPATCH
426 typename Tag = typename tag<Geometry>::type
428 struct centroid: not_implemented<Tag>
431 template <typename Geometry>
432 struct centroid<Geometry, point_tag>
433 : detail::centroid::centroid_point
436 template <typename Box>
437 struct centroid<Box, box_tag>
438 : detail::centroid::centroid_indexed
441 template <typename Segment>
442 struct centroid<Segment, segment_tag>
443 : detail::centroid::centroid_indexed
446 template <typename Ring>
447 struct centroid<Ring, ring_tag>
448 : detail::centroid::centroid_linear_areal
450 detail::centroid::centroid_range<geometry::closure<Ring>::value>
454 template <typename Linestring>
455 struct centroid<Linestring, linestring_tag>
456 : detail::centroid::centroid_linear_areal
458 detail::centroid::centroid_range<closed>
462 template <typename Polygon>
463 struct centroid<Polygon, polygon_tag>
464 : detail::centroid::centroid_linear_areal
466 detail::centroid::centroid_polygon
470 template <typename MultiLinestring>
471 struct centroid<MultiLinestring, multi_linestring_tag>
472 : detail::centroid::centroid_linear_areal
474 detail::centroid::centroid_multi
476 detail::centroid::centroid_range_state<closed>
481 template <typename MultiPolygon>
482 struct centroid<MultiPolygon, multi_polygon_tag>
483 : detail::centroid::centroid_linear_areal
485 detail::centroid::centroid_multi
487 detail::centroid::centroid_polygon_state
492 template <typename MultiPoint>
493 struct centroid<MultiPoint, multi_point_tag>
494 : detail::centroid::centroid_multi
496 detail::centroid::centroid_multi_point_state
501 } // namespace dispatch
502 #endif // DOXYGEN_NO_DISPATCH
505 namespace resolve_strategy {
507 template <typename Geometry>
510 template <typename Point, typename Strategy>
511 static inline void apply(Geometry const& geometry, Point& out, Strategy const& strategy)
513 dispatch::centroid<Geometry>::apply(geometry, out, strategy);
516 template <typename Point>
517 static inline void apply(Geometry const& geometry, Point& out, default_strategy)
519 typedef typename strategy::centroid::services::default_strategy
521 typename cs_tag<Geometry>::type,
524 typename tag<Geometry>::type,
529 dimension<Geometry>::type::value,
532 >::type strategy_type;
534 dispatch::centroid<Geometry>::apply(geometry, out, strategy_type());
538 } // namespace resolve_strategy
541 namespace resolve_variant {
543 template <typename Geometry>
546 template <typename Point, typename Strategy>
547 static inline void apply(Geometry const& geometry, Point& out, Strategy const& strategy)
549 concepts::check_concepts_and_equal_dimensions<Point, Geometry const>();
550 resolve_strategy::centroid<Geometry>::apply(geometry, out, strategy);
554 template <BOOST_VARIANT_ENUM_PARAMS(typename T)>
555 struct centroid<boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)> >
557 template <typename Point, typename Strategy>
558 struct visitor: boost::static_visitor<void>
561 Strategy const& m_strategy;
563 visitor(Point& out, Strategy const& strategy)
564 : m_out(out), m_strategy(strategy)
567 template <typename Geometry>
568 void operator()(Geometry const& geometry) const
570 centroid<Geometry>::apply(geometry, m_out, m_strategy);
574 template <typename Point, typename Strategy>
576 apply(boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)> const& geometry,
578 Strategy const& strategy)
580 boost::apply_visitor(visitor<Point, Strategy>(out, strategy), geometry);
584 } // namespace resolve_variant
588 \brief \brief_calc{centroid} \brief_strategy
590 \details \details_calc{centroid,geometric center (or: center of mass)}. \details_strategy_reasons
591 \tparam Geometry \tparam_geometry
592 \tparam Point \tparam_point
593 \tparam Strategy \tparam_strategy{Centroid}
594 \param geometry \param_geometry
595 \param c \param_point \param_set{centroid}
596 \param strategy \param_strategy{centroid}
598 \qbk{distinguish,with strategy}
599 \qbk{[include reference/algorithms/centroid.qbk]}
600 \qbk{[include reference/algorithms/centroid_strategies.qbk]}
604 template<typename Geometry, typename Point, typename Strategy>
605 inline void centroid(Geometry const& geometry, Point& c,
606 Strategy const& strategy)
608 resolve_variant::centroid<Geometry>::apply(geometry, c, strategy);
613 \brief \brief_calc{centroid}
615 \details \details_calc{centroid,geometric center (or: center of mass)}. \details_default_strategy
616 \tparam Geometry \tparam_geometry
617 \tparam Point \tparam_point
618 \param geometry \param_geometry
619 \param c The calculated centroid will be assigned to this point reference
621 \qbk{[include reference/algorithms/centroid.qbk]}
628 template<typename Geometry, typename Point>
629 inline void centroid(Geometry const& geometry, Point& c)
631 geometry::centroid(geometry, c, default_strategy());
636 \brief \brief_calc{centroid}
638 \details \details_calc{centroid,geometric center (or: center of mass)}. \details_return{centroid}.
639 \tparam Point \tparam_point
640 \tparam Geometry \tparam_geometry
641 \param geometry \param_geometry
642 \return \return_calc{centroid}
644 \qbk{[include reference/algorithms/centroid.qbk]}
646 template<typename Point, typename Geometry>
647 inline Point return_centroid(Geometry const& geometry)
650 geometry::centroid(geometry, c);
655 \brief \brief_calc{centroid} \brief_strategy
657 \details \details_calc{centroid,geometric center (or: center of mass)}. \details_return{centroid}. \details_strategy_reasons
658 \tparam Point \tparam_point
659 \tparam Geometry \tparam_geometry
660 \tparam Strategy \tparam_strategy{centroid}
661 \param geometry \param_geometry
662 \param strategy \param_strategy{centroid}
663 \return \return_calc{centroid}
665 \qbk{distinguish,with strategy}
666 \qbk{[include reference/algorithms/centroid.qbk]}
667 \qbk{[include reference/algorithms/centroid_strategies.qbk]}
669 template<typename Point, typename Geometry, typename Strategy>
670 inline Point return_centroid(Geometry const& geometry, Strategy const& strategy)
673 geometry::centroid(geometry, c, strategy);
678 }} // namespace boost::geometry
681 #endif // BOOST_GEOMETRY_ALGORITHMS_CENTROID_HPP