假设2d空间中一系列不自相交的点,确定所得多边形面积的有效方法是什么?
附带说明,这不是功课,我也不在寻找代码。我正在寻找可以用来实现自己的方法的描述。我有从点列表中拉出一系列三角形的想法,但是我知道有很多关于凸多边形和凹多边形的边沿案例,我可能不会注意到。
假设2d空间中一系列不自相交的点,确定所得多边形面积的有效方法是什么?
附带说明,这不是功课,我也不在寻找代码。我正在寻找可以用来实现自己的方法的描述。我有从点列表中拉出一系列三角形的想法,但是我知道有很多关于凸多边形和凹多边形的边沿案例,我可能不会注意到。
Answers:
这是标准方法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
abs()
的符号,但去除符号。
叉积是经典。
如果您要进行大量此类计算,请尝试下面的优化版本,其乘法量减少一半:
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;
此页面显示公式
可以简化为:
如果您写出一些术语并根据的共同因素对其进行分组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
没有任何其他约束的一组点不一定唯一地定义多边形。
因此,首先您必须决定从这些点构建什么多边形-也许是凸包?http://en.wikipedia.org/wiki/Convex_hull
然后进行三角剖分并计算面积。 http://www.mathopenref.com/polygonirregulararea.html
为了扩展三角形和求和三角形区域,如果碰巧具有凸多边形,或者碰巧选择了一个不会生成与该多边形相交的其他所有点的线的点,这些方法将起作用。
对于一般的非相交多边形,您需要对向量的叉积求和(参考点,点a),(参考点,点b),其中a和b彼此“相邻”。
假设您具有按顺序定义多边形的点的列表(点i和i + 1构成多边形的一条线):
总和(i = 1至n-1的叉积((点0,点i),(点0,点i + 1))
取该叉积的大小,便得到表面积。
这样就可以处理凹面多边形,而不必担心选择一个好的参考点。生成不在多边形内部的三角形的任何三个点将具有与多边形内部任何三角形相反的叉积,因此可以正确求和面积。
计算多边形的面积
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);
}
独立于语言的解决方案:
给定:多边形可以总是由不重叠的n-2个三角形组成(n =点数或边数)。1个三角形= 3个面多边形= 1个三角形;1个正方形= 4个面多边形= 2个三角形;等等广告恶心的QED
因此,可以通过“切掉”三角形来减少多边形,总面积将是这些三角形的面积之和。用一张纸和剪刀尝试一下,最好是在执行以下操作之前先将其可视化。
如果您在多边形路径中选取任意3个连续点,并使用这些点创建一个三角形,那么您将只有以下三种可能的情况之一:
我们只对属于第一个选项(完全包含)的情况感兴趣。
每次找到其中一个,我们都将其切碎,计算其面积(easy peasy,此处将不解释公式),并制作一个边少的新多边形(相当于将该三角形切掉的多边形)。直到我们只剩下一个三角形。
如何以编程方式实现此目的:
创建一个(连续的)点数组,这些点代表多边形周围的路径。从点0开始。运行数组,从点x,x + 1和x + 2制作三角形(一次一个)。将每个三角形从形状转换为面积,并将其与由多边形创建的面积相交。如果所得的交点与原始三角形相同,则该三角形完全包含在多边形中,可以将其切掉。从数组中删除x + 1,然后从x = 0重新开始。否则(如果三角形在[部分或完全]多边形外部),则移至数组中的下一个点x + 1。
另外,如果您希望与地图集成并且从地理点开始,则必须先从地理点转换为屏蔽点。这需要确定地球形状的模型和公式(尽管我们倾向于将地球视为球形,但实际上它是带有凹痕的不规则卵形(卵形))。有很多模型,有关进一步的信息Wiki。一个重要的问题是您是否将区域视为平面还是弯曲的。通常,如果点是平面的,而不是凸的,则这些点相距不超过几公里的“小”区域不会产生明显的误差。
比对三角形求和还好,对笛卡尔空间中的梯形求和:
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;
}
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
注意:我已经为另一个问题写了这个答案,我在这里只是为了提供完整的解决方案列表而提及。
我的倾向是简单地开始切出三角形。我看不出有什么其他方法可以避免毛茸茸的。
取三个组成多边形的连续点。确保角度小于180。现在您将拥有一个新三角形,该三角形应该可以计算出来,将其从多边形的点列表中删除。重复直到您只剩下三点。
如此处所述: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
我将提供一些简单的函数来计算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;
}