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。
接口形式
-
基础版本:
cpptemplate <typename Geometry, typename Distance> void densify(Geometry const& input, Geometry& output, Distance const& max_distance);使用默认坐标系策略(通常是笛卡尔坐标系)。
-
带策略版本:
cpptemplate <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) 返回几何体 g1 与 g2 的集合差 ,即属于 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 之间的计算。
接口形式
-
基础版本:
cpptemplate <typename Geometry1, typename Geometry2> auto discrete_frechet_distance(Geometry1 const& g1, Geometry2 const& g2); -
带策略版本:
cpptemplate <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 通过统一的接口设计和策略模式,使得开发者既能快速上手基础功能,又能灵活适配复杂地理场景。理解这些核心算法的语义与适用范围,是构建高性能空间应用的关键一步。