如果点和多边形具有相同的最小边界框

时间:2016-09-22 04:51:04

标签: python gis geospatial shapely geopandas

我有一个匀称的多边形代表洛杉矶市的边界。我还在一个geopandas GeoDataFrame中有一组~100万个lat-long points ,所有这些都落在该多边形的最小边界框内。其中一些点位于多边形本身内,但其他点则不然。我想只保留洛杉矶边界内的那些点,并且由于洛杉矶的不规则形状,其最小边界框内只有大约1/3的点位于多边形本身内。

使用Python,鉴于点和多边形具有相同的最小边界框,识别多边形中哪些点的最快方法是什么?

我尝试使用geopandas及其r-tree空间索引:

sindex = gdf['geometry'].sindex
possible_matches_index = list(sindex.intersection(polygon.bounds))
possible_matches = gdf.iloc[possible_matches_index]
points_in_polygon = possible_matches[possible_matches.intersects(polygon)]

这使用GeoDataFrame的r-tree空间索引快速找到可能的匹配项,然后找到多边形与那些可能匹配项的精确交集。但是,因为多边形的最小边界框与点的集合相同。最小边界框,r-tree认为每个点是可能的匹配。因此,使用r树空间索引使得交叉点运行不比没有空间索引的情况更快。这种方法非常慢:完成需要大约30分钟。

我还尝试将多边形划分为小的子多边形,然后使用空间索引查找哪些点可能与这些子多边形中的每一个相交。该方法成功找到较少的可能匹配,因为每个子多边形'最小边界框远小于最小边界框的点集。然而,将这组可能的匹配与我的多边形相交仍然只能减少约25%的计算时间,因此它仍然是一个非常缓慢的过程。

我应该使用更好的空间索引方法吗?如果点和多边形具有相同的最小边界框,那么找到多边形内哪些点的最快方法是什么?

2 个答案:

答案 0 :(得分:2)

稍微复制问题的一个小例子

import pandas as pd
import shapely
import matplotlib.pyplot as plt

from matplotlib.collections import PatchCollection
from matplotlib.patches import Polygon
from shapely.geometry import Point
import seaborn as sns
import numpy as np

# some lon/lat points in a DataFrame
n = 1000000
data = {'lat':np.random.uniform(low=0.0, high=3.0, size=(n,)), 'lon':np.random.uniform(low=0.0, high=3.0, size=(n,))}
df = pd.DataFrame(data)

# the 'bounding' polygon
poly1 = shapely.geometry.Polygon([(1,1), (1.5,1.2), (2,.7), (2.1,1.2), (1.8,2.3), (1.6,1.8), (1.2,3)])
# poly2 = shapely.geometry.Polygon([(1,1), (1.3,1.6), (1.4,1.55), (1.5,1.2), (2,.7), (2.1,1.2), (1.8,2.3), (1.6,1.8), (1.2,3), (.8,1.5),(.91,1.3)])
# poly3 = shapely.geometry.Polygon([(1,1), (1.3,1.6), (1.4,1.55), (1.5,1.2), (2,.7), (2.1,1.2), (1.8,2.3), (1.6,1.8), (1.5,2), (1.4,2.5),(1.3,2.4), (1.2,3), (.8,2.8),(1,2.8),(1.3,2.2),(.7,1.5),(.66,1.4)])

# limit DataFrame to interior points
mask = [poly1.intersects(shapely.geometry.Point(lat,lon)) for lat,lon in zip(df.lat,df.lon)]
df = df[mask]

# plot bounding polygon
fig1, ax1 = sns.plt.subplots(1, figsize=(4,4))
patches  = PatchCollection([Polygon(poly1.exterior)], facecolor='red', linewidth=.5, alpha=.5)
ax1.add_collection(patches, autolim=True)

# plot the lat/lon points
df.plot(x='lat',y='lon', kind='scatter',ax=ax1)
plt.show()

在简单多边形上调用带有一百万个点的intersects()并不需要花费很多时间。使用poly1,我得到以下图像。在多边形内找到纬度/经度点不到10秒。仅绘制边界多边形顶部的内部点如下所示:

enter image description here

In [45]: %timeit mask = [Point(lat,lon).intersects(poly1) for lat,lon in zip(df.lat,df.lon)]
1 loops, best of 3: 9.23 s per loop

Poly3更大更有趣。新图像看起来像这样,通过瓶颈相交()线需要大约一分钟。

enter image description here

In [2]: %timeit mask = [poly3.intersects(shapely.geometry.Point(lat,lon)) for lat,lon in zip(df.lat,df.lon)]
1 loops, best of 3: 51.4 s per loop

因此,罪犯不一定是纬度/经度点的数量。同样糟糕的是边界多边形的复杂性。首先,我建议poly.simplify(),或者你可以采取任何措施来减少边界多边形中的点数(显然不会大幅改变它)。

接下来,我建议考虑一些概率方法。如果点p被全部在边界多边形内的点包围,那么p很可能也在边界多边形中。通常,在速度和准确度之间需要进行一些权衡,但也许它可以减少您需要检查的点数。我尝试使用k-nearest neighbors classifier

from sklearn.neighbors import KNeighborsClassifier

# make a knn object, feed it some training data
neigh = KNeighborsClassifier(n_neighbors=4)
df_short = df.sample(n=40000)
df_short['labels'] = np.array([poly3.intersects(shapely.geometry.Point(lat,lon)) for lat,lon in zip(df_short.lat,df_short.lon)])*1
neigh.fit(df_short[['lat','lon']], df_short['labels'])

# now use the training data to guess whether a point is in polygon or not
df['predict'] = neigh.predict(df[['lat','lon']])

给我这个形象。不完美,但此块的%timeit仅需3.62秒(n = 50000时为4.39秒),而检查每一点的时间约为50秒。

enter image description here

如果相反,我只想丢弃那些有30%几率进入多边形的点(只是抛弃明显的违规者并手工检查其余部分)。我可以使用knn regression

from sklearn.neighbors import KNeighborsRegressor
neigh = KNeighborsRegressor(n_neighbors=3, weights='distance')
#everything else using 'neigh' is the same as before

# only keep points with more than 30\% chance of being inside
df = df[df.predict>.30]

现在我只需要检查大约138000个点,如果我想使用intersects()检查每个点,这将非常快。

当然,如果我增加邻居的数量,或者训练集的大小,我仍然可以获得更清晰的图像。关于这种概率方法的一些好处是(1)它的算法,所以你可以将它扔在任何时髦的边界多边形上,(2)你可以轻松地上/下调整它的准确度,(3)它' sa更快,并且很好地扩展(至少对暴力更好)。

像机器学习中的很多东西一样,有100种方法可以做到。希望这可以帮助你找到有用的东西。这是另外一张具有以下设置的图片(使用分类器,而不是回归)。你可以看到它越来越好。

neigh = KNeighborsClassifier(n_neighbors=3, weights='distance')
df_short = df.sample(n=80000)

enter image description here

答案 1 :(得分:0)

总结问题:当多边形的边界框与点集合相同时,r-tree将每个点标识为可能的匹配,从而不提供加速。当与许多点和具有许多顶点的多边形结合时,交叉过程慢。

解决方案:从此geopandas r-tree spatial index tutorial开始,使用quadrat例程将多边形划分为子多边形。然后,对于每个子多边形,首先将它与点“a”相交。 r-tree索引获取一小组可能的匹配,然后将这些可能的匹配与子多边形相交以获得精确匹配的集合。这提供了大约100倍的加速。