Layer System
Purpose and Scope
Layer System 提供了在 Leaflet 地图上显示地理和非地理数据的基础架构。本文档涵盖基础的 Layer Class、Layer 生命周期、Map 集成以及控制渲染顺序的 Pane System。
关于特定 Layer 实现的详细信息,请参阅:
- Layer Base Class - 详细的生命周期和 Attribution 管理
- Tile Layers and Grid System - 栅格瓦片渲染
- Markers and Icons - 点要素
- Vector Layers - 基于 Path 的渲染
- Layer Groups and Feature Groups - Layer 集合
- Popups and Tooltips - 绑定到 Layer 的 UI 覆盖层
- GeoJSON Support - GeoJSON 解析和渲染
Architecture Overview
Layer System 遵循分层架构,其中所有 Layer 都从一个共同的基类扩展,并通过标准化的生命周期与 Map 集成。
Layer System Architecture
Layer Base Class
Layer Class 在 src/layer/Layer.js29-117 中作为所有 Layer 类型的抽象基类。它扩展了 Evented 以提供事件处理能力。
Core Layer Structure
Default Options
Layer 在 src/layer/Layer.js31-45 中设置了默认选项:
| Option | Default | Description |
|---|---|---|
pane | 'overlayPane' | Layer 将被渲染到的 Map Pane |
attribution | null | Layer 的 Attribution 文本 |
bubblingPointerEvents | true | 指针事件是否冒泡到 Map |
Key Methods
addTo(map)src/layer/Layer.js53-56 - 将 Layer 添加到 Map 或 LayerGroupremove()src/layer/Layer.js60-62 - 从其当前 Map 中移除 LayerremoveFrom(obj)src/layer/Layer.js70-73 - 从特定的 Map 或 LayerGroup 中移除getPane(name)src/layer/Layer.js77-79 - 返回命名 Pane 的 HTMLElementgetAttribution()src/layer/Layer.js93-95 - 返回 Attribution 文本
Layer Lifecycle
Layer 遵循严格的生命周期,通过子类实现的生命周期钩子进行管理。该生命周期确保与 Map 的渲染和事件系统正确集成。
Layer Lifecycle Flow
Lifecycle Hooks
子类必须或可以实现以下方法:
beforeAdd(map)- 可选的早期初始化 src/layer/Layer.js136-137- 在 Layer 被添加到
_layers之前调用 - 事件尚未初始化
- 用于不需要就绪 Map 的设置
- 在 Layer 被添加到
getEvents()- 可选的事件注册 src/layer/Layer.js130-131- 返回类似
{viewreset: this._reset, zoom: this._update}的对象 - Layer 被添加时自动绑定事件
- Layer 被移除时自动移除事件
- 返回类似
onAdd(map)- 必需实现 src/layer/Layer.js124-125- 创建并追加 DOM 元素
- 添加事件监听器
- 初始化 Layer 状态
- 返回
this以支持方法链式调用
onRemove(map)- 可选清理 src/layer/Layer.js127-128- 移除 DOM 元素
- 清理事件监听器
- 释放资源
Layer Removal
Layer Removal Flow
Layer-Map Integration
Map 维护内部注册表以跟踪 Layer 及其属性。理解这种集成对于实现自定义 Layer 至关重要。
Layer Registry
Map 使用唯一标识将 Layer 存储在 _layers 中 src/map/Map.js153-162:
// 内部结构(概念性)
map._layers = {
[stamp(layer)]: layer,
// 例如:"42": tileLayer,
// "43": marker,
// "44": polygon
}Util.stamp() 函数为每个 Layer 对象分配唯一 ID,确保高效查找。
Zoom Bounds Tracking
具有 minZoom 或 maxZoom 选项的 Layer 在 _zoomBoundLayers 中单独跟踪 src/map/Map.js227-241:
Zoom Bounds Management
Layer Iteration
Map 提供 eachLayer() 用于遍历所有 Layer src/map/Map.js212-217:
map.eachLayer(function(layer) {
layer.bindPopup('Hello');
});这会遍历 Object.values(this._layers),提供干净的 API 而不暴露内部结构。
Pane System
Pane System 控制 Layer 的 z-order 和渲染层次结构。每个 Pane 是一个 DOM 元素,包含特定类型的 Layer。
Default Pane Hierarchy
Pane Creation
Pane 在 Map 初始化期间创建 src/map/Map.js1256-1286:
// 在 _initPanes() 中创建的默认 Pane
this._panes = {};
this._paneRenderers = {};
this._mapPane = this.createPane('mapPane', this._container);
DomUtil.setPosition(this._mapPane, new Point(0, 0));
this.createPane('tilePane');
this.createPane('overlayPane');
this.createPane('shadowPane');
this.createPane('markerPane');
this.createPane('tooltipPane');
this.createPane('popupPane');Custom Panes
Layer 可以使用自定义 Pane 来满足特定的 z-order 需求 src/map/Map.js821-829:
// 创建自定义 Pane
map.createPane('customPane');
// 在 Layer 中使用
const layer = L.marker([0, 0], {
pane: 'customPane'
});createPane() 方法在 src/map/Map.js821-829:
- 创建带有
leaflet-pane类的<div> - 将其添加到
_panes注册表 - 将其附加到指定的容器(或默认的
mapPane)
Accessing Panes
Layer 通过 getPane() 访问其 Pane src/layer/Layer.js77-79:
getPane(name) {
return this._map.getPane(name ? (this.options[name] || name) : this.options.pane);
}这允许灵活的 Pane 规范:
layer.getPane()- 返回 Layer 的默认 Panelayer.getPane('customPane')- 按名称返回特定 Panelayer.getPane('tooltipPane')- 从选项属性返回 Pane
Attribution System
Layer 可以提供 Attribution 文本,显示在 Map 的 Attribution Control 中。这通常用于瓦片 Layer 的版权和数据源致谢。
Attribution Flow
Setting Attribution
Attribution 通过 attribution 选项设置 src/layer/Layer.js39-40:
const layer = L.tileLayer('https://tile.example.com/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors'
});Retrieving Attribution
getAttribution() 方法 src/layer/Layer.js93-95 返回 Attribution 字符串:
getAttribution() {
return this.options.attribution;
}子类可以覆盖此方法以提供动态 Attribution 或聚合来自多个源的 Attribution(如 LayerGroup 所做的)。
Interactive Target Registration
Layer 可以将 DOM 元素注册为交互目标,允许 Map 将事件路由到正确的 Layer,即使事件源自子元素。
Registration Methods
addInteractiveTarget(targetEl) src/layer/Layer.js81-84
addInteractiveTarget(targetEl) {
this._map._targets[Util.stamp(targetEl)] = this;
return this;
}removeInteractiveTarget(targetEl) src/layer/Layer.js86-89
removeInteractiveTarget(targetEl) {
delete this._map._targets[Util.stamp(targetEl)];
return this;
}此机制允许 Map:
- 在元素上接收 DOM 事件
- 在
_targets中查找元素的 stamp - 将事件路由到所属的 Layer
- 触发 Layer 特定的事件(例如
click、pointerover)
Interactive Target Lookup
Layer Events
Layer 在其整个生命周期中触发和监听各种事件。
Layer-Specific Events
add- Layer 被添加到 Map 后触发remove- Layer 从 Map 中移除后触发
Map Layer Events
当 Layer 被添加或移除时,Map 会触发事件 src/layer/Layer.js143-148:
layeradd- 当新 Layer 被添加时触发,包含{layer: Layer}layerremove- 当 Layer 被移除时触发,包含{layer: Layer}
Example Event Usage
// 监听 Layer 添加
map.on('layeradd', function(e) {
console.log('Layer added:', e.layer);
});
// 监听 Layer 自身的 add 事件
layer.on('add', function() {
console.log('I was added to the map');
});
// Layer 注册 Map 事件
getEvents() {
return {
viewreset: this._reset,
zoom: this._onZoom,
moveend: this._update
};
}Layer Type Categories
Layer System 支持多种 Layer 类别,每个类别都有专门的行为:
| Category | Base Class | Purpose | Subsection |
|---|---|---|---|
| Raster Tiles | GridLayer, TileLayer | 从 URL 加载瓦片图像 | 3.2 |
| Vector Shapes | Path, Polyline, Polygon, Circle | 绘制几何形状 | 3.4 |
| Markers | Marker | 使用 Icon 显示点位置 | 3.3 |
| Layer Collections | LayerGroup, FeatureGroup | 将多个 Layer 分组 | 3.5 |
| HTML Overlays | DivOverlay, Popup, Tooltip | 显示 HTML 内容 | 3.6 |
| GeoJSON | GeoJSON | 解析和渲染 GeoJSON 数据 | 3.7 |
每个类别都扩展了基础的 Layer Class 并实现了适合其渲染策略的生命周期钩子。
Layer Type Hierarchy (Simplified)
Implementation Guidelines
实现自定义 Layer 时,请遵循以下指南:
Minimum Implementation
Extend
Layerclass CustomLayer extends Layer { // Implementation }Implement
onAdd(map)src/layer/Layer.js124-125- 创建 DOM 元素
- 追加到适当的 Pane
- 初始化状态
- 添加事件监听器
Implement
onRemove(map)src/layer/Layer.js127-128- 移除 DOM 元素
- 清理监听器
- 释放资源
Optional Enhancements
- Implement
getEvents()- 自动注册 Map 事件处理器 - Implement
beforeAdd(map)- 早期初始化 - Override
getAttribution()- 提供 Attribution - Register interactive targets - 启用事件路由
- Set default options - 使用
setDefaultOptions()静态方法
Example Custom Layer
class CustomLayer extends Layer {
static {
this.setDefaultOptions({
pane: 'overlayPane',
attribution: '© Custom Layer'
});
}
getEvents() {
return {
zoom: this._update,
viewreset: this._reset
};
}
onAdd(map) {
this._container = L.DomUtil.create('div', 'custom-layer');
this.getPane().appendChild(this._container);
this._update();
return this;
}
onRemove(map) {
this._container.remove();
return this;
}
_update() {
// 基于 Map 状态更新 Layer
const zoom = this._map.getZoom();
const center = this._map.getCenter();
// ... 更新逻辑
}
_reset() {
// 在视图重置时重置 Layer 状态
}
}Testing Considerations
测试自定义 Layer 时,请验证:
- Lifecycle Events -
add和remove事件正确触发 spec/suites/map/MapSpec.js989-1011 - DOM Integration - 元素被添加到正确的 Pane spec/suites/map/MapSpec.js734-786
- Memory Cleanup -
onRemove正确清理 spec/suites/map/MapSpec.js61-81 - Event Registration - Map 事件被绑定/解绑 src/layer/Layer.js106-110
- Attribution -
getAttribution()返回预期值 - Layer Registry - Layer 被添加时出现在
_layers中 src/map/Map.js160-162 - Zoom Bounds - 具有 min/max zoom 的 Layer 更新 Map 限制 spec/suites/map/MapSpec.js616-674