20.Boost.Geometry 中常用空间算法详解:crosses、densify、difference 与离散距离度量

Boost.Geometry(也称 Boost.GIL 的一部分,但更准确地说是独立的几何库)为 C++ 开发者提供了强大而灵活的地理空间计算能力。本文将深入解析五个核心算法接口:crosses(相交判断)densify(线段加密)difference(几何差集) ,以及两种用于形状相似性评估的距离度量方法------离散 Fréchet 距离离散 Hausdorff 距离


1. crosses ------ 判断两个几何体是否"交叉"

功能概述

crosses 算法用于判断两个几何对象在空间中是否"交叉"(cross)。该行为严格遵循 OGC Simple Feature Specification 中对 "Crosses" 关系的定义。

什么是"交叉"?

在 OGC 标准中,"交叉"特指两个几何体的内部有交集,且交集的维度小于至少其中一个几何体的维度。例如:

  • 一条线穿过一个多边形(线与多边形内部相交,但不完全包含于其中);
  • 两条线在非端点处相交;
  • 一个点位于一条线的内部(但不是端点)------这种情况不算交叉,因为点的维度为0,线为1,但点必须严格在内部才可能满足某些条件,不过 OGC 对 Point-Line 的 crosses 定义通常返回 false。

注意:如果两个几何体仅在边界接触(如线刚好碰到多边形边缘),则不视为交叉

接口形式

cpp 复制代码
template <typename Geometry1, typename Geometry2>
bool crosses(Geometry1 const& g1, Geometry2 const& g2);

使用默认策略进行交叉判断。

支持的几何类型组合

支持多种几何类型的组合,包括 Point、Linestring、Polygon、MultiPolygon 等,具体取决于维度差异是否符合"交叉"的数学定义。

示例代码

cpp 复制代码
#include <iostream>
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/geometries/polygon.hpp>
#include <boost/geometry/geometries/linestring.hpp>

int main() {
    namespace bg = boost::geometry;

    // 定义一个矩形多边形
    bg::model::polygon<bg::model::d2::point_xy<double>> poly;
    bg::read_wkt("POLYGON((0 0,0 3,3 3,3 0,0 0))", poly);

    // 定义一条穿过多边形内部的线
    bg::model::linestring<bg::model::d2::point_xy<double>> line1;
    bg::read_wkt("LINESTRING(1 1,2 2,4 4)", line1);

    // 判断是否交叉(穿过内部)
    bool result1 = bg::crosses(poly, line1);
    std::cout << "Crosses (line1): " << (result1 ? "Yes" : "No") << std::endl;

    // 定义一条仅接触边界的线(不穿过内部)
    bg::model::linestring<bg::model::d2::point_xy<double>> line2;
    bg::read_wkt("LINESTRING(1 1,1 2,1 3)", line2);

    bool result2 = bg::crosses(poly, line2);
    std::cout << "Crosses (line2): " << (result2 ? "Yes" : "No") << std::endl;

    return 0;
}

输出:

复制代码
Crosses (line1): Yes
Crosses (line2): No

2. densify ------ 几何体线段加密(细分)

功能概述

densify 算法用于对几何体中的线段进行"加密"------即在原始顶点之间插入新的点,使得任意相邻两点之间的距离不超过给定的最大阈值 max_distance。此操作常用于:

  • 提高曲线或折线的分辨率;
  • 为后续插值、投影或可视化做准备;
  • 模拟更平滑的路径(尽管结果仍是折线)。

该算法并非 OGC 标准函数 ,但功能类似于 PostGIS 中的 ST_Segmentize

接口形式

  • 基础版本

    cpp 复制代码
    template <typename Geometry, typename Distance>
    void densify(Geometry const& input, Geometry& output, Distance const& max_distance);

    使用默认坐标系策略(通常是笛卡尔坐标系)。

  • 带策略版本

    cpp 复制代码
    template <typename Geometry, typename Distance, typename Strategy>
    void densify(Geometry const& input, Geometry& output, Distance const& max_distance, Strategy const& strategy);

    支持指定坐标系策略,如:

    • cartesian(平面直角坐标)
    • spherical(球面坐标)
    • geographic(椭球地理坐标,考虑地球曲率)

注意 :距离单位由所选策略决定。例如,在地理坐标系下,max_distance 应以米为单位;而在笛卡尔系下,则与坐标单位一致(如度、米等)。

