Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
geographika committed Apr 30, 2024
2 parents cea0e51 + e0ca6bd commit f1a3bcb
Show file tree
Hide file tree
Showing 6 changed files with 45 additions and 34 deletions.
4 changes: 2 additions & 2 deletions requirements.demo.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ Jinja2
matplotlib
geopandas==0.14.3
Fiona==1.9.6
shapely==2.0.3
shapely==2.0.4
osmnx==1.9.2
uvicorn[standard]==0.29.0
fastapi==0.110.1
fastapi==0.110.2
# following required for isochrones
scipy==1.13.0
odbcutils==0.2.1
18 changes: 12 additions & 6 deletions tests/test_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,15 @@ def test_get_shortest_edge_mixed_keys():
assert res[0] == "A"


def test_get_shortest_edge_no_length():
edges = {
"A": {"EDGE_ID": "A", "LEN_": 100, "NODEID_FROM": 1, "NODEID_TO": 2},
"B": {"EDGE_ID": "B", "LEN_": 20, "NODEID_FROM": 2, "NODEID_TO": 3},
}
res = functions.get_shortest_edge(edges, length_field=None)
assert res[0] == "A"


def test_get_unique_ordered_list():
lst = [2, 2, 1, 1, 4, 4, 1, 2, 3]
res = functions.get_unique_ordered_list(lst)
Expand Down Expand Up @@ -470,7 +479,6 @@ def test_has_no_overlaps_loop_with_split():


def test_add_edge():

net = networkx.MultiGraph()
edge = Edge(0, 1, "A", {"EDGE_ID": 1, "OFFSET": 5, "LEN_": 10})
functions.add_edge(net, edge.start_node, edge.end_node, edge.key, edge.attributes)
Expand All @@ -495,7 +503,6 @@ def test_add_single_edge():


def test_remove_edge():

net = networkx.MultiGraph()
edge = Edge(0, 1, "A", {"EDGE_ID": 1, "OFFSET": 5, "LEN_": 10})
net.add_edge(edge.start_node, edge.end_node, edge.key, **edge.attributes)
Expand All @@ -505,7 +512,6 @@ def test_remove_edge():


def test_remove_edge_and_key():

net = loader.create_graph()
edge = Edge(0, 1, "A", {"EDGE_ID": 1, "OFFSET": 5, "LEN_": 10})
functions.add_edge(net, **edge._asdict())
Expand All @@ -517,7 +523,6 @@ def test_remove_edge_and_key():


def test_remove_edge_by_key():

net = loader.create_graph()
edge = Edge(0, 1, "A", {"EDGE_ID": 1, "OFFSET": 5, "LEN_": 10})
functions.add_edge(net, **edge._asdict())
Expand Down Expand Up @@ -561,6 +566,7 @@ def test_doctest():
# test_remove_edge()
# test_remove_edge_and_key()
# test_remove_edge_by_key()
test_add_edge_shorthand()
test_add_single_edge()
# test_add_edge_shorthand()
# test_add_single_edge()
test_get_shortest_edge_no_length()
print("Done!")
22 changes: 10 additions & 12 deletions tests/test_routing.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,18 @@


def test_solve_all_simple_paths():

net = networks.simple_network()
simple_paths = routing.solve_all_simple_paths(net, 1, 5)
assert list(simple_paths) == [[1, 2, 3, 4, 5]]


def test_solve_all_simple_paths_no_node():

net = networks.simple_network()
with pytest.raises(NodeNotFound):
routing.solve_all_simple_paths(net, 1, 99)
list(routing.solve_all_simple_paths(net, 1, 99))


def test_solve_all_simple_paths_cutoff():

net = networks.simple_network()
simple_paths = routing.solve_all_simple_paths(net, 1, 5, cutoff=3)
assert list(simple_paths) == []
Expand All @@ -36,15 +33,13 @@ def test_solve_all_simple_paths_cutoff():


def test_solve_all_shortest_paths():

net = networks.circle_network()
all_paths = routing.solve_all_shortest_paths(net, 1, 3)
# print(list(all_paths))
assert list(all_paths) == [[1, 2, 3], [1, 4, 3]]


def test_solve_all_shortest_paths_no_node():

net = networks.circle_network()
# it seems NetworkXNoPath is raised rather than NodeNotFound
# when using all_shortest_paths in networkx
Expand All @@ -54,31 +49,34 @@ def test_solve_all_shortest_paths_no_node():


def test_get_path_ends():

edges = [Edge(0, 1, 1, {}), Edge(1, 2, 1, {})]
ends = routing.get_path_ends(edges)
# print(ends)
assert ends == (0, 2)


def test_get_path_ends_single_edge():

edges = [Edge(0, 1, 1, {})]
ends = routing.get_path_ends(edges)
# print(ends)
assert ends == (0, 1)


def test_solve_shortest_path():

