Python计算非均匀顶点密度多边形的面积与重心方法
核心问题说明
直接对边界点集取坐标平均计算重心,本质是计算离散点的质心,结果天然会向点密度更高的区域偏移,和你需要的城镇多边形面状重心不是同一个概念,完全不需要通过均衡点密度的方式绕路解决。
你之前写的重采样代码存在两个明确bug,才会出现局部点密度过高、部分区域留空白的问题:
- 遍历原始边界点时步长设为
j = j+2,直接跳过了一半相邻顶点构成的边界段,自然会出现点位空白 - 重采样完成后又将原始x、y坐标列表整体追加到新点集中,相当于把原始所有点重复加入了一次,原本点密集的区域密度会进一步升高
- 逐字符拆分读取坐标的逻辑鲁棒性极差,很容易因为空格、换行的格式问题出现坐标解析错误。
最优方案:直接使用多边形面状重心标准公式
计算几何中已有成熟的闭合多边形重心计算公式,只需要按轮廓顺序排列的多边形顶点即可计算,完全不依赖边界点密度,结果仅由多边形边的位置决定,不需要单独构建边数据库,计算复杂度为O(n),精度远高于点重采样后平均的方案。
公式原理
将多边形拆分为若干以坐标原点为公共顶点的三角形,分别计算每个三角形的面积与重心,再按面积加权平均即可得到整个多边形的重心,公式如下:
对于顶点按顺序排列的闭合多边形$(x_0,y_0), (x_1,y_1), ..., (x_{n-1},y_{n-1}), (x_0,y_0)$:
- 面积修正项 $A_i = x_i y_{i+1} - x_{i+1} y_i$
- 多边形总面积 $A = 0.5 * \sum_{i=0}^{n-1} A_i$
- 重心坐标 $C_x = \frac{1}{6A}\sum_{i=0}^{n-1} (x_i + x_{i+1})A_i$,$C_y = \frac{1}{6A}\sum_{i=0}^{n-1} (y_i + y_{i+1})A_i$
代码实现
纯numpy实现版本,无额外依赖:
import numpy as np def polygon_centroid(vertices): """ 参数vertices: N行2列的numpy数组,每行为按轮廓顺序排列的多边形顶点(x,y)坐标,需闭合 返回: (重心x坐标, 重心y坐标, 多边形面积) """ x = vertices[:, 0] y = vertices[:, 1] area_term = x[:-1] * y[1:] - x[1:] * y[:-1] area = 0.5 * np.sum(area_term) cx = np.sum((x[:-1] + x[1:]) * area_term) / (6 * area) cy = np.sum((y[:-1] + y[1:]) * area_term) / (6 * area) return cx, cy, abs(area) # 读取边界点示例 points = [] with open("borderpoints.txt", "r", encoding="utf8") as f: for line in f: line = line.strip() if not line: continue x_val, y_val = map(float, line.split()) points.append((x_val, y_val)) # 补全闭合点 points.append(points[0]) vertices_arr = np.array(points) cx, cy, poly_area = polygon_centroid(vertices_arr) print(f"多边形重心坐标:x={cx:.6f}, y={cy:.6f}, 面积={poly_area:.2f}")
如果可以安装第三方空间计算库,直接用shapely的内置方法即可,鲁棒性更高,原生支持带内环(洞)的多边形:
from shapely.geometry import Polygon poly = Polygon(points) centroid = poly.centroid print(f"重心坐标:x={centroid.x:.6f}, y={centroid.y:.6f}")
注意事项
- 传入的顶点必须沿城镇轮廓按顺时针或逆时针连续排列,不能是乱序点,否则计算结果完全错误
- 如果城镇轮廓包含不属于城镇范围的内环(如内部水域、绿地),只需要在构造多边形时传入内环顶点列表即可,上述shapely方案直接支持,手动实现公式时只需对内环的面积项取负加权即可。
备选:边界点等间隔重采样方法
如果后续其他场景必须使用沿边界均匀分布的点集,修正原有代码的逻辑即可:
- 将遍历原始边界段的步长从
j = j+2改为j += 1,逐段处理所有相邻顶点构成的边,不要跳段 - 删除代码末尾
newxs.append(x)、newys.append(y)两行,不要重复追加原始点集 - 改为沿边界累计周长等间隔取点,避免每段单独插值导致的段首尾点重复
注意:即便完成了完美的等间隔点重采样,对点集取平均得到的也只是边界线的质心,不是多边形面的重心,和你需要的城镇轮廓重心结果会存在固定偏差,不建议用这种方案计算重心。
内容的提问来源于stack exchange,提问作者kazimierz___tetmajer
相关产品推荐
相关产品推荐

