Jinuss's blog Jinuss's blog
首页
  • 前端文章

    • JavaScript
  • 学习笔记

    • 《JavaScript高级程序设计》
    • 《Vue》
    • 《React》
    • 《Git》
    • JS设计模式总结
  • HTML
  • CSS
  • 技术文档
  • GitHub技巧
  • 学习
  • 实用技巧
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

东流

前端可视化
首页
  • 前端文章

    • JavaScript
  • 学习笔记

    • 《JavaScript高级程序设计》
    • 《Vue》
    • 《React》
    • 《Git》
    • JS设计模式总结
  • HTML
  • CSS
  • 技术文档
  • GitHub技巧
  • 学习
  • 实用技巧
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 框架

  • core模块

  • dom模块

  • control

  • geometry

  • geo

  • layer

    • marker

    • tile

      • GridLayer
      • TileLayer
        • 概述
        • 源码分析
          • 源码实现
          • 源码解析
          • initialize方法
          • 方法详解
          • 瓦片管理
          • 关键逻辑
        • 总结
      • TileLayer.WMS
    • vector

    • Layer
    • LayerGroup
    • FeatureGroup
    • DivOverlay
    • Popup
    • Tooltip
    • ImageOverlay
    • SVGOverlay
    • VideoOverlay
    • GeoJSON
  • Map

  • 《Leaflet源码》笔记
  • layer
  • tile
东流
2025-04-11
目录

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

# 源码解析

# 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
GridLayer
TileLayer.WMS

← GridLayer TileLayer.WMS→

最近更新
01
GeoJSON
05-08
02
Circle
04-15
03
CircleMarker
04-15
更多文章>
Theme by Vdoing | Copyright © 2024-2025 东流 | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式