
做 GIS 和地图这块十来年被问得最多的一句话就是openstreetmap 的地图怎么下。问的人里有做物流路径规划的、有做城市研究的、有搞室内外一体化导航的也有只是想给自己项目画一张好看底图的前端同学。大家的共同困惑是OpenStreetMap 听起来是个地图网站可打开一看数据是能编辑的、页面是能看的但就是找不到一个显眼的下载按钮。于是有人去截屏拼图有人去扒瓦片还有人以为这东西只能在线看。先给一个结论性的定位OpenStreetMap下面简称 OSM本质上是一个全球性的开放地理数据库它同时对外提供三种东西——原始矢量数据、渲染好的栅格瓦片、以及各种衍生产品。你要下载的到底是哪一种直接决定了后面该走哪条路。这跟去菜市场买菜是一个道理你想买毛坯食材、半成品净菜还是直接点一份外卖三种需求对应三个完全不同的摊位。选错了摊位不是买不到而是买回来没法用。这篇文章我把这些年用过的下载路径从头捋一遍从几十平方公里的点位导出到几百 GB 的全球全量数据中间还有 Overpass 查询、区域切块、命令行裁剪、格式转换这些绕不开的环节。每一种方法我都会说清楚它适合什么场景、坑在哪里、参数怎么定。新手可以照着抄命令有经验的同学可以直接跳到第 4、5 章看工具链和瓦片部分的细节。1. 先分清你要的到底是哪种地图数据很多人一上来就问下载地图这个问题本身是模糊的。OSM 对外输出的数据形态至少有三大类用法完全不同混着理解会一直卡住。1.1 三种数据形态原始矢量、矢量瓦片、栅格瓦片第一类是原始矢量数据通常以.osm.pbf、.osm、.osm.xml这些后缀出现。里面存的是赤裸裸的几何 标签比如一条道路就是一条折线加一堆键值对highwayresidential、name某某路、onewayyes、maxspeed30。这个形态信息量最大能拿去做空间分析、路网计算、专题制图。缺点是它只存几何和属性不存样式你打开它看到的是线条和点不是一张图。第二类是矢量瓦片常见后缀.mvt、.pbf注意和上面的.osm.pbf不是一回事只是扩展名撞车了。它把矢量数据按瓦片编号切好、按图层组织好浏览器端配合样式表渲染能做出可缩放、可旋转、可换肤的动态地图。它是给前端用的不是给分析用的因为跨瓦片查询和属性检索很不方便。第三类是栅格瓦片就是我们熟悉的z/x/y.png这种图片金字塔。它直接就是图谁都能用但它不可查询、不可编辑、放大到一定级别就糊。离线地图包、PPT 里的插图、CAD 底图多数用的是这一类。提示如果你要做的是算出两点之间的最短路径统计某个片区的学校分布选第一类如果是给自己的 App 做一张能换主题的地图选第二类如果只是要一张截图当底图第三类最省事。1.2 下载前必须锁定的四个参数不管走哪种方式动手前先把下面四件事定下来能省掉大量返工。范围是经纬度矩形框bbox还是行政边界多边形relation还是某个地名匹配出来的区域。这三者的数据量和复杂度差一个数量级。矩形框最好算多边形最灵活地名匹配最容易出意外重名。要素类型全要素、只要路网、只要建筑轮廓、只要 POI。只要你明确了这一点后面用标签过滤可以从几百 GB 直接缩到几十 MB。格式PBF、GeoJSON、Shapefile、GPKG、CSV。格式决定了你后面用什么工具打开也决定了坐标系和字段长度这些隐形限制。时间版本OSM 是持续编辑的今天下载的路网和下个月下载的会不一样。做对比分析的时候一定要把版本日期记下来否则半年后你根本说不清两版数据的差异是城市真变了还是有人改错了。我见过最典型的翻车案例一个团队做交通流量模型两个人在不同时间各下了一份路网结果合并的时候发现同一条路一边有、一边没有排查了一整天才意识到是数据版本差了两周。这种坑只要在文件名里加个日期就能避免。2. 小范围取数官方导出面板与 Overpass 查询范围在几十到几百平方公里这个量级完全不需要碰全量数据。两条路图形化点选或者写查询语句。2.1 官方导出面板点选式下载适合几十平方公里OSM 主站上有一个导出入口你在页面里把矩形框拖到目标区域点导出就能拿到一个.osm文件。它的优势是零门槛浏览器打开就能用劣势也很明确有面积上限超了会提示缩小范围。而且不同时间点的限制不完全一致通常会根据当前服务压力动态调整。这个面板适合的场景很具体临时看一下某个片区的数据长什么样、给一个小项目做一次性的底图、教学演示。如果你要的是一个地级市全市的路网这条路走不通。另外它的输出格式是.osmXML体积比 PBF 大好几倍几十平方公里就可能上百 MB加载慢是正常的。2.2 Overpass API按标签精确提取Overpass 是我个人用得最多的一类方案。它不是一个下载按钮而是一套查询语言加一个查询端点。你写一段查询语句描述我要哪个区域里满足什么标签的要素它把结果回给你。返回格式可以是 XML、JSON 或者 CSV。它的价值在于精确过滤。比如你只要充电桩位置写一个node[amenitycharging_station]就完事了不需要把整个城市的建筑轮廓都下下来再删。数据量能降两三个数量级。用法上有两种一是网页版查询界面左边写语句右边看结果确认无误后导出二是直接用 HTTP 请求打端点配合脚本批量跑。后一种在需要重复取数比如每月更新一次的时候非常划算。2.3 Overpass 查询语句的写法与超时控制Overpass QL 的语法看着有点怪但结构其实很固定。一个典型的路网查询长这样[out:xml][timeout:180][maxsize:1073741824]; area[name杭州市][admin_level5]-.a; ( way[highway](area.a); ); (._;;); out body;逐行解释一下这几行是最容易写错的地方第一行是全局设置。timeout是服务端最长执行时间秒为单位maxsize限制返回数据量上限。这两个值给太小会直接超时给太大又可能被服务端拒绝一般从小往大试。第二行用area锁定行政区域admin_level的取值因地区而异写错就匹配不到。省事的做法是先用简单查询把 area 的 id 查出来直接写area(3600000000)这种数字 id稳定性最高。第三行是要素选择way[highway]表示所有带highway标签的线要素。想只要主干路可以加值过滤比如way[highway~motorway|trunk|primary]。第四行(._;;)是递归向下取完整几何把构成这些线的节点也一起拉出来。少了这一行你拿到的线是断的导进 GIS 会变成一堆不闭合的碎线。第五行out body控制输出内容out skel qt只出骨架不带标签速度更快。注意Overpass 是公共资源官方明确希望大家别做高频批量请求。需要大规模反复取数的时候自己搭一个实例或者改用区域切块文件别硬怼公共端点。3. 中等到大范围按区域切块下载范围上升到省、国家甚至洲的级别前两种方式就不合适了。这时候要用预切好的区域文件。3.1 区域切分逻辑大洲到国家的层级目前最常用的区域切块服务是按大洲 → 国家 → 省级子区域这样的层级把全量数据切好每个区域一个.osm.pbf文件并且提供每日或每几小时更新一次的版本。它的便利之处在于你只需要知道自己要的国家或省份直接下对应的文件就行不用自己动手切。拿中国区域来说通常对应一个单独的文件体积在数百 MB 到 1 GB 出头这个量级具体随数据增长逐年变化。再往下细分部分省份也会有独立文件。如果你的目标就是某一个省的路网这种切块文件的效率远比自己去切全量高。需要留意的是切块文件的边界是按行政区域划分的如果你要的范围横跨两个省的接壤地带需要下两份再合并。另外行政区域的映射关系relation在 OSM 里是一个嵌套结构取子区域之前最好先确认一下 relation 的 id不要凭名字猜。3.2 自定义边界导出工具区域切块服务的局限是只能按它切好的粒度拿。如果你要的是一个非行政形状的范围比如流域、某个经济圈、或者你自己画的多边形就得用支持自定义边界的导出工具。这类工具的工作方式是你上传一个多边形文件GeoJSON、KML、WKT 都行选好要的要素类型它后台去拉数据、裁剪、打包完成后给你一个下载链接。整个过程通常几分钟到几十分钟取决于范围和要素量。它的实现原理其实不神秘本质上就是调用大数据集的子集提取接口。所以你在用的时候要注意两点。第一它给的成果是异步的任务量大时排队很正常别以为是卡住了。第二选要素类型的时候尽量收敛全要素导出会显著变慢而且很多用不上的字段会让后续处理变重。3.3 全量数据文件与硬件门槛再往上就是全球全量文件一般被称为 Planet 文件。压缩后的体积在几十 GB 这个量级解压后翻好几倍。这个量级的东西下载不是难点处理才是。先算一笔账。假设压缩包 70 GB磁盘上占 70 GB解压成 XML 可能到 800 GB 以上再转成可查询的数据库又要一份空间。也就是说你要留出 1 TB 以上的可用磁盘才比较从容。内存方面做全球范围的空间索引至少得 64 GB 起步128 GB 比较舒服。CPU 核心数决定了提取和转换的耗时全球级别的处理动辄是以天为单位计时的。我的建议很直接除非你确实需要全球范围的完整数据否则不要碰 Planet。绝大多数项目需要的是某个国家、某个省、甚至某个城市。省下来的时间用来调参和验证价值高得多。数据源类型典型适用范围获取方式主要限制官方导出面板几十平方公里网页拖框有面积上限格式为 XMLOverpass 查询市县级按标签写查询语句 / HTTP公共端点有频率限制区域切块文件国家、省级直接下载.osm.pbf粒度固定跨区需合并自定义边界导出任意多边形上传边界异步生成需排队全要素较慢全球全量文件全球下载大文件磁盘、内存、时间成本高4. 拿到 PBF 之后的裁剪与格式转换下载只是第一步。真正让很多人卡住的是文件下来了怎么变成我能用的东西。4.1 命令行工具链的安装与常用命令处理.osm.pbf最顺手的是一套命令行工具核心是三个命令提取、过滤、导出。安装方式在主流系统上都很简单包管理器基本都有。# 按矩形范围裁剪 osmium extract -b 120.10,30.20,120.35,30.40 china-latest.osm.pbf -o hangzhou.osm.pbf # 按边界多边形裁剪 osmium extract -p boundary.geojson china-latest.osm.pbf -o area.osm.pbf # 只保留路网要素 osmium tags-filter hangzhou.osm.pbf w/highway -o hangzhou-roads.osm.pbf # 转成 GeoJSON osmium export hangzhou-roads.osm.pbf -o hangzhou-roads.geojson # 查看文件基本信息 osmium fileinfo hangzhou.osm.pbf这里面的关键点是先裁剪、再过滤、最后转换。顺序反了会非常痛苦。原因很简单裁剪是纯几何操作不涉及属性解析速度最快过滤要解析全部标签稍慢转换到 GeoJSON 要重建几何、处理坐标系、序列化成文本最慢而且最吃内存。先把数据量砍下来后面的每一步都省力。-b后面的参数顺序是最小经度,最小纬度,最大经度,最大纬度注意是经度在前。这个顺序错一次你会得到一个在太平洋中间的空文件而且不会报错。我第一年做这行的时候在这个点上栽过不止一次。4.2 格式转换时的字段与坐标系处理导出成 Shapefile 或者 GeoJSON 的时候有几个隐形的坑要提前知道。Shapefile 的字段名长度上限是 10 个字符。OSM 里很多标签名超过这个长度转换工具会做截断或者重命名导致你后面按字段名匹配的时候对不上。解决办法是改用 GPKG 格式它是基于 SQLite 的字段名长度基本没限制而且单文件、支持索引现在是我的默认选择。Shapefile 的单文件体积上限是 2 GB。数据量大了会静默截断这种错误极难排查因为文件看起来是完整的。同样用 GPKG 可以绕开。坐标系默认是 WGS84EPSG:4326经纬度单位。如果你要做面积计算或者距离统计直接在这个坐标系下算出来的数值是度没有物理意义。要么在计算前投影到合适的平面坐标系要么用测地线算法。# 用 ogr2ogr 转成投影坐标系再做分析 ogr2ogr -f GPKG hangzhou-roads-proj.gpkg hangzhou-roads.geojson \ -t_srs EPSG:32651EPSG:32651 是 UTM 51 带覆盖华东一带。选带号的时候看经度每 6 度一个带中央经线从 0 度开始带号大致是经度除以 6 再加 31。这个换算过程看起来麻烦但只用算一次写进脚本就行。4.3 加载到 GIS 软件时的性能取舍直接在 GIS 软件里打开.osm.pbf是支持的因为底层驱动可以解析它。但性能不一定好尤其是全要素加载的时候。原因在于 OSM 的数据模型是多层嵌套的点、线、面关系、以及它们之间的引用关系。驱动解析时要把这些引用关系重新拼装CPU 开销很大。我的做法是先在命令行把范围裁小、把要素过滤干净再丢进 GIS。比如一个省的路网 PBF 可能有 500 MB裁到市区、过滤掉非道路要素之后可能只剩 20 MB加载速度差几十倍。另外驱动本身有一个配置文件可以控制哪些标签被解析成字段。默认配置会解析一大堆你用不上的键导致属性表宽到没法看。按需精简这个配置能让属性表瘦身一半以上也让后续的字段筛选轻松很多。5. 瓦片与离线底图的获取方式前面讲的都是矢量数据。如果你的目标是一张能在没网环境下显示的地图那你要的是瓦片思路完全不同。5.1 瓦片编号规则与范围估算瓦片金字塔的编号规则是固定的。给定缩放级别z和经纬度瓦片编号可以用下面这个公式算出来import math def lonlat_to_tile(lon, lat, z): n 2 ** z x int((lon 180.0) / 360.0 * n) lat_rad math.radians(lat) y int((1.0 - math.asinh(math.tan(lat_rad)) / math.pi) / 2.0 * n) return x, y # 示例杭州附近z14 print(lonlat_to_tile(120.15, 30.27, 14))知道这个公式的意义在于估算工作量。一个矩形范围在z级别下需要的瓦片数量是数量 (x_max - x_min 1) × (y_max - y_min 1)每往下一级瓦片数翻四倍。z14 下覆盖一个市区的瓦片可能是几百张z18 下同样的范围就变成几万张。很多人做离线包的时候只算了几百张做到一半发现要下几万张时间预算直接崩掉。先把数量算出来再决定要不要降级是必要的功课。5.2 自建瓦片服务的思路公共瓦片服务的资源是有限的做大规模离线包的时候不适合直接对公共端点做批量抓取。可行的替代方案是自建渲染服务拿前面下载的 PBF 数据用渲染引擎自己出图。整个链路大致是PBF 数据导入数据库 → 按瓦片按需查询 → 用样式文件渲染成矢量瓦片 → 前端或离线包消费。这条路的前期投入明显更大要装数据库、导入数据、配样式但跑通之后你就有了一套完全自主的出图能力想改配色改配色想加图层加图层还不用担心数据量的问题。导入数据库这一步是耗时大头。以省级数据为例导入可能需要几个小时全球数据要以天计。索引建好之后查询和渲染就很快了。5.3 瓦片抓取的礼貌原则如果你确实只需要一小块区域的少量瓦片直接抓也不是不行但有几条底线要守住。第一遵守服务方的使用条款。公共瓦片服务通常会在文档里写明允许的使用量级和禁止的行为批量下载整座城市这种做法基本都在禁止之列。第二设置合理的请求间隔不要并发几十个线程去轰。第三填一个能识别身份的请求头让对方能联系到你这既是礼貌也是出事时的自保。第四能缓存就缓存同一张瓦片不要反复请求。对绝大多数项目来说更稳妥的路径是矢量数据用前面几章讲的方法拿到本地瓦片在自己机器上渲染一次投入长期省心。抓取只是在只要几张图这种极小需求下才合理。6. 常见问题与排查速查前面几章讲的是正常流程。实际做下来出问题的概率远大于一次成功。下面这些是我踩过的、也算比较有代表性的几个坑。6.1 下载与文件层面的坑下载中断导致文件损坏是最常见的。大文件下载到一半断了文件大小看起来差不多但解压报错或者解析到一半崩掉。用支持断点续传的命令行工具比网页下载靠谱得多wget -c https://example.org/region-latest.osm.pbf-c参数是续传的关键。断了重新执行同一条命令就行不用从头开始。文件其实很小但加载很慢十有八九是把.osmXML当成了.osm.pbf。前者是纯文本体积能大到后者的五到十倍。用文件头或者文件大小快速判断一下能省很多排查时间。另外还有一种情况是文件其实是空的原因就是前面说的 bbox 参数顺序写反了地理范围落在了没有数据的地方。磁盘空间不够导致处理中途失败。转换格式的时候中间文件、临时文件、输出文件会同时占用空间峰值可能是最终文件的好几倍。动手前先用df -h看一眼剩余空间留出三倍余量比较稳。6.2 数据内容层面的坑行政区划匹配不到。用名称去匹配区域的时候同名地名非常常见。稳妥的做法是先查 id 再按 id 取数别用名字硬匹配。OSM 里每个区域都有一个稳定的数字 id一旦查到就可以长期复用。关系relation要素导出异常。面要素在 OSM 里是用关系表达的包含外环和内环洞。有些转换工具处理内环的时候会丢洞导致本该中间空的区域被填满。遇到这种情况要么换工具要么先检查一下原始数据里内环的成员角色是否正确。中文名称拿不到。OSM 的名称标签分主标签和语言子标签。主标签name存的是当地最常见的写法中文名称通常在name:zh里。做中文专题图的时候要显式指定用哪个字段否则可能出现一半中文一半外语的情况。城市边界、道路名称都存在这个问题。6.3 性能与渲染层面的坑属性表宽到无法操作。前面提过默认解析会带出一大堆用不上的标签列。处理办法是精简驱动配置文件或者转换的时候只用tags-filter保留你关心的键。空间索引缺失导致查询极慢。GeoJSON 和 Shapefile 加载时通常会自动建索引但如果你是自己拼装的数据或者从 CSV 转过来的一定要手动建索引。GPKG 格式可以显式创建空间索引建不建索引在筛选操作上的速度差几十倍。文字标注不显示或乱码。这一般是字体问题跟数据无关。渲染时要用支持目标语言的字体中文字体缺失是最常见的原因。现象高概率原因快速验证方式解析报错、文件损坏下载中断-c续传后重试加载极慢、文件超大误用 XML 格式看文件大小和头部导出结果为空bbox 参数顺序写反检查经纬度顺序面要素中间被填满内环丢失检查 relation 成员角色属性表几十列未精简驱动配置看配置文件解析列表查询筛选慢缺空间索引GPKG 里建索引后重测中文标注不显示字体缺失换含中文字体的渲染配置最后分享一个我自己用了很多年的小习惯每次下完数据在同目录下顺手建一个文本文件记下三行信息——下载日期、数据源地址、范围参数。听起来很幼稚但当你半年后回头看一堆名字叫data、data2、data_new的文件夹时这三行字能救你的命。