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

缩小到 3 级时地图重复了三次,noWrap 没开

先看结论

缩到最小级别,屏幕上并排出现三个中国,左右两边还是镜像拼接的。

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

现象

地图容器宽 1600 像素,缩放到 3 级时, 屏幕上同时出现三份中国地图,横向排列; 左右两份与中间的不连续,接缝处是海岸线直接拼内陆。

拖动地图时,重复的图案会跟着滚动,缩小到 2 级时变成五份。

原因

Leaflet 默认开启世界环绕(wrap):当 2^z × tileSize 小于视口宽度时, 会横向重复平铺瓦片,让地图看起来「无限宽」。 3 级时全球宽度为 8 × 256 = 2048 像素,不足以填满 1600 以上的容器多次, 于是出现重复。

只在中国范围使用的瓦片服务,没有全球数据, 重复出来的部分要么是同一张图,要么是 404,视觉上很怪。

排查过程

  1. 算当前级别的世界宽度 2^z × tileSize,与容器宽度比较。
  2. 看是否有 noWrap 配置,默认是 false。
  3. 检查 bounds 是否设置,设置了 bounds 会自动限制到该范围。
  4. 看 minZoom 是否太小,很多国内底图最小只支持 3 或 4 级。
const worldWidth = Math.pow(2, map.getZoom()) * 256;
console.log(worldWidth, map.getSize().x);
// 3 级: 2048 vs 1600  -> 世界比视口宽,不应重复
// 2 级: 1024 vs 1600  -> 世界比视口窄,必然重复

解决方案

国内范围底图关闭环绕,并限制最小缩放级别。

L.tileLayer('https://tile.example.com/{z}/{x}/{y}.png', {
  minZoom: 4,
  maxZoom: 18,
  noWrap: true,
  bounds: [[3.5, 73.0], [53.6, 135.1]]   // 中国范围
}).addTo(map);

// 同时限制地图本身的最小级别
const map = L.map('map', {
  minZoom: 4,
  maxBounds: [[3.5, 73.0], [53.6, 135.1]],
  maxBoundsViscosity: 0.8
});
// OpenLayers:在 tileGrid extent 上限制
new TileLayer({
  source: new XYZ({
    url: 'https://tile.example.com/{z}/{x}/{y}.png',
    wrapX: false,
    tileGrid: createXYZ({
      extent: getProjection('EPSG:3857').getExtent(),
      minZoom: 4,
      maxZoom: 18,
      tileSize: 256
    })
  })
});

验证

缩放到 4 级时画面只有一份中国,左右边缘是空白而不是重复图案; 继续缩小被 minZoom 拦住,不会出现 2 级。

console.assert(map.getZoom() >= 4, '仍可缩到 4 级以下');
console.assert(layer.options.noWrap === true, 'noWrap 未开');
// 各级世界宽度与视口宽度的关系
console.assert(Math.pow(2, 4) * 256 >= map.getSize().x, '4 级世界仍窄于视口');

适用版本

库版本说明
Leaflet1.9.xnoWrap 默认 false,bounds 单独设也可限制
OpenLayers9.xXYZ 的 wrapX 默认 true
Mapbox GL JS2.x / 3.x同样默认横向重复
引用本文

经纬志(2026)。《缩小到 3 级时地图重复了三次,noWrap 没开》。https://www.jingweizhi.com/cookbook/tile-nowrap-out-of-range.html(访问日期 2026-11-03)。