web-dev-qa-db-ja.com

与えられた(x、y)座標のポリゴンの面積を計算する

ポイントのセットがあり、ポイントのセットで囲まれた面積を計算できる関数(利便性とおそらく速度のため)があるかどうかを知りたいです。

例えば:

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

points = Zip(x,y)

pointsの場合、面積は(pi-2)/4とほぼ等しくなります。これを行うために、scipy、matplotlib、numpy、shapelyなどから何かがありますか? x座標またはy座標のいずれにも負の値はありません。関数は定義されていないポリゴンになります。

編集:

ほとんどの場合、ポイントは指定された順序(時計回りまたは反時計回り)ではなく、一連の境界の下のシェープファイルからのutm座標のセットであるため、非常に複雑になる可能性があります

30
pbreach

Shoelace formula の実装は、Numpyで実行できます。これらの頂点を仮定:

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

Numpyで関数を再定義して、領域を見つけることができます。

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

forループを回避すると、この関数は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.

タイミングはJupyterノートブックで行われます。

67
Mahdi

shoelace formula を使用できます。例えば

def PolygonArea(corners):
    n = len(corners) # of corners
    area = 0.0
    for i in range(n):
        j = (i + 1) % n
        area += corners[i][0] * corners[j][1]
        area -= corners[j][0] * corners[i][1]
    area = abs(area) / 2.0
    return area

# examples
corners = [(2.0, 1.0), (4.0, 5.0), (7.0, 8.0)]

これは単純なポリゴンでのみ機能します


  • 穴のあるポリゴンがある場合:外側のリングの面積を計算し、内側のリングの面積をサブトラックします

  • 自己交差リングがある場合:単純なセクターに分解する必要があります

27

Mahdiの答えを分析した結果、ほとんどの時間はnp.roll()の実行に費やされたと結論付けました。ロールの必要をなくし、さらにnumpyを使用することで、Mahdiの41µsに比べてループあたり実行時間を4〜5µsに短縮しました(比較のため、Mahdiの関数はマシンで平均37µsかかりました)。

_def polygon_area(x,y):
    correction = x[-1] * y[0] - y[-1]* x[0]
    main_area = np.dot(x[:-1], y[1:]) - np.dot(y[:-1], x[1:])
    return 0.5*np.abs(main_area + correction)
_

補正項を計算し、配列をスライスすることにより、新しい配列を作成したり作成したりする必要はありません。

ベンチマーク:

_10000 iterations
PolyArea(x,y): 37.075µs per loop
polygon_area(x,y): 4.665µs per loop
_

タイミングはtimeモジュールとtime.clock()を使用して行われました

5
maxb

maxbの答えは優れたパフォーマンスを提供しますが、座標値またはポイントの数が大きい場合、簡単に精度の低下につながる可能性があります。これは、単純な座標シフトで軽減できます。

_def polygon_area(x,y):
    # coordinate shift
    x_ = x - x.mean()
    y_ = y - y.mean()
    # everything else is the same as maxb's code
    correction = x_[-1] * y_[0] - y_[-1]* x_[0]
    main_area = np.dot(x_[:-1], y_[1:]) - np.dot(y_[:-1], x_[1:])
    return 0.5*np.abs(main_area + correction)
_

たとえば、一般的な地理参照システムはUTMであり、UTMは_(488685.984, 7133035.984)_の(x、y)座標を持つ場合があります。これら2つの値の積は_3485814708748.448_です。この単一の製品はすでに精度のエッジにあることがわかります(入力と同じ小数点以下の桁数を持っています)。数千は言うまでもなく、これらの製品のほんの一部を追加すると、精度が失われます。

これを軽減する簡単な方法は、たとえば上記のコードのように重心を引くことにより、ポリゴンを大きな正の座標から(0,0)に近いものにシフトすることです。これは2つの方法で役立ちます。

  1. 各製品からx.mean() * y.mean()の要素を排除します
  2. 各内積内で正の値と負の値が混在し、大部分がキャンセルされます。

座標シフトは総面積を変更せず、計算を数値的に安定させるだけです。

4
Trenton

上記のコードには、反復ごとに絶対値をとらないため、エラーがあります。上記のコードは常にゼロを返します。 (数学的には、署名済みエリアまたはウェッジ製品と実際のエリアの違いです http://en.wikipedia.org/wiki/Exterior_algebra 。)代替コードを次に示します。

def area(vertices):
    n = len(vertices) # of corners
    a = 0.0
    for i in range(n):
        j = (i + 1) % n
        a += abs(vertices[i][0] * vertices[j][1]-vertices[j][0] * vertices[i][1])
    result = a / 2.0
    return result
2
Chris Judge

通常のポリゴンの場合、これははるかに簡単です。

import math

def area_polygon(n, s):
    return 0.25 * n * s**2 / math.tan(math.pi/n)

式は¼n s2/tan(π/ n)であるためです。辺の数nと各辺の長さs

1
Bizarre

に基づく

https://www.mathsisfun.com/geometry/area-irregular-polygons.html

def _area_(coords):
    t=0
    for count in range(len(coords)-1):
        y = coords[count+1][1] + coords[count][1]
        x = coords[count+1][0] - coords[count][0]
        z = y * x
        t += z
    return abs(t/2.0)

a=[(5.09,5.8), (1.68,4.9), (1.48,1.38), (4.76,0.1), (7.0,2.83), (5.09,5.8)]
print _area_(a)

秘Theは、最初の座標も最後にする必要があるということです。

0
Takis Tsiberis