如何用libosmium Handler实现本地PBF文件的边界框数据提取?
基于Libosmium从本地PBF文件提取指定边界框数据的最优方案
问题背景
此前通过Overpass API提取指定边界框内的OSM数据,传入Libosmium处理链进行后续处理。现在希望切换为使用本地国家级PBF文件,但手动逐个检查节点是否在边界框内的方式效率低下,且对Libosmium的API不够熟悉,无法找到更优实现方案。
当前代码片段:
osmium::area::AssemblerConfig assemblerConfig; assemblerConfig.ignore_invalid_locations = true; assemblerConfig.create_way_polygons = false; // These are handled as normal ways in the handler assemblerConfig.create_empty_areas = true; osmium::area::MultipolygonManager<osmium::area::Assembler> multipolygonManager{ assemblerConfig }; osm::DefaultNodeLocationsForWaysHandler n2wHandler; n2wHandler.ignore_errors(); // SWNE bounding box std::array<double,4> bounds{50.9065510, -1.4500237, 50.9517765, -1.3419628}; osmium::io::File f; // Check if local file exists, if not then do an overpass api call and save to file if (!std::filesystem::exists(std::string("/britain-and-ireland-latest.osm.pbf"))) { f = osmium::io::File{rawResponse(bounds)}; } else { f = osmium::io::File{std::string("/britain-and-ireland-latest.osm.pbf")}; //TODO: Apply some logic here to only give results within a bounding box } read_relations(f, multipolygonManager); // handlers is a parameter pack of osmium handlers passed in osmium::io::Reader reader{f, osmium::osm_entity_bits::all}; osmium::apply(reader, n2wHandler, multipolygonManager.handler(), std::forward<THandlers>(handlers)...); osmium::apply(multipolygonManager.buffer(), std::forward<THandlers>(handlers)...);
解决方案
使用Libosmium内置的osmium::filter::BoxFilter实现高效的空间过滤,该过滤器底层基于空间索引优化,能直接在读取PBF文件时仅保留边界框内(或与边界框相交)的OSM实体,无需手动逐个检查节点,大幅提升处理效率。
修改后的完整代码
#include <osmium/filter/box.hpp> #include <osmium/geom/coordinates.hpp> #include <osmium/osm/box.hpp> osmium::area::AssemblerConfig assemblerConfig; assemblerConfig.ignore_invalid_locations = true; assemblerConfig.create_way_polygons = false; // These are handled as normal ways in the handler assemblerConfig.create_empty_areas = true; osmium::area::MultipolygonManager<osmium::area::Assembler> multipolygonManager{ assemblerConfig }; osm::DefaultNodeLocationsForWaysHandler n2wHandler; n2wHandler.ignore_errors(); // SWNE bounding box std::array<double,4> bounds{50.9065510, -1.4500237, 50.9517765, -1.3419628}; // 将SWNE坐标转换为Libosmium的Box对象(注意坐标顺序:经度在前,纬度在后) osmium::Box filter_box{ osmium::Coordinates{bounds[1], bounds[0]}, // 西南角 osmium::Coordinates{bounds[3], bounds[2]} // 东北角 }; // 创建Box过滤器,保留与边界框相交的实体 osmium::filter::BoxFilter filter{filter_box, true}; osmium::io::File f; // 检查本地文件是否存在,不存在则调用Overpass API获取 if (!std::filesystem::exists(std::string("/britain-and-ireland-latest.osm.pbf"))) { f = osmium::io::File{rawResponse(bounds)}; } else { f = osmium::io::File{std::string("/britain-and-ireland-latest.osm.pbf")}; } // 读取并过滤关系数据,供MultipolygonManager处理 osmium::io::Reader relation_reader{f, osmium::osm_entity_bits::relation}; osmium::apply_filter(relation_reader, filter, multipolygonManager.handler()); relation_reader.close(); // 读取所有实体,应用过滤后传入处理链 osmium::io::Reader reader{f, osmium::osm_entity_bits::all}; osmium::apply_filter(reader, filter, n2wHandler, std::forward<THandlers>(handlers)...); reader.close(); // 对生成的区域数据再次过滤,确保仅保留边界框内的区域 osmium::apply_filter(multipolygonManager.buffer(), filter, std::forward<THandlers>(handlers)...);
关键修改说明
- 边界框转换:将原有的SWNE坐标数组转换为Libosmium标准的
osmium::Box对象,注意Libosmium采用(经度, 纬度)的坐标顺序。 - 过滤器初始化:
osmium::filter::BoxFilter的第二个参数设为true,表示保留所有与边界框相交的实体(包括完全在框内、部分落入框内的方式和关系),同时自动维护OSM数据的依赖完整性(比如保留方式关联的所有节点)。 - 分阶段过滤:
- 单独读取关系实体并过滤,确保
MultipolygonManager仅处理边界框相关的多边形容器 - 读取全量实体时通过过滤器直接筛选,避免无效数据进入处理链
- 对生成的区域数据再次过滤,保证最终输出的区域符合边界框要求
- 单独读取关系实体并过滤,确保
- 替换处理入口:用
osmium::apply_filter替代原有的osmium::apply,实现过滤逻辑与处理链的无缝结合。
注意事项
- 需确保代码中包含必要的头文件:
<osmium/filter/box.hpp>、<osmium/geom/coordinates.hpp>、<osmium/osm/box.hpp> - 该方案无需手动处理实体依赖关系,Libosmium会自动保留必要的关联数据,保证处理后的OSM数据结构完整
内容的提问来源于stack exchange,提问作者aliaksei
相关产品推荐
相关产品推荐

