summaryrefslogtreecommitdiff
path: root/boost/geometry/index/detail/rtree/visitors/insert.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'boost/geometry/index/detail/rtree/visitors/insert.hpp')
-rw-r--r--boost/geometry/index/detail/rtree/visitors/insert.hpp527
1 files changed, 527 insertions, 0 deletions
diff --git a/boost/geometry/index/detail/rtree/visitors/insert.hpp b/boost/geometry/index/detail/rtree/visitors/insert.hpp
new file mode 100644
index 0000000000..388b3193f6
--- /dev/null
+++ b/boost/geometry/index/detail/rtree/visitors/insert.hpp
@@ -0,0 +1,527 @@
+// Boost.Geometry Index
+//
+// R-tree inserting visitor implementation
+//
+// Copyright (c) 2011-2013 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_VISITORS_INSERT_HPP
+#define BOOST_GEOMETRY_INDEX_DETAIL_RTREE_VISITORS_INSERT_HPP
+
+#include <boost/geometry/index/detail/algorithms/content.hpp>
+
+namespace boost { namespace geometry { namespace index {
+
+namespace detail { namespace rtree {
+
+// Default choose_next_node
+template <typename Value, typename Options, typename Box, typename Allocators, typename ChooseNextNodeTag>
+class choose_next_node;
+
+template <typename Value, typename Options, typename Box, typename Allocators>
+class choose_next_node<Value, Options, Box, Allocators, choose_by_content_diff_tag>
+{
+public:
+ typedef typename Options::parameters_type parameters_type;
+
+ typedef typename rtree::node<Value, parameters_type, Box, Allocators, typename Options::node_tag>::type node;
+ typedef typename rtree::internal_node<Value, parameters_type, Box, Allocators, typename Options::node_tag>::type internal_node;
+ typedef typename rtree::leaf<Value, parameters_type, Box, Allocators, typename Options::node_tag>::type leaf;
+
+ typedef typename rtree::elements_type<internal_node>::type children_type;
+
+ typedef typename index::detail::default_content_result<Box>::type content_type;
+
+ template <typename Indexable>
+ static inline size_t apply(internal_node & n,
+ Indexable const& indexable,
+ parameters_type const& /*parameters*/,
+ size_t /*node_relative_level*/)
+ {
+ children_type & children = rtree::elements(n);
+
+ BOOST_GEOMETRY_INDEX_ASSERT(!children.empty(), "can't choose the next node if children are empty");
+
+ size_t children_count = children.size();
+
+ // choose index with smallest content change or smallest content
+ size_t choosen_index = 0;
+ content_type smallest_content_diff = (std::numeric_limits<content_type>::max)();
+ content_type smallest_content = (std::numeric_limits<content_type>::max)();
+
+ // caculate areas and areas of all nodes' boxes
+ for ( size_t i = 0 ; i < children_count ; ++i )
+ {
+ typedef typename children_type::value_type child_type;
+ child_type const& ch_i = children[i];
+
+ // expanded child node's box
+ Box box_exp(ch_i.first);
+ geometry::expand(box_exp, indexable);
+
+ // areas difference
+ content_type content = index::detail::content(box_exp);
+ content_type content_diff = content - index::detail::content(ch_i.first);
+
+ // update the result
+ if ( content_diff < smallest_content_diff ||
+ ( content_diff == smallest_content_diff && content < smallest_content ) )
+ {
+ smallest_content_diff = content_diff;
+ smallest_content = content;
+ choosen_index = i;
+ }
+ }
+
+ return choosen_index;
+ }
+};
+
+// ----------------------------------------------------------------------- //
+
+// Not implemented here
+template <typename Value, typename Options, typename Translator, typename Box, typename Allocators, typename RedistributeTag>
+struct redistribute_elements
+{
+ BOOST_MPL_ASSERT_MSG(
+ (false),
+ NOT_IMPLEMENTED_FOR_THIS_REDISTRIBUTE_TAG_TYPE,
+ (redistribute_elements));
+};
+
+// ----------------------------------------------------------------------- //
+
+// Split algorithm
+template <typename Value, typename Options, typename Translator, typename Box, typename Allocators, typename SplitTag>
+class split
+{
+ BOOST_MPL_ASSERT_MSG(
+ (false),
+ NOT_IMPLEMENTED_FOR_THIS_SPLIT_TAG_TYPE,
+ (split));
+};
+
+// Default split algorithm
+template <typename Value, typename Options, typename Translator, typename Box, typename Allocators>
+class split<Value, Options, Translator, Box, Allocators, split_default_tag>
+{
+protected:
+ typedef typename Options::parameters_type parameters_type;
+
+ typedef typename rtree::node<Value, parameters_type, Box, Allocators, typename Options::node_tag>::type node;
+ typedef typename rtree::internal_node<Value, parameters_type, Box, Allocators, typename Options::node_tag>::type internal_node;
+ typedef typename rtree::leaf<Value, parameters_type, Box, Allocators, typename Options::node_tag>::type leaf;
+
+ typedef rtree::node_auto_ptr<Value, Options, Translator, Box, Allocators> node_auto_ptr;
+
+public:
+ typedef index::detail::varray<
+ typename rtree::elements_type<internal_node>::type::value_type,
+ 1
+ > nodes_container_type;
+
+ template <typename Node>
+ static inline void apply(nodes_container_type & additional_nodes,
+ Node & n,
+ Box & n_box,
+ parameters_type const& parameters,
+ Translator const& translator,
+ Allocators & allocators)
+ {
+ // TODO - consider creating nodes always with sufficient memory allocated
+
+ // create additional node, use auto ptr for automatic destruction on exception
+ node_auto_ptr second_node(rtree::create_node<Allocators, Node>::apply(allocators), allocators); // MAY THROW, STRONG (N: alloc)
+ // create reference to the newly created node
+ Node & n2 = rtree::get<Node>(*second_node);
+
+ // NOTE: thread-safety
+ // After throwing an exception by redistribute_elements the original node may be not changed or
+ // both nodes may be empty. In both cases the tree won't be valid r-tree.
+ // The alternative is to create 2 (or more) additional nodes here and store backup info
+ // in the original node, then, if exception was thrown, the node would always have more than max
+ // elements.
+ // The alternative is to use moving semantics in the implementations of redistribute_elements,
+ // it will be possible to throw from boost::move() in the case of e.g. static size nodes.
+
+ // redistribute elements
+ Box box2;
+ redistribute_elements<
+ Value,
+ Options,
+ Translator,
+ Box,
+ Allocators,
+ typename Options::redistribute_tag
+ >::apply(n, n2, n_box, box2, parameters, translator, allocators); // MAY THROW (V, E: alloc, copy, copy)
+
+ // check numbers of elements
+ BOOST_GEOMETRY_INDEX_ASSERT(parameters.get_min_elements() <= rtree::elements(n).size() &&
+ rtree::elements(n).size() <= parameters.get_max_elements(),
+ "unexpected number of elements");
+ BOOST_GEOMETRY_INDEX_ASSERT(parameters.get_min_elements() <= rtree::elements(n2).size() &&
+ rtree::elements(n2).size() <= parameters.get_max_elements(),
+ "unexpected number of elements");
+
+ // return the list of newly created nodes (this algorithm returns one)
+ additional_nodes.push_back(rtree::make_ptr_pair(box2, second_node.get())); // MAY THROW, STRONG (alloc, copy)
+
+ // release the ptr
+ second_node.release();
+ }
+};
+
+// ----------------------------------------------------------------------- //
+
+namespace visitors { namespace detail {
+
+template <typename InternalNode, typename InternalNodePtr, typename SizeType>
+struct insert_traverse_data
+{
+ typedef typename rtree::elements_type<InternalNode>::type elements_type;
+ typedef typename elements_type::value_type element_type;
+ typedef typename elements_type::size_type elements_size_type;
+ typedef SizeType size_type;
+
+ insert_traverse_data()
+ : parent(0), current_child_index(0), current_level(0)
+ {}
+
+ void move_to_next_level(InternalNodePtr new_parent,
+ elements_size_type new_child_index)
+ {
+ parent = new_parent;
+ current_child_index = new_child_index;
+ ++current_level;
+ }
+
+ bool current_is_root() const
+ {
+ return 0 == parent;
+ }
+
+ elements_type & parent_elements() const
+ {
+ BOOST_GEOMETRY_INDEX_ASSERT(parent, "null pointer");
+ return rtree::elements(*parent);
+ }
+
+ element_type & current_element() const
+ {
+ BOOST_GEOMETRY_INDEX_ASSERT(parent, "null pointer");
+ return rtree::elements(*parent)[current_child_index];
+ }
+
+ InternalNodePtr parent;
+ elements_size_type current_child_index;
+ size_type current_level;
+};
+
+// Default insert visitor
+template <typename Element, typename Value, typename Options, typename Translator, typename Box, typename Allocators>
+class insert
+ : public rtree::visitor<Value, typename Options::parameters_type, Box, Allocators, typename Options::node_tag, false>::type
+{
+protected:
+ typedef typename Options::parameters_type parameters_type;
+
+ typedef typename rtree::node<Value, parameters_type, Box, Allocators, typename Options::node_tag>::type node;
+ typedef typename rtree::internal_node<Value, parameters_type, Box, Allocators, typename Options::node_tag>::type internal_node;
+ typedef typename rtree::leaf<Value, parameters_type, Box, Allocators, typename Options::node_tag>::type leaf;
+
+ typedef rtree::node_auto_ptr<Value, Options, Translator, Box, Allocators> node_auto_ptr;
+ typedef typename Allocators::node_pointer node_pointer;
+ typedef typename Allocators::size_type size_type;
+
+ //typedef typename Allocators::internal_node_pointer internal_node_pointer;
+ typedef internal_node * internal_node_pointer;
+
+ inline insert(node_pointer & root,
+ size_type & leafs_level,
+ Element const& element,
+ parameters_type const& parameters,
+ Translator const& translator,
+ Allocators & allocators,
+ size_type relative_level = 0
+ )
+ : m_element(element)
+ , m_parameters(parameters)
+ , m_translator(translator)
+ , m_relative_level(relative_level)
+ , m_level(leafs_level - relative_level)
+ , m_root_node(root)
+ , m_leafs_level(leafs_level)
+ , m_traverse_data()
+ , m_allocators(allocators)
+ {
+ BOOST_GEOMETRY_INDEX_ASSERT(m_relative_level <= leafs_level, "unexpected level value");
+ BOOST_GEOMETRY_INDEX_ASSERT(m_level <= m_leafs_level, "unexpected level value");
+ BOOST_GEOMETRY_INDEX_ASSERT(0 != m_root_node, "there is no root node");
+ // TODO
+ // assert - check if Box is correct
+ }
+
+ template <typename Visitor>
+ inline void traverse(Visitor & visitor, internal_node & n)
+ {
+ // choose next node
+ size_t choosen_node_index = rtree::choose_next_node<Value, Options, Box, Allocators, typename Options::choose_next_node_tag>::
+ apply(n, rtree::element_indexable(m_element, m_translator), m_parameters, m_leafs_level - m_traverse_data.current_level);
+
+ // expand the node to contain value
+ geometry::expand(
+ rtree::elements(n)[choosen_node_index].first,
+ rtree::element_indexable(m_element, m_translator));
+
+ // next traversing step
+ traverse_apply_visitor(visitor, n, choosen_node_index); // MAY THROW (V, E: alloc, copy, N:alloc)
+ }
+
+ // TODO: awulkiew - change post_traverse name to handle_overflow or overflow_treatment?
+
+ template <typename Node>
+ inline void post_traverse(Node &n)
+ {
+ BOOST_GEOMETRY_INDEX_ASSERT(m_traverse_data.current_is_root() ||
+ &n == &rtree::get<Node>(*m_traverse_data.current_element().second),
+ "if node isn't the root current_child_index should be valid");
+
+ // handle overflow
+ if ( m_parameters.get_max_elements() < rtree::elements(n).size() )
+ {
+ // NOTE: If the exception is thrown current node may contain more than MAX elements or be empty.
+ // Furthermore it may be empty root - internal node.
+ split(n); // MAY THROW (V, E: alloc, copy, N:alloc)
+ }
+ }
+
+ template <typename Visitor>
+ inline void traverse_apply_visitor(Visitor & visitor, internal_node &n, size_t choosen_node_index)
+ {
+ // save previous traverse inputs and set new ones
+ insert_traverse_data<internal_node, internal_node_pointer, size_type>
+ backup_traverse_data = m_traverse_data;
+
+ // calculate new traverse inputs
+ m_traverse_data.move_to_next_level(&n, choosen_node_index);
+
+ // next traversing step
+ rtree::apply_visitor(visitor, *rtree::elements(n)[choosen_node_index].second); // MAY THROW (V, E: alloc, copy, N:alloc)
+
+ // restore previous traverse inputs
+ m_traverse_data = backup_traverse_data;
+ }
+
+ // TODO: consider - split result returned as OutIter is faster than reference to the container. Why?
+
+ template <typename Node>
+ inline void split(Node & n) const
+ {
+ typedef rtree::split<Value, Options, Translator, Box, Allocators, typename Options::split_tag> split_algo;
+
+ typename split_algo::nodes_container_type additional_nodes;
+ Box n_box;
+
+ split_algo::apply(additional_nodes, n, n_box, m_parameters, m_translator, m_allocators); // MAY THROW (V, E: alloc, copy, N:alloc)
+
+ BOOST_GEOMETRY_INDEX_ASSERT(additional_nodes.size() == 1, "unexpected number of additional nodes");
+
+ // TODO add all additional nodes
+ // For kmeans algorithm:
+ // elements number may be greater than node max elements count
+ // split and reinsert must take node with some elements count
+ // and container of additional elements (std::pair<Box, node*>s or Values)
+ // and translator + allocators
+ // where node_elements_count + additional_elements > node_max_elements_count
+ // What with elements other than std::pair<Box, node*> ?
+ // Implement template <node_tag> struct node_element_type or something like that
+
+ // for exception safety
+ node_auto_ptr additional_node_ptr(additional_nodes[0].second, m_allocators);
+
+ // node is not the root - just add the new node
+ if ( !m_traverse_data.current_is_root() )
+ {
+ // update old node's box
+ m_traverse_data.current_element().first = n_box;
+ // add new node to parent's children
+ m_traverse_data.parent_elements().push_back(additional_nodes[0]); // MAY THROW, STRONG (V, E: alloc, copy)
+ }
+ // node is the root - add level
+ else
+ {
+ BOOST_GEOMETRY_INDEX_ASSERT(&n == &rtree::get<Node>(*m_root_node), "node should be the root");
+
+ // create new root and add nodes
+ node_auto_ptr new_root(rtree::create_node<Allocators, internal_node>::apply(m_allocators), m_allocators); // MAY THROW, STRONG (N:alloc)
+
+ BOOST_TRY
+ {
+ rtree::elements(rtree::get<internal_node>(*new_root)).push_back(rtree::make_ptr_pair(n_box, m_root_node)); // MAY THROW, STRONG (E:alloc, copy)
+ rtree::elements(rtree::get<internal_node>(*new_root)).push_back(additional_nodes[0]); // MAY THROW, STRONG (E:alloc, copy)
+ }
+ BOOST_CATCH(...)
+ {
+ // clear new root to not delete in the ~node_auto_ptr() potentially stored old root node
+ rtree::elements(rtree::get<internal_node>(*new_root)).clear();
+ BOOST_RETHROW // RETHROW
+ }
+ BOOST_CATCH_END
+
+ m_root_node = new_root.get();
+ ++m_leafs_level;
+
+ new_root.release();
+ }
+
+ additional_node_ptr.release();
+ }
+
+ // TODO: awulkiew - implement dispatchable split::apply to enable additional nodes creation
+
+ Element const& m_element;
+ parameters_type const& m_parameters;
+ Translator const& m_translator;
+ size_type const m_relative_level;
+ size_type const m_level;
+
+ node_pointer & m_root_node;
+ size_type & m_leafs_level;
+
+ // traversing input parameters
+ insert_traverse_data<internal_node, internal_node_pointer, size_type> m_traverse_data;
+
+ Allocators & m_allocators;
+};
+
+} // namespace detail
+
+// Insert visitor forward declaration
+template <typename Element, typename Value, typename Options, typename Translator, typename Box, typename Allocators, typename InsertTag>
+class insert;
+
+// Default insert visitor used for nodes elements
+// After passing the Element to insert visitor the Element is managed by the tree
+// I.e. one should not delete the node passed to the insert visitor after exception is thrown
+// because this visitor may delete it
+template <typename Element, typename Value, typename Options, typename Translator, typename Box, typename Allocators>
+class insert<Element, Value, Options, Translator, Box, Allocators, insert_default_tag>
+ : public detail::insert<Element, Value, Options, Translator, Box, Allocators>
+{
+public:
+ typedef detail::insert<Element, Value, Options, Translator, Box, Allocators> base;
+ typedef typename base::node node;
+ typedef typename base::internal_node internal_node;
+ typedef typename base::leaf leaf;
+
+ typedef typename Options::parameters_type parameters_type;
+ typedef typename base::node_pointer node_pointer;
+ typedef typename base::size_type size_type;
+
+ inline insert(node_pointer & root,
+ size_type & leafs_level,
+ Element const& element,
+ parameters_type const& parameters,
+ Translator const& translator,
+ Allocators & allocators,
+ size_type relative_level = 0
+ )
+ : base(root, leafs_level, element, parameters, translator, allocators, relative_level)
+ {}
+
+ inline void operator()(internal_node & n)
+ {
+ BOOST_GEOMETRY_INDEX_ASSERT(base::m_traverse_data.current_level < base::m_leafs_level, "unexpected level");
+
+ if ( base::m_traverse_data.current_level < base::m_level )
+ {
+ // next traversing step
+ base::traverse(*this, n); // MAY THROW (E: alloc, copy, N: alloc)
+ }
+ else
+ {
+ BOOST_GEOMETRY_INDEX_ASSERT(base::m_level == base::m_traverse_data.current_level, "unexpected level");
+
+ BOOST_TRY
+ {
+ // push new child node
+ rtree::elements(n).push_back(base::m_element); // MAY THROW, STRONG (E: alloc, copy)
+ }
+ BOOST_CATCH(...)
+ {
+ // if the insert fails above, the element won't be stored in the tree
+
+ rtree::visitors::destroy<Value, Options, Translator, Box, Allocators> del_v(base::m_element.second, base::m_allocators);
+ rtree::apply_visitor(del_v, *base::m_element.second);
+
+ BOOST_RETHROW // RETHROW
+ }
+ BOOST_CATCH_END
+ }
+
+ base::post_traverse(n); // MAY THROW (E: alloc, copy, N: alloc)
+ }
+
+ inline void operator()(leaf &)
+ {
+ BOOST_GEOMETRY_INDEX_ASSERT(false, "this visitor can't be used for a leaf");
+ }
+};
+
+// Default insert visitor specialized for Values elements
+template <typename Value, typename Options, typename Translator, typename Box, typename Allocators>
+class insert<Value, Value, Options, Translator, Box, Allocators, insert_default_tag>
+ : public detail::insert<Value, Value, Options, Translator, Box, Allocators>
+{
+public:
+ typedef detail::insert<Value, Value, Options, Translator, Box, Allocators> base;
+ typedef typename base::node node;
+ typedef typename base::internal_node internal_node;
+ typedef typename base::leaf leaf;
+
+ typedef typename Options::parameters_type parameters_type;
+ typedef typename base::node_pointer node_pointer;
+ typedef typename base::size_type size_type;
+
+ inline insert(node_pointer & root,
+ size_type & leafs_level,
+ Value const& value,
+ parameters_type const& parameters,
+ Translator const& translator,
+ Allocators & allocators,
+ size_type relative_level = 0
+ )
+ : base(root, leafs_level, value, parameters, translator, allocators, relative_level)
+ {}
+
+ inline void operator()(internal_node & n)
+ {
+ BOOST_GEOMETRY_INDEX_ASSERT(base::m_traverse_data.current_level < base::m_leafs_level, "unexpected level");
+ BOOST_GEOMETRY_INDEX_ASSERT(base::m_traverse_data.current_level < base::m_level, "unexpected level");
+
+ // next traversing step
+ base::traverse(*this, n); // MAY THROW (V, E: alloc, copy, N: alloc)
+
+ base::post_traverse(n); // MAY THROW (E: alloc, copy, N: alloc)
+ }
+
+ inline void operator()(leaf & n)
+ {
+ BOOST_GEOMETRY_INDEX_ASSERT(base::m_traverse_data.current_level == base::m_leafs_level, "unexpected level");
+ BOOST_GEOMETRY_INDEX_ASSERT(base::m_level == base::m_traverse_data.current_level ||
+ base::m_level == (std::numeric_limits<size_t>::max)(), "unexpected level");
+
+ rtree::elements(n).push_back(base::m_element); // MAY THROW, STRONG (V: alloc, copy)
+
+ base::post_traverse(n); // MAY THROW (V: alloc, copy, N: alloc)
+ }
+};
+
+}}} // namespace detail::rtree::visitors
+
+}}} // namespace boost::geometry::index
+
+#endif // BOOST_GEOMETRY_INDEX_DETAIL_RTREE_VISITORS_INSERT_HPP