空间索引介绍

本文详细介绍了空间索引的实现原理,特别是Boost库中的Rtree及其变种(如GIST-Tree、quad-tree和bin),探讨了不同算法的选择,包括查询方式(如空间查询、最近邻查询和用户自定义条件),以及如何在实际场景中使用和优化这些索引结构。
摘要由CSDN通过智能技术生成

  空间索引的实现方式:Rtree 和其变种树 GIST-Tree、quad-tree(四叉树)、bin(网格索引)

  所有的空间索引都是先插入数据,把数据在内部数据结构进行划分,然后才能进行 query。

  这里介绍 boost 的开源 R-tree 库。

boost R-tree

boost 官网

构造

// index 在 namespace boost::geometry 里

rtree<Value,
      Parameters,
      IndexableGetter = index::indexable<Value>,
      EqualTo = index::equal_to<Value>,
      Allocator = std::allocator<Value> >

# 创建一个 rtree 对象
index::rtree<Value, index::linear<16>> rt;

  rtree 的第一个参数 value,必须要是能提取出 indexable 的对象,默认的有 boost 库的 point, box, segment。可以接受 pair 和 tuple, 但是这两个数据结构的第一个参数必须是 indexable 的。

  第二个参数表示 R-tree 插入和分割的算法类型,有下面三种算法可选。

index::linear<16>: 使用线性复杂性的方法来维护 R-tree。在插入和删除操作时,它会尝试保持节点的均衡,使得树的高度保持较小。由于它的复杂性是线性的,所以在某些情况下可能比其他算法更快。但是,它在处理大量数据和频繁更新时可能会失去一些性能。

index::quadratic<16>: 使用平方复杂性的方法来维护 R-tree。在插入和删除操作时更加注重节点的均衡,相对于线性算法,它可能在某些情况下提供更好的查询性能。但是,它的更新操作可能比线性算法慢一些。

index::rstar<16>: R*-tree 算法,一种优化的算法,旨在最小化节点的重叠并通过强制重新插入来进行平衡。这可以提高查询性能并减少树的高度。它通常在需要高性能的查询场景中使用,但可能会在更新操作时变得更加复杂。

  每种平衡算法都有其优缺点,最适合的选择取决于您的数据和应用程序的要求。如果您的数据集和查询方式是已知的,可以通过比较不同算法在实际情况下的性能表现来做出选择。

Query

三种 query 方式:

  • spatial predicates - spatial conditions that must be met by stored Value and some Geometry
  • distance predicates - distance conditions that must be met by stored Value and some Geometry
  • user-defined unary predicate - function, function object or lambda expression checking user-defined condition
Spatial queries

第一种是空间查询,查询和某个区域相关的操作。

std::vector<Value> result;
rt.query(index::contains(box), std::back_inserter(result));
rt.query(index::covered_by(box), std::back_inserter(result));
rt.query(index::covers(box), std::back_inserter(result));
rt.query(index::disjont(box), std::back_inserter(result));
rt.query(index::intersects(box), std::back_inserter(result));
rt.query(index::overlaps(box), std::back_inserter(result));
rt.query(index::within(box), std::back_inserter(result));

除了 box,还可以查询 segment

Nearest neighbours queries

查询和某个区域最近的元素。

std::vector<Value> result;
Point pt(/*...*/);
# k 表示返回离点 pt 最近的 k 个元素
rt.query(bgi::nearest(pt, k), std::back_inserter(result));

Box box(/*...*/);
rt.query(bgi::nearest(box, k), std::back_inserter(result));

Segment seg(/*...*/);
rt.query(bgi::nearest(seg, k), std::back_inserter(result));
User-defined unary predicate

用户可以自定义额外的条件,查询的结果是:满足查询条件,并且满足用户自定义的条件的元素。

bool is_red(Value const& v)
{
  return v.is_red();
}

struct is_red_o
{
  template <typename Value>
  bool operator()(Value const& v)
  {
    return v.is_red();
  }
}

// ...

rt.query(index::intersects(box) && index::satisfies(is_red),
         std::back_inserter(result));

