问题 boost :: geometry:使用圆圈的最近邻居


我正在使用 RTREE 实现boost :: geometry来存储(大量)2D点。现在我需要做距离最近的neigbors查询。

但是,手册 仅描述查询 作为矩形框(即“让我得到这个矩形内的所有点”)或“KNN”查询(“从这里获取最近的'n'点)。

我想要的实际上是“让我得到距离小于'n'的点集。”

我注意到你可以定义一元谓词,但是是......一元(因此,不适合两点的条件)。

一些手册文件 model::ring 我最初认为可能适合圆形的类,但它实际上更像是一种分段线(多边形)。这个假设是否正确?

有没有其他方法来处理这样的查询?或者它根本不可能?


1455
2018-04-07 10:00


起源



答案:


记录在案的“用户定义的查询”中的最后一个示例 展示了如何在谓词中使用lambda。此lambda可以绑定范围中的其他变量,例如,您要查找其邻居的点。

这是一个小例子。对于位于直线上的点集合,该示例查找更接近(5,5)而不是2个单位的点 y = x。应该很容易修改,以便首先检查所寻找的点是否在R树中,或者直接从R树中获取它。

#include <iostream>

#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point.hpp>
#include <boost/geometry/index/rtree.hpp>


namespace bg = boost::geometry;
namespace bgi = boost::geometry::index;

typedef bg::model::point<float, 2, bg::cs::cartesian> point;
typedef std::pair<point, unsigned> value;

int main(int argc, char *argv[])
{
    bgi::rtree< value, bgi::quadratic<16> > rtree;

    // create some values
    for ( unsigned i = 0 ; i < 10 ; ++i )
    {
        point p = point(i, i);
        rtree.insert(std::make_pair(p, i));
    }

    // search for nearest neighbours
    std::vector<value> returned_values;
    point sought = point(5, 5);
    rtree.query(bgi::satisfies([&](value const& v) {return bg::distance(v.first, sought) < 2;}),
                std::back_inserter(returned_values));

    // print returned values
    value to_print_out;
    for (size_t i = 0; i < returned_values.size(); i++) {
        to_print_out = returned_values[i];
        float x = to_print_out.first.get<0>();
        float y = to_print_out.first.get<1>();
        std::cout << "Select point: " << to_print_out.second << std::endl;
        std::cout << "x: " << x << ", y: " << y << std::endl;
    }

    return 0;
}

在OS X上通过Macports安装Boost进行编译和运行:

$ c++ -std=c++11 -I/opt/local/include -L/opt/local/lib main.cpp -o geom && ./geom
Select point: 4
x: 4, y: 4
Select point: 5
x: 5, y: 5
Select point: 6
x: 6, y: 6

8
2018-04-07 10:58



对于未来的读者,这可以在Debian / Ubuntu上轻松编译 g++ -o main.o -c main.cpp -std=c++0x,通过安装gcc 4.6.3和Boost 1.54 apt-get - kebs
该解决方案可行,但效率不高。仅对值(这意味着不使用rtree的索引功能)检查satisfies()谓词,如果没有其他空格或最近谓词,则必须检查所有值。您希望在一定距离内返回所有值,而不是NEAREST值。这不是kNN查询,而是空间查询。最简单和最有效的方法是在封闭你的区域的Box中搜索,然后检查距离或传递额外的满足()谓词,例如: - Adam Wulkiewicz
你可以在上面的查询中添加bgi :: within()来实际在空间上搜索索引:rt.query(bgi :: within(enc_box)&& bgi :: satisfies(distance_pred),out); - Adam Wulkiewicz
另一种方法是使用未正式发布的扩展 - nsphere(github.com/boostorg/geometry/tree/develop/include/boost/...)。如果使用空间谓词传递,则查询将返回圆内的所有值。上次我检查(前一段时间)这样的东西有效,但我无法保证它仍然有效:bgi :: query(bgi :: intersects(my_circle),out); - Adam Wulkiewicz
看我的 测试代码的分支。在这里,您会发现边界框实际上有所不同。 - Weidenrinde


答案:


记录在案的“用户定义的查询”中的最后一个示例 展示了如何在谓词中使用lambda。此lambda可以绑定范围中的其他变量,例如,您要查找其邻居的点。

这是一个小例子。对于位于直线上的点集合,该示例查找更接近(5,5)而不是2个单位的点 y = x。应该很容易修改,以便首先检查所寻找的点是否在R树中,或者直接从R树中获取它。

#include <iostream>

#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point.hpp>
#include <boost/geometry/index/rtree.hpp>


