2Dポリゴンの面積を計算するにはどうすればよいですか?


81

自己交差しない2D空間内の一連の点を想定すると、結果のポリゴンの面積を決定する効率的な方法は何ですか?

ちなみに、これは宿題ではなく、コードを探していません。独自のメソッドを実装するために使用できる説明を探しています。ポイントのリストから三角形のシーケンスを引き出すことについての私の考えはありますが、凸多角形と凹多角形に関しては、おそらく捕まえられないエッジケースがたくさんあることを知っています。


6
「表面積」という用語は少し誤解を招く恐れがあります。あなたが望んでいるように見えるのは、(通常の)領域だけです。3Dでは、表面積は外面の面積であるため、この概念の自然な2Dの一般化は、ポリゴンの周囲の長さであり、明らかにあなたが探しているものではありません。
batty 2010

def area(polygon):return abs(numpy.cross(polygon、numpy.roll(polygon、-1、0))。sum()/ 2)
iouvxz 2017年

回答:


111

これが標準的な方法であるAFAIKです。基本的に、各頂点の周りの外積を合計します。三角測量よりもはるかに簡単です。

(x、y)頂点座標のリストとして表されるポリゴンが与えられ、最後の頂点から最初の頂点に暗黙的にラップアラウンドするPythonコード:

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)= integer_area((-(-dy)/ dy + dx / dx)dydyx = 2 Area
David Lehavi 2009年

6
投稿内のリンクが切れています。他に誰かいますか?
ヤコフ

1
compgeom-discuss@research.bell-labs.comメーリングリストでのリンクされたディスカッションは、私には利用できません。:私はGoogleのキャッシュからのメッセージコピー gist.github.com/1200393
アンドリューАндрейЛисточкин

2
@ perfectionm1ng方向を切り替えると、合計の符号が反転しますが、符号がabs()削除されます。
ダリウスベーコン

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が偶数でない場合は、最初のポイントのコピーを追加します。

このアルゴリズムは、古典的な外積アルゴリズムの2つの連続する反復を展開して結合することによって取得されます。

数値の精度に関して、2つのアルゴリズムがどのように比較されるかはよくわかりません。私の印象では、乗算は減算の精度の低下を回復する傾向があるため、上記のアルゴリズムは従来のアルゴリズムよりも優れています。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
2番目のコードスニペットが機能することは想像できません。ポリゴンが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 2014年


4

三角形分割と三角形の合計の領域を拡張するには、凸多角形がある場合、または多角形と交差する他のすべての点への線を生成しない点を選択した場合に機能します。

一般的な交差しないポリゴンの場合、aとbが互いに「隣接」しているベクトル(参照点、点a)、(参照点、点b)の外積を合計する必要があります。

ポリゴンを順番に定義するポイントのリストがあると仮定します(ポイントiとi + 1がポリゴンのラインを形成する順番):

Sum(cross product((point 0、point i)、(point 0、point i + 1))for i = 1 to n-1。

その外積の大きさをとると、表面積が得られます。

これにより、適切な参照点を選択することを心配することなく、凹多角形を処理できます。ポリゴンの内側にない三角形を生成する3つのポイントには、ポリゴンの内側にある三角形の反対方向を指す外積があるため、領域は正しく合計されます。


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の賛成票があります。あなたの答えが、すでに投稿されている他のどの答えよりも優れていることを教えてください。
マークテイラー

3
これはcの例であり、Pythonではありません。良くはありませんが、異なる言語でそれを持っていることは素晴らしいです
underdoeg 2015

2

または、周回積分を行います。ストークスの定理を使用すると、面積積分を周回積分として表すことができます。小さなガウス求積法とボブはあなたの叔父です。


2

言語に依存しないソリューション:

与えられた:ポリゴンは常に重なり合わないn-2個の三角形で構成できます(n =点または辺の数)。1つの三角形= 1つの側面のポリゴン= 1つの三角形; 1つの正方形= 4辺のポリゴン= 2つの三角形; など広告吐き気QED

したがって、三角形を「切り落とす」ことでポリゴンを縮小でき、総面積はこれらの三角形の面積の合計になります。一枚の紙とはさみで試してみてください。フォローする前にプロセスを視覚化するのが最善です。

ポリゴンパス内の3つの連続するポイントを取得し、これらのポイントで三角形を作成すると、考えられる3つのシナリオのうちの1つだけが発生します。

  1. 結果の三角形は完全に元のポリゴンの内側にあります
  2. 結果の三角形は完全に元のポリゴンの外側にあります
  3. 結果の三角形は、元のポリゴンに部分的に含まれています

最初のオプション(完全に含まれている)に該当する場合にのみ関心があります。

これらのいずれかを見つけるたびに、それを切り取り、その面積を計算し(簡単で、ここでは式を説明しません)、辺が1つ少ない新しいポリゴンを作成します(この三角形を切り取ったポリゴンに相当します)。三角形が1つだけ残るまで。

これをプログラムで実装する方法:

ポリゴンの周りのパスを表す(連続した)ポイントの配列を作成します。ポイント0から開始します。ポイントx、x + 1、およびx + 2から三角形を作成する配列を(一度に1つずつ)実行します。各三角形を形状から領域に変換し、ポリゴンから作成された領域と交差させます。結果の交差が元の三角形と同一である場合、その三角形は完全にポリゴンに含まれており、切り取ることができます。配列からx + 1を削除し、x = 0から再開します。それ以外の場合(三角形が[部分的または完全に]ポリゴンの外側にある場合)、配列内の次の点x +1に移動します。

さらに、マッピングとの統合を検討していて、ジオポイントから開始する場合は、最初にジオポイントからスクリーンポイントに変換する必要があります。これには、地球の形状のモデリングと式を決定する必要があります(地球は球体と考える傾向がありますが、実際には、へこみのある不規則な卵形(卵形)です)。詳細については、wikiに多くのモデルがあります。重要な問題は、その領域を平面と見なすか、曲線と見なすかです。一般に、ポイントが最大数km離れている「小さな」領域は、平面で凸状ではないと見なした場合、重大なエラーを生成しません。



1
  1. 基点(最も凸の点)を設定します。これが三角形のピボットポイントになります。
  2. 基点以外の最も左の点(任意)を計算します。
  3. 左から2番目の点を計算して、三角形を完成させます。
  4. この三角形の領域を保存します。
  5. 反復ごとに1ポイント右にシフトします。
  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

靴紐式の実装は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

ループを回避すると、この関数は次の関数よりも最大50倍高速になりPolygonAreaます。

%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

私の傾向は、単に三角形を切り取り始めることです。他の何かがひどく毛むくじゃらになるのを避けることができる方法がわかりません。

ポリゴンを構成する3つの連続したポイントを取ります。角度が180未満であることを確認します。これで、計算に問題がないはずの新しい三角形ができました。ポリゴンの点のリストから中点を削除してください。残り3点になるまで繰り返します。


これについての厄介な部分は、3つの連続するポイントがポリゴンの外側または部分的に外側の三角形を定義する場合、問題が発生することです。
リチャード

@リチャード:それが約180度の資格である理由です。ポリゴンの外側の三角形をスライスすると、度が多すぎます。
Loren Pechtel 2012年

あなたはあなたがどのように角度を見つけているかをよりよく説明する必要があるかもしれません。平面ジオメトリでは、三角形の一部として3つのポイントを持ち、角度または角度の組み合わせが180度を超える方法はありません。チェックは無意味に思えます。
リチャード

@Richard:ポリゴンには、すべてのジャンクションの角度があります。関連する三角形がポリゴンの外側にある場合、2つのセグメント間の角度は180度より大きくなります。
Loren Pechtel 2012年

つまり、隣接する2つのエッジセグメントの内角は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;
}

cp2つの引数を取りますが、1つで呼び出しています。
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.