rt.query(index::intersects(box) && index::satisfies(is_red_o()),
         std::back_inserter(result));

rt.query(index::intersects(box) && index::satisfies([](Value const& v) { return v.is_red(); }),
         std::back_inserter(result));
Passing set of predicates

可以传递多个查询条件

rt.query(index::intersects(box1) && !index::within(box2),
         std::back_inserter(result));

rt.query(index::intersects(box1) && !index::within(box2) && index::overlaps(box3),
         std::back_inserter(result));

不同类型的查询也可以放在一起

index::query(rt, index::nearest(pt, k) && index::within(b), std::back_inserter(returned_values));

BOOST_FOREACH(Value & v, rt | index::adaptors::queried(index::nearest(pt, k) && index::covered_by(b)))
  ; // do something with v
Iterative queries

可以用迭代器遍历查询的结果。

for ( Rtree::const_query_iterator it = tree.qbegin(bgi::nearest(pt, 10000)) ;
      it != tree.qend() ; ++it )
{
    // do something with value
    if ( has_enough_nearest_values() )
        break;
}
Inserting query results into another R-tree

查询的结果可以插入另一个 RTree

namespace bgi = boost::geometry::index;
typedef std::pair<Box, int> Value;
typedef bgi::rtree< Value, bgi::linear<32, 8> > RTree;

RTree rt1;
/* some inserting into the tree */

std::vector<Value> result;
rt1.query(bgi::intersects(Box(/*...*/)), std::back_inserter(result));
RTree rt2(result.begin(), result.end());

RTree rt3;
rt1.query(bgi::intersects(Box(/*...*/))), bgi::inserter(rt3));

RTree rt4(rt1 | bgi::adaptors::queried(bgi::intersects(Box(/*...*/)))));

bin

fastBin

对于 bin 算法的理解:

  1. 通过 add 获取到所有的数据
  2. 构建 bin,根据数据的多少,划分 bin 的格点。
  3. 遍历所有的数据,构建每个格点和在格点上的数据的联系。
  4. query 时,所有在 query 区域内部的格点的数据直接加入结果,只需要计算touch 的格点。

格点之间的关系可以分为 body x y corner, 这样可以简化计算

去重:smallbin 只返回包含左下角
fastbin: 每个shape 只返回一次

data structure
vector<vector<unsigned>>            _nodes; // nx,  ny * 4
// _nodes.resize(_nx), _nodes[0].resize(ny << 2, UINT_MAX);

// first is data, second is next on the corner list
mutable deque<pair<T, unsigned>>    _data;

// first is data index, second is next on the list
deque<pair<unsigned, unsigned>>     _indexes;
addBin

对每一个 shape addBin 的时候

  1. 先计算出这个 shape 所在的 bin 格点的范围。getRange
  2. 遍历所有 shape 所在的格点,计算 shape 和 每个格点的关系:getListType
  3. 然后 addList, 获取当前格点中已有的同类型的 shape _nodes[x][(y << 2) + lt] cur
    1. 如果 shape 的 listType 是 corner: _data 中当前 shape 的下一个指向 cur, nodes 中当前格点的这个类型的对象变为当前 shape
    2. 如果 shape 中的 listTpe 不是 corner:
query
  1. 获取要查询的 box 属于 bin 中的哪些格点
  2. 遍历每一个格点
    1. 获取当前格点的 ListType: myType, 这个格点在所有和 box 有关的格点中的位置,body,x,y,还是 corner
    2. 如果当前格点属于box 的内部格点,don’tcheck
    3. 遍历当前格点的 4 种 ListType

不会重复计算:通过下面的比较方式
c check c, x, y, b
x chekc c, y
y chekc c, x
b check c

smallBin

query:

getRange(): 获取 box 在 bin 上的范围 x1~x2 y1~y2

遍历所有格点,获取每一个格点相关的data list; 遍历 data list,每一个数据再具体比较,判断是不是在 box 上

checkBox 只有在 corner 位置的data 才会加入查询结果,所以不会有重复的问题

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值