def test_decomposed_local_centrality(): # centralities on the original nodes within the decomposed network should equal non-decomposed workflow betas = np.array([-0.02, -0.01, -0.005, -0.0008, -0.0]) distances = networks.distance_from_beta(betas) measure_keys = ('node_density', 'node_farness', 'node_cycles', 'node_harmonic', 'node_beta', 'segment_density', 'segment_harmonic', 'segment_beta', 'node_betweenness', 'node_betweenness_beta', 'segment_betweenness') # test a decomposed graph G = mock.mock_graph() G = graphs.nX_simple_geoms(G) node_uids, node_data, edge_data, node_edge_map = graphs.graph_maps_from_nX(G) # generate node and edge maps measures_data = centrality.local_centrality(node_data, edge_data, node_edge_map, distances, betas, measure_keys, angular=False) G_decomposed = graphs.nX_decompose(G, 20) # generate node and edge maps node_uids, node_data, edge_data, node_edge_map = graphs.graph_maps_from_nX(G_decomposed) checks.check_network_maps(node_data, edge_data, node_edge_map) measures_data_decomposed = centrality.local_centrality(node_data, edge_data, node_edge_map, distances, betas, measure_keys, angular=False) # test harmonic closeness on original nodes for non-decomposed vs decomposed d_range = len(distances) m_range = len(measure_keys) assert measures_data.shape == (m_range, d_range, len(G)) assert measures_data_decomposed.shape == (m_range, d_range, len(G_decomposed)) original_node_idx = np.where(node_data[:, 3] == 0) # with increasing decomposition: # - node based measures will not match # - node based segment measures will match - these measure to the cut endpoints per thresholds # - betweenness based segment won't match - doesn't measure to cut endpoints for m_idx in range(m_range): print(m_idx) for d_idx in range(d_range): match = np.allclose(measures_data[m_idx][d_idx], measures_data_decomposed[m_idx][d_idx][original_node_idx], atol=0.1, rtol=0) # relax precision if not match: print('key', measure_keys[m_idx], 'dist:', distances[d_idx], 'match:', match) if m_idx in [5, 6, 7]: assert match
def test_to_networkX(): # also see test_graphs.test_networkX_from_graph_maps for underlying graph maps version # check round trip to and from graph maps results in same graph G = mock.mock_graph() G = graphs.nX_simple_geoms(G) # explicitly set live and weight params for equality checks # graph_maps_from_networkX generates these implicitly if missing G = graphs.nX_decompose(G, decompose_max=20) for n in G.nodes(): G.nodes[n]['live'] = bool(np.random.randint(0, 1)) for s, e in G.edges(): G[s][e]['imp_factor'] = np.random.randint(0, 2) # add random data to check persistence at other end baa_node = None for n in G.nodes(): baa_node = n G.nodes[n]['boo'] = 'baa' break boo_edge = None for s, e in G.edges(): boo_edge = (s, e) G[s][e]['baa'] = 'boo' # test with metrics N = networks.Network_Layer_From_nX(G, distances=[500]) N.compute_centrality(measures=['node_harmonic']) metrics_dict = N.metrics_to_dict() G_round_trip = N.to_networkX() for n, d in G.nodes(data=True): assert G_round_trip.nodes[n]['x'] == d['x'] assert G_round_trip.nodes[n]['y'] == d['y'] assert G_round_trip.nodes[n]['live'] == d['live'] for s, e, d in G.edges(data=True): assert G_round_trip[s][e]['geom'] == d['geom'] assert G_round_trip[s][e]['imp_factor'] == d['imp_factor'] # check that metrics came through for uid, metrics in metrics_dict.items(): assert G_round_trip.nodes[uid]['metrics'] == metrics # check data persistence assert G_round_trip.nodes[baa_node]['boo'] == 'baa' assert G_round_trip[boo_edge[0]][boo_edge[1]]['baa'] == 'boo'
from cityseer.metrics import networks, layers from cityseer.util import mock, graphs, plot plt.style.use('./matplotlibrc') base_path = path.dirname(__file__) # # # INTRO PLOT G = mock.mock_graph() plot.plot_nX(G, path='graph.png', labels=True, dpi=150) # INTRO EXAMPLE PLOTS G = graphs.nX_simple_geoms(G) G = graphs.nX_decompose(G, 20) N = networks.Network_Layer_From_nX(G, distances=[400, 800]) N.compute_centrality(measures=['segment_harmonic']) data_dict = mock.mock_data_dict(G, random_seed=25) D = layers.Data_Layer_From_Dict(data_dict) D.assign_to_network(N, max_dist=400) landuse_labels = mock.mock_categorical_data(len(data_dict), random_seed=25) D.hill_branch_wt_diversity(landuse_labels, qs=[0]) G_metrics = N.to_networkX() segment_harmonic_vals = [] mixed_uses_vals = [] for node, data in G_metrics.nodes(data=True): segment_harmonic_vals.append(
# %% # test maps version import os os.environ['CITYSEER_QUIET_MODE'] = '1' from cityseer.util import mock, graphs from src.explore.toy_models import mmm_layercake_b iters = 200 graph = mock.mock_graph() graph = graphs.nX_simple_geoms(graph) graph = graphs.nX_decompose(graph, 20) layer_specs = { 'cap_step': 0.5, 'dist_threshold': 600, 'pop_threshold': 30, 'kill_threshold': 0.5, 'explore_rate': 0.5 } pop_map, landuse_maps, capacitance_maps = mmm_layercake_b(graph, iters, _layer_specs=layer_specs, seed=False, random_seed=0) # %% import networkx as nx from src import util_funcs
def test_nX_from_graph_maps(): # also see test_networks.test_to_networkX for tests on implementation via Network layer # check round trip to and from graph maps results in same graph G = mock.mock_graph() G = graphs.nX_simple_geoms(G) # explicitly set live params for equality checks # graph_maps_from_networkX generates these implicitly if missing for n in G.nodes(): G.nodes[n]['live'] = bool(np.random.randint(0, 1)) # test directly from and to graph maps node_uids, node_data, edge_data, node_edge_map = graphs.graph_maps_from_nX(G) G_round_trip = graphs.nX_from_graph_maps(node_uids, node_data, edge_data, node_edge_map) assert list(G_round_trip.nodes) == list(G.nodes) assert list(G_round_trip.edges) == list(G.edges) # check with metrics dictionary N = networks.Network_Layer_From_nX(G, distances=[500, 1000]) N.compute_centrality(measures=['node_harmonic']) data_dict = mock.mock_data_dict(G) landuse_labels = mock.mock_categorical_data(len(data_dict)) D = layers.Data_Layer_From_Dict(data_dict) D.assign_to_network(N, max_dist=400) D.compute_aggregated(landuse_labels, mixed_use_keys=['hill', 'shannon'], accessibility_keys=['a', 'c'], qs=[0, 1]) metrics_dict = N.metrics_to_dict() # without backbone G_round_trip_data = graphs.nX_from_graph_maps(node_uids, node_data, edge_data, node_edge_map, metrics_dict=metrics_dict) for uid, metrics in metrics_dict.items(): assert G_round_trip_data.nodes[uid]['metrics'] == metrics # with backbone G_round_trip_data = graphs.nX_from_graph_maps(node_uids, node_data, edge_data, node_edge_map, networkX_graph=G, metrics_dict=metrics_dict) for uid, metrics in metrics_dict.items(): assert G_round_trip_data.nodes[uid]['metrics'] == metrics # test with decomposed G_decomposed = graphs.nX_decompose(G, decompose_max=20) # set live explicitly for n in G_decomposed.nodes(): G_decomposed.nodes[n]['live'] = bool(np.random.randint(0, 1)) node_uids_d, node_data_d, edge_data_d, node_edge_map_d = graphs.graph_maps_from_nX(G_decomposed) G_round_trip_d = graphs.nX_from_graph_maps(node_uids_d, node_data_d, edge_data_d, node_edge_map_d) assert list(G_round_trip_d.nodes) == list(G_decomposed.nodes) for n, node_data in G_round_trip.nodes(data=True): assert n in G_decomposed assert node_data['live'] == G_decomposed.nodes[n]['live'] assert node_data['x'] == G_decomposed.nodes[n]['x'] assert node_data['y'] == G_decomposed.nodes[n]['y'] assert G_round_trip_d.edges == G_decomposed.edges # error checks for when using backbone graph: # mismatching numbers of nodes corrupt_G = G.copy() corrupt_G.remove_node(0) with pytest.raises(ValueError): graphs.nX_from_graph_maps(node_uids, node_data, edge_data, node_edge_map, networkX_graph=corrupt_G) # mismatching node uid with pytest.raises(ValueError): corrupt_node_uids = list(node_uids) corrupt_node_uids[0] = 'boo' graphs.nX_from_graph_maps(corrupt_node_uids, node_data, edge_data, node_edge_map, networkX_graph=G)
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)
filters = '["area"!~"yes"]' \ '["highway"!~"path|footway|motor|proposed|construction|abandoned|platform|raceway|service"]' \ '["foot"!~"no"]' \ '["service"!~"private"]' \ '["access"!~"private"]' query = f'[out:json][timeout:{timeout}];(way["highway"]{filters}(poly:"{geom_osm}"); >;);out skel qt;' try: response = requests.get('https://overpass-api.de/api/interpreter', timeout=timeout, params={'data': query}) except requests.exceptions.RequestException as e: raise e G_wgs = graphs.nX_from_osm(osm_json=response.text) G_utm = graphs.nX_wgs_to_utm(G_wgs) plt.cla() plt.clf() plot.plot_nX(G_utm, 'graph_raw.png', dpi=150, figsize=(20, 20)) G_messy = graphs.nX_simple_geoms(G_utm) G_messy = graphs.nX_remove_dangling_nodes(G_messy) G_messy = graphs.nX_remove_filler_nodes(G_messy) plt.cla() plt.clf() plot.plot_nX(G_messy, 'graph_topo.png', dpi=150, figsize=(20, 20)) G_messy = graphs.nX_decompose(G_messy, 50) G_messy = graphs.nX_consolidate_parallel(G_messy) plt.cla() plt.clf() plot.plot_nX(G_messy, 'graph_consolidated.png', dpi=150, figsize=(20, 20))