如何跨时间识别道路几何?——基于OSMNX 0.12匹配阿姆斯特丹不同日期的特定道路路段
匹配不同日期OSM路网中的对应路段:可行方案
当然可以实现!你遇到的节点ID变化问题在OSM历史数据中很常见——路段可能因为编辑(比如节点微调、路段拆分/合并)导致节点或路段ID变更,但空间位置基本不变。下面给你两种实用的解决思路,结合你的代码来落地:
方法1:基于空间特征匹配(最简单直接)
既然两个路段在空间上是同一段路,我们可以通过计算空间重叠度或邻近性来匹配。用GeoPandas的空间分析功能就能快速实现:
步骤:
- 提取你关注的1月1日目标路段,统一使用阿姆斯特丹本地投影坐标系(EPSG:28992),避免地理坐标系的距离误差
- 计算3月1日路网中每个路段与目标路段的重叠比例,取比例最高的即为对应路段
修改你的代码,加入匹配逻辑:
import osmnx import datetime import geopandas as gpd bbox = (52.36477309990933, 4.897592764542301, 52.369790450504155, 4.912605158495796) custom_filter = '["area"!~"yes"]'\ '["highway"!~"cycleway|footway|path|pedestrian|steps|track|corridor|proposed|construction|bridleway|abandoned|platform|raceway"]'\ '["motor_vehicle"!~"no"]'\ '["motorcar"!~"no"]'\ '["access"!~"private"]'\ '["service"!~"private|emergency_access"]' dates_to_query = ["20200101", "20200301"] road_networks = [] modified_road_segments = [] node_of_interest = 25596477 # 加载两个日期的路网并转换坐标系 for date_to_query in dates_to_query: date_to_query = datetime.datetime.strptime(date_to_query, "%Y%m%d").isoformat() custom_settings = f"""[out:json][timeout:180][date:"{date_to_query}"]""" graph = osmnx.graph_from_bbox(north=bbox[2], south=bbox[0], west=bbox[1], east=bbox[3], custom_settings=custom_settings, custom_filter=custom_filter) nodes, road_network_gdf = osmnx.graph_to_gdfs(graph.to_undirected()) road_network_gdf = road_network_gdf.to_crs(epsg=28992) road_networks.append(road_network_gdf) modified_road_segments.append(road_network_gdf[ road_network_gdf['u'] == node_of_interest ] ) # 提取1月的目标路段 jan_target = modified_road_segments[0].iloc[0] # 定义函数计算路段重叠比例 def calculate_overlap(row): intersection = jan_target.geometry.intersection(row.geometry) if intersection.length == 0: return 0 # 取重叠长度与较短路段长度的比例,更准确反映匹配度 return intersection.length / min(jan_target.geometry.length, row.geometry.length) # 计算3月路网中所有路段的重叠比例 march_gdf = road_networks[1] march_gdf['overlap_ratio'] = march_gdf.apply(calculate_overlap, axis=1) # 获取匹配的路段(重叠比例最高的) matching_segment = march_gdf[march_gdf['overlap_ratio'] == march_gdf['overlap_ratio'].max()] print("匹配到的3月1日路段信息:") print(matching_segment[['u', 'v', 'osmid', 'geometry']])
这个方法的优势是不需要依赖OSM的历史API,适合批量处理多个路段的匹配。
方法2:基于OSM元素历史追踪(精确溯源)
如果需要精确追踪OSM元素的演变(比如知道路段是否被拆分、合并或修改),可以直接调用OSM的API查询元素的历史版本:
步骤:
- 从1月的路段中获取其OSM Way ID(即
osmid列的值) - 调用OSM历史API查询该Way的所有版本,找到2020年3月1日对应的版本,或查看是否有替代的Way
示例代码:
import requests import xml.etree.ElementTree as ET # 获取1月目标路段的OSM Way ID jan_way_id = modified_road_segments[0]['osmid'].iloc[0] # 调用OSM历史API history_url = f"https://api.openstreetmap.org/api/0.6/way/{jan_way_id}/history" response = requests.get(history_url) root = ET.fromstring(response.content) # 遍历所有版本,找到2020-03-01之前的最新版本 target_date = datetime.datetime(2020, 3, 1) latest_version = None for way in root.findall('way'): version_date = datetime.datetime.strptime(way.attrib['timestamp'], "%Y-%m-%dT%H:%M:%SZ") if version_date <= target_date: latest_version = way break if latest_version: print(f"目标Way在2020-03-01的版本ID: {latest_version.attrib['version']}") # 查看该版本的节点组成 nodes = [nd.attrib['ref'] for nd in latest_version.findall('nd')] print(f"该版本的节点列表: {nodes}") else: print("该Way在2020-03-01已被删除,可能被新的Way替代")
如果原Way被删除,你可以通过查询该区域的历史变更,找到空间位置高度重合的替代Way。
为什么会出现节点ID变化?
OSM的节点和Way ID并非永久不变:
- 节点可能因为位置微调被删除后重建,导致ID变更
- 路段(Way)可能被拆分、合并,或编辑时替换了部分节点,导致u/v的节点ID变化
- 部分编辑操作会直接创建新的Way替代旧的,旧Way被标记为删除
内容的提问来源于stack exchange,提问作者Diana
相关产品推荐
相关产品推荐

