如何计算2D多边形的面积?


81

假设2d空间中一系列不自相交的点,确定所得多边形面积的有效方法是什么?

附带说明,这不是功课,我也不在寻找代码。我正在寻找可以用来实现自己的方法的描述。我有从点列表中拉出一系列三角形的想法,但是我知道有很多关于凸多边形和凹多边形的边沿案例,我可能不会注意到。


6
术语“表面积”有点误导。您似乎想要的只是(常规)区域。在3D中,表面积是外表面的面积,因此此概念的自然2D概括将是多边形周长的长度,这显然不是您想要的。
batty 2010年

def area(polygon):返回abs(numpy.cross(polygon,numpy.roll(polygon,-1,0))。sum()/ 2)
iouvxz

Answers:


110

这是标准方法AFAIK。基本上求和每个顶点周围的叉积。比三角剖分简单得多。

Python代码,给出一个表示为(x,y)顶点坐标列表的多边形,从最后一个顶点到第一个顶点隐式环绕:

def area(p):
    return 0.5 * abs(sum(x0*y1 - x1*y0
                         for ((x0, y0), (x1, y1)) in segments(p)))

def segments(p):
    return zip(p, p[1:] + [p[0]])

大卫·莱哈维(David Lehavi)评论:值得一提的是,该算法为何起作用:这是格林定理对-y和x函数的一个应用;恰好是平面仪的工作方式。进一步来说:

上面的公式=
integral_over_perimeter(-y dx + x dy) =
integral_over_area((-(-dy)/dy+dx/dx) dy dx) =
2 Area


