TileLayer
# 概述
TileLayer
是 Layer
的子类,继承自GridLayer
基类,用于加载和显示瓦片地图。它提供了加载和显示瓦片地图的功能,支持自定义瓦片的 URL 格式和参数。
# 源码分析
# 源码实现
TileLayer
的源码实现如下:
export var TileLayer = GridLayer.extend({
options: {
minZoom: 0, // 最小缩放级别
maxZoom: 18, // 最大缩放级别
subdomains: "abc", // 子域名,用于分散请求,绕过浏览器的并发限制
errorTileUrl: "", // 错误瓦片的URL,加载失败时显示的备用图片URL
zoomOffset: 0, // 缩放偏移量,调整请求中的z值
tms: false,// 是否使用TMS格式,y轴反转
zoomReverse: false, // 是否反转缩放级别
detectRetina: false, // 是否检测视网膜显示,加载高分辨率瓦片
crossOrigin: false, // 控制跨域请求
referrerPolicy: false, // 防盗链策略
},
initialize: function (url, options) {
this._url = url;
options = Util.setOptions(this, options);
if (options.detectRetina && Browser.retina && options.maxZoom > 0) {
options.tileSize = Math.floor(options.tileSize / 2);
if (!options.zoomReverse) {
options.zoomOffset++;
options.maxZoom = Math.max(options.minZoom, options.maxZoom - 1);
} else {
options.zoomOffset--;
options.minZoom = Math.min(options.maxZoom, options.minZoom + 1);
}
options.minZoom = Math.max(0, options.minZoom);
} else if (!options.zoomReverse) {
options.maxZoom = Math.max(options.minZoom, options.maxZoom);
} else {
options.minZoom = Math.min(options.maxZoom, options.minZoom);
}
if (typeof options.subdomains === "string") {
options.subdomains = options.subdomains.split("");
}
this.on("tileunload", this._onTileRemove);
},
setUrl: function (url, noRedraw) {
if (this._url === url && noRedraw === undefined) {
noRedraw = true;
}
this._url = url;
if (!noRedraw) {
this.redraw();
}
return this;
},
createTile: function (coords, done) {
var tile = document.createElement("img");
DomEvent.on(tile, "load", Util.bind(this._tileOnLoad, this, done, tile));
DomEvent.on(tile, "error", Util.bind(this._tileOnError, this, done, tile));
if (this.options.crossOrigin || this.options.crossOrigin === "") {
tile.crossOrigin =
this.options.crossOrigin === true ? "" : this.options.crossOrigin;
}
if (typeof this.options.referrerPolicy === "string") {
tile.referrerPolicy = this.options.referrerPolicy;
}
tile.alt = "";
tile.src = this.getTileUrl(coords);
return tile;
},
getTileUrl: function (coords) {
var data = {
r: Browser.retina ? "@2x" : "",
s: this._getSubdomain(coords),
x: coords.x,
y: coords.y,
z: this._getZoomForUrl(),
};
if (this._map && !this._map.options.crs.infinite) {
var invertedY = this._globalTileRange.max.y - coords.y;
if (this.options.tms) {
data["y"] = invertedY;
}
data["-y"] = invertedY;
}
return Util.template(this._url, Util.extend(data, this.options));
},
_tileOnLoad: function (done, tile) {
if (Browser.ielt9) {
setTimeout(Util.bind(done, this, null, tile), 0);
} else {
done(null, tile);
}
},
_tileOnError: function (done, tile, e) {
var errorUrl = this.options.errorTileUrl;
if (errorUrl && tile.getAttribute("src") !== errorUrl) {
tile.src = errorUrl;
}
done(e, tile);
},
_onTileRemove: function (e) {
e.tile.onload = null;
},
_getZoomForUrl: function () {
var zoom = this._tileZoom,
maxZoom = this.options.maxZoom,
zoomReverse = this.options.zoomReverse,
zoomOffset = this.options.zoomOffset;
if (zoomReverse) {
zoom = maxZoom - zoom;
}
return zoom + zoomOffset;
},
_getSubdomain: function (tilePoint) {
var index =
Math.abs(tilePoint.x + tilePoint.y) % this.options.subdomains.length;
return this.options.subdomains[index];
},
_abortLoading: function () {
var i, tile;
for (i in this._tiles) {
if (this._tiles[i].coords.z !== this._tileZoom) {
tile = this._tiles[i].el;
tile.onload = Util.falseFn;
tile.onerror = Util.falseFn;
if (!tile.complete) {
tile.src = Util.emptyImageUrl;
var coords = this._tiles[i].coords;
DomUtil.remove(tile);
delete this._tiles[i];
this.fire("tileabort", { tile, coords });
}
}
}
},
_removeTile: function (key) {
var tile = this._tiles[key];
tile.el.setAttribute("src", Util.emptyImageUrl);
return GridLayer.prototype._removeTile.call(this, key);
},
_tileReady: function (coords, err, tile) {
if (
!this._map ||
(tile && tile.getAttribute("src") === Util.emptyImageUrl)
) {
return;
}
return GridLayer.prototype._tileReady.call(this, coords, err, tile);
},
});
export function tileLayer(url, options) {
return new TileLayer(url, options);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# 源码解析
# initialize
方法
initialize
方法用于初始化TileLayer
对象,设置默认选项和事件监听器,保存URL末班
this._url
:存储瓦片图层的URL。options
:用户提供的选项,用于覆盖默认选项。options.detectRetina
:如果启用了视网膜检测,将瓦片大小减半,并调整缩放级别和偏移量。options.subdomains
:如果是字符串,将其拆分为子域名数组。this.on("tileunload", this._onTileRemove)
:监听tileunload
事件,当瓦片被卸载时调
# 方法详解
setUrl(url,noRedraw)
:
- 更新URL模版,若URL未变化且
noRedraw
为true
,则不重绘图层。 - 调用
redraw()
方法强制刷新,重新绘制图层。
createTile(coords,done)
:
- 创建一个新的HTML图片元素作为瓦片。
- 设置图片元素的
load
和error
事件监听器,当图片加载完成或加载失败时调用done
回调函数。 - 设置图片元素的
crossOrigin
属性,控制跨域请求。 - 设置图片元素的
referrerPolicy
属性,控制防盗链策略。 - 设置图片元素的
alt
属性为空字符串。 - 设置图片元素的
src
属性为getTileUrl(coords)
方法返回的URL。 - 返回创建的图片元素。
getTileUrl(coords)
:
- 计算并返回URL模版,将URL中的占位符替换为实际的值。
- 占位符包括:
{r}
:如果启用了视网膜检测,替换为@2x
,否则为空字符串。{s}
:根据坐标计算子域名。{x}
:替换为X坐标。{y}
:替换为Y坐标。{z}
:替换为缩放级别。{-y}
:如果启用了TMS格式,替换为反转的Y坐标,否则为空字符串。
- 返回替换后的URL。
_tileOnLoad(done,tile)
:
- 当图片加载完成时调用
done
回调函数,IE9以下延迟触发。
_tileOnError(done,tile,e)
:
- 当图片加载失败时调用
done
回调函数,并设置错误信息。 - 如果启用了错误瓦片的URL,将图片元素的
src
属性设置为错误瓦片的URL,再次尝试加载。 - 返回错误信息。
_getZoomForUrl()
:
- 根据
zoomReverse
和zoomOffset
选项计算缩放级别。 - 如果启用了
zoomReverse
,将缩放级别反转。 - 返回计算后的缩放级别。
_getSubdomain(tilePoint)
: - 根据X和Y坐标计算子域名。
- 使用
subdomains
选项中的子域名数组,根据X和Y坐标的绝对值取模计算索引。 - 返回计算得到的子域名。
# 瓦片管理
_abortLoading()
:中止非当前缩放级别的瓦片加载
- 重置
onload
/onerror
事件监听器, - 将非当前缩放级别的瓦片的
src
属性设置为空字符串,并从_tiles
对象中移除。
_removeTile(key)
:
- 从
_tiles
对象中移除指定键的瓦片。 - 将瓦片的
src
属性设置为空字符串,取消网络请求。 - 调用父类的
_removeTile
方法,移除瓦片元素。
_tileReady(coords,err,tile)
:
- 当瓦片准备就绪时调用。
- 如果URL为空字符串或
_map
不存在,返回。 - 调用父类的
_tileReady
方法,处理瓦片就绪事件。
# 关键逻辑
- Retina
适配:通过detectRetina
自动调整瓦片尺寸和缩放,加载高分辨率图片。
- 子域名轮询:分散请求,避免浏览器并发限制。
- TMS坐标反转:通过
tms: true
适配不同的瓦片服务标准。 - 跨域处理:通过
crossOrigin
属性解决CORS
问题,访问像素数据。 - 错误恢复:加载失败时显示
errorTileUrl
,提升用户体验
# 总结
TileLayer
是Leaflet
中用于加载和显示瓦片地图的核心组件,通过继承GridLayer
基类,实现了瓦片的加载和管理,支持自定义URL格式和参数,提供了丰富的功能和选项,适用于各种瓦片地图服务。
编辑 (opens new window)
上次更新: 2025/05/16, 06:30:31