web-dev-qa-db-ja.com

Pythonを使用して地表面上のポリゴンの面積を計算する方法は?

タイトルは基本的にそれをすべて言います。 Pythonを使用して、地表面のポリゴン内部の面積を計算する必要があります。 地球の表面上の任意の多角形で囲まれた領域を計算しています はそれについて何かを述べていますが、技術的な詳細については曖昧なままです:

これをより「GIS」風にしたい場合は、エリアの測定単位を選択し、エリアを保持する適切な投影法を見つける必要があります(すべてではありません)。あなたは任意の多角形を計算することについて話しているので、私はランバート正距方位図法のようなものを使用します。投影の原点/中心をポリゴンの中心に設定し、ポリゴンを新しい座標系に投影してから、標準の平面手法を使用して面積を計算します。

では、Pythonでこれを行うにはどうすればよいですか?

31
andreas-h

コロラド州の状態をGeoJSON形式で表現しているとしましょう

{"type": "Polygon", 
 "coordinates": [[
   [-102.05, 41.0], 
   [-102.05, 37.0], 
   [-109.05, 37.0], 
   [-109.05, 41.0]
 ]]}

すべての座標は経度、緯度です。 pyproj を使用して座標を投影し、 Shapely を使用して投影されたポリゴンの領域を見つけることができます。

co = {"type": "Polygon", "coordinates": [
    [(-102.05, 41.0),
     (-102.05, 37.0),
     (-109.05, 37.0),
     (-109.05, 41.0)]]}
lon, lat = Zip(*co['coordinates'][0])
from pyproj import Proj
pa = Proj("+proj=aea +lat_1=37.0 +lat_2=41.0 +lat_0=39.0 +lon_0=-106.55")

これは、関心のある領域を中心とし、その範囲をまとめた等面積投影です。次に、新しい投影されたGeoJSON表現を作成し、Shapely幾何学的オブジェクトに変換して、面積を取得します。

x, y = pa(lon, lat)
cop = {"type": "Polygon", "coordinates": [Zip(x, y)]}
from shapely.geometry import shape
shape(cop).area  # 268952044107.43506

これは、調査対象地域に非常に近いものです。より複雑な機能の場合、正確な値を取得するには、頂点間のエッジに沿ってサンプリングする必要があります。日付変更線などに関する上記のすべての警告が適用されます。地域のみに関心がある場合は、投影する前に、機能を日付変更線から遠ざけることができます。

34
sgillies

これを行う最も簡単な方法は(私の意見では)、(非常に単純な)等面積投影に物を投影し、面積を計算するために通常の平面手法の1つを使用することです。

まず、あなたがこの質問をしているのであれば、球状の地球はあなたの目的にとって十分に近いと仮定します。そうでない場合は、適切な楕円体を使用してデータを再投影する必要があります。その場合は、python GDAL/OGR へのバインディングまたは(はるかに友好的な) pyproj

ただし、球形の地球で問題がなければ、特別なライブラリなしでこれを行うのは非常に簡単です。

計算する最も単純な等面積投影は 正弦波投影 です。基本的には、緯度に1度の緯度の長さを掛け、経度に1度の緯度の長さと緯度の余弦を掛けます。

def reproject(latitude, longitude):
    """Returns the x & y coordinates in meters using a sinusoidal projection"""
    from math import pi, cos, radians
    earth_radius = 6371009 # in meters
    lat_dist = pi * earth_radius / 180.0

    y = [lat * lat_dist for lat in latitude]
    x = [long * lat_dist * cos(radians(lat)) 
                for lat, long in Zip(latitude, longitude)]
    return x, y

わかりました...あとは、平面内の任意の多角形の面積を計算するだけです。

これを行うにはいくつかの方法があります。ここでは おそらく最も一般的なもの を使用します。

def area_of_polygon(x, y):
    """Calculates the area of an arbitrary polygon given its verticies"""
    area = 0.0
    for i in range(-1, len(x)-1):
        area += x[i] * (y[i+1] - y[i-1])
    return abs(area) / 2.0

うまくいけば、とにかく正しい方向にあなたを指すでしょう...

24
Joe Kington

