Skip to content

Commit

Permalink
improves performance of network_structure_from_gpd
Browse files Browse the repository at this point in the history
  • Loading branch information
songololo committed Jan 10, 2025
1 parent ef1a8ec commit c5d197a
Show file tree
Hide file tree
Showing 4 changed files with 23 additions and 21 deletions.
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "cityseer"
version = '4.17.4'
version = '4.17.5'
description = "Computational tools for network-based pedestrian-scale urban analysis"
readme = "README.md"
requires-python = ">=3.10, <3.14"
Expand Down
2 changes: 1 addition & 1 deletion pysrc/cityseer/metrics/layers.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ def compute_accessibilities(
raise ValueError("The specified landuse column name can't be found in the GeoDataFrame.")
data_map, data_gdf = assign_gdf_to_network(data_gdf, network_structure, max_netw_assign_dist, data_id_col)
if not config.QUIET_MODE:
logger.info(f'Computing land-use accessibility for: {", ".join(accessibility_keys)}')
logger.info(f"Computing land-use accessibility for: {', '.join(accessibility_keys)}")
# extract landuses
landuses_map: dict[str, str] = data_gdf[landuse_column_label].to_dict() # type: ignore
# call the underlying function
Expand Down
37 changes: 20 additions & 17 deletions pysrc/cityseer/tools/io.py
Original file line number Diff line number Diff line change
Expand Up @@ -687,7 +687,7 @@ def nx_from_osm(osm_json: str) -> nx.MultiGraph:
nd_x_y = f"{x}-{y}"
# when deduplicating, sometimes overlapping stairways / underpasses might share an x, y space...
if "tags" in elem and "level" in elem["tags"]:
nd_x_y = f'{nd_x_y}-{elem["tags"]["level"]}'
nd_x_y = f"{nd_x_y}-{elem['tags']['level']}"
# only add if non-duplicate
if nd_x_y not in xy_nd_map:
xy_nd_map[nd_x_y] = nd_idx
Expand Down Expand Up @@ -725,7 +725,7 @@ def get_merged_nd_keys(_idx: int) -> tuple[str, str]:
levels = [tags["level"]]
levels = [int(round(float(level))) for level in levels]
except Exception:
logger.warning(f'Unable to parse level info: {tags["level"]}')
logger.warning(f"Unable to parse level info: {tags['level']}")
is_tunnel = False
if "tunnel" in tags:
# tends to be "yes" or "building_passage"
Expand Down Expand Up @@ -1251,31 +1251,34 @@ def network_structure_from_gpd(
for col in edges_cols:
if col not in edges_gdf.columns:
raise ValueError(f"Missing expected column in edges GDF: {col}")
nodes_gdf["node_idx_mapping"] = -1
for nd_key, node_data in tqdm(nodes_gdf.iterrows(), disable=config.QUIET_MODE):
# pre-compute node index values and create mapping dictionary for faster lookups
node_indices = nodes_gdf.index.values
node_mapping = {}
# process nodes and build mapping
for nd_key, node_data in tqdm(nodes_gdf.iterrows(), total=len(nodes_gdf), disable=config.QUIET_MODE):
ns_node_idx = network_structure.add_node(
str(nd_key),
float(node_data["x"]),
float(node_data["y"]),
bool(node_data["live"]),
float(node_data["weight"]),
)
nodes_gdf.loc[nd_key, "node_idx_mapping"] = ns_node_idx # type: ignore
for _edge_key, edge_data in tqdm(edges_gdf.iterrows(), disable=config.QUIET_MODE):
# start node network structure idx
# store mapping in dictionary instead of writing back to GeoDataFrame
node_mapping[nd_key] = ns_node_idx
# process edges using the mapping dictionary
for _edge_key, edge_data in tqdm(edges_gdf.iterrows(), total=len(edges_gdf), disable=config.QUIET_MODE):
start_nx_nd_key = edge_data["nx_start_node_key"]
if start_nx_nd_key not in nodes_gdf.index.values:
logger.info(f"Skipping edge as start node nx key not found {start_nx_nd_key}")
continue
start_nd_data = nodes_gdf.loc[nodes_gdf.index == start_nx_nd_key]
start_ns_nd_key: int = start_nd_data["node_idx_mapping"].values[0]
# end node network structure idx
end_nx_nd_key = edge_data["nx_end_node_key"]
if end_nx_nd_key not in nodes_gdf.index.values:
logger.info(f"Skipping edge as end node nx key not found {end_nx_nd_key}")
# fast lookup using dict and set membership
if start_nx_nd_key not in node_indices or end_nx_nd_key not in node_indices:
if start_nx_nd_key not in node_indices:
logger.info(f"Skipping edge as start node nx key not found {start_nx_nd_key}")
if end_nx_nd_key not in node_indices:
logger.info(f"Skipping edge as end node nx key not found {end_nx_nd_key}")
continue
end_nd_data = nodes_gdf.loc[nodes_gdf.index == end_nx_nd_key]
end_ns_nd_key: int = end_nd_data["node_idx_mapping"].values[0]
# get node indices from mapping dictionary - much faster than DataFrame lookups
start_ns_nd_key = node_mapping[start_nx_nd_key]
end_ns_nd_key = node_mapping[end_nx_nd_key]
# add edge
network_structure.add_edge(
start_ns_nd_key,
Expand Down
3 changes: 1 addition & 2 deletions pysrc/cityseer/tools/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -473,8 +473,7 @@ def add_node(
else:
if dupe:
logger.debug(
f"A node of the same name already exists in the graph, "
f"adding this node as {new_nd_name} instead."
f"A node of the same name already exists in the graph, adding this node as {new_nd_name} instead."
)
break
# add
Expand Down

0 comments on commit c5d197a

Please sign in to comment.