namespace bg = boost::geometry;
namespace bgi = boost::geometry::index;

typedef bg::model::point<float, 2, bg::cs::cartesian> point;
typedef std::pair<point, unsigned> value;

int main(int argc, char *argv[])
{
    bgi::rtree< value, bgi::quadratic<16> > rtree;

    // create some values
    for ( unsigned i = 0 ; i < 10 ; ++i )
    {
        point p = point(i, i);
        rtree.insert(std::make_pair(p, i));
    }

    // search for nearest neighbours
    std::vector<value> returned_values;
    point sought = point(5, 5);
    rtree.query(bgi::satisfies([&](value const& v) {return bg::distance(v.first, sought) < 2;}),
                std::back_inserter(returned_values));

    // print returned values
    value to_print_out;
    for (size_t i = 0; i < returned_values.size(); i++) {
        to_print_out = returned_values[i];
        float x = to_print_out.first.get<0>();
        float y = to_print_out.first.get<1>();
        std::cout << "Select point: " << to_print_out.second << std::endl;
        std::cout << "x: " << x << ", y: " << y << std::endl;
    }

    return 0;
}

在OS X上通过Macports安装Boost进行编译和运行:

$ c++ -std=c++11 -I/opt/local/include -L/opt/local/lib main.cpp -o geom && ./geom
Select point: 4
x: 4, y: 4
Select point: 5
x: 5, y: 5
Select point: 6
x: 6, y: 6

8
2018-04-07 10:58



对于未来的读者,这可以在Debian / Ubuntu上轻松编译 g++ -o main.o -c main.cpp -std=c++0x,通过安装gcc 4.6.3和Boost 1.54 apt-get - kebs
该解决方案可行,但效率不高。仅对值(这意味着不使用rtree的索引功能)检查satisfies()谓词,如果没有其他空格或最近谓词,则必须检查所有值。您希望在一定距离内返回所有值,而不是NEAREST值。这不是kNN查询,而是空间查询。最简单和最有效的方法是在封闭你的区域的Box中搜索,然后检查距离或传递额外的满足()谓词,例如: - Adam Wulkiewicz
你可以在上面的查询中添加bgi :: within()来实际在空间上搜索索引:rt.query(bgi :: within(enc_box)&& bgi :: satisfies(distance_pred),out); - Adam Wulkiewicz
另一种方法是使用未正式发布的扩展 - nsphere(github.com/boostorg/geometry/tree/develop/include/boost/...)。如果使用空间谓词传递,则查询将返回圆内的所有值。上次我检查(前一段时间)这样的东西有效,但我无法保证它仍然有效:bgi :: query(bgi :: intersects(my_circle),out); - Adam Wulkiewicz
看我的 测试代码的分支。在这里,您会发现边界框实际上有所不同。 - Weidenrinde


手册记录了一些我认为最初可能适合圆形的模型::环类,但它实际上更像是一种分段线(多边形)。这个假设是否正确?

我认为这是对的。

我注意到你可以定义一元谓词,但是是......一元(因此,不适合两点的条件)。

“第二个”(或参考)点是否会被修复?因为那时您可以使用简单的绑定表达式来提供参考点。


此外,您可以使用非常大的KNN算法 n 并在谓词上添加某种破坏条件:

中断或暂停查询

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;
}

这可以很好地工作,假设算法已经按照距离上升的顺序遍历点(当然,您需要检查该假设)。


4
2018-04-07 10:39



coliru.stacked-crooked.com/a/d60bf73560cded82 - llonesmiz
@kebs我的意思是你可以使用标准的NNK并在距离阈值之后切割它。这假设以最接近最远的顺序访问点(在空间索引中可能是真的,但可能不是 - 我对库的这一部分知之甚少) - sehe
请记住,kNN查询会更慢,因为内部完成的事情比空间查询更多。 - Adam Wulkiewicz
在最近的查询迭代器的情况下,它保证首先得到最近的点。在query()函数的情况下,您可以假设输出的顺序是随机的(尽管它并非完全正确)。 - Adam Wulkiewicz
@sehe我写了。我将在文档中添加一些其他信息。顺便说一句,Boost现在可以在GitHub上使用,所以如果你认为应该添加/扩展的东西随意分叉回购并创建一个拉取请求。文档采用QuickBook格式,例如有关查询的章节可以在这里找到: github.com/boostorg/geometry/blob/develop/doc/index/rtree/... 如果您想自己构建文档并遇到问题,可以通过邮件列表与我们联系: lists.boost.org/mailman/listinfo.cgi/geometry - Adam Wulkiewicz