空间索引

空间索引

  空间索引的实现方式: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

对于 bin 算法的理解:

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

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

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

posted @   卑以自牧lq  阅读(163)  评论(0编辑  收藏  举报
努力加载评论中...
点击右上角即可分享
微信分享提示