简体   繁体   English

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

[英]Calculate area of polygon given (x,y) coordinates

I have a set of points and would like to know if there is a function (for the sake of convenience and probably speed) that can calculate the area enclosed by a set of points.我有一组点,想知道是否有一个函数(为了方便和可能的速度)可以计算一组点所包围的面积。

for example:例如:

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

points = zip(x,y)

given points the area should be approximately equal to (pi-2)/4 .points的面积应该大约等于(pi-2)/4 Maybe there is something from scipy, matplotlib, numpy, shapely, etc. to do this?也许有来自 scipy、matplotlib、numpy、shapely 等的东西来做到这一点? I won't be encountering any negative values for either the x or y coordinates... and they will be polygons without any defined function.我不会遇到 x 或 y 坐标的任何负值......它们将是没有任何定义函数的多边形。

EDIT:编辑:

points will most likely not be in any specified order (clockwise or counterclockwise) and may be quite complex as they are a set of utm coordinates from a shapefile under a set of boundaries点很可能不会按任何指定的顺序(顺时针或逆时针)并且可能非常复杂,因为它们是一组边界下 shapefile 中的一组 utm 坐标

Implementation of Shoelace formula could be done in Numpy . Shoelace 公式的实现可以在Numpy中完成。 Assuming these vertices:假设这些顶点:

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

We can redefine the function in numpy to find the area:我们可以在numpy中重新定义函数来查找面积:

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

And getting results:并得到结果:

print PolyArea(x,y)
# 0.26353377782163534

Avoiding for loop makes this function ~50X faster than PolygonArea :避免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.

Timing is done in Jupyter notebook.计时在 Jupyter notebook 中完成。

The most optimized solution that covers all possible cases, would be to use a geometry package, like shapely , scikit-geometry or pygeos .涵盖所有可能情况的最优化解决方案是使用几何包,如shapelyscikit-geometrypygeos All of them use C++ geometry packages under the hood.它们都在底层使用 C++ 几何包。 The first one is easy to install via pip:第一个很容易通过 pip 安装:

pip install shapely

and simple to use:使用简单:

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

print(pgon.area)

To build it from scratch or understand how the underlying algorithm works, check the shoelace formula :要从头开始构建它或了解底层算法的工作原理,请查看鞋带公式

# 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

Since this works for simple polygons:因为这适用于简单的多边形:

  • If you have a polygon with holes : Calculate the area of the outer ring and subtrack the areas of the inner rings如果您有一个带孔的多边形:计算外环的面积并子跟踪内环的面积

  • If you have self-intersecting rings : You have to decompose them into simple sectors如果您有自相交环:您必须将它们分解为简单的扇区

By analysis of Mahdi's answer, I concluded that the majority of time was spent doing np.roll() .通过分析 Mahdi 的回答,我得出结论,大部分时间都花在了np.roll()上。 By removing the need of the roll, and still using numpy, I got the execution time down to 4-5µs per loop compared to Mahdi's 41µs (for comparison Mahdi's function took an average of 37µs on my machine).通过消除滚动的需要,并且仍然使用 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)

By calculating the correctional term, and then slicing the arrays, there is no need to roll or create a new array.通过计算校正项,然后对数组进行切片,无需滚动或创建新数组。

Benchmarks:基准:

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

Timing was done using the time module and time.clock()使用time模块和time.clock()完成计时

maxb's answer gives good performance but can easily lead to loss of precision when coordinate values or the number of points are large. maxb 的答案提供了良好的性能,但当坐标值或点数很大时,很容易导致精度损失。 This can be mitigated with a simple coordinate shift:这可以通过简单的坐标偏移来缓解:

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)

For example, a common geographic reference system is UTM, which might have (x,y) coordinates of (488685.984, 7133035.984) .例如,常见的地理参考系统是 UTM,其 (x,y) 坐标可能为(488685.984, 7133035.984) The product of those two values is 3485814708748.448 .这两个值的乘积是3485814708748.448 You can see that this single product is already at the edge of precision (it has the same number of decimal places as the inputs).您可以看到这个单一的产品已经处于精度的边缘(它与输入的小数位数相同)。 Adding just a few of these products, let alone thousands, will result in loss of precision.仅添加其中的几个,更不用说数千个,将导致精度损失。

