Skip to content

Commit e965a45

Browse files
committed
4.24.0b2
1 parent 428c158 commit e965a45

File tree

5 files changed

+76
-65
lines changed

5 files changed

+76
-65
lines changed

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "cityseer"
3-
version = '4.24.0b1'
3+
version = '4.24.0b2'
44
description = "Computational tools for network-based pedestrian-scale urban analysis"
55
readme = "README.md"
66
requires-python = ">=3.10, <3.14" # pending fiona support for 3.14

pysrc/cityseer/tools/graphs.py

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,19 @@ def _node_has_z(nd_data: NodeData) -> bool:
3737
def _make_geom_between(nd_data_a: NodeData, nd_data_b: NodeData) -> geometry.LineString:
3838
"""Create a LineString between two nodes, using 3D coords only if both nodes have z."""
3939
if _node_has_z(nd_data_a) and _node_has_z(nd_data_b):
40-
return geometry.LineString([
41-
(nd_data_a["x"], nd_data_a["y"], nd_data_a["z"]),
42-
(nd_data_b["x"], nd_data_b["y"], nd_data_b["z"]),
43-
])
44-
return geometry.LineString([
45-
(nd_data_a["x"], nd_data_a["y"]),
46-
(nd_data_b["x"], nd_data_b["y"]),
47-
])
40+
return geometry.LineString(
41+
[
42+
(nd_data_a["x"], nd_data_a["y"], nd_data_a["z"]),
43+
(nd_data_b["x"], nd_data_b["y"], nd_data_b["z"]),
44+
]
45+
)
46+
return geometry.LineString(
47+
[
48+
(nd_data_a["x"], nd_data_a["y"]),
49+
(nd_data_b["x"], nd_data_b["y"]),
50+
]
51+
)
52+
4853

4954
logging.basicConfig(level=logging.INFO)
5055
logger = logging.getLogger(__name__)

