经纬志
首页 / 实战手册 / 瓦片

TMS 的 Y 轴反着数,底图南北颠倒

先看结论

瓦片全部能加载出来,拼接也严丝合缝,就是整张图上下翻转。

最后验证 2026-11-03 · 多技术栈 · 更新 2026-10-29 · 引用本文

现象

用本地 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。

排查过程

  1. 打开一张已知位置的瓦片,用编号反算经纬度,看南北是否相反。
  2. 取 z 级、x 列最上方的瓦片,若文件名里的 y 接近 2^z,就是 TMS。
  3. 看服务文档里写的是 TMS 还是 XYZ,GeoServer 的 GWC 两种都提供。
  4. 在浏览器里手工请求 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 编号不符');

适用版本

库 / 服务版本写法
Leaflet1.9.x{-y} 或 tms: true
OpenLayers9.x需自定义 tileUrlFunction
GeoServer GWC2.20 及以上/gwc/service/tms/1.0.0 为 TMS 编号
Cesium1.100 及以上UrlTemplateImageryProvider 用 {reverseY}
引用本文

经纬志(2026)。《TMS 的 Y 轴反着数,底图南北颠倒》。https://www.jingweizhi.com/cookbook/tms-y-flip.html(访问日期 2026-11-03)。