A simple way to mitigate this is to shift the polygon from large positive coordinates to something closer to (0,0), for example by subtracting the centroid as in the code above.缓解这种情况的一种简单方法是将多边形从大的正坐标移动到更接近 (0,0) 的位置,例如通过减去上面代码中的质心。 This helps in two ways:这有两个方面的帮助:

  1. It eliminates a factor of x.mean() * y.mean() from each product它从每个产品中消除了x.mean() * y.mean()的一个因子
  2. It produces a mix of positive and negative values within each dot product, which will largely cancel.它在每个点积中产生正负值的混合,这将在很大程度上抵消。

The coordinate shift does not alter the total area, it just makes the calculation more numerically stable.坐标偏移不会改变总面积,它只是使计算在数值上更加稳定。

cv2.contourArea() in OpenCV gives an alternative method. OpenCV 中的 cv2.contourArea() 提供了另一种方法。

example:例子:

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

The argument (points, in the above example) is a numpy array with dtype int, representing the vertices of a polygon: [[x1,y1],[x2,y2], ...]参数(点,在上面的例子中)是一个 dtype int 的 numpy 数组,表示多边形的顶点:[[x1,y1],[x2,y2], ...]

There's an error in the code above as it doesn't take absolute values on each iteration.上面的代码中有一个错误,因为它在每次迭代中都没有采用绝对值。 The above code will always return zero.上面的代码将始终返回零。 (Mathematically, it's the difference between taking signed area or wedge product and the actual area http://en.wikipedia.org/wiki/Exterior_algebra .) Here's some alternate code. (从数学上讲,这是带符号区域或楔形乘积与实际区域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

a bit late here, but have you considered simply using sympy ?这里有点晚了,但是您是否考虑过简单地使用sympy

a simple code is :一个简单的代码是:

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

It's faster to use shapely.geometry.Polygon rather than to calculate yourself.使用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]

With those codes, and do %timeit有了这些代码,然后做%timeit

%timeit PolyArea(x,y)
46.4 µs ± 2.24 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
%timeit Polygon(coords).area
20.2 µs ± 414 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

I compared every solutions offered here to Shapely's area method result, they had the right integer part but the decimal numbers differed.我将这里提供的每个解决方案都与 Shapely 的面积法结果进行了比较,它们具有正确的整数部分,但小数部分不同。 Only @Trenton's solution provided the the correct result.只有@Trenton 的解决方案提供了正确的结果。

Now improving on @Trenton's answer to process coordinates as a list of tuples, I came up with the following:现在改进@Trenton 将坐标处理为元组列表的答案,我想出了以下内容:

import numpy as np

def polygon_area(coords):
    # get x and y in vectors
    x = [point[0] for point in coords]
    y = [point[1] for point in coords]
    # shift coordinates
    x_ = x - np.mean(x)
    y_ = y - np.mean(y)
    # calculate area
    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)

#### Example output
coords = [(385495.19520441635, 6466826.196947694), (385496.1951836388, 6466826.196947694), (385496.1951836388, 6466825.196929455), (385495.19520441635, 6466825.196929455), (385495.19520441635, 6466826.196947694)]

Shapely's area method:  0.9999974610685296
@Trenton's area method: 0.9999974610685296

This is much simpler, for regular polygons:对于正多边形,这要​​简单得多:

import math

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

since the formula is ¼ n s2 / tan(π/n).因为公式是 ¼ n s2 / tan(π/n)。 Given the number of sides, n, and the length of each side, s给定边数 n 和每边的长度 s

Based on基于

https://www.mathsisfun.com/geometry/area-irregular-polygons.html 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)

The trick is that the first coordinate should also be last.诀窍是第一个坐标也应该是最后一个。

def find_int_coordinates(n: int, coords: list[list[int]]) -> float:
    rez = 0
    x, y = coords[n - 1]
    for coord in coords:
        rez += (x + coord[0]) * (y - coord[1])
        x, y = coord
    return abs(rez / 2)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM