源码分析之Leaflet中TileLayer

发布于:2025-05-21 ⋅ 阅读:(18) ⋅ 点赞:(0)

概述

TileLayerLayer 的子类,继承自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);
}

源码解析

initialize方法

initialize方法用于初始化TileLayer对象,设置默认选项和事件监听器,保存URL末班

  • this._url:存储瓦片图层的URL。
  • options:用户提供的选项,用于覆盖默认选项。
  • options.detectRetina:如果启用了视网膜检测,将瓦片大小减半,并调整缩放级别和偏移量。
  • options.subdomains:如果是字符串,将其拆分为子域名数组。
  • this.on("tileunload", this._onTileRemove):监听tileunload事件,当瓦片被卸载时调
方法详解

setUrl(url,noRedraw):

  • 更新URL模版,若URL未变化且noRedrawtrue,则不重绘图层。
  • 调用redraw()方法强制刷新,重新绘制图层。

createTile(coords,done):

  • 创建一个新的HTML图片元素作为瓦片。
  • 设置图片元素的loaderror事件监听器,当图片加载完成或加载失败时调用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():

  • 根据zoomReversezoomOffset选项计算缩放级别。
  • 如果启用了zoomReverse,将缩放级别反转。
  • 返回计算后的缩放级别。
    _getSubdomain(tilePoint):
  • 根据XY坐标计算子域名。
  • 使用subdomains选项中的子域名数组,根据XY坐标的绝对值取模计算索引。
  • 返回计算得到的子域名。
瓦片管理

_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,提升用户体验

总结

TileLayerLeaflet中用于加载和显示瓦片地图的核心组件,通过继承GridLayer基类,实现了瓦片的加载和管理,支持自定义URL格式和参数,提供了丰富的功能和选项,适用于各种瓦片地图服务。


网站公告

今日签到

点亮在社区的每一天
去签到