自己交差しない2D空間内の一連の点を想定すると、結果のポリゴンの面積を決定する効率的な方法は何ですか?
ちなみに、これは宿題ではなく、コードを探していません。独自のメソッドを実装するために使用できる説明を探しています。ポイントのリストから三角形のシーケンスを引き出すことについての私の考えはありますが、凸多角形と凹多角形に関しては、おそらく捕まえられないエッジケースがたくさんあることを知っています。
自己交差しない2D空間内の一連の点を想定すると、結果のポリゴンの面積を決定する効率的な方法は何ですか?
ちなみに、これは宿題ではなく、コードを探していません。独自のメソッドを実装するために使用できる説明を探しています。ポイントのリストから三角形のシーケンスを引き出すことについての私の考えはありますが、凸多角形と凹多角形に関しては、おそらく捕まえられないエッジケースがたくさんあることを知っています。
回答:
これが標準的な方法である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
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が偶数でない場合は、最初のポイントのコピーを追加します。
このアルゴリズムは、古典的な外積アルゴリズムの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;
このページは、式が
次のように簡略化できます。
いくつかの用語を書き、の一般的な要因に従ってそれらをグループ化すると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がポリゴンのラインを形成する順番):
Sum(cross product((point 0、point i)、(point 0、point i + 1))for i = 1 to n-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);
}
言語に依存しないソリューション:
与えられた:ポリゴンは常に重なり合わないn-2個の三角形で構成できます(n =点または辺の数)。1つの三角形= 1つの側面のポリゴン= 1つの三角形; 1つの正方形= 4辺のポリゴン= 2つの三角形; など広告吐き気QED
したがって、三角形を「切り落とす」ことでポリゴンを縮小でき、総面積はこれらの三角形の面積の合計になります。一枚の紙とはさみで試してみてください。フォローする前にプロセスを視覚化するのが最善です。
ポリゴンパス内の3つの連続するポイントを取得し、これらのポイントで三角形を作成すると、考えられる3つのシナリオのうちの1つだけが発生します。
最初のオプション(完全に含まれている)に該当する場合にのみ関心があります。
これらのいずれかを見つけるたびに、それを切り取り、その面積を計算し(簡単で、ここでは式を説明しません)、辺が1つ少ない新しいポリゴンを作成します(この三角形を切り取ったポリゴンに相当します)。三角形が1つだけ残るまで。
これをプログラムで実装する方法:
ポリゴンの周りのパスを表す(連続した)ポイントの配列を作成します。ポイント0から開始します。ポイントx、x + 1、およびx + 2から三角形を作成する配列を(一度に1つずつ)実行します。各三角形を形状から領域に変換し、ポリゴンから作成された領域と交差させます。結果の交差が元の三角形と同一である場合、その三角形は完全にポリゴンに含まれており、切り取ることができます。配列からx + 1を削除し、x = 0から再開します。それ以外の場合(三角形が[部分的または完全に]ポリゴンの外側にある場合)、配列内の次の点x +1に移動します。
さらに、マッピングとの統合を検討していて、ジオポイントから開始する場合は、最初にジオポイントからスクリーンポイントに変換する必要があります。これには、地球の形状のモデリングと式を決定する必要があります(地球は球体と考える傾向がありますが、実際には、へこみのある不規則な卵形(卵形)です)。詳細については、wikiに多くのモデルがあります。重要な問題は、その領域を平面と見なすか、曲線と見なすかです。一般に、ポイントが最大数km離れている「小さな」領域は、平面で凸状ではないと見なした場合、重大なエラーを生成しません。
これを行う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
注:私は別の質問のためにこの回答を書きました。解決策の完全なリストを用意するために、ここでこれについて言及します。
私の傾向は、単に三角形を切り取り始めることです。他の何かがひどく毛むくじゃらになるのを避けることができる方法がわかりません。
ポリゴンを構成する3つの連続したポイントを取ります。角度が180未満であることを確認します。これで、計算に問題がないはずの新しい三角形ができました。ポリゴンの点のリストから中点を削除してください。残り3点になるまで繰り返します。
ここで説明されているように: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;
}