Пример #1
0
plt.cla()
plt.clf()
plot.plot_nX(G_decomposed, path='graph_decomposed.png', dpi=150)

plt.cla()
plt.clf()
G_dual = graphs.nX_to_dual(G_simple)
plot.plot_nX_primal_or_dual(G_simple, G_dual, 'graph_dual.png', dpi=150)

# graph cleanup examples
osm_json = mock.mock_osm_data()
G_messy = graphs.nX_from_osm(osm_json=osm_json)
G_messy = graphs.nX_wgs_to_utm(G_messy)
G_messy = graphs.nX_simple_geoms(G_messy)
G_messy = graphs.nX_remove_filler_nodes(G_messy)
G_messy = graphs.nX_remove_dangling_nodes(G_messy)
G_messy_decomp = graphs.nX_decompose(G_messy, 20)
plt.cla()
plt.clf()
plot.plot_nX(G_messy_decomp, 'graph_messy.png', dpi=150, figsize=(20, 20))

# spatial cleanup
G_clean_spatial = graphs.nX_consolidate_spatial(G_messy_decomp)
plt.cla()
plt.clf()
plot.plot_nX(G_clean_spatial,
             'graph_clean_spatial.png',
             dpi=150,
             figsize=(20, 20))
Пример #2
0
def test_nX_consolidate():
    # create a test graph
    G = nx.Graph()
    nodes = [
        (0, {'x': 700620, 'y': 5719720}),
        (1, {'x': 700620, 'y': 5719700}),
        (2, {'x': 700660, 'y': 5719720}),
        (3, {'x': 700660, 'y': 5719700}),
        (4, {'x': 700660, 'y': 5719660}),
        (5, {'x': 700700, 'y': 5719800}),
        (6, {'x': 700720, 'y': 5719800}),
        (7, {'x': 700700, 'y': 5719720}),
        (8, {'x': 700720, 'y': 5719720}),
        (9, {'x': 700700, 'y': 5719700}),
        (10, {'x': 700720, 'y': 5719700}),
        (11, {'x': 700700, 'y': 5719620}),
        (12, {'x': 700720, 'y': 5719620}),
        (13, {'x': 700760, 'y': 5719760}),
        (14, {'x': 700800, 'y': 5719760}),
        (15, {'x': 700780, 'y': 5719720}),
        (16, {'x': 700780, 'y': 5719700}),
        (17, {'x': 700840, 'y': 5719720}),
        (18, {'x': 700840, 'y': 5719700})]
    edges = [
        (0, 2),
        (0, 2),
        (1, 3),
        (2, 3),
        (2, 7),
        (3, 4),
        (3, 9),
        (5, 7),
        (6, 8),
        (7, 8),
        (7, 9),
        (8, 10),
        (8, 15),
        (9, 11),
        (9, 10),
        (10, 12),
        (10, 16),
        (13, 14),
        (13, 15),
        (14, 15),
        (15, 16),
        (15, 17),
        (16, 18)
    ]
    G.add_nodes_from(nodes)
    G.add_edges_from(edges)

    G = graphs.nX_simple_geoms(G)
    # behaviour confirmed visually
    # from cityseer.util import plot
    # plot.plot_nX(G, labels=True)

    # simplify first to test lollipop self-loop from node 15
    G = graphs.nX_remove_filler_nodes(G)
    # plot.plot_nX(G, labels=True, figsize=(10, 10), dpi=150)
    G_merged_parallel = graphs.nX_consolidate_parallel(G, buffer_dist=25)
    # plot.plot_nX(G_merged_parallel, labels=True, figsize=(10, 10), dpi=150)

    assert G_merged_parallel.number_of_nodes() == 8
    assert G_merged_parallel.number_of_edges() == 8

    node_coords = []
    for n, d in G_merged_parallel.nodes(data=True):
        node_coords.append((d['x'], d['y']))
    assert node_coords == [
        (700660, 5719660),
        (700620.0, 5719710.0),
        (700660.0, 5719710.0),
        (700710.0, 5719800.0),
        (700710.0, 5719710.0),
        (700710.0, 5719620.0),
        (700780.0, 5719710.0),
        (700840.0, 5719710.0)]

    edge_lens = []
    for s, e, d in G_merged_parallel.edges(data=True):
        edge_lens.append(d['geom'].length)
    assert edge_lens == [50.0, 40.0, 50.0, 90.0, 90.0, 70.0, 147.70329614269008, 60.0]

    G_merged_spatial = graphs.nX_consolidate_spatial(G, buffer_dist=25)
    # plot.plot_nX(G_merged_spatial, labels=True)

    assert G_merged_spatial.number_of_nodes() == 8
    assert G_merged_spatial.number_of_edges() == 8

    node_coords = []
    for n, d in G_merged_spatial.nodes(data=True):
        node_coords.append((d['x'], d['y']))
    assert node_coords == [
        (700660, 5719660),
        (700620.0, 5719710.0),
        (700660.0, 5719700.0),
        (700710.0, 5719800.0),
        (700710.0, 5719710.0),
        (700710.0, 5719620.0),
        (700780.0, 5719720.0),
        (700840.0, 5719710.0)]

    edge_lens = []
    for s, e, d in G_merged_spatial.edges(data=True):
        edge_lens.append(d['geom'].length)
    assert edge_lens == [40.0, 41.23105625617661, 50.99019513592785, 90.0, 90.0, 70.71067811865476, 129.4427190999916,
                         60.8276253029822]

    # visual tests on OSM data
    # TODO: can furnish more extensive tests, e.g. to verify veracity of new geoms
    osm_json = mock.mock_osm_data()
    g_1 = graphs.nX_from_osm(osm_json=osm_json)

    osm_json_alt = mock.mock_osm_data(alt=True)
    g_2 = graphs.nX_from_osm(osm_json=osm_json_alt)

    for g in [g_1, g_2]:
        G_utm = graphs.nX_wgs_to_utm(g)
        G = graphs.nX_simple_geoms(G_utm)
        G = graphs.nX_remove_filler_nodes(G)
        G = graphs.nX_remove_dangling_nodes(G)
        # G_decomp = graphs.nX_decompose(G, 25)
        # from cityseer.util import plot
        # plot.plot_nX(G, figsize=(10, 10), dpi=150)
        G_spatial = graphs.nX_consolidate_spatial(G, buffer_dist=15)
        # plot.plot_nX(G_spatial, figsize=(10, 10), dpi=150)
        G_parallel = graphs.nX_consolidate_parallel(G, buffer_dist=14)
