Skip to content

Layer System

Purpose and Scope

Layer System 提供了在 Leaflet 地图上显示地理和非地理数据的基础架构。本文档涵盖基础的 Layer Class、Layer 生命周期、Map 集成以及控制渲染顺序的 Pane System。

关于特定 Layer 实现的详细信息,请参阅:

Architecture Overview

Layer System 遵循分层架构,其中所有 Layer 都从一个共同的基类扩展,并通过标准化的生命周期与 Map 集成。

Layer System Architecture

SVG
100%

Layer Base Class

Layer Class 在 src/layer/Layer.js29-117 中作为所有 Layer 类型的抽象基类。它扩展了 Evented 以提供事件处理能力。

Core Layer Structure

SVG
100%

Default Options

Layer 在 src/layer/Layer.js31-45 中设置了默认选项:

OptionDefaultDescription
pane'overlayPane'Layer 将被渲染到的 Map Pane
attributionnullLayer 的 Attribution 文本
bubblingPointerEventstrue指针事件是否冒泡到 Map

Key Methods

Layer Lifecycle

Layer 遵循严格的生命周期,通过子类实现的生命周期钩子进行管理。该生命周期确保与 Map 的渲染和事件系统正确集成。

Layer Lifecycle Flow

SVG
100%

Lifecycle Hooks

子类必须或可以实现以下方法:

  1. beforeAdd(map) - 可选的早期初始化 src/layer/Layer.js136-137

    • 在 Layer 被添加到 _layers 之前调用
    • 事件尚未初始化
    • 用于不需要就绪 Map 的设置
  2. getEvents() - 可选的事件注册 src/layer/Layer.js130-131

    • 返回类似 {viewreset: this._reset, zoom: this._update} 的对象
    • Layer 被添加时自动绑定事件
    • Layer 被移除时自动移除事件
  3. onAdd(map) - 必需实现 src/layer/Layer.js124-125

    • 创建并追加 DOM 元素
    • 添加事件监听器
    • 初始化 Layer 状态
    • 返回 this 以支持方法链式调用
  4. onRemove(map) - 可选清理 src/layer/Layer.js127-128

    • 移除 DOM 元素
    • 清理事件监听器
    • 释放资源

Layer Removal

Layer Removal Flow

SVG
100%

Layer-Map Integration

Map 维护内部注册表以跟踪 Layer 及其属性。理解这种集成对于实现自定义 Layer 至关重要。

Layer Registry

Map 使用唯一标识将 Layer 存储在 _layerssrc/map/Map.js153-162

// 内部结构(概念性)
map._layers = {
  [stamp(layer)]: layer,
  // 例如:"42": tileLayer,
  //       "43": marker,
  //       "44": polygon
}

Util.stamp() 函数为每个 Layer 对象分配唯一 ID,确保高效查找。

Zoom Bounds Tracking

具有 minZoommaxZoom 选项的 Layer 在 _zoomBoundLayers 中单独跟踪 src/map/Map.js227-241

Zoom Bounds Management

SVG
100%

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

SVG
100%

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 的默认 Pane
  • layer.getPane('customPane') - 按名称返回特定 Pane
  • layer.getPane('tooltipPane') - 从选项属性返回 Pane

Attribution System

Layer 可以提供 Attribution 文本,显示在 Map 的 Attribution Control 中。这通常用于瓦片 Layer 的版权和数据源致谢。

Attribution Flow

SVG
100%

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:

  1. 在元素上接收 DOM 事件
  2. _targets 中查找元素的 stamp
  3. 将事件路由到所属的 Layer
  4. 触发 Layer 特定的事件(例如 clickpointerover

Interactive Target Lookup

SVG
100%

Layer Events

Layer 在其整个生命周期中触发和监听各种事件。

Layer-Specific Events

定义在 src/layer/Layer.js21-26

  • 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 类别,每个类别都有专门的行为:

CategoryBase ClassPurposeSubsection
Raster TilesGridLayer, TileLayer从 URL 加载瓦片图像3.2
Vector ShapesPath, Polyline, Polygon, Circle绘制几何形状3.4
MarkersMarker使用 Icon 显示点位置3.3
Layer CollectionsLayerGroup, FeatureGroup将多个 Layer 分组3.5
HTML OverlaysDivOverlay, Popup, Tooltip显示 HTML 内容3.6
GeoJSONGeoJSON解析和渲染 GeoJSON 数据3.7

每个类别都扩展了基础的 Layer Class 并实现了适合其渲染策略的生命周期钩子。

Layer Type Hierarchy (Simplified)

SVG
100%

Implementation Guidelines

实现自定义 Layer 时,请遵循以下指南:

Minimum Implementation

  1. Extend Layer

    class CustomLayer extends Layer {
        // Implementation
    }
  2. Implement onAdd(map) src/layer/Layer.js124-125

    • 创建 DOM 元素
    • 追加到适当的 Pane
    • 初始化状态
    • 添加事件监听器
  3. Implement onRemove(map) src/layer/Layer.js127-128

    • 移除 DOM 元素
    • 清理监听器
    • 释放资源

Optional Enhancements

  1. Implement getEvents() - 自动注册 Map 事件处理器
  2. Implement beforeAdd(map) - 早期初始化
  3. Override getAttribution() - 提供 Attribution
  4. Register interactive targets - 启用事件路由
  5. 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 时,请验证:

  1. Lifecycle Events - addremove 事件正确触发 spec/suites/map/MapSpec.js989-1011
  2. DOM Integration - 元素被添加到正确的 Pane spec/suites/map/MapSpec.js734-786
  3. Memory Cleanup - onRemove 正确清理 spec/suites/map/MapSpec.js61-81
  4. Event Registration - Map 事件被绑定/解绑 src/layer/Layer.js106-110
  5. Attribution - getAttribution() 返回预期值
  6. Layer Registry - Layer 被添加时出现在 _layerssrc/map/Map.js160-162
  7. Zoom Bounds - 具有 min/max zoom 的 Layer 更新 Map 限制 spec/suites/map/MapSpec.js616-674