行为说明

  • 仅对线段(如 LineString、Polygon 边界)进行处理;
  • 不改变几何拓扑结构(如多边形仍保持封闭);
  • 内部环(如多边形的孔洞)也会被同样处理。

示例代码-无策略版本

cpp 复制代码
#include <iostream>
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/geometries/linestring.hpp>

int main() {
    namespace bg = boost::geometry;
    using point = bg::model::d2::point_xy<double>;
    using linestring = bg::model::linestring<point>;

    linestring ls;
    bg::read_wkt("LINESTRING(0 0, 0 10)", ls);

    linestring densified;
    // 将线段加密,使得每段不超过 3 单位长度
    bg::densify(ls, densified, 3.0);

    std::cout << "Original:   " << bg::wkt(ls) << std::endl;
    std::cout << "Densified:  " << bg::wkt(densified) << std::endl;

    return 0;
}

输出

复制代码
Original:   LINESTRING(0 0,0 10)
Densified:  LINESTRING(0 0,0 2.5,0 5,0 7.5,0 10)

示例代码-带策略版本

cpp 复制代码
#include <iostream>
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point.hpp>
#include <boost/geometry/geometries/linestring.hpp>
#include <boost/geometry/srs/spheroid.hpp>

int main() {
    namespace bg = boost::geometry;

    // 使用地理坐标系(经纬度)
    using point = bg::model::point<double, 2, bg::cs::geographic<bg::degree>>;
    using linestring = bg::model::linestring<point>;

    linestring ls;
    bg::read_wkt("LINESTRING(0 0, 1 1)", ls); // 从赤道到东北方向

    linestring densified;
    double max_distance_meters = 50000.0; // 每段不超过 50 公里

    // 创建 WGS84 椭球体
    bg::srs::spheroid<double> spheroid(6378137.0, 6356752.3142451793);
    bg::strategy::densify::geographic<> strategy(spheroid);

    // 使用地理策略进行加密(按真实地球距离)
    bg::densify(ls, densified, max_distance_meters, strategy);

    std::cout << "Original:   " << bg::wkt(ls) << std::endl;
    std::cout << "Densified:  " << bg::wkt(densified) << std::endl;

    return 0;
}

输出

复制代码
Original:   LINESTRING(0 0,1 1)
Densified:  LINESTRING(0 0,0.249972 0.250011,0.499954 0.500017,0.749954 0.750013,1 1)

3. difference ------ 计算两个几何体的空间差集

功能概述

difference(g1, g2) 返回几何体 g1g2集合差 ,即属于 g1 但不属于 g2 的部分。这是 GIS 中最基础的空间分析操作之一,广泛应用于区域裁剪、障碍物排除等场景。

该函数严格实现 OGC 的 Difference 操作。

接口形式

基础版本

cpp 复制代码
template <typename Geometry1, typename Geometry2, typename Collection>
void difference(Geometry1 const& g1, Geometry2 const& g2, Collection& output);

支持的几何类型组合

  • 面状几何(如 Polygon、MultiPolygon):支持所有面-面组合,结果为 MultiPolygon。
  • 线状与面状:线减面,结果为被面裁剪后的线段集合(MultiLineString)。
  • 点状几何:点减点,结果为未被覆盖的点集合(MultiPoint)。
  • 不支持:三维几何、球面几何。

重要提示 :输入的多边形必须满足 Boost.Geometry 的 Polygon Concept,例如外环顺时针、内环逆时针、无自相交等。

示例版本

cpp 复制代码
#include <iostream>
#include <vector>
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/geometries/polygon.hpp>

int main() {
    namespace bg = boost::geometry;
    using point = bg::model::d2::point_xy<double>;
    using polygon = bg::model::polygon<point>;

    polygon green, blue;
    bg::read_wkt("POLYGON((2 1.3,2.4 1.7,2.8 1.8,3.4 1.2,3.7 1.6,3.4 2,4.1 3,5.3 2.6,5.4 1.2,4.9 0.8,2.9 0.7,2 1.3))", green);
    bg::read_wkt("POLYGON((4.0 -0.5,3.5 1.0,2.0 1.5,3.5 2.0,4.0 3.5,4.5 2.0,6.0 1.5,4.5 1.0,4.0 -0.5))", blue);

    std::vector<polygon> output;
    bg::difference(green, blue, output);

    std::cout << "green - blue has " << output.size() << " parts." << std::endl;
    for (size_t i = 0; i < output.size(); ++i) {
        std::cout << "Part " << i << " area: " << bg::area(output[i]) << std::endl;
    }

    return 0;
}

输出

复制代码
green - blue has 5 parts.
Part 0 area: 0.02375
Part 1 area: 0.542951
Part 2 area: 0.0149697
Part 3 area: 0.226855
Part 4 area: 0.861646

4. discrete_frechet_distance ------ 离散 Fréchet 距离

功能概述

Fréchet 距离是一种衡量两条曲线相似性的经典方法,形象地比喻为"一个人牵着狗沿两条路径行走,彼此通过 leash(绳子)连接,求 leash 最短长度"。离散 Fréchet 距离 是其简化版本,仅考虑曲线上的顶点序列。

该算法当前仅支持 LineString 与 LineString 之间的计算

接口形式

  • 基础版本

    cpp 复制代码
    template <typename Geometry1, typename Geometry2>
    auto discrete_frechet_distance(Geometry1 const& g1, Geometry2 const& g2);
  • 带策略版本

    cpp 复制代码
    template <typename Geometry1, typename Geometry2, typename Strategy>
    auto discrete_frechet_distance(Geometry1 const& g1, Geometry2 const& g2, Strategy const& strategy);

可用策略

  • pythagoras:适用于笛卡尔坐标系(欧氏距离);
  • haversine:适用于球面坐标(大圆距离);
  • geographic:适用于 WGS84 等椭球模型(更精确的地球表面距离)。

特点:比 Hausdorff 距离更能反映曲线的整体走向相似性,对局部噪声相对鲁棒。

示例代码-无策略版本

cpp 复制代码
#include <iostream>
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/geometries/linestring.hpp>

int main() {
    namespace bg = boost::geometry;
    using point = bg::model::d2::point_xy<double>;
    using linestring = bg::model::linestring<point>;

    linestring ls1, ls2;
    bg::read_wkt("LINESTRING(0 0,1 1,1 2,2 1,2 2)", ls1);
    bg::read_wkt("LINESTRING(1 0,0 1,1 1,2 1,3 1)", ls2);

    double dist = bg::discrete_frechet_distance(ls1, ls2);
    std::cout << "Discrete Frechet Distance (Cartesian): " << dist << std::endl;

    return 0;
}

输出

复制代码
Discrete Frechet Distance (Cartesian): 1.41421

示例代码-带策略版本

cpp 复制代码
#include <iostream>
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point.hpp>
#include <boost/geometry/geometries/linestring.hpp>
#include <boost/geometry/srs/spheroid.hpp>

int main() {
    namespace bg = boost::geometry;

    using point = bg::model::point<double, 2, bg::cs::geographic<bg::degree>>;
    using linestring = bg::model::linestring<point>;

    linestring ls1, ls2;
    bg::read_wkt("LINESTRING(0 0,1 1,1 2,2 1,2 2)", ls1);
    bg::read_wkt("LINESTRING(1 0,0 1,1 1,2 1,3 1)", ls2);

    bg::srs::spheroid<double> spheroid(6378137.0, 6356752.3142451793);
    bg::strategy::distance::geographic<> strategy(spheroid);

    double dist = bg::discrete_frechet_distance(ls1, ls2, strategy);
    std::cout << "Discrete Frechet Distance (Geographic, meters): " << dist << std::endl;

    return 0;
}

输出

复制代码
Discrete Frechet Distance (Geographic, meters): 156874

5. discrete_hausdorff_distance ------ 离散 Hausdorff 距离

功能概述

Hausdorff 距离衡量两个点集之间的"最大最小距离",定义为:
H ( A , B ) = max ⁡ ( sup ⁡ a ∈ A inf ⁡ b ∈ B d ( a , b ) , sup ⁡ b ∈ B inf ⁡ a ∈ A d ( a , b ) ) H(A, B) = \max\left( \sup_{a \in A} \inf_{b \in B} d(a,b),\ \sup_{b \in B} \inf_{a \in A} d(a,b) \right) H(A,B)=max(a∈Asupb∈Binfd(a,b), b∈Bsupa∈Ainfd(a,b))
离散版本 仅在几何体的顶点上计算。