Пример #3
0
def test_nX_decompose():
    # check that missing geoms throw an error
    G = mock.mock_graph()
    with pytest.raises(KeyError):
        graphs.nX_decompose(G, 20)

    # check that non-LineString geoms throw an error
    G = mock.mock_graph()
    for s, e in G.edges():
        G[s][e]['geom'] = geometry.Point([G.nodes[s]['x'], G.nodes[s]['y']])
    with pytest.raises(TypeError):
        graphs.nX_decompose(G, 20)

    # test decomposition
    G = mock.mock_graph()
    G = graphs.nX_simple_geoms(G)
    # first clean the graph to strip disconnected looping component
    # this gives a start == end node situation for testing
    G_simple = graphs.nX_remove_filler_nodes(G)
    G_decompose = graphs.nX_decompose(G_simple, 20)

    # from cityseer.util import plot
    # plot.plot_nX(G_simple, labels=True)
    # plot.plot_nX(G_decompose)
    assert nx.number_of_nodes(G_decompose) == 661
    assert nx.number_of_edges(G_decompose) == 682

    # check that total lengths are the same
    G_lens = 0
    for s, e, e_data in G_simple.edges(data=True):
        G_lens += e_data['geom'].length
    G_d_lens = 0
    for s, e, e_data in G_decompose.edges(data=True):
        G_d_lens += e_data['geom'].length
    assert np.allclose(G_lens, G_d_lens, atol=0.001, rtol=0)

    # check that all ghosted edges have one or two edges
    for n, n_data in G_decompose.nodes(data=True):
        if 'ghosted' in n_data and n_data['ghosted']:
            nbs = list(G_decompose.neighbors(n))
            assert len(nbs) == 1 or len(nbs) == 2

    # check that all new nodes are ghosted
    for n, n_data in G_decompose.nodes(data=True):
        if not G_simple.has_node(n):
            assert n_data['ghosted']

    # check that geoms are correctly flipped
    G_forward = mock.mock_graph()
    G_forward = graphs.nX_simple_geoms(G_forward)
    G_forward_decompose = graphs.nX_decompose(G_forward, 20)

    G_backward = mock.mock_graph()
    G_backward = graphs.nX_simple_geoms(G_backward)
    for i, (s, e, d) in enumerate(G_backward.edges(data=True)):
        # flip each third geom
        if i % 3 == 0:
            flipped_coords = np.fliplr(d['geom'].coords.xy)
            G[s][e]['geom'] = geometry.LineString([[x, y] for x, y in zip(flipped_coords[0], flipped_coords[1])])
    G_backward_decompose = graphs.nX_decompose(G_backward, 20)

    for n, d in G_forward_decompose.nodes(data=True):
        assert d['x'] == G_backward_decompose.nodes[n]['x']
        assert d['y'] == G_backward_decompose.nodes[n]['y']

    # test that geom coordinate mismatch throws an error
    G = mock.mock_graph()
    for k in ['x', 'y']:
        for n in G.nodes():
            G.nodes[n][k] = G.nodes[n][k] + 1
            break
        with pytest.raises(KeyError):
            graphs.nX_decompose(G, 20)
