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.
7 // This file was modified by Oracle on 2015, 2018.
8 // Modifications copyright (c) 2015, 2018, Oracle and/or its affiliates.
10 // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
12 // Parts of Boost.Geometry are redesigned from Geodan's Geographic Library
13 // (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands.
15 // Use, modification and distribution is subject to the Boost Software License,
16 // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
17 // http://www.boost.org/LICENSE_1_0.txt)
19 #ifndef BOOST_GEOMETRY_STRATEGIES_CARTESIAN_CENTROID_BASHEIN_DETMER_HPP
20 #define BOOST_GEOMETRY_STRATEGIES_CARTESIAN_CENTROID_BASHEIN_DETMER_HPP
25 #include <boost/math/special_functions/fpclassify.hpp>
26 #include <boost/mpl/if.hpp>
27 #include <boost/numeric/conversion/cast.hpp>
28 #include <boost/type_traits/is_void.hpp>
30 #include <boost/geometry/arithmetic/determinant.hpp>
31 #include <boost/geometry/core/coordinate_type.hpp>
32 #include <boost/geometry/core/point_type.hpp>
33 #include <boost/geometry/strategies/centroid.hpp>
34 #include <boost/geometry/util/math.hpp>
35 #include <boost/geometry/util/select_coordinate_type.hpp>
38 namespace boost { namespace geometry
41 // Note: when calling the namespace "centroid", it sometimes,
42 // somehow, in gcc, gives compilation problems (confusion with function centroid).
44 namespace strategy { namespace centroid
50 \brief Centroid calculation using algorithm Bashein / Detmer
52 \details Calculates centroid using triangulation method published by
54 \tparam Point point type of centroid to calculate
55 \tparam PointOfSegment point type of segments, defaults to Point
56 \tparam CalculationType \tparam_calculation
58 \author Adapted from "Centroid of a Polygon" by
59 Gerard Bashein and Paul R. Detmer<em>,
60 in "Graphics Gems IV", Academic Press, 1994</em>
65 [link geometry.reference.algorithms.centroid.centroid_3_with_strategy centroid (with strategy)]
71 The algorithm gives the same results as Oracle and PostGIS but
73 (tried 5.0.21 / 5.0.45 / 5.0.51a / 5.1.23).
76 - this: POINT(4.06923363095238 1.65055803571429)
77 - geolib: POINT(4.07254 1.66819)
78 - MySQL: POINT(3.6636363636364 1.6272727272727)'
79 - PostGIS: POINT(4.06923363095238 1.65055803571429)
80 - Oracle: 4.06923363095238 1.65055803571429
81 - SQL Server: POINT(4.06923362245959 1.65055804168294)
84 - \b MySQL/PostGIS: select AsText(Centroid(GeomFromText(
85 'POLYGON((2 1.3,2.4 1.7,2.8 1.8,3.4 1.2,3.7 1.6,3.4 2,4.1 3,5.3 2.6
86 ,5.4 1.2,4.9 0.8,2.9 0.7,2 1.3))')))
87 - \b Oracle: select sdo_geom.sdo_centroid(sdo_geometry(2003, null, null,
88 sdo_elem_info_array(1, 1003, 1), sdo_ordinate_array(
89 2,1.3,2.4,1.7,2.8,1.8,3.4,1.2,3.7,1.6,3.4,2,4.1,3,5.3,2.6
90 ,5.4,1.2,4.9,0.8,2.9,0.7,2,1.3))
91 , mdsys.sdo_dim_array(mdsys.sdo_dim_element('x',0,10,.00000005)
92 ,mdsys.sdo_dim_element('y',0,10,.00000005)))
94 - \b SQL Server 2008: select geometry::STGeomFromText(
95 'POLYGON((2 1.3,2.4 1.7,2.8 1.8,3.4 1.2,3.7 1.6,3.4 2,4.1 3,5.3 2.6
96 ,5.4 1.2,4.9 0.8,2.9 0.7,2 1.3))',0)
101 - this: POINT(4.04663 1.6349)
102 - geolib: POINT(4.04675 1.65735)
103 - MySQL: POINT(3.6090580503834 1.607573932092)
104 - PostGIS: POINT(4.0466265060241 1.63489959839357)
105 - Oracle: 4.0466265060241 1.63489959839357
106 - SQL Server: POINT(4.0466264962959677 1.6348996057331333)
109 - \b MySQL/PostGIS: select AsText(Centroid(GeomFromText(
110 'POLYGON((2 1.3,2.4 1.7,2.8 1.8,3.4 1.2
111 ,3.7 1.6,3.4 2,4.1 3,5.3 2.6,5.4 1.2,4.9 0.8,2.9 0.7,2 1.3)
112 ,(4 2,4.2 1.4,4.8 1.9,4.4 2.2,4 2))')));
113 - \b Oracle: select sdo_geom.sdo_centroid(sdo_geometry(2003, null, null
114 , sdo_elem_info_array(1, 1003, 1, 25, 2003, 1)
115 , sdo_ordinate_array(2,1.3,2.4,1.7,2.8,1.8,3.4,1.2,3.7,1.6,3.4,
116 2,4.1,3,5.3,2.6,5.4,1.2,4.9,0.8,2.9,0.7,2,1.3,4,2, 4.2,1.4,
117 4.8,1.9, 4.4,2.2, 4,2))
118 , mdsys.sdo_dim_array(mdsys.sdo_dim_element('x',0,10,.00000005)
119 ,mdsys.sdo_dim_element('y',0,10,.00000005)))
125 typename PointOfSegment = Point,
126 typename CalculationType = void
131 // If user specified a calculation type, use that type,
132 // whatever it is and whatever the point-type(s) are.
133 // Else, use the most appropriate coordinate type
134 // of the two points, but at least double
138 boost::is_void<CalculationType>::type::value,
139 typename select_most_precise
141 typename select_coordinate_type
149 >::type calculation_type;
151 /*! subclass to keep state */
154 friend class bashein_detmer;
156 calculation_type sum_a2;
157 calculation_type sum_x;
158 calculation_type sum_y;
163 , sum_a2(calculation_type())
164 , sum_x(calculation_type())
165 , sum_y(calculation_type())
170 typedef sums state_type;
172 static inline void apply(PointOfSegment const& p1,
173 PointOfSegment const& p2, sums& state)
178 ai = x1 * y2 - x2 * y1;
180 sum_x += ai * (x1 + x2);
181 sum_y += ai * (y1 + y2);
183 return POINT(sum_x / (3 * sum_a2), sum_y / (3 * sum_a2) )
186 // Get coordinates and promote them to calculation_type
187 calculation_type const x1 = boost::numeric_cast<calculation_type>(get<0>(p1));
188 calculation_type const y1 = boost::numeric_cast<calculation_type>(get<1>(p1));
189 calculation_type const x2 = boost::numeric_cast<calculation_type>(get<0>(p2));
190 calculation_type const y2 = boost::numeric_cast<calculation_type>(get<1>(p2));
191 calculation_type const ai = geometry::detail::determinant<calculation_type>(p1, p2);
194 state.sum_x += ai * (x1 + x2);
195 state.sum_y += ai * (y1 + y2);
198 static inline bool result(sums const& state, Point& centroid)
200 calculation_type const zero = calculation_type();
201 if (state.count > 0 && ! math::equals(state.sum_a2, zero))
203 calculation_type const v3 = 3;
204 calculation_type const a3 = v3 * state.sum_a2;
206 typedef typename geometry::coordinate_type
209 >::type coordinate_type;
211 // Prevent NaN centroid coordinates
212 if (boost::math::isfinite(a3))
214 // NOTE: above calculation_type is checked, not the centroid coordinate_type
215 // which means that the centroid can still be filled with INF
216 // if e.g. calculation_type is double and centroid contains floats
218 boost::numeric_cast<coordinate_type>(state.sum_x / a3));
220 boost::numeric_cast<coordinate_type>(state.sum_y / a3));
230 #ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS
235 // Register this strategy for rings and (multi)polygons, in two dimensions
236 template <typename Point, typename Geometry>
237 struct default_strategy<cartesian_tag, areal_tag, 2, Point, Geometry>
239 typedef bashein_detmer
242 typename point_type<Geometry>::type
247 } // namespace services
250 #endif // DOXYGEN_NO_STRATEGY_SPECIALIZATIONS
253 }} // namespace strategy::centroid
256 }} // namespace boost::geometry
259 #endif // BOOST_GEOMETRY_STRATEGIES_CARTESIAN_CENTROID_BASHEIN_DETMER_HPP