资讯动态

Python+OpenStreetMap交通数据可视化:路网数据下载与转换实战

发布时间:2026/10/15 1:25:55 来源:尧图企业网站定制
1. 为什么交通数据可视化要从地图数据说起做交通数据分析的人都有一个共同的痛点手里攥着几万条GPS轨迹、卡口过车记录或者公交刷卡数据却不知道怎么把它们放到一张能看的地图上。用商业地图API吧调用次数一多就开始计费坐标系还经常对不上自己画个空白坐标系吧数据点撒上去完全没有空间感看不出路段拥堵、流向分布这些关键信息。我最早做交通流量分析的时候就是拿Excel硬画散点图结果被甲方一句这图看不出在哪条路上直接打回重做。后来摸索出一套稳定又免费的方案Python OpenStreetMap。OpenStreetMap简称OSM是一个开放的地图数据协作项目任何人都可以下载、使用、修改它的地图数据而且完全免费、没有调用次数限制。这一点对做交通数据分析的人来说太重要了——你可以把整个城市的路网数据一次性拉下来离线慢慢处理不用担心配额和费用。这个系列我打算分几篇来写第一篇专门解决最基础也最容易卡住的一步怎么把OpenStreetMap的地图数据下载到本地并且转成Python能直接读取的格式。很多人一上来就去搜Python画地图结果卡在数据源上因为OSM的原始数据格式.osm并不是拿来就能用的需要经过下载、解析、转换几个环节。这篇会把每个环节讲透包括我踩过的坑。适合谁看只要你会一点点Python基础能装库、能跑脚本不管你是做交通规划、物流路径分析、共享单车调度还是单纯想给自己的数据加个地图底图这篇都能直接抄作业。整个流程不需要任何付费账号也不需要复杂的服务器配置一台普通笔记本就能跑。2. 先搞清楚OpenStreetMap的数据到底长什么样2.1 OSM数据的三种基本元素在动手下载之前必须先把OSM的数据模型搞清楚否则后面解析的时候会一头雾水。OSM的数据本质上是一个巨大的图结构由三种基本元素组成Node节点一个带经纬度的点是最小的数据单元。路灯、公交站、路口都可以是一个node。Way路径一串有序node连成的线可以表示一条道路、一条河流、一栋建筑的轮廓。Relation关系多个node、way甚至其他relation的组合用来表达复杂结构比如一条公交线路包含多个路段和站点、一个多边形的行政区划。每个元素都可以带一组Tag标签用键值对描述属性。比如一条高速公路的way上会挂着highwaymotorway、lanes4、maxspeed120这样的标签。做交通分析时我们最关心的就是highway这个键它决定了这条路是什么等级——是高速公路、主干道、还是小区里的步行道。理解这一点很关键OSM不是一张图片而是一个结构化的数据库。你下载下来的不是地图图片而是构成地图的所有几何元素和属性。这既是它的强大之处你可以按属性筛选任意道路也是它的门槛所在你得自己把这些元素渲染成图。2.2 为什么不能直接用.osm文件OSM官方提供的数据格式是XML结构的.osm文件长这样node id123456 lat39.9042 lon116.4074/ way id789012 nd ref123456/ nd ref123457/ tag khighway vprimary/ tag kname v某某路/ /way这种格式人眼能看懂但直接拿Python处理有两个大问题。第一是体积巨大一个中等城市的完整OSM数据动辄几百MB到几个GBXML解析起来又慢又占内存。第二是结构不适合空间分析XML是树形结构而交通分析需要的是能快速做空间查询、能算距离、能找相邻路段的数据结构。所以业界通用的做法是把.osm转换成更适合分析的格式。目前主流的有两种路线一种是转成Shapefile用GIS软件打开另一种是转成GeoJSON或直接用OSMnx这样的Python库在线拉取。这篇先讲最通用、最可控的下载和转换流程把数据牢牢握在自己手里。2.3 下载范围的选择逻辑下载OSM数据最大的坑就是范围。OSM官方按区域提供数据小到一个城市大到整个洲。如果你直接下载整个国家的数据文件可能几十GB处理起来非常痛苦。所以第一步要想清楚你到底需要多大范围的数据我的经验是分三种情况分析需求推荐范围数据来源单个城市交通分析城市级Geofabrik按国家/地区切分的数据跨城市走廊分析自定义矩形框Overpass API按经纬度范围拉取全国路网研究国家级Geofabrik国家包需分块处理对于绝大多数交通可视化项目城市级数据完全够用。比如你要分析某个城市的早晚高峰拥堵只需要这个城市的路网下载下来通常也就几十到几百MBPython处理起来毫无压力。下面我就以城市级数据为主线把下载流程一步步拆开。3. 下载OpenStreetMap数据的三种主流方式3.1 方式一Geofabrik按区域下载最省事Geofabrik是一个长期提供OSM数据镜像下载的服务它把全球数据按大洲、国家、地区切分好直接提供.osm.pbf格式的文件。PBF是OSM官方推出的一种压缩二进制格式体积比XML小很多解析也快得多是目前最推荐的下载格式。操作步骤很简单打开Geofabrik的下载页面找到对应的大洲和地区。选择你要的国家或省份下载.osm.pbf文件。如果只需要某个城市很多地区还提供更细的子区域下载。这个方式的优点是稳定、快、文件规整适合做城市级或省级分析。缺点是范围是固定的行政边界如果你要分析的范围跨了两个行政区就得下载两份数据再合并稍微麻烦一点。提示下载下来的.osm.pbf文件不能直接用文本编辑器打开它是二进制格式。要查看内容需要用专门的工具后面会讲。3.2 方式二Overpass API按坐标范围拉取最灵活如果你要的范围不是标准行政区比如以某个路口为中心方圆5公里的路网那Geofabrik就不合适了。这时候用Overpass API最方便。它是一个专门为查询OSM数据设计的接口你可以用类似SQL的查询语句按经纬度范围、按道路类型、按标签精确拉取数据。一个典型的Overpass查询长这样意思是拉取某个矩形范围内的所有道路[out:json]; ( way[highway](39.90,116.40,39.91,116.41); ); out body; ; out skel qt;这段查询会返回指定矩形框内所有带highway标签的way以及构成这些way的所有node。返回格式可以是JSONPython直接就能解析非常方便。Overpass的优点是精确、灵活、按需拉取缺点是数据量大时容易超时。如果你拉的范围内路网特别密集服务器可能会拒绝或者返回不完整。我的经验是单次查询范围不要超过一个中等城区的面积如果范围大就分块拉取再合并。3.3 方式三OSMnx库一键拉取最适合Python党如果你不想碰命令行工具只想在Python里几行代码搞定那OSMnx是最佳选择。它封装了Overpass API你只要给一个地名或者坐标它就能自动帮你把路网拉下来并且直接返回成NetworkX图对象或者GeoDataFrame拿来就能做分析。import osmnx as ox # 按地名拉取路网 G ox.graph_from_place(某市中心城区, network_typedrive) # 或者按坐标点半径拉取 G ox.graph_from_point((39.9042, 116.4074), dist3000, network_typedrive) # 转成GeoDataFrame方便可视化 nodes, edges ox.graph_to_gdfs(G)network_type参数很关键它决定了拉取哪些道路drive只拉机动车道bike拉自行车道walk拉步行道all拉全部。做交通流量分析一般用drive做慢行系统分析用bike或walk。OSMnx的优点是简单、和Python生态无缝衔接缺点是依赖网络、大批量拉取时慢而且它内部也是调Overpass范围太大同样会超时。所以我的建议是小范围快速验证用OSMnx大范围稳定生产用Geofabrik下载本地解析。3.4 三种方式怎么选把三种方式放一起对比一下方便你按需选择方式适合场景优点缺点Geofabrik城市/省级完整路网稳定、快、文件规整范围固定跨区需合并Overpass API自定义范围、按标签筛选灵活、精确、返回JSON大范围易超时OSMnxPython内快速验证几行代码、直接可用依赖网络、大批量慢我个人的工作流通常是先用OSMnx小范围试一下确认数据没问题正式分析时用Geofabrik下载完整城市数据本地解析成GeoDataFrame。这样既有灵活性又有稳定性。4. 把下载的数据转成Python能用的格式4.1 从.pbf到可读格式的转换工具下载下来的.osm.pbf是二进制文件Python不能直接读。需要先转成通用格式。这里推荐两个工具osmium-tool命令行工具转换速度快支持按标签过滤是处理PBF的首选。pyosmiumosmium的Python绑定可以在Python脚本里直接读PBF适合做自动化流程。用osmium把PBF转成GeoJSON的命令大致是这样osmium export input.osm.pbf -o output.geojson如果只想导出道路可以加过滤条件osmium export input.osm.pbf -o roads.geojson --filterw/highway这个--filterw/highway的意思是只导出带highway标签的way也就是只保留道路把建筑、河流、POI全过滤掉。这一步能大幅减小文件体积做交通分析时非常实用。4.2 用GeoPandas读取和检查数据转成GeoJSON之后用GeoPandas读取就是一行代码的事import geopandas as gpd roads gpd.read_file(roads.geojson) print(roads.columns) print(roads.crs) print(roads.head())读进来之后第一件事是检查坐标系。OSM原始数据用的是WGS84EPSG:4326也就是经纬度。做距离计算和面积统计时经纬度单位是度不能直接用需要投影到平面坐标系。国内常用的是Web MercatorEPSG:3857或者对应的高斯投影。# 投影到Web Mercator方便算距离 roads_proj roads.to_crs(epsg3857)第二件事是检查道路类型分布看看数据里都有哪些highway值print(roads[highway].value_counts())这一步能帮你快速判断数据质量。正常情况下你会看到residential、primary、secondary、tertiary、service这些值。如果发现大量footway或者path说明你拉的范围里步行道太多做机动车分析时需要过滤掉。4.3 数据清洗的几个关键动作原始OSM数据不能直接拿来可视化必须清洗。我一般做这几件事第一过滤道路类型。只保留机动车道把步行道、自行车道、台阶去掉motor_roads roads[roads[highway].isin([ motorway, trunk, primary, secondary, tertiary, residential, service ])]第二处理缺失值。有些道路没有name标签有些没有maxspeed可视化时要注意别让空值报错。第三简化几何。如果路网特别密可以做几何简化减少节点数量加快渲染速度motor_roads[geometry] motor_roads[geometry].simplify(0.0001)第四统一字段。把highway映射成中文或者分级编号方便后面按等级配色。注意清洗时一定要保留原始数据的一份备份。我踩过的坑就是清洗完发现过滤条件写错了把主干道也删了结果只能重新下载。5. 实操从零下载一个城市的完整路网5.1 环境准备与依赖安装先把环境搭好。我用的Python版本是3.9以上依赖库如下pip install geopandas osmnx shapely matplotlib contextily如果要用osmium命令行工具还需要单独安装# macOS brew install osmium-tool # Ubuntu/Debian sudo apt install osmium-toolGeoPandas的安装有时候会因为GDAL依赖出问题如果pip装不上建议用condaconda install -c conda-forge geopandas osmnx这一步的坑主要在于GDAL版本冲突。如果你之前装过其他GIS相关的库可能会报GDAL not found。解决办法是先用conda建一个干净的环境再装GeoPandas基本能避免90%的依赖问题。5.2 完整下载与转换流程下面是我实际用的完整流程以某个中等城市为例。第一步下载PBF文件。从Geofabrik找到对应区域下载.osm.pbf。假设文件叫city.osm.pbf大小约200MB。第二步用osmium过滤出道路。只保留带highway标签的wayosmium export city.osm.pbf -o city_roads.geojson --filterw/highway这一步跑完文件通常从200MB降到30-50MB处理速度快很多。第三步用GeoPandas读取并投影import geopandas as gpd roads gpd.read_file(city_roads.geojson) roads roads.to_crs(epsg3857) # 过滤机动车道 motor roads[roads[highway].isin([ motorway, trunk, primary, secondary, tertiary, residential ])].copy() print(f原始道路数: {len(roads)}) print(f机动车道数: {len(motor)})第四步按道路等级计算长度。投影之后长度单位是米可以直接算motor[length] motor.geometry.length print(motor.groupby(highway)[length].sum() / 1000) # 单位转成公里这一步的输出能让你对城市路网结构有个量化认识。比如你会发现residential占了总长度的大头而motorway虽然条数少但总长度可观。第五步快速可视化验证。用matplotlib画一下确认数据没问题import matplotlib.pyplot as plt fig, ax plt.subplots(figsize(12, 12)) motor.plot(axax, linewidth0.5, colorgray) ax.set_axis_off() plt.savefig(roads_check.png, dpi150, bbox_inchestight)如果图上能看出清晰的城市路网骨架说明数据下载和转换都成功了。这一步是整个流程的验收环节千万别跳过。5.3 参数选择背后的计算逻辑有几个参数的选择不是拍脑袋背后有明确的计算逻辑这里展开说一下。关于投影坐标系的选择。为什么用EPSG:3857而不是别的因为Web Mercator在中小范围内一个城市的形变很小而且它是绝大多数在线地图底图用的坐标系后面叠加底图时不用再转换。如果你要做精确的面积统计比如算某个区域的建筑面积那应该用对应的高斯投影形变更小。但对于交通长度分析3857完全够用。关于几何简化的阈值。simplify(0.0001)里的0.0001是度在纬度40度附近大约对应11米。这个阈值的意思是把偏离原线小于11米的节点删掉。为什么选这个值因为城市道路的GPS精度通常在5-10米简化到11米不会丢失肉眼可见的形状特征但能减少30%-50%的节点数。如果阈值设成0.001约110米道路形状就会明显变形拐弯处会变成直线不可接受。关于道路过滤的范围。为什么把service也保留因为很多小区内部道路、停车场通道都是service做末端配送分析时这些路很重要。但如果只做主干路网分析可以把service也去掉只留primary以上。6. 常见问题与排查技巧实录6.1 下载和转换阶段的典型报错做这个流程新手最容易卡在几个地方。我把常见问题和解决方法整理成表方便对照排查问题现象可能原因解决方法osmium命令找不到没装或没加PATH重新安装检查环境变量GeoPandas导入报GDAL错依赖冲突用conda建干净环境重装读取GeoJSON特别慢文件太大先用osmium过滤再读投影后坐标是天文数字投影错了检查to_crs参数可视化一片空白范围或坐标系不对打印bounds确认范围关于可视化一片空白这个坑我单独说一下。有一次我画了半天图结果出来是空白排查了半小时才发现是数据范围的问题——我下载的是A城市的数据但画图时坐标轴范围设成了B城市的位置数据全在画布外面。解决办法很简单画图前先打印一下数据的边界print(motor.total_bounds)这个值会告诉你数据的实际范围画图时用ax.set_xlim和ax.set_ylim对齐就行。6.2 数据质量问题的识别与处理OSM是众包数据质量参差不齐做分析前必须做质量检查。我一般看三个指标第一道路连通性。理想情况下路网应该是连通的但OSM里经常有断头路、孤立路段。可以用NetworkX检查连通分量import networkx as nx # 把GeoDataFrame转成图检查连通性如果发现大量孤立的小分量说明数据有断裂可能是下载范围截断导致的需要扩大范围重新下载。第二标签完整性。统计关键字段的缺失率for col in [name, maxspeed, lanes, oneway]: if col in motor.columns: missing motor[col].isna().sum() / len(motor) print(f{col} 缺失率: {missing:.1%})实测下来name的缺失率通常在10%-20%maxspeed和lanes的缺失率更高可能到50%以上。这意味着不能依赖这些字段做精确分析只能做参考。如果项目需要精确的限速数据得另外找交管部门的权威数据补充。第三几何有效性。有些way的几何是自相交的或者无效的会导致后续空间分析报错invalid motor[~motor.geometry.is_valid] print(f无效几何数: {len(invalid)})如果数量不多可以直接用buffer(0)修复如果数量很多说明数据源有问题建议换个区域的数据。6.3 性能优化的几个实用技巧当数据量上来之后性能就成了问题。分享几个我实测有效的优化技巧。技巧一用Parquet代替GeoJSON存储。GeoJSON是文本格式读写慢、体积大。转成Parquet之后读取速度能快3-5倍体积能小一半motor.to_parquet(roads.parquet) # 下次直接读 motor gpd.read_parquet(roads.parquet)技巧二建立空间索引。做空间查询前先建索引能大幅加速motor.sindex技巧三按需加载字段。如果只需要几何和道路等级读的时候就把其他字段丢掉减少内存占用。技巧四分块处理大范围数据。如果要处理省级数据不要一次性读进来按城市分块处理每块处理完就释放内存。提示Parquet格式对GeoPandas的版本有要求建议用0.10以上版本。如果读Parquet报错先升级GeoPandas。7. 数据到手之后能做什么数据下载和清洗只是第一步但这一步做扎实了后面的分析就顺了。基于这份路网数据你可以做很多事情按道路等级做分级配色渲染、计算路网密度、分析交叉口分布、叠加GPS轨迹做匹配、算最短路径、做可达性分析等等。这些内容我会在后续的系列里逐个展开。我个人在实际操作中的体会是交通数据可视化最难的不是画图而是数据准备。很多人急着上手画图结果在数据源上反复返工。把下载、转换、清洗这三个环节标准化成一套固定流程后面每次做新项目直接复用效率能提升好几倍。我现在的习惯是每拿到一个新城市的数据先跑一遍这套流程把清洗好的路网存成Parquet后面所有分析都基于这份干净数据再也不用重复处理。最后再分享一个小技巧如果你经常需要下载不同城市的数据可以写一个脚本把Geofabrik的下载、osmium的转换、GeoPandas的清洗串起来输入城市名就自动产出干净的路网文件。这个脚本我用了两年多省下的时间相当可观。下一篇我会讲怎么在这份路网数据上叠加交通流量数据做出真正能说明问题的可视化图。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