From bf9b851538e63ee42ac0d0801fa601ca697f3922 Mon Sep 17 00:00:00 2001 From: pmag <pmag@dtu.dk> Date: Mon, 4 Dec 2023 01:41:17 +0100 Subject: [PATCH] Simplygraph --- src/topupopt/data/gis/calculate.py | 8 +- src/topupopt/data/gis/identify.py | 1502 +++++--------- src/topupopt/data/gis/modify.py | 18 +- src/topupopt/data/gis/utils.py | 8 +- tests/examples_gis.py | 16 +- tests/examples_gis_utils.py | 6 +- tests/test_gis_identify.py | 3093 ++++++++++++++++++---------- tests/test_gis_modify.py | 6 +- tests/test_gis_utils.py | 6 +- 9 files changed, 2518 insertions(+), 2145 deletions(-) diff --git a/src/topupopt/data/gis/calculate.py b/src/topupopt/data/gis/calculate.py index 901ee9b..8846eb9 100644 --- a/src/topupopt/data/gis/calculate.py +++ b/src/topupopt/data/gis/calculate.py @@ -243,9 +243,9 @@ def node_path_length(network: MultiDiGraph, # get the arcs between these two nodes - arc_keys = ident.all_arcs_from_a_to_b(network, - path[node_pair], - path[node_pair+1]) + arc_keys = ident.get_edges_from_a_to_b(network, + path[node_pair], + path[node_pair+1]) number_arc_keys = len(arc_keys) @@ -440,4 +440,4 @@ def count_ocurrences(gdf: GeoDataFrame, return count_dict #****************************************************************************** -#****************************************************************************** \ No newline at end of file +#****************************************************************************** diff --git a/src/topupopt/data/gis/identify.py b/src/topupopt/data/gis/identify.py index c185296..940a008 100644 --- a/src/topupopt/data/gis/identify.py +++ b/src/topupopt/data/gis/identify.py @@ -67,7 +67,7 @@ def find_edges_in_reverse( return { edge_key: [ other_edge_key - for other_edge_key in all_arcs_from_a_to_b( + for other_edge_key in get_edges_from_a_to_b( network, node_start=edge_key[1], node_end=edge_key[0] @@ -182,7 +182,7 @@ def edges_are_in_reverse( """ - # the arcs are the same but in reverse: + # the edges are the same but in reverse: # - all attributes have to be the same or lists with # the same content, as in a set, except for # the geometry and reversed attributes @@ -208,7 +208,7 @@ def edges_are_in_reverse( # incoherent inputs return False - # for each key, value pair in the forward arc's dict + # for each key, value pair in the forward edge's dict for attr_key, attr_value in fw_dict.items(): if (type(attr_value) == list and ((type(rv_dict[attr_key]) == list and @@ -231,7 +231,7 @@ def edges_are_in_reverse( type(rv_dict[attr_key]) != LineString)): # either the geometries are not reversed # or, there is no geometry attribute in the reverse dict - # or, the geometry in the reverse arc is not for a LineString + # or, the geometry in the reverse edge is not for a LineString # print('ping3:'+str(attr_key)) return False elif (attr_key == osm.KEY_OSMNX_GEOMETRY and @@ -480,9 +480,9 @@ def find_roundabouts(network: nx.MultiDiGraph, for edge_key in new_network.edges(keys=True): - arc_data_dict = new_network.get_edge_data(*edge_key) + edge_data_dict = new_network.get_edge_data(*edge_key) - if osm.KEY_OSMNX_ONEWAY not in arc_data_dict: + if osm.KEY_OSMNX_ONEWAY not in edge_data_dict: # no information about being oneway or not: not a oneway edge @@ -494,9 +494,9 @@ def find_roundabouts(network: nx.MultiDiGraph, else: - # there is information about the arc being oneway or not + # there is information about the edge being oneway or not - if not arc_data_dict[osm.KEY_OSMNX_ONEWAY]: + if not edge_data_dict[osm.KEY_OSMNX_ONEWAY]: # oneway attr is False: not a oneway edge @@ -651,45 +651,45 @@ def is_roundabout(network: nx.MultiDiGraph, # check if the last node connects to the first - arc_keys = all_arcs_from_a_to_b(network, + edge_keys = get_edges_from_a_to_b(network, path[-1], path[0]) - if len(arc_keys) == 0: + if len(edge_keys) == 0: return False else: - # among the arcs between them, find at least one compatible + # among the edges between them, find at least one compatible - compatible_arc_exists = False + compatible_edge_exists = False - for arc_key in arc_keys: + for edge_key in edge_keys: # get its data - arc_data_dict = network.get_edge_data(u=arc_key[0], - v=arc_key[1], - key=arc_key[2]) + edge_data_dict = network.get_edge_data(u=edge_key[0], + v=edge_key[1], + key=edge_key[2]) - # ensure that this arc has the oneway attribute + # ensure that this edge has the oneway attribute - if osm.KEY_OSMNX_ONEWAY in arc_data_dict: + if osm.KEY_OSMNX_ONEWAY in edge_data_dict: # ensure that it is true - if arc_data_dict[osm.KEY_OSMNX_ONEWAY]: + if edge_data_dict[osm.KEY_OSMNX_ONEWAY]: - compatible_arc_exists = True + compatible_edge_exists = True break - # check for compatible arcs + # check for compatible edges - if not compatible_arc_exists: + if not compatible_edge_exists: - # no compatible arcs exist between these two nodes + # no compatible edges exist between these two nodes return False @@ -697,37 +697,37 @@ def is_roundabout(network: nx.MultiDiGraph, for node_pair in range(len(path)-1): - # for each arc between them, find at least one compatible arc + # for each edge between them, find at least one compatible edge - compatible_arc_exists = False + compatible_edge_exists = False - for arc_key in all_arcs_from_a_to_b(network, + for edge_key in get_edges_from_a_to_b(network, path[node_pair], path[node_pair+1]): # get its data - arc_data_dict = network.get_edge_data(u=arc_key[0], - v=arc_key[1], - key=arc_key[2]) + edge_data_dict = network.get_edge_data(u=edge_key[0], + v=edge_key[1], + key=edge_key[2]) - # ensure that this arc has the oneway attribute + # ensure that this edge has the oneway attribute - if osm.KEY_OSMNX_ONEWAY in arc_data_dict: + if osm.KEY_OSMNX_ONEWAY in edge_data_dict: # ensure that it is true - if arc_data_dict[osm.KEY_OSMNX_ONEWAY]: + if edge_data_dict[osm.KEY_OSMNX_ONEWAY]: - compatible_arc_exists = True + compatible_edge_exists = True break - # check for compatible arcs + # check for compatible edges - if not compatible_arc_exists: + if not compatible_edge_exists: - # no compatible arcs exist between these two nodes + # no compatible edges exist between these two nodes return False @@ -740,11 +740,11 @@ def is_roundabout(network: nx.MultiDiGraph, # TODO: change name to get_multi_edge_tuples -def all_arcs_from_a_to_b(network: nx.MultiDiGraph, - node_start, - node_end) -> list: +def get_edges_from_a_to_b(network: nx.MultiDiGraph, + node_start, + node_end) -> list: """ - Retrieve the keys for arcs from one node to another. + Retrieve the keys for edges from one node to another. Parameters ---------- @@ -758,7 +758,7 @@ def all_arcs_from_a_to_b(network: nx.MultiDiGraph, Returns ------- list - A list of arc keys from the start to the end node. + A list of edge keys from the start to the end node. """ if network.has_edge(u=node_start, v=node_end): @@ -770,12 +770,12 @@ def all_arcs_from_a_to_b(network: nx.MultiDiGraph, #****************************************************************************** #****************************************************************************** -def all_arcs_between_two_nodes(network: nx.MultiDiGraph, u, v) -> list: +def get_edges_between_two_nodes(network: nx.MultiDiGraph, u, v) -> list: """ - Retrieve the keys for all arcs involving two specific nodes. + Retrieve the keys for all edges involving two specific nodes. - The keys concern arcs in both directions. For a single direction, consider - using the method all_arcs_from_a_to_b instead. + The keys concern edges in both directions. For a single direction, consider + using the method get_edges_from_a_to_b instead. Parameters ---------- @@ -789,41 +789,41 @@ def all_arcs_between_two_nodes(network: nx.MultiDiGraph, u, v) -> list: Returns ------- list - A list of arc keys involving both nodes, in both directions. + A list of edge keys involving both nodes, in both directions. """ if network.has_edge(u, v): - # arcs exist from u to v + # edges exist from u to v _out = [(u,v,k) for k in network._adj[u][v]] try: - # try finding out if arcs exist from v to u + # try finding out if edges exist from v to u _out.extend([(v,u,k) for k in network._adj[v][u]]) except KeyError: - # arcs do not exist from v to u + # edges do not exist from v to u pass # return what was obtained return _out elif network.has_edge(v, u): - # arcs do not exist from u to v but exist from v to u + # edges do not exist from u to v but exist from v to u return [(v,u,k) for k in network._adj[v][u]] else: - # no arcs found + # no edges found return [] #****************************************************************************** #****************************************************************************** -def all_arcs_involving_node(network: nx.MultiDiGraph, +def get_edges_involving_node(network: nx.MultiDiGraph, node_key, - include_outgoing_arcs: bool = True, - include_incoming_arcs: bool = True, + include_outgoing_edges: bool = True, + include_incoming_edges: bool = True, include_self_loops: bool = True) -> list: """ - Retrieve the keys for all arcs involving a specific node. + Retrieve the keys for all edges involving a specific node. - The keys concern incoming and outgoing arcs. Optionally, the keys retrieved - can concern only incoming or outgoing arcs, self-loops included or not. + The keys concern incoming and outgoing edges. Optionally, the keys retrieved + can concern only incoming or outgoing edges, self-loops included or not. Parameters ---------- @@ -831,17 +831,17 @@ def all_arcs_involving_node(network: nx.MultiDiGraph, The object describing the network. node_key : hashable-type The key to the node under consideration. - include_outgoing_arcs : bool, optional - If True, outgoing arcs are considered. The default is True. - include_incoming_arcs : bool, optional - If True, incoming arcs are considered. The default is True. + include_outgoing_edges : bool, optional + If True, outgoing edges are considered. The default is True. + include_incoming_edges : bool, optional + If True, incoming edges are considered. The default is True. include_self_loops : bool, optional If True, self-loops are considered. The default is True. Returns ------- list - A list of arc keys involving the specified node. + A list of edge keys involving the specified node. """ @@ -849,12 +849,12 @@ def all_arcs_involving_node(network: nx.MultiDiGraph, edge_key for edge_key in network.edges(keys=True) if node_key in edge_key[0:2] - # outgoing arcs - if ((node_key != edge_key[0] and not include_outgoing_arcs) or - include_outgoing_arcs) - # incoming arcs - if ((node_key != edge_key[1] and not include_incoming_arcs) or - include_incoming_arcs) + # outgoing edges + if ((node_key != edge_key[0] and not include_outgoing_edges) or + include_outgoing_edges) + # incoming edges + if ((node_key != edge_key[1] and not include_incoming_edges) or + include_incoming_edges) # self-loops if ((edge_key[0] != edge_key[1] and not include_self_loops) or include_self_loops) @@ -900,12 +900,19 @@ def neighbours(network: nx.MultiDiGraph or nx.MultiGraph, return nx.all_neighbors(network, node_key) -#****************************************************************************** -#****************************************************************************** +# ***************************************************************************** +# ***************************************************************************** -def is_node_path(network: nx.MultiDiGraph, path: list) -> bool: +def is_node_path( + network: nx.MultiDiGraph, + path: list, + consider_reversed_edges: bool = False) -> bool: """ Indicates if a given path qualifies as a node path in a directed network. + + A node path consists of a sequence of nodes connected by directed edges. + + The sequence must include at least two nodes. Parameters ---------- @@ -913,6 +920,9 @@ def is_node_path(network: nx.MultiDiGraph, path: list) -> bool: The network object. path : list The tentative node path. + consider_reversed_edges : bool, optional + If True, reversed edges can be used to identify a path. If False, only + edges in the travel direction will be considered. The default is False. Returns ------- @@ -920,13 +930,25 @@ def is_node_path(network: nx.MultiDiGraph, path: list) -> bool: Returns True if path is a node path and False otherwise. """ - if len(path) == 0: + if len(path) <= 1: return False + elif consider_reversed_edges: + # all nodes must exist in the network + for node in path: + if not network.has_node(node): + return False + # each pair of nodes must be connected, in one way or another + for n1, n2 in nx.utils.pairwise(path): + if network.has_edge(n1, n2) or network.has_edge(n2, n1): + continue + else: + return False + return True else: - return nx.is_path(network, path) + return nx.is_path(network, path) -#****************************************************************************** -#****************************************************************************** +# ***************************************************************************** +# ***************************************************************************** def is_edge_path(network: nx.MultiDiGraph, path: list, @@ -1060,7 +1082,7 @@ def convert_edge_path(network: nx.MultiDiGraph, path : list A list of sequential edge keys that form a path. allow_reversed_edges : bool, optional - If True, arcs in the opposite direction also count to form paths, as + If True, edges in the opposite direction also count to form paths, as long as the same nodes are involved. The default is False. Returns @@ -1075,69 +1097,43 @@ def convert_edge_path(network: nx.MultiDiGraph, ignore_edge_direction=allow_reversed_edges): # path is a sequence of edge keys: convert to node path - if allow_reversed_edges: - # reverse arcs are allowed - + # reverse edges are allowed # drop self-loops, if any - edge_path = [ edge_key for edge_key in path if edge_key[0] != edge_key[1] # exclude self loops ] - # if there is only one arc, the node path is straightforward - + # if there is only one edge, the node path is straightforward if len(edge_path) == 1: - return [edge_path[0][0], edge_path[0][1]] node_path = [] - for edge_index, edge_key in enumerate(edge_path): # if there are no nodes yet on the path - if len(node_path) == 0: - # find out which node comes first - if edge_key[0] in edge_path[1]: - # the start node is in the second edge too: reversed - - node_path.append(edge_key[1]) - - node_path.append(edge_key[0]) - - else: # the arc is not reversed - + node_path.append(edge_key[1]) node_path.append(edge_key[0]) - + else: # the edge is not reversed + node_path.append(edge_key[0]) node_path.append(edge_key[1]) - else: - # find out which node comes after the previous node - if node_path[-1] == edge_key[0]: - # the start node is the same as the previous node - node_path.append(edge_key[1]) - else: - # the end node is the same as the previous node - node_path.append(edge_key[0]) - else: - - # no reversed arcs - + # no reversed edges node_path = [ edge_key[0] for edge_key in path @@ -1145,682 +1141,115 @@ def convert_edge_path(network: nx.MultiDiGraph, ] # add the last edge's end node - node_path.append(path[-1][1]) # return statement - return node_path else: # not an edge path - return [] -#****************************************************************************** -#****************************************************************************** - -# TODO: split method into is_edge_path_simplifiable and is_node_path_simplifiable +# ***************************************************************************** +# ***************************************************************************** -def is_path_simplifiable(network: nx.MultiDiGraph, - path: list, - path_as_node_keys: bool = True, - ignore_self_loops: bool = True, - ignore_arc_directions: bool = True) -> bool: +def is_path_straight(network: nx.MultiDiGraph, + path: list, + consider_reversed_edges: bool = False, + ignore_self_loops: bool = False) -> bool: """ - Returns True if the path provided can be simplified and False otherwise. + Returns True if the path is straight and False otherwise. + + A path is defined to be straight if it presents no options along it. Parameters ---------- network : nx.MultiDiGraph The objet describing the network. path : list - The list of nodes along the path. If path_as_node_keys is set to False, - the path is instead described using a list of edge keys. - path_as_node_keys : bool, optional - If True, the path is provided as a list of node keys. If False, the - path is provided as a list of edge keys. The default is True. + The list of nodes along the path. + consider_reversed_edges : bool, optional + If True, reversed edges can also be used to form paths. If False, only + edges in the stated direction will be considered. The default is False. ignore_self_loops : bool, optional - If True, self-loops are ignored. If False, paths containing self-loops - cannot be simplified. The default is True. - ignore_arc_directions : bool, optional - If True, paths with arcs in different directions can still be - simplified. If False, the paths with arcs in different directions - cannot be simplified. The default is True. + If True, paths with self-loops can still be straight. If False, paths + containing self-loops cannot be straight. The default is False. Returns ------- bool - A boolean indicating whether the path can be simplified or not. + A boolean indicating whether the path is straight or not. """ - # path format - - if path_as_node_keys: - - # the path is provided as a list of node keys - - # a simplifiable path must contain at least three nodes - - path_length = len(path) - - if path_length < 3: - - return False - - # verify that the elements are node keys in the network - - for node_key in path: - - if not network.has_node(node_key): - - return False - - # verify that there are arcs between each consecutive node pair - - for node_pair in range(path_length-1): - - if ignore_arc_directions: - - # check for arcs from A to B or vice-versa - - arc_keys = all_arcs_between_two_nodes(network, - path[node_pair], - path[node_pair+1]) - - # if there are no arcs between the two nodes, no path exists - - if len(arc_keys) == 0: - - return False - - else: - - # check for arcs from A to B - - if not network.has_edge(u=path[node_pair], - v=path[node_pair+1]): - - return False - - # verify that each node in the list, except the first and last, only - # has two neighbours and that these are before and after it in the path - - for node_index, node_key in enumerate(path): - - if node_index == 0 or node_index == path_length-1: - - continue - - # get the respective neighbours - - _neighbours = list( - neighbours( - network, node_key, ignore_self_loops=ignore_self_loops - ) - ) - - # check the number of neighbours - - if len(_neighbours) != 2: - - # if not two, the path is not straight - - return False - - # for each neighbour - - for neighbour in _neighbours: - - # # verify that it is adjacent to node_key - - # if (neighbour != path[node_index-1] and - # neighbour != path[node_index+1]): - - # return False - - assert (neighbour == path[node_index-1] or - neighbour == path[node_index+1]) - - #********************************************************************** - #********************************************************************** - - else: - - # the path is provided as a list of arc keys - - # there must be at least two arc keys: there must be a middle node - - path_length = len(path) - - if path_length < 2: - - return False # TODO: reach this statement - - # each arc key must have the (node_A, node_B, extra_arc_key) format - - node_path = [] - - for arc_key in path: - - # check format - - if len(arc_key) != 3: - - return False # TODO: reach this statement - - # check that it exists in the network - - if ignore_arc_directions: - - # verify that it exists as provided - - if not network.has_edge(u=arc_key[0], - v=arc_key[1], - key=arc_key[2]): - - # if not, verify that it exists in the opposite direction - - if not network.has_edge(u=arc_key[1], - v=arc_key[0]): - # TODO: reach this statement - # if not either, then it does not exist - - return False - - else: - - # verify that it exists as provided - - if not network.has_edge(u=arc_key[0], - v=arc_key[1], - key=arc_key[2]): - - # if not, then there is no link between the nodes - - return False # TODO: reach this statement - - #********************************************************************** - #********************************************************************** + # confirm that it is a path + if not is_node_path(network, path, consider_reversed_edges): + return False - # otherwise, return True + # a straight path requires at least two nodes + path_length = len(path) + if path_length == 2: + return True # path with two nodes is always straight + # check if the intermediate nodes have the right number of neighbours + for intermediate_node in path[1:-1]: + if len(tuple(neighbours( + network, + intermediate_node, + ignore_self_loops=ignore_self_loops)) + ) != 2: + # the path is not straight if the intermediate nodes do not have + # two distinct neighbours + return False + + # if all intermediate nodes have two neighbours, return True return True #****************************************************************************** #****************************************************************************** -def find_straight_paths(network: nx.MultiDiGraph, - excluded_nodes: list, - paths_must_be_oneway: bool = True, - paths_must_be_unique: bool = True): +def find_simplifiable_paths(network: nx.MultiDiGraph, + excluded_nodes: list, + ignore_self_loops: bool = False, + consider_reversed_edges: bool = False, + include_both_directions: bool = False) -> list: + """ + Enumerates the simplifiable paths found in a given graph. + + A path is defined to be simplifiable if it presents no options along it + and involves at least three different nodes: two-node paths are straight + but are not simplifiable, with or without reversed edges. + + Parameters + ---------- + network : nx.MultiDiGraph + The object describing the graph. + excluded_nodes : list + A list of keys for nodes that cannot be in any straight path. + ignore_self_loops : bool, optional + If True, paths including self-loops can still be straight. If False, + paths including self-loops cannot be straight. The default is False. + consider_reversed_edges : bool, optional + If True, a straight path can include nodes connected in reverse. If + False, only edges in the stated direction will be considered. The + default is False. + include_both_directions : bool, optional + If True, and if reverse edges are allowed, simplifiable paths will + appear once per direction, otherwise just once. The default is False. + + Returns + ------- + list + A list of the straight paths in the graph. + + """ # a straight path is a path where the intermediate nodes (all excluding the # first and the last) have to exist and have exactly 2 distinct neighbours - #************************************************************************** - - def find_path_case_a( - network: nx.MultiDiGraph, - list_valid_nodes: list, - path: list - ) -> list: - - #paths_must_be_oneway=True - #paths_must_be_unique=True - # cycles have to involve 3 nodes, e.g. [0, 1, 2, 0] - # [0, 1, 0] is ruled out, since it is not oneway (2 anti-parallel arcs) - - def find_path_forward(network: nx.MultiDiGraph, - list_valid_nodes: list, - path: list): - # identify the last node's neighbours - current_neighbours = set( - neighbours(network, path[-1], ignore_self_loops=True) - ) - # check each neighbour - for a_neighbour in current_neighbours: - # determine which kinds of edges it has - n_forward = network.number_of_edges(path[-1], a_neighbour) - n_reverse = network.number_of_edges(a_neighbour, path[-1]) - if a_neighbour in list_valid_nodes: - # two neighbours, one of which must be the current node - # this means there is at least another node to be added - # which requires recursion, unless there is a loop - if a_neighbour not in path: - # the node is not on the path - if n_forward == 1 and n_reverse == 0: - # the node is connected to the path via a forward e - path.append(a_neighbour) - return find_path_forward( - network, - list_valid_nodes, - path - ) - elif (a_neighbour == path[0] and - n_forward == 1 and n_reverse == 0): - # the neighbour matches the first node - # and there is a forward edge - # a cycle was found: finalise and return it - path.append(a_neighbour) - return path - elif n_forward == 1 and n_reverse == 0: - # one neighbour only: end node - path.append(a_neighbour) - return path - # all neighbours have been visited: return the current path - return path - - def find_path_backward(network: nx.MultiDiGraph, - list_valid_nodes: list, - path: list): - # identify the last node's neighbours - current_neighbours = set( - neighbours(network, path[0], ignore_self_loops=True) - ) - # check all neighbours (should be two at most) - for a_neighbour in current_neighbours: - # determine which kinds of edges it has - n_forward = network.number_of_edges(a_neighbour, path[0]) - n_reverse = network.number_of_edges(path[0], a_neighbour) - if a_neighbour in list_valid_nodes: - # two neighbours, one of which must be the current node - # this means there is at least another node to be added - # which requires recursion, unless there is a loop - if a_neighbour not in path: - # the node is not on the path - if n_forward == 1 and n_reverse == 0: - # the node is connected to the path via a forward e - path.insert(0, a_neighbour) - return find_path_backward( - network, - list_valid_nodes, - path - ) - elif n_forward == 1 and n_reverse == 0: - # one neighbour only: start node - path.insert(0, a_neighbour) - return path - # all neighbours have been visited: return the current path - return path - - # explore paths in the forward sense - path = find_path_forward( - network, - list_valid_nodes, - path - ) - # check for cycles - if len(path) >= 3 and path[0] == path[-1]: - # it is a cycle: no need to search backwards - return path - # explore paths in the backward sense and return the path - return find_path_backward( - network, - list_valid_nodes, - path - ) - - #************************************************************************** - - def find_path_case_b( - network: nx.MultiDiGraph, - list_valid_nodes: list, - path: list - ) -> list: - - #paths_must_be_oneway=True - #paths_must_be_unique=False - # cycles have to involve 3 nodes, e.g. [0, 1, 2, 0] - # [0, 1, 0] is ruled out, since it is not oneway (2 anti-parallel arcs) - - def find_path_forward(network: nx.MultiDiGraph, - list_valid_nodes: list, - path: list): - # identify the last node's neighbours - current_neighbours = set( - neighbours(network, path[-1], ignore_self_loops=True) - ) - # check each neighbour - for a_neighbour in current_neighbours: - # determine which kinds of edges it has - n_forward = network.number_of_edges(path[-1], a_neighbour) - n_reverse = network.number_of_edges(a_neighbour, path[-1]) - if a_neighbour in list_valid_nodes: - # two neighbours, one of which must be the current node - # this means there is at least another node to be added - # which requires recursion, unless there is a loop - if a_neighbour not in path: - # the node is not on the path - if n_forward >= 1 and n_reverse == 0: - # the node is connected to the path via a forward edge - path.append(a_neighbour) - return find_path_forward( - network, - list_valid_nodes, - path - ) - elif (a_neighbour == path[0] and - n_forward >= 1 and n_reverse == 0): - # the neighbour matches the first node - # and there is a forward edge - # a cycle was found: finalise and return it - path.append(a_neighbour) - return path - elif n_forward >= 1 and n_reverse == 0: - # one neighbour only: end node - path.append(a_neighbour) - return path - # all neighbours have been visited: return the current path - return path - - def find_path_backward(network: nx.MultiDiGraph, - list_valid_nodes: list, - path: list): - # identify the last node's neighbours - current_neighbours = set( - neighbours(network, path[0], ignore_self_loops=True) - ) - # check all neighbours (should be two at most) - for a_neighbour in current_neighbours: - # determine which kinds of edges it has - n_forward = network.number_of_edges(a_neighbour, path[0]) - n_reverse = network.number_of_edges(path[0], a_neighbour) - if a_neighbour in list_valid_nodes: - # two neighbours, one of which must be the current node - # this means there is at least another node to be added - # which requires recursion, unless there is a loop - if a_neighbour not in path: - # the node is not on the path - if n_forward >= 1 and n_reverse == 0: - # the node is connected to the path via a forward edge - path.insert(0, a_neighbour) - return find_path_backward( - network, - list_valid_nodes, - path - ) - elif n_forward >= 1 and n_reverse == 0: - # one neighbour only: start node - path.insert(0, a_neighbour) - return path - # all neighbours have been visited: return the current path - return path - - # explore paths in the forward sense - path = find_path_forward( - network, - list_valid_nodes, - path - ) - # check for cycles - if len(path) >= 3 and path[0] == path[-1]: - # it is a cycle: no need to search backwards - return path - # explore paths in the backward sense and return the path - return find_path_backward( - network, - list_valid_nodes, - path - ) - - #************************************************************************** - - def find_path_case_c( - network: nx.MultiDiGraph, - list_valid_nodes: list, - path: list - ) -> list: - - #paths_must_be_oneway=False - #paths_must_be_unique=True - # [0,1,2,0] - # [0,1,0] is allowed since arcs anti-parallel arcs are allowed - # [1,0,1] is allowed since arcs anti-parallel arcs are allowed - # [0,1,0] and [1,0,1] should not be returned together - - #********************************************************************** - - def find_path_forward(network: nx.MultiDiGraph, - list_valid_nodes: list, - path: list): - # identify the last node's neighbours - current_neighbours = set( - neighbours(network, path[-1], ignore_self_loops=True) - ) - # check each neighbour - for a_neighbour in current_neighbours: - # determine which kinds of edges it has - n_forward = network.number_of_edges(path[-1], a_neighbour) - n_reverse = network.number_of_edges(a_neighbour, path[-1]) - if a_neighbour in list_valid_nodes: - # two neighbours, one of which must be the current node - # this means there is at least another node to be added - # which requires recursion, unless there is a loop - if a_neighbour not in path: - # the node is not on the path - if n_forward + n_reverse == 1: - # either kind of edge will do - path.append(a_neighbour) - return find_path_forward( - network, - list_valid_nodes, - path - ) - elif (a_neighbour == path[0] and len(path) >= 3 and - n_forward + n_reverse == 1): - # the neighb. is in the path and matches the first node - # and there is only one edge - # a cycle was found: finalise and return it - path.append(a_neighbour) - return path - # if the neighbour is on the path and not a cycle, continue - elif n_forward + n_reverse == 1 and a_neighbour not in path: - # the neighbour is not on the list of valid nodes, id est, - # it has one neighbour only: start/end node - path.append(a_neighbour) - return path - # else: - # print('here4') - # path.insert(0, a_neighbour) - # return path - # all neighbours have been visited: return the current path - return path - - #********************************************************************** - - def find_path_backward(network: nx.MultiDiGraph, - list_valid_nodes: list, - path: list): - # identify the last node's neighbours - current_neighbours = set( - neighbours(network, path[0], ignore_self_loops=True) - ) - # check all neighbours (should be two at most) - for a_neighbour in current_neighbours: - # determine which kinds of edges it has - n_forward = network.number_of_edges(a_neighbour, path[0]) - n_reverse = network.number_of_edges(path[0], a_neighbour) - if a_neighbour in list_valid_nodes: - # two neighbours, one of which must be the current node - # this means there is at least another node to be added - # which requires recursion, unless there is a loop - if a_neighbour not in path: - # the node is not on the path - if n_forward + n_reverse == 1: - # the node is connected to the path via a forward e - path.insert(0, a_neighbour) - return find_path_backward( - network, - list_valid_nodes, - path - ) - elif n_forward + n_reverse == 1 and a_neighbour not in path: - # one neighbour only: start node - path.insert(0, a_neighbour) - return path - # all neighbours have been visited: return the current path - return path - - #********************************************************************** - - # explore paths in the forward sense - path = find_path_forward( - network, - list_valid_nodes, - path - ) - # check for cycles - if len(path) >= 3 and path[0] == path[-1]: - # it is a cycle: no need to search backwards - return path - # explore paths in the backward sense and return the path - return find_path_backward( - network, - list_valid_nodes, - path - ) - - #************************************************************************** - - def find_path_case_d( - network: nx.MultiDiGraph, - list_valid_nodes: list, - path: list - ) -> list: - - # paths_must_be_oneway=False - # paths_must_be_unique=False - # cycles have to involve 3 nodes, e.g. [0, 1, 2, 0] - # [0, 1, 0] is ruled out, since it is not oneway (2 anti-parallel arcs) - - #********************************************************************** - - def find_path_forward(network: nx.MultiDiGraph, - list_valid_nodes: list, - path: list): - # identify the last node's neighbours - current_neighbours = set( - neighbours(network, path[-1], ignore_self_loops=True) - ) - # print('current neighbours: '+str(current_neighbours)) - # check each neighbour - for a_neighbour in current_neighbours: - # print('neighbour: '+str(a_neighbour)) - # print('current path: '+str(path)) - # determine which kinds of edges it has - n_forward = network.number_of_edges(path[-1], a_neighbour) - n_reverse = network.number_of_edges(a_neighbour, path[-1]) - # print('n_forward: '+str(n_forward)) - # print('n_reverse: '+str(n_reverse)) - if a_neighbour in list_valid_nodes: - # two neighbours, one of which must be the current node - # this means there is at least another node to be added - # which requires recursion, unless there is a loop - - if a_neighbour not in path: - # the node is not on the path - if n_forward + n_reverse >= 1: - # either kind of edge will do - # print('here') - path.append(a_neighbour) - return find_path_forward( - network, - list_valid_nodes, - path - ) - elif (a_neighbour == path[0] and len(path) >= 3 and - n_forward + n_reverse >= 1): - # the neighb. is in the path and matches the first node - # and there is only one edge - # a cycle was found: finalise and return it - # print('here2') - path.append(a_neighbour) - return path - # print('here23') - # if the neighbour is on the path and not a cycle, continue - elif n_forward + n_reverse >= 1 and a_neighbour not in path: - # the neighbour is not on the list of valid nodes, id est, - # it has one neighbour only: start/end node - # print('here3') - path.append(a_neighbour) - return path - # print('here34') - # else: - # print('here4') - # path.insert(0, a_neighbour) - # return path - # all neighbours have been visited: return the current path - # print('here4') - return path - - #********************************************************************** - - def find_path_backward(network: nx.MultiDiGraph, - list_valid_nodes: list, - path: list): - # identify the last node's neighbours - current_neighbours = set( - neighbours(network, path[0], ignore_self_loops=True) - ) - # print('current neighbours: '+str(current_neighbours)) - # check all neighbours (should be two at most) - for a_neighbour in current_neighbours: - # print('neighbour: '+str(a_neighbour)) - # print('current path: '+str(path)) - # determine which kinds of edges it has - n_forward = network.number_of_edges(a_neighbour, path[0]) - n_reverse = network.number_of_edges(path[0], a_neighbour) - # print('n_forward: '+str(n_forward)) - # print('n_reverse: '+str(n_reverse)) - if a_neighbour in list_valid_nodes: - # two neighbours, one of which must be the current node - # this means there is at least another node to be added - # which requires recursion, unless there is a loop - if a_neighbour not in path: - # the node is not on the path - if n_forward + n_reverse >= 1: - # print('here5') - # the node is connected to the path via a forward e - path.insert(0, a_neighbour) - return find_path_backward( - network, - list_valid_nodes, - path - ) - elif n_forward + n_reverse >= 1 and a_neighbour not in path: - # print('here6') - # one neighbour only: start node - path.insert(0, a_neighbour) - return path - # print('here67') - # all neighbours have been visited: return the current path - # print('here7') - return path - - #********************************************************************** - # print('path:'+str(path)) - # explore paths in the forward sense - path = find_path_forward( - network, - list_valid_nodes, - path - ) - # print('post-forward search path:'+str(path)) - # check for cycles - if len(path) >= 3 and path[0] == path[-1]: - # it is a cycle: no need to search backwards - return path - # explore paths in the backward sense and return the path - return find_path_backward( - network, - list_valid_nodes, - path - ) - - #************************************************************************** + # ************************************************************************* + # ************************************************************************* # locate all the non-excluded nodes that can form straight paths @@ -1829,7 +1258,7 @@ def find_straight_paths(network: nx.MultiDiGraph, for node_key in network.nodes() # the node cannot be among those excluded if node_key not in excluded_nodes - # the node can only be linked to two other nodes other than itself + # the node has to be linked to two other nodes other than itself if len(set( neighbours( network, @@ -1837,12 +1266,15 @@ def find_straight_paths(network: nx.MultiDiGraph, ignore_self_loops=True ) )) == 2 + # exclude nodes with self-loops if desired: + # 1) self-loops are tolerated (no need to check) + # 2) self-loops are not tolerated and they do not exist + if (ignore_self_loops or + (not ignore_self_loops and + not network.has_edge(node_key, node_key))) ] - # print('******************************************************************') - # print('excluded_nodes:'+str(excluded_nodes)) - # print('intermediate_candidate_nodes:'+str(intermediate_candidate_nodes)) - - #************************************************************************** + + # ************************************************************************* # find out paths around the nodes identified @@ -1851,56 +1283,30 @@ def find_straight_paths(network: nx.MultiDiGraph, list_nodes_joined = [] - for i, candidate_node in enumerate(intermediate_candidate_nodes): - # print('-------------------------------------------------------------') - # print('candidate_node: '+str(candidate_node)) - # if node is already in previous paths, skip + # try to form paths around the candidate nodes + for candidate_node in intermediate_candidate_nodes: + + # skip if the node is already in a path if candidate_node in list_nodes_joined: continue - # a neighbour can extend the path under the following conditions: - # i) if it is among the valid nodes (has 2 neighbours only); - # ii) if it is not on the path already or it is its first node for a - # path of at least 3 nodes (with the current neighbour included) - # iii) - # a) with paths_must_be_oneway=True and paths_must_be_unique=True, if - # only one forward edge links the last node and the neighbour - # b) with paths_must_be_oneway=True and paths_must_be_unique=False, if - # no reverse edges link the last node and the neighbour - # c) with paths_must_be_oneway=False and paths_must_be_unique=True, if - # there is one forward or one reverse edge linking the nodes - # d) with paths_must_be_oneway=False and paths_must_be_unique=False, if - # there are any edges linking the last node and the neighbour - - if paths_must_be_oneway and paths_must_be_unique: - # case a - new_sequence = find_path_case_a( + # select method + if consider_reversed_edges: + # reversed edges are accepted + new_sequence = _find_path_direction_insensitive( network, - intermediate_candidate_nodes, - [candidate_node] + list_valid_nodes=intermediate_candidate_nodes, + start_node=candidate_node ) - elif paths_must_be_oneway and not paths_must_be_unique: - # case b - new_sequence = find_path_case_b( - network, - intermediate_candidate_nodes, - [candidate_node] - ) - elif not paths_must_be_oneway and paths_must_be_unique: - # case c - new_sequence = find_path_case_c( - network, - intermediate_candidate_nodes, - [candidate_node] - ) else: - # case d - new_sequence = find_path_case_d( + # reversed edges are not accepted + new_sequence = _find_path_direction_sensitive( network, - intermediate_candidate_nodes, - [candidate_node] + list_valid_nodes=intermediate_candidate_nodes, + start_node=candidate_node ) - # print('new_sequence: '+str(new_sequence)) + + # make sure the sequence is not redundant if (len(new_sequence) <= 2 or new_sequence in list_paths or set(new_sequence) in list_paths_nodes): @@ -1910,262 +1316,286 @@ def find_straight_paths(network: nx.MultiDiGraph, # add the path list_paths.append(new_sequence) list_paths_nodes.append(set(new_sequence)) - + if consider_reversed_edges and include_both_directions: + # directions do not matter: + list_paths.append(new_sequence[::-1]) + # update the list of intermediate nodes already on paths list_nodes_joined.extend( (element for element in new_sequence[1:-1] if element != candidate_node) # used to keep it shorter? ) - #************************************************************************** + # ************************************************************************* + # ************************************************************************* return list_paths - #************************************************************************** - -#****************************************************************************** -#****************************************************************************** - -def find_all_straight_paths(network: nx.MultiDiGraph, - excluded_nodes: list, - return_paths_as_arc_keys: bool = False, - pick_shortest_parallel_edge: bool = True): - - # TODO: pick shortest parallel edge - - #************************************************************************** - - # locate all the non-excluded nodes that form straight paths, that is, - # nodes that only have arcs with two other nodes - - removable_nodes = [] - - # for each node in the network + # ************************************************************************* + # ************************************************************************* - for node_key in network.nodes(): - - # check if node_key is in the list of nodes not to be considered - - if node_key in excluded_nodes: - - continue - - # identify the node's neighbours (while ignoring self-loops) - - _node_neighbours = list( - neighbours(network, node_key, ignore_self_loops=True) +# ***************************************************************************** +# ***************************************************************************** + +def _find_path_direction_sensitive( + network: nx.MultiDiGraph, + list_valid_nodes: list, + start_node + ) -> list: + + def find_path_forward(network: nx.MultiDiGraph, + current_node, + path: list): + # identify the last node's neighbours + current_neighbours = set( + neighbours(network, current_node, ignore_self_loops=True) ) - - # if the node only has two neighbours - - if len(_node_neighbours) == 2: - - # store the node's key - - removable_nodes.append(node_key) - - #************************************************************************** - - # TODO: make the method work with ignore_arc_directions=False - - def get_path_valid_nodes(network: nx.MultiDiGraph, - list_valid_nodes: list, - path: list, - ignore_arc_directions: bool = True) -> list: - - # forward direction - - path = get_path_valid_nodes_forward(network, - list_valid_nodes, - path, - ignore_arc_directions) - - # backward direction - - path = get_path_valid_nodes_backward(network, - list_valid_nodes, - path, - ignore_arc_directions) - - if len(path) == 1: - - _neighbours = list(neighbours(network, path[0])) - - path.insert(0, _neighbours[0]) - path.append(_neighbours[1]) - - return path - - # add the ends - - for neighbour in neighbours(network, path[0]): - - if neighbour not in path: - - path.insert(0, neighbour) - - break - - for neighbour in neighbours(network, path[-1]): - - if neighbour not in path or neighbour == path[0]: - - path.append(neighbour) - - break - + # check each neighbour + for a_neighbour in current_neighbours: + # check the direction of edge towards the neighbour + if network.has_edge(current_node, a_neighbour): + # forward edge + # 1) node is not on the path + # 1.1) is a valid node >> path continues + # 1.2) is not a valid node >> end of path + # 2) node is on the path already + # 2.1) matches the start node = cycle + # 2.2) does not match the start node (reversed arc) = ignore + if a_neighbour not in path: + # neighbour is not on the path + if a_neighbour in list_valid_nodes: + # is a valid node: path continues + # add the neighbour to the end of the path + path.append(a_neighbour) + # recursive call with extended path + return find_path_forward( + network, + path[-1], + path + ) + else: # is not a valid node: path ends + # add the neighbour to the end of the path: + path.append(a_neighbour) + # return the path + return path + elif a_neighbour == path[0]: + # neighbour is already on the path and matches the start + # add the neighbour to the end of the path: + path.append(a_neighbour) + # return the path + return path + # all neighbours have been visited: return the current path return path - def get_path_valid_nodes_forward( - network: nx.MultiDiGraph, - list_valid_nodes: list, - path: list, - ignore_arc_directions: bool = True) -> list: - - _node_neighbours = list( - neighbours(network, path[-1], ignore_self_loops=True) - ) - - for node_neighbour in _node_neighbours: - - # if it is a valid node - - if node_neighbour in list_valid_nodes: - - # if it not in the path - - if node_neighbour not in path: - - # neighbour is not on the path yet, and is valid - - # append it to the list - - path.append(node_neighbour) - - # call method again - - return get_path_valid_nodes_forward(network, - list_valid_nodes, - path) - - return path - - def get_path_valid_nodes_backward( - network: nx.MultiDiGraph, - list_valid_nodes: list, - path: list, - ignore_arc_directions: bool = True) -> list: - - _node_neighbours = list( - neighbours(network, path[0], ignore_self_loops=True) + def find_path_backward(network: nx.MultiDiGraph, + current_node, + path: list): + # identify the last node's neighbours + current_neighbours = set( + neighbours(network, current_node, ignore_self_loops=True) ) - - for node_neighbour in _node_neighbours: - - if node_neighbour in list_valid_nodes: - - # node is already known - - if node_neighbour not in path: - - # neighbour is not on the path yet, and is valid - - # append it to the list - - path.insert(0,node_neighbour) - - # call method again - - return get_path_valid_nodes_backward(network, - list_valid_nodes, - path) - + # check each neighbour + # 1) if the neighbour is ahead and is a valid node: + # >> recursion w/ neighbour and then return the final path + # 2) if the neighbour is ahead and is not a valid node: + # >> add it to the path and then return it + # 3) if the neighbour is not ahead and is on the path: + # >> continue, namely to check the other neighbour + # 4) if the neighbour is not ahead and is not on the path: + # >> add it to the beginning of the path and continue + + # check each neighbour + for a_neighbour in current_neighbours: + # check the direction of edge towards the neighbour + if network.has_edge(a_neighbour, current_node): + # backward edge + # 1) node is not on the path + # 1.1) is a valid node >> path continues + # 1.2) is not a valid node >> end of path + # 2) node is on the path already + # 2.1) matches the start node = cycle + # 2.2) does not match the start node (reversed arc) = ignore + if a_neighbour not in path: + # neighbour is not on the path + if a_neighbour in list_valid_nodes: + # is a valid node: path continues + # add the neighbour to the start of the path + path.insert(0, a_neighbour) + # recursive call with extended path + return find_path_backward( + network, + path[0], + path + ) + else: # is not a valid node: path ends + # add the neighbour to the start of the path + path.insert(0, a_neighbour) + # return the path + return path + elif a_neighbour == path[-1] and len(path) >= 3: + # neighbour is already on the path, matches the end, and + # the path length is greater than or equal to 3 + # add the neighbour to the start of the path + path.insert(0, a_neighbour) + # return the path + return path + # all neighbours have been visited: return the current path return path - - #************************************************************************** - - # find out paths around the nodes identified - list_paths = [] + # ************************************************************************* - list_nodes_joined = [] + # find the path forward, check for cycles and then find the path backwards + # find the forward path segment + path = find_path_forward( + network, + start_node, + [start_node] + ) + # cycles have to be detected on the first try + if len(path) >= 3 and path[0] == path[-1]: + # it is a cycle: no need to search backwards + return path + # find the backward path segment + return find_path_backward( + network, + path[0], + path + ) - for i, node in enumerate(removable_nodes): - - # if node is already in previous paths, skip - - if node in list_nodes_joined: - - continue - - #********************************************************************** - - # get sequence of valid nodes - - new_sequence = get_path_valid_nodes(network, - removable_nodes, - [node]) - - # add the ends - - list_paths.append(new_sequence) - - list_nodes_joined.extend( - (element - for element in new_sequence[1:-1] - if element != node) - ) - - # print('new run:') - # print('iteration: '+str(i)) - # print('node: '+str(node)) - # print('paths: '+str(list_paths)) - # print('nodes joined: '+str(list_nodes_joined)) + # ************************************************************************* + +# ***************************************************************************** +# ***************************************************************************** - #************************************************************************** +def _find_path_direction_insensitive( + network: nx.MultiDiGraph, + list_valid_nodes: list, + start_node + ) -> list: - if return_paths_as_arc_keys: + def find_path_forward(network: nx.MultiDiGraph, + current_node, + path: list): - # transform list of node lists into list of arc key lists + # identify the last node's neighbours + current_neighbours = set( + neighbours(network, current_node, ignore_self_loops=True) + ) + # check each neighbour + for a_neighbour in current_neighbours: + # check the direction of edge towards the neighbour: + # 1) node is not on the path + # 1.1) is a valid node >> path continues + # 1.2) is not a valid node >> end of path + # 2) node is on the path already + # 2.1) matches the start node = cycle + # 2.2) does not match the start node (reversed arc) = ignore + if a_neighbour not in path: + # neighbour is not on the path + if a_neighbour in list_valid_nodes: + # is a valid node: path continues + # add the neighbour to the end of the path + path.append(a_neighbour) + # recursive call with extended path + return find_path_forward( + network, + path[-1], + path + ) + else: # is not a valid node: path ends + # add the neighbour to the end of the path: + path.append(a_neighbour) + # return the path + return path + elif a_neighbour == path[0] and len(path) >= 3: + # neighbour is already on the path and matches the start, and + # the path length is greater than or equal to 3 + # add the neighbour to the end of the path: + path.append(a_neighbour) + # return the path + return path + # all neighbours have been visited: return the current path + return path - list_path_arcs = [] + def find_path_backward(network: nx.MultiDiGraph, + current_node, + path: list): - for path in list_paths: - - temp_path_arcs = [] - - for node_pair_index in range(len(path)-1): - - arcs = all_arcs_from_a_to_b(network, - path[node_pair_index], - path[node_pair_index+1]) - - if len(arcs) == 0: - - arcs = all_arcs_from_a_to_b(network, - path[node_pair_index+1], - path[node_pair_index]) - - # if len(arcs) == 0: - # print(list_paths) - # print(path) - # print((path[node_pair_index],path[node_pair_index+1])) - # print((path[node_pair_index+1],path[node_pair_index])) - - temp_path_arcs.append( - arcs[0] - ) - - list_path_arcs.append(temp_path_arcs) - - return list_path_arcs - - else: - - return list_paths + # identify the last node's neighbours + current_neighbours = set( + neighbours(network, current_node, ignore_self_loops=True) + ) + # check each neighbour + # 1) if the neighbour is ahead and is a valid node: + # >> recursion w/ neighbour and then return the final path + # 2) if the neighbour is ahead and is not a valid node: + # >> add it to the path and then return it + # 3) if the neighbour is not ahead and is on the path: + # >> continue, namely to check the other neighbour + # 4) if the neighbour is not ahead and is not on the path: + # >> add it to the beginning of the path and continue + + # check each neighbour + for a_neighbour in current_neighbours: + # check the direction of edge towards the neighbour + # 1) node is not on the path + # 1.1) is a valid node >> path continues + # 1.2) is not a valid node >> end of path + # 2) node is on the path already + # 2.1) matches the start node = cycle + # 2.2) does not match the start node (reversed arc) = ignore + if a_neighbour not in path: + # neighbour is not on the path + if a_neighbour in list_valid_nodes: + # is a valid node: path continues + # add the neighbour to the start of the path + path.insert(0, a_neighbour) + # recursive call with extended path + return find_path_backward( + network, + path[0], + path + ) + else: # is not a valid node: path ends + # add the neighbour to the start of the path + path.insert(0, a_neighbour) + # return the path + return path + elif a_neighbour == path[-1] and len(path) >= 3: + # neighbour is already on the path, matches the end, and + # the path length is greater than or equal to 3 + # add the neighbour to the start of the path + path.insert(0, a_neighbour) + # return the path + return path + # all neighbours have been visited: return the current path + return path # TODO: reach statement + + # ************************************************************************* + + # find the path forward, check for cycles and then find the path backwards + # explore paths in the forward sense + path = find_path_forward( + network, + start_node, + [start_node] + ) + # check for cycles + if len(path) >= 3 and path[0] == path[-1]: + # it is a cycle: no need to search backwards + return path + # explore paths in the backward sense and return the path + return find_path_backward( + network, + path[0], + path + ) -#****************************************************************************** -#****************************************************************************** + # ************************************************************************* + # ************************************************************************* + +# ***************************************************************************** +# ***************************************************************************** def find_self_loops(network: nx.MultiDiGraph) -> list: """ @@ -2363,12 +1793,12 @@ def is_start_or_end_point(line: LineString, # ***************************************************************************** # ***************************************************************************** -def identify_arc_closest_to_node( +def identify_edge_closest_to_node( network: nx.MultiDiGraph, node_keys: list, crs: str = None) -> Tuple[list, nx.MultiDiGraph]: """ - Identify the arcs that are closest to a given set of nodes. + Identify the edges that are closest to a given set of nodes. The network object should formatted according to OSMnx standards. @@ -2387,8 +1817,8 @@ def identify_arc_closest_to_node( Returns ------- - nearest_arc_keys : list - The list of keys for the arcs that are closest to the nodes (1:1). + nearest_edge_keys : list + The list of keys for the edges that are closest to the nodes (1:1). network : nx.MultiDiGraph The object for the projected network. @@ -2397,7 +1827,7 @@ def identify_arc_closest_to_node( #************************************************************************** # 1) ensure that the network crs is correct and convert if not - # 2) identify the arcs that are nearest to the nodes + # 2) identify the edges that are nearest to the nodes # 3) revert network crs back to the original, if necessary #************************************************************************** @@ -2416,9 +1846,9 @@ def identify_arc_closest_to_node( #************************************************************************** - # 2) identify the arcs that are nearest to the nodes + # 2) identify the edges that are nearest to the nodes - nearest_arc_keys = nearest_edges( + nearest_edge_keys = nearest_edges( projected_network, X=[projected_network.nodes[node_key][osm.KEY_OSMNX_X] for node_key in node_keys], @@ -2428,7 +1858,7 @@ def identify_arc_closest_to_node( # return statement - return nearest_arc_keys, projected_network + return nearest_edge_keys, projected_network # ***************************************************************************** -# ***************************************************************************** \ No newline at end of file +# ***************************************************************************** diff --git a/src/topupopt/data/gis/modify.py b/src/topupopt/data/gis/modify.py index d041e92..2ff69db 100644 --- a/src/topupopt/data/gis/modify.py +++ b/src/topupopt/data/gis/modify.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - # standard library imports import math @@ -170,7 +168,7 @@ def transform_roundabouts_into_crossroads( # if it is not in the roundabout itself if other_node_key not in roundabout # for each arc between the two nodes - for edge_key in gis_iden.all_arcs_between_two_nodes( + for edge_key in gis_iden.get_edges_between_two_nodes( network, node_key, other_node_key) @@ -581,13 +579,13 @@ def replace_path( if ignore_directions: # edge directions can be ignored - edge_key = list(gis_iden.all_arcs_between_two_nodes( + edge_key = list(gis_iden.get_edges_between_two_nodes( network, path[node_pair_index], path[node_pair_index+1] ))[0] else: # edge directions cannot be ignored - edge_key = list(gis_iden.all_arcs_from_a_to_b( + edge_key = list(gis_iden.get_edges_from_a_to_b( network, path[node_pair_index], path[node_pair_index+1] @@ -755,7 +753,7 @@ def remove_redundant_arcs(network: nx.MultiDiGraph, # get the arcs between the two nodes - # list_arcs = gis_iden.all_arcs_between_two_nodes(network, + # list_arcs = gis_iden.get_edges_between_two_nodes(network, # node_one, # node_two) @@ -779,7 +777,7 @@ def remove_redundant_arcs(network: nx.MultiDiGraph, # get the arcs between the two nodes - list_arcs = gis_iden.all_arcs_from_a_to_b(network, + list_arcs = gis_iden.get_edges_from_a_to_b(network, node_one, node_two) @@ -847,13 +845,13 @@ def remove_longer_parallel_arcs(network: nx.MultiDiGraph, continue # get the arcs between the two nodes if ignore_arc_directions: # both directions - list_arcs = gis_iden.all_arcs_between_two_nodes( + list_arcs = gis_iden.get_edges_between_two_nodes( network, node_one, node_two ) else: # one direction - list_arcs = gis_iden.all_arcs_from_a_to_b( + list_arcs = gis_iden.get_edges_from_a_to_b( network, node_start=node_one, node_end=node_two @@ -1731,4 +1729,4 @@ def create_reverse_edges( return edges_created # ***************************************************************************** -# ***************************************************************************** \ No newline at end of file +# ***************************************************************************** diff --git a/src/topupopt/data/gis/utils.py b/src/topupopt/data/gis/utils.py index 3ea3216..2e6e082 100644 --- a/src/topupopt/data/gis/utils.py +++ b/src/topupopt/data/gis/utils.py @@ -928,7 +928,7 @@ def identify_building_entrance_arcs( node_keys = list(node_key_to_gdf_index_dict.keys()) - closest_arc_keys, network = gis_iden.identify_arc_closest_to_node( + closest_arc_keys, network = gis_iden.identify_edge_closest_to_node( network=network, node_keys=node_keys, crs=crs) # do not revert back to the original yet @@ -1032,14 +1032,14 @@ def identify_building_entrance_arcs( # get adjacent/neighbouring arcs - other_arcs = gis_iden.all_arcs_involving_node( + other_arcs = gis_iden.get_edges_involving_node( network=network, node_key=closest_arc_key[0], include_self_loops=False ) other_arcs.extend( - gis_iden.all_arcs_involving_node( + gis_iden.get_edges_involving_node( network=network, node_key=closest_arc_key[1], include_self_loops=False @@ -1215,4 +1215,4 @@ def convert_edge_path(edge_path: list) -> list: return out #****************************************************************************** -#****************************************************************************** \ No newline at end of file +#****************************************************************************** diff --git a/tests/examples_gis.py b/tests/examples_gis.py index c90190e..9a8d30a 100644 --- a/tests/examples_gis.py +++ b/tests/examples_gis.py @@ -652,7 +652,7 @@ def example_roundabouts_protocol(network: nx.MultiDiGraph, # if it is not in the roundabout itself if other_node_key not in original_roundabout_nodes # for each arc between the two nodes - for edge_key in gis_ident.all_arcs_between_two_nodes( + for edge_key in gis_ident.get_edges_between_two_nodes( network, node_key, other_node_key) @@ -769,7 +769,7 @@ def example_roundabouts_protocol(network: nx.MultiDiGraph, for ra_i in roundabout_candidates: - for arc_key in gis_ident.all_arcs_from_a_to_b( + for arc_key in gis_ident.get_edges_from_a_to_b( network, edge_key[0], new_roundabout_nodes[ra_i]): @@ -804,7 +804,7 @@ def example_roundabouts_protocol(network: nx.MultiDiGraph, for ra_i in roundabout_candidates: - for arc_key in gis_ident.all_arcs_from_a_to_b( + for arc_key in gis_ident.get_edges_from_a_to_b( network, new_roundabout_nodes[ra_i], edge_key[1]): @@ -856,7 +856,7 @@ def example_roundabouts_protocol(network: nx.MultiDiGraph, for ra_i in roundabout_candidates: - for arc_key in gis_ident.all_arcs_from_a_to_b( + for arc_key in gis_ident.get_edges_from_a_to_b( network, edge_key[0], new_roundabout_nodes[ra_i]): @@ -893,7 +893,7 @@ def example_roundabouts_protocol(network: nx.MultiDiGraph, for ra_i in roundabout_candidates: - for arc_key in gis_ident.all_arcs_from_a_to_b( + for arc_key in gis_ident.get_edges_from_a_to_b( network, new_roundabout_nodes[ra_i], edge_key[1]): @@ -945,7 +945,7 @@ def example_roundabouts_protocol(network: nx.MultiDiGraph, for ra_si_i in roundabout_candidates_sink: - for arc_key in gis_ident.all_arcs_from_a_to_b( + for arc_key in gis_ident.get_edges_from_a_to_b( network, new_roundabout_nodes[ra_so_i], new_roundabout_nodes[ra_si_i]): @@ -1011,7 +1011,7 @@ def example_intermediate_nodes_along_path(network: nx.MultiDiGraph, # replace the path - gis_mod.old_replace_path(network, + gis_mod.replace_path(network, path, path_as_arc_keys=return_paths_as_arc_keys) @@ -1105,7 +1105,7 @@ def example_removal_redundant_arcs(seed_number: int = None): for arc in network.edges(keys=True): assert len( - gis_ident.all_arcs_from_a_to_b(network,arc[0],arc[1]) + gis_ident.get_edges_from_a_to_b(network,arc[0],arc[1]) ) == 1 #************************************************************************** diff --git a/tests/examples_gis_utils.py b/tests/examples_gis_utils.py index c628dcd..d591726 100644 --- a/tests/examples_gis_utils.py +++ b/tests/examples_gis_utils.py @@ -933,7 +933,7 @@ def example_identify_entrances_simple_no_driveway_closest(): # find out which is the closest arc - nearest_arc_keys, network = gis_iden.identify_arc_closest_to_node( + nearest_arc_keys, network = gis_iden.identify_edge_closest_to_node( network, node_keys=['P']) @@ -2025,7 +2025,7 @@ def example_get_directed(network: nx.MultiDiGraph): edge_dict = directed_network.edges[edge_key] - for other_edge_key in gis_iden.all_arcs_from_a_to_b( + for other_edge_key in gis_iden.get_edges_from_a_to_b( network, edge_key[0], edge_key[1]): # check all attributes @@ -2060,4 +2060,4 @@ def example_get_directed(network: nx.MultiDiGraph): assert number_matching_attributes == len(edge_dict) #****************************************************************************** -#****************************************************************************** \ No newline at end of file +#****************************************************************************** diff --git a/tests/test_gis_identify.py b/tests/test_gis_identify.py index 7377f03..90b5637 100644 --- a/tests/test_gis_identify.py +++ b/tests/test_gis_identify.py @@ -29,8 +29,7 @@ class TestGisIdentify: network: nx.MultiDiGraph, path: list, excluded_nodes: list, - paths_must_be_oneway: bool = True, - paths_must_be_unique: bool = True): + consider_reversed_edges: bool): # find out the unique nodes set_nodes = set(path) # at least three nodes @@ -40,7 +39,7 @@ class TestGisIdentify: # count the nodes node_count = path.count(node) if node_count >= 3: - # the same node cannot appear three times + # the same node cannot appear three or more times assert False elif node_count == 2: # cycle: the first and last nodes must match @@ -49,542 +48,1712 @@ class TestGisIdentify: # no excluded nodes in the intermediate positions for node in excluded_nodes: assert node not in path[1:-1] - # make sure it qualifies as a pth - if paths_must_be_oneway and paths_must_be_oneway: - assert gis_iden.is_node_path(network, path) - + # make sure it qualifies as a path + assert gis_iden.is_node_path( + network, + path, + consider_reversed_edges=consider_reversed_edges + ) + # ************************************************************************* # ************************************************************************* - def test_find_straight_paths_case_a(self): + def test_find_straight_path_empty_graph(self): + + # test variations: + # 1) excluded nodes + # 2) self loops + # 3) reversed edges - # case a: - paths_must_be_oneway = True # reverse-parallel arcs prevent paths - paths_must_be_unique = True # anti-/parallel arcs prevent paths + # ********************************************************************* + # ********************************************************************* - # network without straight paths + # network with a two edge path network = nx.MultiDiGraph() + + # no reversed edges, no self loops, no excluded nodes + consider_reversed_edges = False + ignore_self_loops = False excluded_nodes = [] - straight_paths = gis_iden.find_straight_paths( + + # test path validator with non-path + error_raised = False + try: + assert not self.path_validator( + network, + [1, 1, 1], + excluded_nodes, + False + ) + except AssertionError: + error_raised = True + assert error_raised + + straight_paths = gis_iden.find_simplifiable_paths( network, excluded_nodes, - paths_must_be_oneway=paths_must_be_oneway, - paths_must_be_unique=paths_must_be_unique + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops ) true_straight_paths = [] assert len(straight_paths) == len(true_straight_paths) assert len(straight_paths) == 0 - + # ********************************************************************* - - # network with the simplest straight path - network = nx.MultiDiGraph() - network.add_edges_from([ - (0, 1, 0), - (1, 2, 0), - # simple cycle - (3, 4, 0), - (4, 5, 0), - (5, 3, 0), - # two node loop >> not a straight path because it is not one-way - (6, 7, 0), - (7, 6, 0) - ]) + # ********************************************************************* + + # no reversed edges, no self loops, no excluded nodes + consider_reversed_edges = False + ignore_self_loops = True excluded_nodes = [] - straight_paths = gis_iden.find_straight_paths( + + straight_paths = gis_iden.find_simplifiable_paths( network, excluded_nodes, - paths_must_be_oneway=paths_must_be_oneway, - paths_must_be_unique=paths_must_be_unique + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops ) - true_straight_paths = [[0, 1, 2], [3, 4, 5, 3]] + true_straight_paths = [] assert len(straight_paths) == len(true_straight_paths) - for straight_path in straight_paths: - assert straight_path in true_straight_paths - self.path_validator(network, straight_path, excluded_nodes) - - # try a path in which a node appears three times + assert len(straight_paths) == 0 - error_raised = False - try: - self.path_validator(network, [0, 1, 2, 1, 3, 1], excluded_nodes) - except AssertionError: - error_raised = True - assert error_raised - # ********************************************************************* - - # network with longer path + # ********************************************************************* + + # network with a two edge path network = nx.MultiDiGraph() - network.add_edges_from([ - (0, 1, 0), - (1, 2, 0), - (2, 3, 0), - # - (4, 5, 0), - (5, 6, 0), - (6, 7, 0), - (7, 8, 0) - ]) + + # no reversed edges, no self loops, no excluded nodes + consider_reversed_edges = True + ignore_self_loops = False excluded_nodes = [] - straight_paths = gis_iden.find_straight_paths( + + straight_paths = gis_iden.find_simplifiable_paths( network, excluded_nodes, - paths_must_be_oneway=paths_must_be_oneway, - paths_must_be_unique=paths_must_be_unique + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops ) - true_straight_paths = [[0, 1, 2, 3], [4, 5, 6, 7, 8]] + true_straight_paths = [] assert len(straight_paths) == len(true_straight_paths) - for straight_path in straight_paths: - assert straight_path in true_straight_paths - self.path_validator(network, straight_path, excluded_nodes) + assert len(straight_paths) == 0 - #************************************************************************** - - # network with excluded nodes - + # ********************************************************************* + # ********************************************************************* + + # network with a two edge path network = nx.MultiDiGraph() - network.add_edges_from([ - (0, 1, 0), - (1, 2, 0), - (2, 3, 0), - (3, 4, 0), - ]) - excluded_nodes = [2] - straight_paths = gis_iden.find_straight_paths( + + # no reversed edges, no self loops, no excluded nodes + consider_reversed_edges = True + ignore_self_loops = True + excluded_nodes = [] + + straight_paths = gis_iden.find_simplifiable_paths( network, excluded_nodes, - paths_must_be_oneway=paths_must_be_oneway, - paths_must_be_unique=paths_must_be_unique + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops ) - true_straight_paths = [[0, 1, 2], [2, 3, 4]] + true_straight_paths = [] assert len(straight_paths) == len(true_straight_paths) - for straight_path in straight_paths: - assert straight_path in true_straight_paths - self.path_validator(network, straight_path, excluded_nodes) - - #************************************************************************** + assert len(straight_paths) == 0 + + # ********************************************************************* + # ********************************************************************* + + # enhance test coverage + + # add single node + network.add_node(0) + + path = gis_iden._find_path_direction_insensitive(network, [], 0) + assert type(path) == list + assert len(path) == 1 + assert repr(path) == repr([0]) + + # ************************************************************************* + # ************************************************************************* - # network with self loops and excluded nodes + def test_find_one_edge_path(self): + # ********************************************************************* + # ********************************************************************* + + # network with a two edge path network = nx.MultiDiGraph() network.add_edges_from([ - (0, 1, 0), - (2, 2, 0), - (1, 2, 0), - (2, 3, 0), - (3, 3, 0), - (3, 4, 0), + (0, 1, 0) ]) - excluded_nodes = [2] - straight_paths = gis_iden.find_straight_paths( + + # ********************************************************************* + # ********************************************************************* + + # do not consider reversed edges + consider_reversed_edges = False + + # ********************************************************************* + # ********************************************************************* + + # no reversed edges, no self loops, no excluded nodes + ignore_self_loops = False + excluded_nodes = [] + + straight_paths = gis_iden.find_simplifiable_paths( network, excluded_nodes, - paths_must_be_oneway=paths_must_be_oneway, - paths_must_be_unique=paths_must_be_unique + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops ) - true_straight_paths = [[0, 1, 2], [2, 3, 4]] + true_straight_paths = [] assert len(straight_paths) == len(true_straight_paths) - for straight_path in straight_paths: - assert straight_path in true_straight_paths - self.path_validator(network, straight_path, excluded_nodes) - - #************************************************************************** - - # network with parallel arcs + + # ********************************************************************* + # ********************************************************************* + + # no reversed edges, allow self loops, no excluded nodes + ignore_self_loops = True + excluded_nodes = [] - network = nx.MultiDiGraph() - network.add_edges_from([ - (0, 1, 0), - (1, 2, 0), - (0, 1, 1), - (2, 3, 0), - (3, 4, 0), - ]) + straight_paths = gis_iden.find_simplifiable_paths( + network, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops + ) + true_straight_paths = [] + assert len(straight_paths) == len(true_straight_paths) + + # ********************************************************************* + # ********************************************************************* + + # do not allow reversed edges, no self loops, excluded the middle node + ignore_self_loops = False + excluded_nodes = [1] + + straight_paths = gis_iden.find_simplifiable_paths( + network, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops + ) + true_straight_paths = [] + assert len(straight_paths) == len(true_straight_paths) + + # ********************************************************************* + # ********************************************************************* + + # do not allow reversed edges, no self loops, excluded the start node + ignore_self_loops = False + excluded_nodes = [0] + + straight_paths = gis_iden.find_simplifiable_paths( + network, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops + ) + true_straight_paths = [] + assert len(straight_paths) == len(true_straight_paths) + + # ********************************************************************* + # ********************************************************************* + + # do not allow reversed edges, no self loops, excluded the end node + ignore_self_loops = False excluded_nodes = [2] - straight_paths = gis_iden.find_straight_paths( + + straight_paths = gis_iden.find_simplifiable_paths( network, excluded_nodes, - paths_must_be_oneway=paths_must_be_oneway, - paths_must_be_unique=paths_must_be_unique + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops ) - true_straight_paths = [[2,3,4]] # [0,1,2] involves parallel arcs + true_straight_paths = [] assert len(straight_paths) == len(true_straight_paths) - for straight_path in straight_paths: - assert straight_path in true_straight_paths - self.path_validator(network, straight_path, excluded_nodes) - - #************************************************************************** - - # network with parallel and anti-parallel arcs - - network = nx.MultiDiGraph() - network.add_edges_from([ - (0, 1, 0), - (1, 2, 0), - (0, 1, 1), - (1, 2, 1), - (2, 3, 0), - (3, 4, 0), - (3, 2, 0), - (4, 3, 0), - ]) + + # ********************************************************************* + # ********************************************************************* + + # consider reversed edges + consider_reversed_edges = True + + # ********************************************************************* + # ********************************************************************* + + # allow reversed edges, allow self loops, no excluded nodes + ignore_self_loops = True + excluded_nodes = [] + + straight_paths = gis_iden.find_simplifiable_paths( + network, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops + ) + true_straight_paths = [] + assert len(straight_paths) == len(true_straight_paths) + + # ********************************************************************* + # ********************************************************************* + + # allow reversed edges, no self loops, no excluded nodes + ignore_self_loops = False + excluded_nodes = [] + + straight_paths = gis_iden.find_simplifiable_paths( + network, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops + ) + true_straight_paths = [] + assert len(straight_paths) == len(true_straight_paths) + + # ********************************************************************* + # ********************************************************************* + + # allow reversed edges, no self loops, excluded the middle node + ignore_self_loops = False + excluded_nodes = [1] + + straight_paths = gis_iden.find_simplifiable_paths( + network, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops + ) + true_straight_paths = [] + assert len(straight_paths) == len(true_straight_paths) + + # ********************************************************************* + # ********************************************************************* + + # allow reversed edges, no self loops, excluded the start node + ignore_self_loops = False + excluded_nodes = [0] + + straight_paths = gis_iden.find_simplifiable_paths( + network, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops + ) + true_straight_paths = [] + assert len(straight_paths) == len(true_straight_paths) + + # ********************************************************************* + # ********************************************************************* + + # allow reversed edges, no self loops, excluded the end node + ignore_self_loops = False excluded_nodes = [2] - straight_paths = gis_iden.find_straight_paths( + + straight_paths = gis_iden.find_simplifiable_paths( network, excluded_nodes, - paths_must_be_oneway=paths_must_be_oneway, - paths_must_be_unique=paths_must_be_unique + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops ) - true_straight_paths = [] # [0,1,2] has parallel arcs, [2,3,4] anti-parallel + true_straight_paths = [] assert len(straight_paths) == len(true_straight_paths) - assert len(straight_paths) == 0 - - #************************************************************************** + + # ********************************************************************* + # ********************************************************************* + + # ************************************************************************* + # ************************************************************************* - # network with cycles + def test_find_one_edge_path_w_reversed_edge(self): + # ********************************************************************* + # ********************************************************************* + + # network with a two edge path network = nx.MultiDiGraph() network.add_edges_from([ - # simplest cycle (with reverse-parallel arcs) - (0, 1, 0), - (1, 0, 0), - # 3-node cycle (with/without reverse-/-parallel arcs) - (2, 3, 0), - (3, 4, 0), - (4, 2, 0), - # 2-node cycle (with parallel and reverse-parallel arcs) - (5, 6, 0), - (6, 5, 0), - (5, 6, 1), - (6, 5, 1), - # 3-node cycle (only without parallel arcs) - (7, 8, 0), - (8, 9, 0), - (9, 7, 0), - (9, 7, 1) + (0, 1, 0), (1, 0, 0) ]) + + # ********************************************************************* + # ********************************************************************* + + # do not consider reversed edges + consider_reversed_edges = False + + # ********************************************************************* + # ********************************************************************* + + # no reversed edges, no self loops, no excluded nodes + ignore_self_loops = False + excluded_nodes = [] + + straight_paths = gis_iden.find_simplifiable_paths( + network, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops + ) + true_straight_paths = [] + assert len(straight_paths) == len(true_straight_paths) + + # ********************************************************************* + # ********************************************************************* + + # no reversed edges, allow self loops, no excluded nodes + ignore_self_loops = True + excluded_nodes = [] + + straight_paths = gis_iden.find_simplifiable_paths( + network, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops + ) + true_straight_paths = [] + assert len(straight_paths) == len(true_straight_paths) + + # ********************************************************************* + # ********************************************************************* + + # do not allow reversed edges, no self loops, excluded the middle node + ignore_self_loops = False + excluded_nodes = [1] + + straight_paths = gis_iden.find_simplifiable_paths( + network, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops + ) + true_straight_paths = [] + assert len(straight_paths) == len(true_straight_paths) + + # ********************************************************************* + # ********************************************************************* + + # do not allow reversed edges, no self loops, excluded the start node + ignore_self_loops = False + excluded_nodes = [0] + + straight_paths = gis_iden.find_simplifiable_paths( + network, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops + ) + true_straight_paths = [] + assert len(straight_paths) == len(true_straight_paths) + + # ********************************************************************* + # ********************************************************************* + + # do not allow reversed edges, no self loops, excluded the end node + ignore_self_loops = False + excluded_nodes = [2] + + straight_paths = gis_iden.find_simplifiable_paths( + network, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops + ) + true_straight_paths = [] + assert len(straight_paths) == len(true_straight_paths) + + # ********************************************************************* + # ********************************************************************* + + # consider reversed edges + consider_reversed_edges = True + + # ********************************************************************* + # ********************************************************************* + + # allow reversed edges, allow self loops, no excluded nodes + ignore_self_loops = True + excluded_nodes = [] + + straight_paths = gis_iden.find_simplifiable_paths( + network, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops + ) + true_straight_paths = [] + assert len(straight_paths) == len(true_straight_paths) + + # ********************************************************************* + # ********************************************************************* + + # allow reversed edges, no self loops, no excluded nodes + ignore_self_loops = False + excluded_nodes = [] + + straight_paths = gis_iden.find_simplifiable_paths( + network, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops + ) + true_straight_paths = [] + assert len(straight_paths) == len(true_straight_paths) + + # ********************************************************************* + # ********************************************************************* + + # allow reversed edges, no self loops, excluded the middle node + ignore_self_loops = False + excluded_nodes = [1] + + straight_paths = gis_iden.find_simplifiable_paths( + network, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops + ) + true_straight_paths = [] + assert len(straight_paths) == len(true_straight_paths) + + # ********************************************************************* + # ********************************************************************* + + # allow reversed edges, no self loops, excluded the start node + ignore_self_loops = False + excluded_nodes = [0] + + straight_paths = gis_iden.find_simplifiable_paths( + network, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops + ) + true_straight_paths = [] + assert len(straight_paths) == len(true_straight_paths) + + # ********************************************************************* + # ********************************************************************* + + # allow reversed edges, no self loops, excluded the end node + ignore_self_loops = False + excluded_nodes = [2] + + straight_paths = gis_iden.find_simplifiable_paths( + network, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops + ) + true_straight_paths = [] + assert len(straight_paths) == len(true_straight_paths) + + # ********************************************************************* + # ********************************************************************* + + # ************************************************************************* + # ************************************************************************* + + def test_find_simple_straight_path(self): + + # ********************************************************************* + # ********************************************************************* + + # network with a two edge path + network = nx.MultiDiGraph() + network.add_edges_from([ + (0, 1, 0), (1, 2, 0) + ]) + + # ********************************************************************* + # ********************************************************************* + + # do not consider reversed edges + consider_reversed_edges = False + + # ********************************************************************* + # ********************************************************************* + + # no reversed edges, no self loops, no excluded nodes + ignore_self_loops = False + excluded_nodes = [] + + straight_paths = gis_iden.find_simplifiable_paths( + network, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops + ) + true_straight_paths = [[0, 1, 2]] + assert len(straight_paths) == len(true_straight_paths) + for straight_path in straight_paths: + assert straight_path in true_straight_paths + self.path_validator( + network, + straight_path, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges + ) + + # ********************************************************************* + # ********************************************************************* + + # no reversed edges, allow self loops, no excluded nodes + ignore_self_loops = True + excluded_nodes = [] + + straight_paths = gis_iden.find_simplifiable_paths( + network, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops + ) + true_straight_paths = [[0, 1, 2]] + assert len(straight_paths) == len(true_straight_paths) + for straight_path in straight_paths: + assert straight_path in true_straight_paths + self.path_validator( + network, + straight_path, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges + ) + + # ********************************************************************* + # ********************************************************************* + + # do not allow reversed edges, no self loops, excluded the middle node + ignore_self_loops = False + excluded_nodes = [1] + + straight_paths = gis_iden.find_simplifiable_paths( + network, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops + ) + true_straight_paths = [] + assert len(straight_paths) == len(true_straight_paths) + + # ********************************************************************* + # ********************************************************************* + + # do not allow reversed edges, no self loops, excluded the start node + ignore_self_loops = False + excluded_nodes = [0] + + straight_paths = gis_iden.find_simplifiable_paths( + network, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops + ) + true_straight_paths = [[0, 1, 2]] + assert len(straight_paths) == len(true_straight_paths) + for straight_path in straight_paths: + assert straight_path in true_straight_paths + self.path_validator( + network, + straight_path, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges + ) + + # ********************************************************************* + # ********************************************************************* + + # do not allow reversed edges, no self loops, excluded the end node + ignore_self_loops = False + excluded_nodes = [2] + + straight_paths = gis_iden.find_simplifiable_paths( + network, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops + ) + true_straight_paths = [[0, 1, 2]] + assert len(straight_paths) == len(true_straight_paths) + for straight_path in straight_paths: + assert straight_path in true_straight_paths + self.path_validator( + network, + straight_path, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges + ) + + # ********************************************************************* + # ********************************************************************* + + # consider reversed edges + consider_reversed_edges = True + + # ********************************************************************* + # ********************************************************************* + + # allow reversed edges, allow self loops, no excluded nodes + ignore_self_loops = True + excluded_nodes = [] + + straight_paths = gis_iden.find_simplifiable_paths( + network, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops + ) + true_straight_paths = [[0, 1, 2], [2, 1, 0]] + assert len(straight_paths) == len(true_straight_paths)-1 + for straight_path in straight_paths: + assert straight_path in true_straight_paths + self.path_validator( + network, + straight_path, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges + ) + + # ********************************************************************* + # ********************************************************************* + + # allow reversed edges, no self loops, no excluded nodes + ignore_self_loops = False + excluded_nodes = [] + + straight_paths = gis_iden.find_simplifiable_paths( + network, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops + ) + true_straight_paths = [[0, 1, 2], [2, 1, 0]] + assert len(straight_paths) == len(true_straight_paths)-1 + for straight_path in straight_paths: + assert straight_path in true_straight_paths + self.path_validator( + network, + straight_path, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges + ) + + # ********************************************************************* + # ********************************************************************* + + # allow reversed edges, no self loops, excluded the middle node + ignore_self_loops = False + excluded_nodes = [1] + + straight_paths = gis_iden.find_simplifiable_paths( + network, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops + ) + true_straight_paths = [] + assert len(straight_paths) == len(true_straight_paths) + + # ********************************************************************* + # ********************************************************************* + + # allow reversed edges, no self loops, excluded the start node + ignore_self_loops = False + excluded_nodes = [0] + + straight_paths = gis_iden.find_simplifiable_paths( + network, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops + ) + true_straight_paths = [[0, 1, 2], [2, 1, 0]] + assert len(straight_paths) == len(true_straight_paths)-1 + for straight_path in straight_paths: + assert straight_path in true_straight_paths + self.path_validator( + network, + straight_path, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges + ) + + # ********************************************************************* + # ********************************************************************* + + # allow reversed edges, no self loops, excluded the end node + ignore_self_loops = False + excluded_nodes = [2] + + straight_paths = gis_iden.find_simplifiable_paths( + network, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops + ) + true_straight_paths = [[0, 1, 2], [2, 1, 0]] + assert len(straight_paths) == len(true_straight_paths)-1 + for straight_path in straight_paths: + assert straight_path in true_straight_paths + self.path_validator( + network, + straight_path, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges + ) + + # ********************************************************************* + # ********************************************************************* + + # ************************************************************************* + # ************************************************************************* + + def test_find_simple_straight_path_with_antiparallel_edge(self): + + # ********************************************************************* + # ********************************************************************* + + # network with a two edge path + network = nx.MultiDiGraph() + network.add_edges_from([ + (0, 1, 0), (1, 2, 0), (2, 1, 0) + ]) + + # ********************************************************************* + # ********************************************************************* + + # do not consider reversed edges + consider_reversed_edges = False + + # ********************************************************************* + # ********************************************************************* + + # no reversed edges, no self loops, no excluded nodes + ignore_self_loops = False + excluded_nodes = [] + + straight_paths = gis_iden.find_simplifiable_paths( + network, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops + ) + true_straight_paths = [[0, 1, 2]] + assert len(straight_paths) == len(true_straight_paths) + for straight_path in straight_paths: + assert straight_path in true_straight_paths + self.path_validator( + network, + straight_path, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges + ) + + # ********************************************************************* + # ********************************************************************* + + # no reversed edges, allow self loops, no excluded nodes + ignore_self_loops = True + excluded_nodes = [] + + straight_paths = gis_iden.find_simplifiable_paths( + network, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops + ) + true_straight_paths = [[0, 1, 2]] + assert len(straight_paths) == len(true_straight_paths) + for straight_path in straight_paths: + assert straight_path in true_straight_paths + self.path_validator( + network, + straight_path, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges + ) + + # ********************************************************************* + # ********************************************************************* + + # do not allow reversed edges, no self loops, excluded the middle node + ignore_self_loops = False + excluded_nodes = [1] + + straight_paths = gis_iden.find_simplifiable_paths( + network, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops + ) + true_straight_paths = [] + assert len(straight_paths) == len(true_straight_paths) + + # ********************************************************************* + # ********************************************************************* + + # do not allow reversed edges, no self loops, excluded the start node + ignore_self_loops = False + excluded_nodes = [0] + + straight_paths = gis_iden.find_simplifiable_paths( + network, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops + ) + true_straight_paths = [[0, 1, 2]] + assert len(straight_paths) == len(true_straight_paths) + for straight_path in straight_paths: + assert straight_path in true_straight_paths + self.path_validator( + network, + straight_path, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges + ) + + # ********************************************************************* + # ********************************************************************* + + # do not allow reversed edges, no self loops, excluded the end node + ignore_self_loops = False + excluded_nodes = [2] + + straight_paths = gis_iden.find_simplifiable_paths( + network, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops + ) + true_straight_paths = [[0, 1, 2]] + assert len(straight_paths) == len(true_straight_paths) + for straight_path in straight_paths: + assert straight_path in true_straight_paths + self.path_validator( + network, + straight_path, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges + ) + + # ********************************************************************* + # ********************************************************************* + + # consider reversed edges + consider_reversed_edges = True + + # ********************************************************************* + # ********************************************************************* + + # allow reversed edges, allow self loops, no excluded nodes + ignore_self_loops = True + excluded_nodes = [] + + straight_paths = gis_iden.find_simplifiable_paths( + network, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops + ) + true_straight_paths = [[0, 1, 2], [2, 1, 0]] + assert len(straight_paths) == len(true_straight_paths)-1 + for straight_path in straight_paths: + assert straight_path in true_straight_paths + self.path_validator( + network, + straight_path, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges + ) + + # ********************************************************************* + # ********************************************************************* + + # allow reversed edges, no self loops, no excluded nodes + ignore_self_loops = False + excluded_nodes = [] + + straight_paths = gis_iden.find_simplifiable_paths( + network, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops + ) + true_straight_paths = [[0, 1, 2], [2, 1, 0]] + assert len(straight_paths) == len(true_straight_paths)-1 + for straight_path in straight_paths: + assert straight_path in true_straight_paths + self.path_validator( + network, + straight_path, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges + ) + + # ********************************************************************* + # ********************************************************************* + + # allow reversed edges, no self loops, excluded the middle node + ignore_self_loops = False + excluded_nodes = [1] + + straight_paths = gis_iden.find_simplifiable_paths( + network, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops + ) + true_straight_paths = [] + assert len(straight_paths) == len(true_straight_paths) + + # ********************************************************************* + # ********************************************************************* + + # allow reversed edges, no self loops, excluded the start node + ignore_self_loops = False + excluded_nodes = [0] + + straight_paths = gis_iden.find_simplifiable_paths( + network, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops + ) + true_straight_paths = [[0, 1, 2], [2, 1, 0]] + assert len(straight_paths) == len(true_straight_paths)-1 + for straight_path in straight_paths: + assert straight_path in true_straight_paths + self.path_validator( + network, + straight_path, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges + ) + + # ********************************************************************* + # ********************************************************************* + + # allow reversed edges, no self loops, excluded the end node + ignore_self_loops = False + excluded_nodes = [2] + + straight_paths = gis_iden.find_simplifiable_paths( + network, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops + ) + true_straight_paths = [[0, 1, 2], [2, 1, 0]] + assert len(straight_paths) == len(true_straight_paths)-1 + for straight_path in straight_paths: + assert straight_path in true_straight_paths + self.path_validator( + network, + straight_path, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges + ) + + # ********************************************************************* + # ********************************************************************* + + # ************************************************************************* + # ************************************************************************* + + def test_find_straight_path_cycle(self): + + # ********************************************************************* + # ********************************************************************* + + # network with a two edge path + network = nx.MultiDiGraph() + network.add_edges_from([ + (0, 1, 0), (1, 2, 0), (2, 0, 0), (0, 2, 0), (1, 1, 0) + ]) + + # ********************************************************************* + # ********************************************************************* + + # do not consider reversed edges + consider_reversed_edges = False + + # ********************************************************************* + # ********************************************************************* + + # no self loops, no excluded nodes + ignore_self_loops = False + excluded_nodes = [] + + straight_paths = gis_iden.find_simplifiable_paths( + network, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops + ) + true_straight_paths = [[1, 2, 0, 1]] + assert len(straight_paths) == len(true_straight_paths) + for straight_path in straight_paths: + assert straight_path in true_straight_paths + self.path_validator( + network, + straight_path, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges + ) + + # ********************************************************************* + # ********************************************************************* + + # allow self loops, no excluded nodes + ignore_self_loops = True + excluded_nodes = [] + + straight_paths = gis_iden.find_simplifiable_paths( + network, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops + ) + true_straight_paths = [[0, 1, 2, 0], [1, 2, 0, 1], [2, 1, 0, 2]] + assert len(straight_paths) == len(true_straight_paths)-2 + for straight_path in straight_paths: + assert straight_path in true_straight_paths + self.path_validator( + network, + straight_path, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges + ) + + # ********************************************************************* + # ********************************************************************* + + # no self loops, excluded the "middle" node + ignore_self_loops = False + excluded_nodes = [1] + + straight_paths = gis_iden.find_simplifiable_paths( + network, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops + ) + true_straight_paths = [[1, 2, 0, 1]] + assert len(straight_paths) == len(true_straight_paths) + for straight_path in straight_paths: + assert straight_path in true_straight_paths + self.path_validator( + network, + straight_path, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges + ) + + # ********************************************************************* + # ********************************************************************* + + # no self loops, excluded the start node + ignore_self_loops = False + excluded_nodes = [0] + + straight_paths = gis_iden.find_simplifiable_paths( + network, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops + ) + true_straight_paths = [[1, 2, 0]] + assert len(straight_paths) == len(true_straight_paths) + for straight_path in straight_paths: + assert straight_path in true_straight_paths + self.path_validator( + network, + straight_path, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges + ) + + # ********************************************************************* + # ********************************************************************* + + # do not allow reversed edges, no self loops, excluded the end node + ignore_self_loops = False + excluded_nodes = [2] + + straight_paths = gis_iden.find_simplifiable_paths( + network, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops + ) + true_straight_paths = [[2, 0, 1]] + assert len(straight_paths) == len(true_straight_paths) + for straight_path in straight_paths: + assert straight_path in true_straight_paths + self.path_validator( + network, + straight_path, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges + ) + + # ********************************************************************* + # ********************************************************************* + + # consider reversed edges + consider_reversed_edges = True + + # ********************************************************************* + # ********************************************************************* + + # allow reversed edges, allow self loops, no excluded nodes + ignore_self_loops = True + excluded_nodes = [] + + straight_paths = gis_iden.find_simplifiable_paths( + network, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops + ) + true_straight_paths = [[0, 1, 2, 0], [1, 2, 0, 1], [2, 1, 0, 2]] + assert len(straight_paths) == len(true_straight_paths)-2 + for straight_path in straight_paths: + assert straight_path in true_straight_paths + self.path_validator( + network, + straight_path, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges + ) + + # ********************************************************************* + # ********************************************************************* + + # allow reversed edges, no self loops, no excluded nodes + ignore_self_loops = False + excluded_nodes = [] + + straight_paths = gis_iden.find_simplifiable_paths( + network, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops + ) + true_straight_paths = [[1, 2, 0, 1], [1, 0, 2, 1]] + assert len(straight_paths) == len(true_straight_paths)-1 + for straight_path in straight_paths: + assert straight_path in true_straight_paths + self.path_validator( + network, + straight_path, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges + ) + + # ********************************************************************* + # ********************************************************************* + + # allow reversed edges, no self loops, excluded the middle node + ignore_self_loops = False + excluded_nodes = [1] + + straight_paths = gis_iden.find_simplifiable_paths( + network, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops + ) + true_straight_paths = [[1, 2, 0, 1], [1, 0, 2, 1]] + assert len(straight_paths) == len(true_straight_paths)-1 + for straight_path in straight_paths: + assert straight_path in true_straight_paths + self.path_validator( + network, + straight_path, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges + ) + + # ********************************************************************* + # ********************************************************************* + + # allow reversed edges, no self loops, excluded the start node + ignore_self_loops = False + excluded_nodes = [0] + + straight_paths = gis_iden.find_simplifiable_paths( + network, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops + ) + true_straight_paths = [[1, 2, 0], [0, 2, 1]] + assert len(straight_paths) == len(true_straight_paths)-1 + for straight_path in straight_paths: + assert straight_path in true_straight_paths + self.path_validator( + network, + straight_path, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges + ) + + # ********************************************************************* + # ********************************************************************* + + # allow reversed edges, no self loops, excluded the end node + ignore_self_loops = False + excluded_nodes = [2] + + straight_paths = gis_iden.find_simplifiable_paths( + network, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops + ) + true_straight_paths = [[1, 0, 2], [2, 0, 1]] + assert len(straight_paths) == len(true_straight_paths)-1 + for straight_path in straight_paths: + assert straight_path in true_straight_paths + self.path_validator( + network, + straight_path, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges + ) + + # ********************************************************************* + # ********************************************************************* + + # ************************************************************************* + # ************************************************************************* + + def test_find_simple_straight_path_w_reversed_edge(self): + + # ********************************************************************* + # ********************************************************************* + + # network with a two edge path + network = nx.MultiDiGraph() + network.add_edges_from([ + (0, 1, 0), (2, 1, 0) + ]) + + # ********************************************************************* + # ********************************************************************* + + # consider reversed edges + consider_reversed_edges = False + + # ********************************************************************* + # ********************************************************************* + + # no reversed edges, no self loops, no excluded nodes + ignore_self_loops = False + excluded_nodes = [] + + straight_paths = gis_iden.find_simplifiable_paths( + network, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops + ) + true_straight_paths = [] + assert len(straight_paths) == len(true_straight_paths) + + # ********************************************************************* + # ********************************************************************* + + # no reversed edges, allow self loops, no excluded nodes + ignore_self_loops = True + excluded_nodes = [] + + straight_paths = gis_iden.find_simplifiable_paths( + network, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops + ) + true_straight_paths = [] + assert len(straight_paths) == len(true_straight_paths) + + # ********************************************************************* + # ********************************************************************* + + # do not allow reversed edges, no self loops, excluded the middle node + ignore_self_loops = False + excluded_nodes = [1] + + straight_paths = gis_iden.find_simplifiable_paths( + network, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops + ) + true_straight_paths = [] + assert len(straight_paths) == len(true_straight_paths) + + # ********************************************************************* + # ********************************************************************* + + # do not allow reversed edges, no self loops, excluded the start node + ignore_self_loops = False + excluded_nodes = [0] + + straight_paths = gis_iden.find_simplifiable_paths( + network, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops + ) + true_straight_paths = [] + assert len(straight_paths) == len(true_straight_paths) + + # ********************************************************************* + # ********************************************************************* + + # do not allow reversed edges, no self loops, excluded the end node + ignore_self_loops = False + excluded_nodes = [2] + + straight_paths = gis_iden.find_simplifiable_paths( + network, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops + ) + true_straight_paths = [] + assert len(straight_paths) == len(true_straight_paths) + + # ********************************************************************* + # ********************************************************************* + + # consider reversed edges + consider_reversed_edges = True + + # ********************************************************************* + # ********************************************************************* + + # allow reversed edges, allow self loops, no excluded nodes + ignore_self_loops = True + excluded_nodes = [] + + straight_paths = gis_iden.find_simplifiable_paths( + network, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops + ) + true_straight_paths = [[0, 1, 2], [2, 1, 0]] + assert len(straight_paths) == len(true_straight_paths)-1 + for straight_path in straight_paths: + assert straight_path in true_straight_paths + self.path_validator( + network, + straight_path, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges + ) + + # ********************************************************************* + # ********************************************************************* + + # allow reversed edges, no self loops, no excluded nodes + ignore_self_loops = False + excluded_nodes = [] + + straight_paths = gis_iden.find_simplifiable_paths( + network, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops + ) + true_straight_paths = [[0, 1, 2], [2, 1, 0]] + assert len(straight_paths) == len(true_straight_paths)-1 + for straight_path in straight_paths: + assert straight_path in true_straight_paths + self.path_validator( + network, + straight_path, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges + ) + + # ********************************************************************* + # ********************************************************************* + + # allow reversed edges, no self loops, excluded the middle node + ignore_self_loops = False + excluded_nodes = [1] + + straight_paths = gis_iden.find_simplifiable_paths( + network, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops + ) + true_straight_paths = [] + assert len(straight_paths) == len(true_straight_paths) + + # ********************************************************************* + # ********************************************************************* + + # allow reversed edges, no self loops, excluded the start node + ignore_self_loops = False + excluded_nodes = [0] + + straight_paths = gis_iden.find_simplifiable_paths( + network, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops + ) + true_straight_paths = [[0, 1, 2], [2, 1, 0]] + assert len(straight_paths) == len(true_straight_paths)-1 + for straight_path in straight_paths: + assert straight_path in true_straight_paths + self.path_validator( + network, + straight_path, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges + ) + + # ********************************************************************* + # ********************************************************************* + + # allow reversed edges, no self loops, excluded the end node + ignore_self_loops = False excluded_nodes = [2] - straight_paths = gis_iden.find_straight_paths( + + straight_paths = gis_iden.find_simplifiable_paths( network, excluded_nodes, - paths_must_be_oneway=paths_must_be_oneway, - paths_must_be_unique=paths_must_be_unique + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops ) - # [0,1,0] or [1,0,1] are ruled out since they involve reverse-parallel arcs - # [5,6,5] or [6,5,6] are ruled out since they involve reverse/parallel arcs - # [7,8,9,7], etc are ruled out since they involve one parallel arc - true_straight_paths = [[2,3,4,2],[7,8,9]] - assert len(straight_paths) == len(true_straight_paths) + true_straight_paths = [[0, 1, 2], [2, 1, 0]] + assert len(straight_paths) == len(true_straight_paths)-1 for straight_path in straight_paths: assert straight_path in true_straight_paths - self.path_validator(network, straight_path, excluded_nodes) - + self.path_validator( + network, + straight_path, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges + ) + + # ********************************************************************* + # ********************************************************************* + # ************************************************************************* # ************************************************************************* - def test_find_straight_paths_case_b(self): - - # case a: - paths_must_be_oneway = True # anti-parallel arcs prevent paths - paths_must_be_unique = False # parallel/anti-parallel arcs do not prevent paths - - # network without straight paths - network = nx.MultiDiGraph() - excluded_nodes = [] - straight_paths = gis_iden.find_straight_paths( - network, - excluded_nodes, - paths_must_be_oneway=paths_must_be_oneway, - paths_must_be_unique=paths_must_be_unique - ) - true_straight_paths = [] - assert len(straight_paths) == len(true_straight_paths) - assert len(straight_paths) == 0 - - #************************************************************************** + def test_find_simple_straight_path_w_reversed_edge2(self): - # network with the simplest straight path + # ********************************************************************* + # ********************************************************************* + + # network with a two edge path network = nx.MultiDiGraph() network.add_edges_from([ - (0, 1, 0), - (1, 2, 0), - # simple cycle - (3, 4, 0), - (4, 5, 0), - (5, 3, 0), - # invalid cycle - (6, 7, 0), - (7, 6, 0) + (1, 0, 0), (1, 2, 0) ]) + + # ********************************************************************* + # ********************************************************************* + + # consider reversed edges + consider_reversed_edges = False + + # ********************************************************************* + # ********************************************************************* + + # no reversed edges, no self loops, no excluded nodes + ignore_self_loops = False excluded_nodes = [] - straight_paths = gis_iden.find_straight_paths( + + straight_paths = gis_iden.find_simplifiable_paths( network, excluded_nodes, - paths_must_be_oneway=paths_must_be_oneway, - paths_must_be_unique=paths_must_be_unique + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops ) - true_straight_paths = [[0, 1, 2], [3, 4, 5, 3]] + true_straight_paths = [] assert len(straight_paths) == len(true_straight_paths) - for straight_path in straight_paths: - assert straight_path in true_straight_paths - self.path_validator(network, straight_path, excluded_nodes) - - #************************************************************************** - - # network with longer path - network = nx.MultiDiGraph() - network.add_edges_from([ - (0, 1, 0), - (1, 2, 0), - (2, 3, 0), - # - (4, 5, 0), - (5, 6, 0), - (6, 7, 0), - (7, 8, 0) - ]) + + # ********************************************************************* + # ********************************************************************* + + # no reversed edges, allow self loops, no excluded nodes + ignore_self_loops = True excluded_nodes = [] - straight_paths = gis_iden.find_straight_paths( + + straight_paths = gis_iden.find_simplifiable_paths( network, excluded_nodes, - paths_must_be_oneway=paths_must_be_oneway, - paths_must_be_unique=paths_must_be_unique + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops ) - true_straight_paths = [[0, 1, 2, 3], [4, 5, 6, 7, 8]] + true_straight_paths = [] assert len(straight_paths) == len(true_straight_paths) - for straight_path in straight_paths: - assert straight_path in true_straight_paths - self.path_validator(network, straight_path, excluded_nodes) + + # ********************************************************************* + # ********************************************************************* + + # do not allow reversed edges, no self loops, excluded the middle node + ignore_self_loops = False + excluded_nodes = [1] - #************************************************************************** - - # network with excluded nodes - network = nx.MultiDiGraph() - network.add_edges_from([ - (0, 1, 0), - (1, 2, 0), - (2, 3, 0), - (3, 4, 0), - # path involving reverse arcs - (6, 5, 0), - (6, 7, 0), - (8, 7, 0), - # another - (8, 9, 0), - (10, 9, 0), - (11, 10, 0), - # cycle - (12, 13, 0), - (14, 13, 0), - (15, 12, 0) - ]) - excluded_nodes = [2] - straight_paths = gis_iden.find_straight_paths( + straight_paths = gis_iden.find_simplifiable_paths( network, excluded_nodes, - paths_must_be_oneway=paths_must_be_oneway, - paths_must_be_unique=paths_must_be_unique + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops ) - true_straight_paths = [[0, 1, 2], [2, 3, 4],[15, 12, 13],[11, 10, 9]] + true_straight_paths = [] assert len(straight_paths) == len(true_straight_paths) - for straight_path in straight_paths: - assert straight_path in true_straight_paths - self.path_validator(network, straight_path, excluded_nodes) - - #************************************************************************** - - # network with self loops and excluded nodes - network = nx.MultiDiGraph() - network.add_edges_from([ - (0, 1, 0), - (2, 2, 0), - (1, 2, 0), - (2, 3, 0), - (3, 3, 0), - (3, 4, 0), - ]) - excluded_nodes = [2] - straight_paths = gis_iden.find_straight_paths( + + # ********************************************************************* + # ********************************************************************* + + # do not allow reversed edges, no self loops, excluded the start node + ignore_self_loops = False + excluded_nodes = [0] + + straight_paths = gis_iden.find_simplifiable_paths( network, excluded_nodes, - paths_must_be_oneway=paths_must_be_oneway, - paths_must_be_unique=paths_must_be_unique + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops ) - true_straight_paths = [[0, 1, 2], [2, 3, 4]] + true_straight_paths = [] assert len(straight_paths) == len(true_straight_paths) - for straight_path in straight_paths: - assert straight_path in true_straight_paths - self.path_validator(network, straight_path, excluded_nodes) - - #************************************************************************** - - # network with parallel arcs - network = nx.MultiDiGraph() - network.add_edges_from([ - (0, 1, 0), - (1, 2, 0), - (0, 1, 1), - (2, 3, 0), - (3, 4, 0), - # more parallel arcs - (5, 6, 0), - (6, 7, 0), - (5, 6, 1), - (6, 7, 1), - (7, 5, 0) - ]) + + # ********************************************************************* + # ********************************************************************* + + # do not allow reversed edges, no self loops, excluded the end node + ignore_self_loops = False excluded_nodes = [2] - straight_paths = gis_iden.find_straight_paths( + + straight_paths = gis_iden.find_simplifiable_paths( network, excluded_nodes, - paths_must_be_oneway=paths_must_be_oneway, - paths_must_be_unique=paths_must_be_unique + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops ) - true_straight_paths = [[2,3,4],[0,1,2],[5,6,7,5]] + true_straight_paths = [] assert len(straight_paths) == len(true_straight_paths) - for straight_path in straight_paths: - assert straight_path in true_straight_paths - self.path_validator(network, straight_path, excluded_nodes) - #************************************************************************** - - # network with parallel and anti-parallel arcs - - network = nx.MultiDiGraph() - network.add_edges_from([ - (0, 1, 0), - (1, 2, 0), - (0, 1, 1), - (1, 2, 1), - (2, 3, 0), - (3, 4, 0), - (3, 2, 0), - (4, 3, 0), - ]) - excluded_nodes = [2] - straight_paths = gis_iden.find_straight_paths( + # ********************************************************************* + # ********************************************************************* + + # consider reversed edges + consider_reversed_edges = True + + # ********************************************************************* + # ********************************************************************* + + # allow reversed edges, allow self loops, no excluded nodes + ignore_self_loops = True + excluded_nodes = [] + + straight_paths = gis_iden.find_simplifiable_paths( network, excluded_nodes, - paths_must_be_oneway=paths_must_be_oneway, - paths_must_be_unique=paths_must_be_unique + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops ) - true_straight_paths = [[0,1,2]] # [2,3,4] has anti-parallel arcs - assert len(straight_paths) == len(true_straight_paths) + true_straight_paths = [[0, 1, 2], [2, 1, 0]] + assert len(straight_paths) == len(true_straight_paths)-1 for straight_path in straight_paths: assert straight_path in true_straight_paths - self.path_validator(network, straight_path, excluded_nodes) - - #************************************************************************** - - # network with cycles - network = nx.MultiDiGraph() - network.add_edges_from([ - # simplest cycle (with reverse-parallel arcs) - (0, 1, 0), - (1, 0, 0), - # 3-node cycle (with/without reverse-/-parallel arcs) - (2, 3, 0), - (3, 4, 0), - (4, 2, 0), - # 2-node cycle (with parallel and reverse-parallel arcs) - (5, 6, 0), - (6, 5, 0), - (5, 6, 1), - (6, 5, 1), - # 3-node cycle (only without parallel arcs) - (7, 8, 0), - (8, 9, 0), - (9, 7, 0), - (9, 7, 1), - (9, 7, 2) - ]) - excluded_nodes = [2] - straight_paths = gis_iden.find_straight_paths( + self.path_validator( + network, + straight_path, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges + ) + + # ********************************************************************* + # ********************************************************************* + + # allow reversed edges, no self loops, no excluded nodes + ignore_self_loops = False + excluded_nodes = [] + + straight_paths = gis_iden.find_simplifiable_paths( network, excluded_nodes, - paths_must_be_oneway=paths_must_be_oneway, - paths_must_be_unique=paths_must_be_unique + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops ) - # [0,1,0] or [1,0,1] are ruled out since they involve reverse-parallel arcs - # [5,6,5] or [6,5,6] are ruled out since they involve reverse/parallel arcs - true_straight_paths = [[2,3,4,2],[7,8,9,7]] - assert len(straight_paths) == len(true_straight_paths) + true_straight_paths = [[0, 1, 2], [2, 1, 0]] + assert len(straight_paths) == len(true_straight_paths)-1 for straight_path in straight_paths: assert straight_path in true_straight_paths - self.path_validator(network, straight_path, excluded_nodes) - - # ************************************************************************* - # ************************************************************************* - - def test_find_straight_paths_case_c(self): + self.path_validator( + network, + straight_path, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges + ) - # case a: - paths_must_be_oneway=False # arcs in the opposite direction do not prevent paths - paths_must_be_unique=True # anti-/parallel arcs prevent paths + # ********************************************************************* + # ********************************************************************* + + # allow reversed edges, no self loops, excluded the middle node + ignore_self_loops = False + excluded_nodes = [1] - # network without straight paths - network = nx.MultiDiGraph() - excluded_nodes = [] - straight_paths = gis_iden.find_straight_paths( + straight_paths = gis_iden.find_simplifiable_paths( network, excluded_nodes, - paths_must_be_oneway=paths_must_be_oneway, - paths_must_be_unique=paths_must_be_unique + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops ) true_straight_paths = [] assert len(straight_paths) == len(true_straight_paths) - assert len(straight_paths) == 0 - - # ************************************************************************** - - # network with the simplest straight path + + # ********************************************************************* + # ********************************************************************* + + # allow reversed edges, no self loops, excluded the start node + ignore_self_loops = False + excluded_nodes = [0] - network = nx.MultiDiGraph() - network.add_edges_from([ - (0, 1, 0), - (1, 2, 0), - # with reverse arcs - (3, 4, 0), - (5, 4, 0) - ]) - excluded_nodes = [] - straight_paths = gis_iden.find_straight_paths( + straight_paths = gis_iden.find_simplifiable_paths( network, excluded_nodes, - paths_must_be_oneway=paths_must_be_oneway, - paths_must_be_unique=paths_must_be_unique + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops ) - true_straight_paths = [[2, 1, 0], [0, 1, 2], [3, 4, 5], [5, 4, 3]] - assert len(straight_paths) == len(true_straight_paths)-2 + true_straight_paths = [[0, 1, 2], [2, 1, 0]] + assert len(straight_paths) == len(true_straight_paths)-1 for straight_path in straight_paths: assert straight_path in true_straight_paths self.path_validator( network, straight_path, excluded_nodes, - paths_must_be_oneway=paths_must_be_oneway, - paths_must_be_unique=paths_must_be_unique + consider_reversed_edges=consider_reversed_edges ) - - # ************************************************************************** - - # network with longer straight path + + # ********************************************************************* + # ********************************************************************* + + # allow reversed edges, no self loops, excluded the end node + ignore_self_loops = False + excluded_nodes = [2] - network = nx.MultiDiGraph() - network.add_edges_from([ - (0, 1, 0), - (1, 2, 0), - (2, 3, 0), - ]) - excluded_nodes = [] - straight_paths = gis_iden.find_straight_paths( + straight_paths = gis_iden.find_simplifiable_paths( network, excluded_nodes, - paths_must_be_oneway=paths_must_be_oneway, - paths_must_be_unique=paths_must_be_unique + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops ) - true_straight_paths = [[0,1,2,3],[3,2,1,0]] + true_straight_paths = [[0, 1, 2], [2, 1, 0]] assert len(straight_paths) == len(true_straight_paths)-1 for straight_path in straight_paths: assert straight_path in true_straight_paths @@ -592,488 +1761,193 @@ class TestGisIdentify: network, straight_path, excluded_nodes, - paths_must_be_oneway=paths_must_be_oneway, - paths_must_be_unique=paths_must_be_unique + consider_reversed_edges=consider_reversed_edges ) + + # ********************************************************************* + # ********************************************************************* - # ************************************************************************** + # ************************************************************************* + # ************************************************************************* + + def test_find_simplifiable_path_four_nodes(self): - # network with excluded nodes + # ********************************************************************* + # ********************************************************************* + # network with a two edge path network = nx.MultiDiGraph() network.add_edges_from([ - (0, 1, 0), - (1, 2, 0), - (2, 3, 0), - (3, 4, 0), - # path involving reverse arcs - (6, 5, 0), - (6, 7, 0), - (8, 7, 0), - # another - (8, 9, 0), - (10, 9, 0), - (11, 10, 0), - # cycle - (12, 13, 0), - (14, 13, 0), - (14, 15, 0), - (15, 12, 0) + (1, 0, 0), (1, 2, 0), (2, 3, 0) ]) - excluded_nodes = [2, 8] - straight_paths = gis_iden.find_straight_paths( + + # ********************************************************************* + # ********************************************************************* + + # do not consider reversed edges + consider_reversed_edges = False + + # ********************************************************************* + # ********************************************************************* + + # no reversed edges, no self loops, no excluded nodes + ignore_self_loops = False + excluded_nodes = [] + + straight_paths = gis_iden.find_simplifiable_paths( network, excluded_nodes, - paths_must_be_oneway=paths_must_be_oneway, - paths_must_be_unique=paths_must_be_unique + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops ) - true_straight_paths = [ - [0,1,2],[2,1,0], - [2,3,4],[4,3,2], - [5,6,7,8],[8,7,6,5], - [8,9,10,11],[11,10,9,8], - [12,13,14,15,12],[14,13,12,15,14],[13,14,15,12,13],[15,12,13,14,15] - ] - assert len(straight_paths) == len(true_straight_paths)-4-3 + true_straight_paths = [[1, 2, 3]] + assert len(straight_paths) == len(true_straight_paths) for straight_path in straight_paths: assert straight_path in true_straight_paths self.path_validator( network, straight_path, excluded_nodes, - paths_must_be_oneway=paths_must_be_oneway, - paths_must_be_unique=paths_must_be_unique + consider_reversed_edges=consider_reversed_edges ) - - # ************************************************************************** - - # network with self loops and excluded nodes - - network = nx.MultiDiGraph() - network.add_edges_from([ - (0, 1, 0), - (2, 2, 0), - (1, 2, 0), - (2, 3, 0), - (3, 3, 0), - (3, 4, 0), - ]) - excluded_nodes = [2] - straight_paths = gis_iden.find_straight_paths(network, excluded_nodes) - true_straight_paths = [[0, 1, 2], [2, 3, 4]] - assert len(straight_paths) == len(true_straight_paths) + + # ********************************************************************* + # ********************************************************************* + + # consider reversed edges + consider_reversed_edges = True + + # ********************************************************************* + # ********************************************************************* + + # no reversed edges, no self loops, no excluded nodes + ignore_self_loops = False + excluded_nodes = [] + + straight_paths = gis_iden.find_simplifiable_paths( + network, + excluded_nodes, + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops + ) + true_straight_paths = [[0, 1, 2, 3], [3, 2, 1, 0]] + assert len(straight_paths) == len(true_straight_paths)-1 for straight_path in straight_paths: assert straight_path in true_straight_paths self.path_validator( network, straight_path, excluded_nodes, - paths_must_be_oneway=paths_must_be_oneway, - paths_must_be_unique=paths_must_be_unique + consider_reversed_edges=consider_reversed_edges ) - - # ************************************************************************** - - # network with parallel arcs + + # ********************************************************************* + # ********************************************************************* - network = nx.MultiDiGraph() - network.add_edges_from([ - (0, 1, 0), - (1, 2, 0), - (0, 1, 1), - (2, 3, 0), - (3, 4, 0), - # 3-node cycle (only without parallel arcs) - (7, 8, 0), - (7, 8, 1), - (8, 9, 0), - (9, 7, 0) - ]) - excluded_nodes = [2] - straight_paths = gis_iden.find_straight_paths( - network, + # no reversed edges, no self loops, no excluded nodes + ignore_self_loops = False + excluded_nodes = [] + + straight_paths = gis_iden.find_simplifiable_paths( + network, excluded_nodes, - paths_must_be_oneway=paths_must_be_oneway, - paths_must_be_unique=paths_must_be_unique - ) - true_straight_paths = [ - [2,3,4],[4,3,2], - [8,9,7],[7,9,8] - ] - assert len(straight_paths) == len(true_straight_paths)-1-1 + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops, + include_both_directions=True + ) + true_straight_paths = [[0, 1, 2, 3], [3, 2, 1, 0]] + assert len(straight_paths) == len(true_straight_paths) for straight_path in straight_paths: assert straight_path in true_straight_paths self.path_validator( network, straight_path, excluded_nodes, - paths_must_be_oneway=paths_must_be_oneway, - paths_must_be_unique=paths_must_be_unique + consider_reversed_edges=consider_reversed_edges ) + + # ********************************************************************* + # ********************************************************************* + + # ************************************************************************* + # ************************************************************************* - # ************************************************************************** - - # network with parallel and anti-parallel arcs + def test_find_simplifiable_path_four_node_cycle(self): + # ********************************************************************* + # ********************************************************************* + + # network with a two edge path network = nx.MultiDiGraph() network.add_edges_from([ - (0, 1, 0), - (1, 2, 0), - (0, 1, 1), - (1, 2, 1), - (2, 3, 0), - (3, 4, 0), - (4, 5, 0), - (5, 2, 0), - (5, 2, 1), - # cycle - (6, 7, 0), - (7, 8, 0), - (8, 9, 0), - (9, 6, 0) + (0, 1, 0), (1, 2, 0), (2, 3, 0), (0, 3, 0) ]) - excluded_nodes = [2] - straight_paths = gis_iden.find_straight_paths( - network, + + # ********************************************************************* + # ********************************************************************* + + # do not consider reversed edges + consider_reversed_edges = False + + # ********************************************************************* + # ********************************************************************* + + # no reversed edges, no self loops, no excluded nodes + ignore_self_loops = False + excluded_nodes = [] + + straight_paths = gis_iden.find_simplifiable_paths( + network, excluded_nodes, - paths_must_be_oneway=paths_must_be_oneway, - paths_must_be_unique=paths_must_be_unique - ) - true_straight_paths = [ - [2,3,4,5],[5,4,3,2], - [6,7,8,9,6],[7,8,9,6,7],[8,9,6,7,8],[9,8,7,6,9], - [6,9,8,7,6],[7,6,9,8,7],[8,7,6,9,8],[9,6,7,8,9] - ] - assert len(straight_paths) == len(true_straight_paths)-1-3-4 + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops + ) + true_straight_paths = [[0, 1, 2, 3]] + assert len(straight_paths) == len(true_straight_paths) for straight_path in straight_paths: assert straight_path in true_straight_paths self.path_validator( network, straight_path, excluded_nodes, - paths_must_be_oneway=paths_must_be_oneway, - paths_must_be_unique=paths_must_be_unique + consider_reversed_edges=consider_reversed_edges ) - - #************************************************************************** - - # network with cycles - - network = nx.MultiDiGraph() - network.add_edges_from([ - # simplest cycle (with reverse-parallel arcs) - (0, 1, 0), - (1, 0, 0), - # 3-node cycle (with/without reverse-/-parallel arcs) - (2, 3, 0), - (3, 4, 0), - (4, 2, 0), - # 2-node cycle (with parallel and reverse-parallel arcs) - (5, 6, 0), - (6, 5, 0), - (5, 6, 1), - (6, 5, 1), - # 3-node cycle (only without parallel arcs) - (7, 8, 0), - (8, 9, 0), - (9, 7, 0), - (9, 7, 1) - ]) - excluded_nodes = [2] - straight_paths = gis_iden.find_straight_paths( - network, + + # ********************************************************************* + # ********************************************************************* + + # consider reversed edges + consider_reversed_edges = True + + # ********************************************************************* + # ********************************************************************* + + # no reversed edges, no self loops, no excluded nodes + ignore_self_loops = False + excluded_nodes = [] + + straight_paths = gis_iden.find_simplifiable_paths( + network, excluded_nodes, - paths_must_be_oneway=True, # reverse-parallel arcs prevent paths - paths_must_be_unique=True # parallel arcs prevent paths + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops ) - # [0,1,0] or [1,0,1] are ruled out since they involve reverse-parallel arcs - # [5,6,5] or [6,5,6] are ruled out since they involve reverse/parallel arcs - # [7,8,9,7], etc are ruled out since they involve one parallel arc true_straight_paths = [ - [2,3,4,2],[3,4,2,3],[4,3,2,4],[2,4,3,2],[3,2,4,3],[4,2,3,4], - [7,8,9],[9,8,7] + [0, 1, 2, 3, 0], [1, 2, 3, 0, 1], [2, 3, 0, 1, 2], [3, 0, 1, 2, 3] ] - assert len(straight_paths) == len(true_straight_paths)-5-1 + assert len(straight_paths) == len(true_straight_paths)-3 for straight_path in straight_paths: assert straight_path in true_straight_paths self.path_validator( network, straight_path, excluded_nodes, - paths_must_be_oneway=paths_must_be_oneway, - paths_must_be_unique=paths_must_be_unique + consider_reversed_edges=consider_reversed_edges ) - - # ************************************************************************* - # ************************************************************************* - - # def test_find_straight_paths_case_d(self): - - # # case a: - # paths_must_be_oneway=False # reverse-parallel arcs prevent paths - # paths_must_be_unique=False # parallel arcs prevent paths - - # # network without straight paths - # network = nx.MultiDiGraph() - # excluded_nodes = [] - # straight_paths = gis_iden.find_straight_paths( - # network, - # excluded_nodes, - # paths_must_be_oneway=paths_must_be_oneway, - # paths_must_be_unique=paths_must_be_unique - # ) - # true_straight_paths = [] - # assert len(straight_paths) == len(true_straight_paths) - # assert len(straight_paths) == 0 - - # # ************************************************************************** - - # # network with the simplest straight path - # network = nx.MultiDiGraph() - # network.add_edges_from([ - # (0, 1, 0), - # (1, 2, 0), - # ]) - # excluded_nodes = [] - # straight_paths = gis_iden.find_straight_paths( - # network, - # excluded_nodes, - # paths_must_be_oneway=paths_must_be_oneway, - # paths_must_be_unique=paths_must_be_unique - # ) - # true_straight_paths = [[0,1,2],[2,1,0]] - - # print('hello here') - # print(network.edges(keys=True, data=True)) - # print('true :' + str(true_straight_paths)) - # print('actual :' + str(straight_paths)) - # assert len(straight_paths) == len(true_straight_paths)-1 - # for straight_path in straight_paths: - # assert straight_path in true_straight_paths - # self.path_validator( - # network, - # straight_path, - # excluded_nodes, - # paths_must_be_oneway=paths_must_be_oneway, - # paths_must_be_unique=paths_must_be_unique - # ) - - # # ************************************************************************** - - # # network with longer straight path - # network = nx.MultiDiGraph() - # network.add_edges_from([ - # (0, 1, 0), - # (1, 2, 0), - # (2, 3, 0), - # ]) - # excluded_nodes = [] - # straight_paths = gis_iden.find_straight_paths( - # network, - # excluded_nodes, - # paths_must_be_oneway=paths_must_be_oneway, - # paths_must_be_unique=paths_must_be_unique - # ) - # true_straight_paths = [[0,1,2,3],[3,2,1,0]] - # assert len(straight_paths) == len(true_straight_paths)-1 - # for straight_path in straight_paths: - # assert straight_path in true_straight_paths - # self.path_validator( - # network, - # straight_path, - # excluded_nodes, - # paths_must_be_oneway=paths_must_be_oneway, - # paths_must_be_unique=paths_must_be_unique - # ) - - # # ************************************************************************** - - # # network with excluded nodes - - # network = nx.MultiDiGraph() - # network.add_edges_from([ - # (0, 1, 0), - # (1, 2, 0), - # (2, 3, 0), - # (3, 4, 0), - # ]) - - # excluded_nodes = [2] - # straight_paths = gis_iden.find_straight_paths( - # network, - # excluded_nodes, - # paths_must_be_oneway=paths_must_be_oneway, - # paths_must_be_unique=paths_must_be_unique - # ) - # true_straight_paths = [[0,1,2],[2,3,4],[2,1,0],[4,3,2]] - # assert len(straight_paths) == len(true_straight_paths)-2 - # for straight_path in straight_paths: - # assert straight_path in true_straight_paths - # self.path_validator( - # network, - # straight_path, - # excluded_nodes, - # paths_must_be_oneway=paths_must_be_oneway, - # paths_must_be_unique=paths_must_be_unique - # ) - - # # ************************************************************************** - - # # network with self loops and excluded nodes - - # network = nx.MultiDiGraph() - # network.add_edges_from([ - # (0, 1, 0), - # (2, 2, 0), - # (1, 2, 0), - # (2, 3, 0), - # (3, 3, 0), - # (3, 4, 0), - # ]) - # excluded_nodes = [2] - # straight_paths = gis_iden.find_straight_paths( - # network, - # excluded_nodes, - # paths_must_be_oneway=paths_must_be_oneway, - # paths_must_be_unique=paths_must_be_unique - # ) - # true_straight_paths = [[0,1,2],[2,3,4],[4,3,2],[2,1,0]] - # assert len(straight_paths) == len(true_straight_paths)-2 - # for straight_path in straight_paths: - # assert straight_path in true_straight_paths - # self.path_validator( - # network, - # straight_path, - # excluded_nodes, - # paths_must_be_oneway=paths_must_be_oneway, - # paths_must_be_unique=paths_must_be_unique - # ) - - # # ************************************************************************** - - # # network with parallel arcs - - # network = nx.MultiDiGraph() - # network.add_edges_from([ - # (0, 1, 0), - # (1, 2, 0), - # (0, 1, 1), - # (2, 3, 0), - # (3, 4, 0), - # ]) - # excluded_nodes = [2,1] - # straight_paths = gis_iden.find_straight_paths( - # network, - # excluded_nodes, - # paths_must_be_oneway=paths_must_be_oneway, - # paths_must_be_unique=paths_must_be_unique - # ) - # true_straight_paths = [[2,3,4],[4,3,2]] # [0,1,2] involves parallel arcs - # assert len(straight_paths) == len(true_straight_paths)-1 - # for straight_path in straight_paths: - # assert straight_path in true_straight_paths - # self.path_validator( - # network, - # straight_path, - # excluded_nodes, - # paths_must_be_oneway=paths_must_be_oneway, - # paths_must_be_unique=paths_must_be_unique - # ) - - # # ************************************************************************** - - # # network with parallel and anti-parallel arcs - - # network = nx.MultiDiGraph() - # network.add_edges_from([ - # (0, 1, 0), - # (1, 2, 0), - # (0, 1, 1), - # (1, 2, 1), - # (2, 3, 0), - # (3, 4, 0), - # (3, 2, 0), - # (3, 3, 0), - # (4, 3, 0), - # ]) - # excluded_nodes = [2] - # straight_paths = gis_iden.find_straight_paths( - # network, - # excluded_nodes, - # paths_must_be_oneway=paths_must_be_oneway, - # paths_must_be_unique=paths_must_be_unique - # ) - # true_straight_paths = [ - # [0, 1, 2], [2, 1, 0], - # [2, 3, 4], [4, 3, 2] - # ] # all are good - # print('hello here') - # print(network.edges(keys=True, data=True)) - # print('true :' + str(true_straight_paths)) - # print('actual :' + str(straight_paths)) - # assert len(straight_paths) == len(true_straight_paths)-2 - # for straight_path in straight_paths: - # assert straight_path in true_straight_paths - # self.path_validator( - # network, - # straight_path, - # excluded_nodes, - # paths_must_be_oneway=paths_must_be_oneway, - # paths_must_be_unique=paths_must_be_unique - # ) - - # #************************************************************************** - - # # network with cycles - - # network = nx.MultiDiGraph() - # network.add_edges_from([ - # # simplest cycle (with reverse-parallel arcs) - # (0, 1, 0), - # (1, 0, 0), - # # 3-node cycle (with/without reverse-/-parallel arcs) - # (2, 3, 0), - # (3, 4, 0), - # (4, 2, 0), - # # 2-node cycle (with parallel and reverse-parallel arcs) - # (5, 6, 0), - # (6, 5, 0), - # (5, 6, 1), - # (6, 5, 1), - # # 3-node cycle (only without parallel arcs) - # (7, 8, 0), - # (8, 9, 0), - # (9, 7, 0), - # (9, 7, 1) - # ]) - # excluded_nodes = [2] - # straight_paths = gis_iden.find_straight_paths( - # network, - # excluded_nodes, - # paths_must_be_oneway=paths_must_be_oneway, - # paths_must_be_unique=paths_must_be_unique - # ) - # true_straight_paths = [ - # [2,3,4,2],[3,4,2,3],[4,3,2,4],[2,4,3,2],[3,2,4,3],[4,2,3,4], - # [7,8,9,7],[8,9,7,8],[9,7,8,9],[7,9,8,7],[8,7,9,8],[9,7,8,9], - # # [0,1,0],[1,0,1], - # # [5,6,5],[6,5,6] - # ] - # print('hello here') - # print(network.edges(keys=True, data=True)) - # print('true :' + str(true_straight_paths)) - # print('actual :' + str(straight_paths)) - # assert len(straight_paths) == len(true_straight_paths)-5-5 - # for straight_path in straight_paths: - # assert straight_path in true_straight_paths - # self.path_validator( - # network, - # straight_path, - # excluded_nodes, - # paths_must_be_oneway=paths_must_be_oneway, - # paths_must_be_unique=paths_must_be_unique - # ) - + + # ********************************************************************* + # ********************************************************************* + # ************************************************************************* # ************************************************************************* @@ -1213,9 +2087,9 @@ class TestGisIdentify: # ************************************************************************* # ************************************************************************* - def test_identify_all_from_a_to_b(self): + def test_identify_get_from_a_to_b(self): - # arc_keys = gis_iden.all_arcs_from_a_to_b(G, 0, 1) + # edge_keys = gis_iden.get_edges_from_a_to_b(G, 0, 1) G = nx.MultiDiGraph() @@ -1230,80 +2104,80 @@ class TestGisIdentify: # from node 0 to node 1 - arc_keys = gis_iden.all_arcs_from_a_to_b(G, 0, 1) + edge_keys = gis_iden.get_edges_from_a_to_b(G, 0, 1) - true_arc_keys = [(0, 1, 0)] + true_edge_keys = [(0, 1, 0)] - assert len(true_arc_keys) == len(arc_keys) + assert len(true_edge_keys) == len(edge_keys) - for arc_key in arc_keys: + for edge_key in edge_keys: - assert arc_key in true_arc_keys + assert edge_key in true_edge_keys # from node 1 to node 0 - arc_keys = gis_iden.all_arcs_from_a_to_b(G, 1, 0) + edge_keys = gis_iden.get_edges_from_a_to_b(G, 1, 0) - true_arc_keys = [(1, 0, 0)] + true_edge_keys = [(1, 0, 0)] - assert len(true_arc_keys) == len(arc_keys) + assert len(true_edge_keys) == len(edge_keys) - for arc_key in arc_keys: + for edge_key in edge_keys: - assert arc_key in true_arc_keys + assert edge_key in true_edge_keys # from node 1 to node 2 - arc_keys = gis_iden.all_arcs_from_a_to_b(G, 1, 2) + edge_keys = gis_iden.get_edges_from_a_to_b(G, 1, 2) - true_arc_keys = [(1, 2, 0)] + true_edge_keys = [(1, 2, 0)] - assert len(true_arc_keys) == len(arc_keys) + assert len(true_edge_keys) == len(edge_keys) - for arc_key in arc_keys: + for edge_key in edge_keys: - assert arc_key in true_arc_keys + assert edge_key in true_edge_keys # from node 2 to node 1 - arc_keys = gis_iden.all_arcs_from_a_to_b(G, 2, 1) + edge_keys = gis_iden.get_edges_from_a_to_b(G, 2, 1) - true_arc_keys = [] + true_edge_keys = [] - assert len(true_arc_keys) == len(arc_keys) + assert len(true_edge_keys) == len(edge_keys) # from node 2 to node 3 - arc_keys = gis_iden.all_arcs_from_a_to_b(G, 2, 3) + edge_keys = gis_iden.get_edges_from_a_to_b(G, 2, 3) - true_arc_keys = [(2, 3, 0), (2, 3, 1)] + true_edge_keys = [(2, 3, 0), (2, 3, 1)] - assert len(true_arc_keys) == len(arc_keys) + assert len(true_edge_keys) == len(edge_keys) - for arc_key in arc_keys: + for edge_key in edge_keys: - assert arc_key in true_arc_keys + assert edge_key in true_edge_keys # from node 3 to node 2 - true_arc_keys = [(3, 2, 0)] + true_edge_keys = [(3, 2, 0)] - arc_keys = gis_iden.all_arcs_from_a_to_b(G, 3, 2) + edge_keys = gis_iden.get_edges_from_a_to_b(G, 3, 2) - assert len(true_arc_keys) == len(arc_keys) + assert len(true_edge_keys) == len(edge_keys) - for arc_key in arc_keys: + for edge_key in edge_keys: - assert arc_key in true_arc_keys + assert edge_key in true_edge_keys # ************************************************************************* # ************************************************************************* - def test_identify_all_arcs_involving_nodes(self): + def test_identify_get_edges_involving_nodes(self): G = nx.MultiDiGraph() - G.add_node('noarcs') + G.add_node('noedges') G.add_edges_from( [(0, 1, 0), @@ -1311,31 +2185,31 @@ class TestGisIdentify: (1, 2, 1)] ) - # no arcs - node_key = 'noarcs' - arcs = gis_iden.all_arcs_involving_node(G, node_key) - assert len(arcs) == 0 + # no edges + node_key = 'noedges' + edges = gis_iden.get_edges_involving_node(G, node_key) + assert len(edges) == 0 - # one arc + # one edge node_key = 0 - true_arcs = [(0,1,0)] - arcs = gis_iden.all_arcs_involving_node(G, node_key) - assert len(arcs) == len(true_arcs) - for arc in arcs: - assert arc in true_arcs + true_edges = [(0,1,0)] + edges = gis_iden.get_edges_involving_node(G, node_key) + assert len(edges) == len(true_edges) + for edge in edges: + assert edge in true_edges - # multiple arcs + # multiple edges node_key = 1 - true_arcs = [(0,1,0), (1,2,1), (1,2,0)] - arcs = gis_iden.all_arcs_involving_node(G, node_key) - assert len(arcs) == len(true_arcs) - for arc in arcs: - assert arc in true_arcs + true_edges = [(0,1,0), (1,2,1), (1,2,0)] + edges = gis_iden.get_edges_involving_node(G, node_key) + assert len(edges) == len(true_edges) + for edge in edges: + assert edge in true_edges # ************************************************************************* # ************************************************************************* - def test_identify_all_arcs_between_nodes(self): + def test_identify_get_edges_between_nodes(self): G = nx.MultiDiGraph() @@ -1351,80 +2225,80 @@ class TestGisIdentify: # between two nodes that are not connected - arc_keys = gis_iden.all_arcs_between_two_nodes(G, 2, 4) + edge_keys = gis_iden.get_edges_between_two_nodes(G, 2, 4) - assert 0 == len(arc_keys) + assert 0 == len(edge_keys) # between nodes 0 and 1, nominal direction - arc_keys = gis_iden.all_arcs_between_two_nodes(G, 0, 1) + edge_keys = gis_iden.get_edges_between_two_nodes(G, 0, 1) - true_arc_keys = [(0, 1, 0), (1, 0, 0)] + true_edge_keys = [(0, 1, 0), (1, 0, 0)] - assert len(true_arc_keys) == len(arc_keys) + assert len(true_edge_keys) == len(edge_keys) - for arc_key in arc_keys: + for edge_key in edge_keys: - assert arc_key in true_arc_keys + assert edge_key in true_edge_keys # between nodes 0 and 1, reverse direction - arc_keys = gis_iden.all_arcs_between_two_nodes(G, 1, 0) + edge_keys = gis_iden.get_edges_between_two_nodes(G, 1, 0) - assert len(true_arc_keys) == len(arc_keys) + assert len(true_edge_keys) == len(edge_keys) - for arc_key in arc_keys: + for edge_key in edge_keys: - assert arc_key in true_arc_keys + assert edge_key in true_edge_keys # between nodes 1 and 2, nominal direction - arc_keys = gis_iden.all_arcs_between_two_nodes(G, 1, 2) + edge_keys = gis_iden.get_edges_between_two_nodes(G, 1, 2) - true_arc_keys = [(1, 2, 0)] + true_edge_keys = [(1, 2, 0)] - assert len(true_arc_keys) == len(arc_keys) + assert len(true_edge_keys) == len(edge_keys) - for arc_key in arc_keys: + for edge_key in edge_keys: - assert arc_key in true_arc_keys + assert edge_key in true_edge_keys # between nodes 1 and 2, reverse direction - arc_keys = gis_iden.all_arcs_between_two_nodes(G, 2, 1) + edge_keys = gis_iden.get_edges_between_two_nodes(G, 2, 1) - assert len(true_arc_keys) == len(arc_keys) + assert len(true_edge_keys) == len(edge_keys) - for arc_key in arc_keys: + for edge_key in edge_keys: - assert arc_key in true_arc_keys + assert edge_key in true_edge_keys # between nodes 2 and 3, nominal direction - arc_keys = gis_iden.all_arcs_between_two_nodes(G, 2, 3) + edge_keys = gis_iden.get_edges_between_two_nodes(G, 2, 3) - true_arc_keys = [(2, 3, 0), (2, 3, 1), (3, 2, 0)] + true_edge_keys = [(2, 3, 0), (2, 3, 1), (3, 2, 0)] - assert len(true_arc_keys) == len(arc_keys) + assert len(true_edge_keys) == len(edge_keys) - for arc_key in arc_keys: + for edge_key in edge_keys: - assert arc_key in true_arc_keys + assert edge_key in true_edge_keys # between nodes 2 and 3, reverse direction - arc_keys = gis_iden.all_arcs_between_two_nodes(G, 3, 2) + edge_keys = gis_iden.get_edges_between_two_nodes(G, 3, 2) - assert len(true_arc_keys) == len(arc_keys) + assert len(true_edge_keys) == len(edge_keys) - for arc_key in arc_keys: + for edge_key in edge_keys: - assert arc_key in true_arc_keys + assert edge_key in true_edge_keys # ************************************************************************* # ************************************************************************* - def test_identify_arcs_closest_to_nodes(self): + def test_identify_edges_closest_to_nodes(self): # network should be a OSM-nx formatted graph @@ -1441,25 +2315,25 @@ class TestGisIdentify: while len(node_keys) > number_nodes: node_keys.pop(random.randint(0, len(node_keys)-1)) - # find out which arcs are closest - arc_keys, projected_network = gis_iden.identify_arc_closest_to_node( + # find out which edges are closest + edge_keys, projected_network = gis_iden.identify_edge_closest_to_node( network=network, node_keys=node_keys ) - # for each node, verify that there is no closer arc - # prepare the arc geometries - arc_key_geos = [ - (projected_network.edges[arc_key][gis_osm.KEY_OSMNX_GEOMETRY] - if gis_osm.KEY_OSMNX_GEOMETRY in projected_network.edges[arc_key] else + # for each node, verify that there is no closer edge + # prepare the edge geometries + edge_key_geos = [ + (projected_network.edges[edge_key][gis_osm.KEY_OSMNX_GEOMETRY] + if gis_osm.KEY_OSMNX_GEOMETRY in projected_network.edges[edge_key] else LineString([ - (projected_network.nodes[arc_key[0]][gis_osm.KEY_OSMNX_X], - projected_network.nodes[arc_key[0]][gis_osm.KEY_OSMNX_Y]), - (projected_network.nodes[arc_key[1]][gis_osm.KEY_OSMNX_X], - projected_network.nodes[arc_key[1]][gis_osm.KEY_OSMNX_Y]) + (projected_network.nodes[edge_key[0]][gis_osm.KEY_OSMNX_X], + projected_network.nodes[edge_key[0]][gis_osm.KEY_OSMNX_Y]), + (projected_network.nodes[edge_key[1]][gis_osm.KEY_OSMNX_X], + projected_network.nodes[edge_key[1]][gis_osm.KEY_OSMNX_Y]) ]) ) - for arc_key in arc_keys + for edge_key in edge_keys ] for node_index, node_key in enumerate(node_keys): # prepare the node geometry @@ -1467,22 +2341,22 @@ class TestGisIdentify: projected_network.nodes[node_key][gis_osm.KEY_OSMNX_X], projected_network.nodes[node_key][gis_osm.KEY_OSMNX_Y] ) - # calculate the distances to every arc - arc_point_distances = the_point.distance(arc_key_geos) - # find the distance to the arc identified as the closest one - shortest_distance = arc_point_distances[node_index] - # the arc identified must lead to the shortest distance - assert shortest_distance == min(arc_point_distances) - - # find out which arcs are closest using the projected network - arc_keys_proj, _ = gis_iden.identify_arc_closest_to_node( + # calculate the distances to every edge + edge_point_distances = the_point.distance(edge_key_geos) + # find the distance to the edge identified as the closest one + shortest_distance = edge_point_distances[node_index] + # the edge identified must lead to the shortest distance + assert shortest_distance == min(edge_point_distances) + + # find out which edges are closest using the projected network + edge_keys_proj, _ = gis_iden.identify_edge_closest_to_node( network=projected_network, node_keys=node_keys ) - # assert that the same arcs have been returned (with the projected network) - assert len(arc_keys) == len(arc_keys_proj) - for arc_key1, arc_key2 in zip(arc_keys, arc_keys_proj): - assert arc_key1 == arc_key2 + # assert that the same edges have been returned (with the projected network) + assert len(edge_keys) == len(edge_keys_proj) + for edge_key1, edge_key2 in zip(edge_keys, edge_keys_proj): + assert edge_key1 == edge_key2 # ************************************************************************* # ************************************************************************* @@ -1526,7 +2400,7 @@ class TestGisIdentify: # connect roundabouts 1 and 3 with a edge (1, 6, {'length': 9, 'oneway': False}), (6, 1, {'length': 24, 'oneway': False}), - # create an arc between node 7 and 5 that cannot be used, + # create an edge between node 7 and 5 that cannot be used, (7, 5, {'length': 6, 'oneway': False}) ] @@ -1570,7 +2444,7 @@ class TestGisIdentify: [16, 17, 18, 19], # true roundabout with one non-existent node added [2, 3, 'h', 4], - # path whose last node does not lead to the first through a valid arc + # path whose last node does not lead to the first through a valid edge [5, 6, 7], # true roundabout without the last node (8) [5, 6, 7, 19], @@ -1674,17 +2548,7 @@ class TestGisIdentify: ] for edge_index, edge_path in enumerate(edge_paths): - - # try: - # assert gis_mod.convert_edge_path( - # network, - # edge_path, - # allow_reversed_edges=allow_reversed_edges) == expected_node_paths[ - # edge_index] - # except AssertionError: - # print(edge_path) - # print(expected_node_paths[edge_index]) - # assert False + assert gis_iden.convert_edge_path( network, edge_path, @@ -2329,7 +3193,7 @@ class TestGisIdentify: truncate_by_edge=True ) - # create arc to trigger the negative case with a different length + # create edge to trigger the negative case with a different length # (317812803, 317812802, 2) edge_k = network.add_edge( 317812803, @@ -2343,7 +3207,7 @@ class TestGisIdentify: 'length': 27.601+1} ) assert edge_k == 2 - # create arc tp trigger the negative case with a different geometry + # create edge tp trigger the negative case with a different geometry # (317812802, 317812803, 2) edge_k = network.add_edge( 317812802, @@ -2360,31 +3224,31 @@ class TestGisIdentify: ) assert edge_k == 2 - # find arcs that have matching arcs in the reverse direction - for arc_key in network.edges(keys=True): - arc_dict = network.get_edge_data(*arc_key) + # find edges that have matching edges in the reverse direction + for edge_key in network.edges(keys=True): + edge_dict = network.get_edge_data(*edge_key) for _attr in gis_osm.KEYS_OSMNX_EDGES_ESSENTIAL: - assert _attr in arc_dict.keys() - # for each arc, find if there is one in the opposite direction - if network.has_edge(u=arc_key[1], v=arc_key[0]): - # there is an arc in the opposite sense - for other_arc_key in gis_iden.all_arcs_from_a_to_b( + assert _attr in edge_dict.keys() + # for each edge, find if there is one in the opposite direction + if network.has_edge(u=edge_key[1], v=edge_key[0]): + # there is an edge in the opposite sense + for other_edge_key in gis_iden.get_edges_from_a_to_b( network=network, - node_start=arc_key[1], - node_end=arc_key[0] + node_start=edge_key[1], + node_end=edge_key[0] ): - # check if the arcs are the same but in reverse + # check if the edges are the same but in reverse if gis_iden.edges_are_in_reverse( network, - edge_a=arc_key, - edge_b=other_arc_key + edge_a=edge_key, + edge_b=other_edge_key ): - # the arcs are the same but in reverse: + # the edges are the same but in reverse: # - all attributes have to be the same or lists with # the same content, as in a set, except for # the geometry and reversed attributes - fw_dict = network.get_edge_data(*arc_key) - rv_dict = network.get_edge_data(*other_arc_key) + fw_dict = network.get_edge_data(*edge_key) + rv_dict = network.get_edge_data(*other_edge_key) # should have the same attributes assert set(fw_dict.keys()) == set(rv_dict.keys()) @@ -2424,11 +3288,11 @@ class TestGisIdentify: # nor reversed attributes: they must match assert attr_value == rv_dict[attr_key] - else: # the arcs are not the same in reverse + else: # the edges are not the same in reverse # at least one of their attributes must be different or # incompatible - fw_dict = network.get_edge_data(*arc_key) - rv_dict = network.get_edge_data(*other_arc_key) + fw_dict = network.get_edge_data(*edge_key) + rv_dict = network.get_edge_data(*other_edge_key) error_raised = False try: # should have the same attributes @@ -2563,13 +3427,11 @@ class TestGisIdentify: assert gis_iden.is_edge_consistent_with_geometry(network, edge_key) edge_key = (1104936963, 115831, 0) assert gis_iden.is_edge_consistent_with_geometry(network, edge_key) - - - + # ************************************************************************* # ************************************************************************* - def test_valid_paths(self): + def test_valid_node_paths(self): # ********************************************************************* # ********************************************************************* @@ -2604,6 +3466,7 @@ class TestGisIdentify: invalid_node_paths = [ [], # empty list + [1], # single node path [-1,0,1,2,3], # nodes do not belong to the network [3,2,1,0], # path 1 reversed [3,4,5,6], # path 2 reversed @@ -2611,16 +3474,72 @@ class TestGisIdentify: [6,7,8,10] # node 10 is connected to node 8 but not the other way arou. ] + # make sure valid node paths are valid for path in valid_node_paths: - assert gis_iden.is_node_path(network, path) - # make sure invalid node paths are invalid - for path in invalid_node_paths: - assert not gis_iden.is_node_path(network, path) - + + # ********************************************************************* + # ********************************************************************* + + consider_reversed_edges = True + + valid_node_paths = [ + [0,1,2,3], + [6,5,4,3], + [6,7,8,9], + [10,8,9], + [3,2,1,0], # path 1 reversed + [3,4,5,6], # path 2 reversed + [9,8,7,6], # path 3 reversed + [6,7,8,10] # node 10 is connected to node 8 but not the other way arou. + ] + + invalid_node_paths = [ + [], # empty list + [1], # single node path + [-1,0,1,2,3], # nodes do not belong to the network + ] + + # make sure valid node paths are valid + for path in valid_node_paths: + assert gis_iden.is_node_path(network, path, consider_reversed_edges) + # make sure invalid node paths are invalid + for path in invalid_node_paths: + assert not gis_iden.is_node_path( + network, + path, + consider_reversed_edges + ) + + # ************************************************************************* + # ************************************************************************* + + def test_valid_edge_paths(self): + + # ********************************************************************* + # ********************************************************************* + + # create network + + network = nx.MultiDiGraph() + + # define and add edges + + list_edges = [ + (0,1),(1,2),(2,3), # path 1 + (4,3),(5,4),(6,5), # path 2 + (6,7),(7,8),(8,9), # path 3 + (2,2), # self loop on path 1 + (4,4), # self loop on path 2 + (9,9), # self loop on path 3 + (10,8) # extra lone neighbour for node 8 on + ] + + network.add_edges_from(list_edges) + # ********************************************************************* # ********************************************************************* @@ -2790,18 +3709,17 @@ class TestGisIdentify: # ********************************************************************* # ********************************************************************* - - def test_simplifiable_paths(self): + + # ************************************************************************* + # ************************************************************************* - # ********************************************************************* - # ********************************************************************* + def test_straight_paths_reversed_edges_self_loops(self): # create network network = nx.MultiDiGraph() # define and add edges - list_edges = [ (0,1),(1,2),(2,3), # path 1 (4,3),(5,4),(6,5), # path 2 @@ -2811,75 +3729,78 @@ class TestGisIdentify: (9,9), # self loop on path 3 (10,8) # extra lone neighbour for node 8 on ] - network.add_edges_from(list_edges) - # ********************************************************************* - # ********************************************************************* - - # node paths - - path_as_node_keys = True - - # arc directions matter - - ignore_arc_directions = False + # reversed edges are okay, self loops too ignore_self_loops = True + consider_reversed_edges = True # valid node paths - valid_straight_node_paths = [ [0,1,2], [1,2,3], [0,1,2,3], [5,4,3], [6,5,4], - [6,5,4,3] + [6,5,4,3], + [0,1], # just two nodes + [0,1,2,3,4], # node 4 is connected using an edge in the opposite dir. + [6,5,4,3,2], # node 2 is connected using an edge in the opposite dir. + [3,4,5,6], # the path is reversed ] # invalid node paths - invalid_straight_node_paths = [ - [0,1], # just two nodes - [0,1,2,3,4], # node 4 is connected using an arc in the opposite dir. - [6,5,4,3,2], # node 2 is connected using an arc in the opposite dir. [6,7,8,9], # node 8 has three neighbours (7, 9 and 10) - [3,4,5,6], # the path is reversed [0,4,1], # node 4 is not a neighbour of nodes 0 and 1 [11,3,4,5,6] # there is no node 11 ] # make sure valid node paths are valid - for path in valid_straight_node_paths: - - assert gis_iden.is_path_simplifiable( + assert gis_iden.is_path_straight( network, path, - path_as_node_keys=path_as_node_keys, - ignore_arc_directions=ignore_arc_directions, + consider_reversed_edges=consider_reversed_edges, ignore_self_loops=ignore_self_loops) # make sure invalid node paths are invalid - for path in invalid_straight_node_paths: - - assert not gis_iden.is_path_simplifiable( + assert not gis_iden.is_path_straight( network, path, - path_as_node_keys=path_as_node_keys, - ignore_arc_directions=ignore_arc_directions, + consider_reversed_edges=consider_reversed_edges, ignore_self_loops=ignore_self_loops) # ********************************************************************* # ********************************************************************* - # ignore arc directions + # ************************************************************************* + # ************************************************************************* + + def test_straight_paths_no_reversed_edges_self_loops(self): + + # create network + + network = nx.MultiDiGraph() + + # define and add edges + list_edges = [ + (0,1),(1,2),(2,3), # path 1 + (4,3),(5,4),(6,5), # path 2 + (6,7),(7,8),(8,9), # path 3 + (2,2), # self loop on path 1 + (4,4), # self loop on path 2 + (9,9), # self loop on path 3 + (10,8) # extra lone neighbour for node 8 on + ] + network.add_edges_from(list_edges) - ignore_arc_directions = True + # no reversed edges, self loops are okay ignore_self_loops = True + consider_reversed_edges = False # valid node paths @@ -2887,25 +3808,25 @@ class TestGisIdentify: [0,1,2], [1,2,3], [0,1,2,3], - [2,1,0], - [3,2,1], - [3,2,1,0], [5,4,3], [6,5,4], [6,5,4,3], - [3,4,5], - [4,5,6], - [3,4,5,6], - [0,1,2,3,4,5,6,7,8], - [8,7,6,5,4,3,2,1,0], - [0,1,2,3,4], - [6,5,4,3,2] + [0,1], # just two nodes ] # invalid node paths invalid_straight_node_paths = [ - [0,1], # just two nodes + [2,1,0], # reversed path + [3,2,1], # reversed path + [3,2,1,0], # reversed path + [3,4,5], # reversed path + [4,5,6], # reversed path + [3,4,5,6], # reversed path + [0,1,2,3,4,5,6,7,8], # path with reversed elements + [8,7,6,5,4,3,2,1,0], # path with reversed element + [0,1,2,3,4], # path with reversed elements + [6,5,4,3,2], # the last edge is reversed [6,7,8,9], # node 8 has three neighbours (7, 9 and 10) [0,4,1], # node 4 is not a neighbour of nodes 0 and 1 [11,3,4,5,6], # there is no node 11 @@ -2913,34 +3834,50 @@ class TestGisIdentify: [9,8,7,6,5,4,3,2,1,0] # node 8 has three neighbours (7, 9 and 10) ] - # make sure valid node paths are valid - - for path in valid_straight_node_paths: - - assert gis_iden.is_path_simplifiable( + # make sure valid node paths are valid + for path in valid_straight_node_paths: + assert gis_iden.is_path_straight( network, path, - path_as_node_keys=path_as_node_keys, - ignore_arc_directions=ignore_arc_directions, + consider_reversed_edges=consider_reversed_edges, ignore_self_loops=ignore_self_loops) - # make sure invalid node paths are invalid - + # make sure invalid node paths are invalid for path in invalid_straight_node_paths: - - assert not gis_iden.is_path_simplifiable( + assert not gis_iden.is_path_straight( network, path, - path_as_node_keys=path_as_node_keys, - ignore_arc_directions=ignore_arc_directions, + consider_reversed_edges=consider_reversed_edges, ignore_self_loops=ignore_self_loops) # ********************************************************************* # ********************************************************************* - ignore_arc_directions = False + # ************************************************************************* + # ************************************************************************* + + def test_straight_paths_no_reversed_edges_no_self_loops(self): + + # create network + + network = nx.MultiDiGraph() + + # define and add edges + list_edges = [ + (0,1),(1,2),(2,3), # path 1 + (4,3),(5,4),(6,5), # path 2 + (6,7),(7,8),(8,9), # path 3 + (2,2), # self loop on path 1 + (4,4), # self loop on path 2 + (9,9), # self loop on path 3 + (10,8) # extra lone neighbour for node 8 on + ] + network.add_edges_from(list_edges) + + # no reversed edges, self loops are not okay ignore_self_loops = False + consider_reversed_edges = False # (0,1),(1,2),(2,3), # path 1 # (4,3),(5,4),(6,5), # path 2 @@ -2969,36 +3906,50 @@ class TestGisIdentify: [2,1,0] ] - # make sure valid node paths are valid - - for path in valid_straight_node_paths: - - assert gis_iden.is_path_simplifiable( + # make sure valid node paths are valid + for path in valid_straight_node_paths: + assert gis_iden.is_path_straight( network, path, - path_as_node_keys=path_as_node_keys, - ignore_arc_directions=ignore_arc_directions, + consider_reversed_edges=consider_reversed_edges, ignore_self_loops=ignore_self_loops) - # make sure invalid node paths are invalid - + # make sure invalid node paths are invalid for path in invalid_straight_node_paths: - - assert not gis_iden.is_path_simplifiable( + assert not gis_iden.is_path_straight( network, path, - path_as_node_keys=path_as_node_keys, - ignore_arc_directions=ignore_arc_directions, + consider_reversed_edges=consider_reversed_edges, ignore_self_loops=ignore_self_loops) # ********************************************************************* # ********************************************************************* - # ignore arc directions + # ************************************************************************* + # ************************************************************************* + + def test_straight_paths_reversed_edges_no_self_loops(self): + + # create network + + network = nx.MultiDiGraph() + + # define and add edges + list_edges = [ + (0,1),(1,2),(2,3), # path 1 + (4,3),(5,4),(6,5), # path 2 + (6,7),(7,8),(8,9), # path 3 + (2,2), # self loop on path 1 + (4,4), # self loop on path 2 + (9,9), # self loop on path 3 + (10,8) # extra lone neighbour for node 8 on + ] + network.add_edges_from(list_edges) - ignore_arc_directions = True + # reversed edges are okay, self loops are not ignore_self_loops = False + consider_reversed_edges = True # valid node paths @@ -3020,33 +3971,27 @@ class TestGisIdentify: [6,5,4,3] ] - # make sure valid node paths are valid - - for path in valid_straight_node_paths: - - assert gis_iden.is_path_simplifiable( + # make sure valid node paths are valid + for path in valid_straight_node_paths: + assert gis_iden.is_path_straight( network, path, - path_as_node_keys=path_as_node_keys, - ignore_arc_directions=ignore_arc_directions, + consider_reversed_edges=consider_reversed_edges, ignore_self_loops=ignore_self_loops) - # make sure invalid node paths are invalid - + # make sure invalid node paths are invalid for path in invalid_straight_node_paths: - - assert not gis_iden.is_path_simplifiable( + assert not gis_iden.is_path_straight( network, path, - path_as_node_keys=path_as_node_keys, - ignore_arc_directions=ignore_arc_directions, + consider_reversed_edges=consider_reversed_edges, ignore_self_loops=ignore_self_loops) # ********************************************************************* # ********************************************************************* - #************************************************************************** - #************************************************************************** + # ************************************************************************* + # ************************************************************************* def test_nearest_node_keys(self): @@ -3179,4 +4124,4 @@ class TestGisIdentify: abs_tol=1) #****************************************************************************** -#****************************************************************************** \ No newline at end of file +#****************************************************************************** diff --git a/tests/test_gis_modify.py b/tests/test_gis_modify.py index 96c21ba..90a7f5b 100644 --- a/tests/test_gis_modify.py +++ b/tests/test_gis_modify.py @@ -748,7 +748,7 @@ class TestGisModify: for edge_key in removed_edges: # confirm that there is at least one edge in reverse reverse_edge_found = False - for other_edge_key in gis_iden.all_arcs_from_a_to_b( + for other_edge_key in gis_iden.get_edges_from_a_to_b( network, edge_key[1], edge_key[0]): @@ -774,7 +774,7 @@ class TestGisModify: for edge_key in removed_edges: # confirm that there is at least one edge in reverse reverse_edge_found = False - for other_edge_key in gis_iden.all_arcs_from_a_to_b( + for other_edge_key in gis_iden.get_edges_from_a_to_b( network, edge_key[1], edge_key[0]): @@ -3201,4 +3201,4 @@ class TestGisModify: # ********************************************************************* # ***************************************************************************** -# ***************************************************************************** \ No newline at end of file +# ***************************************************************************** diff --git a/tests/test_gis_utils.py b/tests/test_gis_utils.py index 05697d8..961100c 100644 --- a/tests/test_gis_utils.py +++ b/tests/test_gis_utils.py @@ -945,7 +945,7 @@ class TestGisUtils: # find out which is the closest arc - nearest_arc_keys, network = gis_iden.identify_arc_closest_to_node( + nearest_arc_keys, network = gis_iden.identify_edge_closest_to_node( network, node_keys=['P']) @@ -2049,7 +2049,7 @@ class TestGisUtils: edge_dict = directed_network.edges[edge_key] - for other_edge_key in gis_iden.all_arcs_from_a_to_b( + for other_edge_key in gis_iden.get_edges_from_a_to_b( network, edge_key[0], edge_key[1]): # check all attributes @@ -2087,4 +2087,4 @@ class TestGisUtils: # ************************************************************************* # ************************************************************************* -# ************************************************************************* \ No newline at end of file +# ************************************************************************* -- GitLab