使用edge_attrs_differ参数时OSMnx simplify_graph函数导致双向边几何方向不一致问题
1. Is the mixed geometry direction behavior expected or a bug?
This is expected behavior of the simplify_graph function when using edge_attrs_differ. Here's the breakdown based on the OSMnx source code you examined:
The simplification workflow relies on the _get_paths_to_simplify() function to identify edge sequences that can be merged—these are paths with intermediate nodes that meet the simplification criteria. Edges that directly connect two endpoints with no intermediate nodes never get included in these simplification paths, since there’s nothing to merge for them.
Since these edges aren’t part of a simplification path, the code that rebuilds geometry to align with edge direction never runs for them. They retain their original geometry from the input graph, even if that geometry’s orientation doesn’t match the edge’s u→v direction.
2. How to enforce consistent geometry direction for all edges?
There’s no official built-in flag for this, but you can add a straightforward post-processing step to normalize geometry direction for every edge in your simplified graph. The goal is to ensure every u→v edge has a LineString where the first coordinate matches node u’s location and the last matches node v’s location.
Here’s a reusable function you can integrate into your code:
import numpy as np from shapely.geometry import LineString def normalize_edge_geometries(graph): """Ensure every u→v edge's geometry starts at u and ends at v.""" # Pre-fetch node coordinates for quick lookup node_coords = {n: (data['x'], data['y']) for n, data in graph.nodes(data=True)} for u, v, key, data in graph.edges(keys=True, data=True): geom = data.get('geometry') if not isinstance(geom, LineString): continue # Get start/end points of the current geometry geom_start = (geom.coords[0][0], geom.coords[0][1]) geom_end = (geom.coords[-1][0], geom.coords[-1][1]) # Get expected start/end points from node coordinates expected_start = node_coords[u] expected_end = node_coords[v] # Use tolerance to handle floating-point precision issues if not np.allclose(geom_start, expected_start, atol=1e-8) or not np.allclose(geom_end, expected_end, atol=1e-8): # Reverse geometry to match edge direction reversed_coords = list(reversed(geom.coords)) data['geometry'] = LineString(reversed_coords) return graph
Apply this function right after your custom simplification step:
# Run custom simplification graph_custom_simplification = ox.simplification.simplify_graph(graph, edge_attrs_differ=["tags"]) # Normalize all edge geometries to align with direction graph_custom_simplification = normalize_edge_geometries(graph_custom_simplification) # Save the corrected graph save_geoparquets(graph_custom_simplification, "custom_simplification_normalized")
Key Notes:
- The
np.allclosecheck accounts for floating-point precision errors common in geospatial data—adjust theatol(absolute tolerance) if needed for your coordinate system. - This function only modifies edges where geometry direction doesn’t match the edge’s
u→vorientation, so it won’t alter edges that already have correct geometry. - It works for both simplified and unsimplified graphs, making it a reliable final cleanup step regardless of your simplification parameters.
内容的提问来源于stack exchange,提问作者chrisail