7
值得一提的是该算法为何起作用:它是格林定理对-y和x函数的一个应用;完全按照平面仪的工作方式 更具体地说:上面的公式= integer_permieter(-y dx + x dy)=积分_area((-(-dy)/ dy + dx / dx)dydyx = 2区域
David Lehavi 09年

6
帖子中的链接已消失。是否还有其他链接?
Yakov

1
对我来说,关于compgeom-discuss@research.bell-labs.com邮件列表的链接讨论不可用。我复制了Google缓存中的消息: gist.github.com/1200393
AndrewАндрейЛисточкин2011年

2
@ perfectionm1ng切换方向将翻转总和中abs()的符号,但去除符号。
Darius Bacon

3
局限性:这种方法将对自相交多边形产生错误的答案,即一侧与另一侧相交,如右图所示。但是,它将对三角形,规则和不规则多边形,凸面或凹面多边形正确运行。(mathopenref.com/coordpolygonarea.html
OneWorld的

14

叉积是经典。

如果您要进行大量此类计算,请尝试下面的优化版本,其乘法量减少一半:

area = 0;
for( i = 0; i < N; i += 2 )
   area += x[i+1]*(y[i+2]-y[i]) + y[i+1]*(x[i]-x[i+2]);
area /= 2;

为了清楚起见,我使用数组下标。使用指针更有效。虽然优秀的编译器会为您做这件事。

多边形被假定为“封闭”,这意味着您将第一个点复制为带有下标N的点。它还假定该多边形具有偶数个点。如果N不为偶数,请附加第一点的副本。

通过展开并组合经典叉积算法的两个连续迭代来获得该算法。

我不确定这两种算法在数值精度方面如何进行比较。我的印象是上述算法比经典算法更好,因为乘法往往会恢复减法精度的损失。与GPU一样,在限制使用浮点数时,这可以带来很大的不同。

编辑:“三角形和多边形2D和3D的面积”描述了一种甚至更有效的方法

// "close" polygon
x[N] = x[0];
x[N+1] = x[1];
y[N] = y[0];
y[N+1] = y[1];

// compute area
area = 0;
for( size_t i = 1; i <= N; ++i )
  area += x[i]*( y[i+1] - y[i-1] );
area /= 2;

1
我无法想象第二个代码片段会起作用。很明显,多边形在X轴上的距离越远,其面积就越大。
Cygon 2012年

1
这是上述算法的正确数学重排,节省了一些乘法。您是对的,但其他顶点定义的面积将相减。但这确实可能导致精度下降。
chmike 2012年

2
您忽略的是,由于y的减法,加法始终带有一些否定项。考虑任何2d多边形形状,并比较连续顶点的y值。您会看到一些减法将产生负值,而一些则将产生正值。
chmike 2012年

2
的确,最后一段是我无法确定的!当i <= N时,它起作用。感谢您的耐心配合,我将一切退还给您:)
Cygon 2012年

1
另外,算法返回的面积是“有符号的”(根据点的顺序为负或正),因此,如果您希望始终为正,则只需使用绝对值即可。
NightElfik 2014年

11

此页面显示公式

在此处输入图片说明

可以简化为:

在此处输入图片说明

如果您写出一些术语并根据的共同因素对其进行分组xi,则不难发现相等性。

最终求和的效率更高,因为它只需要n乘法而不是2n

def area(x, y):
    return abs(sum(x[i] * (y[i + 1] - y[i - 1]) for i in xrange(-1, len(x) - 1))) / 2.0

我从这里的乔·肯顿那里学到了这种简化。


如果您有NumPy,则此版本速度更快(适用​​于除非常小的数组之外的所有数组):

def area_np(x, y):        
    x = np.asanyarray(x)
    y = np.asanyarray(y)
    n = len(x)
    shift_up = np.arange(-n+1, 1)
    shift_down = np.arange(-1, n-1)    
    return (x * (y.take(shift_up) - y.take(shift_down))).sum() / 2.0

1
感谢您的NumPy版本。
physicsmichael


4

为了扩展三角形和求和三角形区域,如果碰巧具有凸多边形,或者碰巧选择了一个不会生成与该多边形相交的其他所有点的线的点,这些方法将起作用。

对于一般的非相交多边形,您需要对向量的叉积求和(参考点,点a),(参考点,点b),其中a和b彼此“相邻”。

假设您具有按顺序定义多边形的点的列表(点i和i + 1构成多边形的一条线):

总和(i = 1至n-1的叉积((点0,点i),(点0,点i + 1))

取该叉积的大小,便得到表面积。

这样就可以处理凹面多边形,而不必担心选择一个好的参考点。生成不在多边形内部的三角形的任何三个点将具有与多边形内部任何三角形相反的叉积,因此可以正确求和面积。


3

计算多边形的面积

http://community.topcoder.com/tc?module=Static&d1=tutorials&d2=geometry1#polygon_area

int cross(vct a,vct b,vct c)
{
    vct ab,bc;
    ab=b-a;
    bc=c-b;
    return ab.x*bc.y-ab.y*bc.x;
}    
double area(vct p[],int n)
{ 
    int ar=0;
    for(i=1;i+1<n;i++)
    {
        vct a=p[i]-p[0];
        vct b=p[i+1]-p[0];
        area+=cross(a,b);
    }
    return abs(area/2.0);
}    

这是一个已有3年历史的问题,在接受的答案上有34个投票。告诉我们您的答案比其他已发布的答案更好。
Mark Taylor

3
这是c而不是python中的示例。没有更好,但很高兴有它在不同的语言
underdoeg

2

或进行轮廓积分。斯托克斯定理允许您将面积积分表示为轮廓积分。有点高斯正交,鲍勃是你的叔叔。


2

独立于语言的解决方案:

给定:多边形可以总是由不重叠的n-2个三角形组成(n =点数或边数)。1个三角形= 3个面多边形= 1个三角形;1个正方形= 4个面多边形= 2个三角形;等等广告恶心的QED

因此,可以通过“切掉”三角形来减少多边形,总面积将是这些三角形的面积之和。用一张纸和剪刀尝试一下,最好是在执行以下操作之前先将其可视化。

如果您在多边形路径中选取任意3个连续点,并使用这些点创建一个三角形,那么您将只有以下三种可能的情况之一:

  1. 产生的三角形完全在原始多边形内
  2. 产生的三角形完全在原始多边形之外
  3. 产生的三角形部分包含在原始多边形中

我们只对属于第一个选项(完全包含)的情况感兴趣。

每次找到其中一个,我们都将其切碎,计算其面积(easy peasy,此处将不解释公式),并制作一个边少的新多边形(相当于将该三角形切掉的多边形)。直到我们只剩下一个三角形。

如何以编程方式实现此目的:

创建一个(连续的)点数组,这些点代表多边形周围的路径。从点0开始。运行数组,从点x,x + 1和x + 2制作三角形(一次一个)。将每个三角形从形状转换为面积,并将其与由多边形创建的面积相交。如果所得的交点与原始三角形相同,则该三角形完全包含在多边形中,可以将其切掉。从数组中删除x + 1,然后从x = 0重新开始。否则(如果三角形在[部分或完全]多边形外部),则移至数组中的下一个点x + 1。

另外,如果您希望与地图集成并且从地理点开始,则必须先从地理点转换为屏蔽点。这需要确定地球形状的模型和公式(尽管我们倾向于将地球视为球形,但实际上它是带有凹痕的不规则卵形(卵形))。有很多模型,有关进一步的信息Wiki。一个重要的问题是您是否将区域视为平面还是弯曲的。通常,如果点是平面的,而不是凸的,则这些点相距不超过几公里的“小”区域不会产生明显的误差。



1
  1. 设置一个基点(最凸的点)。这将是三角形的枢轴点。
  2. 计算除基点以外的最左点(任意)。
  3. 计算最左第二点以完成三角形。
  4. 保存此三角区域。
  5. 每次迭代都向右移一个点。
  6. 对三角区域求和

如果下一个点“向后移动”,请确保否定三角形区域。
递归

1

比对三角形求和还好,对笛卡尔空间中的梯形求和:

area = 0;
for (i = 0; i < n; i++) {
  i1 = (i + 1) % n;
  area += (vertex[i].y + vertex[i1].y) * (vertex[i1].x - vertex[i].x) / 2.0;
}

1

Shoelace公式的实现可以在Numpy中完成。假设这些顶点:

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

我们可以定义以下函数来查找区域:

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

避免循环使此功能比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

注意:我已经为另一个问题写了这个答案,我在这里只是为了提供完整的解决方案列表而提及。


0

我的倾向是简单地开始切出三角形。我看不出有什么其他方法可以避免毛茸茸的。

取三个组成多边形的连续点。确保角度小于180。现在您将拥有一个新三角形,该三角形应该可以计算出来,将其从多边形的点列表中删除。重复直到您只剩下三点。


与此有关的有毛的部分是,如果您的三个连续点在多边形外部或部分在多边形外部定义了一个三角形,那么您将遇到问题。
理查德

@理查德:这就是为什么资格约180度。如果在多边形外部切出三角形,则最终会产生太多的度数。
罗伦·佩希特

您可能需要更好地描述如何找到角度。平面几何中不可能有3个点作为三角形的一部分,并且任何角度或角度组合都超过180度-这项检查似乎毫无意义。
理查德

@Richard:在多边形上,每个结点都有角度。如果相关三角形位于多边形的外部,则两个线段之间的角度将大于180度。
罗伦·佩希特

您的意思是两个相邻边线段的内角将大于180度。
理查德

0

C方法:

float areaForPoly(const int numVerts, const Point *verts)
{
    Point v2;
    float area = 0.0f;

    for (int i = 0; i<numVerts; i++){
        v2 = verts[(i + 1) % numVerts];
        area += verts[i].x*v2.y - verts[i].y*v2.x;
    }

    return area / 2.0f;
}

0

Python代码

如此处所述:http : //www.wikihow.com/Calculate-the-Area-of-a-Polygon

与熊猫

import pandas as pd

df = pd.DataFrame({'x': [10, 20, 20, 30, 20, 10, 0], 'y': [-10, -10, -10, 0, 10, 30, 20]})
df = df.append(df.loc[0])

first_product = (df['x'].shift(1) * df['y']).fillna(0).sum()
second_product = (df['y'].shift(1) * df['x']).fillna(0).sum()

(first_product - second_product) / 2
600

0

我将提供一些简单的函数来计算2d多边形的面积。这对凸多边形和凹多边形均适用。 我们将多边形简单地分为许多子三角形。

//don't forget to include cmath for abs function
struct Point{
  double x;
  double y;
}
// cross_product
double cp(Point a, Point b){ //returns cross product
  return a.x*b.y-a.y*b.x;
}

double area(Point * vertices, int n){  //n is number of sides
  double sum=0.0;
  for(i=0; i<n; i++){
    sum+=cp(vertices[i], vertices[(i+1)%n]); //%n is for last triangle
  }
  return abs(sum)/2.0;
}

cp需要两个参数,但是您要用一个参数来调用它。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.