タイトルは基本的にそれをすべて言います。 Pythonを使用して、地表面のポリゴン内部の面積を計算する必要があります。 地球の表面上の任意の多角形で囲まれた領域を計算しています はそれについて何かを述べていますが、技術的な詳細については曖昧なままです:
これをより「GIS」風にしたい場合は、エリアの測定単位を選択し、エリアを保持する適切な投影法を見つける必要があります(すべてではありません)。あなたは任意の多角形を計算することについて話しているので、私はランバート正距方位図法のようなものを使用します。投影の原点/中心をポリゴンの中心に設定し、ポリゴンを新しい座標系に投影してから、標準の平面手法を使用して面積を計算します。
では、Pythonでこれを行うにはどうすればよいですか?
コロラド州の状態を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
これは、調査対象地域に非常に近いものです。より複雑な機能の場合、正確な値を取得するには、頂点間のエッジに沿ってサンプリングする必要があります。日付変更線などに関する上記のすべての警告が適用されます。地域のみに関心がある場合は、投影する前に、機能を日付変更線から遠ざけることができます。
これを行う最も簡単な方法は(私の意見では)、(非常に単純な)等面積投影に物を投影し、面積を計算するために通常の平面手法の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
うまくいけば、とにかく正しい方向にあなたを指すでしょう...
少し遅いかもしれませんが、ここではジラールの定理を使用した別の方法を示します。大円のポリゴンの面積は、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であることがわかります
地球は閉じた表面なので、その表面に描かれた閉じた多角形は[〜#〜] two [〜#〜]多角形の領域を作成します。どちらが内側にあり、どちらが外側にあるかも定義する必要があります!
ほとんどの場合、人々は小さなポリゴンを扱うため、それは「明白」ですが、海や大陸のサイズのものを用意したら、これを正しい方法で確実に行うようにします。
また、行は(-179,0)から(+179,0)まで2つの異なる方法で移動できることに注意してください。一方は他方より非常に長いです。繰り返しますが、ほとんどの場合、これは(-179,0)から(-180,0)に(+180,0)から(+179,0)に至る行であると仮定しますが、日...それはしません。
Lat-longを単純な(x、y)座標系のように扱うか、座標投影がゆがんだり壊れたりするという事実を無視すると、球体で大失敗する可能性があります。
または単にライブラリを使用します: 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
...面積を平方メートルで返します。
座標変換にbasemap
とpyproj
の代わりに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になります。