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

时间:2014-06-28 14:49:04

标签: python coordinates polygon area

我有一组点,想知道是否有一个函数(为了方便和可能的速度)可以计算由一组点包围的区域。

例如:

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坐标

11 个答案:

答案 0 :(得分:60)

Shoelace formula的实施可以在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笔记本中完成的。

答案 1 :(得分:25)

您可以使用shoelace formula,例如

def PolygonArea(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

# examples
corners = [(2.0, 1.0), (4.0, 5.0), (7.0, 8.0)]

这仅适用于简单多边形


  • 如果您有带孔的多边形:计算外圈的面积并减去内圈的区域

  • 如果您有自相交的环:您必须将它们分解为简单的扇区

答案 2 :(得分:4)

通过对马赫迪回答的分析,我得出结论,大部分时间花费在np.roll()上。通过消除滚动的需要,并且仍然使用numpy,与Mahdi的41μs相比,我将执行时间降低到每循环4-5μs(相比之下,Mahdi的功能在我的机器上平均花费了37μs)。

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()

完成时间安排

答案 3 :(得分:2)

上面的代码中存在错误,因为它不会在每次迭代时采用绝对值。上面的代码将始终返回零。 (数学上,这是采用签名区域或楔形产品与实际区域之间的区别 http://en.wikipedia.org/wiki/Exterior_algebra。)这是一些替代代码。

def area(vertices):
    n = len(vertices) # of corners
    a = 0.0
    for i in range(n):
        j = (i + 1) % n
        a += abs(vertices[i][0] * vertices[j][1]-vertices[j][0] * vertices[i][1])
    result = a / 2.0
    return result

答案 4 :(得分:2)

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. 它在每个点积内产生正值和负值的混合,这将在很大程度上抵消。

坐标移动不会改变总面积,只会使计算在数值上更稳定。

答案 5 :(得分:2)

到这里有点晚了,但是您是否考虑过简单地使用sympy

一个简单的代码是:

from sympy import Polygon
a = Polygon((0, 0), (2, 0), (2, 2), (0, 2)).area
print(a)

答案 6 :(得分:1)

对于常规多边形,这更简单:

import math

def area_polygon(n, s):
    return 0.25 * n * s**2 / math.tan(math.pi/n)

因为公式是¼ns2 / tan(π/ n)。 给定边数,n和每边的长度,s

答案 7 :(得分:1)

基于

https://www.mathsisfun.com/geometry/area-irregular-polygons.html

def _area_(coords):
    t=0
    for count in range(len(coords)-1):
        y = coords[count+1][1] + coords[count][1]
        x = coords[count+1][0] - coords[count][0]
        z = y * x
        t += z
    return abs(t/2.0)

a=[(5.09,5.8), (1.68,4.9), (1.48,1.38), (4.76,0.1), (7.0,2.83), (5.09,5.8)]
print _area_(a)

诀窍是第一个坐标也应该是最后一个。

答案 8 :(得分:1)

使用Parent比计算自己的速度更快。

Child

使用这些代码,然后执行shapely.geometry.Polygon

from shapely.geometry import Polygon
import numpy as np
def PolyArea(x,y):
    return 0.5*np.abs(np.dot(x,np.roll(y,1))-np.dot(y,np.roll(x,1)))
coords = np.random.rand(6, 2)
x, y = coords[:, 0], coords[:, 1]

答案 9 :(得分:0)

我将这里提供的每个解决方案与Shapely的面积法结果进行了比较,它们具有正确的整数部分,但十进制数有所不同。只有@Trenton的解决方案可以提供正确的结果。

现在改进@Trenton将处理坐标作为元组列表的答案,我想到了以下内容:

julia> using PyCall
ImportError: No module named site

答案 10 :(得分:0)

OpenCV中的

cv2.contourArea()提供了一种替代方法。

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

,其中的参数(点)是一个numpy数组,其dtype为int,表示多边形的顶点:[[x1,y1],[x2,y2],...]

相关问题