Как рассчитать площадь многоугольника на поверхности земли с помощью питона?
Название в основном говорит все. Мне нужно вычислить площадь внутри многоугольника на поверхности Земли, используя Python. Вычисление области, заключенной в произвольный многоугольник на поверхности Земли, говорит о чем-то, но остается неопределенным по техническим деталям:
Если вы хотите сделать это с помощью более "ГИС", тогда вам нужно выбрать единицу измерения для вашего района и найти соответствующую проекцию, которая (не все). Поскольку вы говорят о вычислении произвольный многоугольник, я бы использовал что-то вроде Ламберта азимутального Проецирование равных площадей. Установить начало/центр проекции центр вашего полигона, проект многоугольник к новой координате системы, затем вычислить область, используя стандартные плоские методы.
Итак, как мне это сделать в Python?
Ответы
Ответ 1
Скажем, у вас есть представление о состоянии Колорадо в формате 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
Это очень близкое приближение к исследуемой области. Для более сложных функций вам нужно будет пробовать по краям между вершинами, чтобы получить точные значения. Все оговорки выше о датах и т.д. Применяются. Если вас интересует только область, вы можете перевести свою функцию в сторону от линии передачи данных перед проецированием.
Ответ 2
Самый простой способ сделать это (на мой взгляд) - проецировать вещи на (очень простую) проекцию равных площадей и использовать один из обычных планарных методов вычисления области.
Прежде всего, я собираюсь предположить, что сферическая земля достаточно близко для ваших целей, если вы задаете этот вопрос. Если нет, то вам нужно перепрограммировать свои данные с помощью соответствующего эллипсоида, и в этом случае вам захочется использовать фактическую проекционную библиотеку (в наши дни все использует proj4 за кадром), такие как привязки python к GDAL/OGR или (гораздо более дружелюбный) pyproj.
Однако, если вы в порядке со сферической землей, это довольно просто сделать без каких-либо специализированных библиотек.
Простейшая проекция равной площади для вычисления представляет собой синусоидальную проекцию . В принципе, вы просто умножаете широту на длину одной градусной широты и долготу на длину градуса широты и косинус широты.
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
Надеюсь, это будет указывать на вас в правильном направлении, во всяком случае...
Ответ 3
Возможно, немного поздно, но вот другой метод, используя теорему Жирарда. Он утверждает, что площадь многоугольника больших кругов равна R ** в 2 раза больше суммы углов между полигонами минус (N-2) * pi, где 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)
Теперь я могу использовать это, чтобы найти углы, а затем область (В следующем случае, конечно, должны быть указаны интервалы и латы, и они должны быть в правильном порядке. Также необходимо указать радиус сферы.)
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 км, я получаю, что область 268930758560.74808
Ответ 4
Поскольку земля является закрытой поверхностью, замкнутый многоугольник, нарисованный на его поверхности, создает ДВЕ многоугольные области. Вам также нужно определить, какой из них находится внутри и снаружи!
В большинстве случаев люди будут иметь дело с маленькими полигонами, и поэтому это "очевидно", но как только у вас есть вещи размером с океаны или континенты, вы должны убедиться, что вы правильно это сделаете.
Кроме того, помните, что строки могут идти от (-179,0) до (+179,0) двумя разными способами. Один из них намного длиннее другого. Опять же, в основном вы сделаете предположение, что это строка, которая идет от (-179,0) до (-180,0), которая равна (+180,0), а затем (+179,0), но одна день... это не будет.
Рассмотрение lat-long как простой (x, y) системы координат или даже пренебрежение тем фактом, что любая проекция координат будет иметь искажения и разрывы, может заставить вас терпеть неудачу в сферах.
Ответ 5
Вот решение, которое использует basemap
вместо pyproj
и shapely
для преобразования координат. Идея такая же, как и предложенная @sgillies. Учтите, что я добавил пятую точку, чтобы путь был замкнутым контуром.
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
Результат - 268993.609651 в км ^ 2.
Ответ 6
Или просто используйте библиотеку: 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
... возвращает площадь в квадратных метрах.