如何无内存压力分析德国OpenStreetMap PBF文件并构建街道与城镇名称数据库?
解决Pyrosm处理德国OSM道路数据时的内存不足问题
嘿,我完全理解你的困扰——德国的OSM道路数据体量确实很大,一次性加载整个数据集到内存里肯定会爆内存。你之前尝试用生成器的思路是对的,但问题在于get_network()方法本身是一次性把所有数据加载成GeoDataFrame,所以直接迭代它的行其实还是先把整个数据读进内存了,没从根源解决问题。下面给你几个可行的方案:
方案1:按行政区批量处理(最容易上手)
既然你已经确认处理单个行政区的数据是可行的,那可以自动化遍历德国的所有行政区,逐个处理后合并结果。Pyrosm支持直接下载单个州/行政区的数据,不用先下载整个德国的大文件:
from pyrosm import get_data, OSM import pandas as pd # 德国的州列表,你可以按需调整更细的行政区 german_states = ["baden-wuerttemberg", "bayern", "berlin", "brandenburg", "bremen", "hamburg", "hessen", "mecklenburg-vorpommern", "niedersachsen", "nordrhein-westfalen", "rheinland-pfalz", "saarland", "sachsen", "sachsen-anhalt", "schleswig-holstein", "thueringen"] all_road_names = [] for state in german_states: print(f"正在处理 {state}...") # 下载单个州的数据 state_data = get_data(state) osm = OSM(state_data) # 提取驾驶道路 roads = osm.get_network(network_type="driving") # 提取非空的道路名称并添加到总列表 state_roads = roads["name"].dropna().tolist() all_road_names.extend(state_roads) # 手动释放内存 del roads, osm, state_data # 去重(可选,根据你的需求) unique_road_names = list(set(all_road_names))
这个方法的好处是完全复用你已经熟悉的Pyrosm API,每个批次的数据量小,不会爆内存。
方案2:用Pyrosm的过滤参数减少加载的数据
get_network()其实支持通过filter参数只加载你需要的字段,不用把所有道路属性都读进来,这样能大幅减少内存占用:
from pyrosm import get_data, OSM # 下载德国数据 de = get_data("germany") osm = OSM(de) # 只提取道路名称和必要的基础字段,过滤掉不需要的属性 roads = osm.get_network( network_type="driving", filter={"name": True, "highway": True} # 只保留name和highway字段 ) road_names = roads["name"].dropna().tolist()
这样加载的GeoDataFrame会小很多,因为默认get_network()会加载很多额外的属性(比如宽度、表面材质等),这些你如果不需要的话可以直接过滤掉。
方案3:用Osmium做流式处理(最省内存)
如果上面的方法还是不够,那可以直接用Pyrosm底层依赖的osmium库,它支持流式读取OSM文件,不需要把整个数据集加载到内存里。这样你可以逐行处理每个道路元素,只提取名称:
import osmium class RoadNameHandler(osmium.SimpleHandler): def __init__(self): super().__init__() self.road_names = [] def way(self, w): # 只处理驾驶道路(根据OSM的highway标签判断) if w.tags.get("highway") in ["motorway", "trunk", "primary", "secondary", "tertiary", "unclassified", "residential", "service", "motorway_link", "trunk_link", "primary_link", "secondary_link", "tertiary_link"]: name = w.tags.get("name") if name: self.road_names.append(name) # 初始化处理器并处理文件 handler = RoadNameHandler() handler.apply_file("germany-latest.osm.pbf") # 替换成你的PBF文件路径 # 去重后的道路名称列表 unique_road_names = list(set(handler.road_names))
这个方法是真正的流式处理,内存占用极低,因为它每次只处理一个OSM元素,处理完就丢弃,只保留你需要的名称。你需要先确保安装了osmium:pip install osmium。
为什么你之前的生成器尝试失败?
你之前的两个尝试都没解决问题,原因是:
- 尝试1里的
(OSM(obj) for obj in de)完全不对,de是PBF文件的路径字符串,不是可迭代的对象,所以这个生成器根本没用。 - 尝试2里的
for i in OSM(de).get_network(...)其实是迭代GeoDataFrame的行,但get_network()已经把整个数据集加载到内存里了,所以迭代行并没有减少内存占用。
希望这些方案能帮到你!
内容的提问来源于stack exchange,提问作者MatthiasHerp
相关产品推荐
相关产品推荐

