首页 / 实战手册 / 瓦片
TMS 的 Y 轴反着数,底图南北颠倒
先看结论
瓦片全部能加载出来,拼接也严丝合缝,就是整张图上下翻转。
现象
用本地 tiles/{z}/{x}/{y}.png 目录挂载瓦片,18 级下北京被拼到了南半球,
太行山的走向倒过来,地图整体上下颠倒但左右正常。
同一批瓦片用 QGIS 的 XYZ 图层打开是正的,用「TMS」图层打开也正常。
原因
两套编号的 Y 轴原点不同:
| 规范 | 原点 | 18 级第 6 行(北京)的 y |
|---|---|---|
| XYZ(Google / OSM) | 左上角,向下递增 | 约 105000 |
| TMS | 左下角,向上递增 | 约 157000 |
换算关系:y_tms = 2^z - 1 - y_xyz。
18 级共 262144 行,两者相加恒为 262143。
排查过程
- 打开一张已知位置的瓦片,用编号反算经纬度,看南北是否相反。
- 取
z级、x列最上方的瓦片,若文件名里的 y 接近2^z,就是 TMS。 - 看服务文档里写的是
TMS还是XYZ,GeoServer 的 GWC 两种都提供。 - 在浏览器里手工请求
y=0与y=2^z-1两张,看哪张是南极。
// 18 级
const z = 18, n = Math.pow(2, z); // 262144
// 北京某瓦片 XYZ 编号
const yXyz = 105000;
const yTms = n - 1 - yXyz; // 157143
解决方案
Leaflet 支持 {-y} 占位符自动翻转,这是最简单的写法。
// TMS 服务:用 {-y}
L.tileLayer('/tiles/{z}/{x}/{-y}.png', {
tileSize: 256,
minZoom: 3,
maxZoom: 18,
tms: false
}).addTo(map);
// XYZ 服务:普通 {y},且 tms 保持 false
L.tileLayer('/tiles/{z}/{x}/{y}.png', { tileSize: 256 }).addTo(map);
OpenLayers 的 XYZ 数据源默认按 XYZ 解释,
接 TMS 时把 tileUrlFunction 里的 y 翻转。
new TileLayer({
source: new XYZ({
tileUrlFunction: (coord) => {
const [z, x, y] = coord;
const n = Math.pow(2, z);
return `/tiles/${z}/${x}/${n - 1 - y}.png`;
}
})
});
验证
叠加一个已知的 OSM 底图,两层的道路应完全重合,不再出现上下颠倒;
取 z=18, x=... , y=105000 的瓦片,中心应为北京城区。
console.assert(n - 1 - 105000 === 157143, '翻转公式不对');
// 用同一坐标反算瓦片编号,与文件名比对
const t = tileIndex(116.397, 39.909, 18);
console.assert(t[1] === 105000, 'XYZ 编号不符');
适用版本
| 库 / 服务 | 版本 | 写法 |
|---|---|---|
| Leaflet | 1.9.x | {-y} 或 tms: true |
| OpenLayers | 9.x | 需自定义 tileUrlFunction |
| GeoServer GWC | 2.20 及以上 | /gwc/service/tms/1.0.0 为 TMS 编号 |
| Cesium | 1.100 及以上 | UrlTemplateImageryProvider 用 {reverseY} |
经纬志(2026)。《TMS 的 Y 轴反着数,底图南北颠倒》。https://www.jingweizhi.com/cookbook/tms-y-flip.html(访问日期 2026-11-03)。