Вычислить расстояние до ближайшего объекта с помощью Geopandas

Я ищу, чтобы сделать эквивалент ArcPy Создать Рядом Таблицу использование Geopandas / Shapely. Я очень новичок в Geopandas и Shapely и разработал методологию, которая работает, но мне интересно, есть ли более эффективный способ сделать это.

у меня есть два набора данных точечных файлов - центроиды блока переписи и рестораны. Я ищу, чтобы найти, для каждого центроида блока переписи, расстояние до ближайшего ресторана. Нет никаких ограничений с точки зрения того же ресторан-ближайший ресторан на несколько кварталов.

причина, по которой это становится немного сложнее для меня, заключается в том, что функция расстояния Geopandas вычисляет поэлементно, сопоставления, основанные на индексе. Поэтому моя общая методология состоит в том, чтобы превратить файл ресторанов в многоточечный файл, а затем установить индекс файла блоков для всех одинаковое значение. Тогда все центроиды блоков и рестораны имеют одинаковый индекс значение.

import pandas as pd
import geopandas as gpd
from shapely.geometry import Polygon, Point, MultiPoint

теперь читайте в блоке Centroid и ресторане Шейп-файлы:

Blocks=gpd.read_file(BlockShp)
Restaurants=gpd.read_file(RestaurantShp)

поскольку функция расстояния Geopandas вычисляет расстояние по элементам, я преобразую ресторан GeoSeries в многоточечный GeoSeries:

RestMulti=gpd.GeoSeries(Restaurants.unary_union)
RestMulti.crs=Restaurants.crs
RestMulti.reset_index(drop=True)

затем я установил индекс для блоков равным 0 (то же значение, что и для ресторанов multipoint) в качестве работы для elementwise расчета.

Blocks.index=[0]*len(Blocks)

наконец, я использую функцию расстояния Geopandas для рассчитайте расстояние до ближайшего ресторана для каждого блока центроид.

Blocks['Distance']=Blocks.distance(RestMulti)

пожалуйста, предложите любые предложения о том, как любой аспект этого может быть улучшен. Я не привязан к использованию Geopandas или Shapely, но я ищу альтернативу ArcPy.

Спасибо за помощь!

1 ответов


если я правильно понимаю вашу проблему, блоки и рестораны могут иметь очень разные размеры. По этой причине, вероятно, это плохой подход, чтобы попытаться заставить формат таблицы путем переиндексирования.

Я бы просто зациклился на блоках и получил минимальное расстояние до ресторанов (как и предлагал @shongololo).

Я собираюсь быть немного более общим (потому что у меня уже записан этот код) и делать расстояние от точек до строк, но тот же код должен работать от точек к точкам или от полигонов к полигонам. Я начну с GeoDataFrame для точек, и я создам новый столбец, который имеет минимальное расстояние до линий.

%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()

enter image description here

теперь получите расстояние от точек до линий и сохраните только минимальное расстояние для каждой точки (см. ниже для версии с apply)

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)

что дает

    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:

def min_distance(point, lines):
    return lines.distance(point).min()

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