瓦片图层与网格系统
本文档涵盖 Leaflet 的瓦片图层架构,包括 GridLayer 基类和 TileLayer 实现。它解释了瓦片坐标系统、URL 模板、瓦片加载机制和缓存策略,这些功能共同实现了瓦片地图数据的高效显示。
关于其他图层类型的信息,请参阅 矢量图层。关于渲染系统详情,请参阅 矢量渲染系统。
架构概览
Leaflet 的瓦片系统采用双层架构,其中 GridLayer 提供基础的网格管理功能,TileLayer 实现从瓦片服务器加载图片瓦片的具体逻辑。
GridLayer 职责:
- 通过
_tileCoordsToKey()和_keyToTileCoords()进行瓦片坐标计算和网格管理 - 通过
_addTile()、_removeTile()、_tileReady()管理瓦片生命周期 - 使用
_updateLevels()、_setView()、_clampZoom()处理缩放级别 - 通过
_pruneTiles()、_retainParent()、_retainChildren()实现瓦片缓存和修剪
TileLayer 职责:
- 使用
getTileUrl()和Util.template()处理 URL 模板 - 在
createTile()中创建图片瓦片并带有异步加载回调 - 通过
_getSubdomain()实现子域轮换,通过Browser.retina支持 Retina 显示屏 - 在
_tileOnError()中进行错误处理,并提供errorTileUrl回退机制
GridLayer 基类
GridLayer 作为 Leaflet 中所有瓦片图层的基础。它管理缩放级别的层次结构,每个级别包含一个瓦片网格,使用 CSS transforms 进行定位。
核心数据结构
图表:GridLayer 内部状态结构
_levels 对象将缩放级别映射到带有定位信息(origin、zoom)的 DOM 容器。_tiles 对象使用 _tileCoordsToKey(coords) 生成类似 '2048:1536:12' 的字符串键,这些键映射到包含 DOM 元素(el)、坐标信息(coords)和状态标志(current、loaded、active、retain)的瓦片对象。
瓦片坐标系统
GridLayer 使用标准的 Web Mercator 瓦片坐标系统,每个瓦片由 {x, y, z} 坐标标识:
z:缩放级别(0 = 整个世界在单个瓦片中)x:列索引(从西到东)y:行索引(从北到南)
图表:瓦片坐标到缓存键的映射
坐标到键的转换在 _tileCoordsToKey() 中实现,它创建字符串键,而 _keyToTileCoords() 执行反向操作,通过解析键字符串并返回带有 z 属性的 Point。
TileLayer 实现
TileLayer 扩展 GridLayer,提供使用 URL 模板从瓦片服务器加载图片瓦片的功能。
URL 模板系统
TileLayer 处理带有占位符替换的 URL 模板:
| 占位符 | 描述 | 来源 | 示例 |
|---|---|---|---|
{z} | 缩放级别 | this._getZoomForUrl() | 12 |
{x} | 瓦片 X 坐标 | coords.x | 2048 |
{y} | 瓦片 Y 坐标 | coords.y 或反转 | 1536 |
{s} | 子域 | this._getSubdomain(coords) | a, b, c |
{r} | Retina 后缀 | Browser.retina ? '@2x' : '' | @2x 或 `` |
{-y} | 反转 Y 坐标 | _globalTileRange.max.y - coords.y | 用于 TMS |
| 自定义 | 任意选项键 | this.options[key] | 用户自定义 |
图表:URL 模板处理流程
模板系统使用 Util.template(),它从数据对象中替换 {key} 占位符。可以通过图层选项添加自定义占位符,这些占位符将被自动包含。
瓦片创建和加载
TileLayer 实现 createTile() 方法来生成带有异步加载的 <img> 元素:
图表:瓦片创建和异步加载序列
错误处理包括如果设置了 options.errorTileUrl,则回退到该 URL,错误通过 done 回调传递,触发 tileerror 事件。
瓦片加载生命周期
瓦片加载过程涉及坐标计算、瓦片创建和基于距视口中心距离的渐进式加载。
瓦片更新流程
图表:瓦片更新和加载流程
瓦片使用 coords.distanceTo(tileCenter) 按距视口中心的距离排序,并按该顺序加载以优先显示可见内容。_update() 方法使用 DocumentFragment 批量进行 DOM 插入以提高性能。
瓦片修剪和保留
GridLayer 实现复杂的瓦片保留逻辑,以平衡内存使用和平滑的缩放过渡:
图表:瓦片金字塔保留策略
这种基于金字塔的保留方式保持父瓦片(最多低 5 个缩放级别)和子瓦片(最多高 2 个缩放级别),以实现平滑的缩放过渡。保留基于瓦片的 "active" 状态,该状态在瓦片完成淡入动画后设置。
缩放级别管理
GridLayer 与 Map 协调以建立缩放约束并管理多分辨率瓦片显示。
缩放边界集成
图表:带有图层的 Map 缩放边界计算
GridLayers 通过 _addZoomLimit() 向地图注册自身,这会更新 _zoomBoundLayers 并触发 _updateZoomLevels()。地图的有效缩放边界将显式的 options.minZoom/maxZoom(如果设置)与所有图层缩放边界的并集结合起来。当图层被移除时,_removeZoomLimit() 会相应更新边界。
原生缩放边界限制
minNativeZoom 和 maxNativeZoom 选项控制瓦片源仅在特定缩放级别提供瓦片时的瓦片缩放级别限制:
图表:原生缩放边界的 _clampZoom() 逻辑
例如,如果地图处于缩放级别 15 但设置了 maxNativeZoom: 12,GridLayer 将请求缩放级别 12 的瓦片,并通过 CSS transforms 将其放大 2³ = 8 倍。这在使用 map.getZoomScale(mapZoom, tileZoom) 的 _setZoomTransform() 中计算。
性能考量
GridLayer 包含多项优化,以实现平滑的瓦片显示和高效的内存使用。
更新节流
图表:地图移动期间的瓦片更新节流
updateWhenIdle 选项(在移动设备上默认 true,通过 Browser.mobile)决定是否在平移期间更新瓦片。当为 false 时,_onMove 处理程序被创建为 _onMoveEnd() 的节流版本,使用 options.updateInterval(默认 200ms)来限制连续地图移动期间的更新频率。
瓦片淡入动画
平滑的淡入动画提高了感知性能,同时与瓦片修剪协调以优化内存使用。