diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c3a5c30..7ffd1e23 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ ## 2.0.2 (TBD) - improve docstrings (#1272) +- fix bug where consolidate_intersections function would mutate the passed-in graph (#1273) +- provide user-friendly error message if consolidate_intersections is run more than once (#1273) ## 2.0.1 (2025-01-01) diff --git a/osmnx/io.py b/osmnx/io.py index bd8ad056..e169de58 100644 --- a/osmnx/io.py +++ b/osmnx/io.py @@ -188,7 +188,10 @@ def load_graphml( raise ValueError(msg) # specify default graph/node/edge attribute values' data types - default_graph_dtypes = {"simplified": _convert_bool_string} + default_graph_dtypes = { + "consolidated": _convert_bool_string, + "simplified": _convert_bool_string, + } default_node_dtypes = { "elevation": float, "elevation_res": float, diff --git a/osmnx/simplification.py b/osmnx/simplification.py index 32976476..ca100873 100644 --- a/osmnx/simplification.py +++ b/osmnx/simplification.py @@ -335,7 +335,7 @@ def simplify_graph( # noqa: C901, PLR0912 Returns ------- - G + Gs Topologically simplified graph, with a new `geometry` attribute on each simplified edge. """ @@ -513,19 +513,19 @@ def consolidate_intersections( Returns ------- - G or gs + Gc or gs If `rebuild_graph=True`, returns MultiDiGraph with consolidated intersections and (optionally) reconnected edge geometries. If `rebuild_graph=False`, returns GeoSeries of Points representing the centroids of street intersections. """ + # make a copy to not mutate original graph object caller passed in + G = G.copy() + # if dead_ends is False, discard dead-ends to retain only intersections if not dead_ends: spn = stats.streets_per_node(G) dead_end_nodes = [node for node, count in spn.items() if count <= 1] - - # make a copy to not mutate original graph object caller passed in - G = G.copy() G.remove_nodes_from(dead_end_nodes) if rebuild_graph: @@ -630,6 +630,10 @@ def _consolidate_intersections_rebuild_graph( # noqa: C901,PLR0912,PLR0915 A rebuilt graph with consolidated intersections and (optionally) reconnected edge geometries. """ + if G.graph.get("consolidated"): # pragma: no cover + msg = "This graph has already been consolidated, cannot consolidate it again." + raise GraphSimplificationError(msg) + # default node attributes to aggregate upon consolidation if node_attr_aggs is None: node_attr_aggs = {"elevation": "mean"} @@ -715,6 +719,9 @@ def _consolidate_intersections_rebuild_graph( # noqa: C901,PLR0912,PLR0915 node_attrs[col] = unique_vals Gc.add_node(cluster_label, **node_attrs) + # mark the graph as having been consolidated + G.graph["consolidated"] = True + if len(G.edges) == 0 or not reconnect_edges: # if reconnect_edges is False or there are no edges in original graph # (after dead-end removed), then skip edges and return new graph as-is