// Boost.Geometry Index // // R-tree quadratic split algorithm implementation // // Copyright (c) 2011-2014 Adam Wulkiewicz, Lodz, Poland. // // Use, modification and distribution is subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #ifndef BOOST_GEOMETRY_INDEX_DETAIL_RTREE_QUADRATIC_REDISTRIBUTE_ELEMENTS_HPP #define BOOST_GEOMETRY_INDEX_DETAIL_RTREE_QUADRATIC_REDISTRIBUTE_ELEMENTS_HPP #include #include #include #include #include #include #include namespace boost { namespace geometry { namespace index { namespace detail { namespace rtree { namespace quadratic { template inline void pick_seeds(Elements const& elements, Parameters const& parameters, Translator const& tr, size_t & seed1, size_t & seed2) { typedef typename Elements::value_type element_type; typedef typename rtree::element_indexable_type::type indexable_type; typedef Box box_type; typedef typename index::detail::default_content_result::type content_type; typedef index::detail::bounded_view bounded_indexable_view; const size_t elements_count = parameters.get_max_elements() + 1; BOOST_GEOMETRY_INDEX_ASSERT(elements.size() == elements_count, "wrong number of elements"); BOOST_GEOMETRY_INDEX_ASSERT(2 <= elements_count, "unexpected number of elements"); content_type greatest_free_content = 0; seed1 = 0; seed2 = 1; for ( size_t i = 0 ; i < elements_count - 1 ; ++i ) { for ( size_t j = i + 1 ; j < elements_count ; ++j ) { indexable_type const& ind1 = rtree::element_indexable(elements[i], tr); indexable_type const& ind2 = rtree::element_indexable(elements[j], tr); box_type enlarged_box; //geometry::convert(ind1, enlarged_box); detail::bounds(ind1, enlarged_box); geometry::expand(enlarged_box, ind2); bounded_indexable_view bounded_ind1(ind1); bounded_indexable_view bounded_ind2(ind2); content_type free_content = ( index::detail::content(enlarged_box) - index::detail::content(bounded_ind1) ) - index::detail::content(bounded_ind2); if ( greatest_free_content < free_content ) { greatest_free_content = free_content; seed1 = i; seed2 = j; } } } ::boost::ignore_unused_variable_warning(parameters); } } // namespace quadratic template struct redistribute_elements { typedef typename Options::parameters_type parameters_type; typedef typename rtree::node::type node; typedef typename rtree::internal_node::type internal_node; typedef typename rtree::leaf::type leaf; typedef typename index::detail::default_content_result::type content_type; template static inline void apply(Node & n, Node & second_node, Box & box1, Box & box2, parameters_type const& parameters, Translator const& translator, Allocators & allocators) { typedef typename rtree::elements_type::type elements_type; typedef typename elements_type::value_type element_type; typedef typename rtree::element_indexable_type::type indexable_type; elements_type & elements1 = rtree::elements(n); elements_type & elements2 = rtree::elements(second_node); BOOST_GEOMETRY_INDEX_ASSERT(elements1.size() == parameters.get_max_elements() + 1, "unexpected elements number"); // copy original elements - use in-memory storage (std::allocator) // TODO: move if noexcept typedef typename rtree::container_from_elements_type::type container_type; container_type elements_copy(elements1.begin(), elements1.end()); // MAY THROW, STRONG (alloc, copy) container_type elements_backup(elements1.begin(), elements1.end()); // MAY THROW, STRONG (alloc, copy) // calculate initial seeds size_t seed1 = 0; size_t seed2 = 0; quadratic::pick_seeds(elements_copy, parameters, translator, seed1, seed2); // prepare nodes' elements containers elements1.clear(); BOOST_GEOMETRY_INDEX_ASSERT(elements2.empty(), "second node's elements container should be empty"); BOOST_TRY { // add seeds elements1.push_back(elements_copy[seed1]); // MAY THROW, STRONG (copy) elements2.push_back(elements_copy[seed2]); // MAY THROW, STRONG (alloc, copy) // calculate boxes //geometry::convert(rtree::element_indexable(elements_copy[seed1], translator), box1); detail::bounds(rtree::element_indexable(elements_copy[seed1], translator), box1); //geometry::convert(rtree::element_indexable(elements_copy[seed2], translator), box2); detail::bounds(rtree::element_indexable(elements_copy[seed2], translator), box2); // remove seeds if (seed1 < seed2) { rtree::move_from_back(elements_copy, elements_copy.begin() + seed2); // MAY THROW, STRONG (copy) elements_copy.pop_back(); rtree::move_from_back(elements_copy, elements_copy.begin() + seed1); // MAY THROW, STRONG (copy) elements_copy.pop_back(); } else { rtree::move_from_back(elements_copy, elements_copy.begin() + seed1); // MAY THROW, STRONG (copy) elements_copy.pop_back(); rtree::move_from_back(elements_copy, elements_copy.begin() + seed2); // MAY THROW, STRONG (copy) elements_copy.pop_back(); } // initialize areas content_type content1 = index::detail::content(box1); content_type content2 = index::detail::content(box2); size_t remaining = elements_copy.size(); // redistribute the rest of the elements while ( !elements_copy.empty() ) { typename container_type::reverse_iterator el_it = elements_copy.rbegin(); bool insert_into_group1 = false; size_t elements1_count = elements1.size(); size_t elements2_count = elements2.size(); // if there is small number of elements left and the number of elements in node is lesser than min_elems // just insert them to this node if ( elements1_count + remaining <= parameters.get_min_elements() ) { insert_into_group1 = true; } else if ( elements2_count + remaining <= parameters.get_min_elements() ) { insert_into_group1 = false; } // insert the best element else { // find element with minimum groups areas increses differences content_type content_increase1 = 0; content_type content_increase2 = 0; el_it = pick_next(elements_copy.rbegin(), elements_copy.rend(), box1, box2, content1, content2, translator, content_increase1, content_increase2); if ( content_increase1 < content_increase2 || ( content_increase1 == content_increase2 && ( content1 < content2 || ( content1 == content2 && elements1_count <= elements2_count ) ) ) ) { insert_into_group1 = true; } else { insert_into_group1 = false; } } // move element to the choosen group element_type const& elem = *el_it; indexable_type const& indexable = rtree::element_indexable(elem, translator); if ( insert_into_group1 ) { elements1.push_back(elem); // MAY THROW, STRONG (copy) geometry::expand(box1, indexable); content1 = index::detail::content(box1); } else { elements2.push_back(elem); // MAY THROW, STRONG (alloc, copy) geometry::expand(box2, indexable); content2 = index::detail::content(box2); } BOOST_GEOMETRY_INDEX_ASSERT(!elements_copy.empty(), "expected more elements"); typename container_type::iterator el_it_base = el_it.base(); rtree::move_from_back(elements_copy, --el_it_base); // MAY THROW, STRONG (copy) elements_copy.pop_back(); BOOST_GEOMETRY_INDEX_ASSERT(0 < remaining, "expected more remaining elements"); --remaining; } } BOOST_CATCH(...) { //elements_copy.clear(); elements1.clear(); elements2.clear(); rtree::destroy_elements::apply(elements_backup, allocators); //elements_backup.clear(); BOOST_RETHROW // RETHROW, BASIC } BOOST_CATCH_END } // TODO: awulkiew - change following function to static member of the pick_next class? template static inline It pick_next(It first, It last, Box const& box1, Box const& box2, content_type const& content1, content_type const& content2, Translator const& translator, content_type & out_content_increase1, content_type & out_content_increase2) { typedef typename boost::iterator_value::type element_type; typedef typename rtree::element_indexable_type::type indexable_type; content_type greatest_content_incrase_diff = 0; It out_it = first; out_content_increase1 = 0; out_content_increase2 = 0; // find element with greatest difference between increased group's boxes areas for ( It el_it = first ; el_it != last ; ++el_it ) { indexable_type const& indexable = rtree::element_indexable(*el_it, translator); // calculate enlarged boxes and areas Box enlarged_box1(box1); Box enlarged_box2(box2); geometry::expand(enlarged_box1, indexable); geometry::expand(enlarged_box2, indexable); content_type enlarged_content1 = index::detail::content(enlarged_box1); content_type enlarged_content2 = index::detail::content(enlarged_box2); content_type content_incrase1 = (enlarged_content1 - content1); content_type content_incrase2 = (enlarged_content2 - content2); content_type content_incrase_diff = content_incrase1 < content_incrase2 ? content_incrase2 - content_incrase1 : content_incrase1 - content_incrase2; if ( greatest_content_incrase_diff < content_incrase_diff ) { greatest_content_incrase_diff = content_incrase_diff; out_it = el_it; out_content_increase1 = content_incrase1; out_content_increase2 = content_incrase2; } } return out_it; } }; }} // namespace detail::rtree }}} // namespace boost::geometry::index #endif // BOOST_GEOMETRY_INDEX_DETAIL_RTREE_QUADRATIC_REDISTRIBUTE_ELEMENTS_HPP