geometry_collection.hpp 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. // Boost.Geometry
  2. // Copyright (c) 2021 Oracle and/or its affiliates.
  3. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
  4. // Licensed under the Boost Software License version 1.0.
  5. // http://www.boost.org/users/license.html
  6. #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISTANCE_GEOMETRY_COLLECTION_HPP
  7. #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISTANCE_GEOMETRY_COLLECTION_HPP
  8. #include <vector>
  9. #include <boost/range/size.hpp>
  10. #include <boost/geometry/algorithms/dispatch/distance.hpp>
  11. #include <boost/geometry/algorithms/detail/visit.hpp>
  12. #include <boost/geometry/core/assert.hpp>
  13. #include <boost/geometry/core/point_type.hpp>
  14. #include <boost/geometry/index/rtree.hpp>
  15. #include <boost/geometry/strategies/distance.hpp>
  16. #include <boost/geometry/strategies/distance_result.hpp>
  17. namespace boost { namespace geometry
  18. {
  19. #ifndef DOXYGEN_NO_DETAIL
  20. namespace detail { namespace distance
  21. {
  22. template <typename Geometry, typename GeometryCollection, typename Strategies>
  23. inline auto geometry_to_collection(Geometry const& geometry,
  24. GeometryCollection const& collection,
  25. Strategies const& strategies)
  26. {
  27. using result_t = typename geometry::distance_result<Geometry, GeometryCollection, Strategies>::type;
  28. result_t result = 0;
  29. bool is_first = true;
  30. detail::visit_breadth_first([&](auto const& g)
  31. {
  32. result_t r = dispatch::distance
  33. <
  34. Geometry, util::remove_cref_t<decltype(g)>, Strategies
  35. >::apply(geometry, g, strategies);
  36. if (is_first)
  37. {
  38. result = r;
  39. is_first = false;
  40. }
  41. else if (r < result)
  42. {
  43. result = r;
  44. }
  45. return result > result_t(0);
  46. }, collection);
  47. return result;
  48. }
  49. template <typename GeometryCollection1, typename GeometryCollection2, typename Strategies>
  50. inline auto collection_to_collection(GeometryCollection1 const& collection1,
  51. GeometryCollection2 const& collection2,
  52. Strategies const& strategies)
  53. {
  54. using result_t = typename geometry::distance_result<GeometryCollection1, GeometryCollection2, Strategies>::type;
  55. using point1_t = typename geometry::point_type<GeometryCollection1>::type;
  56. using box1_t = model::box<point1_t>;
  57. using point2_t = typename geometry::point_type<GeometryCollection2>::type;
  58. using box2_t = model::box<point2_t>;
  59. using rtree_value_t = std::pair<box1_t, typename boost::range_iterator<GeometryCollection1 const>::type>;
  60. using rtree_params_t = index::parameters<index::rstar<4>, Strategies>;
  61. using rtree_t = index::rtree<rtree_value_t, rtree_params_t>;
  62. rtree_params_t rtree_params(index::rstar<4>(), strategies);
  63. rtree_t rtree(rtree_params);
  64. // Build rtree of boxes and iterators of elements of GC1
  65. // TODO: replace this with visit_breadth_first_iterator to avoid creating an unnecessary container?
  66. {
  67. std::vector<rtree_value_t> values;
  68. visit_breadth_first_impl<true>::apply([&](auto & g1, auto it)
  69. {
  70. box1_t b1 = geometry::return_envelope<box1_t>(g1, strategies);
  71. geometry::detail::expand_by_epsilon(b1);
  72. values.emplace_back(b1, it);
  73. return true;
  74. }, collection1);
  75. rtree_t rt(values.begin(), values.end(), rtree_params);
  76. rtree = std::move(rt);
  77. }
  78. result_t const zero = 0;
  79. auto const rtree_qend = rtree.qend();
  80. result_t result = 0;
  81. bool is_first = true;
  82. visit_breadth_first([&](auto const& g2)
  83. {
  84. box2_t b2 = geometry::return_envelope<box2_t>(g2, strategies);
  85. geometry::detail::expand_by_epsilon(b2);
  86. for (auto it = rtree.qbegin(index::nearest(b2, rtree.size())) ; it != rtree_qend ; ++it)
  87. {
  88. // If the distance between boxes is greater than or equal to previously found
  89. // distance between geometries then stop processing the current b2 because no
  90. // closer b1 will be found
  91. if (! is_first)
  92. {
  93. result_t const bd = dispatch::distance
  94. <
  95. box1_t, box2_t, Strategies
  96. >::apply(it->first, b2, strategies);
  97. if (bd >= result)
  98. {
  99. break;
  100. }
  101. }
  102. // Boxes are closer than the previously found distance (or it's the first time),
  103. // calculate the new distance between geometries and check if it's closer (or assign it).
  104. traits::iter_visit<GeometryCollection1>::apply([&](auto const& g1)
  105. {
  106. result_t const d = dispatch::distance
  107. <
  108. util::remove_cref_t<decltype(g1)>, util::remove_cref_t<decltype(g2)>,
  109. Strategies
  110. >::apply(g1, g2, strategies);
  111. if (is_first)
  112. {
  113. result = d;
  114. is_first = false;
  115. }
  116. else if (d < result)
  117. {
  118. result = d;
  119. }
  120. }, it->second);
  121. // The smallest possible distance found, end searching.
  122. if (! is_first && result <= zero)
  123. {
  124. return false;
  125. }
  126. }
  127. // Just in case
  128. return is_first || result > zero;
  129. }, collection2);
  130. return result;
  131. }
  132. }} // namespace detail::distance
  133. #endif // DOXYGEN_NO_DETAIL
  134. #ifndef DOXYGEN_NO_DISPATCH
  135. namespace dispatch
  136. {
  137. template
  138. <
  139. typename Geometry, typename GeometryCollection, typename Strategies, typename Tag1
  140. >
  141. struct distance
  142. <
  143. Geometry, GeometryCollection, Strategies,
  144. Tag1, geometry_collection_tag, void, false
  145. >
  146. {
  147. static inline auto apply(Geometry const& geometry,
  148. GeometryCollection const& collection,
  149. Strategies const& strategies)
  150. {
  151. assert_dimension_equal<Geometry, GeometryCollection>();
  152. return detail::distance::geometry_to_collection(geometry, collection, strategies);
  153. }
  154. };
  155. template
  156. <
  157. typename GeometryCollection, typename Geometry, typename Strategies, typename Tag2
  158. >
  159. struct distance
  160. <
  161. GeometryCollection, Geometry, Strategies,
  162. geometry_collection_tag, Tag2, void, false
  163. >
  164. {
  165. static inline auto apply(GeometryCollection const& collection,
  166. Geometry const& geometry,
  167. Strategies const& strategies)
  168. {
  169. assert_dimension_equal<Geometry, GeometryCollection>();
  170. return detail::distance::geometry_to_collection(geometry, collection, strategies);
  171. }
  172. };
  173. template
  174. <
  175. typename GeometryCollection1, typename GeometryCollection2, typename Strategies
  176. >
  177. struct distance
  178. <
  179. GeometryCollection1, GeometryCollection2, Strategies,
  180. geometry_collection_tag, geometry_collection_tag, void, false
  181. >
  182. {
  183. static inline auto apply(GeometryCollection1 const& collection1,
  184. GeometryCollection2 const& collection2,
  185. Strategies const& strategies)
  186. {
  187. assert_dimension_equal<GeometryCollection1, GeometryCollection2>();
  188. // Build the rtree for the smaller GC (ignoring recursive GCs)
  189. return boost::size(collection1) <= boost::size(collection2)
  190. ? detail::distance::collection_to_collection(collection1, collection2, strategies)
  191. : detail::distance::collection_to_collection(collection2, collection1, strategies);
  192. }
  193. };
  194. } // namespace dispatch
  195. #endif // DOXYGEN_NO_DISPATCH
  196. }} // namespace boost::geometry
  197. #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISTANCE_SEGMENT_TO_BOX_HPP