简介:面向地图开发与GIS应用场景的参数速查文档,聚焦谷歌地图、搜搜地图和天地图三类瓦片地图服务的关键配置,适合接入在线地图、制作切片或进行坐标投影转换的开发人员参考。文档以1个docx文件交付,压缩包约15KB,体量轻便,便于随项目资料归档检索。已有101人学习,说明其在瓦片地图参数对照中具有一定参考价值。内容围绕切片原点、全图范围、地图投影、轴方向、显示级别及比例尺集合展开,分别梳理WGS84 Web Mercator与China Geodetic Coordinate System 2000等差异,并列出0—17级、4—18级、1—18级等范围。读者可据此快速核对参数口径,为切片生成、API集成、数据转换和跨服务兼容排查提供依据。
1. 三套瓦片参数摆一起:坐标系原点差一位小数就能让地图整体偏移
上个月帮朋友排查一个离线地图错位的问题,现象很怪:底图能出来,标注也能出来,但放大到街道级之后,路网整体往右下角飘了几百米。他用的是从三家服务商各扒了一层的拼接方案,图源混着用,自己写着写着就串了参数。最后定位到的原因很简单——切片原点差了不到一米,Y 轴方向还是反的。瓦片地图参数这东西,看文档时觉得都是常识,真动手拼的时候,一个比例尺集合对不上、一个原点正负号写反,就能让你在半夜对着屏幕怀疑人生。这篇就把三套典型瓦片服务的参数拆开对比:切片原点、全图范围、地图投影、显示级别与比例尺集合,讲清楚它们为什么不一样、混用时要注意什么、怎么自己算一遍验证。GIS 开发和地图数据处理的同学可以直接拿去对照,做切片拼接和坐标转换的也能少走几段弯路。
2. 先看四个核心参数:切片原点、全图范围、投影、比例尺集合
2.1 切片原点为什么是最先要看的东西
切片原点决定了瓦片编号的起点,也就是整张世界地图被切格子的时候,左上角那个格子从哪个坐标开始数。业内对 Web Mercator 的常规做法是把原点放在投影坐标系的左上角,因为屏幕坐标 Y 轴朝下,瓦片行号往下递增,和这个原点配合起来最自然。但注意,所谓「左上角」在不同的服务里并不是同一个数。投影坐标下的理论边界是 ±20037508.342789244 左右,实际实现里各家取的精度不一样,写出来的值就有末位差异。
我在做的模拟项目里同时接了三种图源,一开始直接把原点当成常量写死,结果换源就错。后来改成按图源读配置,问题才消失。常见的思路是:原点不要靠记忆,靠配置;配置不要手抄,靠验证。验证方式很简单——取一个已知坐标点,用 0 级瓦片的范围反算一次,看算出来的瓦片号是不是 0。算出来是 0,原点就是对的;算出来是 1 或者 -1,说明原点或者轴方向出了问题。
2.2 全图范围和 Y 轴方向:最容易翻车的一对
三套服务的全图范围差异,比原点更能说明问题。谷歌地图的 Y 轴正方向向下,全图范围在投影坐标下是左下角到右上角一整个方形;搜搜地图的 Y 轴正方向向上,全图范围的写法就跟着换了一套符号;天地图则干脆不在投影坐标里写范围,直接用经纬度 -180 到 180、-90 到 90,原点也写成 (-180, 90)。
这三种写法背后是同一个逻辑:原点加上轴方向,才能确定瓦片编号的映射关系。只看范围不看轴方向,等于只看了半张说明书。我一般会这么做——把「原点 + 轴方向 + 全图范围」当成一个三元组记,缺一个就不下结论。血泪经验是,只要这三者里有一个是从别的图源抄来的,错位就是必然的,而且往往只在放大到一定级别后才明显,排查时极容易误判成投影转换写错。
| 服务 | 切片原点 | X 轴方向 | Y 轴方向 | 全图范围写法 |
|---|---|---|---|---|
| 谷歌地图 | (-20037508.342789199, 20037508.342789199) | 向右 | 向下 | 投影坐标方形范围 |
| 搜搜地图 | (-20037508.342789244, 20037508.342789091) | 向右 | 向上 | 投影坐标方形范围 |
| 天地图 | (-180, 90) | 向右 | 向下 | 经纬度 -180~180、-90~90 |
2.3 投影不同,参数就不能互抄
谷歌和搜搜都用 WGS84 Web Mercator,天地图用的是 China Geodetic Coordinate System 2000。前两者是同一套投影,参数差异主要是精度和实现习惯,理论上可以把瓦片编号对得上,只是原点末位差一点,严谨起来还是要按各自的数算。天地图不一样,原点直接落在经纬度上,全图范围也是经纬度,这意味着它的瓦片编号是在经纬度平面上切格子的,和 Web Mercator 的切片思路不是一回事。
所以「天地图能不能和另外两个混着拼」这个问题,答案不是一个能不能,而是看你在哪一层混。在经纬度层面混,需要先把 Web Mercator 的瓦片反投影回经纬度,再按天地图的规则重新编号;在投影坐标层面混,就要做一次投影转换。前者精度损失小一些,后者在高纬度地区会累积误差。我见过的做法是统一在经纬度层面做中间层,图源各自负责自己那一段,拼接层只认经纬度,这样换源的时候改动面最小。这个思路在模拟项目里验证过,代价是每次渲染多一次转换开销,但在级别不高、瓦片数量可控的情况下完全能接受。
2.4 比例尺集合要整段对,不是对首尾
比例尺集合是三套参数里最容易被忽略的部分。很多人只对首尾两个值,觉得中间差一点无所谓,实际上瓦片的级别映射就是靠这个集合逐级对应的。谷歌地图的集合从 1.6901635716026553E-9 起,到 2.2153311965710323E-4 收;搜搜地图从 2.7042617145642484E-8 起,到 4.4306623931420646E-4 收;天地图从 3.3803271432053105E-9 起,到 4.4306623931420646E-4 收。
盯一眼能看出来,搜搜的 4 到 18 级和谷歌的 2 到 16 级在数值上是重合的,天地图 1 到 18 级的前 18 个值也和谷歌有交集。这不是巧合,级别比例尺是按 2 的幂递推出来的,只要起点一致,后续自然对齐。对齐的部分可以互认,不对齐的部分必须各自算。常见的误用是拿级别号直接互转,比如把谷歌的 5 级当成搜搜的 5 级,结果就是差两级的错位,画面糊成一团。
3. 三家参数逐项对比:哪些能互认,哪些必须各算各的
3.1 谷歌与搜搜:同一投影下的精度差异
这两个放一起比最有价值,因为它们是同一套投影,差异全在实现细节上。原点的差距在末位,X 方向一个是 -20037508.342789199,一个是 -20037508.342789244,差了大概 4.5e-8 米,这个量级在实际渲染里可以忽略,但严谨起来不能直接等号。Y 方向更明显:谷歌是 20037508.342789199,搜搜是 20037508.342789091,而且搜搜的 Y 轴正方向向上,等于符号系统整个换了。
这带来的直接后果是行号的递增方向相反。谷歌的瓦片行号往下递增,搜搜的往上递增,同一个地理位置的瓦片行号大概率不一样。我一开始就是栽在这里,以为投影一样就能直接换,结果地图上下颠倒,还以为是渲染管线写反了。回过头看,判断依据应该落在轴方向上,而不是投影名称上。
| 对比项 | 谷歌地图 | 搜搜地图 | 能否互认 |
|---|---|---|---|
| 投影 | WGS84 Web Mercator | WGS84 Web Mercator | 可以 |
| 原点 X | -20037508.342789199 | -20037508.342789244 | 精度差可忽略 |
| 原点 Y | 20037508.342789199 | 20037508.342789091 | 数值接近但轴方向不同 |
| Y 轴方向 | 向下 | 向上 | 不可互认 |
| 级别范围 | 0-17 | 4-18 | 部分级别数值重合 |
级别范围那一栏值得多说一句。谷歌 0-17 级,搜搜 4-18 级,重合区间的比例尺数值是一致的。这意味着如果你在做级别映射,正确的换算是按比例尺值找对应级别,而不是按级别号加减。写代码的时候可以建一张「级别-比例尺」的字典,两边都按值查,就不用管级别号差几级了。
3.2 天地图:换了一套坐标系,就别硬拼
天地图的参数看着简单,原点 (-180, 90),范围 -180 到 180、-90 到 90,但简单恰恰是它难拼的原因——它在经纬度平面上切,和投影坐标平面不是同一个空间。在模拟项目里,我一开始想省事,直接拿经纬度当投影坐标去做像素换算,结果比例完全不对,一个 0 级瓦片铺不满屏幕,缩到小比例尺就成大花脸。
正确的做法是先明确你在哪一层做拼接。如果是渲染层,把另外两家的瓦片反投影回经纬度,和天地图对齐;如果是数据层,统一转成同一种投影再切。前者改动小、精度够用,后者一劳永逸但工程量大。我倾向前者,因为瓦片本身是很难回头重切的,加一层转换比重新造一套切片便宜得多。
提示:跨坐标系拼接时,先拿一个已知地标做验证点,算出三个图源各自对应的瓦片行列号,比对是否落在同一地理区域,再决定在哪一层做转换。
3.3 用一段脚本把参数算一遍,别只看文档
参数对不对,算一遍最踏实。下面这段是我常用的验证脚本,思路是拿参数反算瓦片行列号,再用同一坐标去比对三套服务的结果。脚本里所有数值都来自前面列的参数,没有额外编造。
import math # 三套服务的参数,原点与轴方向决定编号映射 SERVICES = { "google": { "origin": (-20037508.342789199, 20037508.342789199), "y_down": True, "max_level": 17, }, "soso": { "origin": (-20037508.342789244, 20037508.342789091), "y_down": False, # Y 轴向上,行号递增方向相反 "max_level": 18, }, "tianditu": { "origin": (-180.0, 90.0), # 经纬度平面,不是投影坐标 "y_down": True, "max_level": 18, "is_lonlat": True, }, } def tile_index(x, y, level, svc): cfg = SERVICES[svc] ox, oy = cfg["origin"] # 每级把整幅图分成 2^level 个格子,先求出单格跨度 span = (360.0 if cfg.get("is_lonlat") else 2 * 20037508.342789244) / (2 ** level) col = int((x - ox) / span) dy = (oy - y) if cfg["y_down"] else (y - oy) row = int(dy / span) return col, row # 用同一组坐标交叉验证,若三套结果落在相近格子内说明参数没串 print(tile_index(116.39, 39.91, 10, "tianditu")) print(tile_index(116.39, 39.91, 10, "google"))这段代码有几个地方需要说明。span 是按全图范围除以 2 的级别次幂算的,Web Mercator 那边用的是 ±20037508.342789244 的整幅宽度,天地图用的是 360 度经度跨度,两者单位不同,所以用 is_lonlat 区分。dy 那行是核心,y_down 为真时用原点减坐标,为假时反过来,这样搜搜的向上轴也能算对行列号。参数改起来很简单,换服务就换 key,级别按需调,但注意天地图传的是经纬度,另外两家要先转投影坐标再传,否则算出来的格子没有意义。
跑完之后如果三套结果对应的地理区域一致,说明参数关系理解对了;如果不一致,先看轴方向,再看单位,最后才怀疑精度。这个顺序能省掉八成排查时间。
4. 避坑与排查:拼接瓦片时最容易踩的五个坑
4.1 坑一:把 Y 轴方向当成全局常量
现象是换图源后地图整体上下颠倒,或者行号对不上、瓦片错位一整行。原因是代码里把 Y 轴向下写死,而搜搜地图是向上的,符号一错整条映射就反了。解决方式是把轴方向做成配置项,和原点绑在一起,凡是算行号的地方都读这个配置,不在任何一处硬编码正负。我后来的习惯是,参数对象里必须有 y_down 字段,缺了就直接报错,宁可启动失败也不带着错跑。
4.2 坑二:级别号直接互转
现象是切到某个级别后画面模糊,或者明明放大了一级看起来没变化。原因是按级别号做加减,而三家级别范围的起点不同,谷歌 0 级、搜搜 4 级、天地图 1 级,同样的级别号对应的比例尺根本不是一回事。解决方式是建立「比例尺-级别」双向表,按比例尺值查级别,代码里禁止出现level + 2这类硬编码偏移。
4.3 坑三:拿天地图的经纬度当投影坐标用
现象是小比例尺下瓦片铺不满或者严重拉伸,放大后稍微正常一点。原因是天地图原点和范围都是经纬度,直接代入 Web Mercator 的公式,单位对不上,算出来的跨度差了十几万倍。解决方式是先判断当前图源属于哪套坐标平面,经纬度平面走一套公式,投影平面走另一套,中间需要时再做投影转换。
4.4 坑四:比例尺集合只看首尾
现象是部分级别正常、部分级别偏移,且偏移量随级别变化。原因是对比例尺集合做了首尾对齐,中间值其实不完全一致,级别映射在中间段就开始漂。解决方式是整段比,或者干脆按 2 的幂递推自己生成一份,和服务商给的对一遍,对不上的段落单独处理。
4.5 坑五:原点精度差异被当成错误
现象是发现末位差了几位小数,以为参数错了,回头去改配置,结果越改越乱。原因是各家实现精度本来就不同,末位差异在渲染层面可以忽略。解决方式是定一个容差,比如相对误差小于千分之一就当相同处理,把精力放在轴方向和坐标系上,别在末位上浪费夜里的时间。
注意:排查顺序建议固定为轴方向、坐标平面、级别映射、精度容差,从影响大的往影响小的查,不要一上来就怀疑浮点误差。
5. 落到一份参数配置表:怎么写才不怕换源
5.1 把参数写成配置而不是散落的常量
我踩完前面那些坑之后,做法是把三套参数整理成一份配置,每个图源一个对象,字段固定:原点、Y 轴方向、坐标平面类型、级别范围、比例尺集合。加载瓦片时只认这个对象,任何计算都从这里取数。这样做的好处是换源只是换一个 key,代码路径不变,出问题也只可能出在配置本身,排查面小得多。
配置里有个细节值得强调,比例尺集合要原样保留科学计数法的精度,别在序列化的时候被截断。我见过一次因为 JSON 解析丢精度,导致中间几级映射偏了半格,画面看着不糊但标注偏了几十米,查了很久才怀疑到精度上。稳妥的做法是集合按字符串存,用的时候再转浮点。
5.2 验证环节不能省
配置写完不等于对了,要有一个验证入口。我的做法是挑若干个已知地标,分别算出三个图源对应的瓦片行列号,人工比对是否落在同一区域。这个环节一次也就十分钟,但能挡掉绝大部分配置错误。模拟项目上线前跑了这一遍,当场发现天地图那段单位没转,省了上线后的一次回滚。
5.3 换源时的检查清单
换源之前先问四个问题:投影是不是同一套、原点是不是同一套符号系统、级别范围有没有交集、比例尺集合能不能逐级对上。四个都能答上来再动手改,答不上来就先补参数。这个清单我用到现在还没失效过,后悔药能少吃不少。
| 检查项 | 判断依据 | 不通过的后果 |
|---|---|---|
| 投影一致 | 投影名称与坐标单位 | 比例失真、拉伸 |
| 轴方向一致 | 行号递增方向 | 上下颠倒、错行 |
| 级别有交集 | 级别范围重叠区间 | 部分级别缺图 |
| 比例尺可对 | 逐级数值比对 | 中间级别偏移 |
5.4 一个具体技巧:用 0 级瓦片反推原点
最后留一个我自己常用的技巧。不管参数从哪来,先算 0 级瓦片的行列号,整幅世界地图在 0 级只有一张瓦片,行列号必须是 0。如果算出来是别的数,说明原点或者轴方向有问题;如果算出来是 0 但地图还是偏,问题就在级别映射或者投影转换上,不在原点。这个检查只用一行计算,却能第一时间把问题范围砍掉一半。从那以后我每次接新图源,都强制先用 0 级瓦片验一遍原点,再往下做别的,希望帮到你。
本文还有配套的精品资源,点击获取