그래프를 사용하여 도시 블록을 찾는 것은 놀라운 일이 아닙니다. 기본적으로 이것은 NP- 완전 문제인 가장 작은 링 세트 (SSSR)를 찾는 것입니다. 이 문제 (및 관련 문제)에 대한 검토는 여기 에서 찾을 수 있습니다 . SO에는 여기 에서 해결하는 알고리즘에 대한 설명이 있습니다 . 내가 알 수있는 한, networkx
(또는 그 문제에 대한 파이썬 )에는 해당 구현이 없습니다 . 나는이 접근법을 잠깐 시도한 후 버렸다. 오늘날 나의 두뇌는 그런 종류의 작업에 흠집이 없다. 즉 , 나중에이 페이지를 방문하여 Python에서 SSSR을 찾는 알고리즘의 테스트 구현을 게시 할 수있는 사람에게 현상금을 수여합니다.
대신 그래프가 평면임을 보장한다는 사실을 활용하여 다른 접근법을 추구했습니다. 간단히, 이것을 그래프 문제로 취급하는 대신, 이미지 분할 문제로 취급합니다. 먼저 이미지에서 연결된 모든 영역을 찾습니다. 그런 다음 각 영역 주위의 윤곽을 결정하고 이미지 좌표의 윤곽을 경도와 위도로 다시 변환합니다.
다음과 같은 가져 오기 및 함수 정의가 제공됩니다.
#!/usr/bin/env python
# coding: utf-8
"""
Find house blocks in osmnx graphs.
"""
import numpy as np
import osmnx as ox
import networkx as nx
import matplotlib.pyplot as plt
from matplotlib.path import Path
from matplotlib.patches import PathPatch
from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
from skimage.measure import label, find_contours, points_in_poly
from skimage.color import label2rgb
ox.config(log_console=True, use_cache=True)
def k_core(G, k):
H = nx.Graph(G, as_view=True)
H.remove_edges_from(nx.selfloop_edges(H))
core_nodes = nx.k_core(H, k)
H = H.subgraph(core_nodes)
return G.subgraph(core_nodes)
def plot2img(fig):
# remove margins
fig.subplots_adjust(left=0, bottom=0, right=1, top=1, wspace=0, hspace=0)
# convert to image
# https://stackoverflow.com/a/35362787/2912349
# https://stackoverflow.com/a/54334430/2912349
canvas = FigureCanvas(fig)
canvas.draw()
img_as_string, (width, height) = canvas.print_to_buffer()
as_rgba = np.fromstring(img_as_string, dtype='uint8').reshape((height, width, 4))
return as_rgba[:,:,:3]
데이터를로드하십시오. 반복적으로 테스트하는 경우 가져 오기를 캐시하십시오. 그렇지 않으면 계정이 차단 될 수 있습니다. 여기에서 경험을 말하면.
G = ox.graph_from_address('Nørrebrogade 20, Copenhagen Municipality',
network_type='all', distance=500)
G_projected = ox.project_graph(G)
ox.save_graphml(G_projected, filename='network.graphml')
# G = ox.load_graphml('network.graphml')
사이클의 일부가 될 수없는 노드와 모서리를 제거합니다. 이 단계는 꼭 필요한 것은 아니지만 윤곽이 더 좋습니다.
H = k_core(G, 2)
fig1, ax1 = ox.plot_graph(H, node_size=0, edge_color='k', edge_linewidth=1)
플롯을 이미지로 변환하고 연결된 영역을 찾습니다.
img = plot2img(fig1)
label_image = label(img > 128)
image_label_overlay = label2rgb(label_image[:,:,0], image=img[:,:,0])
fig, ax = plt.subplots(1,1)
ax.imshow(image_label_overlay)
레이블이 지정된 각 영역에 대해 윤곽을 찾고 윤곽 픽셀 좌표를 다시 데이터 좌표로 변환합니다.
# using a large region here as an example;
# however we could also loop over all unique labels, i.e.
# for ii in np.unique(labels.ravel()):
ii = np.argsort(np.bincount(label_image.ravel()))[-5]
mask = (label_image[:,:,0] == ii)
contours = find_contours(mask.astype(np.float), 0.5)
# Select the largest contiguous contour
contour = sorted(contours, key=lambda x: len(x))[-1]
# display the image and plot the contour;
# this allows us to transform the contour coordinates back to the original data cordinates
fig2, ax2 = plt.subplots()
ax2.imshow(mask, interpolation='nearest', cmap='gray')
ax2.autoscale(enable=False)
ax2.step(contour.T[1], contour.T[0], linewidth=2, c='r')
plt.close(fig2)
# first column indexes rows in images, second column indexes columns;
# therefor we need to swap contour array to get xy values
contour = np.fliplr(contour)
pixel_to_data = ax2.transData + ax2.transAxes.inverted() + ax1.transAxes + ax1.transData.inverted()
transformed_contour = pixel_to_data.transform(contour)
transformed_contour_path = Path(transformed_contour, closed=True)
patch = PathPatch(transformed_contour_path, facecolor='red')
ax1.add_patch(patch)
오리지널 그래프에서 컨투어 내부 (또는 위에있는)에있는 모든 포인트를 결정합니다.
x = G.nodes.data('x')
y = G.nodes.data('y')
xy = np.array([(x[node], y[node]) for node in G.nodes])
eps = (xy.max(axis=0) - xy.min(axis=0)).mean() / 100
is_inside = transformed_contour_path.contains_points(xy, radius=-eps)
nodes_inside_block = [node for node, flag in zip(G.nodes, is_inside) if flag]
node_size = [50 if node in nodes_inside_block else 0 for node in G.nodes]
node_color = ['r' if node in nodes_inside_block else 'k' for node in G.nodes]
fig3, ax3 = ox.plot_graph(G, node_color=node_color, node_size=node_size)
두 블록이 이웃인지 알아내는 것은 매우 쉽습니다. 노드를 공유하는지 확인하십시오.
if set(nodes_inside_block_1) & set(nodes_inside_block_2): # empty set evaluates to False
print("Blocks are neighbors.")