Das Auffinden von Stadtblöcken mithilfe des Diagramms ist überraschenderweise nicht trivial. Grundsätzlich bedeutet dies, den kleinsten Satz kleinster Ringe (SSSR) zu finden, was ein NP-vollständiges Problem darstellt. Eine Übersicht über dieses Problem (und verwandte Probleme) finden Sie hier . Auf SO gibt es eine Beschreibung eines Algorithmus, um ihn hier zu lösen . Soweit ich das beurteilen kann, gibt es in networkx
(oder in Python) keine entsprechende Implementierung . Ich habe diesen Ansatz kurz ausprobiert und ihn dann aufgegeben - mein Gehirn ist für diese Art von Arbeit heute nicht mehr auf dem neuesten Stand. That being said, werde ich eine Prämie niemandem vergeben , die diese Seite zu einem späteren Zeitpunkt besuchen könnte und eine getestete Implementierung eines Algorithmus schreiben, der die SSSR in Python findet.
Ich habe stattdessen einen anderen Ansatz verfolgt und die Tatsache genutzt, dass der Graph garantiert planar ist. Kurz gesagt, anstatt dies als Diagrammproblem zu behandeln, behandeln wir dies als Bildsegmentierungsproblem. Zunächst finden wir alle verbundenen Regionen im Bild. Wir bestimmen dann die Kontur um jede Region und transformieren die Konturen in Bildkoordinaten zurück in Längen- und Breitengrade.
Angesichts der folgenden Importe und Funktionsdefinitionen:
#!/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]
Laden Sie die Daten. Zwischenspeichern Sie die Importe, wenn Sie dies wiederholt testen. Andernfalls kann Ihr Konto gesperrt werden. Ich spreche aus Erfahrung hier.
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')
Beschneiden Sie Knoten und Kanten, die nicht Teil eines Zyklus sein können. Dieser Schritt ist nicht unbedingt erforderlich, führt aber zu schöneren Konturen.
H = k_core(G, 2)
fig1, ax1 = ox.plot_graph(H, node_size=0, edge_color='k', edge_linewidth=1)
Konvertieren Sie das Diagramm in ein Bild und suchen Sie nach verbundenen Regionen:
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)
Suchen Sie für jeden beschrifteten Bereich die Kontur und konvertieren Sie die Konturpixelkoordinaten zurück in Datenkoordinaten.
# 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)
Bestimmen Sie alle Punkte im Originaldiagramm, die innerhalb (oder auf) der Kontur liegen.
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)
Es ist ziemlich einfach herauszufinden, ob zwei Blöcke Nachbarn sind. Überprüfen Sie einfach, ob sie einen Knoten gemeinsam nutzen:
if set(nodes_inside_block_1) & set(nodes_inside_block_2): # empty set evaluates to False
print("Blocks are neighbors.")