Skip to content

Commit

Permalink
consolidate_intersections: Allow tolerance to accept dictionary
Browse files Browse the repository at this point in the history
This commit updates the consolidate_intersections function to accept the tolerance parameter as either a float or a dictionary mapping node IDs to floats.

It removes the previously suggested tolerance_attribute.
  • Loading branch information
EwoutH committed Apr 25, 2024
1 parent 18d8b69 commit 933a7e3
Showing 1 changed file with 19 additions and 25 deletions.
44 changes: 19 additions & 25 deletions osmnx/simplification.py
Original file line number Diff line number Diff line change
Expand Up @@ -446,12 +446,11 @@ def simplify_graph( # noqa: C901, PLR0912
def consolidate_intersections(
G: nx.MultiDiGraph,
*,
tolerance: float = 10,
tolerance: float | dict[int, float] = 10,
rebuild_graph: bool = True,
dead_ends: bool = False,
reconnect_edges: bool = True,
node_attr_aggs: dict[str, Any] | None = None,
tolerance_attribute: str | None = None,
) -> nx.MultiDiGraph | gpd.GeoSeries:
"""
Consolidate intersections comprising clusters of nearby nodes.
Expand Down Expand Up @@ -493,7 +492,8 @@ def consolidate_intersections(
A projected graph.
tolerance
Nodes are buffered to this distance (in graph's geometry's units) and
subsequent overlaps are dissolved into a single node.
subsequent overlaps are dissolved into a single node. Can be a float
value or a dictionary mapping node IDs to individual tolerance values.
rebuild_graph
If True, consolidate the nodes topologically, rebuild the graph, and
return as MultiDiGraph. Otherwise, consolidate the nodes geometrically
Expand All @@ -513,9 +513,6 @@ def consolidate_intersections(
(anything accepted as an argument by `pandas.agg`). Node attributes
not in `node_attr_aggs` will contain the unique values across the
merged nodes. If None, defaults to `{"elevation": numpy.mean}`.
tolerance_attribute : str, optional
The name of the attribute that contains individual tolerance values for
each node. If None, the default tolerance is used for all nodes.
Returns
-------
Expand Down Expand Up @@ -545,7 +542,6 @@ def consolidate_intersections(
tolerance,
reconnect_edges,
node_attr_aggs,
tolerance_attribute,
)

# otherwise, if we're not rebuilding the graph
Expand All @@ -554,11 +550,12 @@ def consolidate_intersections(
return gpd.GeoSeries(crs=G.graph["crs"])

# otherwise, return the centroids of the merged intersection polygons
return _merge_nodes_geometric(G, tolerance, tolerance_attribute).centroid
return _merge_nodes_geometric(G, tolerance).centroid


def _merge_nodes_geometric(
G: nx.MultiDiGraph, tolerance: float, tolerance_attribute: str | None = None,
G: nx.MultiDiGraph,
tolerance: float | dict[int, float],
) -> gpd.GeoSeries:
"""
Geometrically merge nodes within some distance of each other.
Expand All @@ -570,9 +567,8 @@ def _merge_nodes_geometric(
tolerance
Buffer nodes to this distance (in graph's geometry's units) then merge
overlapping polygons into a single polygon via unary union operation.
tolerance_attribute : str, optional
The name of the attribute that contains individual tolerance values for
each node. If None, the default tolerance is used for all nodes.
Can be a float value or a dictionary mapping node IDs to individual
tolerance values.
Returns
-------
Expand All @@ -581,14 +577,15 @@ def _merge_nodes_geometric(
"""
gdf_nodes = convert.graph_to_gdfs(G, edges=False)

if tolerance_attribute and tolerance_attribute in gdf_nodes.columns:
# If a node does not have a value in the tolerance_attribute, use the default tolerance
buffer_distances = gdf_nodes[tolerance_attribute].fillna(tolerance)
# Buffer nodes to the specified distances and merge them
merged = gdf_nodes["geometry"].buffer(distance=buffer_distances).unary_union
if isinstance(tolerance, dict):
# Map tolerances to node IDs, using NaN for nodes not in the dictionary, and fill NaN with zero
buffer_distances = gdf_nodes.index.to_series().map(tolerance).fillna(0)

Check warning on line 582 in osmnx/simplification.py

View check run for this annotation

Codecov / codecov/patch

osmnx/simplification.py#L582

Added line #L582 was not covered by tests
else:
# Use the default tolerance for all nodes
merged = gdf_nodes["geometry"].buffer(distance=tolerance).unary_union
buffer_distances = tolerance

# Buffer nodes to the specified distances and merge them
merged = gdf_nodes.geometry.buffer(distance=buffer_distances).unary_union

# if only a single node results, make it iterable to convert to GeoSeries
merged = MultiPolygon([merged]) if isinstance(merged, Polygon) else merged
Expand All @@ -597,10 +594,9 @@ def _merge_nodes_geometric(

def _consolidate_intersections_rebuild_graph( # noqa: C901,PLR0912,PLR0915
G: nx.MultiDiGraph,
tolerance: float,
tolerance: float | dict[int, float],
reconnect_edges: bool, # noqa: FBT001
node_attr_aggs: dict[str, Any] | None,
tolerance_attribute: str | None = None,
) -> nx.MultiDiGraph:
"""
Consolidate intersections comprising clusters of nearby nodes.
Expand All @@ -623,7 +619,8 @@ def _consolidate_intersections_rebuild_graph( # noqa: C901,PLR0912,PLR0915
A projected graph.
tolerance
Nodes are buffered to this distance (in graph's geometry's units) and
subsequent overlaps are dissolved into a single node.
subsequent overlaps are dissolved into a single node. Can be a float
value or a dictionary mapping node IDs to individual tolerance values.
reconnect_edges
If True, reconnect edges (and their geometries) to the consolidated
nodes in rebuilt graph, and update the edge length attributes. If
Expand All @@ -635,9 +632,6 @@ def _consolidate_intersections_rebuild_graph( # noqa: C901,PLR0912,PLR0915
(anything accepted as an argument by `pandas.agg`). Node attributes
not in `node_attr_aggs` will contain the unique values across the
merged nodes. If None, defaults to `{"elevation": numpy.mean}`.
tolerance_attribute : str, optional
The name of the attribute that contains individual tolerance values for
each node. If None, the default tolerance is used for all nodes.
Returns
-------
Expand All @@ -653,7 +647,7 @@ def _consolidate_intersections_rebuild_graph( # noqa: C901,PLR0912,PLR0915
# buffer nodes to passed-in distance and merge overlaps. turn merged nodes
# into gdf and get centroids of each cluster as x, y
node_clusters = gpd.GeoDataFrame(
geometry=_merge_nodes_geometric(G, tolerance, tolerance_attribute),
geometry=_merge_nodes_geometric(G, tolerance),
)
centroids = node_clusters.centroid
node_clusters["x"] = centroids.x
Expand Down

0 comments on commit 933a7e3

Please sign in to comment.