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.
8 // Modifications copyright (c) 2015 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/select_coordinate_type.hpp>
37 namespace boost { namespace geometry
40 // Note: when calling the namespace "centroid", it sometimes,
41 // somehow, in gcc, gives compilation problems (confusion with function centroid).
43 namespace strategy { namespace centroid
49 \brief Centroid calculation using algorithm Bashein / Detmer
51 \details Calculates centroid using triangulation method published by
53 \tparam Point point type of centroid to calculate
54 \tparam PointOfSegment point type of segments, defaults to Point
55 \tparam CalculationType \tparam_calculation
57 \author Adapted from "Centroid of a Polygon" by
58 Gerard Bashein and Paul R. Detmer<em>,
59 in "Graphics Gems IV", Academic Press, 1994</em>
64 [link geometry.reference.algorithms.centroid.centroid_3_with_strategy centroid (with strategy)]
70 The algorithm gives the same results as Oracle and PostGIS but
72 (tried 5.0.21 / 5.0.45 / 5.0.51a / 5.1.23).
75 - this: POINT(4.06923363095238 1.65055803571429)
76 - geolib: POINT(4.07254 1.66819)
77 - MySQL: POINT(3.6636363636364 1.6272727272727)'
78 - PostGIS: POINT(4.06923363095238 1.65055803571429)
79 - Oracle: 4.06923363095238 1.65055803571429
80 - SQL Server: POINT(4.06923362245959 1.65055804168294)
83 - \b MySQL/PostGIS: select AsText(Centroid(GeomFromText(
84 '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
85 ,5.4 1.2,4.9 0.8,2.9 0.7,2 1.3))')))
86 - \b Oracle: select sdo_geom.sdo_centroid(sdo_geometry(2003, null, null,
87 sdo_elem_info_array(1, 1003, 1), sdo_ordinate_array(
88 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
89 ,5.4,1.2,4.9,0.8,2.9,0.7,2,1.3))
90 , mdsys.sdo_dim_array(mdsys.sdo_dim_element('x',0,10,.00000005)
91 ,mdsys.sdo_dim_element('y',0,10,.00000005)))
93 - \b SQL Server 2008: select geometry::STGeomFromText(
94 '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
95 ,5.4 1.2,4.9 0.8,2.9 0.7,2 1.3))',0)
100 - this: POINT(4.04663 1.6349)
101 - geolib: POINT(4.04675 1.65735)
102 - MySQL: POINT(3.6090580503834 1.607573932092)
103 - PostGIS: POINT(4.0466265060241 1.63489959839357)
104 - Oracle: 4.0466265060241 1.63489959839357
105 - SQL Server: POINT(4.0466264962959677 1.6348996057331333)
108 - \b MySQL/PostGIS: select AsText(Centroid(GeomFromText(
109 'POLYGON((2 1.3,2.4 1.7,2.8 1.8,3.4 1.2
110 ,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)
111 ,(4 2,4.2 1.4,4.8 1.9,4.4 2.2,4 2))')));
112 - \b Oracle: select sdo_geom.sdo_centroid(sdo_geometry(2003, null, null
113 , sdo_elem_info_array(1, 1003, 1, 25, 2003, 1)
114 , sdo_ordinate_array(2,1.3,2.4,1.7,2.8,1.8,3.4,1.2,3.7,1.6,3.4,
115 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,
116 4.8,1.9, 4.4,2.2, 4,2))
117 , mdsys.sdo_dim_array(mdsys.sdo_dim_element('x',0,10,.00000005)
118 ,mdsys.sdo_dim_element('y',0,10,.00000005)))
124 typename PointOfSegment = Point,
125 typename CalculationType = void
130 // If user specified a calculation type, use that type,
131 // whatever it is and whatever the point-type(s) are.
132 // Else, use the most appropriate coordinate type
133 // of the two points, but at least double
137 boost::is_void<CalculationType>::type::value,
138 typename select_most_precise
140 typename select_coordinate_type
148 >::type calculation_type;
150 /*! subclass to keep state */
153 friend class bashein_detmer;
155 calculation_type sum_a2;
156 calculation_type sum_x;
157 calculation_type sum_y;
162 , sum_a2(calculation_type())
163 , sum_x(calculation_type())
164 , sum_y(calculation_type())
169 typedef sums state_type;
171 static inline void apply(PointOfSegment const& p1,
172 PointOfSegment const& p2, sums& state)
177 ai = x1 * y2 - x2 * y1;
179 sum_x += ai * (x1 + x2);
180 sum_y += ai * (y1 + y2);
182 return POINT(sum_x / (3 * sum_a2), sum_y / (3 * sum_a2) )
185 // Get coordinates and promote them to calculation_type
186 calculation_type const x1 = boost::numeric_cast<calculation_type>(get<0>(p1));
187 calculation_type const y1 = boost::numeric_cast<calculation_type>(get<1>(p1));
188 calculation_type const x2 = boost::numeric_cast<calculation_type>(get<0>(p2));
189 calculation_type const y2 = boost::numeric_cast<calculation_type>(get<1>(p2));
190 calculation_type const ai = geometry::detail::determinant<calculation_type>(p1, p2);
193 state.sum_x += ai * (x1 + x2);
194 state.sum_y += ai * (y1 + y2);
197 static inline bool result(sums const& state, Point& centroid)
199 calculation_type const zero = calculation_type();
200 if (state.count > 0 && ! math::equals(state.sum_a2, zero))
202 calculation_type const v3 = 3;
203 calculation_type const a3 = v3 * state.sum_a2;
205 typedef typename geometry::coordinate_type
208 >::type coordinate_type;
210 // Prevent NaN centroid coordinates
211 if (boost::math::isfinite(a3))
213 // NOTE: above calculation_type is checked, not the centroid coordinate_type
214 // which means that the centroid can still be filled with INF
215 // if e.g. calculation_type is double and centroid contains floats
217 boost::numeric_cast<coordinate_type>(state.sum_x / a3));
219 boost::numeric_cast<coordinate_type>(state.sum_y / a3));
229 #ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS
234 // Register this strategy for rings and (multi)polygons, in two dimensions
235 template <typename Point, typename Geometry>
236 struct default_strategy<cartesian_tag, areal_tag, 2, Point, Geometry>
238 typedef bashein_detmer
241 typename point_type<Geometry>::type
246 } // namespace services
249 #endif // DOXYGEN_NO_STRATEGY_SPECIALIZATIONS
252 }} // namespace strategy::centroid
255 }} // namespace boost::geometry
258 #endif // BOOST_GEOMETRY_STRATEGIES_CARTESIAN_CENTROID_BASHEIN_DETMER_HPP