Boost.Geometry 中多边形面积计算使用


1、Boost.Geometry 的核心概念

Boost.Geometry(又称 Generic Geometry Library, GGL)支持多种几何类型:

  • Point(点)
  • Linestring(折线)
  • Polygon(多边形,支持外环 + 内环/孔洞)
  • MultiPolygon

面积计算统一通过:

boost::geometry::area(geometry)

2、Polygon 的基本结构

2.1 多边形类型定义的设计含义

using Point   = bg::model::d2::point_xy<double>;
using Polygon = bg::model::polygon<Point>;

这两行代码背后隐含了 Boost.Geometry 的三层抽象设计


① Point:坐标 + 坐标系

bg::model::d2::point_xy<double>

等价于:

p=(x,y)∈R2 \mathbf{p} = (x, y) \in \mathbb{R}^2 p=(x,y)R2

并且显式声明:

  • 维度:2D
  • 坐标系:Cartesian
  • 存储类型:double

重要:坐标系信息会直接影响:

  • 面积计算方式
  • 使用的 strategy(Cartesian vs Geographic)

② Polygon:拓扑结构,而非点集

bg::model::polygon<Point>

在 Boost.Geometry 中:

Polygon 是一个 拓扑对象(Topological Object),而不是简单的顶点集合。

其数学定义为:

P=(R0,R1,…,Rk) \mathcal{P} = \left( \mathcal{R}_0, {\mathcal{R}_1, \dots, \mathcal{R}_k} \right) P=(R0,R1,,Rk)
其中:

  • R0\mathcal{R}_0R0:外环(outer ring)
  • Ri\mathcal{R}_iRi:内环(inner ring / hole)

2.2 Polygon 的组成结构(数据层面)

Polygon
 ├── outer()     // 外边界(必须,闭合)
 └── inners()    // 内环(孔洞,可选)

2.2.1 Outer Ring(外环)

外环满足以下几何 + 拓扑约束

  1. 简单闭合曲线

    p[0] == p[n-1]
    
  2. 无自交

  3. 默认方向:逆时针(CCW)

在 Cartesian 坐标系下:

  • CCW ⇒ 正面积
  • CW ⇒ 负面积

数学上:

Area(R0)>0 \mathrm{Area}(\mathcal{R}_0) > 0 Area(R0)>0


2.2.2 Inner Rings(内环 / 孔洞)

每个 inner ring:

  • 代表一个“被挖空”的区域
  • 必须 完全包含在外环内部
  • 方向与外环相反(CW)

因此:

Area(Ri)<0 \mathrm{Area}(\mathcal{R}_i) < 0 Area(Ri)<0

Boost.Geometry 在计算面积时直接做代数求和:

Area(P)Area(R0)+∑iArea(Ri) \mathrm{Area}(\mathcal{P}) \mathrm{Area}(\mathcal{R}_0) + \sum_i \mathrm{Area}(\mathcal{R}_i) Area(P)Area(R0)+iArea(Ri)

不需要手动“减去孔洞面积”


2.3 为什么方向(Orientation)是“结构的一部分”?

在 Boost.Geometry 中:

Ring 的方向 ≠ 视觉约定,而是数学定义的一部分

原因是:

  • 面积计算基于 有符号 Shoelace Formula
  • 方向决定符号

Shoelace 公式:

A=12∑i=0n−1(xiyi+1−xi+1yi) A = \frac{1}{2} \sum_{i=0}^{n-1} (x_i y_{i+1} - x_{i+1} y_i) A=21i=0n1(xiyi+1xi+1yi)

方向面积符号
CCW
CW

因此:

  • 外环 CCW ⇒ 正贡献
  • 内环 CW ⇒ 负贡献(孔洞)

2.4 自动修正机制:bg::correct

Boost.Geometry 提供了 结构规范化工具

bg::correct(polygon);

该函数会:

  • 自动闭合所有 ring
  • 统一外环 / 内环方向
  • 修正轻微的不规范输入

最佳实践

所有 Polygon 在参与 area()union_intersection 之前,都应调用 correct()


2.5 常见错误与真实工程后果

错误 1:只存点,不存方向

poly.outer() = {{0,0}, {1,0}, {1,1}, {0,1}};
  • 未闭合
  • 方向未知

面积不可靠

正确:

bg::correct(poly);

错误 2:孔洞方向错误

poly.inners()[0] = {{1,1},{2,1},{2,2},{1,2},{1,1}}; // CCW

面积会 增加而不是减少


2.6 与 SLAM / 地图构建的关系(应用视角)

  • 占据栅格 → 边界提取 → Polygon
  • 局部地图裁剪 / Mask
  • 车辆 footprint + union → 覆盖面积

这些应用都高度依赖 Polygon 拓扑正确性


2.7 小结

在 Boost.Geometry 中,Polygon 是“有方向的拓扑结构”,而不是简单点序列。

理解这一点,才能正确使用:

  • area
  • union_
  • difference
  • intersection

3、多边形面积计算

3.1 基本用法背后的完整逻辑

double area = bg::area(poly);

