找到一堆多边形覆盖的总面积的最有效方法是什么?

时间:2019-03-26 18:46:34

标签: python optimization

我正在用Python进行这个项目,在那里我有一堆代表地球上晴空区域的多边形。我也有一些代表卫星看点的多边形(它们本质上是正方形)。我试图弄清楚卫星已经覆盖了多少净空区域,我可以做到,但这需要永远。现在我想知道如何加快计算过程。

我可以肯定的是,可以进行相当大的估算。

  1. 最初,我尝试自己制作所有几何函数(例如,一个多边形与另一个多边形的交集,多边形的区域),并且通常可以,但是我的代码很慢。

  2. 发现Shapely库。太棒了!感谢参与其中的人们。现在,我将Shapely形状用于多边形,找到交点和面积等非常容易。但是,它并不总是很快。

  3. 因为面积计算花了很长时间,所以我放弃了尝试使其精确化,现在我只想寻找与面积成比例的东西。现在,我将多边形所覆盖的点数(初始多边形的质心)用作我的面积值。如果您能在适当的时候得到此值以计算实际面积,那么我会为您提供额外的假想点。

我认为目前的代码还不可怕,也许就优化而言,我还没有做更多的事情,但是想把它扔出去,以防万一有人有想法。

虽然为我节省了一些时间,但是使用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

`

0 个答案:

没有答案