Skip to content

图层组与要素组

目的和范围

本文档涵盖 LayerGroupFeatureGroup 类,它们能够将图层集合作为单个单元进行管理。这些类提供了添加、移除和迭代多个图层的机制,并将其与地图的生命周期集成。

关于这些类扩展的基类 Layer 的信息,请参阅 图层基类。关于可以分组的特定图层类型(标记、矢量等)的详细信息,请参阅它们各自的章节(标记矢量图层)。关于 FeatureGroup 的 GeoJSON 集成,请参阅 GeoJSON 支持

概览

LayerGroup 和 FeatureGroup 在 Leaflet 中提供分层图层管理。两个类都扩展基类 Layer 并管理子图层集合,但在事件处理和集体操作能力方面有所不同。

SVG
100%

LayerGroup 类

架构和图层存储

LayerGroup 在内部 _layers 对象中存储子图层,使用 Util.stamp() 生成的唯一数字 ID 进行索引。这种方法支持通过 ID 进行高效的图层查找,并支持方法调用中使用图层对象和 ID。

SVG
100%

initialize 方法设置图层存储并添加构造函数中提供的任何初始图层:

构造函数参数类型描述
layersLayer[] (可选)要添加到组的初始图层集
optionsObject (可选)通过 Util.setOptions 合并的选项对象

核心方法

图层管理

LayerGroup 提供了添加、移除和查询图层的方法:

SVG
100%
方法参数返回描述
addLayerlayer: Layerthis添加图层到组;如果组在地图上,则添加到地图
removeLayerlayer: Layerid: Numberthis从组和地图中移除图层
hasLayerlayer: Layerid: NumberBoolean检查图层是否在组中
clearLayersthis从组中移除所有图层
getLayerid: NumberLayer返回具有给定 ID 的图层
getLayersLayer[]返回所有图层的数组
getLayerIdlayer: LayerNumber返回图层的内部 ID

迭代

eachLayer 方法提供了一种函数式的方法来迭代图层:

// 来自 src/layer/LayerGroup.js:94-98 的签名
eachLayer(method, context)

此方法遍历组中的所有图层,为每个图层调用提供的函数。context 参数设置回调中的 this 值。

地图生命周期集成

LayerGroup 通过从基类 Layer 继承的 onAddonRemove 方法与地图的图层生命周期集成:

SVG
100%

当 LayerGroup 被添加到地图或从地图移除时,它会自动通过 eachLayer 迭代添加或移除其所有子图层。

集体操作

LayerGroup 提供了一个应用于所有子图层的 setZIndex 方法:

// 来自 src/layer/LayerGroup.js:117-119
setZIndex(zIndex) {
    return this.eachLayer(l => l.setZIndex?.(zIndex));
}

此方法使用可选链 (?.) 仅在支持该方法的图层上安全地调用 setZIndex

FeatureGroup 类

LayerGroup 的扩展

FeatureGroup 扩展 LayerGroup,添加了事件传播和额外的集体操作。该类设计用于交互式要素集合,其中来自单个图层的事件应该冒泡到组级别。

SVG
100%

事件传播

FeatureGroup 将自己建立为所有子图层的事件父级,实现从子级到组的事件冒泡:

SVG
100%

事件父级关系在重写的 addLayerremoveLayer 方法中管理:

方法事件父级操作图层事件触发
addLayerlayer.addEventParent(this)layeradd
removeLayerlayer.removeEventParent(this)layerremove

图层事件

FeatureGroup 在添加或移除图层时触发 layeraddlayerremove 事件:

// 来自 addLayer 方法,src/layer/FeatureGroup.js:38-40
// @event layeradd: LayerEvent
// 当图层添加到此 `FeatureGroup` 时触发
return this.fire('layeradd', {layer});

// 来自 removeLayer 方法,src/layer/FeatureGroup.js:55-57
// @event layerremove: LayerEvent
// 当图层从此 `FeatureGroup` 移除时触发
return this.fire('layerremove', {layer});

集体操作

FeatureGroup 提供将操作应用于所有支持该操作的子图层的方法:

方法描述实现策略
setStyle(style)将路径样式应用于所有矢量图层在具有该方法的图层上调用 setStyle
bringToFront()将所有图层移动到渲染顺序的顶部在支持的图层上调用 bringToFront
bringToBack()将所有图层移动到渲染顺序的底部在支持的图层上调用 bringToBack

每个方法使用可选链来安全地处理可能不支持该操作的图层:

// 来自 src/layer/FeatureGroup.js:62-76
setStyle(style) {
    return this.eachLayer(l => l.setStyle?.(style));
}

bringToFront() {
    return this.eachLayer(l => l.bringToFront?.());
}

