在 Boost.Geometry 库的空间索引(Spatial Indexes)模块中,**谓词(Predicates)**是执行高效空间查询的核心机制。它们允许开发者定义几何对象之间的空间关系或自定义逻辑,从而从 R-Tree 等索引结构中精确筛选出目标数据。

1. 查询函数概览

所有谓词最终都通过 rtreequery 成员函数生效。该函数支持组合多个谓词,仅当所有条件均满足时,对应的值才会被返回。

函数原型

template <typename Predicates, typename OutIter>
size_type query(Predicates const& predicates, OutIter out_it);

参数说明

类型 名称 描述
Predicates const& predicates 一个或多个谓词的组合(通过 && 连接)。
OutIter out_it 输出迭代器,用于接收查询结果(例如 std::back_inserter)。

返回值

返回找到的元素数量(size_type)。

核心规则

  • 逻辑与连接:多个谓词可通过 operator&&() 连接,表示必须同时满足所有条件。
  • 最近邻限制:在一个查询中,只能使用一个 nearest 谓词。若传递多个,将导致编译错误。
  • 否定操作:空间谓词支持取反操作(!),用于排除特定空间关系。
  • 方向性关键:所有空间谓词的判定逻辑中,第一个参数始终是 R-Tree 中的“索引值”第二个参数是用户传入的“参考几何”

2. 空间关系谓词 (Spatial Predicates)

此类谓词基于 OGC 标准定义的空间拓扑关系。理解的关键在于明确谁相对于谁的关系。

2.1 contains (包含)

功能描述
生成一个谓词,用于查询那些完全包含给定几何对象的索引值。

  • 直观理解:索引值是“大容器”,给定几何是“小物体”。我们要找的是那些能把给定几何包住的索引值。

判定逻辑
boost::geometry::contains(索引值, 给定几何) 返回 true 时,该索引值被选中。
注意:这里检查的是 索引值 是否包含 给定几何

函数原型

template<typename Geometry>
unspecified contains(Geometry const& g);

参数说明

类型 名称 描述
Geometry const& g 作为被包含对象的参考几何。

2.2 covered_by (被覆盖)

功能描述
生成一个谓词,用于查询那些被给定几何对象完全覆盖的索引值。

  • 直观理解:索引值是“小物体”,给定几何是“大容器”。我们要找的是那些完全落在给定几何内部(含边界)的索引值。

判定逻辑
boost::geometry::covered_by(索引值, 给定几何) 返回 true 时,该索引值被选中。
注意:这里检查的是 索引值 是否被 给定几何 覆盖。

函数原型

template<typename Geometry>
unspecified covered_by(Geometry const& g);

参数说明

类型 名称 描述
Geometry const& g 作为覆盖者的参考几何。

2.3 covers (覆盖)

功能描述
生成一个谓词,用于查询那些完全覆盖给定几何对象的索引值。

  • 直观理解:这是 covered_by 的逆运算。索引值是“大容器”,给定几何是“小物体”。我们要找的是那些能盖住给定几何的索引值。

判定逻辑
boost::geometry::covers(索引值, 给定几何) 返回 true 时,该索引值被选中。
注意:这里检查的是 索引值 是否覆盖 给定几何

函数原型

template<typename Geometry>
unspecified covers(Geometry const& g);

参数说明

类型 名称 描述
Geometry const& g 被覆盖的参考几何。

2.4 disjoint (不相交)

功能描述
生成一个谓词,用于查询那些与给定几何对象完全没有交集的索引值。

  • 应用场景:常用于排除法查询,寻找远离某区域的物体。

判定逻辑
boost::geometry::disjoint(索引值, 给定几何) 返回 true 时,该索引值被选中。
注意:检查 索引值给定几何 是否互不接触。

函数原型

template<typename Geometry>
unspecified disjoint(Geometry const& g);

参数说明

类型 名称 描述
Geometry const& g 用于检测交集的参考几何。

2.5 intersects (相交)

功能描述
生成一个谓词,用于查询那些与给定几何对象存在任何形式交集的索引值。

  • 应用场景:最常用的空间查询,适用于碰撞检测、范围搜索等。

判定逻辑
boost::geometry::intersects(索引值, 给定几何) 返回 true 时,该索引值被选中。
注意:检查 索引值 是否与 给定几何 有重叠。

函数原型

template<typename Geometry>
unspecified intersects(Geometry const& g);

参数说明

类型 名称 描述
Geometry const& g 用于检测相交的参考几何。

2.6 overlaps (重叠)