pysrc/cityseer/tools/io.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,7 @@ def nx_epsg_conversion(
101101
line_geom: geometry.LineString = edge_data["geom"]
102102
# convert, preserving z if present
103103
if line_geom.has_z:
104-
edge_coords: ListCoordsType = [
105-
(*transformer.transform(c[0], c[1]), c[2]) for c in line_geom.coords
106-
]
104+
edge_coords: ListCoordsType = [(*transformer.transform(c[0], c[1]), c[2]) for c in line_geom.coords]
107105
else:
108106
edge_coords = [transformer.transform(x, y) for x, y in line_geom.coords]
109107
# snap ends
@@ -1269,7 +1267,11 @@ def network_structure_from_gpd(
12691267
# process nodes and build mapping
12701268
for nd_key, node_data in tqdm(nodes_gdf.iterrows(), total=len(nodes_gdf), disable=config.QUIET_MODE):
12711269
node_z = None
1272-
if "z" in node_data and node_data["z"] is not None and not (isinstance(node_data["z"], float) and np.isnan(node_data["z"])):
1270+
if (
1271+
"z" in node_data
1272+
and node_data["z"] is not None
1273+
and not (isinstance(node_data["z"], float) and np.isnan(node_data["z"]))
1274+
):
12731275
node_z = float(node_data["z"])
12741276
ns_node_idx = network_structure.add_street_node(
12751277
str(nd_key),
@@ -1466,7 +1468,11 @@ def nx_from_cityseer_geopandas(
14661468
live = nd_data.live if hasattr(nd_data, "live") else True
14671469
weight = nd_data.weight if hasattr(nd_data, "weight") else 1
14681470
node_kwargs: dict[str, Any] = {"x": nd_data.x, "y": nd_data.y, "live": live, "weight": weight}
1469-
if hasattr(nd_data, "z") and nd_data.z is not None and not (isinstance(nd_data.z, float) and np.isnan(nd_data.z)):
1471+
if (
1472+
hasattr(nd_data, "z")
1473+
and nd_data.z is not None
1474+
and not (isinstance(nd_data.z, float) and np.isnan(nd_data.z))
1475+
):
14701476
node_kwargs["z"] = nd_data.z
14711477
g_multi_copy.add_node(str(nd_key), **node_kwargs)
14721478
logger.info("Unpacking edge data.")

tests/rustalgos/test_centrality.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -759,7 +759,9 @@ def test_simplest_betweenness_invariant_to_node_order():
759759
betw_by_edge = {}
760760
for node_pos, node_key in enumerate(nodes_gdf.index):
761761
row = nodes_gdf.loc[node_key]
762-
edge_label = tuple(sorted((idx_to_label[row["primal_edge_node_a"]], idx_to_label[row["primal_edge_node_b"]])))
762+
edge_label = tuple(
763+
sorted((idx_to_label[row["primal_edge_node_a"]], idx_to_label[row["primal_edge_node_b"]]))
764+
)
763765
betw_by_edge[edge_label] = res.node_betweenness[500][node_pos]
764766
results.append(betw_by_edge)
765767
# All orderings must agree
@@ -826,7 +828,9 @@ def test_betweenness_mixed_live_non_live_invariant_to_node_order():
826828
simplest_by_edge = {}
827829
for node_pos, node_key in enumerate(nodes_gdf_dual.index):
828830
row = nodes_gdf_dual.loc[node_key]
829-
edge_label = tuple(sorted((idx_to_label[row["primal_edge_node_a"]], idx_to_label[row["primal_edge_node_b"]])))
831+
edge_label = tuple(
832+
sorted((idx_to_label[row["primal_edge_node_a"]], idx_to_label[row["primal_edge_node_b"]]))
833+
)
830834
simplest_by_edge[edge_label] = res_simplest.node_betweenness[1000][node_pos]
831835
simplest_results.append(simplest_by_edge)
832836

@@ -910,12 +914,8 @@ def test_shortest_brandes_tolerance_clears_stale_predecessors():
910914
pbar_disabled=True,
911915
)
912916

913-
exact = {
914-
node_key: res_exact.node_betweenness[distance][idx] for idx, node_key in enumerate(nodes_gdf.index)
915-
}
916-
tolerant = {
917-
node_key: res_tolerant.node_betweenness[distance][idx] for idx, node_key in enumerate(nodes_gdf.index)
918-
}
917+
exact = {node_key: res_exact.node_betweenness[distance][idx] for idx, node_key in enumerate(nodes_gdf.index)}
918+
tolerant = {node_key: res_tolerant.node_betweenness[distance][idx] for idx, node_key in enumerate(nodes_gdf.index)}
919919

920920
assert np.isclose(exact["A"], 0.0, atol=config.ATOL)
921921
assert np.isclose(exact["B"], 0.0, atol=config.ATOL)

tests/rustalgos/test_data.py

Lines changed: 43 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -456,50 +456,50 @@ def test_aggregate_to_src_idx(primal_graph, dual_graph):
456456
data_map = mock.mock_data_map(data_gdf, dedupe_key_col=dedupe_key_col)
457457
data_map.assign_data_to_network(network_structure_d, 400, n_nearest_candidates=6)
458458
for netw_src_idx in network_structure_d.node_indices():
459-
# aggregate to src...
460-
reachable_entries = data_map.aggregate_to_src_idx(
461-
netw_src_idx, network_structure_d, int(max_seconds), config.SPEED_M_S, angular=True
462-
)
463-
# compare to manual checks on distances:
464-
# get the network distances
465-
_nodes, tree_map = network_structure_d.dijkstra_tree_simplest(
466-
netw_src_idx, int(max_seconds), config.SPEED_M_S
467-
)
468-
# check that reachable entries and respective distances are correct
469-
# should match against the network structure plus data_map.node_data_map distances
470-
manual_reachable = {}
471-
manual_not_reachable = {}
472-
for node_idx, node_visit in enumerate(tree_map): # type: ignore
473-
if np.isfinite(node_visit.agg_seconds):
474-
if node_idx not in data_map.node_data_map:
475-
continue
476-
for assigned_data_idx, assigned_data_dist in data_map.node_data_map[node_idx]:
477-
dist = node_visit.agg_seconds * config.SPEED_M_S + assigned_data_dist
478-
if dist <= max_dist:
479-
manual_reachable[assigned_data_idx] = dist
480-
else:
481-
manual_not_reachable[assigned_data_idx] = dist
482-
# all reachable entries should be in manual reachable and distances should be the same
483-
for reachable_key, reachable_dist in reachable_entries.items():
484-
assert reachable_key in manual_reachable
485-
assert reachable_dist - manual_reachable[reachable_key] < config.ATOL
486-
# manual reachable shouldn't contain keys not in reachable entries
487-
# allow 1m tolerance for floating point errors
488-
# handle shadowed dupe id nodes in deduplication case
489-
shadowed_dupes = ["int:45", "int:46", "int:47", "int:48"]
490-
for reachable_key in manual_reachable:
491-
if deduplicate is True and reachable_key in shadowed_dupes:
492-
# if shadowed by closest dedupe node, then it should not be reachable
493-
# but if within max dist, then "int:49" should be reachable
494-
assert "int:49" in manual_reachable
495-
assert "int:49" in reachable_entries
496-
assert reachable_key not in reachable_entries
459+
# aggregate to src...
460+
reachable_entries = data_map.aggregate_to_src_idx(
461+
netw_src_idx, network_structure_d, int(max_seconds), config.SPEED_M_S, angular=True
462+
)
463+
# compare to manual checks on distances:
464+
# get the network distances
465+
_nodes, tree_map = network_structure_d.dijkstra_tree_simplest(
466+
netw_src_idx, int(max_seconds), config.SPEED_M_S
467+
)
468+
# check that reachable entries and respective distances are correct
469+
# should match against the network structure plus data_map.node_data_map distances
470+
manual_reachable = {}
471+
manual_not_reachable = {}
472+
for node_idx, node_visit in enumerate(tree_map): # type: ignore
473+
if np.isfinite(node_visit.agg_seconds):
474+
if node_idx not in data_map.node_data_map:
497475
continue
498-
try:
499-
assert reachable_key in reachable_entries
500-
except AssertionError:
501-
# If the key is not found, assert the distance condition
502-
assert max_dist - manual_reachable[reachable_key] < 1
476+
for assigned_data_idx, assigned_data_dist in data_map.node_data_map[node_idx]:
477+
dist = node_visit.agg_seconds * config.SPEED_M_S + assigned_data_dist
478+
if dist <= max_dist:
479+
manual_reachable[assigned_data_idx] = dist
480+
else:
481+
manual_not_reachable[assigned_data_idx] = dist
482+
# all reachable entries should be in manual reachable and distances should be the same
483+
for reachable_key, reachable_dist in reachable_entries.items():
484+
assert reachable_key in manual_reachable
485+
assert reachable_dist - manual_reachable[reachable_key] < config.ATOL
486+
# manual reachable shouldn't contain keys not in reachable entries
487+
# allow 1m tolerance for floating point errors
488+
# handle shadowed dupe id nodes in deduplication case
489+
shadowed_dupes = ["int:45", "int:46", "int:47", "int:48"]
490+
for reachable_key in manual_reachable:
491+
if deduplicate is True and reachable_key in shadowed_dupes:
492+
# if shadowed by closest dedupe node, then it should not be reachable
493+
# but if within max dist, then "int:49" should be reachable
494+
assert "int:49" in manual_reachable
495+
assert "int:49" in reachable_entries
496+
assert reachable_key not in reachable_entries
497+
continue
498+
try:
499+
assert reachable_key in reachable_entries
500+
except AssertionError:
501+
# If the key is not found, assert the distance condition
502+
assert max_dist - manual_reachable[reachable_key] < 1
503503

504504

505505
def test_accessibility(primal_graph, dual_graph):

0 commit comments

Comments
 (0)