![]() |
|
|||
File indexing completed on 2025-09-18 08:44:41
0001 // Boost.Geometry (aka GGL, Generic Geometry Library) 0002 0003 // Copyright (c) 2007-2015 Barend Gehrels, Amsterdam, the Netherlands. 0004 // Copyright (c) 2008-2015 Bruno Lalande, Paris, France. 0005 // Copyright (c) 2009-2015 Mateusz Loskot, London, UK. 0006 0007 // This file was modified by Oracle on 2015-2021. 0008 // Modifications copyright (c) 2015-2021, Oracle and/or its affiliates. 0009 // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle 0010 0011 // Parts of Boost.Geometry are redesigned from Geodan's Geographic Library 0012 // (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. 0013 0014 // Use, modification and distribution is subject to the Boost Software License, 0015 // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at 0016 // http://www.boost.org/LICENSE_1_0.txt) 0017 0018 #ifndef BOOST_GEOMETRY_STRATEGIES_CARTESIAN_CENTROID_BASHEIN_DETMER_HPP 0019 #define BOOST_GEOMETRY_STRATEGIES_CARTESIAN_CENTROID_BASHEIN_DETMER_HPP 0020 0021 0022 #include <cstddef> 0023 0024 #include <boost/math/special_functions/fpclassify.hpp> 0025 0026 #include <boost/geometry/arithmetic/determinant.hpp> 0027 #include <boost/geometry/core/coordinate_type.hpp> 0028 #include <boost/geometry/core/point_type.hpp> 0029 #include <boost/geometry/strategies/centroid.hpp> 0030 #include <boost/geometry/util/math.hpp> 0031 #include <boost/geometry/util/numeric_cast.hpp> 0032 #include <boost/geometry/util/select_most_precise.hpp> 0033 0034 0035 namespace boost { namespace geometry 0036 { 0037 0038 // Note: when calling the namespace "centroid", it sometimes, 0039 // somehow, in gcc, gives compilation problems (confusion with function centroid). 0040 0041 namespace strategy { namespace centroid 0042 { 0043 0044 0045 0046 /*! 0047 \brief Centroid calculation using algorithm Bashein / Detmer 0048 \ingroup strategies 0049 \details Calculates centroid using triangulation method published by 0050 Bashein / Detmer 0051 \tparam CalculationType \tparam_calculation 0052 0053 \author Adapted from "Centroid of a Polygon" by 0054 Gerard Bashein and Paul R. Detmer<em>, 0055 in "Graphics Gems IV", Academic Press, 1994</em> 0056 0057 0058 \qbk{ 0059 [heading See also] 0060 [link geometry.reference.algorithms.centroid.centroid_3_with_strategy centroid (with strategy)] 0061 } 0062 */ 0063 0064 /* 0065 \par Research notes 0066 The algorithm gives the same results as Oracle and PostGIS but 0067 differs from MySQL 0068 (tried 5.0.21 / 5.0.45 / 5.0.51a / 5.1.23). 0069 0070 Without holes: 0071 - this: POINT(4.06923363095238 1.65055803571429) 0072 - geolib: POINT(4.07254 1.66819) 0073 - MySQL: POINT(3.6636363636364 1.6272727272727)' 0074 - PostGIS: POINT(4.06923363095238 1.65055803571429) 0075 - Oracle: 4.06923363095238 1.65055803571429 0076 - SQL Server: POINT(4.06923362245959 1.65055804168294) 0077 0078 Statements: 0079 - \b MySQL/PostGIS: select AsText(Centroid(GeomFromText( 0080 '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 0081 ,5.4 1.2,4.9 0.8,2.9 0.7,2 1.3))'))) 0082 - \b Oracle: select sdo_geom.sdo_centroid(sdo_geometry(2003, null, null, 0083 sdo_elem_info_array(1, 1003, 1), sdo_ordinate_array( 0084 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 0085 ,5.4,1.2,4.9,0.8,2.9,0.7,2,1.3)) 0086 , mdsys.sdo_dim_array(mdsys.sdo_dim_element('x',0,10,.00000005) 0087 ,mdsys.sdo_dim_element('y',0,10,.00000005))) 0088 from dual 0089 - \b SQL Server 2008: select geometry::STGeomFromText( 0090 '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 0091 ,5.4 1.2,4.9 0.8,2.9 0.7,2 1.3))',0) 0092 .STCentroid() 0093 .STAsText() 0094 0095 With holes: 0096 - this: POINT(4.04663 1.6349) 0097 - geolib: POINT(4.04675 1.65735) 0098 - MySQL: POINT(3.6090580503834 1.607573932092) 0099 - PostGIS: POINT(4.0466265060241 1.63489959839357) 0100 - Oracle: 4.0466265060241 1.63489959839357 0101 - SQL Server: POINT(4.0466264962959677 1.6348996057331333) 0102 0103 Statements: 0104 - \b MySQL/PostGIS: select AsText(Centroid(GeomFromText( 0105 'POLYGON((2 1.3,2.4 1.7,2.8 1.8,3.4 1.2 0106 ,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) 0107 ,(4 2,4.2 1.4,4.8 1.9,4.4 2.2,4 2))'))); 0108 - \b Oracle: select sdo_geom.sdo_centroid(sdo_geometry(2003, null, null 0109 , sdo_elem_info_array(1, 1003, 1, 25, 2003, 1) 0110 , sdo_ordinate_array(2,1.3,2.4,1.7,2.8,1.8,3.4,1.2,3.7,1.6,3.4, 0111 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, 0112 4.8,1.9, 4.4,2.2, 4,2)) 0113 , mdsys.sdo_dim_array(mdsys.sdo_dim_element('x',0,10,.00000005) 0114 ,mdsys.sdo_dim_element('y',0,10,.00000005))) 0115 from dual 0116 */ 0117 template 0118 < 0119 typename Ignored1 = void, 0120 typename Ignored2 = void, 0121 typename CalculationType = void 0122 > 0123 class bashein_detmer 0124 { 0125 private : 0126 // If user specified a calculation type, use that type, 0127 // whatever it is and whatever the point-type(s) are. 0128 // Else, use the most appropriate coordinate type 0129 // of the two points, but at least double 0130 template <typename GeometryPoint, typename ResultPoint> 0131 struct calculation_type 0132 : std::conditional 0133 < 0134 std::is_void<CalculationType>::value, 0135 typename select_most_precise 0136 < 0137 coordinate_type_t<GeometryPoint>, 0138 coordinate_type_t<ResultPoint>, 0139 double 0140 >::type, 0141 CalculationType 0142 > 0143 {}; 0144 0145 /*! subclass to keep state */ 0146 template <typename GeometryPoint, typename ResultPoint> 0147 class sums 0148 { 0149 typedef typename calculation_type<GeometryPoint, ResultPoint>::type calc_type; 0150 0151 friend class bashein_detmer; 0152 std::size_t count; 0153 calc_type sum_a2; 0154 calc_type sum_x; 0155 calc_type sum_y; 0156 0157 public : 0158 inline sums() 0159 : count(0) 0160 , sum_a2(calc_type()) 0161 , sum_x(calc_type()) 0162 , sum_y(calc_type()) 0163 {} 0164 }; 0165 0166 public : 0167 template <typename GeometryPoint, typename ResultPoint> 0168 struct state_type 0169 { 0170 typedef sums<GeometryPoint, ResultPoint> type; 0171 }; 0172 0173 template <typename GeometryPoint, typename ResultPoint> 0174 static inline void apply(GeometryPoint const& p1, GeometryPoint const& p2, 0175 sums<GeometryPoint, ResultPoint>& state) 0176 { 0177 /* Algorithm: 0178 For each segment: 0179 begin 0180 ai = x1 * y2 - x2 * y1; 0181 sum_a2 += ai; 0182 sum_x += ai * (x1 + x2); 0183 sum_y += ai * (y1 + y2); 0184 end 0185 return POINT(sum_x / (3 * sum_a2), sum_y / (3 * sum_a2) ) 0186 */ 0187 0188 typedef typename calculation_type<GeometryPoint, ResultPoint>::type calc_type; 0189 0190 // Get coordinates and promote them to calculation_type 0191 calc_type const x1 = util::numeric_cast<calc_type>(get<0>(p1)); 0192 calc_type const y1 = util::numeric_cast<calc_type>(get<1>(p1)); 0193 calc_type const x2 = util::numeric_cast<calc_type>(get<0>(p2)); 0194 calc_type const y2 = util::numeric_cast<calc_type>(get<1>(p2)); 0195 calc_type const ai = geometry::detail::determinant<calc_type>(p1, p2); 0196 state.count++; 0197 state.sum_a2 += ai; 0198 state.sum_x += ai * (x1 + x2); 0199 state.sum_y += ai * (y1 + y2); 0200 } 0201 0202 template <typename GeometryPoint, typename ResultPoint> 0203 static inline bool result(sums<GeometryPoint, ResultPoint> const& state, 0204 ResultPoint& centroid) 0205 { 0206 typedef typename calculation_type<GeometryPoint, ResultPoint>::type calc_type; 0207 0208 calc_type const zero = calc_type(); 0209 if (state.count > 0 && ! math::equals(state.sum_a2, zero)) 0210 { 0211 calc_type const v3 = 3; 0212 calc_type const a3 = v3 * state.sum_a2; 0213 0214 using coordinate_type = geometry::coordinate_type_t<ResultPoint>; 0215 0216 // Prevent NaN centroid coordinates 0217 if (boost::math::isfinite(a3)) 0218 { 0219 // NOTE: above calculation_type is checked, not the centroid coordinate_type 0220 // which means that the centroid can still be filled with INF 0221 // if e.g. calculation_type is double and centroid contains floats 0222 set<0>(centroid, 0223 util::numeric_cast<coordinate_type>(state.sum_x / a3)); 0224 set<1>(centroid, 0225 util::numeric_cast<coordinate_type>(state.sum_y / a3)); 0226 return true; 0227 } 0228 } 0229 0230 return false; 0231 } 0232 0233 }; 0234 0235 #ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS 0236 0237 namespace services 0238 { 0239 0240 // Register this strategy for rings and (multi)polygons, in two dimensions 0241 template <typename Point, typename Geometry> 0242 struct default_strategy<cartesian_tag, areal_tag, 2, Point, Geometry> 0243 { 0244 typedef bashein_detmer 0245 < 0246 Point, 0247 point_type_t<Geometry> 0248 > type; 0249 }; 0250 0251 0252 } // namespace services 0253 0254 0255 #endif // DOXYGEN_NO_STRATEGY_SPECIALIZATIONS 0256 0257 0258 }} // namespace strategy::centroid 0259 0260 0261 }} // namespace boost::geometry 0262 0263 0264 #endif // BOOST_GEOMETRY_STRATEGIES_CARTESIAN_CENTROID_BASHEIN_DETMER_HPP
[ Source navigation ] | [ Diff markup ] | [ Identifier search ] | [ general search ] |
This page was automatically generated by the 2.3.7 LXR engine. The LXR team |
![]() ![]() |