get_clusters.hpp 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. // Boost.Geometry (aka GGL, Generic Geometry Library)
  2. // Copyright (c) 2021 Barend Gehrels, Amsterdam, the Netherlands.
  3. // Use, modification and distribution is subject to the Boost Software License,
  4. // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
  5. // http://www.boost.org/LICENSE_1_0.txt)
  6. #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_CLUSTERS_HPP
  7. #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_CLUSTERS_HPP
  8. #include <algorithm>
  9. #include <map>
  10. #include <vector>
  11. #include <boost/geometry/core/access.hpp>
  12. #include <boost/geometry/algorithms/detail/overlay/approximately_equals.hpp>
  13. #include <boost/geometry/algorithms/detail/overlay/cluster_info.hpp>
  14. #include <boost/geometry/algorithms/detail/overlay/get_ring.hpp>
  15. #include <boost/geometry/algorithms/detail/recalculate.hpp>
  16. #include <boost/geometry/policies/robustness/rescale_policy_tags.hpp>
  17. #include <boost/range/value_type.hpp>
  18. #include <boost/geometry/util/math.hpp>
  19. #define BOOST_GEOMETRY_USE_RESCALING_IN_GET_CLUSTERS
  20. namespace boost { namespace geometry
  21. {
  22. #ifndef DOXYGEN_NO_DETAIL
  23. namespace detail { namespace overlay
  24. {
  25. template <typename Tag = no_rescale_policy_tag, bool Integral = false>
  26. struct sweep_equal_policy
  27. {
  28. public:
  29. // Returns true if point are considered equal
  30. template <typename P>
  31. static inline bool equals(P const& p1, P const& p2)
  32. {
  33. using coor_t = typename coordinate_type<P>::type;
  34. static auto const tolerance
  35. = common_approximately_equals_epsilon_multiplier<coor_t>::value();
  36. return approximately_equals(p1, p2, tolerance);
  37. }
  38. template <typename T>
  39. static inline bool exceeds(T value)
  40. {
  41. static auto const tolerance
  42. = common_approximately_equals_epsilon_multiplier<T>::value();
  43. T const limit = T(1) / tolerance;
  44. return value > limit;
  45. }
  46. };
  47. template <>
  48. struct sweep_equal_policy<no_rescale_policy_tag, true>
  49. {
  50. template <typename P>
  51. static inline bool equals(P const& p1, P const& p2)
  52. {
  53. return geometry::get<0>(p1) == geometry::get<0>(p2)
  54. && geometry::get<1>(p1) == geometry::get<1>(p2);
  55. }
  56. template <typename T>
  57. static inline bool exceeds(T value)
  58. {
  59. return value > 0;
  60. }
  61. };
  62. #ifdef BOOST_GEOMETRY_USE_RESCALING_IN_GET_CLUSTERS
  63. template <>
  64. struct sweep_equal_policy<rescale_policy_tag, true>
  65. {
  66. template <typename P>
  67. static inline bool equals(P const& p1, P const& p2)
  68. {
  69. // Neighbouring cells in the "integer grid" are considered as equal
  70. return math::abs(geometry::get<0>(p1) - geometry::get<0>(p2)) <= 1
  71. && math::abs(geometry::get<1>(p1) - geometry::get<1>(p2)) <= 1;
  72. }
  73. template <typename T>
  74. static inline bool exceeds(T value)
  75. {
  76. return value > 1;
  77. }
  78. };
  79. #endif
  80. template <typename Point>
  81. struct turn_with_point
  82. {
  83. std::size_t turn_index;
  84. Point pnt;
  85. };
  86. template <typename Cluster, typename Point>
  87. struct cluster_with_point
  88. {
  89. Cluster cluster;
  90. Point pnt;
  91. };
  92. // Use a sweep algorithm to detect clusters
  93. template
  94. <
  95. typename Turns,
  96. typename Clusters,
  97. typename RobustPolicy
  98. >
  99. inline void get_clusters(Turns& turns, Clusters& clusters,
  100. RobustPolicy const& robust_policy)
  101. {
  102. using turn_type = typename boost::range_value<Turns>::type;
  103. using cluster_type = typename Clusters::mapped_type;
  104. #ifdef BOOST_GEOMETRY_USE_RESCALING_IN_GET_CLUSTERS
  105. // For now still use robust points for rescaled, otherwise points do not match
  106. using point_type = typename geometry::robust_point_type
  107. <
  108. typename turn_type::point_type,
  109. RobustPolicy
  110. >::type;
  111. #else
  112. using point_type = typename turn_type::point_type;
  113. #endif
  114. sweep_equal_policy
  115. <
  116. typename rescale_policy_type<RobustPolicy>::type,
  117. std::is_integral<typename coordinate_type<point_type>::type>::value
  118. > equal_policy;
  119. std::vector<turn_with_point<point_type>> points;
  120. std::size_t turn_index = 0;
  121. for (auto const& turn : turns)
  122. {
  123. if (! turn.discarded)
  124. {
  125. #ifdef BOOST_GEOMETRY_USE_RESCALING_IN_GET_CLUSTERS
  126. point_type pnt;
  127. geometry::recalculate(pnt, turn.point, robust_policy);
  128. points.push_back({turn_index, pnt});
  129. #else
  130. points.push_back({turn_index, turn.point});
  131. #endif
  132. }
  133. turn_index++;
  134. }
  135. // Sort the points from top to bottom
  136. std::sort(points.begin(), points.end(), [](auto const& e1, auto const& e2)
  137. {
  138. return geometry::get<1>(e1.pnt) > geometry::get<1>(e2.pnt);
  139. });
  140. // The output vector will be sorted from bottom too
  141. std::vector<cluster_with_point<cluster_type, point_type>> clustered_points;
  142. // Compare points with each other. Performance is O(n log(n)) because of the sorting.
  143. for (auto it1 = points.begin(); it1 != points.end(); ++it1)
  144. {
  145. // Inner loop, iterates until it exceeds coordinates in y-direction
  146. for (auto it2 = it1 + 1; it2 != points.end(); ++it2)
  147. {
  148. auto const d = geometry::get<1>(it1->pnt) - geometry::get<1>(it2->pnt);
  149. if (equal_policy.exceeds(d))
  150. {
  151. // Points at this y-coordinate or below cannot be equal
  152. break;
  153. }
  154. if (equal_policy.equals(it1->pnt, it2->pnt))
  155. {
  156. std::size_t cindex = 0;
  157. // Most recent clusters (with this y-value) are at the bottom
  158. // therefore we can stop as soon as the y-value is out of reach (TODO)
  159. bool found = false;
  160. for (auto cit = clustered_points.begin();
  161. cit != clustered_points.end(); ++cit)
  162. {
  163. found = equal_policy.equals(cit->pnt, it1->pnt);
  164. if (found)
  165. {
  166. break;
  167. }
  168. cindex++;
  169. }
  170. // Add new cluster
  171. if (! found)
  172. {
  173. cindex = clustered_points.size();
  174. cluster_type newcluster;
  175. clustered_points.push_back({newcluster, it1->pnt});
  176. }
  177. clustered_points[cindex].cluster.turn_indices.insert(it1->turn_index);
  178. clustered_points[cindex].cluster.turn_indices.insert(it2->turn_index);
  179. }
  180. }
  181. }
  182. // Convert to map
  183. signed_size_type cluster_id = 1;
  184. for (auto& trace : clustered_points)
  185. {
  186. clusters[cluster_id++] = trace.cluster;
  187. }
  188. }
  189. }} // namespace detail::overlay
  190. #endif //DOXYGEN_NO_DETAIL
  191. }} // namespace boost::geometry
  192. #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_CLUSTERS_HPP