R-trees 上的多级查询(交集、联合)
Posted
技术标签:
【中文标题】R-trees 上的多级查询(交集、联合)【英文标题】:Multilevel queries on R-trees (Intersection, Union) 【发布时间】:2018-11-20 18:32:50 【问题描述】:假设我有以下设置:
我有一个boost::geometry::index::rtree
,它以二维框为键,以点为值。
盒子的第一个维度,实际上将应用于(实值封闭)区间,而第二个维度仅应用于点。
所以我的盒子看起来像:
using namespace std;
typedef bg::model::point<unsigned long, 2, bg::cs::cartesian> _pt_t;
typedef bg::model::box<_pt_t> _box_t;
typedef pair<_box_t, unsigned long> tree_v_t;
typedef bgi::rtree<tree_v_t, bgi::quadratic<16> > rtree_t;
一个盒子总是会使用以下方法初始化:
_box_t _mb(unsigned long i, unsigned long s, unsigned long d)
_box_t b(_pt_t(s, i), _pt_t(s + d, i));
return b;
现在假设我已经初始化了 rtree,我想做两种复杂的查询:
-
给定一组
si
的间隔set<pair<unsigned int, unsigned int> >
和一组sp
的点set<unsigned int>
,我想迭代作为以下伪代码查询结果的所有值:
any(si, intersect(rtree_level1)) && any(sp, contains(rtree_level2)) && value in ps
换句话说,我想要包含si
中包含的区间和sp
中包含的点的交集的rtree 的子树,并且它的值也在sp
中。如果你愿意,你可以假设 si 中的所有区间都是不相交的。
-
给定一组
spi
的点和区间set<unsigned int, pair<unsigned int, unsigned int> >
,我想迭代作为以下伪代码查询结果的所有值:
any(spi, intersect(rtree_level1)(spi.interval) && contains(rtree_level2)(spi.point) && value in spi.point )
换句话说,我想要来自spi
的每个元素的所有子树的联合,它们是给定区间的交集,并且它们包含 仅作为键(第二级)和值的那些点。如果 si 和 sp 都有一个元素,这就像 union 从 query 1 生成的所有 R-tree .
我可以理解如何使用satisfy
谓词并将transform
应用于qbegin
生成的迭代器,但是
在 boost 中最有效的方法是什么?
【问题讨论】:
“rtree_level1”、“rtree_level2 和“subtree”是什么意思?set
是什么意思?通常是std::set
。是这样吗?如何set<unsigned int>
是一组点吗?你的点是一维的吗?这是什么意思:set<unsigned int, pair<unsigned int, unsigned int> >
?std::set
只接受一个定义 value_type
的类型。你的意思是 std::map
还是有什么不同?这看起来像是一个 XY 问题。您一般想要实现什么目标?
我的意思是std::set
。要了解一般情况下的问题,您可以查看this 帖子(这也是由我完成的)。 set<unsigned int, pair<unsigned int, unsigned int> >
表示一组点,其中每个点在给定的时间间隔内都有效。非常感谢您的关注:)
set<unsigned int, pair<unsigned int, unsigned int> >
在std::set
的上下文中没有意义。 “一组点,每个点在给定间隔内有效”是什么意思?那个数字在区间内?我是否正确理解您的数据是一维的?如果是这种情况,1d rtree 将等同于区间图。您的问题应该在更高的抽象级别上考虑,而不是选择特定数据结构来索引 1d 间隔。这意味着这个问题是XY问题。
我在 StackExchange 上阅读了您的原始帖子。我是否正确理解您在 1d 间隔和某些数据([min, max] key)之间存在双向映射,并且您想双向搜索?由于区间和键都位于不同的空间中,因此您需要为它们提供 2 个单独的索引或排序机制,每个方向一个。
另一件事是:如果同一个键有 2 个重叠间隔怎么办?它们应该被视为一个扩大的区间还是两个独立的区间?例如。 ([1,3]'a') 和 ([2,4]'a') 应该返回为 ([1,4]'a') 吗?如果应该合并间隔,您必须决定是在存储到索引(例如 rtree)之前完成合并还是在执行所需间隔的查询后合并。
【参考方案1】:
这是一个使用双索引(R-tree 和 std::map)进行双向索引的基本程序:从 char 到 box/interval 以及从 box/interval 到 char:
包括,iostream
仅用于输出。
#include <boost/geometry.hpp>
#include <map>
#include <vector>
#include <iostream>
方便起见的命名空间。
namespace bg = boost::geometry;
namespace bgi = boost::geometry::index;
基本双向索引允许插入框/间隔字符对并基于字符查找框或基于框(相交)的字符向量。 insert()
在需要时合并框。
template <typename Box, typename T>
class rtree_map_index
typedef std::map<T, Box> map_type;
typedef typename map_type::iterator map_iterator;
typedef typename map_type::const_iterator map_const_iterator;
typedef std::pair<Box, map_iterator> rtree_value;
typedef bgi::rtree<rtree_value, bgi::rstar<4> > rtree_type;
public:
void insert(Box const& box, T const& v)
std::pair<map_iterator, bool>
p = m_map.insert(std::make_pair(v, box));
map_iterator map_it = p.first;
T const& map_val = map_it->first;
Box & map_box = map_it->second;
// new key,value inserted into map
if (p.second)
// insert it to the r-tree
m_rtree.insert(rtree_value(map_box, map_it));
// key already exists in map and box has to be expanded
else if (! bg::covered_by(box, map_box))
// calculate expanded box
Box new_box = map_box;
bg::expand(new_box, box);
// update r-tree
m_rtree.remove(rtree_value(map_box, map_it));
m_rtree.insert(rtree_value(new_box, map_it));
// update map
map_box = new_box;
bool find(T const& v, Box & result) const
map_const_iterator it = m_map.find(v);
if (it != m_map.end())
result = it->second;
return true;
return false;
void find(Box const& box, std::vector<char> & result) const
std::vector<rtree_value> res;
m_rtree.query(bgi::intersects(box), std::back_inserter(res));
result.resize(res.size());
for (size_t i = 0; i < res.size(); ++i)
result[i] = res[i].second->first;
private:
rtree_type m_rtree;
map_type m_map;
;
带有基本用例的主函数。
int main()
用于存储在 r-tree(框)中的二维数据。
typedef bg::model::point<double, 2, bg::cs::cartesian> point;
typedef bg::model::box<point> box;
rtree_map_index<box, char> index;
index.insert(box(point(0, 0), point(3, 3)), 'a');
index.insert(box(point(1, 1), point(4, 4)), 'a');
index.insert(box(point(5, 5), point(6, 6)), 'b');
box res1;
index.find('a', res1);
std::cout << bg::wkt(res1) << std::endl;
std::vector<char> res2;
index.find(box(point(4, 4), point(5, 5)), res2);
BOOST_ASSERT(res2.size() == 2);
std::cout << res2[0] << std::endl;
std::cout << res2[1] << std::endl;
对于存储在 r-tree 中的一维数据(区间)
typedef bg::model::point<double, 1, bg::cs::cartesian> point;
typedef bg::model::box<point> box;
rtree_map_index<box, char> index;
index.insert(box(point(0), point(3)), 'a');
index.insert(box(point(1), point(4)), 'a');
index.insert(box(point(5), point(6)), 'b');
box res1;
index.find('a', res1);
std::cout << "(" << bg::get<0, 0>(res1) << ", " << bg::get<1, 0>(res1) << ")" << std::endl;
std::vector<char> res2;
index.find(box(point(4), point(5)), res2);
BOOST_ASSERT(res2.size() == 2);
std::cout << res2[0] << std::endl;
std::cout << res2[1] << std::endl;
结束。
return 0;
请注意,您可以使用interval_map
代替rtree
。您应该能够在 rtree_map_index
之上构建。您可以添加一个构造函数,从 std::pair<Box, T>
类型的元素容器创建 map 和 rtree,以利用 r-tree 打包算法。你可以实现任何你需要的find()
函数。等等。
【讨论】:
以上是关于R-trees 上的多级查询(交集、联合)的主要内容,如果未能解决你的问题,请参考以下文章