Python 如果点和多边形具有相同的最小边界框,则在多边形内查找点的空间索引

Python 如果点和多边形具有相同的最小边界框,则在多边形内查找点的空间索引,python,gis,geospatial,shapely,geopandas,Python,Gis,Geospatial,Shapely,Geopandas,我有一个形状优美的多边形,代表洛杉矶市的边界。我在geopandas GeoDataFrame中还有一组约100万lat长的点,所有这些点都位于多边形的最小边界框内。其中一些点位于多边形本身内,但其他点不在多边形内。我只想保留洛杉矶边界内的那些点,由于洛杉矶的不规则形状,在其最小边界框内只有大约1/3的点在多边形本身内 如果点和多边形具有相同的最小边界框,那么使用Python识别这些点中哪些位于多边形内的最快方法是什么? 我尝试使用geopandas及其r树空间索引: sindex = gdf[

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

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

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

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树空间索引快速查找可能的匹配,然后查找多边形与这些可能匹配的精确交点。但是,由于多边形的最小边界框与点集的最小边界框相同,因此r树将每个点视为可能的匹配。因此,使用r树空间索引使交叉点的运行速度不会比没有空间索引时快。这种方法非常慢:大约需要30分钟才能完成

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


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

一个小例子来稍微重复一下这个问题

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,我得到下图。在多边形内查找lat/lon点不到10秒。仅在边界多边形顶部打印内部点如下所示:

Poly3更大更有趣。新图像如下所示,大约需要一分钟才能穿过瓶颈相交()线

因此,罪犯不一定是lat/lon点数。同样糟糕的是边界多边形的复杂性。首先,我建议您使用
poly.simplify()
,或者您可以做的任何事情来减少边界多边形中的点数(显然,不需要对其进行剧烈更改)

接下来,我建议考虑一些概率方法。如果一个点
p
被所有位于边界多边形内的点包围,则
p
也很有可能位于边界多边形内。一般来说,在速度和准确度之间有一点折衷,但也许这可以减少你需要检查的点数。以下是我的尝试:

给我这个图像。这并不完美,但是%timeit对于这个块只需要3.62秒(对于n=50000为4.39秒),而检查每个点大约需要50秒

如果相反,我只想删除,比如说,有30%机会在多边形中的点(只是扔掉明显的违规者,并用手检查其余的)。我可以使用:

现在我只需要检查138000个点,如果我想使用
intersects()
检查每个点,那么检查速度会非常快

当然,如果我增加邻居的数量或训练集的大小,我仍然可以得到更清晰的图像。这种概率方法的一些优点是:(1)它是算法,所以你可以把它扔到任何时髦的边界多边形上,(2)你可以很容易地上下调整它的精度,(3)它速度更快,伸缩性也很好(至少使用蛮力更好)

就像机器学习中的许多事情一样,有100种方法可以做到这一点。希望这能帮助你找到一些可行的方法。这里还有一张带有以下设置的图片(使用分类器,而不是回归)。你可以看到情况正在好转

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

举个小例子,稍微重复一下这个问题

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,我得到下图。在多边形内查找lat/lon点不到10秒。仅在边界多边形顶部打印内部点如下所示:

Poly3更大更有趣。新图像如下所示,大约需要一分钟才能穿过瓶颈相交()线

因此,罪犯不一定是lat/lon点数。同样糟糕的是边界多边形的复杂性。首先,我建议您使用
poly.simplify()
,或者您可以做的任何事情来减少边界多边形中的点数(显然,不需要对其进行剧烈更改)

接下来,我建议考虑一些概率方法。如果一个点
p
被所有位于边界多边形内的点包围,则
p
也很有可能位于边界多边形内。一般来说,在速度和准确度之间有一点折衷,但也许这可以减少你需要检查的点数。以下是我的尝试:

给我这个图像。这并不完美,但是%timeit对于这个块只需要3.62秒(对于n=50000为4.39秒),而检查每个点大约需要50秒

如果相反,我只想删除,比如说,有30%几率在多边形中的点(只是抛出
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']])
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]
neigh = KNeighborsClassifier(n_neighbors=3, weights='distance')
df_short = df.sample(n=80000)