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&lt;pair&lt;unsigned int, unsigned int&gt; &gt; 和一组sp 的点set&lt;unsigned int&gt;,我想迭代作为以下伪代码查询结果的所有值:
any(si, intersect(rtree_level1)) &&
any(sp, contains(rtree_level2)) &&
value in ps

换句话说,我想要包含si 中包含的区间和sp 中包含的点的交集的rtree 的子树,并且它的值也在sp 中。如果你愿意,你可以假设 si 中的所有区间都是不相交的。

    给定一组spi 的点和区间set&lt;unsigned int, pair&lt;unsigned int, unsigned int&gt; &gt;,我想迭代作为以下伪代码查询结果的所有值:
any(spi, intersect(rtree_level1)(spi.interval) && 
         contains(rtree_level2)(spi.point) &&
         value in spi.point
   )

换句话说,我想要来自spi 的每个元素的所有子树的联合,它们是给定区间的交集,并且它们包含 仅作为键(第二级)和值的那些点。如果 sisp 都有一个元素,这就像 unionquery 1 生成的所有 R-tree .

我可以理解如何使用satisfy 谓词并将transform 应用于qbegin 生成的迭代器,但是 在 boost 中最有效的方法是什么?

【问题讨论】:

“rtree_level1”、“rtree_level2 和“subtree”是什么意思?set 是什么意思?通常是std::set。是这样吗?如何set&lt;unsigned int&gt; 是一组点吗?你的点是一维的吗?这是什么意思:set&lt;unsigned int, pair&lt;unsigned int, unsigned int&gt; &gt;std::set 只接受一个定义 value_type 的类型。你的意思是 std::map 还是有什么不同?这看起来像是一个 XY 问题。您一般想要实现什么目标? 我的意思是std::set。要了解一般情况下的问题,您可以查看this 帖子(这也是由我完成的)。 set&lt;unsigned int, pair&lt;unsigned int, unsigned int&gt; &gt; 表示一组点,其中每个点在给定的时间间隔内都有效。非常感谢您的关注:) set&lt;unsigned int, pair&lt;unsigned int, unsigned int&gt; &gt;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&lt;Box, T&gt; 类型的元素容器创建 map 和 rtree,以利用 r-tree 打包算法。你可以实现任何你需要的find() 函数。等等。

【讨论】:

以上是关于R-trees 上的多级查询(交集、联合)的主要内容,如果未能解决你的问题,请参考以下文章

检索表交集的标准 SQL 查询是啥?

SQL 联合查询

将联合类型转换为交集类型[重复]

ado.net 和 dapper 之间的交集/联合是啥?

打字稿将联合转换为交集[重复]

使用 TensorFlow 实现联合损失的交集