首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >使用Geopandas计算到最近要素的距离

使用Geopandas计算到最近要素的距离
EN

Stack Overflow用户
提问于 2015-06-10 02:31:30
回答 3查看 23.4K关注 0票数 15

我希望使用Geopandas / Shapely实现与ArcPy Generate Near Table相当的功能。我对Geopandas和Shapely非常陌生,并且已经开发出了一种有效的方法,但我想知道是否有更有效的方法来做到这一点。

我有两个点文件数据集-人口普查块质心和餐馆。我正在寻找,对于每个人口普查区的质心,到它最近的餐厅的距离。对于同一家餐厅在多个街区是最近的餐厅,没有任何限制。

对我来说,这变得有点复杂的原因是因为Geopandas Distance function是按元素计算的,基于索引进行匹配。因此,我的一般方法是将Restaurants文件转换为多点文件,然后将块文件的索引设置为相同的值。那么所有的街区质心和餐馆都有相同的索引值。

代码语言:javascript
运行
复制
import pandas as pd
import geopandas as gpd
from shapely.geometry import Polygon, Point, MultiPoint

现在阅读Block Centroid和Restaurant Shapefiles:

代码语言:javascript
运行
复制
Blocks=gpd.read_file(BlockShp)
Restaurants=gpd.read_file(RestaurantShp)

由于Geopandas distance函数按元素计算距离,因此我将餐厅GeoSeries转换为MultiPoint GeoSeries:

代码语言:javascript
运行
复制
RestMulti=gpd.GeoSeries(Restaurants.unary_union)
RestMulti.crs=Restaurants.crs
RestMulti.reset_index(drop=True)

然后,我将块的索引设置为等于0(与Restaurants多点的值相同),作为逐个元素计算的变通方法。

代码语言:javascript
运行
复制
Blocks.index=[0]*len(Blocks)

最后,我使用Geopandas距离函数来计算每个Block质心到最近餐厅的距离。

代码语言:javascript
运行
复制
Blocks['Distance']=Blocks.distance(RestMulti)

请就如何改进这一方面提供任何建议。我并不局限于使用Geopandas或Shapely,但我希望学习ArcPy的替代品。

谢谢你的帮助!

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2015-09-19 06:59:30

如果我正确理解你的问题,街区和餐馆可以有非常不同的维度。出于这个原因,尝试通过重新索引来强制转换为表格式可能是一种糟糕的方法。

我只是在街区上循环,得到到餐馆的最短距离(就像@shongololo建议的那样)。

我会稍微通用点(因为我已经写下了这段代码),并且从点到线的距离,但相同的代码应该从点到点,或者从多边形到多边形。我将从点的GeoDataFrame开始,然后创建一个到线的距离最小的新列。

代码语言:javascript
运行
复制
%matplotlib inline
import matplotlib.pyplot as plt
import shapely.geometry as geom
import numpy as np
import pandas as pd
import geopandas as gpd

lines = gpd.GeoSeries(
    [geom.LineString(((1.4, 3), (0, 0))),
        geom.LineString(((1.1, 2.), (0.1, 0.4))),
        geom.LineString(((-0.1, 3.), (1, 2.)))])

# 10 points
n  = 10
points = gpd.GeoSeries([geom.Point(x, y) for x, y in np.random.uniform(0, 3, (n, 2))])

# Put the points in a dataframe, with some other random column
df_points = gpd.GeoDataFrame(np.array([points, np.random.randn(n)]).T)
df_points.columns = ['Geometry', 'Property1']

points.plot()
lines.plot()

现在得到点到线的距离,只保存每个点的最小距离(参见下面的apply版本)

代码语言:javascript
运行
复制
min_dist = np.empty(n)
for i, point in enumerate(points):
    min_dist[i] = np.min([point.distance(line) for line in lines])
df_points['min_dist_to_lines'] = min_dist
df_points.head(3)

