def test_merge_nearby_nodes(): """ --2 2 / / -- / / / 1 / \ / 0-----------5 => 0------------5 / 3 | 4 """ coords = [[ 0, 0], [-1, 1], [ 2, 6], [-1, -1], [-1, -2], [ 5, 0]] edges = [(0, 1), (1, 2), (0, 3), (3, 4), (0, 5)] geo = GeoGraph(coords=dict(enumerate(coords)), data=edges) geo.merge_nearby_nodes(radius=2.0) assert geo.edges() == [(0, 2), (0, 5)],\ "nodes were not merged correctly"
def build_network(demand_nodes, existing=None, min_node_count=2, single_network=True, network_algorithm='mod_boruvka', one_based=False ): """ project demand nodes onto optional existing supply network and return the 'optimized' network Args: demand_nodes: GeoGraph of demand nodes existing: GeoGraph of existing grid (assumes node ids don't conflict with demand_nodes min_node_count: minimum number of nodes allowed in a subgraph of the result network_algorithm: Algorithm from ALGOS to run one_based: Whether result GeoGraph's nodes should be one_based (if not, they are 0 based) Returns: msf: GeoGraph of minimum spanning forest proposed by the chosen network algorithm existing: The existing grid GeoGraph (None if it doesn't exist) """ geo_graph = subgraphs = rtree = None if existing: log.info("merging network and nodes") geo_graph, subgraphs, rtree = \ merge_network_and_nodes(existing, demand_nodes, single_network=single_network) else: geo_graph = demand_nodes log.info("running {} on {} demand nodes and {} total nodes".format( network_algorithm, len(demand_nodes), len(geo_graph))) # now run the selected algorithm network_algo = NetworkerRunner.ALGOS[network_algorithm] result_geo_graph = network_algo(geo_graph, subgraphs=subgraphs, rtree=rtree) # TODO: Remove unreferenced fake nodes? # now filter out subnetworks via minimum node count # TODO: update union_all to support GeoGraph? filtered_graph = nx.union_all(filter( lambda sub: len(sub.node) >= min_node_count, nx.connected_component_subgraphs(result_geo_graph))) # map coords back to geograph # NOTE: explicit relabel to int as somewhere in filtering above, some # node ids are set to numpy types which screws up comparisons to tuples # in write op # NOTE: relabeling nodes in-place here drops node attributes for some # reason so create a copy for now def id_label(i): id = int(i+1) if one_based else int(i) return id msf = None if filtered_graph: coords = {id_label(i): result_geo_graph.coords[i] for i in filtered_graph} relabeled = nx.relabel_nodes(filtered_graph, {i: id_label(i) for i in filtered_graph}, copy=True) msf = GeoGraph(result_geo_graph.srs, coords=coords, data=relabeled) log.info("filtered result has {} nodes and {} edges".format( len(msf.nodes()), len(msf.edges()))) return msf
def build_network(demand_nodes, existing=None, min_node_count=2, single_network=True, network_algorithm='mod_boruvka', spherical_accuracy=False, one_based=False): """ project demand nodes onto optional existing supply network and return the 'optimized' network Args: demand_nodes: GeoGraph of demand nodes existing: GeoGraph of existing grid (assumes node ids don't conflict with demand_nodes min_node_count: minimum number of nodes allowed in a subgraph of the result network_algorithm: Algorithm from ALGOS to run spherical_accuracy: Whether to connect nodes to network on a sphere one_based: Whether result GeoGraph's nodes should be one_based (if not, they are 0 based) Returns: msf: GeoGraph of minimum spanning forest proposed by the chosen network algorithm existing: The existing grid GeoGraph (None if it doesn't exist) """ geo_graph = subgraphs = rtree = None if existing: log.info("merging network and nodes") geo_graph, subgraphs, rtree = \ merge_network_and_nodes(existing, demand_nodes, single_network=single_network, spherical_accuracy=spherical_accuracy) else: geo_graph = demand_nodes log.info("running {} on {} demand nodes and {} total nodes".format( network_algorithm, len(demand_nodes), len(geo_graph))) # now run the selected algorithm network_algo = NetworkerRunner.ALGOS[network_algorithm] result_geo_graph = network_algo(geo_graph, subgraphs=subgraphs, rtree=rtree) filtered_graph = filter_min_node_subnetworks(result_geo_graph, min_node_count) # map coords back to geograph # NOTE: explicit relabel to int as somewhere in filtering above, some # node ids are set to numpy types which screws up comparisons to tuples # in write op # NOTE: relabeling nodes in-place here drops node attributes for some # reason so create a copy for now def id_label(i): id = int(i+1) if one_based else int(i) return id msf = GeoGraph(result_geo_graph.srs) if filtered_graph: coords = {id_label(i): result_geo_graph.coords[i] for i in filtered_graph} relabeled = nx.relabel_nodes(filtered_graph, {i: id_label(i) for i in filtered_graph}, copy=True) msf = GeoGraph(result_geo_graph.srs, coords=coords, data=relabeled) log.info("filtered result has {} nodes and {} edges".format( len(msf.nodes()), len(msf.edges()))) return msf