Skip to content

Commit ae12e95

Browse files
[feature]L.TiledLayer支持视野范围外预加载瓦片
AI-GEN: 70% Cursor
1 parent 8dda06a commit ae12e95

11 files changed

Lines changed: 133 additions & 2 deletions

src/leaflet/core/Base.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import L from "leaflet";
55
import './Proj4Leaflet';
66
import './TileLayerCache';
7+
import './TileLayerBuffer';
78
import Attributions from './Attributions';
89

910
L.Control.Attribution.include({
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/* Copyright© 2000 - 2026 SuperMap Software Co.Ltd. All rights reserved.
2+
* This program are made available under the terms of the Apache License, Version 2.0
3+
* which accompanies this distribution and is available at http://www.apache.org/licenses/LICENSE-2.0.html.*/
4+
import L from 'leaflet';
5+
6+
/**
7+
* 扩展 Leaflet `L.TileLayer`,在视野外预加载若干圈同级瓦片,减少平移时的空白块。
8+
* `_getTiledPixelBounds` 定义在 `L.GridLayer`,TileLayer 继承该方法;此处覆盖后仅对 TileLayer 及其子类生效。
9+
*
10+
* @param {number} [options.bufferTiles=0] - 视野外预加载的瓦片圈数。默认 0 表示不预加载;为 1 时上下左右各多加载一圈。支持小数。
11+
*/
12+
var originalGetTiledPixelBounds = L.GridLayer.prototype._getTiledPixelBounds;
13+
14+
L.TileLayer.mergeOptions({
15+
bufferTiles: 0
16+
});
17+
18+
L.TileLayer.include({
19+
_getTiledPixelBounds: function (center) {
20+
var pixelBounds = originalGetTiledPixelBounds.call(this, center);
21+
var bufferTiles = this.options.bufferTiles;
22+
if (bufferTiles > 0) {
23+
var pad = this.getTileSize().multiplyBy(bufferTiles);
24+
pixelBounds = new L.Bounds(pixelBounds.min.subtract(pad), pixelBounds.max.add(pad));
25+
}
26+
return pixelBounds;
27+
}
28+
});

src/leaflet/core/TileLayerCache.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import L from 'leaflet';
55

66
/**
7-
* 扩展 Leaflet `L.TileLayer`,提供与 Mapbox GL 类似的瓦片填充
7+
* 扩展 Leaflet `L.TileLayer`:
88
* 1. 放大时沿用原生父级瓦片占位,不提前拆掉原级别。
99
* 2. 额外保留邻近缩放级别的已加载瓦片(含放大前四周的瓦片)。
1010
* 3. 被裁掉的瓦片进入内存缓存;再次需要时立刻复用,缺失的再发请求。

src/leaflet/mapping/BaiduTileLayer.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import Attributions from '../core/Attributions'
1818
* @param {L.LatLngBounds} [options.bounds=L.latLngBounds([-85.0511287798, -180],[85.0511287798, 180])] - 显示范围。
1919
* @param {L.Browser} [options.retina=L.Browser.retina] - 浏览器显示分辨率。
2020
* @param {string} [options.tileProxy] - 服务代理地址。
21+
* @param {number} [options.bufferTiles=0] - 视野外预加载的瓦片圈数。默认 0 表示不预加载;为 1 时上下左右各多加载一圈,可减少平移时的空白块。支持小数。
2122
* @param {boolean} [options.tileCache=false] - 是否启用客户端瓦片内存缓存。开启后,缩放时优先使用已缓存瓦片填充(放大后再缩小可避免空白块),再按需请求新瓦片。
2223
* @param {number} [options.tileCacheZoomRange=5] - 动态缓存相对当前视野的缩放范围倍数。缓存上限为视野瓦片数 × tileCacheZoomRange。
2324
* @param {number} [options.maxOverzooming=8] - 缩放时保留父级(更低级别)瓦片的深度,用于放大过程中的占位填充。

src/leaflet/mapping/CloudTileLayer.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import Attributions from '../core/Attributions'
1818
* @param {number} [options.minZoom=3] - 最小缩放级别。
1919
* @param {number} [options.maxZoom=18] - 最大缩放级别。
2020
* @param {string} [options.mapName='quanguo'] - 地图名称。
21+
* @param {number} [options.bufferTiles=0] - 视野外预加载的瓦片圈数。默认 0 表示不预加载;为 1 时上下左右各多加载一圈,可减少平移时的空白块。支持小数。
2122
* @param {boolean} [options.tileCache=false] - 是否启用客户端瓦片内存缓存。开启后,缩放时优先使用已缓存瓦片填充(放大后再缩小可避免空白块),再按需请求新瓦片。
2223
* @param {number} [options.tileCacheZoomRange=5] - 动态缓存相对当前视野的缩放范围倍数。缓存上限为视野瓦片数 × tileCacheZoomRange。
2324
* @param {number} [options.maxOverzooming=8] - 缩放时保留父级(更低级别)瓦片的深度,用于放大过程中的占位填充。

src/leaflet/mapping/ImageTileLayer.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import { SecurityManager } from '@supermapgis/iclient-common/security/SecurityMa
2626
* @param {string} [options.format='png'] - 瓦片表述类型,瓦片格式目前支持 png、jpg 和 webp 三种格式。
2727
* @param {boolean} [options.transparent=true] - 瓦片是否透明。默认透明。
2828
* @param {boolean} [options.cacheEnabled=true] - 是否启用服务端缓存。
29+
* @param {number} [options.bufferTiles=0] - 视野外预加载的瓦片圈数。默认 0 表示不预加载;为 1 时上下左右各多加载一圈,可减少平移时的空白块。支持小数。
2930
* @param {boolean} [options.tileCache=false] - 是否启用客户端瓦片内存缓存。与 cacheEnabled(服务端缓存)不同;开启后缩放时优先使用已缓存瓦片填充(放大后再缩小可避免空白块),再按需请求新瓦片。
3031
* @param {number} [options.tileCacheZoomRange=5] - 动态缓存相对当前视野的缩放范围倍数。缓存上限为视野瓦片数 × tileCacheZoomRange。
3132
* @param {number} [options.maxOverzooming=8] - 缩放时保留父级(更低级别)瓦片的深度,用于放大过程中的占位填充。

src/leaflet/mapping/TiandituTileLayer.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import Attributions from '../core/Attributions';
2323
* @param {Array.<number>} [options.subdomains=[0, 1, 2, 3, 4, 5, 6, 7]] - 子域名数组。
2424
* @param {string} [options.attribution='Map Data <a href='https://www.tianditu.gov.cn' target='_blank'><img style='background-color:transparent;bottom:2px;opacity:1;' src='https://api.tianditu.gov.cn/img/map/logo.png' width='53px' height='22px' opacity='0'></a>'] - 版权描述信息。
2525
* @param {string} [options.noWrap=true] - 图层是否 X 方向平铺。
26+
* @param {number} [options.bufferTiles=0] - 视野外预加载的瓦片圈数。默认 0 表示不预加载;为 1 时上下左右各多加载一圈,可减少平移时的空白块。支持小数。
2627
* @param {boolean} [options.tileCache=false] - 是否启用客户端瓦片内存缓存。开启后,缩放时优先使用已缓存瓦片填充(放大后再缩小可避免空白块),再按需请求新瓦片。
2728
* @param {number} [options.tileCacheZoomRange=5] - 动态缓存相对当前视野的缩放范围倍数。缓存上限为视野瓦片数 × tileCacheZoomRange。
2829
* @param {number} [options.maxOverzooming=8] - 缩放时保留父级(更低级别)瓦片的深度,用于放大过程中的占位填充。

src/leaflet/mapping/TileLayer.WMTS.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import { Util as CommonUtil } from '@supermapgis/iclient-common/commontypes/Util
2626
* @param {string} [options.version='1.0.0'] - 版本。
2727
* @param {string} [options.attribution] - 版权描述信息。
2828
* @param {string} [options.noWrap=true] - 图层是否 X 方向平铺。
29+
* @param {number} [options.bufferTiles=0] - 视野外预加载的瓦片圈数。默认 0 表示不预加载;为 1 时上下左右各多加载一圈,可减少平移时的空白块。支持小数。
2930
* @param {boolean} [options.tileCache=false] - 是否启用客户端瓦片内存缓存。开启后,缩放时优先使用已缓存瓦片填充(放大后再缩小可避免空白块),再按需请求新瓦片。
3031
* @param {number} [options.tileCacheZoomRange=5] - 动态缓存相对当前视野的缩放范围倍数。缓存上限为视野瓦片数 × tileCacheZoomRange。
3132
* @param {number} [options.maxOverzooming=8] - 缩放时保留父级(更低级别)瓦片的深度,用于放大过程中的占位填充。

src/leaflet/mapping/TiledMapLayer.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import Attributions from '../core/Attributions';
4040
* @param {Array.<number>|Array.<string>} [options.subdomains] - 子域名数组。
4141
* @param {ChartSettingS57|ChartSettingS100} [options.chartSetting] - 海图显示参数设置类,用于管理海图显示环境,包括海图的显示模式、显示类型名称、颜色模式、安全水深线等各种显示风格。
4242
* @param {number} [options.overflowTiles = 0] - 绘制超出图层范围的瓦片圈数。常用于位于地图边缘的要素符号显示不全的场景。默认值为0,表示不绘制超出图层范围的瓦片。当 options.noWrap 为 true 时,overflowTiles有效。
43+
* @param {number} [options.bufferTiles=0] - 视野外预加载的瓦片圈数。默认 0 表示不预加载;为 1 时上下左右各多加载一圈,可减少平移时的空白块。支持小数。
4344
* @param {boolean} [options.tileCache=false] - 是否启用客户端瓦片内存缓存。与 cacheEnabled(服务端缓存)不同;开启后缩放时优先使用已缓存瓦片填充(放大后再缩小可避免空白块),再按需请求新瓦片。
4445
* @param {number} [options.tileCacheZoomRange=5] - 动态缓存相对当前视野的缩放范围倍数。缓存上限为视野瓦片数 × tileCacheZoomRange。
4546
* @param {number} [options.maxOverzooming=8] - 缩放时保留父级(更低级别)瓦片的深度,用于放大过程中的占位填充。
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import '../../../src/leaflet/core/Base';
2+
import { tiledMapLayer } from '../../../src/leaflet/mapping/TiledMapLayer';
3+
import { baiduTileLayer } from '../../../src/leaflet/mapping/BaiduTileLayer';
4+
import { wmtsLayer } from '../../../src/leaflet/mapping/TileLayer.WMTS';
5+
import L from 'leaflet';
6+
7+
var PIXEL =
8+
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==';
9+
10+
describe('TileLayerBuffer', () => {
11+
var testDiv, map;
12+
13+
beforeAll(() => {
14+
testDiv = document.createElement('div');
15+
testDiv.style.width = '256px';
16+
testDiv.style.height = '256px';
17+
document.body.appendChild(testDiv);
18+
map = L.map(testDiv, {
19+
fadeAnimation: false,
20+
zoomAnimation: false,
21+
markerZoomAnimation: false,
22+
center: [0, 0],
23+
zoom: 2,
24+
maxZoom: 18
25+
});
26+
});
27+
28+
afterEach(() => {
29+
map.eachLayer(function (layer) {
30+
map.removeLayer(layer);
31+
});
32+
map.setView([0, 0], 2, { animate: false });
33+
});
34+
35+
afterAll(() => {
36+
map.remove();
37+
document.body.removeChild(testDiv);
38+
});
39+
40+
function pixelBoundsSize(layer) {
41+
var bounds = layer._getTiledPixelBounds(map.getCenter());
42+
return L.point(bounds.max.x - bounds.min.x, bounds.max.y - bounds.min.y);
43+
}
44+
45+
function tileRange(layer) {
46+
return layer._pxBoundsToTileRange(layer._getTiledPixelBounds(map.getCenter()));
47+
}
48+
49+
it('L.TileLayer 默认关闭视野外预加载', () => {
50+
var layer = L.tileLayer(PIXEL);
51+
expect(layer.options.bufferTiles).toBe(0);
52+
});
53+
54+
it('mapping 下 TileLayer 子类继承 bufferTiles 默认值', () => {
55+
var tiled = tiledMapLayer('http://localhost/map');
56+
var baidu = baiduTileLayer();
57+
var wmts = wmtsLayer('http://localhost/wmts', { layer: 'test', tilematrixSet: 'c' });
58+
expect(tiled.options.bufferTiles).toBe(0);
59+
expect(baidu.options.bufferTiles).toBe(0);
60+
expect(wmts.options.bufferTiles).toBe(0);
61+
});
62+
63+
it('bufferTiles 为 0 时不扩展像素范围', () => {
64+
var layer = L.tileLayer(PIXEL, { bufferTiles: 0 }).addTo(map);
65+
var size = map.getSize();
66+
var boundsSize = pixelBoundsSize(layer);
67+
expect(Math.abs(boundsSize.x - size.x)).toBeLessThan(1);
68+
expect(Math.abs(boundsSize.y - size.y)).toBeLessThan(1);
69+
});
70+
71+
it('bufferTiles 为 1 时四边各扩展一圈瓦片', () => {
72+
var layer0 = L.tileLayer(PIXEL, { bufferTiles: 0 }).addTo(map);
73+
var layer1 = L.tileLayer(PIXEL, { bufferTiles: 1 }).addTo(map);
74+
var tileSize = layer1.getTileSize();
75+
var size0 = pixelBoundsSize(layer0);
76+
var size1 = pixelBoundsSize(layer1);
77+
expect(size1.x).toBe(size0.x + tileSize.x * 2);
78+
expect(size1.y).toBe(size0.y + tileSize.y * 2);
79+
80+
var range0 = tileRange(layer0);
81+
var range1 = tileRange(layer1);
82+
expect(range1.min.x).toBe(range0.min.x - 1);
83+
expect(range1.max.x).toBe(range0.max.x + 1);
84+
expect(range1.min.y).toBe(range0.min.y - 1);
85+
expect(range1.max.y).toBe(range0.max.y + 1);
86+
});
87+
88+
it('bufferTiles 支持小数圈数', () => {
89+
var layer0 = L.tileLayer(PIXEL, { bufferTiles: 0 }).addTo(map);
90+
var layerHalf = L.tileLayer(PIXEL, { bufferTiles: 0.5 }).addTo(map);
91+
var tileSize = layerHalf.getTileSize();
92+
var size0 = pixelBoundsSize(layer0);
93+
var sizeHalf = pixelBoundsSize(layerHalf);
94+
expect(sizeHalf.x).toBe(size0.x + tileSize.x);
95+
expect(sizeHalf.y).toBe(size0.y + tileSize.y);
96+
});
97+
});

0 commit comments

Comments
 (0)