瓦片图层与底图切换
底图是地图的第一层内容,Leaflet 把它抽象成 TileLayer:
给出一个带占位符的 URL 模板,剩下的按视野计算、缓存、拼接都由图层完成。
瓦片金字塔与 z/x/y
瓦片服务把世界地图按缩放级别切成正方形图片,第 0 级是一张 256×256 的图, 每升一级在横纵两个方向各切一刀,瓦片数量变成四倍。地址里的三个占位符因此有明确含义:
| 占位符 | 含义 | 取值范围 |
|---|---|---|
{z} | 缩放级别 | 一般为 0–19 |
{x} | 列号,从西经 180° 起算 | 0 到 2z−1 |
{y} | 行号,从北纬 85.05° 起算 | 0 到 2z−1 |
{s} | 子域名,用于突破浏览器并发限制 | 由 subdomains 指定 |
{r} | 高清屏标记 | 普通屏为空串,Retina 屏为 @2x |
{y} 的方向是这套体系里最容易出错的地方。Web 墨卡托瓦片从北向南编号,
而 TMS 规范从南向北编号,两者正好上下翻转。接入只支持 TMS 的服务时,
在图层参数里加 tms: true,Leaflet 会自动换算,不需要自己拼地址。
TileLayer 的常用参数
| 参数 | 默认 | 说明 |
|---|---|---|
minZoom / maxZoom | 0 / 18 | 该图层自身可用的级别,与地图的缩放范围是两回事 |
subdomains | 'abc' | 替换 {s} 的字符集合 |
tileSize | 256 | 瓦片边长,高清瓦片常为 512 |
zoomOffset | 0 | 请求级别与地图级别的差值,512 瓦片通常设 −1 |
detectRetina | false | 高清屏时自动把 tileSize 减半并提升请求级别 |
noWrap | false | 关闭东西方向无限重复,缩到很小的时候不会出现多份世界 |
bounds | — | 只在该矩形范围内请求瓦片,配合 noWrap 限制服务范围 |
errorTileUrl | '' | 单张瓦片失败时替换成的图片地址 |
crossOrigin | false | 需要把瓦片画进 canvas 或做像素处理时必须打开 |
keepBuffer | 2 | 视野外多保留几圈瓦片,拖动回来时不用重新请求 |
updateWhenIdle | 移动端 true | 拖动结束后才加载新瓦片,省流量但会短暂留白 |
opacity | 1 | 图层透明度,叠两层底图做对比时有用 |
className | '' | 加到瓦片容器上的类名,可用 CSS 滤镜改色 |
切换底图
标准做法是把所有底图作为 baseLayers 交给 L.control.layers,
控件会自动保证同一时间只有一层可见,并同步右下角的版权信息:
var osm = L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19,
attribution: '© OpenStreetMap contributors'
});
var cartoLight = L.tileLayer(
'https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png', {
subdomains: 'abcd',
maxZoom: 20,
attribution: '© OpenStreetMap contributors © CARTO'
}
);
var map = L.map('map', {
center: [39.907359, 116.391263],
zoom: 11,
layers: [osm]
});
L.control.layers({
'OpenStreetMap': osm,
'Carto 浅色': cartoLight
}, null, { position: 'topright' }).addTo(map);
自己写按钮切换时要多做两件事:移除旧图层、确认新图层的 maxZoom 不小于当前地图级别。
只 addLayer 不移除,旧底图会留在下面继续占用请求,而且版权信息会叠成一串。
国内瓦片与坐标偏移
天地图、高德、腾讯都提供 XYZ 瓦片地址,替换的只是 URL 模板。差别集中在两处:
- 天地图需要
tk参数,影像层用img_w(经纬度投影)或img_c(墨卡托), 图层代码不同,不能混用。 - 高德、腾讯使用 GCJ-02 坐标。把 WGS84 的 GPS 点直接标上去,整体会偏移几百米, 需要在数据侧做坐标转换,而不是调整瓦片地址。
先确认底图坐标系,再决定要不要转换。同一份业务数据通常只能配一套底图。 底图换了、点没换,偏移就会立刻出现。
瓦片加载失败怎么查
底图发灰、出现空白方格,说明瓦片请求没有成功。监听 tileerror 统计失败次数,
比盯着控制台更直观:
var failed = 0;
tiles.on('tileerror', function (e) {
failed++;
if (failed === 3) {
console.warn('瓦片连续失败,检查网络或服务地址', e.tile.src);
}
});
map.on('baselayerchange', function (e) {
failed = 0;
console.log('当前底图', e.name);
});
常见原因按出现频率排列:
- 模板里的
{s}没有对应subdomains,请求到了不存在的子域。 - 服务要求 Referer 白名单或 Key,本地
file://打开时被拒绝。 - 当前缩放级别超过图层
maxZoom,没有瓦片可请求。 - 跨域图片被浏览器拦截,多见于需要读取像素做滤镜的场景。
性能与体验
keepBuffer 决定视野外缓存几圈瓦片。调到 4 以上,快速拖动回原位时几乎不闪白,
代价是内存里多留几十张图。移动端默认 updateWhenIdle: true,
拖动过程中不发请求,松手才加载;桌面端保持 false,拖动时瓦片连续补齐。
叠加多层瓦片(底图 + 注记 + 专题图)时,给上层设置 zIndex,
不要依赖添加顺序。高缩放级别下瓦片数量增长很快,把地图 maxZoom 设成实际需要的值,
能避免用户缩到 22 级看到一片空白。
经纬志(2026)。《瓦片图层与底图切换》。https://www.jingweizhi.com/leaflet/tutorial/tile-layer.html(访问日期 2026-11-03)。