bringToBack() {
    return this.eachLayer(l => l.bringToBack?.());
}

边界计算

getBounds() 方法计算包含所有子图层的地理边界框:

SVG
100%

该实现处理具有边界(多边形、折线)的图层和点图层(标记):

// 来自 src/layer/FeatureGroup.js:80-87
getBounds() {
    const bounds = new LatLngBounds();
    
    for (const layer of Object.values(this._layers)) {
        bounds.extend(layer.getBounds ? layer.getBounds() : layer.getLatLng());
    }
    return bounds;
}

比较:LayerGroup vs FeatureGroup

特性LayerGroupFeatureGroup
基类LayerLayerGroup
图层管理✓ (添加、移除、迭代)✓ (继承)
事件传播✓ (addEventParent)
图层事件✓ (layeradd, layerremove)
集体样式✓ (setStyle)
Z-Order 管理setZIndexsetZIndex, bringToFront, bringToBack
边界计算✓ (getBounds)
典型用例简单分组交互式要素集合

使用模式

基本 LayerGroup 使用

// 来自 src/layer/LayerGroup.js:14-19 的示例
const group = new LayerGroup([marker1, marker2])
    .addLayer(polyline)
    .addTo(map);

// 移除所有图层
group.clearLayers();

// 迭代图层
group.eachLayer(layer => {
    console.log(layer);
});

带事件处理的 FeatureGroup

// 来自 src/layer/FeatureGroup.js:17-22 的示例
new FeatureGroup([marker1, marker2, polyline])
    .bindPopup('Hello world!')
    .on('click', function() { 
        alert('Clicked on a member of the group!'); 
    })
    .addTo(map);

由于事件传播,事件处理程序接收来自任何子图层的事件。

集体样式

// 将样式应用于组中的所有矢量图层
featureGroup.setStyle({
    color: 'red',
    weight: 5
});

// 重新排序所有图层
featureGroup.bringToFront();

实现细节

图层 ID 生成

两个类都使用 Util.stamp(layer) 为图层生成唯一的数字 ID。此函数分配并返回在图层生命周期内保持一致的唯一标识符:

// 来自 src/layer/LayerGroup.js:123-125
getLayerId(layer) {
    return Util.stamp(layer);
}

自动地图同步

当 LayerGroup 被添加到地图时,_map 属性被设置(由基类 Layer 设置),并调用 onAdd。这会触发所有子图层的自动添加:

// 来自 src/layer/LayerGroup.js:81-87
onAdd(map) {
    this.eachLayer(map.addLayer, map);
}

onRemove(map) {
    this.eachLayer(map.removeLayer, map);
}

类似地,当新图层被添加到已经在地图上的组时,它们会立即被添加到地图:

// 来自 src/layer/LayerGroup.js:38-46
addLayer(layer) {
    const id = this.getLayerId(layer);
    this._layers[id] = layer;
    this._map?.addLayer(layer);  // 可选链:仅在 _map 存在时
    return this;
}

可选方法调用

FeatureGroup 广泛使用可选链 (?.) 来安全地调用可能并非所有图层类型都有的方法:

// 来自 src/layer/FeatureGroup.js:62-76
setStyle(style) {
    return this.eachLayer(l => l.setStyle?.(style));  // 仅在方法存在时调用
}

bringToFront() {
    return this.eachLayer(l => l.bringToFront?.());
}

bringToBack() {
    return this.eachLayer(l => l.bringToBack?.());
}

这种模式允许在同一 FeatureGroup 中混合图层类型(例如标记和多边形)而不会出错。

边界扩展策略

FeatureGroup 中的 getBounds() 方法处理两种类型的图层:

  1. 具有边界的图层(多边形、折线、圆形):使用返回 LatLngBounds 对象的 layer.getBounds()
  2. 点图层(标记):使用返回单个 LatLng 坐标的 layer.getLatLng()

LatLngBounds.extend() 方法接受两种类型,扩展边界以包含新几何:

// 来自 src/layer/FeatureGroup.js:80-87
getBounds() {
    const bounds = new LatLngBounds();
    for (const layer of Object.values(this._layers)) {
        bounds.extend(layer.getBounds ? layer.getBounds() : layer.getLatLng());
    }
    return bounds;
}

测试覆盖

LayerGroup 测试套件验证核心功能:

测试类别验证行为
hasLayer对无效参数抛出错误;正确识别图层成员关系
addLayer添加图层并返回组实例
removeLayer移除图层并返回组实例
clearLayers一次性移除所有图层
getLayers返回所有图层的准确数组
eachLayer使用正确的图层和上下文迭代
toGeoJSON为分组图层生成有效的 GeoJSON