功能描述
生成一个谓词,用于查询那些与给定几何对象部分重叠的索引值。

  • 注意:重叠通常指两个几何体维度相同,且有公共内部点,但互不包含。

判定逻辑
boost::geometry::overlaps(索引值, 给定几何) 返回 true 时,该索引值被选中。
注意:检查 索引值 是否与 给定几何 发生重叠。

函数原型

template<typename Geometry>
unspecified overlaps(Geometry const& g);

参数说明

类型 名称 描述
Geometry const& g 用于检测重叠的参考几何。

2.7 within (在…内部)

功能描述
生成一个谓词,用于查询那些完全位于给定几何对象内部的索引值。

  • 直观理解:索引值是“小物体”,给定几何是“大容器”。我们要找的是那些完全在给定几何里面的索引值。
  • 对比:这与 contains 正好相反。within(g) 找的是在 g 里面的;contains(g) 找的是包住 g 的。

判定逻辑
boost::geometry::within(索引值, 给定几何) 返回 true 时,该索引值被选中。
注意:检查 索引值 是否在 给定几何 内部。

函数原型

template<typename Geometry>
unspecified within(Geometry const& g);

参数说明

类型 名称 描述
Geometry const& g 作为容器的参考几何。

3. 特殊功能谓词

除了标准的空间拓扑关系外,Boost 还提供了更灵活的谓词以满足复杂需求。

3.1 satisfies (满足自定义条件)

功能描述
这是一个通用的包装器,允许用户传入自定义的一元谓词(函数、函数对象或 Lambda 表达式)。

  • 作用:对通过空间过滤后的候选值进行二次筛选,或者在不依赖空间关系时直接过滤数据。
  • 灵活性:可以检查值的属性(如 ID、颜色、类型等非几何属性)。

判定逻辑
当用户定义的谓词函数 pred(索引值) 返回 true 时,该索引值被选中。

函数原型

template<typename UnaryPredicate>
unspecified satisfies(UnaryPredicate const& pred);

参数说明

类型 名称 描述
UnaryPredicate const& pred 一元谓词函数或函数对象。该对象需接受索引值类型作为参数,并返回 bool

使用提示
常与空间谓词组合使用,例如:intersects(box) && satisfies(is_red),意为“查找与框相交且为红色的元素”。

3.2 nearest (最近邻搜索)

功能描述
触发 K-最近邻 (K-Nearest Neighbor, KNN) 搜索模式。

  • 作用:根据距离度量,找到离给定几何对象最近的 kkk 个索引值。
  • 底层机制:内部使用 boost::geometry::comparable_distance(索引值, 给定几何) 计算距离。
  • 约束:单次查询中仅限使用一个 nearest 谓词。

判定逻辑
不按布尔值判断,而是按距离排序,选取距离 给定几何 最近的 kkk索引值

函数原型

template<typename Geometry>
unspecified nearest(Geometry const& geometry, std::size_t k);

参数说明

类型 名称 描述
Geometry const& geometry 参考几何对象,距离计算的起点。
std::size_t k 需要返回的最近邻元素的最大数量。

组合使用
nearest 可以与其他空间谓词组合。例如:nearest(pt, 5) && intersects(box) 表示“查找与框相交的、离点 pt 最近的 5 个元素”。


3. 示例代码

#include <iostream>
#include <vector>
#include <string>
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/geometries/box.hpp>
#include <boost/geometry/index/rtree.hpp>

// 命名空间简化
namespace bg = boost::geometry;
namespace bgi = boost::geometry::index;

// 定义几何类型
// 2D 点类型 (double 精度)
typedef bg::model::d2::point_xy<double> point_t;
// 2D 包围盒类型 (最小点, 最大点)
typedef bg::model::box<point_t> box_t;
// R-Tree 存储的值类型: <几何对象, 唯一ID>
typedef std::pair<box_t, int> value_t;