net = networks.simple_network()
edges = routing.solve_shortest_path(net, start_node=1, end_node=5)
edge_ids = [edge.key for edge in edges]
assert edge_ids == [1, 2, 3, 4]


def test_solve_shortest_path_directions():
def test_solve_shortest_path_no_weight():
net = networks.simple_network()
edges = routing.solve_shortest_path(net, start_node=1, end_node=5, weight=None)
edge_ids = [edge.key for edge in edges]
assert edge_ids == [1, 2, 3, 4]


def test_solve_shortest_path_directions():
net = networks.simple_network()
edges = routing.solve_shortest_path(net, start_node=1, end_node=5)

Expand All @@ -92,7 +90,6 @@ def test_solve_shortest_path_directions():


def test_solve_shortest_path_split_network():

net = networks.simple_network()

splitter.split_network_edge(net, 3, [2, 8])
Expand Down Expand Up @@ -129,5 +126,6 @@ def test_doctest():
# test_get_path_ends()
# test_get_path_ends_single_edge()
# test_solve_shortest_path_directions()
test_solve_shortest_path_split_network()
# test_solve_shortest_path_split_network()
test_solve_shortest_path_no_weight()
print("Done!")
2 changes: 1 addition & 1 deletion wayfarer/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from typing import NamedTuple


__version__ = "0.10.0"
__version__ = "0.11.0"

LENGTH_FIELD = "LEN_"
EDGE_ID_FIELD = "EDGE_ID"
Expand Down
20 changes: 12 additions & 8 deletions wayfarer/functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -343,8 +343,8 @@ def get_all_paths_from_nodes(net, node_list, with_direction_flag=False):
return itertools.chain(*all_paths)


def get_path_length(path_edges):
return sum([edge.attributes[LENGTH_FIELD] for edge in path_edges])
def get_path_length(path_edges, length_field: str = LENGTH_FIELD):
return sum([edge.attributes[length_field] for edge in path_edges])


def get_edges_from_node_pair(
Expand Down Expand Up @@ -380,7 +380,7 @@ def get_edges_from_nodes(
net: networkx.MultiGraph | networkx.MultiDiGraph,
node_list: list[int | str],
with_direction_flag: bool = False,
length_field: str = LENGTH_FIELD,
length_field: str | None = LENGTH_FIELD,
return_unique: bool = True,
shortest_path_only: bool = True,
) -> list[Edge]:
Expand Down Expand Up @@ -427,7 +427,7 @@ def get_edges_from_nodes(
return edge_list


def get_shortest_edge(edges: dict, length_field=LENGTH_FIELD):
def get_shortest_edge(edges: dict, length_field: str | None = LENGTH_FIELD):
"""
From a dictionary of edges, get the shortest edge
by its length
Expand All @@ -438,10 +438,14 @@ def get_shortest_edge(edges: dict, length_field=LENGTH_FIELD):
if not edges:
raise ValueError("The edge list is empty")

min_key = min(
edges, key=lambda k: edges[k][length_field]
) # py3 can add default=None
return min_key, edges[min_key]
if length_field is None:
# simply return the first edge
return next(iter(edges)), edges[next(iter(edges))]
else:
min_key = min(
edges, key=lambda k: edges[k][length_field]
) # py3 can add default=None
return min_key, edges[min_key]


def add_direction_flag(start_node, end_node, attributes, **kwargs):
Expand Down
13 changes: 8 additions & 5 deletions wayfarer/routing.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,12 @@ def solve_shortest_path(
start_node: str | int,
end_node: str | int,
with_direction_flag: bool = True,
weight: str = LENGTH_FIELD,
weight: str | None = LENGTH_FIELD,
):
"""
Solve the shortest path between two nodes, returning a list of Edge objects
Solve the shortest path between two nodes, returning a list of Edge objects.
Set weight to the attribute name for deciding the shortest path, or to None
to ignore any weightings (faster).
"""
nodes = solve_shortest_path_from_nodes(net, [start_node, end_node], weight)
return functions.get_edges_from_nodes(
Expand All @@ -32,16 +34,16 @@ def solve_shortest_path(
def solve_shortest_path_from_nodes(
net: networkx.MultiGraph | networkx.MultiDiGraph,
node_list: list[int | str],
weight: str = LENGTH_FIELD,
weight: str | None = LENGTH_FIELD,
) -> list[int | str]:
"""
Return a list of nodes found by solving from each node in node_list to
the next
the next. Set weight to the attribute name for deciding the shortest path, or to None
to ignore any weightings (faster).
"""
nodes_in_path = []

for start_node, end_node in functions.pairwise(node_list):

log.debug("Solving from %s to %s", start_node, end_node)

if start_node == end_node:
Expand Down Expand Up @@ -361,6 +363,7 @@ def solve_all_shortest_paths(
>>> pths = solve_all_shortest_paths(net, 0, 2)
>>> print(list(pths))
[[0, 1, 2]]
# noqa: E501
"""

Expand Down

0 comments on commit f1a3bcb

Please sign in to comment.