这一行代码表面上很简单,但内部隐含了 多层判定与算法分发

Polygon
→ 坐标系(Cartesian)
→ 几何类型(Polygon)
→ strategy::area::cartesian
→ ring-based shoelace accumulation

3.2 示例代码逐行深度解析

Polygon poly;

// 外环
poly.outer().push_back({0, 0});
poly.outer().push_back({4, 0});
poly.outer().push_back({4, 3});
poly.outer().push_back({0, 3});
poly.outer().push_back({0, 0}); // 必须闭合

几何解释

该多边形是一个 轴对齐矩形

  • 左下角:(0,0)(0,0)(0,0)
  • 右上角:(4,3)(4,3)(4,3)
  • 宽度:4
  • 高度:3

拓扑性质

  • 顶点数(含闭合点):5
  • 简单多边形(无自交)
  • 顶点顺序:逆时针(CCW)

3.3 Boost.Geometry 中面积的数学本质

3.3.1 Shoelace(高斯面积)公式

Boost.Geometry 在 Cartesian 2D 下采用 有符号面积公式

A12∑i=0n−1(xiyi+1−xi+1yi) A \frac{1}{2} \sum_{i=0}^{n-1} \left( x_i y_{i+1} - x_{i+1} y_i \right) A21i=0n1(xiyi+1xi+1yi)

  • nnn 为 ring 顶点数(不含重复的最后一个点)
  • 顺序决定符号

3.3.2 将示例代入公式

顶点序列:

(0,0),(4,0),(4,3),(0,3),(0,0) (0,0), (4,0), (4,3), (0,3), (0,0) (0,0),(4,0),(4,3),(0,3),(0,0)

计算:

A=12[(0⋅0+4⋅3+4⋅3+0⋅0) −(0⋅4+0⋅4+3⋅0+3⋅0)] =12 \begin{aligned} A &= \frac{1}{2}[ (0\cdot0 + 4\cdot3 + 4\cdot3 + 0\cdot0) \ &\quad - (0\cdot4 + 0\cdot4 + 3\cdot0 + 3\cdot0) ] \ &= 12 \end{aligned} A=21[(00+43+43+00) (04+04+30+30)] =12

因此:

area = 12.0

3.4 为什么 area() 返回的是“有符号值”?

3.4.1 数学原因

Boost.Geometry 不人为取绝对值,而是保留:

orientation information \text{orientation information} orientation information

这在以下操作中非常关键:

  • 多边形布尔运算(union / difference)
  • 孔洞判定
  • winding number 判断

3.4.2 工程后果

顶点顺序bg::area(poly)
CCW+12
CW-12

若你只关心“物理面积”:

double area = std::abs(bg::area(poly));

3.5 bg::correct() 在面积计算中的关键作用

问题:如果用户没闭合?

poly.outer() = {
    {0,0},{4,0},{4,3},{0,3}
};

行为 未定义或错误

正确姿势:

bg::correct(poly);
double area = bg::area(poly);

correct() 会:

  • 自动闭合 ring
  • 修正方向(outer CCW,inner CW)
  • 修复轻微输入不规范

3.6 含孔洞的面积计算机制

若存在 inner rings:

AreaArea∗outer+∑iArea∗inner,i \mathrm{Area} \mathrm{Area}*{outer} + \sum_i \mathrm{Area}*{inner,i} AreaAreaouter+iAreainner,i

由于 inner 为 CW:

Areainner,i<0 \mathrm{Area}_{inner,i} < 0 Areainner,i<0

孔洞自然被扣除


3.7 面积计算前的有效性检查

std::string reason;
if (!bg::is_valid(poly, reason)) {
    throw std::runtime_error(reason);
}

检查内容包括:

  • 是否闭合
  • 是否自交
  • 孔洞是否在外环内部
  • 方向是否可修复

3.8 常见工程陷阱总结

自交多边形

  • 蝴蝶形、多叉边
  • area() 结果不可依赖

经纬度坐标直接算面积

bg::model::point<double,2,bg::cs::geographic<bg::degree>>

必须指定 geographic strategy


3.9 工程级推荐模板

bg::correct(poly);

std::string msg;
if (!bg::is_valid(poly, msg)) {
    throw std::runtime_error(msg);
}

double area = std::abs(bg::area(poly));

4、环方向(Orientation)与面积符号

Boost.Geometry 使用有符号面积

顶点顺序area(poly)
CCW正值
CW负值

4.1 自动修正方向

bg::correct(poly);
  • 自动闭合
  • 自动调整外环/内环方向

工程中强烈建议在 area 之前调用


5、含孔洞(Inner Ring)的多边形面积示例

// 外环
poly.outer() = {
    {0,0}, {5,0}, {5,5}, {0,5}, {0,0}
};

// 内环(孔)
poly.inners().resize(1);
poly.inners()[0] = {
    {1,1}, {1,2}, {2,2}, {2,1}, {1,1}
};

bg::correct(poly);
double area = bg::area(poly);

计算结果:

area = 25 - 1 = 24

Boost.Geometry 会自动:

  • 计算外环面积
  • 减去所有内环面积