void DemoPredicates()
{
    // ==========================================
    // 1. 初始化 R-Tree
    // ==========================================
    // 创建一个基于二次分割算法的 R-Tree
    bgi::rtree<value_t, bgi::quadratic<16>> rtree;

    // ==========================================
    // 2. 插入测试数据
    // ==========================================
    // 我们创建几个不同位置的矩形盒子
    // Box A: [0, 0] to [2, 2] (左下角区域)
    rtree.insert({box_t(point_t(0, 0), point_t(2, 2)), 1});

    // Box B: [1, 1] to [3, 3] (与 A 重叠的区域)
    rtree.insert({box_t(point_t(1, 1), point_t(3, 3)), 2});

    // Box C: [5, 5] to [7, 7] (远离 A 和 B 的区域)
    rtree.insert({box_t(point_t(5, 5), point_t(7, 7)), 3});

    // Box D: [0, 0] to [10, 10] (一个大盒子,完全包含 A, B, C)
    rtree.insert({box_t(point_t(0, 0), point_t(10, 10)), 4});

    std::cout << "=== Data Inserted ===" << std::endl;
    std::cout << "Total elements in R-Tree: " << rtree.size() << std::endl;
    std::cout << std::endl;

    // 辅助函数:打印查询结果
    auto print_results = [](const std::string& query_name, const std::vector<value_t>& result) {
        std::cout << "--- Query: " << query_name << " ---" << std::endl;
        if (result.empty()) {
            std::cout << "No results found." << std::endl;
        } else {
            for (const auto& v : result) {
                // 获取盒子的最小和最大坐标以便展示
                point_t min_p = bg::return_centroid<point_t>(v.first); // 简单展示用中心点
                std::cout << "Found ID: " << v.second
                          << " (Centroid approx: " << min_p.get<0>() << ", " << min_p.get<1>() << ")" << std::endl;
            }
        }
        std::cout << std::endl;
    };

    // 存储结果的容器
    std::vector<value_t> result;

    // ==========================================
    // 3. 谓词演示
    // ==========================================

    // --- 3.1 intersects (相交) ---
    // 描述:查找与给定查询框有任何重叠部分的元素
    // 场景:查询框 [1.5, 1.5] 到 [2.5, 2.5]
    // 预期:应命中 Box A (ID 1), Box B (ID 2), 和 Box D (ID 4)
    {
        box_t query_box(point_t(1.5, 1.5), point_t(2.5, 2.5));
        result.clear();

        // 执行查询:bgi::intersects 是核心谓词
        rtree.query(bgi::intersects(query_box), std::back_inserter(result));

        print_results("intersects(query_box)", result);
    }

    // --- 3.2 within (在...内部) ---
    // 描述:查找完全位于给定查询框内部的元素
    // 场景:查询框 [0, 0] 到 [2.5, 2.5]
    // 预期:Box A (ID 1) 完全在其中。Box B (ID 2) 部分超出,所以不会被选中。Box D (ID 4) 比查询框大,也不会被选中。
    {
        box_t query_box(point_t(0, 0), point_t(2.5, 2.5));
        result.clear();

        rtree.query(bgi::within(query_box), std::back_inserter(result));

        print_results("within(query_box)", result);
    }

    // --- 3.3 contains (包含) ---
    // 描述:查找完全包含给定查询框的元素
    // 场景:查询框 [1.5, 1.5] 到 [2.5, 2.5]
    // 预期:只有 Box D (ID 4) 足够大,能完全包住这个查询框。
    {
        box_t query_box(point_t(1.5, 1.5), point_t(2.5, 2.5));
        result.clear();

        rtree.query(bgi::contains(query_box), std::back_inserter(result));

        print_results("contains(query_box)", result);
    }

    // --- 3.4 disjoint (不相交) ---
    // 描述:查找与给定查询框完全没有交集的元素
    // 场景:查询框 [0, 0] 到 [2, 2] (即 Box A 的位置)
    // 预期:Box C (ID 3) 离得远,不相交。Box D (ID 4) 包含了它,所以相交(不选)。Box A 和 B 都相交。
    // 注意:通常 disjoint 用于排除法,这里只展示逻辑。
    {
        box_t query_box(point_t(0, 0), point_t(2, 2));
        result.clear();

        rtree.query(bgi::disjoint(query_box), std::back_inserter(result));

        print_results("disjoint(query_box)", result);
    }

    // --- 3.5 nearest (最近邻) ---
    // 描述:查找距离给定点最近的 K 个元素
    // 场景:参考点 (6, 6),查找最近的 2 个元素
    // 预期:Box C (ID 3) 最近 (中心在 6,6),其次是 Box B (ID 2) 或 Box D (ID 4) 取决于距离计算细节
    {
        point_t query_point(6.0, 6.0);
        result.clear();
        size_t k = 2;

        // 语法:bgi::nearest(geometry, k)
        rtree.query(bgi::nearest(query_point, k), std::back_inserter(result));

        print_results("nearest(point, 2)", result);
    }

    // --- 3.6 satisfies (自定义谓词) ---
    // 描述:使用 Lambda 表达式进行自定义逻辑过滤
    // 场景:找出所有 ID 为偶数的元素,不管位置在哪里
    // 注意:satisfies 通常与其他空间谓词组合使用效率更高,但也可以单独使用(会遍历所有)
    {
        result.clear();

        // Lambda: 检查值的 second (ID) 是否为偶数
        auto is_even_id = [](const value_t& v) {
            return v.second % 2 == 0;
        };

        // 语法:bgi::satisfies(predicate_function)
        rtree.query(bgi::satisfies(is_even_id), std::back_inserter(result));

        print_results("satisfies(is_even_id)", result);
    }

    // --- 3.7 组合谓词 (Logical AND) ---
    // 描述:同时满足多个条件
    // 场景:查找与 Box [0,0]-[4,4] 相交 (intersects) 且 ID 为偶数 (satisfies) 的元素
    // 预期:
    // Box A (ID 1): 相交,但 ID 奇数 -> 排除
    // Box B (ID 2): 相交,ID 偶数 -> 选中
    // Box D (ID 4): 相交,ID 偶数 -> 选中
    // Box C (ID 3): 不相交 -> 排除
    {
        box_t query_box(point_t(0, 0), point_t(4, 4));
        result.clear();

        auto is_even_id = [](const value_t& v) {
            return v.second % 2 == 0;
        };

        // 使用 && 操作符连接两个谓词
        // 注意:括号对于运算符优先级很重要
        rtree.query(bgi::intersects(query_box) && bgi::satisfies(is_even_id),
                    std::back_inserter(result));

        print_results("intersects(box) && satisfies(is_even)", result);
    }

    // --- 3.8 否定谓词 (Logical NOT) ---
    // 描述:查找不满足特定空间关系的元素
    // 场景:查找 不 与 Box [0,0]-[2,2] 相交的元素
    // 预期:Box C (ID 3) 和 Box D (ID 4)?
    // 等等,Box D (0,0 to 10,10) 与 (0,0 to 2,2) 是相交的。
    // 所以只有 Box C (ID 3) 会被选中。
    {
        box_t query_box(point_t(0, 0), point_t(2, 2));
        result.clear();

        // 使用 ! 操作符取反
        rtree.query(!bgi::intersects(query_box), std::back_inserter(result));

        print_results("!intersects(query_box)", result);
    }
}