该算法支持:

  • LineString ↔ LineString
  • MultiPoint ↔ MultiPoint
  • Point ↔ MultiPoint
  • MultiLineString ↔ MultiLineString

接口形式

discrete_frechet_distance 类似,提供基础版和带策略版,支持相同的距离策略(Pythagoras、Haversine、Geographic)。

特点:对异常点敏感(因取最大值),适合检测形状是否"完全包含"或存在显著偏离。

示例代码-无策略版本

cpp 复制代码
#include <iostream>
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/geometries/linestring.hpp>

int main() {
    namespace bg = boost::geometry;
    using point = bg::model::d2::point_xy<double>;
    using linestring = bg::model::linestring<point>;

    linestring ls1, ls2;
    bg::read_wkt("LINESTRING(0 0,1 1,1 2,2 1,2 2)", ls1);
    bg::read_wkt("LINESTRING(1 0,0 1,1 1,2 1,3 1)", ls2);

    double dist = bg::discrete_hausdorff_distance(ls1, ls2);
    std::cout << "Discrete Hausdorff Distance (Cartesian): " << dist << std::endl;

    return 0;
}

输出

复制代码
Discrete Hausdorff Distance (Cartesian): 1

示例代码-带策略版本

cpp 复制代码
#include <iostream>
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point.hpp>
#include <boost/geometry/geometries/linestring.hpp>
#include <boost/geometry/srs/spheroid.hpp>

int main() {
    namespace bg = boost::geometry;

    using point = bg::model::point<double, 2, bg::cs::geographic<bg::degree>>;
    using linestring = bg::model::linestring<point>;

    linestring ls1, ls2;
    bg::read_wkt("LINESTRING(0 0,1 1,1 2,2 1,2 2)", ls1);
    bg::read_wkt("LINESTRING(1 0,0 1,1 1,2 1,3 1)", ls2);

    bg::srs::spheroid<double> spheroid(6378137.0, 6356752.3142451793);
    bg::strategy::distance::geographic<> strategy(spheroid);

    double dist = bg::discrete_hausdorff_distance(ls1, ls2, strategy);
    std::cout << "Discrete Hausdorff Distance (Geographic, meters): " << dist << std::endl;

    return 0;
}

输出

复制代码
Discrete Hausdorff Distance (Geographic, meters): 110574

总结对比

算法 用途 是否 OGC 标准 支持策略 典型应用场景
crosses 判断交叉关系 ✅ 是 空间关系查询、拓扑校验
densify 线段加密 ❌ 否 数据预处理、高精度渲染
difference 几何差集 ✅ 是 区域裁剪、障碍分析
discrete_frechet_distance 曲线相似性(路径匹配) ❌ 否 轨迹相似度、手势识别
discrete_hausdorff_distance 点集最大偏差 ❌ 否 形状匹配、质量控制

Boost.Geometry 通过统一的接口设计和策略模式,使得开发者既能快速上手基础功能,又能灵活适配复杂地理场景。理解这些核心算法的语义与适用范围,是构建高性能空间应用的关键一步。

相关推荐
YuTaoShao1 小时前
【LeetCode 每日一题】3713. 最长的平衡子串 I ——(解法二)暴力枚举 + 优化
算法·leetcode·职场和发展
Mr YiRan1 小时前
C++语言学习之面向对象
java·c++·学习
rgb2gray2 小时前
优多元分层地理探测器模型(OMGD)研究
人工智能·算法·机器学习·回归·gwr
码农三叔2 小时前
《卷2:人形机器人的环境感知与多模态融合》
人工智能·嵌入式硬件·算法·机器人·人形机器人
福大大架构师每日一题2 小时前
2026-01-15:下一个特殊回文数。用go语言,给定一个整数 n,求出一个比 n 更大的最小整数,该整数需要满足两条规则: 1. 它的十进制表示从左到右与从右到左完全一致(即读起来是对称的)。 2
python·算法·golang
努力进修2 小时前
算法刷题无边界!Hello-Algo+cpolar 随时随地想学就学
算法·cpolar
寻寻觅觅☆3 小时前
东华OJ-基础题-127-我素故我在(C++)
开发语言·c++·算法
ab1515173 小时前
2.13完成101、102、89
开发语言·c++·算法
HAPPY酷3 小时前
C++中类常见的函数分类
java·开发语言·c++