【问题标题】:How do I calculate the area of a 2d polygon?如何计算二维多边形的面积?
【发布时间】:2010-10-01 20:58:00
【问题描述】:

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

附带说明,这不是家庭作业,我不是在寻找代码。我正在寻找可以用来实现我自己的方法的描述。我有关于从点列表中提取一系列三角形的想法,但我知道有很多关于凸多边形和凹多边形的边缘情况,我可能无法捕捉到。

【问题讨论】:

  • “表面积”这个词有点误导。您似乎想要的只是(常规)区域。在 3D 中,表面积是外表面的面积,因此这个概念的自然 2D 推广将是多边形周长的长度,这显然不是您要寻找的。​​span>
  • def area(polygon): return abs(numpy.cross(polygon, numpy.roll(polygon, -1, 0)).sum() / 2)

标签: algorithm geometry 2d


【解决方案1】:

这里是the standard method,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 cmets:值得一提的是为什么该算法有效:它是函数-y 和x 的Green's theorem 的应用;完全按照planimeter 的工作方式。更具体地说:

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

【讨论】:

  • 值得一提的是为什么这个算法有效:它是格林定理对函数-y和x的应用;完全按照平面计的工作方式。更具体地说:上式 = 积分_permieter(-y dx + x dy) = 积分面积((-(-dy)/dy+dx/dx)dydyx = 2 面积
  • 帖子里的链接失效了,还有人有吗?
  • @perfectm1ng 切换方向会翻转总和中的符号,但 abs() 会去掉符号。
  • 限制:此方法将产生自相交多边形的错误答案,其中一侧与另一侧交叉,如右图所示。但是,对于三角形、规则和不规则多边形、凸面或凹面多边形,它可以正常工作。 (mathopenref.com/coordpolygonarea.html)
  • 这在维基百科中也称为Shoelace formula
【解决方案2】:

叉积是经典。

如果你有无数次这样的计算要做,试试下面的优化版本,它需要的乘法减少一半:

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 一样,这会产生很大的不同。

编辑:"Area of Triangles and Polygons 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;

【讨论】:

  • 我无法想象第二个代码 sn-p 会起作用。很明显,多边形在 X 轴上越远,其面积越大。
  • 这是对上述算法的正确数学重排,节省了一些乘法。你是对的,但是其他顶点定义的区域会减去。但这确实可能导致精度下降。
  • 您忽略的是,由于 y 减法,加法总是有一些负项。考虑任何 2d 多边形形状并比较连续顶点的 y 值。你会看到一些减法会产生一个负值和一些正值。
  • 确实,最后一段是我无法理解的!使用 i
  • 附带说明,算法返回的区域是“有符号的”(负数或正数基于点的顺序),因此如果您希望始终为正数,请使用绝对值。
【解决方案3】:

This page表示公式

可以简化为:

如果写出几个词,按照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

我从 Joe Kington here 那里学到了这种简化。


如果你有 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

【讨论】:

  • 感谢 NumPy 版本。
【解决方案4】:

没有任何其他约束的一组点不一定唯一定义一个多边形。

所以,首先你必须决定从这些点构建什么多边形——也许是凸包? http://en.wikipedia.org/wiki/Convex_hull

然后进行三角测量并计算面积。 http://www.mathopenref.com/polygonirregulararea.html

【讨论】:

    【解决方案5】:

    为了扩展三角形和求和三角形区域,如果您碰巧有一个凸多边形,或者您碰巧选择了一个不会生成与多边形相交的每个其他点的线的点,这些都可以工作。

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

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

    求和(叉积 ((point 0, point i), (point 0, point i + 1)) for i = 1 to n - 1.

    取那个叉积的大小,你就有了表面积。

    这将处理凹多边形,而不必担心选择一个好的参考点;生成不在多边形内的三角形的任何三个点都将具有一个叉积,该叉积指向多边形内的任何三角形的相反方向,因此这些区域得到正确求和。

    【讨论】:

      【解决方案6】:

      计算多边形的面积

      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 个赞成票。告诉我们您的答案比已发布的任何其他答案更好。
      • 这是 c 中的示例,而不是 python 中的示例。不是更好,但很高兴有不同的语言
      【解决方案7】:

      或者做一个轮廓积分。斯托克斯定理允许您将面积积分表示为等高线积分。一点高斯求积和 Bob 是你的叔叔。

      【讨论】:

        【解决方案8】:

        语言无关的解决方案:

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

        因此,可以通过“切掉”三角形来缩小多边形,总面积将是这些三角形面积的总和。用一张纸和剪刀试一试,最好先想象一下这个过程。

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

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

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

        每次我们找到其中一个时,我们将其切掉,计算它的面积(简单易懂,这里不会解释公式)并制作一个边少的新多边形(相当于切掉这个三角形的多边形)。直到我们只剩下一个三角形。

        如何以编程方式实现:

        创建一个(连续)点数组,代表多边形周围的路径。从点 0 开始。运行从点 x、x+1 和 x+2 制作三角形(一次一个)的数组。将每个三角形从一个形状转换为一个区域,并将其与从多边形创建的区域相交。如果生成的交点与原始三角形相同,则所述三角形完全包含在多边形中并且可以被切掉。从数组中删除 x+1 并从 x=0 重新开始。否则(如果三角形在[部分或完全]多边形之外),移动到数组中的下一个点 x+1。

        此外,如果您希望与地图集成并从地理点开始,则必须首先从地理点转换为屏幕点。这需要确定地球形状的模型和公式(尽管我们倾向于将地球视为一个球体,但它实际上是一个不规则的卵形(蛋形),带有凹痕)。那里有很多模型,有关更多信息 wiki。一个重要的问题是您是否会将该区域视为平面还是弯曲的。一般来说,如果考虑平面而不是凸面,点相距几公里的“小”区域不会产生明显的误差。

        【讨论】:

          【解决方案9】:

          一种方法是decompose the polygon into triangles,计算三角形的面积,并将总和作为多边形的面积。

          【讨论】:

            【解决方案10】:
            1. 设置一个基点(最凸的点)。这将是三角形的轴心点。
            2. 计算最左边的点(任意),而不是您的基点。
            3. 计算最左边的第二个点以完成您的三角形。
            4. 保存这个三角区域。
            5. 每次迭代向右移动一个点。
            6. 对三角区域求和

            【讨论】:

            • 如果下一个点“向后”移动,请确保您否定三角形区域。
            【解决方案11】:

            比对三角形求和更好的是在笛卡尔空间中对梯形求和:

            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;
            }
            

            【讨论】:

              【解决方案12】:

              Shoelace formula 的实现可以在 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
              

              注意:我已经为另一个 question 写了这个答案,我只是在这里提到这个以获得完整的解决方案列表。

              【讨论】:

                【解决方案13】:

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

                选取构成多边形的三个连续点。确保角度小于 180。您现在有了一个新的三角形,计算应该没有问题,从多边形的点列表中删除中间点。重复直到你只剩下三个点。

                【讨论】:

                • 关于这个的毛茸茸的部分是,如果你的三个连续点定义了一个在多边形之外或部分在多边形之外的三角形,那么你就有问题了。
                • @Richard:这就是为什么约180度的资格。如果你在多边形外切掉一个三角形,你最终会得到太多的度数。
                • 您可能需要更好地描述您是如何找到角度的。平面几何中不可能有 3 个点作为三角形的一部分,并且任何角度或角度组合都超过 180 度 - 检查似乎没有意义。
                • @Richard:在你的多边形上,你有每个连接点的角度。如果相关三角形位于多边形之外,则两条线段之间的角度将大于 180 度。
                • 你的意思是两个相邻边段的内角大于180度。
                【解决方案14】:

                这样做的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;
                }
                

                【讨论】:

                  【解决方案15】:

                  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
                  

                  【讨论】:

                    【解决方案16】:

                    我将给出一些计算二维多边形面积的简单函数。这适用于凸多边形和凹多边形。 我们简单地将多边形分成许多子三角形。

                    //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 接受两个参数,但你用一个来调用它。
                    猜你喜欢
                    • 1970-01-01
                    • 2013-11-13
                    • 2012-04-07
                    • 1970-01-01
                    • 2013-10-24
                    • 2020-01-24
                    • 2013-09-07
                    • 1970-01-01
                    相关资源
                    最近更新 更多