Grafiği kullanarak şehir bloklarını bulmak şaşırtıcı değil. Temel olarak, bu, NP-tam bir problem olan en küçük halka grubunu (SSSR) bulmak anlamına gelir. Bu sorunun (ve ilgili sorunların) bir incelemesini burada bulabilirsiniz . Benzerleri, bir algoritma biri açıklama bunu çözmek için orada burada . Anlayabildiğim kadarıyla, networkx
(veya bu konuda python) karşılık gelen bir uygulama yoktur . Bu yaklaşımı kısaca denedim ve sonra bıraktım - beynim bugün bu tür işler için çizilmeye hazır değil. Bununla birlikte, bu sayfayı daha sonra ziyaret edebilecek ve SSSR'yi python'da bulan bir algoritmanın test edilmiş bir uygulamasını yayınlayabilecek herkese bir ödül vereceğim.
Bunun yerine farklı bir yaklaşım izledim ve grafiğin düzlemsel olduğu garanti ediliyor. Kısacası, bunu bir grafik problemi olarak ele almak yerine, bunu bir görüntü segmentasyonu problemi olarak ele alıyoruz. İlk olarak, görüntüdeki tüm bağlı bölgeleri buluyoruz. Daha sonra her bölgenin etrafındaki konturu belirleriz, görüntü koordinatlarındaki konturları tekrar boylamlara ve enlemlere dönüştürürüz.
Aşağıdaki içe aktarmalar ve işlev tanımları göz önüne alındığında:
#!/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]
Verileri yükleyin. Bunu tekrar tekrar test ediyorsanız, içe aktarma işlemlerini önbelleğe alın - aksi takdirde hesabınız yasaklanabilir. Burada deneyimden konuşma.
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')
Bir döngünün parçası olamayan düğümleri ve kenarları budanır. Bu adım kesinlikle gerekli değildir, ancak daha güzel konturlarla sonuçlanır.
H = k_core(G, 2)
fig1, ax1 = ox.plot_graph(H, node_size=0, edge_color='k', edge_linewidth=1)
Grafiği resme dönüştürün ve bağlı bölgeleri bulun:
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)
Etiketli her bölge için konturu bulun ve kontur piksel koordinatlarını tekrar veri koordinatlarına dönüştürün.
# 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)
Orijinal grafikte konturun içine (veya üstüne) düşen tüm noktaları belirleyin.
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)
İki bloğun komşu olup olmadığını anlamak oldukça kolaydır. Bir düğümü paylaşıp paylaşmadıklarını kontrol etmeniz yeterlidir:
if set(nodes_inside_block_1) & set(nodes_inside_block_2): # empty set evaluates to False
print("Blocks are neighbors.")