给定(x,y)坐标计算多边形面积

88 投票
13 回答
143148 浏览
提问于 2025-04-18 11:25

我有一组点,想知道有没有什么函数可以计算这些点围成的区域面积,这样做可能更方便,也可能更快。

举个例子:

x = np.arange(0,1,0.001)
y = np.sqrt(1-x**2)

points = zip(x,y)

给定这些points,计算出来的面积应该大约等于(pi-2)/4。也许可以用scipy、matplotlib、numpy、shapely等库来实现这个功能?我的x和y坐标不会有负值,而且这些点会形成多边形,但没有特定的函数来描述它们。

补充说明:

这些点很可能不会按照特定的顺序排列(顺时针或逆时针),而且可能会比较复杂,因为它们是一组来自某个边界的shapefile的utm坐标。

13 个回答

6

在OpenCV中,cv2.contourArea()提供了一种计算面积的替代方法。

举个例子:

points = np.array([[0,0],[10,0],[10,10],[0,10]])
area = cv2.contourArea(points)
print(area) # 100.0

这里的参数(上面例子中的points)是一个numpy数组,数据类型是整数,表示一个多边形的顶点:[[x1,y1],[x2,y2], ...]

13

maxb的回答在性能上表现不错,但当坐标值或点的数量很大时,可能会导致精度损失。我们可以通过简单的坐标平移来解决这个问题:

def polygon_area(x,y):
    # coordinate shift
    x_ = x - x.mean()
    y_ = y - y.mean()
    # everything else is the same as maxb's code
    correction = x_[-1] * y_[0] - y_[-1]* x_[0]
    main_area = np.dot(x_[:-1], y_[1:]) - np.dot(y_[:-1], x_[1:])
    return 0.5*np.abs(main_area + correction)

举个例子,一个常见的地理参考系统是UTM,它的(x,y)坐标可能是(488685.984, 7133035.984)。这两个值相乘的结果是3485814708748.448。你可以看到,这个乘积已经接近精度的极限(它的小数位数和输入值一样多)。如果再加上几个这样的乘积,更别提成千上万的了,就会导致精度损失。

一个简单的解决办法是把多边形的坐标从很大的正值平移到更接近(0,0)的地方,比如通过减去中心点坐标来实现,就像上面的代码那样。这有两个好处:

  1. 它消除了每个乘积中的一个因素x.mean() * y.mean()
  2. 它在每个点积中产生了正值和负值的混合,这样大部分会相互抵消。

坐标平移并不会改变总面积,只是让计算在数值上更稳定。

21

通过分析Mahdi的回答,我发现大部分时间都花在了执行np.roll()这个操作上。通过去掉这个操作,同时仍然使用numpy,我把每次循环的执行时间降低到了4-5微秒,而Mahdi的时间是41微秒(为了比较,Mahdi的函数在我的机器上平均耗时37微秒)。

def polygon_area(x,y):
    correction = x[-1] * y[0] - y[-1]* x[0]
    main_area = np.dot(x[:-1], y[1:]) - np.dot(y[:-1], x[1:])
    return 0.5*np.abs(main_area + correction)

通过计算修正项,然后对数组进行切片,就不需要进行滚动操作或者创建新的数组了。

基准测试:

10000 iterations
PolyArea(x,y): 37.075µs per loop
polygon_area(x,y): 4.665µs per loop

计时是通过time模块和time.clock()来完成的。

63

最优化的解决方案是使用一些几何库,比如shapelyscikit-geometry或者pygeos。这些库的底层都是用C++写的几何包。第一个库通过pip安装非常简单:

pip install shapely

而且使用起来也很简单:

from shapely.geometry import Polygon
pgon = Polygon(zip(x, y)) # Assuming the OP's x,y coordinates

print(pgon.area)

如果你想从头开始构建或者了解底层算法是怎么工作的,可以看看鞋带公式

# e.g. corners = [(2.0, 1.0), (4.0, 5.0), (7.0, 8.0)]
def Area(corners):
    n = len(corners) # of corners
    area = 0.0
    for i in range(n):
        j = (i + 1) % n
        area += corners[i][0] * corners[j][1]
        area -= corners[j][0] * corners[i][1]
    area = abs(area) / 2.0
    return area

这个公式适用于简单的多边形:

  • 如果你有一个有孔的多边形:先计算外环的面积,再减去内环的面积。

  • 如果你有自交的环:你需要把它们分解成简单的扇形。

154

可以用 鞋带公式 来计算面积,使用 Numpy 库来实现。假设我们有这些顶点:

import numpy as np
x = np.arange(0,1,0.001)
y = np.sqrt(1-x**2)

我们可以在 Numpy 中重新定义一个函数来计算面积:

def PolyArea(x,y):
    return 0.5*np.abs(np.dot(x,np.roll(y,1))-np.dot(y,np.roll(x,1)))

然后我们就能得到结果:

print PolyArea(x,y)
# 0.26353377782163534

不使用 for 循环,这个函数的速度比 PolygonArea 快大约 50 倍:

%timeit PolyArea(x,y)
# 10000 loops, best of 3: 42 µs per loop
%timeit PolygonArea(zip(x,y))
# 100 loops, best of 3: 2.09 ms per loop.

时间测量是在 Jupyter notebook 中进行的。

撰写回答