输出

=== Data Inserted ===
Total elements in R-Tree: 4

--- Query: intersects(query_box) ---
Found ID: 1 (Centroid approx: 1, 1)
Found ID: 2 (Centroid approx: 2, 2)
Found ID: 4 (Centroid approx: 5, 5)

--- Query: within(query_box) ---
Found ID: 1 (Centroid approx: 1, 1)

--- Query: contains(query_box) ---
Found ID: 2 (Centroid approx: 2, 2)
Found ID: 4 (Centroid approx: 5, 5)

--- Query: disjoint(query_box) ---
Found ID: 3 (Centroid approx: 6, 6)

--- Query: nearest(point, 2) ---
Found ID: 3 (Centroid approx: 6, 6)
Found ID: 4 (Centroid approx: 5, 5)

--- Query: satisfies(is_even_id) ---
Found ID: 2 (Centroid approx: 2, 2)
Found ID: 4 (Centroid approx: 5, 5)

--- Query: intersects(box) && satisfies(is_even) ---
Found ID: 2 (Centroid approx: 2, 2)
Found ID: 4 (Centroid approx: 5, 5)

--- Query: !intersects(query_box) ---
Found ID: 3 (Centroid approx: 6, 6)

总结与速查表

Boost.Geometry 的谓词系统提供了一套声明式的查询语言。为了避免混淆,请牢记以下方向性规则:所有谓词 P(g) 的内部逻辑均为 boost::geometry::P(索引值, g)

谓词函数 语义方向 (索引值 vs 参考几何 g) 通俗记忆
contains(g) 索引值 包含 g 找“大盒子” (能装下 g 的)
within(g) 索引值g 内部 找“小物体” (被 g 装下的)
covers(g) 索引值 覆盖 g 找“大盖子” (能盖住 g 的)
covered_by(g) 索引值g 覆盖 找“被盖物” (被 g 盖住的)
intersects(g) 索引值g 相交 找“有接触的”
disjoint(g) 索引值g 不相交 找“没接触的”
overlaps(g) 索引值g 重叠 找“部分重合的”
satisfies(fn) fn(索引值) 为真 找“符合自定义条件的”
nearest(g, k) g 最近的 k 个索引值 找“离得最近的”

通过灵活组合这些谓词(使用 && 连接),开发者可以轻松实现从简单的范围查询到复杂的混合条件搜索,充分发挥 R-Tree 空间索引的性能优势。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