From 483ceeb3a23f5a42c25f04bd817206aba07540cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20L=2E=20Magalh=C3=A3es?= <pedro.magalhaes@uni-bremen.de> Date: Fri, 8 Dec 2023 22:10:47 +0100 Subject: [PATCH] Simplifiable path identification working correctly now. --- src/topupopt/data/gis/calculate.py | 76 +---- src/topupopt/data/gis/identify.py | 110 +++++--- src/topupopt/data/gis/modify.py | 157 +++-------- src/topupopt/data/gis/utils.py | 30 +- tests/test_all.py | 16 -- tests/test_gis_identify.py | 432 ++++++++++++++++++++--------- tests/test_gis_modify.py | 89 +++++- tests/test_gis_utils.py | 27 ++ 8 files changed, 530 insertions(+), 407 deletions(-) diff --git a/src/topupopt/data/gis/calculate.py b/src/topupopt/data/gis/calculate.py index 2935702..ad236f7 100644 --- a/src/topupopt/data/gis/calculate.py +++ b/src/topupopt/data/gis/calculate.py @@ -43,72 +43,52 @@ def edge_lengths(network: MultiDiGraph, edge_keys: tuple = None) -> dict: A dictionary with the lengths for each edge. """ - + # determine if the graph is projected or not graph_is_projected = is_projected(network.graph['crs']) - - # for each edge on the graph - + # check if edge keys were specified if type(edge_keys) == type(None): - + # no particular edge keys were provided: consider all edges (default) edge_keys = network.edges(keys=True) # tuple(network.edges(keys=True)) - + # initialise length dict length_dict = {} - + # for each edge on the graph for edge_key in edge_keys: - # calculate it using the library - if graph_is_projected: - # calculate it using projected coordinates - if osm.KEY_OSMNX_GEOMETRY in network.edges[edge_key]: - # use geometry - length_dict[edge_key] = length( network.edges[edge_key][osm.KEY_OSMNX_GEOMETRY] ) - else: - # use (projected) coordinates - start_point = Point( (network.nodes[edge_key[0]][osm.KEY_OSMNX_X], network.nodes[edge_key[0]][osm.KEY_OSMNX_Y]) ) - end_point = Point( (network.nodes[edge_key[1]][osm.KEY_OSMNX_X], network.nodes[edge_key[1]][osm.KEY_OSMNX_Y]) ) - length_dict[edge_key] = start_point.distance(end_point) else: - # calculate it using unprojected coordinates (lat/long) - if osm.KEY_OSMNX_GEOMETRY in network.edges[edge_key]: - # use geometry - length_dict[edge_key] = great_circle_distance_along_path( network.edges[edge_key][osm.KEY_OSMNX_GEOMETRY] ) - else: - # use (unprojected) coordinates - length_dict[edge_key] = great_circle( lat1=network.nodes[edge_key[0]][osm.KEY_OSMNX_Y], lon1=network.nodes[edge_key[0]][osm.KEY_OSMNX_X], lat2=network.nodes[edge_key[1]][osm.KEY_OSMNX_Y], lon2=network.nodes[edge_key[1]][osm.KEY_OSMNX_X] ) - + # return the dict with lengths of each edge return length_dict # ***************************************************************************** @@ -132,15 +112,10 @@ def great_circle_distance_along_path(path: LineString) -> float: The sum of the individual great circle distances along the path. """ - # get coordinates - lon = tuple(path.coords.xy[0]) - lat = tuple(path.coords.xy[1]) - # sum individual distances and return - return sum( great_circle( lat[:-1], # latitudes of starting points @@ -167,7 +142,6 @@ def update_street_count(network: MultiDiGraph): None. """ - # update street count street_count_dict = count_streets_per_node(network) network.add_nodes_from( @@ -207,8 +181,6 @@ def node_path_length(network: MultiDiGraph, path_length = len(path) if path_length == 0: return inf - - #************************************************************************** # if the path is given as a list of node keys, then it is subjective # i.e., it may refer to many paths, namely if parallel edges exist @@ -298,23 +270,15 @@ def edge_path_length(network: MultiDiGraph, """ # check the number of - path_length = len(path) - if path_length == 0: - return inf - if ident.is_edge_path(network, path, **kwargs): - return sum( network.edges[edge_key][osm.KEY_OSMNX_LENGTH] for edge_key in path ) - else: - # no path provided - return inf # ***************************************************************************** @@ -347,61 +311,33 @@ def count_ocurrences(gdf: GeoDataFrame, """ if type(column_entries) == list: - # find entries also present in the dict - # initialise dict - count_dict = {} - # for each key in the dict - for key in column_entries: - # # store the number of rows - # count_dict[key] = gdf[gdf[column]==key].shape[0] - # count the number of rows with this key - if isna(key): - count_dict[key] = gdf[gdf[column].isnull()].shape[0] - else: - count_dict[key] = gdf[gdf[column]==key].shape[0] - else: - # find all unique entries - # initialise dict - count_dict = {} - for entry in gdf[column]: - # check if it is already in the dict - if entry in count_dict: - # it is, skip - continue - # it is not, count and store the number of rows with said entry - if isna(entry): #type(entry) == type(None): - count_dict[entry] = gdf[gdf[column].isnull()].shape[0] - else: - count_dict[entry] = gdf[gdf[column]==entry].shape[0] - # return statement - return count_dict # ***************************************************************************** diff --git a/src/topupopt/data/gis/identify.py b/src/topupopt/data/gis/identify.py index 5f89cbf..1449bac 100644 --- a/src/topupopt/data/gis/identify.py +++ b/src/topupopt/data/gis/identify.py @@ -235,7 +235,6 @@ def edges_are_in_reverse( type(rv_dict[attr_key]) != list)): # the sets of list arguments do not match # or, the arguments are not equivalent - # print('ping2:'+str(attr_key)) return False elif (type(attr_value) == list and type(rv_dict[attr_key]) == list and @@ -251,7 +250,6 @@ def edges_are_in_reverse( # either the geometries are not reversed # or, there is no geometry attribute in the reverse dict # 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 type(rv_dict[attr_key]) == LineString and @@ -266,7 +264,6 @@ def edges_are_in_reverse( type(rv_dict[attr_key]) != bool)): # either the reversed flags match # or, there is no reversed flag in the reverse dict - # print('ping4:'+str(attr_key)) return False elif (attr_key == osm.KEY_OSMNX_REVERSED and attr_key in rv_dict and @@ -281,7 +278,6 @@ def edges_are_in_reverse( # either the lengths differ too much # or, there is no length attribute in the reverse dict # or it is not a numeric type - # print('ping5:'+str(attr_key)) return False elif (attr_key == osm.KEY_OSMNX_LENGTH and attr_key in rv_dict and @@ -291,7 +287,6 @@ def edges_are_in_reverse( continue elif attr_key in rv_dict and attr_value != rv_dict[attr_key]: # either the attributes do not match - # print('ping6:'+str(attr_key)) return False # else: # the argument does not exist @@ -371,7 +366,7 @@ def close_to_extremities( if line_distance < end_distance: # the point is closer to the line than to the start/end points continue - # TODO: reach these statements + # reach these statements if use_start_point_equidistant: _start.append(i) else: @@ -1032,7 +1027,6 @@ def is_path_straight(network: nx.MultiDiGraph, A boolean indicating whether the path is straight or not. """ - # confirm that it is a path if not is_node_path(network, path, consider_reversed_edges): return False @@ -1103,7 +1097,7 @@ def find_simplifiable_paths(network: nx.MultiDiGraph, # locate all the non-excluded nodes that can form straight paths - intermediate_candidate_nodes = [ + intermediate_candidate_nodes = set([ node_key for node_key in network.nodes() # the node cannot be among those excluded @@ -1122,7 +1116,7 @@ def find_simplifiable_paths(network: nx.MultiDiGraph, if (ignore_self_loops or (not ignore_self_loops and not network.has_edge(node_key, node_key))) - ] + ]) # ************************************************************************* @@ -1130,12 +1124,10 @@ def find_simplifiable_paths(network: nx.MultiDiGraph, list_paths = [] list_paths_nodes = [] - - list_nodes_joined = [] + list_nodes_joined = set([]) # 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 @@ -1145,21 +1137,21 @@ def find_simplifiable_paths(network: nx.MultiDiGraph, # reversed edges are accepted new_sequence = _find_path_direction_insensitive( network, - list_valid_nodes=intermediate_candidate_nodes, - start_node=candidate_node + list_valid_nodes=intermediate_candidate_nodes-list_nodes_joined, + start_node=candidate_node, + ignore_self_loops=ignore_self_loops ) else: # reversed edges are not accepted new_sequence = _find_path_direction_sensitive( network, - list_valid_nodes=intermediate_candidate_nodes, - start_node=candidate_node + list_valid_nodes=intermediate_candidate_nodes-list_nodes_joined, + start_node=candidate_node, + ignore_self_loops=ignore_self_loops ) - # 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): + new_sequence in list_paths): # path is just one edge or has already been included continue @@ -1170,11 +1162,8 @@ def find_simplifiable_paths(network: nx.MultiDiGraph, # 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? - ) + # update the list of intermediate nodes already on paths + list_nodes_joined.update(set(new_sequence[1:-1])) # ************************************************************************* # ************************************************************************* @@ -1190,7 +1179,8 @@ def find_simplifiable_paths(network: nx.MultiDiGraph, def _find_path_direction_sensitive( network: nx.MultiDiGraph, list_valid_nodes: list, - start_node + start_node, + ignore_self_loops: bool ) -> list: def find_path_forward(network: nx.MultiDiGraph, @@ -1228,12 +1218,19 @@ def _find_path_direction_sensitive( 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 + elif a_neighbour == path[0] and len(path) >= 3: + # the path length is greater than or equal to 3, + # neighbour is already on the path, matches the start, + # and has two neighbours other than itself: + # close the loop and return the path + if (len(set(neighbours( + network, + a_neighbour, + ignore_self_loops=ignore_self_loops))) == 2): + # 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 @@ -1253,7 +1250,6 @@ def _find_path_direction_sensitive( # >> 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 @@ -1283,9 +1279,18 @@ def _find_path_direction_sensitive( # 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 + # the path length is greater than or equal to 3, + # neighbour is already on the path, matches the end, + # and has two neighbours other than itself: + # close the loop and return the path + # if (len(set(neighbours( + # network, + # a_neighbour, + # ignore_self_loops=ignore_self_loops))) == 2): + # # add the neighbour to the start of the path + # path.insert(0, a_neighbour) + # # return the path + # return path path.insert(0, a_neighbour) # return the path return path @@ -1320,7 +1325,8 @@ def _find_path_direction_sensitive( def _find_path_direction_insensitive( network: nx.MultiDiGraph, list_valid_nodes: list, - start_node + start_node, + ignore_self_loops: bool ) -> list: def find_path_forward(network: nx.MultiDiGraph, @@ -1358,12 +1364,18 @@ def _find_path_direction_insensitive( # 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 + # the path length is greater than or equal to 3, + # neighbour is already on the path, matches the start, + # and has two neighbours other than itself: + # close the loop and return the path + if (len(set(neighbours( + network, + a_neighbour, + ignore_self_loops=ignore_self_loops))) == 2): + # 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 @@ -1412,8 +1424,18 @@ def _find_path_direction_insensitive( # 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 + # the path length is greater than or equal to 3, + # neighbour is already on the path, matches the end, + # and has two neighbours other than itself: + # close the loop and return the path + # if (len(set(neighbours( + # network, + # a_neighbour, + # ignore_self_loops=ignore_self_loops))) == 2): + # # add the neighbour to the start of the path + # path.insert(0, a_neighbour) + # # return the path + # return path # add the neighbour to the start of the path path.insert(0, a_neighbour) # return the path diff --git a/src/topupopt/data/gis/modify.py b/src/topupopt/data/gis/modify.py index 643e7cc..97990b5 100644 --- a/src/topupopt/data/gis/modify.py +++ b/src/topupopt/data/gis/modify.py @@ -75,88 +75,57 @@ def transform_roundabouts_into_crossroads( """ # declare the output list - list_roundabout_centroids = [] # for each roundabout - for roundabout in roundabouts: - - #********************************************************************** - # make sure roundabout qualifies as a roundabout - if not gis_iden.is_roundabout(network, roundabout): - + # not a roundabout, skip list_roundabout_centroids.append(None) - continue - # if any node in the roundabout also appears in another one - roundabout_overlaps = False - # for each node forming this roundabout - for node_roundabout in roundabout: - # check every other roundabout - for other_roundabout in roundabouts: - if other_roundabout == roundabout: - continue - # if the node exists in another roundabout - if node_roundabout in other_roundabout: - + # roundabouts overlap roundabout_overlaps = True - break - if roundabout_overlaps: - + # break out of the loop break - if roundabout_overlaps: - + # the roundabout overlaps with some other one, skip it list_roundabout_centroids.append(None) - continue - - # do nothing - - #********************************************************************** - + + # ********************************************************************* + # ********************************************************************* # create a new node whose location is the roundabout's centroid - list_point_coordinates = [ (network.nodes[node_key][osm.KEY_OSMNX_X], network.nodes[node_key][osm.KEY_OSMNX_Y]) for node_key in roundabout ] - new_geo = LineString(list_point_coordinates) - roundabout_centroid_key = generate_pseudo_unique_key(network) - network.add_node( roundabout_centroid_key, **{osm.KEY_OSMNX_X: new_geo.centroid.coords.xy[0][0], osm.KEY_OSMNX_Y: new_geo.centroid.coords.xy[1][0]} ) - list_roundabout_centroids.append(roundabout_centroid_key) - - #********************************************************************** - + # ********************************************************************* + # ********************************************************************* # create new edges to link each node leading to the roundabout to the # node just created (new_node_key) - # find the edges leading to the roundabout - list_edges_leading_to_roundabout = [ edge_key # for each node in the roundabout @@ -171,24 +140,16 @@ def transform_roundabouts_into_crossroads( node_key, other_node_key) ] - # for each edge leading to the roundabout - for edge_key in list_edges_leading_to_roundabout: - # replace it with a new edge to the new node - # get edge dict - edge_dict = network.get_edge_data(edge_key[0], edge_key[1], edge_key[2]) - if osm.KEY_OSMNX_GEOMETRY in edge_dict: - # geometry exists old_geometry = edge_dict[osm.KEY_OSMNX_GEOMETRY] - else: # geometry does not exist # create it @@ -198,53 +159,37 @@ def transform_roundabouts_into_crossroads( (network.nodes[edge_key[1]][osm.KEY_OSMNX_X], network.nodes[edge_key[1]][osm.KEY_OSMNX_Y])] ) + # if osm.KEY_OSMNX_LENGTH in edge_dict: + # # length exists + # old_length = edge_dict[osm.KEY_OSMNX_LENGTH] + # else: + # # length does not exist + # old_length = edge_lengths( + # network, + # edge_keys=[edge_key])[edge_key] + # the old length has to exist + old_length = edge_dict[osm.KEY_OSMNX_LENGTH] - if osm.KEY_OSMNX_LENGTH in edge_dict: - # length exists - old_length = edge_dict[osm.KEY_OSMNX_LENGTH] - else: - # length does not exist - old_length = edge_lengths( - network, - edge_keys=[edge_key])[edge_key] - - # # calculate it - # old_length = great_circle( - # lat1=network.nodes[edge_key[0]][osm.KEY_OSMNX_Y], - # lon1=network.nodes[edge_key[0]][osm.KEY_OSMNX_X], - # lat2=network.nodes[edge_key[1]][osm.KEY_OSMNX_Y], - # lon2=network.nodes[edge_key[1]][osm.KEY_OSMNX_X] - # ) - - #****************************************************************** - #****************************************************************** + # ***************************************************************** + # ***************************************************************** # find closest point - if edge_key[0] in roundabout: - # this edge starts from the roundabout - new_edge_start_node = roundabout_centroid_key - new_edge_end_node = edge_key[1] - # create geometry object between old roundabout point to the # roundabout's centroid - extra_geometry = LineString( [(network.nodes[roundabout_centroid_key][osm.KEY_OSMNX_X], network.nodes[roundabout_centroid_key][osm.KEY_OSMNX_Y]), (network.nodes[edge_key[0]][osm.KEY_OSMNX_X], network.nodes[edge_key[0]][osm.KEY_OSMNX_Y])] ) - if is_projected(network.graph['crs']): - + # projected graph: use direct method extra_length = length(extra_geometry) - - else: - + else: # unprojected graph: use great circle method extra_length = great_circle( lat1=network.nodes[ roundabout_centroid_key][osm.KEY_OSMNX_Y], @@ -253,31 +198,23 @@ def transform_roundabouts_into_crossroads( lat2=network.nodes[edge_key[0]][osm.KEY_OSMNX_Y], lon2=network.nodes[edge_key[0]][osm.KEY_OSMNX_X] ) - elif edge_key[1] in roundabout: - # this edge ends in the roundabout - new_edge_start_node = edge_key[0] - new_edge_end_node = roundabout_centroid_key - # create geometry object between old roundabout point to the # roundabout's centroid - extra_geometry = LineString( [(network.nodes[roundabout_centroid_key][osm.KEY_OSMNX_X], network.nodes[roundabout_centroid_key][osm.KEY_OSMNX_Y]), (network.nodes[edge_key[1]][osm.KEY_OSMNX_X], network.nodes[edge_key[1]][osm.KEY_OSMNX_Y])] ) - if is_projected(network.graph['crs']): - + # projected graph, use direct method extra_length = length(extra_geometry) - else: - + # unprojected graph, use great circle method extra_length = great_circle( lat1=network.nodes[ roundabout_centroid_key][osm.KEY_OSMNX_Y], @@ -287,40 +224,32 @@ def transform_roundabouts_into_crossroads( lon2=network.nodes[edge_key[1]][osm.KEY_OSMNX_X] ) - #****************************************************************** - #****************************************************************** + # ***************************************************************** + # ***************************************************************** edge_dict[osm.KEY_OSMNX_GEOMETRY] = linemerge( [old_geometry, extra_geometry]) - edge_dict[osm.KEY_OSMNX_LENGTH] = old_length+extra_length - network.add_edge(new_edge_start_node, new_edge_end_node, **edge_dict) - #************************************************************************** - #************************************************************************** + # ************************************************************************* + # ************************************************************************* # remove the roundabout nodes for roundabout_index, roundabout in enumerate(roundabouts): - # if the transformation of the roundabout was successful... - if list_roundabout_centroids[roundabout_index] != None: - # remove the roundabout nodes - network.remove_nodes_from(roundabout) - # return - return list_roundabout_centroids - #************************************************************************** - #************************************************************************** + # ************************************************************************* + # ************************************************************************* # ***************************************************************************** # ***************************************************************************** @@ -1110,9 +1039,7 @@ def recreate_edges(network: nx.MultiDiGraph, # TODO: try to create all the edges (with lengths included) in one go # calculate the lengths - edge_lengths_by_dict = edge_lengths(network, edge_keys=segment_keys) - network.add_edges_from( tuple( (*segment_key, @@ -1122,14 +1049,10 @@ def recreate_edges(network: nx.MultiDiGraph, ) # update the outputs - if len(line_segments) > 0: - recreated_edges[edge_key] = segment_keys - # print('here segment keyse') - # print(segment_keys) connection_node_keys_per_edge[edge_key] = _node_keys - + # return statement return connection_node_keys_per_edge, recreated_edges # ***************************************************************************** @@ -1208,41 +1131,27 @@ def connect_nodes_to_edges( # 2) for each edge, and node that is to be connected to it, find its closest # point on the edge - points_per_edge = {} - for edge_key, _node_keys in nodes_to_connect_to_edge.items(): # check if the geometry exists - if osm.KEY_OSMNX_GEOMETRY in network.edges[edge_key]: - # the geometry object exists, get it - edge_geo = network.edges[edge_key][osm.KEY_OSMNX_GEOMETRY] - else: - # the geometry object does not exist, make it - edge_geo = LineString( [(network.nodes[edge_key[0]][osm.KEY_OSMNX_X], network.nodes[edge_key[0]][osm.KEY_OSMNX_Y]), (network.nodes[edge_key[1]][osm.KEY_OSMNX_X], network.nodes[edge_key[1]][osm.KEY_OSMNX_Y])] ) - # store the geometry - if store_unsimplified_geometries: - # update the edge - network.add_edge(*edge_key, **{osm.KEY_OSMNX_GEOMETRY: edge_geo}) - # use nearest_points to locate the closest points on the edge - points_per_edge[edge_key] = [ nearest_points( edge_geo, @@ -1251,9 +1160,7 @@ def connect_nodes_to_edges( )[0] # [0] to get the point on the edge for node_key in _node_keys ] - # TIP: exclude the points that can be considered close to the start or end nodes - # TIP: use the shortest line method to obtain the line geometry #************************************************************************** diff --git a/src/topupopt/data/gis/utils.py b/src/topupopt/data/gis/utils.py index 44b9ab2..bd64e2e 100644 --- a/src/topupopt/data/gis/utils.py +++ b/src/topupopt/data/gis/utils.py @@ -48,7 +48,7 @@ def find_gpkg_packable_columns(gdf: GeoDataFrame) -> set: # packable columns: 1), 2) and 3) - #************************************************************************** + # ************************************************************************* # 1) columns with equivalent lowercase names @@ -63,7 +63,7 @@ def find_gpkg_packable_columns(gdf: GeoDataFrame) -> set: if lowercase_columns.count(lccolumn) >= 2 ) - #************************************************************************** + # ************************************************************************* # for each column @@ -108,7 +108,7 @@ def find_gpkg_packable_columns(gdf: GeoDataFrame) -> set: set_columns.add(column) - #************************************************************************** + # ************************************************************************* return set_columns @@ -572,7 +572,7 @@ def prepare_node_data_from_geodataframe( node_key_to_gdf_index_dict[node_key] = gdf.index[gdf_entry] - #************************************************************************** + # ************************************************************************* return node_keys, node_data_container, node_key_to_gdf_index_dict @@ -812,27 +812,22 @@ def simplify_network(network: MultiDiGraph, protected_nodes, max_iterations=dead_end_probing_depth ) - # 2) remove longer parallel edges (tends to create straight paths) gis_mod.remove_longer_parallel_edges( network, ignore_edge_directions=remove_opposite_parallel_edges ) - # 3) remove self loops (tends to create straight paths and dead ends) gis_mod.remove_self_loops(network) - # 4) join segments (can create self-loops) - simplifiable_paths = gis_iden.find_all_straight_paths( + simplifiable_paths = gis_iden.find_simplifiable_paths( network, protected_nodes ) for path in simplifiable_paths: gis_mod.replace_path(network, path) - # 4) remove self loops (tends to create straight paths and dead ends) gis_mod.remove_self_loops(network) - # 5) transform roundabouts into crossroads (can create straight paths) list_roundabout_nodes = gis_iden.find_roundabouts( network, @@ -841,8 +836,7 @@ def simplify_network(network: MultiDiGraph, network, list_roundabout_nodes ) - - # update street count + # 6) update street count if update_street_count_per_node: gis_calc.update_street_count(network) @@ -915,13 +909,13 @@ def identify_building_entrance_edges( # output: a list of edge keys (one per building entrance) # exceptions: if a building cannot be linked to an edge key, link it to None - #************************************************************************** + # ************************************************************************* if revert_to_original_crs: original_crs = network.graph['crs'] - #************************************************************************** + # ************************************************************************* # 1) for each building (entrance), identify the closest edge @@ -938,7 +932,7 @@ def identify_building_entrance_edges( _closest_edge_keys_dict = dict(building_entrance_edges) - #************************************************************************** + # ************************************************************************* # 2) identify the nodes that require additional precautions (i.e., those # that should not be connected to their closest edges) @@ -1109,7 +1103,7 @@ def identify_building_entrance_edges( trouble_nodes.append(node_key) - #************************************************************************** + # ************************************************************************* # 3) for the nodes whose closest edges that cannot be linked back to the no- # des, find the edges that can, if any, (via their street names) and select @@ -1165,11 +1159,11 @@ def identify_building_entrance_edges( building_entrance_edges[node_key] = other_closest_edge - #************************************************************************** + # ************************************************************************* # 4) for all other cases, use the closest edge among all - #************************************************************************** + # ************************************************************************* # revert network crs back to the original, if necessary diff --git a/tests/test_all.py b/tests/test_all.py index 7e75605..1a028d4 100644 --- a/tests/test_all.py +++ b/tests/test_all.py @@ -12,7 +12,6 @@ from examples_esipp_network import examples as examples_esipp_network from examples_esipp_problem import examples as examples_esipp_problem from examples_esipp_resource import examples as examples_esipp_resource from examples_esipp import examples as examples_esipp -from examples_gis import examples as examples_gis from examples_signal import examples as examples_signal #****************************************************************************** @@ -40,9 +39,6 @@ def test_suite(): test_examples_esipp = True # test_examples_esipp = False - # test_examples_gis = True - test_examples_gis = False - test_examples_signal = True # test_examples_signal = False @@ -230,18 +226,6 @@ def test_suite(): #************************************************************************** - # gis - - if test_examples_gis: - - print('\'gis\': testing about to start...') - - examples_gis(seed_number=None) - - print('\'gis\': testing complete.') - - #************************************************************************** - # signal if test_examples_signal: diff --git a/tests/test_gis_identify.py b/tests/test_gis_identify.py index ed3728e..0486809 100644 --- a/tests/test_gis_identify.py +++ b/tests/test_gis_identify.py @@ -24,12 +24,13 @@ class TestGisIdentify: # ************************************************************************* # ************************************************************************* - def path_validator( + def straight_path_validator( self, network: nx.MultiDiGraph, path: list, excluded_nodes: list, - consider_reversed_edges: bool): + consider_reversed_edges: bool, + ignore_self_loops: bool): # find out the unique nodes set_nodes = set(path) # at least three nodes @@ -48,12 +49,181 @@ class TestGisIdentify: # no excluded nodes in the intermediate positions for node in excluded_nodes: assert node not in path[1:-1] + # intermediate nodes can only have two neighbours + for node_key in path[1:-1]: + assert len( + set( + gis_iden.neighbours( + network, + node_key, + ignore_self_loops=True + ) + ) + ) == 2 + # end nodes need to have at least one neighbour, except in loops, + # wherein they need to have two neighbours + if path[0] == path[-1]: + # end nodes in loops need to have at least two neighbours + assert len( + set( + gis_iden.neighbours( + network, + path[0], + ignore_self_loops=True + ) + ) + ) >= 2 + else: + # end nodes need to have at least one neighbour + assert len( + set( + gis_iden.neighbours( + network, + path[0], + ignore_self_loops=True + ) + ) + ) >= 1 + assert len( + set( + gis_iden.neighbours( + network, + path[-1], + ignore_self_loops=True + ) + ) + ) >= 1 + # if ignore_self_loops=False, intermediate nodes cannot have self-loops + if not ignore_self_loops: + for node in path[1:-1]: + assert not network.has_edge(node, node) + # if path[0] == path[-1]: + # # loop, applies to all nodes + # for node in path: + # assert not network.has_edge(node, node) + # else: + # # not a loop, applies only to intermediate nodes + # for node in path[1:-1]: + # assert not network.has_edge(node, node) # make sure it qualifies as a path assert gis_iden.is_node_path( network, path, consider_reversed_edges=consider_reversed_edges ) + # make sure it is a straight path + assert gis_iden.is_path_straight( + network, + path, + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops + ) + + + # ************************************************************************* + # ************************************************************************* + + def test_finding_simplifiable_paths_osmnx(self): + + # network should be a OSM-nx formatted graph + network = ox.graph_from_point( + (55.71654,9.11728), + network_type='drive', + custom_filter='["highway"~"residential|tertiary|unclassified|service"]', + truncate_by_edge=True + ) + + # ********************************************************************* + # ********************************************************************* + + consider_reversed_edges = False + ignore_self_loops = False + + # paths + paths = gis_iden.find_simplifiable_paths( + network, + excluded_nodes=[], + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops + ) + # verify the paths + for path in paths: + self.straight_path_validator( + network, + path, + excluded_nodes=[], + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops + ) + + # ********************************************************************* + # ********************************************************************* + + consider_reversed_edges = False + ignore_self_loops = True + + # paths + paths = gis_iden.find_simplifiable_paths( + network, + excluded_nodes=[], + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops + ) + # verify the paths + for path in paths: + self.straight_path_validator( + network, + path, + excluded_nodes=[], + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops) + + # ********************************************************************* + # ********************************************************************* + + consider_reversed_edges = True + ignore_self_loops = False + + # paths + paths = gis_iden.find_simplifiable_paths( + network, + excluded_nodes=[], + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops + ) + # verify the paths + for path in paths: + self.straight_path_validator( + network, + path, + excluded_nodes=[], + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops) + + # ********************************************************************* + # ********************************************************************* + + consider_reversed_edges = True + ignore_self_loops = True + + # paths + paths = gis_iden.find_simplifiable_paths( + network, + excluded_nodes=[], + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops + ) + # verify the paths + for path in paths: + self.straight_path_validator( + network, + path, + excluded_nodes=[], + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops) + + # ********************************************************************* + # ********************************************************************* # ************************************************************************* # ************************************************************************* @@ -79,11 +249,12 @@ class TestGisIdentify: # test path validator with non-path error_raised = False try: - assert not self.path_validator( + assert not self.straight_path_validator( network, [1, 1, 1], excluded_nodes, - False + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops ) except AssertionError: error_raised = True @@ -167,7 +338,7 @@ class TestGisIdentify: # add single node network.add_node(0) - path = gis_iden._find_path_direction_insensitive(network, [], 0) + path = gis_iden._find_path_direction_insensitive(network, [], 0, False) assert type(path) == list assert len(path) == 1 assert repr(path) == repr([0]) @@ -587,11 +758,12 @@ class TestGisIdentify: assert len(straight_paths) == len(true_straight_paths) for straight_path in straight_paths: assert straight_path in true_straight_paths - self.path_validator( + self.straight_path_validator( network, straight_path, excluded_nodes, - consider_reversed_edges=consider_reversed_edges + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops ) # ********************************************************************* @@ -611,11 +783,12 @@ class TestGisIdentify: assert len(straight_paths) == len(true_straight_paths) for straight_path in straight_paths: assert straight_path in true_straight_paths - self.path_validator( + self.straight_path_validator( network, straight_path, - excluded_nodes, - consider_reversed_edges=consider_reversed_edges + excluded_nodes, + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops ) # ********************************************************************* @@ -651,11 +824,12 @@ class TestGisIdentify: assert len(straight_paths) == len(true_straight_paths) for straight_path in straight_paths: assert straight_path in true_straight_paths - self.path_validator( + self.straight_path_validator( network, straight_path, excluded_nodes, - consider_reversed_edges=consider_reversed_edges + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops ) # ********************************************************************* @@ -675,11 +849,12 @@ class TestGisIdentify: assert len(straight_paths) == len(true_straight_paths) for straight_path in straight_paths: assert straight_path in true_straight_paths - self.path_validator( + self.straight_path_validator( network, straight_path, excluded_nodes, - consider_reversed_edges=consider_reversed_edges + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops ) # ********************************************************************* @@ -705,11 +880,12 @@ class TestGisIdentify: 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( + self.straight_path_validator( network, straight_path, excluded_nodes, - consider_reversed_edges=consider_reversed_edges + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops ) # ********************************************************************* @@ -729,11 +905,12 @@ class TestGisIdentify: 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( + self.straight_path_validator( network, straight_path, excluded_nodes, - consider_reversed_edges=consider_reversed_edges + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops ) # ********************************************************************* @@ -769,11 +946,12 @@ class TestGisIdentify: 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( + self.straight_path_validator( network, straight_path, excluded_nodes, - consider_reversed_edges=consider_reversed_edges + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops ) # ********************************************************************* @@ -793,11 +971,12 @@ class TestGisIdentify: 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( + self.straight_path_validator( network, straight_path, excluded_nodes, - consider_reversed_edges=consider_reversed_edges + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops ) # ********************************************************************* @@ -840,11 +1019,12 @@ class TestGisIdentify: assert len(straight_paths) == len(true_straight_paths) for straight_path in straight_paths: assert straight_path in true_straight_paths - self.path_validator( + self.straight_path_validator( network, straight_path, excluded_nodes, - consider_reversed_edges=consider_reversed_edges + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops ) # ********************************************************************* @@ -864,11 +1044,12 @@ class TestGisIdentify: assert len(straight_paths) == len(true_straight_paths) for straight_path in straight_paths: assert straight_path in true_straight_paths - self.path_validator( + self.straight_path_validator( network, straight_path, excluded_nodes, - consider_reversed_edges=consider_reversed_edges + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops ) # ********************************************************************* @@ -904,11 +1085,12 @@ class TestGisIdentify: assert len(straight_paths) == len(true_straight_paths) for straight_path in straight_paths: assert straight_path in true_straight_paths - self.path_validator( + self.straight_path_validator( network, straight_path, excluded_nodes, - consider_reversed_edges=consider_reversed_edges + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops ) # ********************************************************************* @@ -928,11 +1110,12 @@ class TestGisIdentify: assert len(straight_paths) == len(true_straight_paths) for straight_path in straight_paths: assert straight_path in true_straight_paths - self.path_validator( + self.straight_path_validator( network, straight_path, excluded_nodes, - consider_reversed_edges=consider_reversed_edges + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops ) # ********************************************************************* @@ -958,11 +1141,12 @@ class TestGisIdentify: 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( + self.straight_path_validator( network, straight_path, excluded_nodes, - consider_reversed_edges=consider_reversed_edges + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops ) # ********************************************************************* @@ -982,11 +1166,12 @@ class TestGisIdentify: 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( + self.straight_path_validator( network, straight_path, excluded_nodes, - consider_reversed_edges=consider_reversed_edges + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops ) # ********************************************************************* @@ -1022,11 +1207,12 @@ class TestGisIdentify: 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( + self.straight_path_validator( network, straight_path, excluded_nodes, - consider_reversed_edges=consider_reversed_edges + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops ) # ********************************************************************* @@ -1046,11 +1232,12 @@ class TestGisIdentify: 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( + self.straight_path_validator( network, straight_path, excluded_nodes, - consider_reversed_edges=consider_reversed_edges + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops ) # ********************************************************************* @@ -1093,11 +1280,12 @@ class TestGisIdentify: assert len(straight_paths) == len(true_straight_paths) for straight_path in straight_paths: assert straight_path in true_straight_paths - self.path_validator( + self.straight_path_validator( network, straight_path, excluded_nodes, - consider_reversed_edges=consider_reversed_edges + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops ) # ********************************************************************* @@ -1117,11 +1305,12 @@ class TestGisIdentify: 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( + self.straight_path_validator( network, straight_path, excluded_nodes, - consider_reversed_edges=consider_reversed_edges + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops ) # ********************************************************************* @@ -1141,11 +1330,12 @@ class TestGisIdentify: assert len(straight_paths) == len(true_straight_paths) for straight_path in straight_paths: assert straight_path in true_straight_paths - self.path_validator( + self.straight_path_validator( network, straight_path, excluded_nodes, - consider_reversed_edges=consider_reversed_edges + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops ) # ********************************************************************* @@ -1165,11 +1355,12 @@ class TestGisIdentify: assert len(straight_paths) == len(true_straight_paths) for straight_path in straight_paths: assert straight_path in true_straight_paths - self.path_validator( + self.straight_path_validator( network, straight_path, excluded_nodes, - consider_reversed_edges=consider_reversed_edges + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops ) # ********************************************************************* @@ -1189,11 +1380,12 @@ class TestGisIdentify: assert len(straight_paths) == len(true_straight_paths) for straight_path in straight_paths: assert straight_path in true_straight_paths - self.path_validator( + self.straight_path_validator( network, straight_path, excluded_nodes, - consider_reversed_edges=consider_reversed_edges + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops ) # ********************************************************************* @@ -1219,11 +1411,12 @@ class TestGisIdentify: 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( + self.straight_path_validator( network, straight_path, excluded_nodes, - consider_reversed_edges=consider_reversed_edges + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops ) # ********************************************************************* @@ -1240,14 +1433,15 @@ class TestGisIdentify: 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 + 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( + self.straight_path_validator( network, straight_path, excluded_nodes, - consider_reversed_edges=consider_reversed_edges + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops ) # ********************************************************************* @@ -1263,15 +1457,16 @@ class TestGisIdentify: consider_reversed_edges=consider_reversed_edges, ignore_self_loops=ignore_self_loops ) - true_straight_paths = [[1, 2, 0, 1], [1, 0, 2, 1]] + 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( + self.straight_path_validator( network, straight_path, excluded_nodes, - consider_reversed_edges=consider_reversed_edges + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops ) # ********************************************************************* @@ -1291,11 +1486,12 @@ class TestGisIdentify: 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( + self.straight_path_validator( network, straight_path, excluded_nodes, - consider_reversed_edges=consider_reversed_edges + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops ) # ********************************************************************* @@ -1315,11 +1511,12 @@ class TestGisIdentify: 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( + self.straight_path_validator( network, straight_path, excluded_nodes, - consider_reversed_edges=consider_reversed_edges + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops ) # ********************************************************************* @@ -1448,11 +1645,12 @@ class TestGisIdentify: 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( + self.straight_path_validator( network, straight_path, excluded_nodes, - consider_reversed_edges=consider_reversed_edges + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops ) # ********************************************************************* @@ -1472,11 +1670,12 @@ class TestGisIdentify: 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( + self.straight_path_validator( network, straight_path, excluded_nodes, - consider_reversed_edges=consider_reversed_edges + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops ) # ********************************************************************* @@ -1512,11 +1711,12 @@ class TestGisIdentify: 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( + self.straight_path_validator( network, straight_path, excluded_nodes, - consider_reversed_edges=consider_reversed_edges + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops ) # ********************************************************************* @@ -1536,11 +1736,12 @@ class TestGisIdentify: 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( + self.straight_path_validator( network, straight_path, excluded_nodes, - consider_reversed_edges=consider_reversed_edges + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops ) # ********************************************************************* @@ -1669,11 +1870,12 @@ class TestGisIdentify: 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( + self.straight_path_validator( network, straight_path, excluded_nodes, - consider_reversed_edges=consider_reversed_edges + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops ) # ********************************************************************* @@ -1693,11 +1895,12 @@ class TestGisIdentify: 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( + self.straight_path_validator( network, straight_path, excluded_nodes, - consider_reversed_edges=consider_reversed_edges + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops ) # ********************************************************************* @@ -1733,11 +1936,12 @@ class TestGisIdentify: 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( + self.straight_path_validator( network, straight_path, excluded_nodes, - consider_reversed_edges=consider_reversed_edges + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops ) # ********************************************************************* @@ -1757,11 +1961,12 @@ class TestGisIdentify: 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( + self.straight_path_validator( network, straight_path, excluded_nodes, - consider_reversed_edges=consider_reversed_edges + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops ) # ********************************************************************* @@ -1804,11 +2009,12 @@ class TestGisIdentify: assert len(straight_paths) == len(true_straight_paths) for straight_path in straight_paths: assert straight_path in true_straight_paths - self.path_validator( + self.straight_path_validator( network, straight_path, excluded_nodes, - consider_reversed_edges=consider_reversed_edges + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops ) # ********************************************************************* @@ -1834,11 +2040,12 @@ class TestGisIdentify: 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( + self.straight_path_validator( network, straight_path, excluded_nodes, - consider_reversed_edges=consider_reversed_edges + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops ) # ********************************************************************* @@ -1859,11 +2066,12 @@ class TestGisIdentify: assert len(straight_paths) == len(true_straight_paths) for straight_path in straight_paths: assert straight_path in true_straight_paths - self.path_validator( + self.straight_path_validator( network, straight_path, excluded_nodes, - consider_reversed_edges=consider_reversed_edges + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops ) # ********************************************************************* @@ -1906,11 +2114,12 @@ class TestGisIdentify: assert len(straight_paths) == len(true_straight_paths) for straight_path in straight_paths: assert straight_path in true_straight_paths - self.path_validator( + self.straight_path_validator( network, straight_path, excluded_nodes, - consider_reversed_edges=consider_reversed_edges + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops ) # ********************************************************************* @@ -1938,11 +2147,12 @@ class TestGisIdentify: 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( + self.straight_path_validator( network, straight_path, excluded_nodes, - consider_reversed_edges=consider_reversed_edges + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops ) # ********************************************************************* @@ -4246,51 +4456,31 @@ class TestGisIdentify: # ********************************************************************* # create a network - network = nx.MultiDiGraph() - network.graph['crs'] = "EPSG:4326" - # network.graph['crs'] = 'init' - # add edges and nodes - number_edges = random.randint(3,10) - edge_keys = [ (random.randint(0,number_edges),random.randint(0,number_edges)) for edge_index in range(number_edges)] - network.add_edges_from(edge_keys) - # add attributes to the nodes used in the edges - for node_key in network.nodes(): - _xy = (random.random(), random.random()) - - network.add_node(node_key, x=_xy[0], y=_xy[0]) - + network.add_node(node_key, x=_xy[0], y=_xy[0]) # add new (unconnected) nodes - number_new_nodes = random.randint(3,5) - unconnected_node_keys = [] - for node_index in range(number_new_nodes): - new_node_key = uuid.uuid4() - _xy = (random.random(), random.random()) - network.add_node(new_node_key, x=_xy[0], y=_xy[0]) - unconnected_node_keys.append(new_node_key) # ********************************************************************* # find the nearest nodes using the osmnx method - nearest_node_keys = gis_iden.nearest_nodes( network, [network.nodes[node_key]['x'] @@ -4300,60 +4490,40 @@ class TestGisIdentify: ) # assert that the test is meaningful - assert len(nearest_node_keys) != 0 - assert len(nearest_node_keys) == len(unconnected_node_keys) # assert that the nodes are the same - for i, node_key in enumerate(unconnected_node_keys): - assert node_key == nearest_node_keys[i] # ********************************************************************* # find the nodes nearest to select nodes excluding themselves - nearest_node_keys = gis_iden.nearest_nodes_other_than_themselves( network, unconnected_node_keys) # assert that the test is meaningful - assert len(nearest_node_keys) != 0 - assert len(nearest_node_keys) == len(unconnected_node_keys) - all_node_keys = list(network.nodes()) - list_all_geos = [] - for node_key in all_node_keys: - list_all_geos.append(Point( - (network.nodes[node_key]['x'], - network.nodes[node_key]['y']) + (network.nodes[node_key]['x'],network.nodes[node_key]['y']) ) ) - all_node_geos = { node_key: list_all_geos[i] for i, node_key in enumerate(all_node_keys) } - # for each node - for i, node_key in enumerate(unconnected_node_keys): - # assert that they are not the same - assert node_key != nearest_node_keys[i] - # verify that the distance between is the lowest among all - unconnected_node_geo = all_node_geos[node_key] - all_distances = [ unconnected_node_geo.distance( all_node_geos[other_node_key] @@ -4361,11 +4531,9 @@ class TestGisIdentify: for other_node_key in all_node_keys if other_node_key != node_key ] - actual_distance = unconnected_node_geo.distance( all_node_geos[nearest_node_keys[i]] ) - assert isclose( min(all_distances), actual_distance, @@ -4410,7 +4578,9 @@ class TestGisIdentify: network = ox.graph_from_point( (55.71654,9.11728), network_type='drive', - custom_filter='["highway"~"residential|tertiary|unclassified|service"]', + custom_filter=( + '["highway"~"residential|tertiary|unclassified|service"]' + ), truncate_by_edge=True ) # find edges in reverse diff --git a/tests/test_gis_modify.py b/tests/test_gis_modify.py index 4bbd419..a6b7181 100644 --- a/tests/test_gis_modify.py +++ b/tests/test_gis_modify.py @@ -151,12 +151,28 @@ class TestGisModify: custom_filter='["highway"~"residential|tertiary|unclassified|service"]', truncate_by_edge=True ) + + # define the settings + ignore_self_loops = False + consider_reversed_edges = True + # find paths paths = gis_iden.find_simplifiable_paths( _net, excluded_nodes=[], - ignore_self_loops=False, - consider_reversed_edges=True) + ignore_self_loops=ignore_self_loops, + consider_reversed_edges=consider_reversed_edges + ) + + # verify the paths + for path in paths: + gis_iden.is_path_straight( + _net, + path, + consider_reversed_edges=consider_reversed_edges, + ignore_self_loops=ignore_self_loops + ) + # modify an edge in one of the paths to have list attributes _edge_key = tuple(gis_iden.get_edges_from_a_to_b( _net, @@ -1154,7 +1170,7 @@ class TestGisModify: node_keys=node_keys, all_paths_ab=all_paths_ab, original_path_lengths_ab=original_path_lengths_ab, - abs_tol=0.294 # 0.29327665321937957 + abs_tol=0.925 # 0.29327665321937957, 0.9249539991553775 ) # there should be at least one extra edge per node @@ -1304,6 +1320,15 @@ class TestGisModify: assert nx.has_path(network, edge_key[0], node_key) # length from beginning to end must be roughly the same for new_path in new_paths_ab[edge_key]: + # exclude new paths with self-loops + no_self_loops = True + for edge_in_path in new_path: + if edge_in_path[0] == edge_in_path[1]: + no_self_loops = False + break + if not no_self_loops: + # there are self loops, skip path + continue if new_path in all_paths_ab[edge_key]: # old path: it must have the same length (the edge is unchanged) assert isclose( @@ -3190,5 +3215,63 @@ class TestGisModify: error_raised = True assert error_raised + # ************************************************************************* + # ************************************************************************* + + def test_modify_roundabouts_unprojected(self): + + # network should be a OSM-nx formatted graph + network = ox.graph_from_point( + (55.71654,9.11728), + network_type='drive', + custom_filter='["highway"~"residential|tertiary|unclassified|service"]', + truncate_by_edge=True + ) + # find all roundabouts + roundabouts = gis_iden.find_roundabouts(network) + # confirm they are roundabouts + for roundabout in roundabouts: + assert gis_iden.is_roundabout(network, roundabout) + # insert fake roundabout + roundabouts.append([0, 1, 2]) + # modify the roundabouts + node_replacements = gis_mod.transform_roundabouts_into_crossroads( + network, + roundabouts + ) + # make sure the fake roundabout was detected + assert type(node_replacements[-1]) == type(None) + # TODO: test the conversion itself + + # ************************************************************************* + # ************************************************************************* + + def test_modify_roundabouts_projected(self): + + # network should be a OSM-nx formatted graph + network = ox.graph_from_point( + (55.71654,9.11728), + network_type='drive', + custom_filter='["highway"~"residential|tertiary|unclassified|service"]', + truncate_by_edge=True + ) + # project the network + network = ox.project_graph(G=network) + # find all roundabouts + roundabouts = gis_iden.find_roundabouts(network) + # confirm they are roundabouts + for roundabout in roundabouts: + assert gis_iden.is_roundabout(network, roundabout) + # insert fake roundabout + roundabouts.append([0, 1, 2]) + # modify the roundabouts + node_replacements = gis_mod.transform_roundabouts_into_crossroads( + network, + roundabouts + ) + # make sure the fake roundabout was detected + assert type(node_replacements[-1]) == type(None) + # TODO: test the conversion itself + # ***************************************************************************** # ***************************************************************************** \ No newline at end of file diff --git a/tests/test_gis_utils.py b/tests/test_gis_utils.py index f67148a..19c6040 100644 --- a/tests/test_gis_utils.py +++ b/tests/test_gis_utils.py @@ -2114,6 +2114,33 @@ class TestGisUtils: # ************************************************************************* # ************************************************************************* + + def test_simplifying_graph(self): + + # get a network + network = ox.graph_from_point( + (55.71654,9.11728), + network_type='drive', + custom_filter='["highway"~"residential|tertiary|unclassified|service"]', + truncate_by_edge=True + ) + # protect some nodes + number_nodes_protected = 4 + node_keys = tuple(network.nodes()) + protected_nodes = [ + node_keys[random.randint(0,len(node_keys)-1)] + for i in range(number_nodes_protected) + ] + # try simplifying it + gis_utils.simplify_network(network, protected_nodes) + # TODO: verify the changes + # confirm that the protected nodes still exist and have the same attr. + # for node_key in protected_nodes: + # assert network.has_node(node_key) + # TODO: check if [335762579, 335762585, 1785975921, 360252989, 335762632, 335762579] is a path + + # ************************************************************************* + # ************************************************************************* # ***************************************************************************** # ***************************************************************************** \ No newline at end of file -- GitLab