这给了我们

代码语言:javascript
运行
复制
    Geometry                                       Property1    min_dist_to_lines
0   POINT (0.2479424516236574 2.944916965334865)    2.621823    0.193293
1   POINT (1.465768457667432 2.605673714922998)     0.6074484   0.226353
2   POINT (2.831645235202689 1.125073838462032)     0.657191    1.940127

--编辑

(取自github的一个问题)使用apply更好,并且与在pandas中使用它的方式更一致

代码语言:javascript
运行
复制
def min_distance(point, lines):
    return lines.distance(point).min()

df_points['min_dist_to_lines'] = df_points.geometry.apply(min_distance, df_lines)

编辑:至少到2019-10-04为止,pandas中的更改似乎需要在最后一个代码块中使用不同的输入,使用.apply()中的args参数

代码语言:javascript
运行
复制
df_points['min_dist_to_lines'] = df_points.geometry.apply(min_distance, args=(df_lines,))
票数 21
EN

Stack Overflow用户

发布于 2020-08-01 02:27:06

我将使用不同维度的geopandas中的两个样本数据集进行演示。

代码语言:javascript
运行
复制
import geopandas as gpd

# read geodata for five nyc boroughs
gdf_nyc = gpd.read_file(gpd.datasets.get_path('nybb'))
# read geodata for international cities
gdf_cities = gpd.read_file(gpd.datasets.get_path('naturalearth_cities'))

# convert to a meter projection
gdf_nyc.to_crs(epsg=3857, inplace=True)
gdf_cities.to_crs(epsg=3857, inplace=True)

我们可以简单地将lambda函数应用于GeoSeries。例如,如果我们想要获得每个纽约市行政区(多边形)与其最近的国际城市(点)之间的最小距离。我们可以执行以下操作:

代码语言:javascript
运行
复制
gdf_nyc.geometry.apply(lambda x: gdf_cities.distance(x).min())

这将会给我们

代码语言:javascript
运行
复制
0    384422.953323
1    416185.725507
2    412520.308816
3    419511.323677
4    440292.945096
Name: geometry, dtype: float64

同样,如果我们想要每个国际城市与其最近的纽约市之间的最小距离。我们可以执行以下操作:

代码语言:javascript
运行
复制
gdf_cities.geometry.apply(lambda x: gdf_nyc.distance(x).min())

这将会给我们

代码语言:javascript
运行
复制
0      9.592104e+06
1      9.601345e+06
2      9.316354e+06
3      8.996945e+06
4      2.614927e+07
           ...     
197    1.177410e+07
198    2.377188e+07
199    8.559704e+06
200    8.902146e+06
201    2.034579e+07
Name: geometry, Length: 202, dtype: float64

备注:

  1. 在计算距离之前,将GeoDataFrame转换为笛卡尔投影。在本例中,我使用了epsg:3857,因此距离将以米为单位。如果使用椭球体(基于经度/纬度)投影,则结果将是度数。在获得polygons.
  2. There质心之类的任何东西之前,首先转换投影只是两个点之间的一个距离。当你想要得到一个点和一条线之间的距离时,.distance()方法返回的最小距离是有意义的。换句话说,.distance()方法可以计算任意两个geo-objects.
  3. When之间的距离如果GeoDataFrame中有多个geometry列,请确保将lambda函数应用于所需的GeoSeries,并从所需的GeoSeries调用.distance()方法。在本例中,我直接从GeoDataFrame调用该方法,因为它们都只有一个GeoSeries列。
票数 4
EN

Stack Overflow用户

发布于 2018-12-06 01:36:02

你的代码缺少一个细节,args = (df_lines)

代码语言:javascript
运行
复制
def min_distance(point, lines):
    return lines.distance(point).min()

df_points['min_dist_to_lines'] = df_points.geometry.apply(min_distance, args=(df_lines,))# Notice the change to this line
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/30740046

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档