少し遅いかもしれませんが、ここではジラールの定理を使用した別の方法を示します。大円のポリゴンの面積は、R ** 2ポリゴン間の角度の合計から(N-2)* piを引いた値がR ** 2であり、Nはコーナーの数です。

Numpy以外のライブラリに依存せず、他のライブラリとはかなり異なる方法であるため、これは投稿する価値があると思いました。もちろん、これは球体でのみ機能するため、地球に適用すると多少不正確になります。

最初に、大円に沿ったポイント1からポイント2までの方位角を計算する関数を定義します。

import numpy as np
from numpy import cos, sin, arctan2

d2r = np.pi/180

def greatCircleBearing(lon1, lat1, lon2, lat2):
    dLong = lon1 - lon2

    s = cos(d2r*lat2)*sin(d2r*dLong)
    c = cos(d2r*lat1)*sin(d2r*lat2) - sin(lat1*d2r)*cos(d2r*lat2)*cos(d2r*dLong)

    return np.arctan2(s, c)

これを使用して角度と面積を見つけることができます(以下では、lonsとlatsはもちろん指定する必要があり、正しい順序である必要があります。また、球の半径も指定する必要があります)。

N = len(lons)

angles = np.empty(N)
for i in range(N):

    phiB1, phiA, phiB2 = np.roll(lats, i)[:3]
    LB1, LA, LB2 = np.roll(lons, i)[:3]

    # calculate angle with north (eastward)
    beta1 = greatCircleBearing(LA, phiA, LB1, phiB1)
    beta2 = greatCircleBearing(LA, phiA, LB2, phiB2)

    # calculate angle between the polygons and add to angle array
    angles[i] = np.arccos(cos(-beta1)*cos(-beta2) + sin(-beta1)*sin(-beta2))

area = (sum(angles) - (N-2)*np.pi)*R**2

コロラド座標が別の応答で与えられ、地球の半径が6371 kmの場合、面積は268930758560.74808であることがわかります

6
sulkeh

地球は閉じた表面なので、その表面に描かれた閉じた多角形は[〜#〜] two [〜#〜]多角形の領域を作成します。どちらが内側にあり、どちらが外側にあるかも定義する必要があります!

ほとんどの場合、人々は小さなポリゴンを扱うため、それは「明白」ですが、海や大陸のサイズのものを用意したら、これを正しい方法で確実に行うようにします。

また、行は(-179,0)から(+179,0)まで2つの異なる方法で移動できることに注意してください。一方は他方より非常に長いです。繰り返しますが、ほとんどの場合、これは(-179,0)から(-180,0)に(+180,0)から(+179,0)に至る行であると仮定しますが、日...それはしません。

Lat-longを単純な(x、y)座標系のように扱うか、座標投影がゆがんだり壊れたりするという事実を無視すると、球体で大失敗する可能性があります。

4
Spacedman

または単にライブラリを使用します: https://github.com/scisco/area

from area import area
>>> obj = {'type':'Polygon','coordinates':[[[-180,-90],[-180,90],[180,90],[180,-90],[-180,-90]]]}
>>> area(obj)
511207893395811.06

...面積を平方メートルで返します。

3
Ikar Pohorský

座標変換にbasemappyprojの代わりにshapelyを使用するソリューションを次に示します。アイデアは、@ sgilliesによって提案されたものと同じです。パスが閉ループになるように5番目のポイントを追加したことに注意してください。

import numpy
from mpl_toolkits.basemap import Basemap

coordinates=numpy.array([
[-102.05, 41.0], 
[-102.05, 37.0], 
[-109.05, 37.0], 
[-109.05, 41.0],
[-102.05, 41.0]])

lats=coordinates[:,1]
lons=coordinates[:,0]

lat1=numpy.min(lats)
lat2=numpy.max(lats)
lon1=numpy.min(lons)
lon2=numpy.max(lons)

bmap=Basemap(projection='cea',llcrnrlat=lat1,llcrnrlon=lon1,urcrnrlat=lat2,urcrnrlon=lon2)
xs,ys=bmap(lons,lats)

area=numpy.abs(0.5*numpy.sum(ys[:-1]*numpy.diff(xs)-xs[:-1]*numpy.diff(ys)))
area=area/1e6

print area

結果はkm ^ 2で268993.609651になります。

3
Jason