redistribute_elements.hpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468
  1. // Boost.Geometry Index
  2. //
  3. // R-tree linear split algorithm implementation
  4. //
  5. // Copyright (c) 2008 Federico J. Fernandez.
  6. // Copyright (c) 2011-2014 Adam Wulkiewicz, Lodz, Poland.
  7. //
  8. // This file was modified by Oracle on 2019.
  9. // Modifications copyright (c) 2019 Oracle and/or its affiliates.
  10. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
  11. //
  12. // Use, modification and distribution is subject to the Boost Software License,
  13. // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
  14. // http://www.boost.org/LICENSE_1_0.txt)
  15. #ifndef BOOST_GEOMETRY_INDEX_DETAIL_RTREE_LINEAR_REDISTRIBUTE_ELEMENTS_HPP
  16. #define BOOST_GEOMETRY_INDEX_DETAIL_RTREE_LINEAR_REDISTRIBUTE_ELEMENTS_HPP
  17. #include <boost/core/ignore_unused.hpp>
  18. #include <boost/type_traits/is_unsigned.hpp>
  19. #include <boost/geometry/index/detail/algorithms/bounds.hpp>
  20. #include <boost/geometry/index/detail/algorithms/content.hpp>
  21. #include <boost/geometry/index/detail/bounded_view.hpp>
  22. #include <boost/geometry/index/detail/rtree/node/node.hpp>
  23. #include <boost/geometry/index/detail/rtree/visitors/insert.hpp>
  24. #include <boost/geometry/index/detail/rtree/visitors/is_leaf.hpp>
  25. namespace boost { namespace geometry { namespace index {
  26. namespace detail { namespace rtree {
  27. namespace linear {
  28. template <typename R, typename T>
  29. inline R difference_dispatch(T const& from, T const& to, ::boost::mpl::bool_<false> const& /*is_unsigned*/)
  30. {
  31. return to - from;
  32. }
  33. template <typename R, typename T>
  34. inline R difference_dispatch(T const& from, T const& to, ::boost::mpl::bool_<true> const& /*is_unsigned*/)
  35. {
  36. return from <= to ? R(to - from) : -R(from - to);
  37. }
  38. template <typename R, typename T>
  39. inline R difference(T const& from, T const& to)
  40. {
  41. BOOST_MPL_ASSERT_MSG(!boost::is_unsigned<R>::value, RESULT_CANT_BE_UNSIGNED, (R));
  42. typedef ::boost::mpl::bool_<
  43. boost::is_unsigned<T>::value
  44. > is_unsigned;
  45. return difference_dispatch<R>(from, to, is_unsigned());
  46. }
  47. // TODO: awulkiew
  48. // In general, all aerial Indexables in the tree with box-like nodes will be analyzed as boxes
  49. // because they must fit into larger box. Therefore the algorithm could be the same for Bounds type.
  50. // E.g. if Bounds type is sphere, Indexables probably should be analyzed as spheres.
  51. // 1. View could be provided to 'see' all Indexables as Bounds type.
  52. // Not ok in the case of big types like Ring, however it's possible that Rings won't be supported,
  53. // only simple types. Even then if we consider storing Box inside the Sphere we must calculate
  54. // the bounding sphere 2x for each box because there are 2 loops. For each calculation this means
  55. // 4-2d or 8-3d expansions or -, / and sqrt().
  56. // 2. Additional container could be used and reused if the Indexable type is other than the Bounds type.
  57. // IMPORTANT!
  58. // Still probably the best way would be providing specialized algorithms for each Indexable-Bounds pair!
  59. // Probably on pick_seeds algorithm level - For Bounds=Sphere seeds would be choosen differently
  60. // TODO: awulkiew
  61. // there are loops inside find_greatest_normalized_separation::apply()
  62. // iteration is done for each DimensionIndex.
  63. // Separations and seeds for all DimensionIndex(es) could be calculated at once, stored, then the greatest would be choosen.
  64. // The following struct/method was adapted for the preliminary version of the R-tree. Then it was called:
  65. // void find_normalized_separations(std::vector<Box> const& boxes, T& separation, unsigned int& first, unsigned int& second) const
  66. template <typename Elements, typename Parameters, typename Translator, typename Tag, size_t DimensionIndex>
  67. struct find_greatest_normalized_separation
  68. {
  69. typedef typename Elements::value_type element_type;
  70. typedef typename rtree::element_indexable_type<element_type, Translator>::type indexable_type;
  71. typedef typename coordinate_type<indexable_type>::type coordinate_type;
  72. typedef typename boost::mpl::if_c<
  73. boost::is_integral<coordinate_type>::value,
  74. double,
  75. coordinate_type
  76. >::type separation_type;
  77. typedef typename geometry::point_type<indexable_type>::type point_type;
  78. typedef geometry::model::box<point_type> bounds_type;
  79. typedef index::detail::bounded_view
  80. <
  81. indexable_type, bounds_type,
  82. typename index::detail::strategy_type<Parameters>::type
  83. > bounded_view_type;
  84. static inline void apply(Elements const& elements,
  85. Parameters const& parameters,
  86. Translator const& translator,
  87. separation_type & separation,
  88. size_t & seed1,
  89. size_t & seed2)
  90. {
  91. const size_t elements_count = parameters.get_max_elements() + 1;
  92. BOOST_GEOMETRY_INDEX_ASSERT(elements.size() == elements_count, "unexpected number of elements");
  93. BOOST_GEOMETRY_INDEX_ASSERT(2 <= elements_count, "unexpected number of elements");
  94. typename index::detail::strategy_type<Parameters>::type const&
  95. strategy = index::detail::get_strategy(parameters);
  96. // find the lowest low, highest high
  97. bounded_view_type bounded_indexable_0(rtree::element_indexable(elements[0], translator),
  98. strategy);
  99. coordinate_type lowest_low = geometry::get<min_corner, DimensionIndex>(bounded_indexable_0);
  100. coordinate_type highest_high = geometry::get<max_corner, DimensionIndex>(bounded_indexable_0);
  101. // and the lowest high
  102. coordinate_type lowest_high = highest_high;
  103. size_t lowest_high_index = 0;
  104. for ( size_t i = 1 ; i < elements_count ; ++i )
  105. {
  106. bounded_view_type bounded_indexable(rtree::element_indexable(elements[i], translator),
  107. strategy);
  108. coordinate_type min_coord = geometry::get<min_corner, DimensionIndex>(bounded_indexable);
  109. coordinate_type max_coord = geometry::get<max_corner, DimensionIndex>(bounded_indexable);
  110. if ( max_coord < lowest_high )
  111. {
  112. lowest_high = max_coord;
  113. lowest_high_index = i;
  114. }
  115. if ( min_coord < lowest_low )
  116. lowest_low = min_coord;
  117. if ( highest_high < max_coord )
  118. highest_high = max_coord;
  119. }
  120. // find the highest low
  121. size_t highest_low_index = lowest_high_index == 0 ? 1 : 0;
  122. bounded_view_type bounded_indexable_hl(rtree::element_indexable(elements[highest_low_index], translator),
  123. strategy);
  124. coordinate_type highest_low = geometry::get<min_corner, DimensionIndex>(bounded_indexable_hl);
  125. for ( size_t i = highest_low_index ; i < elements_count ; ++i )
  126. {
  127. bounded_view_type bounded_indexable(rtree::element_indexable(elements[i], translator),
  128. strategy);
  129. coordinate_type min_coord = geometry::get<min_corner, DimensionIndex>(bounded_indexable);
  130. if ( highest_low < min_coord &&
  131. i != lowest_high_index )
  132. {
  133. highest_low = min_coord;
  134. highest_low_index = i;
  135. }
  136. }
  137. coordinate_type const width = highest_high - lowest_low;
  138. // highest_low - lowest_high
  139. separation = difference<separation_type>(lowest_high, highest_low);
  140. // BOOST_GEOMETRY_INDEX_ASSERT(0 <= width);
  141. if ( std::numeric_limits<coordinate_type>::epsilon() < width )
  142. separation /= width;
  143. seed1 = highest_low_index;
  144. seed2 = lowest_high_index;
  145. ::boost::ignore_unused(parameters);
  146. }
  147. };
  148. // Version for points doesn't calculate normalized separation since it would always be equal to 1
  149. // It returns two seeds most distant to each other, separation is equal to distance
  150. template <typename Elements, typename Parameters, typename Translator, size_t DimensionIndex>
  151. struct find_greatest_normalized_separation<Elements, Parameters, Translator, point_tag, DimensionIndex>
  152. {
  153. typedef typename Elements::value_type element_type;
  154. typedef typename rtree::element_indexable_type<element_type, Translator>::type indexable_type;
  155. typedef typename coordinate_type<indexable_type>::type coordinate_type;
  156. typedef coordinate_type separation_type;
  157. static inline void apply(Elements const& elements,
  158. Parameters const& parameters,
  159. Translator const& translator,
  160. separation_type & separation,
  161. size_t & seed1,
  162. size_t & seed2)
  163. {
  164. const size_t elements_count = parameters.get_max_elements() + 1;
  165. BOOST_GEOMETRY_INDEX_ASSERT(elements.size() == elements_count, "unexpected number of elements");
  166. BOOST_GEOMETRY_INDEX_ASSERT(2 <= elements_count, "unexpected number of elements");
  167. // find the lowest low, highest high
  168. coordinate_type lowest = geometry::get<DimensionIndex>(rtree::element_indexable(elements[0], translator));
  169. coordinate_type highest = geometry::get<DimensionIndex>(rtree::element_indexable(elements[0], translator));
  170. size_t lowest_index = 0;
  171. size_t highest_index = 0;
  172. for ( size_t i = 1 ; i < elements_count ; ++i )
  173. {
  174. coordinate_type coord = geometry::get<DimensionIndex>(rtree::element_indexable(elements[i], translator));
  175. if ( coord < lowest )
  176. {
  177. lowest = coord;
  178. lowest_index = i;
  179. }
  180. if ( highest < coord )
  181. {
  182. highest = coord;
  183. highest_index = i;
  184. }
  185. }
  186. separation = highest - lowest;
  187. seed1 = lowest_index;
  188. seed2 = highest_index;
  189. if ( lowest_index == highest_index )
  190. seed2 = (lowest_index + 1) % elements_count; // % is just in case since if this is true lowest_index is 0
  191. ::boost::ignore_unused(parameters);
  192. }
  193. };
  194. template <typename Elements, typename Parameters, typename Translator, size_t Dimension>
  195. struct pick_seeds_impl
  196. {
  197. BOOST_STATIC_ASSERT(0 < Dimension);
  198. typedef typename Elements::value_type element_type;
  199. typedef typename rtree::element_indexable_type<element_type, Translator>::type indexable_type;
  200. typedef find_greatest_normalized_separation<
  201. Elements, Parameters, Translator,
  202. typename tag<indexable_type>::type, Dimension - 1
  203. > find_norm_sep;
  204. typedef typename find_norm_sep::separation_type separation_type;
  205. static inline void apply(Elements const& elements,
  206. Parameters const& parameters,
  207. Translator const& tr,
  208. separation_type & separation,
  209. size_t & seed1,
  210. size_t & seed2)
  211. {
  212. pick_seeds_impl<Elements, Parameters, Translator, Dimension - 1>::apply(elements, parameters, tr, separation, seed1, seed2);
  213. separation_type current_separation;
  214. size_t s1, s2;
  215. find_norm_sep::apply(elements, parameters, tr, current_separation, s1, s2);
  216. // in the old implementation different operator was used: <= (y axis prefered)
  217. if ( separation < current_separation )
  218. {
  219. separation = current_separation;
  220. seed1 = s1;
  221. seed2 = s2;
  222. }
  223. }
  224. };
  225. template <typename Elements, typename Parameters, typename Translator>
  226. struct pick_seeds_impl<Elements, Parameters, Translator, 1>
  227. {
  228. typedef typename Elements::value_type element_type;
  229. typedef typename rtree::element_indexable_type<element_type, Translator>::type indexable_type;
  230. typedef typename coordinate_type<indexable_type>::type coordinate_type;
  231. typedef find_greatest_normalized_separation<
  232. Elements, Parameters, Translator,
  233. typename tag<indexable_type>::type, 0
  234. > find_norm_sep;
  235. typedef typename find_norm_sep::separation_type separation_type;
  236. static inline void apply(Elements const& elements,
  237. Parameters const& parameters,
  238. Translator const& tr,
  239. separation_type & separation,
  240. size_t & seed1,
  241. size_t & seed2)
  242. {
  243. find_norm_sep::apply(elements, parameters, tr, separation, seed1, seed2);
  244. }
  245. };
  246. // from void linear_pick_seeds(node_pointer const& n, unsigned int &seed1, unsigned int &seed2) const
  247. template <typename Elements, typename Parameters, typename Translator>
  248. inline void pick_seeds(Elements const& elements,
  249. Parameters const& parameters,
  250. Translator const& tr,
  251. size_t & seed1,
  252. size_t & seed2)
  253. {
  254. typedef typename Elements::value_type element_type;
  255. typedef typename rtree::element_indexable_type<element_type, Translator>::type indexable_type;
  256. typedef pick_seeds_impl
  257. <
  258. Elements, Parameters, Translator,
  259. geometry::dimension<indexable_type>::value
  260. > impl;
  261. typedef typename impl::separation_type separation_type;
  262. separation_type separation = 0;
  263. impl::apply(elements, parameters, tr, separation, seed1, seed2);
  264. }
  265. } // namespace linear
  266. // from void split_node(node_pointer const& n, node_pointer& n1, node_pointer& n2) const
  267. template <typename Value, typename Options, typename Translator, typename Box, typename Allocators>
  268. struct redistribute_elements<Value, Options, Translator, Box, Allocators, linear_tag>
  269. {
  270. typedef typename Options::parameters_type parameters_type;
  271. typedef typename rtree::node<Value, parameters_type, Box, Allocators, typename Options::node_tag>::type node;
  272. typedef typename rtree::internal_node<Value, parameters_type, Box, Allocators, typename Options::node_tag>::type internal_node;
  273. typedef typename rtree::leaf<Value, parameters_type, Box, Allocators, typename Options::node_tag>::type leaf;
  274. template <typename Node>
  275. static inline void apply(Node & n,
  276. Node & second_node,
  277. Box & box1,
  278. Box & box2,
  279. parameters_type const& parameters,
  280. Translator const& translator,
  281. Allocators & allocators)
  282. {
  283. typedef typename rtree::elements_type<Node>::type elements_type;
  284. typedef typename elements_type::value_type element_type;
  285. typedef typename rtree::element_indexable_type<element_type, Translator>::type indexable_type;
  286. typedef typename index::detail::default_content_result<Box>::type content_type;
  287. typename index::detail::strategy_type<parameters_type>::type const&
  288. strategy = index::detail::get_strategy(parameters);
  289. elements_type & elements1 = rtree::elements(n);
  290. elements_type & elements2 = rtree::elements(second_node);
  291. const size_t elements1_count = parameters.get_max_elements() + 1;
  292. BOOST_GEOMETRY_INDEX_ASSERT(elements1.size() == elements1_count, "unexpected number of elements");
  293. // copy original elements - use in-memory storage (std::allocator)
  294. // TODO: move if noexcept
  295. typedef typename rtree::container_from_elements_type<elements_type, element_type>::type
  296. container_type;
  297. container_type elements_copy(elements1.begin(), elements1.end()); // MAY THROW, STRONG (alloc, copy)
  298. // calculate initial seeds
  299. size_t seed1 = 0;
  300. size_t seed2 = 0;
  301. linear::pick_seeds(elements_copy, parameters, translator, seed1, seed2);
  302. // prepare nodes' elements containers
  303. elements1.clear();
  304. BOOST_GEOMETRY_INDEX_ASSERT(elements2.empty(), "unexpected container state");
  305. BOOST_TRY
  306. {
  307. // add seeds
  308. elements1.push_back(elements_copy[seed1]); // MAY THROW, STRONG (copy)
  309. elements2.push_back(elements_copy[seed2]); // MAY THROW, STRONG (alloc, copy)
  310. // calculate boxes
  311. detail::bounds(rtree::element_indexable(elements_copy[seed1], translator),
  312. box1, strategy);
  313. detail::bounds(rtree::element_indexable(elements_copy[seed2], translator),
  314. box2, strategy);
  315. // initialize areas
  316. content_type content1 = index::detail::content(box1);
  317. content_type content2 = index::detail::content(box2);
  318. BOOST_GEOMETRY_INDEX_ASSERT(2 <= elements1_count, "unexpected elements number");
  319. size_t remaining = elements1_count - 2;
  320. // redistribute the rest of the elements
  321. for ( size_t i = 0 ; i < elements1_count ; ++i )
  322. {
  323. if (i != seed1 && i != seed2)
  324. {
  325. element_type const& elem = elements_copy[i];
  326. indexable_type const& indexable = rtree::element_indexable(elem, translator);
  327. // if there is small number of elements left and the number of elements in node is lesser than min_elems
  328. // just insert them to this node
  329. if ( elements1.size() + remaining <= parameters.get_min_elements() )
  330. {
  331. elements1.push_back(elem); // MAY THROW, STRONG (copy)
  332. index::detail::expand(box1, indexable, strategy);
  333. content1 = index::detail::content(box1);
  334. }
  335. else if ( elements2.size() + remaining <= parameters.get_min_elements() )
  336. {
  337. elements2.push_back(elem); // MAY THROW, STRONG (alloc, copy)
  338. index::detail::expand(box2, indexable, strategy);
  339. content2 = index::detail::content(box2);
  340. }
  341. // choose better node and insert element
  342. else
  343. {
  344. // calculate enlarged boxes and areas
  345. Box enlarged_box1(box1);
  346. Box enlarged_box2(box2);
  347. index::detail::expand(enlarged_box1, indexable, strategy);
  348. index::detail::expand(enlarged_box2, indexable, strategy);
  349. content_type enlarged_content1 = index::detail::content(enlarged_box1);
  350. content_type enlarged_content2 = index::detail::content(enlarged_box2);
  351. content_type content_increase1 = enlarged_content1 - content1;
  352. content_type content_increase2 = enlarged_content2 - content2;
  353. // choose group which box content have to be enlarged least or has smaller content or has fewer elements
  354. if ( content_increase1 < content_increase2 ||
  355. ( content_increase1 == content_increase2 &&
  356. ( content1 < content2 ||
  357. ( content1 == content2 && elements1.size() <= elements2.size() ) ) ) )
  358. {
  359. elements1.push_back(elem); // MAY THROW, STRONG (copy)
  360. box1 = enlarged_box1;
  361. content1 = enlarged_content1;
  362. }
  363. else
  364. {
  365. elements2.push_back(elem); // MAY THROW, STRONG (alloc, copy)
  366. box2 = enlarged_box2;
  367. content2 = enlarged_content2;
  368. }
  369. }
  370. BOOST_GEOMETRY_INDEX_ASSERT(0 < remaining, "unexpected value");
  371. --remaining;
  372. }
  373. }
  374. }
  375. BOOST_CATCH(...)
  376. {
  377. elements1.clear();
  378. elements2.clear();
  379. rtree::destroy_elements<Value, Options, Translator, Box, Allocators>::apply(elements_copy, allocators);
  380. //elements_copy.clear();
  381. BOOST_RETHROW // RETHROW, BASIC
  382. }
  383. BOOST_CATCH_END
  384. }
  385. };
  386. }} // namespace detail::rtree
  387. }}} // namespace boost::geometry::index
  388. #endif // BOOST_GEOMETRY_INDEX_DETAIL_RTREE_LINEAR_REDISTRIBUTE_ELEMENTS_HPP