我正在用Python进行这个项目,在那里我有一堆代表地球上晴空区域的多边形。我也有一些代表卫星看点的多边形(它们本质上是正方形)。我试图弄清楚卫星已经覆盖了多少净空区域,我可以做到,但这需要永远。现在我想知道如何加快计算过程。
我可以肯定的是,可以进行相当大的估算。
最初,我尝试自己制作所有几何函数(例如,一个多边形与另一个多边形的交集,多边形的区域),并且通常可以,但是我的代码很慢。
发现Shapely库。太棒了!感谢参与其中的人们。现在,我将Shapely形状用于多边形,找到交点和面积等非常容易。但是,它并不总是很快。
因为面积计算花了很长时间,所以我放弃了尝试使其精确化,现在我只想寻找与面积成比例的东西。现在,我将多边形所覆盖的点数(初始多边形的质心)用作我的面积值。如果您能在适当的时候得到此值以计算实际面积,那么我会为您提供额外的假想点。
我认为目前的代码还不可怕,也许就优化而言,我还没有做更多的事情,但是想把它扔出去,以防万一有人有想法。
虽然为我节省了一些时间,但是使用Shapely并不是此代码的必要条件。
我曾经想到的一个想法是,我应该把所有的小多边形合并成一个大多边形,然后计算出它的重叠面积,但是我不确定该怎么做。
在Ubuntu上运行(不是真的很重要,对吗?),并使用Python 3.7。如果您需要其他详细信息,我们将很乐意提供更多信息。
`
# for the area stuff we care about
import numpy as np
from shapely.geometry import Polygon, Point
# for timing things
from functools import wraps
from time import time
def timing(f):
"""Prints elapsed time after a function runs"""
# Thanks, яүυк!
# https://codereview.stackexchange.com/questions/169870/decorator-to-measure-execution-time-of-a-function
# I'm aware the timing won't be perfect, this is just to get an idea
@wraps(f)
def wrapper(*args, **kwargs):
start = time()
result = f(*args, **kwargs)
end = time()
print('Elapsed time: {}'.format(end-start))
return result
return wrapper
@timing
def generate_points_polys():
"""I do this in a different way in the actual code, but have simplified
the approach for this question"""
# domain of interest
x_low = -1000
x_high = 1000
y_low = -1000
y_high = 1000
resolution = 0.1
# these are the points I want to cover
x_points = np.arange(x_low, x_high, resolution)
y_points = np.arange(y_low, y_high, resolution)
# convert them into shapely points for easier geometrical operations
points = zip(x_points, y_points)
points = np.array([Point(x, y) for x, y in points], dtype=object)
# generate polygons
polys_per_col = 10
polys_per_row = 10
polys = np.empty((polys_per_col, polys_per_row), dtype=object)
for index, _ in np.ndenumerate(polys):
# let's say they're 5x5 for the sake of the question
# the shapes are dynamic in real life
x = np.random.uniform(low=x_low, high=x_high)
y = np.random.uniform(low=y_low, high=y_high)
vertices = [[x,y],[x+5,y],[x+5,y+5],[x,y+5]]
polys[index] = Polygon(vertices)
return points, polys
@timing
def calculate_area(points, polys):
"""This is the function we're trying to optimize"""
areas = np.empty(polys.shape, dtype=float)
for index, poly in np.ndenumerate(polys):
# get number of polygons at least partially covered
num_covered = len([1 for point in points if poly.intersects(point)])
# area is not equal to this but I figure it's proportional
# and calculating the real area would be expensive
areas[index] = num_covered
return areas
@timing
def do_the_things():
points, polys = generate_points_polys()
calculate_area(points, polys)
do_the_things()
`
我希望能够快速计算总面积,但是使用我尝试的方法要花费很长时间。我应该使用任何代码想法或其他近似方法吗?
编辑:
我尝试了注释中的一项建议,一项关于使用MultiPolygon的建议。以下是使用相同点数进行的涉及此类事情的几次试验:
`
# Function 1, finds how many points are covered
def calculate_area(points, polys):
areas = np.empty(polys.shape, dtype=float)
for index, poly in np.ndenumerate(polys):
num_covered = len([1 for point in points if poly.intersects(point)])
areas[index] = num_covered
return areas
# Function 2, number of background polygons (b_polys) covered
def calculate_area2(b_polys, polys):
areas = np.empty(polys.shape, dtype=float)
for index, poly in np.ndenumerate(polys):
close_polys = [p for p in b_polys if poly.intersects(p)]
areas[index] = len(close_polys)
return areas
# Function 3, Function 2, but with actual areas
def calculate_area3(b_polys, polys):
areas = np.empty(polys.shape, dtype=float)
for index, poly in np.ndenumerate(polys):
close_polys = [p for p in b_polys if poly.intersects(p)]
total_area = 0
for p in close_polys:
total_area += p.area
areas[index] = total_area
return areas
# Function 4, calculates overlap area with cascaded_union
def calculate_area4(b_polys, polys):
areas = np.empty(polys.shape, dtype=float)
for index, poly in np.ndenumerate(polys):
area = shapely.ops.cascaded_union(list(b_polys)+[poly]).area
areas[index] = area
return areas
`
我的发现(对于每个函数使用相同的计时器和相同的点/多边形数):
`
Elapsed time (s)
1: 0.8620626926422119
2: 0.8469529151916504
3: 1.2330029010772705
4: 2.49029541015625
`