web-dev-qa-db-ja.com

ストリートデータ(グラフ)での近隣(クリーク)の検索

都市の近隣をグラフ上のポリゴンとして自動的に定義する方法を探しています。

私の近所の定義には2つの部分があります。

  1. ブロック:いくつかの道路の間で囲まれたエリアで、道路(エッジ)と交差点(ノード)の数は最低3つです(a三角形)。
  2. A Neighbourhood:特定のブロックについて、そのブロックに直接隣接するすべてのブロックとブロック自体。

例については、この図を参照してください。

enter image description here

例えば。 B4は、7つのノードとそれらを接続する6つのエッジによって定義されるブロックです。ここでのほとんどの例と同様に、他のブロックは4つのノードとそれらを接続する4つのエッジによって定義されます。また、B1neighbourhoodにはB2(およびその逆)一方、B2にはB3

OSMからストリートデータを取得するために osmnx を使用しています。

  1. osmnxとnetworkxを使用して、グラフを走査して各ブロックを定義するノードとエッジを見つけるにはどうすればよいですか
  2. 各ブロックについて、隣接するブロックをどのように見つけることができますか?

私は、グラフと座標(緯度、経度)のペアを入力として受け取り、関連するブロックを識別して、そのブロックのポリゴンと上記で定義した近傍を返すコードに向けて取り組んでいます。

以下は、マップを作成するために使用されるコードです。

import osmnx as ox
import networkx as nx
import matplotlib.pyplot as plt

G = ox.graph_from_address('Nørrebrogade 20, Copenhagen Municipality',
                          network_type='all', 
                          distance=500)

そして、ノードと次数の異なるクリークを見つけようとする私の試み。

def plot_cliques(graph, number_of_nodes, degree):
    ug = ox.save_load.get_undirected(graph)
    cliques = nx.find_cliques(ug)
    cliques_nodes = [clq for clq in cliques if len(clq) >= number_of_nodes]
    print("{} cliques with more than {} nodes.".format(len(cliques_nodes), number_of_nodes))
    nodes = set(n for clq in cliques_nodes for n in clq)
    h = ug.subgraph(nodes)
    deg = nx.degree(h)
    nodes_degree = [n for n in nodes if deg[n] >= degree]
    k = h.subgraph(nodes_degree)
    nx.draw(k, node_size=5)

関連するかもしれない理論:

無向グラフのすべてのサイクルを列挙

10
tmo

これは Hashemi Emadのアイデア の実装です。開始位置がきつい円で反時計回りに進む方法が存在するように選択されている限り、うまく機能します。一部のエッジ、特にマップの外側では、これは不可能です。良い開始位置を選択する方法、またはソリューションをフィルターする方法はわかりませんが、おそらく他の誰かが持っています。

作業例(Edge(1204573687、4555480822)から開始):

enter image description here

このアプローチが機能しない例(Edge(1286684278、5818325197)以降):

enter image description here

コード

#!/usr/bin/env python
# coding: utf-8

"""
Find house blocks in osmnx graphs.
"""

import numpy as np
import networkx as nx
import osmnx as ox

import matplotlib.pyplot as plt; plt.ion()

from matplotlib.path import Path
from matplotlib.patches import PathPatch


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 get_vector(G, n1, n2):
    dx = np.diff([G.nodes.data()[n]['x'] for n in (n1, n2)])
    dy = np.diff([G.nodes.data()[n]['y'] for n in (n1, n2)])
    return np.array([dx, dy])


def angle_between(v1, v2):
    # https://stackoverflow.com/a/31735642/2912349
    ang1 = np.arctan2(*v1[::-1])
    ang2 = np.arctan2(*v2[::-1])
    return (ang1 - ang2) % (2 * np.pi)


def step_counterclockwise(G, Edge, path):
    start, stop = Edge
    v1 = get_vector(G, stop, start)
    neighbors = set(G.neighbors(stop))
    candidates = list(set(neighbors) - set([start]))
    if not candidates:
        raise Exception("Ran into a dead end!")
    else:
        angles = np.zeros_like(candidates, dtype=float)
        for ii, neighbor in enumerate(candidates):
            v2 = get_vector(G, stop, neighbor)
            angles[ii] = angle_between(v1, v2)
        next_node = candidates[np.argmin(angles)]
        if next_node in path:
            # next_node might not be the same as the first node in path;
            # therefor, we backtrack until we end back at next_node
            closed_path = [next_node]
            for node in path[::-1]:
                closed_path.append(node)
                if node == next_node:
                    break
            return closed_path[::-1] # reverse to have counterclockwise path
        else:
            path.append(next_node)
            return step_counterclockwise(G, (stop, next_node), path)


def get_city_block_patch(G, boundary_nodes, *args, **kwargs):
    xy = []
    for node in boundary_nodes:
        x = G.nodes.data()[node]['x']
        y = G.nodes.data()[node]['y']
        xy.append((x, y))
    path = Path(xy, closed=True)
    return PathPatch(path, *args, **kwargs)


if __name__ == '__main__':

    # --------------------------------------------------------------------------------
    # load data

    # # DO CACHE RESULTS -- otherwise you can get banned for repeatedly querying the same address
    # 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')

    # --------------------------------------------------------------------------------
    # Prune nodes and edges that should/can not be part of a cycle;
    # this also reduces the chance of running into a dead end when stepping counterclockwise

    H = k_core(G, 2)

    # --------------------------------------------------------------------------------
    # pick an Edge and step counterclockwise until you complete a circle

    # random Edge
    total_edges = len(H.edges)
    idx = np.random.choice(total_edges)
    start, stop, _ = list(H.edges)[idx]

    # good Edge
    # start, stop = 1204573687, 4555480822

    # bad Edge
    # start, stop = 1286684278, 5818325197

    steps = step_counterclockwise(H, (start, stop), [start, stop])

    # --------------------------------------------------------------------------------
    # plot

    patch = get_city_block_patch(G, steps, facecolor='red', edgecolor='red', zorder=-1)

    node_size = [100 if node in steps else 20 for node in G.nodes]
    node_color = ['crimson' if node in steps else 'black' for node in G.nodes]
    fig1, ax1 = ox.plot_graph(G, node_size=node_size, node_color=node_color, Edge_color='k', Edge_linewidth=1)
    ax1.add_patch(patch)
    fig1.savefig('city_block.png')
0
Paul Brodersen