geometry icon indicating copy to clipboard operation
geometry copied to clipboard

predicates::satisfies doesn't(?) allow control over tree traversal

Open BenFrantzDale opened this issue 5 years ago • 5 comments

AFAICT, predicates::satisfies<Fun, Negated> lets you give an arbitrary function object and then use it to, say, visit every rtree::value_type in an rtree. It seems like it just needs a second function, which could default to [](auto&&...) { return true; }, to let you control the traversal of the tree. For example, suppose I want to visit an rtree<Point2D> and visit all points in the (-,+) direction of the line y=x. I think(?) there's no way to do that right now. If I have struct IsAboveLeft { bool operator()(const Point2D& pt) const { return pt[0] - pt[1] > 0; } }; then I could use predicates::satisfies<IsAboveLeft>, but it would be an O(n) query of my rtree. But if I could do something like

struct FindAboveLeft {
    bool partially_contains(const Box&) const; // check if ay part of AABB is above-left of the line y=x
    bool contains(const Value&) const; // check if the point is above-left of the line y=x
};
// ...
rtree.query(satisfies<FindAboveLeft>(), std::back_inserter(results));

then the query could be O(log n) per result returned. As it is, I can't see how to get this behavior without injecting things into the boost::geometry::index::predicates::detail namespace. I think a predicate like this could be very expressive and allow for all sorts of interesting queries.

BenFrantzDale avatar Jul 14 '20 21:07 BenFrantzDale

Yes, satisfies predicate is only applied to Values. It is not a spatial predicate.

It is possible to perform such query by implementing a new class like LineBoundedSpace and then passing and object of this class in bgi::intersects into rtree::query after overloading bg::intersects for:

  • LineBoundedSpace and Box
  • LineBoundedSpace and Value/Point2D

But I agree that an explicit user-defined spatial predicate allowing the users to define tree traversal and filtering of values could be useful. So I'll treat this issue as a feature request.

awulkiew avatar Jul 14 '20 22:07 awulkiew

I’m not sure I follow. Are you saying I would specialize detail::spatial_predicate< LineBoundedSpace, ...>? That feels wrong since I’d assume anything in detail is an implementation detail not a customization point...

BenFrantzDale avatar Jul 14 '20 23:07 BenFrantzDale

No, it is not required to touch anything in detail. You only has to overload:

namespace boost { namespace geometry {
inline bool intersects(rtree_t::bounds_type const& box, MyLine const& l) { return true; }
inline bool intersects(Point2D const& pt, MyLine const& l) { return true; }
}}

and then call:

MyLine my_line;
rtree.query(bgi::intersects(my_line), std::back_inserter(result));

See also: https://stackoverflow.com/questions/19490435/boost-geometry-spatial-query-shapes

awulkiew avatar Jul 15 '20 00:07 awulkiew

OK, with that I get a compile error: https://godbolt.org/z/3qxdPM

I'll take a stab at implementing the multi-level satisfies equivalent.

BenFrantzDale avatar Jul 15 '20 11:07 BenFrantzDale

Yes, the compiler has problems with picking the overloads. It chooses to instantiate the original bg::intersects template, as mentioned here: https://stackoverflow.com/questions/19490435/boost-geometry-spatial-query-shapes and fails to do it.

To force it to do it you can e.g. add some forward declarations before including the library:

#include <boost/geometry/geometries/geometries.hpp>

namespace bg = boost::geometry;
using Pt = bg::model::point<double, 2, bg::cs::cartesian>;

struct RightHalfPlane;

namespace boost::geometry {

bool intersects(const bg::model::box<Pt>& box, const RightHalfPlane& x);
bool intersects(const Pt& box, const RightHalfPlane& x);

} // boost::geometry

#include <boost/geometry/geometry.hpp>

namespace bgi = boost::geometry::index;

then the rest of your code.

awulkiew avatar Jul 15 '20 16:07 awulkiew