问题
I am using the Rtree implementation of boost::geometry to store (lots of) 2D points. Now I need to do distance-based nearest neigbors queries.
However, the manual only describes queries as rectangular boxes (i.e. "Get me all the points that are inside this rectangle") or "KNN" queries ("Get me the nearest 'n' points from here).
What I want is actually "Get me the set of points that are at a distance less than 'n'".
I noticed that you can define a unary-predicate, but is is... unary (thus, not suitable for a condition on two points).
The manual documents some model::ring class that I thought at first might fit for a circle, but it is actually more a kind of a piece-wise line (a polygon). Is that assumption correct ?
Is there another way to process such a query ? Or is it simply not possible ?
回答1:
The last example in the documented "User-defined queries" shows how to use a lambda in the predicate. This lambda can bind other variables in the scope, for instance, the point whose neighbors you are looking for.
Here is a small example. The example looks for points that are closer to (5, 5) than 2 units, for a collection of points that lie on the straight y = x
. It should be easy to modify in order to check first if the sought point is in the R-tree, or get it directly out of the R-tree.
#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;
}
Compile and run with Boost installed via Macports on OS X:
$ 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
回答2:
The manual documents some model::ring class that I thought at first might fit for a circle, but it is actually more a kind of a piece-wise line (a polygon). Is that assumption correct ?
I think that's correct.
I noticed that you can define a unary-predicate, but is is... unary (thus, not suitable for a condition on two points).
Would the 'second' (or reference) point not be fixed? Because then you can use a simple bind expression to supply the reference point.
Additionally you can use the KNN algorithm with a very large n
and add some kind of breaking condition on the predicate:
Breaking or pausing the query
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; }
This could work pretty well, assuming that the algorithm already traverses the points in order of ascending distance (you will want to check that assumption of course).
来源:https://stackoverflow.com/questions/22909171/boostgeometry-nearest-neighbors-using-a-circle