centroid_bashein_detmer.hpp 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. // Boost.Geometry (aka GGL, Generic Geometry Library)
  2. // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
  3. // Copyright (c) 2008-2012 Bruno Lalande, Paris, France.
  4. // Copyright (c) 2009-2012 Mateusz Loskot, London, UK.
  5. // Parts of Boost.Geometry are redesigned from Geodan's Geographic Library
  6. // (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands.
  7. // Use, modification and distribution is subject to the Boost Software License,
  8. // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
  9. // http://www.boost.org/LICENSE_1_0.txt)
  10. #ifndef BOOST_GEOMETRY_STRATEGIES_CARTESIAN_CENTROID_BASHEIN_DETMER_HPP
  11. #define BOOST_GEOMETRY_STRATEGIES_CARTESIAN_CENTROID_BASHEIN_DETMER_HPP
  12. #include <boost/mpl/if.hpp>
  13. #include <boost/numeric/conversion/cast.hpp>
  14. #include <boost/type_traits.hpp>
  15. #include <boost/geometry/arithmetic/determinant.hpp>
  16. #include <boost/geometry/core/coordinate_type.hpp>
  17. #include <boost/geometry/core/point_type.hpp>
  18. #include <boost/geometry/strategies/centroid.hpp>
  19. #include <boost/geometry/util/select_coordinate_type.hpp>
  20. namespace boost { namespace geometry
  21. {
  22. // Note: when calling the namespace "centroid", it sometimes,
  23. // somehow, in gcc, gives compilation problems (confusion with function centroid).
  24. namespace strategy { namespace centroid
  25. {
  26. /*!
  27. \brief Centroid calculation using algorithm Bashein / Detmer
  28. \ingroup strategies
  29. \details Calculates centroid using triangulation method published by
  30. Bashein / Detmer
  31. \tparam Point point type of centroid to calculate
  32. \tparam PointOfSegment point type of segments, defaults to Point
  33. \tparam CalculationType \tparam_calculation
  34. \author Adapted from "Centroid of a Polygon" by
  35. Gerard Bashein and Paul R. Detmer<em>,
  36. in "Graphics Gems IV", Academic Press, 1994</em>
  37. \qbk{
  38. [heading See also]
  39. [link geometry.reference.algorithms.centroid.centroid_3_with_strategy centroid (with strategy)]
  40. }
  41. */
  42. /*
  43. \par Research notes
  44. The algorithm gives the same results as Oracle and PostGIS but
  45. differs from MySQL
  46. (tried 5.0.21 / 5.0.45 / 5.0.51a / 5.1.23).
  47. Without holes:
  48. - this: POINT(4.06923363095238 1.65055803571429)
  49. - geolib: POINT(4.07254 1.66819)
  50. - MySQL: POINT(3.6636363636364 1.6272727272727)'
  51. - PostGIS: POINT(4.06923363095238 1.65055803571429)
  52. - Oracle: 4.06923363095238 1.65055803571429
  53. - SQL Server: POINT(4.06923362245959 1.65055804168294)
  54. Statements:
  55. - \b MySQL/PostGIS: select AsText(Centroid(GeomFromText(
  56. '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
  57. ,5.4 1.2,4.9 0.8,2.9 0.7,2 1.3))')))
  58. - \b Oracle: select sdo_geom.sdo_centroid(sdo_geometry(2003, null, null,
  59. sdo_elem_info_array(1, 1003, 1), sdo_ordinate_array(
  60. 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
  61. ,5.4,1.2,4.9,0.8,2.9,0.7,2,1.3))
  62. , mdsys.sdo_dim_array(mdsys.sdo_dim_element('x',0,10,.00000005)
  63. ,mdsys.sdo_dim_element('y',0,10,.00000005)))
  64. from dual
  65. - \b SQL Server 2008: select geometry::STGeomFromText(
  66. '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
  67. ,5.4 1.2,4.9 0.8,2.9 0.7,2 1.3))',0)
  68. .STCentroid()
  69. .STAsText()
  70. With holes:
  71. - this: POINT(4.04663 1.6349)
  72. - geolib: POINT(4.04675 1.65735)
  73. - MySQL: POINT(3.6090580503834 1.607573932092)
  74. - PostGIS: POINT(4.0466265060241 1.63489959839357)
  75. - Oracle: 4.0466265060241 1.63489959839357
  76. - SQL Server: POINT(4.0466264962959677 1.6348996057331333)
  77. Statements:
  78. - \b MySQL/PostGIS: select AsText(Centroid(GeomFromText(
  79. 'POLYGON((2 1.3,2.4 1.7,2.8 1.8,3.4 1.2
  80. ,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)
  81. ,(4 2,4.2 1.4,4.8 1.9,4.4 2.2,4 2))')));
  82. - \b Oracle: select sdo_geom.sdo_centroid(sdo_geometry(2003, null, null
  83. , sdo_elem_info_array(1, 1003, 1, 25, 2003, 1)
  84. , sdo_ordinate_array(2,1.3,2.4,1.7,2.8,1.8,3.4,1.2,3.7,1.6,3.4,
  85. 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,
  86. 4.8,1.9, 4.4,2.2, 4,2))
  87. , mdsys.sdo_dim_array(mdsys.sdo_dim_element('x',0,10,.00000005)
  88. ,mdsys.sdo_dim_element('y',0,10,.00000005)))
  89. from dual
  90. */
  91. template
  92. <
  93. typename Point,
  94. typename PointOfSegment = Point,
  95. typename CalculationType = void
  96. >
  97. class bashein_detmer
  98. {
  99. private :
  100. // If user specified a calculation type, use that type,
  101. // whatever it is and whatever the point-type(s) are.
  102. // Else, use the most appropriate coordinate type
  103. // of the two points, but at least double
  104. typedef typename
  105. boost::mpl::if_c
  106. <
  107. boost::is_void<CalculationType>::type::value,
  108. typename select_most_precise
  109. <
  110. typename select_coordinate_type
  111. <
  112. Point,
  113. PointOfSegment
  114. >::type,
  115. double
  116. >::type,
  117. CalculationType
  118. >::type calculation_type;
  119. /*! subclass to keep state */
  120. class sums
  121. {
  122. friend class bashein_detmer;
  123. int count;
  124. calculation_type sum_a2;
  125. calculation_type sum_x;
  126. calculation_type sum_y;
  127. public :
  128. inline sums()
  129. : count(0)
  130. , sum_a2(calculation_type())
  131. , sum_x(calculation_type())
  132. , sum_y(calculation_type())
  133. {}
  134. };
  135. public :
  136. typedef sums state_type;
  137. static inline void apply(PointOfSegment const& p1,
  138. PointOfSegment const& p2, sums& state)
  139. {
  140. /* Algorithm:
  141. For each segment:
  142. begin
  143. ai = x1 * y2 - x2 * y1;
  144. sum_a2 += ai;
  145. sum_x += ai * (x1 + x2);
  146. sum_y += ai * (y1 + y2);
  147. end
  148. return POINT(sum_x / (3 * sum_a2), sum_y / (3 * sum_a2) )
  149. */
  150. // Get coordinates and promote them to calculation_type
  151. calculation_type const x1 = boost::numeric_cast<calculation_type>(get<0>(p1));
  152. calculation_type const y1 = boost::numeric_cast<calculation_type>(get<1>(p1));
  153. calculation_type const x2 = boost::numeric_cast<calculation_type>(get<0>(p2));
  154. calculation_type const y2 = boost::numeric_cast<calculation_type>(get<1>(p2));
  155. calculation_type const ai = geometry::detail::determinant<calculation_type>(p1, p2);
  156. state.count++;
  157. state.sum_a2 += ai;
  158. state.sum_x += ai * (x1 + x2);
  159. state.sum_y += ai * (y1 + y2);
  160. }
  161. static inline bool result(sums const& state, Point& centroid)
  162. {
  163. calculation_type const zero = calculation_type();
  164. if (state.count > 0 && ! math::equals(state.sum_a2, zero))
  165. {
  166. calculation_type const v3 = 3;
  167. calculation_type const a3 = v3 * state.sum_a2;
  168. typedef typename geometry::coordinate_type
  169. <
  170. Point
  171. >::type coordinate_type;
  172. set<0>(centroid,
  173. boost::numeric_cast<coordinate_type>(state.sum_x / a3));
  174. set<1>(centroid,
  175. boost::numeric_cast<coordinate_type>(state.sum_y / a3));
  176. return true;
  177. }
  178. return false;
  179. }
  180. };
  181. #ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS
  182. namespace services
  183. {
  184. // Register this strategy for rings and (multi)polygons, in two dimensions
  185. template <typename Point, typename Geometry>
  186. struct default_strategy<cartesian_tag, areal_tag, 2, Point, Geometry>
  187. {
  188. typedef bashein_detmer
  189. <
  190. Point,
  191. typename point_type<Geometry>::type
  192. > type;
  193. };
  194. } // namespace services
  195. #endif // DOXYGEN_NO_STRATEGY_SPECIALIZATIONS
  196. }} // namespace strategy::centroid
  197. }} // namespace boost::geometry
  198. #endif // BOOST_GEOMETRY_STRATEGIES_CARTESIAN_CENTROID_BASHEIN_DETMER_HPP