6、多多边形(MultiPolygon)面积

using MultiPolygon = bg::model::multi_polygon<Polygon>;

MultiPolygon mp;
mp.push_back(poly1);
mp.push_back(poly2);

double area = bg::area(mp);

等价于:

area = area(poly1) + area(poly2)

7、使用的数学原理(Shoelace Formula)

Boost.Geometry 的多边形面积基于 高斯面积公式(鞋带公式)

A=12∣∑i=0n−1(xiyi+1−xi+1yi)∣ A = \frac{1}{2} \left| \sum_{i=0}^{n-1} (x_i y_{i+1} - x_{i+1} y_i) \right| A=21i=0n1(xiyi+1xi+1yi)

  • O(n) 时间复杂度
  • 对非自交简单多边形严格成立

自交多边形 → 行为未定义(需 bg::is_valid() 检查)


8、面积计算前的有效性检查

bool valid = bg::is_valid(poly);

或者输出错误原因:

std::string msg;
bool valid = bg::is_valid(poly, msg);

9、常见坑总结

问题后果
多边形未闭合面积错误
环方向错误面积为负
自交未定义行为
经纬度坐标不能直接用 area(需 geographic strategy)

10、地理坐标(WGS84)下的面积计算

10.1 为什么“经纬度”不能直接用 Cartesian 面积?

在地理坐标系中:

bg::cs::geographic<bg::degree>

点的含义是:

[
\mathbf{p} = (\lambda, \varphi)
]

其中:

  • λ\lambdaλ:经度(longitude)
  • φ\varphiφ:纬度(latitude)
  • 单位:角度(degree)

不是欧氏空间坐标

  • 经度线不是平行线
  • 纬度方向的实际距离随纬度变化
  • 地球是椭球体(WGS84),不是平面

Shoelace 公式在这里是错误的


10.2 Boost.Geometry 的解决方案:Strategy 分离

Boost.Geometry 的核心设计之一是:

几何类型 × 坐标系 × Strategy → 算法

对于 geographic 坐标:

bg::strategy::area::geographic<>

Boost.Geometry 自动切换到:

  • 椭球测地线积分面积算法
  • 基于 WGS84 椭球参数

10.3 geographic<> strategy 的数学原理

10.3.1 地球模型(WGS84)

Boost.Geometry 默认使用:

  • 长半轴:
    a=6378137.0;m a = 6378137.0;\text{m} a=6378137.0;m
  • 扁率:
    f=1298.257223563 f = \frac{1}{298.257223563} f=298.2572235631

这是标准 WGS84 椭球模型


10.3.2 面积计算的本质

地理多边形面积定义为:

A=∬ΩdS A = \iint_{\Omega} \mathrm{d}S A=ΩdS

其中:

  • Ω\OmegaΩ:地表多边形区域
  • dS\mathrm{d}SdS:椭球表面面积元

Boost.Geometry 内部采用:

  • 测地线(Geodesic)边界
  • 椭球面面积积分
  • 数值稳定的累积算法(Karney / GeographicLib 思想)

10.4 示例:经纬度多边形面积

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

bg::model::polygon<GeoPoint> poly;

poly.outer() = {
    {116.0, 39.0},
    {116.1, 39.0},
    {116.1, 39.1},
    {116.0, 39.1},
    {116.0, 39.0}
};

bg::correct(poly);

double area = bg::area(
    poly,
    bg::strategy::area::geographic<>()
);

返回值单位

平方米(m²)

不是“平方度”


10.5 为什么必须显式指定 Strategy?

bg::area(poly);  //  潜在错误

Boost.Geometry 无法自动判断:

  • 你是否希望:

    • 平面近似?
    • 椭球精确?
  • geographic 坐标 没有默认 area 策略

显式指定是强制设计选择


10.6 常见误区(非常重要)

把经纬度当平面坐标

bg::model::point_xy<double>  //  错误

面积误差可达 数十甚至上百倍


忽略单位

  • 输入:degree
  • 输出:

很多人误以为结果仍是“角度平方”


10.7 地理坐标下的方向(Orientation)问题

即使在 geographic 坐标下:

  • Polygon 仍有 方向
  • 外环仍应为 CCW(从地表法向看)

但:

  • geographic strategy 内部会自动处理方向
  • 推荐仍调用:
bg::correct(poly);

10.8 何时应使用 geographic 面积?

场景是否使用
城市级地图
国家/洲级
SLAM 局部地图(<1km)❌(投影即可)
室内

10.9 与投影坐标的对比

替代方案:先投影再算面积

WGS84 → UTM → Cartesian → area

优点:

  • 速度快
  • 算法简单

缺点:

  • 投影误差
  • 跨 UTM zone 复杂

10.10 工程级推荐写法

bg::correct(poly);

std::string reason;
if (!bg::is_valid(poly, reason)) {
    throw std::runtime_error(reason);
}

double area = bg::area(
    poly,
    bg::strategy::area::geographic<>()
);

本节一句话总结

经纬度坐标下的面积计算,本质是“椭球曲面上的积分问题”,必须使用 geographic strategy。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

点云SLAM

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值