Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion LinkBikeNet_MVP.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@
"closest_pairs = []\n",
"for i in range(to_iterate):\n",
" wcc = [H.subgraph(c).copy() for c in sorted(nx.connected_components(H), key=lambda c: sum([l[-1] for l in H.subgraph(c).copy().edges.data('length')]), reverse=True)]\n",
" pair = pair_between_nearest_components(wcc)\n",
" pair = pair_between_closest_components(wcc)\n",
" closest_pairs.append(pair)\n",
" H.add_edge(pair[0], pair[1], length=0)"
],
Expand Down
49 changes: 47 additions & 2 deletions linkbikenet/functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ def pair_between_largest_components(wcc):

return closest_pair

def pair_between_nearest_components(wcc):
def pair_between_largest_and_closest_components(wcc):
"""
Find the pair of nodes connecting the largest component to the
geographically nearest remaining component.
Expand All @@ -161,7 +161,6 @@ def pair_between_nearest_components(wcc):
closest_pair : tuple
The two nodes that should be connected
"""

largest = wcc[0]

# Build KD-tree for the largest component
Expand Down Expand Up @@ -191,6 +190,52 @@ def pair_between_nearest_components(wcc):
)
return closest_pair

def pair_between_closest_components(wcc):
"""
Find the closest pair of nodes belonging to two different connected
components.

Parameters
----------
wcc : list of nx.Graph
Connected components sorted with the largest first.

Returns
-------
closest_pair : tuple
The two nodes that should be connected
"""
closest_pair = None
best_distance = np.inf

for i in range(len(wcc) - 1):
G1 = wcc[i]
nodes1 = list(G1.nodes())
coords1 = np.array([
(G1.nodes[n]["x"], G1.nodes[n]["y"])
for n in nodes1
])
tree = cKDTree(coords1)

for j in range(i + 1, len(wcc)):
G2 = wcc[j]
nodes2 = list(G2.nodes())
coords2 = np.array([
(G2.nodes[n]["x"], G2.nodes[n]["y"])
for n in nodes2
])

distances, indices = tree.query(coords2)
k = np.argmin(distances)
if distances[k] < best_distance:
best_distance = distances[k]
closest_pair = (
nodes1[indices[k]],
nodes2[k]
)

return closest_pair

def get_correct_edgetuples(edge_gdf, nodelist):
"""
helper function that maps a node list (output of nx.shortest_paths)
Expand Down
16 changes: 12 additions & 4 deletions linkbikenet/linkbikenet.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def linkbikenet(
city_name : str
name of the city that the analysis should be performed on
connection_strategy : str, default="largest
strategy to use for connecting between components. Default is "largest", second option is "closest"
strategy to use for connecting between components. Default is "largest", other options are "largest-closest" and "closest"
proj_crs : str, default '3857'
coordinate reference system that is used to project osm data. Default is '3857' (WGS 84 / Pseudo-Mercator)
export_data : bool, optional, default True
Expand All @@ -37,8 +37,8 @@ def linkbikenet(
raise TypeError("city_name must be a string")
if type(proj_crs) != str:
raise TypeError("proj_crs must be a string")
if connection_strategy != "largest" and connection_strategy != "closest":
raise TypeError("connection_strategy must be 'largest' or 'closest'")
if connection_strategy != "largest" and connection_strategy != "largest-closest" and connection_strategy != "closest":
raise TypeError("connection_strategy must be 'largest', 'largest-closest' or 'closest'")
if type(export_data) is not bool:
raise TypeError("export_data must be a boolean")
if export_file_format != "geojson" and export_file_format != "gpkg":
Expand Down Expand Up @@ -99,11 +99,19 @@ def linkbikenet(
closest_pairs.append(pair)
H.add_edge(pair[0], pair[1], length=0)

elif connection_strategy == "largest-closest":
for i in range(to_iterate):
wcc = [H.subgraph(c).copy() for c in sorted(nx.connected_components(H), key=lambda c: sum(
[l[-1] for l in H.subgraph(c).copy().edges.data('length')]), reverse=True)]
pair = pair_between_largest_and_closest_components(wcc)
closest_pairs.append(pair)
H.add_edge(pair[0], pair[1], length=0)

elif connection_strategy == "closest":
for i in range(to_iterate):
wcc = [H.subgraph(c).copy() for c in sorted(nx.connected_components(H), key=lambda c: sum(
[l[-1] for l in H.subgraph(c).copy().edges.data('length')]), reverse=True)]
pair = pair_between_nearest_components(wcc)
pair = pair_between_closest_components(wcc)
closest_pairs.append(pair)
H.add_edge(pair[0], pair[1], length=0)

Expand Down
19 changes: 18 additions & 1 deletion tests/test_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,21 @@ def create_validation_pair_nearest():
return pair

def test_pair_between_nearest_components(create_test_components_nearest, create_validation_pair_nearest):
assert pair_between_nearest_components(create_test_components_nearest) == create_validation_pair_nearest
assert pair_between_largest_and_closest_components(create_test_components_nearest) == create_validation_pair_nearest

@pytest.fixture
def create_test_components_closest():
G = nx.Graph()
G.add_nodes_from([(1, {"x": 1, "y": 1}), (2, {"x": 2, "y": 2}), (3, {"x": 1, "y": 2}), (4, {"x": 30, "y": 30}), (5, {"x": 31, "y": 31}), (6, {"x": 10, "y": 10})])
G.add_edges_from([(1, 2, {'length': 5}), (2, 3, {'length': 2}), (1, 3, {'length': 7}), (4, 5, {'length': 1})])
wcc = [G.subgraph(c).copy() for c in sorted(nx.connected_components(G), key=lambda c: sum(
[l[-1] for l in G.subgraph(c).copy().edges.data('length')]), reverse=True)]
return wcc

@pytest.fixture
def create_validation_pair_closest():
pair = 2,6
return pair

def test_pair_between_closest_components(create_test_components_closest, create_validation_pair_closest):
assert pair_between_closest_components(create_test_components_closest) == create_validation_pair_closest
Loading