Tìm các khối thành phố bằng cách sử dụng biểu đồ là không đáng ngạc nhiên. Về cơ bản, số tiền này để tìm ra tập hợp các vòng nhỏ nhất (SSSR) nhỏ nhất, đây là một vấn đề hoàn chỉnh NP. Một đánh giá về vấn đề này (và các vấn đề liên quan) có thể được tìm thấy ở đây . Trên SO, có một mô tả về một thuật toán để giải quyết nó ở đây . Theo như tôi có thể nói, không có triển khai tương ứng trong networkx
(hoặc trong python cho vấn đề đó). Tôi đã thử cách tiếp cận này một thời gian ngắn và sau đó từ bỏ nó - bộ não của tôi không thể trầy xước cho loại công việc đó ngày hôm nay. Điều đó đang được nói, tôi sẽ trao tiền thưởng cho bất kỳ ai có thể truy cập trang này vào một ngày sau đó và đăng một triển khai thử nghiệm của một thuật toán tìm thấy SSSR trong python.
Thay vào đó, tôi đã theo đuổi một cách tiếp cận khác, tận dụng thực tế là đồ thị được đảm bảo là phẳng. Tóm lại, thay vì coi đây là vấn đề đồ thị, chúng tôi coi đây là vấn đề phân đoạn hình ảnh. Đầu tiên, chúng tôi tìm thấy tất cả các khu vực kết nối trong hình ảnh. Sau đó, chúng tôi xác định đường viền xung quanh từng vùng, biến đổi các đường viền trong tọa độ hình ảnh trở lại kinh độ và vĩ độ.
Đưa ra các định nghĩa nhập khẩu và hàm sau:
#!/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]
Tải dữ liệu. Làm bộ nhớ cache khi nhập, nếu kiểm tra điều này nhiều lần - nếu không tài khoản của bạn có thể bị cấm. Nói từ kinh nghiệm ở đây.
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')
Nút tỉa và các cạnh không thể là một phần của một chu kỳ. Bước này không thực sự cần thiết nhưng dẫn đến đường viền đẹp hơn.
H = k_core(G, 2)
fig1, ax1 = ox.plot_graph(H, node_size=0, edge_color='k', edge_linewidth=1)
Chuyển đổi cốt truyện thành hình ảnh và tìm các khu vực được kết nối:
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)
Đối với mỗi vùng được gắn nhãn, hãy tìm đường viền và chuyển đổi tọa độ pixel đường viền trở lại tọa độ dữ liệu.
# 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ác định tất cả các điểm trong biểu đồ ban đầu nằm bên trong (hoặc trên) đường viền.
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)
Tìm ra nếu hai khối là hàng xóm là khá dễ dàng. Chỉ cần kiểm tra nếu họ chia sẻ một nút:
if set(nodes_inside_block_1) & set(nodes_inside_block_2): # empty set evaluates to False
print("Blocks are neighbors.")