Пример #4
0
def test_nX_remove_filler_nodes():
    # test that redundant intersections are removed, i.e. where degree == 2
    G = mock.mock_graph()
    G = graphs.nX_simple_geoms(G)
    G_messy = make_messy_graph(G)

    # from cityseer.util import plot
    # plot.plot_nX(G_messy, labels=True)

    # simplify and test
    G_simplified = graphs.nX_remove_filler_nodes(G_messy)
    # plot.plot_nX(G_simplified, labels=True)

    # check that the simplified version matches the original un-messified version
    # but note the simplified version will have the disconnected loop of 52-53-54-55 now condensed to only #52
    g_nodes = set(G.nodes)
    g_nodes = g_nodes.difference([53, 54, 55])
    assert list(g_nodes).sort() == list(G_simplified.nodes).sort()
    g_edges = set(G.edges)
    g_edges = g_edges.difference([(52, 53), (53, 54), (54, 55), (52, 55)])  # condensed edges
    g_edges = g_edges.union([(52, 52)])  # the new self loop
    assert list(g_edges).sort() == list(G_simplified.edges).sort()

    # check the integrity of the edges
    for s, e, d in G_simplified.edges(data=True):
        # ignore the new self-looping disconnected edge
        if s == 52 and e == 52:
            continue
        assert G_simplified[s][e]['geom'].length == G[s][e]['geom'].length
    # manually check that the new self-looping edge is equal in length to its original segments
    l = 0
    for s, e in [(52, 53), (53, 54), (54, 55), (52, 55)]:
        l += G[s][e]['geom'].length
    assert l == G_simplified[52][52]['geom'].length

    # check that all nodes still have 'x' and 'y' keys
    for n, d in G_simplified.nodes(data=True):
        assert 'x' in d
        assert 'y' in d

    # lollipop test - where looping component (all nodes == degree 2) suspend off a node with degree > 2
    # lollipops are handled slightly differently from isolated looping components (all nodes == degree 2)
    # there are no lollipops in the mock graph, so create one here

    # generate graph
    G_lollipop = nx.Graph()
    nodes = [
        (1, {'x': 700400, 'y': 5719750}),
        (2, {'x': 700400, 'y': 5719650}),
        (3, {'x': 700500, 'y': 5719550}),
        (4, {'x': 700400, 'y': 5719450}),
        (5, {'x': 700300, 'y': 5719550})
    ]
    G_lollipop.add_nodes_from(nodes)
    edges = [
        (1, 2),
        (2, 3),
        (3, 4),
        (4, 5),
        (5, 2)
    ]
    G_lollipop.add_edges_from(edges)

    # add edge geoms
    G_lollipop = graphs.nX_simple_geoms(G_lollipop)

    # flip some geometry
    G_lollipop[2][5]['geom'] = geometry.LineString(G_lollipop[2][5]['geom'].coords[::-1])
    # simplify
    G_lollipop_simpl = graphs.nX_remove_filler_nodes(G_lollipop)

    # check integrity of graph
    assert nx.number_of_nodes(G_lollipop_simpl) == 2
    assert nx.number_of_edges(G_lollipop_simpl) == 2

    # geoms should still be same cumulative length
    before_len = 0
    for s, e, d in G_lollipop.edges(data=True):
        before_len += d['geom'].length
    after_len = 0
    for s, e, d in G_lollipop_simpl.edges(data=True):
        after_len += d['geom'].length
    assert before_len == after_len
    # end point of stick should match start / end point of lollipop
    assert G_lollipop_simpl[1][2]['geom'].coords[-1] == G_lollipop_simpl[2][2]['geom'].coords[0]
    # start and end point of lollipop should match
    assert G_lollipop_simpl[2][2]['geom'].coords[0] == G_lollipop_simpl[2][2]['geom'].coords[-1]

    # check that missing geoms throw an error
    G_k = G_messy.copy()
    for i, (s, e) in enumerate(G_k.edges()):
        if i % 2 == 0:
            del G_k[s][e]['geom']
    with pytest.raises(KeyError):
        graphs.nX_remove_filler_nodes(G_k)

    # check that non-LineString geoms throw an error
    G_k = G_messy.copy()
    for s, e in G_k.edges():
        G_k[s][e]['geom'] = geometry.Point([G_k.nodes[s]['x'], G_k.nodes[s]['y']])
    with pytest.raises(TypeError):
        graphs.nX_remove_filler_nodes(G_k)

    # catch non-touching Linestrings
    G_corr = G_messy.copy()
    for s, e in G_corr.edges():
        geom = G_corr[s][e]['geom']
        start = list(geom.coords[0])
        end = list(geom.coords[1])
        # corrupt a point
        start[0] = start[0] - 1
        G_corr[s][e]['geom'] = geometry.LineString([start, end])
    with pytest.raises(TypeError):
        graphs.nX_remove_filler_nodes(G_corr)