图层组与要素组
目的和范围
本文档涵盖 LayerGroup 和 FeatureGroup 类,它们能够将图层集合作为单个单元进行管理。这些类提供了添加、移除和迭代多个图层的机制,并将其与地图的生命周期集成。
关于这些类扩展的基类 Layer 的信息,请参阅 图层基类。关于可以分组的特定图层类型(标记、矢量等)的详细信息,请参阅它们各自的章节(标记、矢量图层)。关于 FeatureGroup 的 GeoJSON 集成,请参阅 GeoJSON 支持。
概览
LayerGroup 和 FeatureGroup 在 Leaflet 中提供分层图层管理。两个类都扩展基类 Layer 并管理子图层集合,但在事件处理和集体操作能力方面有所不同。
LayerGroup 类
架构和图层存储
LayerGroup 在内部 _layers 对象中存储子图层,使用 Util.stamp() 生成的唯一数字 ID 进行索引。这种方法支持通过 ID 进行高效的图层查找,并支持方法调用中使用图层对象和 ID。
initialize 方法设置图层存储并添加构造函数中提供的任何初始图层:
| 构造函数参数 | 类型 | 描述 |
|---|---|---|
layers | Layer[] (可选) | 要添加到组的初始图层集 |
options | Object (可选) | 通过 Util.setOptions 合并的选项对象 |
核心方法
图层管理
LayerGroup 提供了添加、移除和查询图层的方法:
| 方法 | 参数 | 返回 | 描述 |
|---|---|---|---|
addLayer | layer: Layer | this | 添加图层到组;如果组在地图上,则添加到地图 |
removeLayer | layer: Layer 或 id: Number | this | 从组和地图中移除图层 |
hasLayer | layer: Layer 或 id: Number | Boolean | 检查图层是否在组中 |
clearLayers | 无 | this | 从组中移除所有图层 |
getLayer | id: Number | Layer | 返回具有给定 ID 的图层 |
getLayers | 无 | Layer[] | 返回所有图层的数组 |
getLayerId | layer: Layer | Number | 返回图层的内部 ID |
迭代
eachLayer 方法提供了一种函数式的方法来迭代图层:
// 来自 src/layer/LayerGroup.js:94-98 的签名
eachLayer(method, context)此方法遍历组中的所有图层,为每个图层调用提供的函数。context 参数设置回调中的 this 值。
地图生命周期集成
LayerGroup 通过从基类 Layer 继承的 onAdd 和 onRemove 方法与地图的图层生命周期集成:
当 LayerGroup 被添加到地图或从地图移除时,它会自动通过 eachLayer 迭代添加或移除其所有子图层。
集体操作
LayerGroup 提供了一个应用于所有子图层的 setZIndex 方法:
// 来自 src/layer/LayerGroup.js:117-119
setZIndex(zIndex) {
return this.eachLayer(l => l.setZIndex?.(zIndex));
}此方法使用可选链 (?.) 仅在支持该方法的图层上安全地调用 setZIndex。
FeatureGroup 类
LayerGroup 的扩展
FeatureGroup 扩展 LayerGroup,添加了事件传播和额外的集体操作。该类设计用于交互式要素集合,其中来自单个图层的事件应该冒泡到组级别。
事件传播
FeatureGroup 将自己建立为所有子图层的事件父级,实现从子级到组的事件冒泡:
事件父级关系在重写的 addLayer 和 removeLayer 方法中管理:
| 方法 | 事件父级操作 | 图层事件触发 |
|---|---|---|
addLayer | layer.addEventParent(this) | layeradd |
removeLayer | layer.removeEventParent(this) | layerremove |
图层事件
FeatureGroup 在添加或移除图层时触发 layeradd 和 layerremove 事件:
// 来自 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() 方法计算包含所有子图层的地理边界框:
该实现处理具有边界(多边形、折线)的图层和点图层(标记):
// 来自 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
| 特性 | LayerGroup | FeatureGroup |
|---|---|---|
| 基类 | Layer | LayerGroup |
| 图层管理 | ✓ (添加、移除、迭代) | ✓ (继承) |
| 事件传播 | ✗ | ✓ (addEventParent) |
| 图层事件 | ✗ | ✓ (layeradd, layerremove) |
| 集体样式 | ✗ | ✓ (setStyle) |
| Z-Order 管理 | 仅 setZIndex | setZIndex, 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() 方法处理两种类型的图层:
- 具有边界的图层(多边形、折线、圆形):使用返回
LatLngBounds对象的layer.getBounds() - 点图层(标记):使用返回单个
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 |