diff --git a/skills/l7/COMPLETENESS_REPORT.md b/skills/l7/COMPLETENESS_REPORT.md
new file mode 100644
index 0000000..179d649
--- /dev/null
+++ b/skills/l7/COMPLETENESS_REPORT.md
@@ -0,0 +1,365 @@
+# L7 Skills 文档完备性分析报告
+
+生成时间:2026年1月26日
+
+## 📊 概览
+
+本报告基于 L7 官方 API 文档(site/docs/api)与当前 Skills 文档(skills/references)的对比分析。
+
+### 现有 Skills 文档统计
+
+**总计:20 个文档**
+
+#### 按目录分类:
+
+- **Core(核心)**: 3 个
+ - scene.md (场景初始化)
+ - scene-lifecycle.md (场景生命周期)
+ - scene-methods.md (场景方法)
+
+- **Data(数据)**: 4 个
+ - source-geojson.md
+ - source-csv.md
+ - source-json.md
+ - source-parser.md
+
+- **Layers(图层)**: 6 个
+ - point.md (点图层)
+ - line.md (线图层)
+ - polygon.md (面图层)
+ - heatmap.md (热力图)
+ - image.md (图片图层)
+ - raster.md (栅格图层)
+
+- **Visual(视觉)**: 2 个
+ - mapping.md (视觉映射)
+ - style.md (样式配置)
+
+- **Interaction(交互)**: 3 个
+ - events.md (事件处理)
+ - popup.md (弹窗)
+ - components.md (组件)
+
+- **Animation(动画)**: 1 个
+ - layer-animation.md (图层动画)
+
+- **Performance(性能)**: 1 个
+ - optimization.md (性能优化)
+
+---
+
+## ✅ 已覆盖的核心功能
+
+### 1. Scene 场景 ✅
+
+- ✅ 场景初始化
+- ✅ 场景生命周期
+- ✅ 场景方法(完整的 API)
+
+### 2. 基础图层 ✅
+
+- ✅ PointLayer(点图层)
+- ✅ LineLayer(线图层)
+- ✅ PolygonLayer(面图层)
+- ✅ HeatmapLayer(热力图)
+- ✅ ImageLayer(图片图层)
+- ✅ RasterLayer(栅格图层)
+
+### 3. 数据源 ✅
+
+- ✅ GeoJSON
+- ✅ CSV
+- ✅ JSON
+- ✅ Parser(解析器)
+
+### 4. 视觉编码 ✅
+
+- ✅ 颜色、大小、形状映射
+- ✅ 样式配置
+
+### 5. 交互功能 ✅
+
+- ✅ 事件处理
+- ✅ Popup
+- ✅ Components(Marker、Controls、Legend)
+
+### 6. 动画和性能 ✅
+
+- ✅ 图层动画
+- ✅ 性能优化
+
+---
+
+## ❌ 缺失的重要功能
+
+### 🔴 高优先级(推荐补充)
+
+#### 1. 地图引擎配置(Map)
+
+**官方文档位置**: `site/docs/api/map/`
+
+缺失内容:
+
+- ❌ **GaodeMap**(高德地图)- 最常用的底图
+- ❌ **Mapbox**(Mapbox 地图)
+- ❌ **MapLibre**(开源地图)
+- ❌ **Map**(独立地图引擎)
+- ❌ BMap(百度地图)
+- ❌ Tencent(腾讯地图)
+- ❌ Tianditu(天地图)
+- ❌ Leaflet
+
+**推荐新增文档**:
+
+- `skills/references/core/map-types.md` - 地图类型配置(已在 index.md 中引用但未实现)
+
+#### 2. 瓦片图层(Tile Layers)
+
+**官方文档位置**: `site/docs/api/tile/`
+
+缺失内容:
+
+- ❌ **VectorTileLayer**(矢量瓦片)- 重要的大数据渲染方案
+- ❌ **RasterTileLayer**(栅格瓦片)
+- ❌ **GeoJSONVTTileLayer**(GeoJSON 切片)
+- ❌ **TileDebugLayer**(瓦片调试)
+
+**推荐新增文档**:
+
+- `skills/references/layers/tile-vector.md`
+- `skills/references/layers/tile-raster.md`
+
+#### 3. 数据源补充
+
+**官方文档位置**: `site/docs/api/source/`
+
+缺失内容:
+
+- ❌ **MVT**(Mapbox Vector Tile)- 矢量瓦片数据源
+- ❌ **Image** 数据源
+- ❌ **Raster** 数据源
+- ❌ **RasterTile** 数据源
+- ❌ **NDI** 数据源(遥感影像)
+- ❌ **RGB** 数据源(遥感影像)
+
+**推荐新增文档**:
+
+- `skills/references/data/source-mvt.md`
+- `skills/references/data/source-raster.md`
+
+#### 4. 图层通用特性
+
+**官方文档位置**: `site/docs/api/base_layer/` 和各图层的子页面
+
+缺失内容:
+
+- ❌ **BaseLayer**(图层基类)- 所有图层的通用方法
+- ❌ **Layer Options**(图层配置)
+- ❌ **Layer Methods**(图层方法:show/hide/fitBounds/setIndex 等)
+- ❌ **Layer Events**(图层事件:click/mousemove/mouseout 等)
+
+每个图层类型都有详细的子文档:
+
+- ❌ color.md(颜色映射详解)
+- ❌ size.md(大小映射详解)
+- ❌ shape.md(形状类型详解)
+- ❌ scale.md(数据映射详解)
+- ❌ animate.md(动画配置详解)
+- ❌ style.md(样式配置详解)
+- ❌ source.md(数据源配置详解)
+- ❌ options.md(图层配置详解)
+
+**推荐新增文档**:
+
+- `skills/references/layers/base-layer.md` - 图层基类和通用方法
+- `skills/references/layers/layer-events.md` - 图层事件详解
+
+#### 5. 高级组件
+
+**官方文档位置**: `site/docs/api/component/`
+
+部分缺失:
+
+- ✅ Marker(已包含在 components.md)
+- ✅ Popup(已有独立文档)
+- ✅ Control(已包含在 components.md)
+- ❌ **MarkerLayer**(Marker 统一管理)
+- ❌ **LayerPopup**(图层绑定弹窗)
+- ❌ **Zoom** 控件(缩放控制)
+- ❌ **Scale** 控件(比例尺)
+- ❌ **Logo** 控件
+- ❌ **Fullscreen** 控件(全屏控制)
+- ❌ **ExportImage** 控件(导出图片)
+- ❌ **MapTheme** 控件(地图主题切换)
+- ❌ **MouseLocation** 控件(鼠标位置显示)
+- ❌ **Geolocate** 控件(定位控制)
+- ❌ **LayerSwitch** 控件(图层切换)
+- ❌ **Swipe** 控件(卷帘对比)
+
+**推荐新增文档**:
+
+- `skills/references/interaction/controls.md` - 各类控件详解
+- `skills/references/interaction/layer-popup.md` - 图层弹窗
+- `skills/references/interaction/marker-layer.md` - MarkerLayer
+
+### 🟡 中优先级(可选补充)
+
+#### 6. React 组件
+
+**官方文档位置**: `site/docs/api/react/`
+
+- ❌ React 绑定和组件封装
+
+**推荐新增文档**:
+
+- `skills/references/frameworks/react.md`
+
+#### 7. 其他特殊图层
+
+**官方文档位置**: `site/docs/api/other/`
+
+- ❌ **MaskLayer**(遮罩图层)
+- ❌ **WindLayer**(风场图层)
+- ❌ **CanvasLayer**(Canvas 图层)
+- ❌ **CityBuildingLayer**(城市建筑)
+- ❌ **GeometryLayer**(几何图层)
+- ❌ **EarthLayer**(地球图层)
+
+**推荐新增文档**:
+
+- `skills/references/layers/other-layers.md`(已在 index.md 引用但内容不完整)
+
+#### 8. 高级特性
+
+**官方文档位置**: `site/docs/api/experiment/` 和 `site/docs/api/debug/`
+
+- ❌ 实验性功能
+- ❌ 调试工具
+
+### 🟢 低优先级(补充性内容)
+
+#### 9. 纹理(Texture)
+
+- ❌ 线图层纹理配置(`line_layer/texture.md`)
+
+#### 10. 教程内容
+
+**官方文档位置**: `site/docs/tutorial/`
+
+当前 skills 主要关注 API,教程性内容较少。
+
+---
+
+## 📋 具体建议补充清单
+
+### 第一批(核心必备,共 5 个)
+
+1. ✅ `skills/references/core/map-types.md` - 地图引擎配置(高德、Mapbox、MapLibre、Map)
+2. ✅ `skills/references/layers/base-layer.md` - 图层基类和通用方法
+3. ✅ `skills/references/layers/tile-vector.md` - 矢量瓦片图层
+4. ✅ `skills/references/data/source-mvt.md` - MVT 数据源
+5. ✅ `skills/references/interaction/controls.md` - 控件详解(Zoom、Scale、Fullscreen 等)
+
+### 第二批(功能完善,共 5 个)
+
+6. `skills/references/layers/layer-events.md` - 图层事件详解
+7. `skills/references/interaction/layer-popup.md` - 图层弹窗
+8. `skills/references/interaction/marker-layer.md` - MarkerLayer
+9. `skills/references/layers/tile-raster.md` - 栅格瓦片图层
+10. `skills/references/data/source-raster.md` - 栅格数据源
+
+### 第三批(深度扩展,共 5 个)
+
+11. `skills/references/visual/color-mapping.md` - 颜色映射详解
+12. `skills/references/visual/size-mapping.md` - 大小映射详解
+13. `skills/references/visual/shape-types.md` - 形状类型详解
+14. `skills/references/layers/mask.md` - 遮罩图层
+15. `skills/references/frameworks/react.md` - React 集成
+
+---
+
+## 📊 完备性评分
+
+| 类别 | 完备度 | 评分 |
+| -------------------------------------- | ---------------- | ---------- |
+| 核心场景(Scene) | ███████████ 100% | ⭐⭐⭐⭐⭐ |
+| 基础图层(Point/Line/Polygon/Heatmap) | ██████████░ 95% | ⭐⭐⭐⭐⭐ |
+| 数据源(GeoJSON/CSV/JSON) | ████████░░░ 75% | ⭐⭐⭐⭐ |
+| 地图引擎配置 | ░░░░░░░░░░░ 0% | ⭐ |
+| 瓦片图层 | ░░░░░░░░░░░ 0% | ⭐ |
+| 视觉编码 | ████████░░░ 80% | ⭐⭐⭐⭐ |
+| 交互组件 | ██████░░░░░ 60% | ⭐⭐⭐ |
+| 动画 | ████████░░░ 80% | ⭐⭐⭐⭐ |
+| 性能优化 | ████████░░░ 80% | ⭐⭐⭐⭐ |
+| 框架集成 | ░░░░░░░░░░░ 0% | ⭐ |
+
+**总体完备度:约 57%**
+
+---
+
+## 🎯 优先级行动建议
+
+### 立即补充(Week 1)
+
+1. **地图引擎配置** - 这是使用 L7 的第一步,非常重要
+2. **图层基类文档** - 帮助理解所有图层的通用特性
+3. **控件详解** - 完善交互功能文档
+
+### 短期补充(Week 2-3)
+
+4. **矢量瓦片** - 大数据渲染的核心方案
+5. **MVT 数据源** - 配合矢量瓦片使用
+6. **图层事件** - 完善交互功能
+
+### 中期补充(Month 2)
+
+7. 各图层的详细子文档(color/size/shape/scale)
+8. 高级组件(LayerPopup、MarkerLayer 等)
+9. 特殊图层(Mask、Wind 等)
+
+### 长期补充(Month 3+)
+
+10. React 框架集成
+11. 实验性功能
+12. 调试工具
+
+---
+
+## 💡 文档结构建议
+
+当前 skills 文档结构清晰,建议保持:
+
+```
+skills/references/
+├── core/ # 核心功能(Scene、Map)
+├── data/ # 数据源
+├── layers/ # 图层类型
+├── visual/ # 视觉编码
+├── interaction/ # 交互功能
+├── animation/ # 动画
+├── performance/ # 性能优化
+└── frameworks/ # 框架集成(新增)
+```
+
+---
+
+## 📝 总结
+
+**优点**:
+
+- ✅ 核心场景(Scene)文档非常完善,包含生命周期和方法
+- ✅ 基础图层(6 种主要图层)都已覆盖
+- ✅ 基础数据源(GeoJSON/CSV/JSON)齐全
+- ✅ 文档结构清晰,遵循统一规范
+- ✅ 代码示例丰富,实用性强
+
+**不足**:
+
+- ❌ 缺少地图引擎配置文档(这是使用 L7 的第一步)
+- ❌ 缺少瓦片图层(大数据场景的核心方案)
+- ❌ 控件文档不够详细(只有概述,缺少各控件的详细用法)
+- ❌ 缺少图层通用特性文档(BaseLayer)
+- ❌ 缺少框架集成文档(React)
+
+**建议**:
+优先补充前 5 个文档(地图引擎、图层基类、矢量瓦片、MVT、控件详解),可将完备度提升至 75% 以上。
diff --git a/skills/l7/README.md b/skills/l7/README.md
new file mode 100644
index 0000000..8122c25
--- /dev/null
+++ b/skills/l7/README.md
@@ -0,0 +1,213 @@
+# L7 Skill Library
+
+> 为 AntV L7 地理空间可视化引擎设计的结构化技能知识库,遵循 skill-creator 最佳实践。
+
+## 🎯 设计原则
+
+基于 [skill-creator](https://github.com/anthropics/skills) 的最佳实践:
+
+1. **渐进式披露** (Progressive Disclosure)
+ - SKILL.md: 概览和快速入门 (~200 lines)
+ - references/: 详细文档,按需加载
+ - metadata/: 机器可读的依赖和标签
+
+2. **按领域组织** (Domain Organization)
+ - references/core/: 核心功能(场景初始化、地图类型)
+ - references/data/: 数据处理(GeoJSON、CSV、解析器)
+ - references/layers/: 图层类型(点、线、面、热力图等)
+ - references/interaction/: 交互组件(事件、Popup、Controls)
+ - references/animation/: 动画效果(图层动画、轨迹动画)
+ - references/performance/: 性能优化
+
+3. **精简高效** (Concise and Efficient)
+ - 避免冗余,信息只存在一个地方
+ - 优先代码示例而非冗长解释
+ - 详细内容移至 references,保持主文件精简
+
+## 📁 目录结构
+
+```
+.skills/
+├── SKILL.md # 主入口:概览 + 快速开始 + 导航
+├── index.md # 技能索引:场景查找 + 文档导航
+├── README.md # 本文件:使用说明
+├── references/ # 详细文档(按需加载)
+│ ├── core/
+│ │ ├── scene.md # Scene 完整文档
+│ │ ├── scene-methods.md # Scene 方法详解
+│ │ ├── scene-lifecycle.md # 场景生命周期
+│ │ └── map-types.md # 地图类型配置
+│ ├── data/
+│ │ ├── geojson.md # GeoJSON 数据处理
+│ │ ├── csv.md # CSV 数据处理
+│ │ ├── json.md # JSON 数据处理
+│ │ ├── source-mvt.md # MVT 瓦片数据源
+│ │ └── parser.md # 数据解析配置
+│ ├── layers/
+│ │ ├── base-layer.md # 基础图层 API
+│ │ ├── point.md # 点图层
+│ │ ├── line.md # 线图层
+│ │ ├── polygon.md # 面图层
+│ │ ├── heatmap.md # 热力图
+│ │ ├── image.md # 图片图层
+│ │ └── tile-vector.md # 矢量瓦片图层
+│ ├── interaction/
+│ │ ├── events.md # 事件处理
+│ │ ├── popup.md # Popup 组件
+│ │ ├── controls.md # 控件组件
+│ │ └── components.md # Marker/Legend
+│ ├── animation/
+│ │ └── layer-animation.md # 图层动画和轨迹动画
+│ ├── performance/
+│ │ └── optimization.md # 性能优化指南
+└── metadata/
+ ├── skill-dependency.json # 技能依赖关系
+ ├── skill-tags.json # 中英文标签
+ └── version-compatibility.json
+```
+
+## 🚀 快速开始
+
+### 对于 AI 模型
+
+**三级加载系统**:
+
+1. **始终加载**: SKILL.md (~200 lines)
+ - 获取概览和快速入门
+ - 查看文档导航表
+
+2. **按需加载**: references/\*.md
+ - 根据用户需求选择具体文档
+ - 示例: "显示点位" → 加载 `references/layers/point.md`
+
+3. **辅助信息**: metadata/\*.json
+ - 检查依赖关系
+ - 搜索相关标签
+
+### 文档选择策略
+
+| 用户请求 | 加载文档 |
+| ---------- | ------------------------------------------------------- |
+| "创建地图" | references/core/scene.md |
+| "显示点位" | references/layers/point.md + references/data/geojson.md |
+| "热力图" | references/layers/heatmap.md |
+| "添加交互" | references/interaction/events.md |
+| "性能慢" | references/performance/optimization.md |
+
+### 技能组合模式
+
+复杂需求需要组合多个 references:
+
+```
+地图可视化 = scene.md + polygon.md + point.md + events.md + popup.md
+轨迹动画 = scene.md + line.md + layer-animation.md
+热力分析 = scene.md + heatmap.md + parser.md
+```
+
+## 📖 Reference 文件格式
+
+每个 reference 文件遵循统一结构:
+
+```markdown
+# 标题
+
+## 目录 (对于 >100 行的文件)
+
+## 快速示例
+
+## 详细配置
+
+## 使用场景
+
+## 常见问题
+
+## 相关文档
+```
+
+## 🔍 检索策略
+
+### 按标签检索
+
+使用 `metadata/skill-tags.json`:
+
+```json
+{
+ "point-layer": ["point", "scatter", "bubble", "点", "散点", "气泡"],
+ "scene-initialization": ["scene", "map", "init", "场景", "地图", "初始化"]
+}
+```
+
+### 按依赖检索
+
+使用 `metadata/skill-dependency.json`:
+
+```json
+{
+ "point-layer": {
+ "requires": ["scene-initialization"],
+ "optional": ["source-geojson", "color-mapping"],
+ "nextSteps": ["event-handling", "popup"]
+ }
+}
+```
+
+## 💡 最佳实践
+
+### 避免重复加载
+
+```
+❌ 不要同时加载 SKILL.md 和所有 references
+✅ 先读 SKILL.md,根据需求加载特定 references
+```
+
+### 组合使用文档
+
+```
+❌ 不要在单个 reference 中重复基础概念
+✅ 通过交叉引用链接相关文档
+```
+
+### 优先示例代码
+
+```
+❌ 避免冗长的文字解释
+✅ 提供清晰的代码示例和注释
+```
+
+## 🔧 维护指南
+
+### 添加新 Reference
+
+1. 确定所属领域(core/data/layers/etc.)
+2. 创建文件到对应 `references/` 子目录
+3. 更新 `SKILL.md` 的导航表
+4. 更新 `index.md` 的文档列表
+5. 添加标签到 `metadata/skill-tags.json`
+6. 添加依赖到 `metadata/skill-dependency.json`
+
+### 更新现有 Reference
+
+1. 保持文件结构一致
+2. 确保交叉引用链接有效
+3. 更新相关的 metadata 文件
+4. 记录版本变化和 breaking changes
+
+## 📊 统计信息
+
+- **核心文档**: 4 个(场景、方法、生命周期、地图类型)
+- **数据处理**: 5 个(GeoJSON、CSV、JSON、MVT、解析器)
+- **图层类型**: 7 个(基础、点、线、面、热力、图片、瓦片)
+- **交互控制**: 4 个(事件、Popup、控件、组件)
+- **动画效果**: 1 个(图层动画)
+- **性能优化**: 1 个
+- **总计**: 22 个核心文档
+
+## 🔗 相关资源
+
+- **官方文档**: https://l7.antv.antgroup.com/
+- **GitHub**: https://github.com/antvis/L7
+- **Skill Creator**: https://github.com/anthropics/skills
+
+---
+
+**注意**: 本技能库设计用于 AI 模型代码生成,建议配合向量检索系统使用以获得最佳效果。
diff --git a/skills/l7/SKILL.md b/skills/l7/SKILL.md
new file mode 100644
index 0000000..80f37a3
--- /dev/null
+++ b/skills/l7/SKILL.md
@@ -0,0 +1,193 @@
+---
+name: antv-l7
+description: |
+ Comprehensive guide for AntV L7 geospatial visualization library. Use when users need to:
+ (1) Create interactive maps with WebGL rendering
+ (2) Visualize geographic data (points, lines, polygons, heatmaps)
+ (3) Build location-based data dashboards
+ (4) Add map layers, interactions, or animations
+ (5) Process and display GeoJSON, CSV, or other spatial data
+ (6) Integrate maps with AMap (GaodeMap), Mapbox, Maplibre, or standalone L7 Map
+ (7) Optimize performance for large-scale geographic datasets
+license: MIT
+---
+
+# AntV L7 Geospatial Visualization
+
+AntV L7 是基于 WebGL 的大规模地理空间数据可视化引擎,支持多种地图底图和丰富的可视化图层类型。
+
+## Quick Start
+
+创建最简单的 L7 地图应用:
+
+```javascript
+import { Scene, PointLayer } from '@antv/l7';
+import { GaodeMap } from '@antv/l7-maps';
+
+// 1. 初始化场景
+const scene = new Scene({
+ id: 'map',
+ map: new GaodeMap({
+ center: [120.19, 30.26],
+ zoom: 10,
+ style: 'light',
+ }),
+});
+
+// 2. 添加图层
+scene.on('loaded', () => {
+ const pointLayer = new PointLayer()
+ .source(data, {
+ parser: { type: 'json', x: 'lng', y: 'lat' },
+ })
+ .shape('circle')
+ .size(10)
+ .color('#5B8FF9');
+
+ scene.addLayer(pointLayer);
+});
+```
+
+## Core Workflow
+
+L7 的典型开发流程:
+
+```
+1. 场景初始化 (Scene) → 2. 数据准备 → 3. 创建图层 (Layer) → 4. 添加交互 → 5. 优化性能
+```
+
+## 📚 Reference Documentation
+
+详细文档按领域组织,根据需要加载:
+
+### 基础功能 (references/core/)
+
+- **[scene.md](references/core/scene.md)** - Scene 初始化、生命周期、方法
+- **[map-types.md](references/core/map-types.md)** - GaodeMap、Mapbox、Maplibre、Map 的配置
+
+### 数据处理 (references/data/)
+
+- **[geojson.md](references/data/geojson.md)** - GeoJSON 格式、解析、转换
+- **[csv.md](references/data/csv.md)** - CSV 数据加载和处理
+- **[json.md](references/data/json.md)** - JSON 数据、OD 数据、路径数据
+- **[parser.md](references/data/parser.md)** - Parser 配置、Transform 转换
+
+### 图层类型 (references/layers/)
+
+- **[point.md](references/layers/point.md)** - 点图层:散点、气泡、3D 柱状
+- **[line.md](references/layers/line.md)** - 线图层:路径、弧线、流线
+- **[polygon.md](references/layers/polygon.md)** - 面图层:填充、3D 建筑、choropleth
+- **[heatmap.md](references/layers/heatmap.md)** - 热力图:密度分布、网格热力
+- **[image.md](references/layers/image.md)** - 图片图层:卫星图、航拍图、平面图
+- **[raster.md](references/layers/raster.md)** - 栅格瓦片图层:XYZ/TMS 瓦片服务
+- **[other-layers.md](references/layers/other-layers.md)** - 其他图层类型
+
+### 视觉映射 (references/visual/)
+
+- **[mapping.md](references/visual/mapping.md)** - 颜色、大小、形状映射
+- **[style.md](references/visual/style.md)** - 透明度、描边、纹理等样式
+
+### 交互组件 (references/interaction/)
+
+- **[events.md](references/interaction/events.md)** - 点击、悬停、选中事件
+- **[components.md](references/interaction/components.md)** - Popup、Marker、Controls、Legend
+
+### 动画效果 (references/animation/)
+
+- **[layer-animation.md](references/animation/layer-animation.md)** - 图层动画、轨迹动画
+
+### 性能优化 (references/performance/)
+
+- **[optimization.md](references/performance/optimization.md)** - 数据过滤、聚合、图层管理
+
+## 使用指南
+
+### 按用户需求选择文档
+
+| 用户请求示例 | 加载的文档 |
+| -------------- | -------------------------------- |
+| "创建一个地图" | core/scene.md |
+| "显示点位数据" | layers/point.md, data/geojson.md |
+| "绘制路径" | layers/line.md |
+| "热力图" | layers/heatmap.md |
+| "添加点击事件" | interaction/events.md |
+| "显示弹窗" | interaction/components.md |
+
+### 技能组合模式
+
+复杂需求需要组合多个技能:
+
+```
+城市可视化 = scene + polygon + point + events + popup
+轨迹动画 = scene + line + animation
+热力分析 = scene + heatmap + data/json
+```
+
+### 依赖检查
+
+使用 `metadata/skill-dependency.json` 检查技能依赖关系:
+
+```json
+{
+ "point-layer": {
+ "requires": ["scene-initialization"],
+ "optional": ["source-geojson", "color-mapping"],
+ "nextSteps": ["event-handling", "popup"]
+ }
+}
+```
+
+## 版本信息
+
+- **当前版本**: L7 2.x
+- **浏览器支持**: Chrome ≥60, Firefox ≥60, Safari ≥12
+- **坐标系**: WGS84 (地理坐标) / Plane coordinates (独立 Map)
+- **底图**: 高德地图、Mapbox、Maplibre、L7 Map (独立)
+
+## 最佳实践
+
+1. **场景初始化优先**: 始终从创建 Scene 开始
+2. **数据格式规范**: 优先使用 GeoJSON 标准格式
+3. **性能优先**: 大数据量时使用数据过滤和聚合
+4. **渐进增强**: 先实现基础功能,再添加交互和动画
+5. **错误处理**: 添加事件监听和数据验证
+
+## 快速参考
+
+### 常用导入
+
+```javascript
+// 核心
+import { Scene } from '@antv/l7';
+import { GaodeMap } from '@antv/l7-maps';
+
+// 图层
+import { PointLayer, LineLayer, PolygonLayer, HeatmapLayer } from '@antv/l7';
+
+// 组件
+import { Popup, Marker } from '@antv/l7';
+```
+
+### 地图样式选项
+
+- `'light'` - 浅色风格
+- `'dark'` - 深色风格
+- `'normal'` - 标准风格
+- `'satellite'` - 卫星影像
+- `'blank'` - 空白底图(独立 Map)
+
+### 坐标格式
+
+```javascript
+[经度, 纬度]; // [120.19, 30.26]
+// 经度: -180 ~ 180
+// 纬度: -90 ~ 90
+```
+
+## 元数据
+
+- **skill-dependency.json** - 技能依赖关系图
+- **skill-tags.json** - 中英文标签检索
+- **version-compatibility.json** - 版本兼容性信息
+
+查看 [index.md](index.md) 获取完整技能列表和导航。
diff --git a/skills/l7/index.md b/skills/l7/index.md
new file mode 100644
index 0000000..6dee970
--- /dev/null
+++ b/skills/l7/index.md
@@ -0,0 +1,69 @@
+# L7 技能索引(Skill Index)
+
+> 从这里开始,快速查找和组合 L7 技能文档
+
+## 📚 按领域查找
+
+### 1. 核心功能 Core
+
+- [scene.md](references/core/scene.md) - Scene 初始化、生命周期、方法
+- [map-types.md](references/core/map-types.md) - 地图类型配置
+
+### 2. 数据处理 Data
+
+- [geojson.md](references/data/geojson.md) - GeoJSON 格式和解析
+- [csv.md](references/data/csv.md) - CSV 数据加载
+- [json.md](references/data/json.md) - JSON 数据源
+- [parser.md](references/data/parser.md) - 数据解析配置
+
+### 3. 图层类型 Layers
+
+- [point.md](references/layers/point.md) - 点图层
+- [line.md](references/layers/line.md) - 线图层
+- [polygon.md](references/layers/polygon.md) - 面图层
+- [heatmap.md](references/layers/heatmap.md) - 热力图
+- [image.md](references/layers/image.md) - 图片图层
+- [raster.md](references/layers/raster.md) - 栅格瓦片图层
+- [other-layers.md](references/layers/other-layers.md) - 其他图层
+
+### 4. 视觉映射 Visual
+
+- [mapping.md](references/visual/mapping.md) - 颜色、大小、形状映射
+- [style.md](references/visual/style.md) - 样式配置
+
+### 5. 交互组件 Interaction
+
+- [events.md](references/interaction/events.md) - 事件处理
+- [popup.md](references/interaction/popup.md) - Popup 弹窗
+- [components.md](references/interaction/components.md) - Marker、Controls、Legend
+
+### 6. 动画效果 Animation
+
+- [layer-animation.md](references/animation/layer-animation.md) - 图层动画、轨迹动画
+
+### 7. 性能优化 Performance
+
+- [optimization.md](references/performance/optimization.md) - 数据过滤、聚合、图层管理
+
+### 🎯 按场景查找
+
+| 用户需求 | 推荐文档 | 难度 |
+| -------- | --------------------------------------------------------------------------------- | ------ |
+| 创建地图 | [scene.md](references/core/scene.md) | ⭐ |
+| 显示点位 | [point.md](references/layers/point.md) + [geojson.md](references/data/geojson.md) | ⭐ |
+| 绘制路径 | [line.md](references/layers/line.md) | ⭐ |
+| 区域填充 | [polygon.md](references/layers/polygon.md) | ⭐ |
+| 热力图 | [heatmap.md](references/layers/heatmap.md) | ⭐⭐ |
+| 点击事件 | [events.md](references/interaction/events.md) | ⭐⭐ |
+| 显示弹窗 | [popup.md](references/interaction/popup.md) | ⭐⭐ |
+| 轨迹动画 | [layer-animation.md](references/animation/layer-animation.md) | ⭐⭐ |
+| 性能优化 | [optimization.md](references/performance/optimization.md) | ⭐⭐⭐ |
+
+---
+
+## 📖 相关资源
+
+- **主文档**:[SKILL.md](SKILL.md) - 快速入门与概览
+- **依赖关系**:[skill-dependency.json](metadata/skill-dependency.json)
+- **标签检索**:[skill-tags.json](metadata/skill-tags.json)
+- **版本兼容性**:[version-compatibility.json](metadata/version-compatibility.json)
diff --git a/skills/l7/metadata/skill-dependency.json b/skills/l7/metadata/skill-dependency.json
new file mode 100644
index 0000000..940a927
--- /dev/null
+++ b/skills/l7/metadata/skill-dependency.json
@@ -0,0 +1,122 @@
+{
+ "scene-initialization": {
+ "requires": [],
+ "optional": [],
+ "conflicts": [],
+ "nextSteps": ["point-layer", "line-layer", "polygon-layer"]
+ },
+ "point-layer": {
+ "requires": ["scene-initialization"],
+ "optional": ["source-geojson", "color-mapping", "size-mapping"],
+ "conflicts": [],
+ "nextSteps": ["event-handling", "popup"]
+ },
+ "line-layer": {
+ "requires": ["scene-initialization"],
+ "optional": ["source-geojson", "color-mapping", "trajectory-animation"],
+ "conflicts": [],
+ "nextSteps": ["event-handling", "popup"]
+ },
+ "polygon-layer": {
+ "requires": ["scene-initialization"],
+ "optional": ["source-geojson", "color-mapping"],
+ "conflicts": [],
+ "nextSteps": ["event-handling", "popup", "highlight-select"]
+ },
+ "heatmap-layer": {
+ "requires": ["scene-initialization"],
+ "optional": ["source-geojson", "aggregation"],
+ "conflicts": [],
+ "nextSteps": []
+ },
+ "event-handling": {
+ "requires": ["scene-initialization"],
+ "optional": ["popup", "marker", "highlight-select"],
+ "conflicts": [],
+ "nextSteps": ["popup", "highlight-select"]
+ },
+ "popup": {
+ "requires": ["scene-initialization"],
+ "optional": ["event-handling"],
+ "conflicts": [],
+ "nextSteps": []
+ },
+ "marker": {
+ "requires": ["scene-initialization"],
+ "optional": [],
+ "conflicts": [],
+ "nextSteps": []
+ },
+ "color-mapping": {
+ "requires": [],
+ "optional": ["categorical-scale", "quantitative-scale"],
+ "conflicts": [],
+ "nextSteps": []
+ },
+ "size-mapping": {
+ "requires": [],
+ "optional": ["quantitative-scale"],
+ "conflicts": [],
+ "nextSteps": []
+ },
+ "layer-animation": {
+ "requires": [],
+ "optional": [],
+ "conflicts": [],
+ "nextSteps": []
+ },
+ "trajectory-animation": {
+ "requires": ["line-layer"],
+ "optional": ["layer-animation"],
+ "conflicts": [],
+ "nextSteps": []
+ },
+ "vector-tile": {
+ "requires": ["scene-initialization"],
+ "optional": ["performance-optimization"],
+ "conflicts": [],
+ "nextSteps": []
+ },
+ "data-filtering": {
+ "requires": [],
+ "optional": [],
+ "conflicts": [],
+ "nextSteps": []
+ },
+ "aggregation": {
+ "requires": [],
+ "optional": ["heatmap-layer"],
+ "conflicts": [],
+ "nextSteps": []
+ },
+ "city-visualization": {
+ "requires": ["scene-initialization", "polygon-layer", "point-layer", "event-handling", "popup"],
+ "optional": ["line-layer", "controls"],
+ "conflicts": [],
+ "nextSteps": []
+ },
+ "trajectory-visualization": {
+ "requires": ["scene-initialization", "line-layer", "trajectory-animation"],
+ "optional": ["marker", "popup"],
+ "conflicts": [],
+ "nextSteps": []
+ },
+ "district-visualization": {
+ "requires": ["scene-initialization", "polygon-layer", "event-handling"],
+ "optional": ["popup", "legend", "highlight-select"],
+ "conflicts": [],
+ "nextSteps": []
+ },
+ "heatmap-analysis": {
+ "requires": ["scene-initialization", "heatmap-layer"],
+ "optional": ["legend", "controls"],
+ "conflicts": [],
+ "nextSteps": []
+ },
+ "od-flow-visualization": {
+ "requires": ["scene-initialization", "line-layer"],
+ "optional": ["trajectory-animation", "point-layer"],
+ "conflicts": [],
+ "nextSteps": []
+ }
+}
diff --git a/skills/l7/metadata/skill-tags.json b/skills/l7/metadata/skill-tags.json
new file mode 100644
index 0000000..4410f6c
--- /dev/null
+++ b/skills/l7/metadata/skill-tags.json
@@ -0,0 +1,40 @@
+{
+ "scene-initialization": ["scene", "map", "init", "setup", "create", "基础", "初始化"],
+ "point-layer": ["point", "scatter", "bubble", "circle", "点", "散点", "气泡"],
+ "line-layer": ["line", "path", "arc", "route", "线", "路径", "弧线", "轨迹"],
+ "polygon-layer": ["polygon", "fill", "area", "region", "面", "区域", "填充"],
+ "heatmap-layer": ["heatmap", "热力图", "密度", "分布"],
+ "raster-layer": ["raster", "image", "栅格", "影像"],
+ "image-layer": ["image", "picture", "图片"],
+ "color-mapping": ["color", "颜色", "映射", "着色", "配色"],
+ "size-mapping": ["size", "大小", "尺寸", "映射"],
+ "shape-mapping": ["shape", "形状", "符号"],
+ "style-config": ["style", "样式", "配置"],
+ "event-handling": ["event", "click", "mouse", "事件", "点击", "交互"],
+ "highlight-select": ["highlight", "select", "高亮", "选中"],
+ "mouse-control": ["mouse", "cursor", "鼠标", "控制"],
+ "popup": ["popup", "tooltip", "info", "弹窗", "提示", "信息框"],
+ "marker": ["marker", "标注", "图标", "pin"],
+ "controls": ["control", "zoom", "scale", "控件", "缩放", "比例尺"],
+ "legend": ["legend", "图例"],
+ "layer-animation": ["animation", "动画", "效果"],
+ "trajectory-animation": ["trajectory", "path", "轨迹", "路径动画"],
+ "quantitative-scale": ["scale", "quantitative", "比例尺", "定量"],
+ "categorical-scale": ["scale", "categorical", "比例尺", "定性", "分类"],
+ "vector-tile": ["tile", "mvt", "瓦片", "矢量"],
+ "raster-tile": ["tile", "raster", "瓦片", "栅格"],
+ "data-filtering": ["filter", "过滤", "筛选", "性能"],
+ "layer-management": ["layer", "manage", "图层管理", "层级"],
+ "aggregation": ["aggregate", "cluster", "聚合", "聚类"],
+ "mask-layer": ["mask", "clip", "遮罩", "裁剪"],
+ "multi-basemap": ["basemap", "底图", "切换"],
+ "custom-layer": ["custom", "extend", "自定义", "扩展"],
+ "city-visualization": ["city", "building", "城市", "建筑", "场景"],
+ "trajectory-visualization": ["trajectory", "path", "轨迹", "场景"],
+ "district-visualization": ["district", "region", "行政区", "区域", "场景"],
+ "heatmap-analysis": ["heatmap", "analysis", "热力", "分析", "场景"],
+ "od-flow-visualization": ["od", "flow", "migration", "流向", "迁徙", "场景"],
+ "data-not-showing": ["问题", "不显示", "debug", "troubleshoot"],
+ "performance-issues": ["问题", "性能", "卡顿", "优化"],
+ "style-not-working": ["问题", "样式", "不生效"]
+}
diff --git a/skills/l7/metadata/version-compatibility.json b/skills/l7/metadata/version-compatibility.json
new file mode 100644
index 0000000..1afd4a6
--- /dev/null
+++ b/skills/l7/metadata/version-compatibility.json
@@ -0,0 +1,65 @@
+{
+ "l7Version": "2.x",
+ "skills": {
+ "scene-initialization": {
+ "minVersion": "2.0.0",
+ "deprecated": false,
+ "breaking_changes": {
+ "2.0.0": "API 重构,不兼容 1.x 版本"
+ }
+ },
+ "point-layer": {
+ "minVersion": "2.0.0",
+ "deprecated": false,
+ "breaking_changes": {}
+ },
+ "line-layer": {
+ "minVersion": "2.0.0",
+ "deprecated": false,
+ "breaking_changes": {}
+ },
+ "polygon-layer": {
+ "minVersion": "2.0.0",
+ "deprecated": false,
+ "breaking_changes": {}
+ },
+ "heatmap-layer": {
+ "minVersion": "2.0.0",
+ "deprecated": false,
+ "breaking_changes": {}
+ },
+ "mask-layer": {
+ "minVersion": "2.7.2",
+ "deprecated": false,
+ "notes": "需要在 Scene 初始化时设置 stencil: true"
+ },
+ "vector-tile": {
+ "minVersion": "2.5.0",
+ "deprecated": false,
+ "notes": "需要在 Scene 初始化时设置 stencil: true"
+ },
+ "layer-animation": {
+ "minVersion": "2.0.0",
+ "deprecated": false,
+ "breaking_changes": {
+ "2.9.0": "动画配置参数调整"
+ }
+ }
+ },
+ "dependencies": {
+ "@antv/l7": "^2.0.0",
+ "@antv/l7-maps": "^2.0.0"
+ },
+ "browsers": {
+ "chrome": ">=60",
+ "firefox": ">=60",
+ "safari": ">=12",
+ "edge": ">=79"
+ },
+ "notes": [
+ "L7 2.x 版本不兼容 1.x",
+ "建议使用 2.11.0 及以上版本",
+ "WebGL 1.0 最低要求",
+ "部分功能需要 WebGL 2.0 支持"
+ ]
+}
diff --git a/skills/l7/references/animation/layer-animation.md b/skills/l7/references/animation/layer-animation.md
new file mode 100644
index 0000000..2cfea55
--- /dev/null
+++ b/skills/l7/references/animation/layer-animation.md
@@ -0,0 +1,362 @@
+# Layer Animation Guide
+
+L7 图层动画和轨迹动画完整指南。
+
+## 图层动画
+
+### 数据更新动画
+
+```javascript
+// 基础数据更新
+layer.setData(newData);
+
+// 带动画的数据更新
+layer.animate(true); // 开启动画
+layer.setData(newData);
+```
+
+### 属性动画
+
+```javascript
+// 大小动画
+layer.size('value', [5, 20]).animate({
+ enable: true,
+ interval: 0.1, // 时间间隔
+ duration: 2, // 动画时长(秒)
+ trailLength: 0.5, // 拖尾长度
+});
+
+// 颜色动画
+layer.color('type', {
+ values: ['#5B8FF9', '#5AD8A6'],
+ animate: {
+ duration: 2000,
+ repeat: true,
+ },
+});
+```
+
+## 轨迹动画
+
+### LineLayer 轨迹动画
+
+```javascript
+import { LineLayer } from '@antv/l7';
+
+const pathData = {
+ type: 'FeatureCollection',
+ features: [
+ {
+ type: 'Feature',
+ properties: { name: '路线1' },
+ geometry: {
+ type: 'LineString',
+ coordinates: [
+ [120.19, 30.26],
+ [120.2, 30.27],
+ [120.21, 30.28],
+ [120.22, 30.29],
+ ],
+ },
+ },
+ ],
+};
+
+const lineLayer = new LineLayer().source(pathData).shape('line').size(3).color('#5B8FF9').animate({
+ enable: true,
+ interval: 0.5, // 速度
+ trailLength: 0.3, // 拖尾长度
+ duration: 4, // 完整动画时长
+});
+
+scene.addLayer(lineLayer);
+```
+
+### PointLayer 运动点
+
+```javascript
+// 运动的点
+const pointLayer = new PointLayer()
+ .source(pathData)
+ .shape('circle')
+ .size(10)
+ .color('#FF6B3B')
+ .animate({
+ enable: true,
+ interval: 0.5,
+ duration: 4,
+ });
+```
+
+### 组合使用:路径 + 运动点
+
+```javascript
+// 路径
+const pathLayer = new LineLayer().source(pathData).shape('line').size(2).color('#ccc');
+
+// 运动的点
+const movingPoint = new PointLayer()
+ .source(pathData)
+ .shape('circle')
+ .size(8)
+ .color('#FF6B3B')
+ .animate({
+ enable: true,
+ interval: 0.5,
+ duration: 4,
+ });
+
+// 轨迹线(拖尾)
+const trailLine = new LineLayer().source(pathData).shape('line').size(3).color('#5B8FF9').animate({
+ enable: true,
+ interval: 0.5,
+ trailLength: 0.5,
+ duration: 4,
+});
+
+scene.addLayer(pathLayer);
+scene.addLayer(trailLine);
+scene.addLayer(movingPoint);
+```
+
+## ArcLayer 弧线动画
+
+```javascript
+import { LineLayer } from '@antv/l7';
+
+const odData = [
+ {
+ from_lng: 120.19,
+ from_lat: 30.26,
+ to_lng: 121.47,
+ to_lat: 31.23,
+ value: 100,
+ },
+];
+
+const arcLayer = new LineLayer()
+ .source(odData, {
+ parser: {
+ type: 'json',
+ x: 'from_lng',
+ y: 'from_lat',
+ x1: 'to_lng',
+ y1: 'to_lat',
+ },
+ })
+ .shape('arc')
+ .size(2)
+ .color('#5B8FF9')
+ .animate({
+ enable: true,
+ interval: 0.3,
+ trailLength: 0.4,
+ duration: 3,
+ })
+ .style({
+ opacity: 0.8,
+ });
+
+scene.addLayer(arcLayer);
+```
+
+## 时序数据动画
+
+### 时间轴控制
+
+```javascript
+// 带时间戳的数据
+const timeSeriesData = [
+ { lng: 120.19, lat: 30.26, time: 1609459200000, value: 10 },
+ { lng: 120.2, lat: 30.27, time: 1609545600000, value: 20 },
+ { lng: 120.21, lat: 30.28, time: 1609632000000, value: 30 },
+];
+
+let currentTime = timeSeriesData[0].time;
+
+function updateVisualization(time) {
+ const filteredData = timeSeriesData.filter((d) => d.time <= time);
+ layer.setData(filteredData);
+}
+
+// 播放动画
+let animationTimer = setInterval(() => {
+ currentTime += 86400000; // 增加一天
+ updateVisualization(currentTime);
+
+ if (currentTime >= timeSeriesData[timeSeriesData.length - 1].time) {
+ clearInterval(animationTimer);
+ }
+}, 100);
+```
+
+## 相机动画
+
+### 飞行动画
+
+```javascript
+// 飞到指定位置
+scene.flyTo({
+ center: [120.19, 30.26],
+ zoom: 12,
+ pitch: 45,
+ bearing: 30,
+ duration: 2000, // 动画时长(毫秒)
+});
+```
+
+### 环绕动画
+
+```javascript
+let bearing = 0;
+
+function rotate() {
+ bearing = (bearing + 0.5) % 360;
+ scene.setBearing(bearing);
+ requestAnimationFrame(rotate);
+}
+
+rotate();
+```
+
+## 动画控制
+
+### 开始/暂停/重置
+
+```javascript
+// 开始动画
+layer.animate(true);
+
+// 暂停动画
+layer.animate(false);
+
+// 重置并重新开始
+layer.animate(false);
+layer.animate(true);
+```
+
+### 动画事件
+
+```javascript
+layer.on('animatestart', () => {
+ console.log('动画开始');
+});
+
+layer.on('animateend', () => {
+ console.log('动画结束');
+});
+```
+
+## 性能优化
+
+### 1. 降低数据密度
+
+```javascript
+// 对复杂路径进行简化
+import * as turf from '@turf/turf';
+
+const simplified = turf.simplify(pathData, {
+ tolerance: 0.01,
+ highQuality: false,
+});
+```
+
+### 2. 控制动画数量
+
+```javascript
+// 限制同时播放的动画数量
+const MAX_ANIMATIONS = 10;
+
+if (scene.getLayers().filter((l) => l.isAnimating()).length < MAX_ANIMATIONS) {
+ layer.animate(true);
+}
+```
+
+### 3. 使用 requestAnimationFrame
+
+```javascript
+function animate() {
+ // 更新状态
+ updateData();
+
+ requestAnimationFrame(animate);
+}
+
+animate();
+```
+
+## 完整示例:出租车轨迹
+
+```javascript
+import { Scene, LineLayer, PointLayer } from '@antv/l7';
+import { GaodeMap } from '@antv/l7-maps';
+
+const scene = new Scene({
+ id: 'map',
+ map: new GaodeMap({
+ center: [120.19, 30.26],
+ zoom: 13,
+ }),
+});
+
+// 轨迹数据
+const trajectoryData = {
+ type: 'FeatureCollection',
+ features: [
+ {
+ type: 'Feature',
+ properties: { taxi_id: 'T001', speed: 40 },
+ geometry: {
+ type: 'LineString',
+ coordinates: [
+ [120.19, 30.26],
+ [120.195, 30.265],
+ [120.2, 30.27],
+ [120.205, 30.275],
+ [120.21, 30.28],
+ ],
+ },
+ },
+ ],
+};
+
+scene.on('loaded', () => {
+ // 1. 历史路径(灰色)
+ const historyPath = new LineLayer().source(trajectoryData).shape('line').size(3).color('#ddd');
+
+ // 2. 运动轨迹(蓝色,带拖尾)
+ const movingTrail = new LineLayer()
+ .source(trajectoryData)
+ .shape('line')
+ .size(4)
+ .color('#5B8FF9')
+ .animate({
+ enable: true,
+ interval: 0.3,
+ trailLength: 0.4,
+ duration: 6,
+ });
+
+ // 3. 运动的点(出租车)
+ const taxi = new PointLayer()
+ .source(trajectoryData)
+ .shape('circle')
+ .size(12)
+ .color('#FF6B3B')
+ .animate({
+ enable: true,
+ interval: 0.3,
+ duration: 6,
+ });
+
+ scene.addLayer(historyPath);
+ scene.addLayer(movingTrail);
+ scene.addLayer(taxi);
+});
+```
+
+## 相关文档
+
+- [line.md](../layers/line.md) - LineLayer 详细配置
+- [point.md](../layers/point.md) - PointLayer 详细配置
+- [events.md](../interaction/events.md) - 事件处理
diff --git a/skills/l7/references/core/map-types.md b/skills/l7/references/core/map-types.md
new file mode 100644
index 0000000..2226048
--- /dev/null
+++ b/skills/l7/references/core/map-types.md
@@ -0,0 +1,580 @@
+---
+skill_id: map-types
+skill_name: 地图引擎配置
+category: core
+difficulty: beginner
+tags: [map, gaode, mapbox, maplibre, map-engine]
+dependencies: []
+version: 2.x
+---
+
+# 地图引擎配置
+
+## 技能描述
+
+L7 支持多种地图引擎作为底图,包括高德地图、Mapbox、MapLibre 以及独立的 Map 引擎。选择合适的地图引擎是创建 L7 可视化项目的第一步。
+
+## 何时使用
+
+- 🗺️ **高德地图(GaodeMap)**:国内项目,需要国内地图服务和 POI 数据
+- 🌍 **Mapbox**:国际项目,需要精美的国际地图样式
+- 🆓 **MapLibre**:开源项目,离线部署,自定义地图服务
+- 📐 **Map(独立引擎)**:室内地图、游戏地图、不需要地理底图的场景
+
+## 前置条件
+
+- 已安装 `@antv/l7`
+- 已安装对应的地图库(如 `@antv/l7-maps`)
+
+## 地图引擎对比
+
+| 特性 | GaodeMap | Mapbox | MapLibre | Map |
+| ---------- | -------- | ---------- | ---------- | ---------- |
+| 国内服务 | ✅ 优秀 | ❌ 需翻墙 | ✅ 可用 | ✅ 不依赖 |
+| 国际服务 | ❌ 较弱 | ✅ 优秀 | ✅ 优秀 | ✅ 不依赖 |
+| 需要 Token | ✅ 是 | ✅ 是 | ❌ 否 | ❌ 否 |
+| 离线部署 | ❌ 否 | ❌ 否 | ✅ 是 | ✅ 是 |
+| POI 数据 | ✅ 丰富 | ✅ 有 | ❌ 无 | ❌ 无 |
+| 自定义样式 | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
+| 坐标系统 | 经纬度 | 经纬度 | 经纬度 | 平面坐标 |
+| 适用场景 | 国内地理 | 国际地理 | 自建服务 | 非地理场景 |
+
+## 代码示例
+
+### 1. 高德地图(GaodeMap)- 推荐国内使用
+
+高德地图是国内最常用的地图服务,提供丰富的 POI 数据和路网信息。
+
+#### 基础用法
+
+```javascript
+import { Scene } from '@antv/l7';
+import { GaodeMap } from '@antv/l7-maps';
+
+const scene = new Scene({
+ id: 'map',
+ map: new GaodeMap({
+ style: 'dark', // 地图样式: dark | light | normal | satellite
+ center: [120.19, 30.26], // 中心点 [经度, 纬度]
+ pitch: 0, // 倾斜角度 0-60
+ zoom: 10, // 缩放级别 0-22
+ token: 'your-amap-key', // 高德地图 Key(可选,建议配置)
+ }),
+});
+
+scene.on('loaded', () => {
+ console.log('地图加载完成');
+ // 添加图层
+});
+```
+
+#### 申请高德地图 Token
+
+1. 访问 [高德开放平台](https://lbs.amap.com/)
+2. 注册账号并创建应用
+3. 获取 Web 端(JS API)Key
+4. 在代码中配置 `token` 参数
+
+⚠️ **注意**:虽然 token 是可选的,但建议配置以避免服务限制。
+
+#### 地图样式
+
+高德地图支持多种内置样式:
+
+```javascript
+// 暗色主题(适合数据可视化)
+map: new GaodeMap({ style: 'dark' });
+
+// 亮色主题
+map: new GaodeMap({ style: 'light' });
+
+// 标准主题
+map: new GaodeMap({ style: 'normal' });
+
+// 卫星影像
+map: new GaodeMap({ style: 'satellite' });
+
+// 自定义样式(使用高德平台的自定义样式)
+map: new GaodeMap({
+ style: 'amap://styles/你的样式ID?isPublic=true',
+});
+```
+
+#### 3D 视角配置
+
+```javascript
+const scene = new Scene({
+ id: 'map',
+ map: new GaodeMap({
+ style: 'dark',
+ center: [120.19, 30.26],
+ zoom: 14,
+ pitch: 45, // 倾斜角度,用于 3D 效果
+ bearing: 30, // 旋转角度
+ }),
+});
+```
+
+#### 传入已有高德地图实例
+
+如果项目中已经创建了高德地图实例,可以直接传入:
+
+```javascript
+// 先创建高德地图实例
+const map = new AMap.Map('map', {
+ viewMode: '3D', // 3D 模式
+ resizeEnable: true,
+ zoom: 11,
+ center: [116.397428, 39.90923],
+});
+
+// 传入 L7 Scene
+const scene = new Scene({
+ id: 'map',
+ map: new GaodeMap({
+ mapInstance: map, // 传入地图实例
+ }),
+});
+```
+
+⚠️ **注意**:
+
+- Scene 的 id 参数需要与地图容器一致
+- 需要自行引入高德地图 API
+- 建议设置 `viewMode: '3D'`(高德 2.0 支持 2D 模式)
+
+#### 使用高德地图插件
+
+```javascript
+const scene = new Scene({
+ id: 'map',
+ map: new GaodeMap({
+ center: [116.475, 39.99],
+ zoom: 13,
+ plugin: ['AMap.ToolBar', 'AMap.LineSearch'], // 注册插件
+ }),
+});
+
+scene.on('loaded', () => {
+ // 使用插件
+ window.AMap.plugin(['AMap.ToolBar', 'AMap.LineSearch'], () => {
+ // 添加工具条
+ scene.map.addControl(new AMap.ToolBar());
+
+ // 使用公交线路搜索
+ const linesearch = new AMap.LineSearch({
+ pageIndex: 1,
+ city: '北京',
+ extensions: 'all',
+ });
+ });
+});
+```
+
+### 2. 独立地图引擎(Map)- 推荐无底图场景
+
+Map 是 L7 内置的独立地图引擎,完全不依赖第三方地图服务,适合室内地图、游戏地图等场景。
+
+#### 基础用法
+
+```javascript
+import { Scene } from '@antv/l7';
+import { Map } from '@antv/l7-maps';
+
+const scene = new Scene({
+ id: 'map',
+ map: new Map({
+ center: [0, 0], // 使用平面坐标,不是经纬度
+ zoom: 3,
+ style: 'blank', // 空白背景
+ minZoom: 0,
+ maxZoom: 18,
+ }),
+});
+
+scene.on('loaded', () => {
+ // Map 使用平面坐标系统
+ const data = [
+ { x: 100, y: 100, value: 10 },
+ { x: 200, y: 200, value: 20 },
+ { x: 300, y: 150, value: 15 },
+ ];
+
+ const pointLayer = new PointLayer()
+ .source(data, {
+ parser: {
+ type: 'json',
+ x: 'x', // 平面 X 坐标
+ y: 'y', // 平面 Y 坐标
+ },
+ })
+ .shape('circle')
+ .size(10)
+ .color('value', ['#ffffcc', '#800026']);
+
+ scene.addLayer(pointLayer);
+});
+```
+
+#### Map 的特点
+
+✅ **优势**:
+
+- 无需第三方地图服务(高德、Mapbox)Key
+- 完全离线可用
+- 使用平面坐标系统,不受经纬度限制
+- 适合室内地图、游戏地图、抽象数据可视化
+- 可以自由添加瓦片底图
+
+❌ **限制**:
+
+- 没有内置地理数据和 POI
+- 需要自己提供底图(或使用空白背景)
+
+#### 添加自定义瓦片底图
+
+```javascript
+import { Scene, RasterLayer } from '@antv/l7';
+import { Map } from '@antv/l7-maps';
+
+const scene = new Scene({
+ id: 'map',
+ map: new Map({
+ center: [120.19, 30.26],
+ zoom: 10,
+ }),
+});
+
+scene.on('loaded', () => {
+ // 添加高德地图瓦片作为底图
+ const layer = new RasterLayer();
+ layer.source(
+ 'https://webrd0{1-3}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}',
+ {
+ parser: {
+ type: 'rasterTile',
+ tileSize: 256,
+ minZoom: 2,
+ maxZoom: 18,
+ },
+ },
+ );
+ scene.addLayer(layer);
+});
+```
+
+#### 室内地图示例
+
+```javascript
+const scene = new Scene({
+ id: 'map',
+ map: new Map({
+ center: [0, 0],
+ zoom: 4,
+ style: 'blank',
+ }),
+});
+
+scene.on('loaded', () => {
+ // 加载室内地图数据(平面坐标)
+ fetch('/indoor-map.json')
+ .then((res) => res.json())
+ .then((data) => {
+ const polygonLayer = new PolygonLayer()
+ .source(data, {
+ parser: {
+ type: 'json',
+ coordinates: 'coordinates', // 平面坐标数组
+ },
+ })
+ .shape('fill')
+ .color('type', {
+ 会议室: '#4575b4',
+ 办公区: '#74add1',
+ 休息区: '#fee090',
+ })
+ .style({
+ opacity: 0.8,
+ });
+
+ scene.addLayer(polygonLayer);
+ });
+});
+```
+
+### 3. Mapbox(国际项目)
+
+```javascript
+import { Scene } from '@antv/l7';
+import { Mapbox } from '@antv/l7-maps';
+
+const scene = new Scene({
+ id: 'map',
+ map: new Mapbox({
+ style: 'mapbox://styles/mapbox/streets-v11', // Mapbox 样式
+ center: [120.19, 30.26],
+ zoom: 10,
+ token: 'your-mapbox-token', // Mapbox Token(必需)
+ }),
+});
+```
+
+#### 申请 Mapbox Token
+
+1. 访问 [Mapbox 官网](https://www.mapbox.com/)
+2. 注册账号
+3. 在 Account 页面获取 Access Token
+4. 配置 `token` 参数
+
+### 4. MapLibre(开源方案)
+
+```javascript
+import { Scene } from '@antv/l7';
+import { Maplibre } from '@antv/l7-maps';
+
+const scene = new Scene({
+ id: 'map',
+ map: new Maplibre({
+ style: 'https://your-server.com/style.json', // 自定义样式 JSON
+ center: [120.19, 30.26],
+ zoom: 10,
+ }),
+});
+```
+
+MapLibre 完全开源,不需要 Token,适合自建地图服务。
+
+## 地图配置参数
+
+所有地图引擎支持的通用配置参数:
+
+| 参数 | 类型 | 默认值 | 说明 |
+| --------- | ---------------- | ------- | --------------------------------- |
+| `center` | [number, number] | [0, 0] | 地图中心点 [经度, 纬度] 或 [x, y] |
+| `zoom` | number | 0 | 缩放级别 (0-22) |
+| `pitch` | number | 0 | 倾斜角度 (0-60) |
+| `bearing` | number | 0 | 旋转角度 (0-360) |
+| `minZoom` | number | 0 | 最小缩放级别 |
+| `maxZoom` | number | 22 | 最大缩放级别 |
+| `style` | string | 'light' | 地图样式 |
+
+### 高德地图特有参数
+
+| 参数 | 类型 | 说明 |
+| ------------- | -------- | ------------------------ |
+| `token` | string | 高德地图 Key(推荐配置) |
+| `plugin` | string[] | 要加载的高德插件数组 |
+| `mapInstance` | AMap.Map | 已有的高德地图实例 |
+
+### Mapbox 特有参数
+
+| 参数 | 类型 | 说明 |
+| ------- | ------ | --------------------------- |
+| `token` | string | Mapbox Access Token(必需) |
+
+## 选择建议
+
+### 国内项目推荐:GaodeMap
+
+```javascript
+✅ 优势:
+- 国内服务稳定快速
+- 丰富的 POI 和路网数据
+- 支持多种地图样式
+- 完善的中文文档
+
+❌ 限制:
+- 需要申请 Token
+- 国际数据相对较弱
+```
+
+### 无底图场景推荐:Map
+
+```javascript
+✅ 优势:
+- 完全离线可用
+- 不需要任何 Token
+- 使用灵活的平面坐标
+- 可自定义底图
+
+❌ 限制:
+- 没有内置地理数据
+- 需要自己处理坐标系统
+```
+
+### 国际项目推荐:Mapbox 或 MapLibre
+
+```javascript
+✅ Mapbox 优势:
+- 精美的地图样式
+- 全球数据完善
+- 强大的自定义能力
+
+✅ MapLibre 优势:
+- 完全开源免费
+- 不需要 Token
+- 支持离线部署
+```
+
+## 实际应用场景
+
+### 1. 国内城市可视化(使用 GaodeMap)
+
+```javascript
+import { Scene, PointLayer } from '@antv/l7';
+import { GaodeMap } from '@antv/l7-maps';
+
+const scene = new Scene({
+ id: 'map',
+ map: new GaodeMap({
+ style: 'dark',
+ center: [116.404, 39.915], // 北京
+ zoom: 10,
+ token: 'your-amap-key',
+ }),
+});
+
+scene.on('loaded', () => {
+ // 显示 POI 数据
+ const poiLayer = new PointLayer()
+ .source(poiData, {
+ parser: { type: 'json', x: 'lng', y: 'lat' },
+ })
+ .shape('circle')
+ .size(8)
+ .color('category', ['#FF6B6B', '#4ECDC4', '#95E1D3']);
+
+ scene.addLayer(poiLayer);
+});
+```
+
+### 2. 室内导航(使用 Map)
+
+```javascript
+import { Scene, LineLayer } from '@antv/l7';
+import { Map } from '@antv/l7-maps';
+
+const scene = new Scene({
+ id: 'map',
+ map: new Map({
+ center: [0, 0],
+ zoom: 5,
+ style: 'blank',
+ }),
+});
+
+scene.on('loaded', () => {
+ // 加载室内地图
+ fetch('/indoor-layout.json')
+ .then((res) => res.json())
+ .then((data) => {
+ // 使用平面坐标绘制室内布局
+ const layoutLayer = new PolygonLayer()
+ .source(data)
+ .shape('fill')
+ .color('roomType', colorMap)
+ .style({ opacity: 0.6 });
+
+ scene.addLayer(layoutLayer);
+ });
+});
+```
+
+### 3. 游戏地图(使用 Map)
+
+```javascript
+const scene = new Scene({
+ id: 'map',
+ map: new Map({
+ center: [500, 500], // 游戏世界中心
+ zoom: 4,
+ style: 'blank',
+ minZoom: 2,
+ maxZoom: 8,
+ }),
+});
+
+scene.on('loaded', () => {
+ // 绘制游戏元素(使用平面坐标)
+ const playerLayer = new PointLayer()
+ .source(players, {
+ parser: {
+ type: 'json',
+ x: 'posX',
+ y: 'posY',
+ },
+ })
+ .shape('player-icon')
+ .size(20);
+
+ scene.addLayer(playerLayer);
+});
+```
+
+## 常见问题
+
+### Q: 如何选择地图引擎?
+
+A:
+
+- **国内地理项目** → GaodeMap
+- **室内地图/游戏** → Map
+- **国际项目** → Mapbox 或 MapLibre
+- **自建服务/离线** → Map 或 MapLibre
+
+### Q: 高德地图不配置 Token 会怎样?
+
+A: 可以正常使用,但可能有请求限制。建议申请 Token 以获得更稳定的服务。
+
+### Q: Map 引擎可以显示地理地图吗?
+
+A: 可以,通过 RasterLayer 加载地图瓦片服务作为底图即可。
+
+### Q: 坐标系统有什么区别?
+
+A:
+
+- **GaodeMap/Mapbox/MapLibre**:使用经纬度坐标(WGS84)
+ - 经度范围:-180 ~ 180
+ - 纬度范围:-90 ~ 90
+- **Map**:使用平面坐标
+ - 任意数值范围
+ - 适合非地理场景
+
+### Q: 切换地图引擎会影响图层代码吗?
+
+A: 基本不会。L7 统一了不同底图的接口,图层代码基本相同。唯一区别是坐标系统(经纬度 vs 平面坐标)。
+
+### Q: 可以在运行时切换地图样式吗?
+
+A: 可以,使用 `scene.setMapStyle(style)` 方法:
+
+```javascript
+// 切换为暗色主题
+scene.setMapStyle('dark');
+
+// 切换为自定义样式
+scene.setMapStyle('amap://styles/your-style-id');
+```
+
+## 注意事项
+
+⚠️ **Token 安全**:不要在公开的代码仓库中暴露 Token,建议使用环境变量
+
+⚠️ **坐标系统**:确保数据坐标系统与地图引擎匹配
+
+⚠️ **网络依赖**:GaodeMap、Mapbox 需要网络连接;Map 可完全离线
+
+⚠️ **性能考虑**:Map 引擎在大数据量时性能更好,因为没有底图渲染开销
+
+## 相关技能
+
+- [场景初始化](./scene.md)
+- [场景生命周期](./scene-lifecycle.md)
+- [场景方法](./scene-methods.md)
+- [点图层](../layers/point.md)
+
+## 在线示例
+
+- [高德地图示例](https://l7.antv.antgroup.com/examples/tutorial/map#amap)
+- [Map 引擎示例](https://l7.antv.antgroup.com/examples/tutorial/map#map)
diff --git a/skills/l7/references/core/scene-lifecycle.md b/skills/l7/references/core/scene-lifecycle.md
new file mode 100644
index 0000000..618fb06
--- /dev/null
+++ b/skills/l7/references/core/scene-lifecycle.md
@@ -0,0 +1,476 @@
+---
+skill_id: scene-lifecycle
+skill_name: 场景生命周期管理
+category: core
+difficulty: intermediate
+tags: [scene, lifecycle, events, destroy]
+dependencies: [scene-initialization]
+version: 2.x
+---
+
+# 场景生命周期管理
+
+## 技能描述
+
+管理 L7 场景的完整生命周期,包括场景加载、运行时事件监听、以及场景销毁。理解和正确使用生命周期事件对于构建稳定的地图应用至关重要。
+
+## 何时使用
+
+- ✅ 需要在场景加载完成后执行初始化操作
+- ✅ 监听地图的缩放、平移等交互事件
+- ✅ 监听容器大小变化,实现响应式布局
+- ✅ 页面卸载或组件销毁时清理资源
+- ✅ 调试时需要开启/关闭实时渲染
+
+## 前置条件
+
+- 已完成[场景初始化](./scene.md)
+
+## 核心概念
+
+### 生命周期阶段
+
+L7 场景的生命周期分为三个主要阶段:
+
+1. **初始化阶段**:创建 Scene 实例
+2. **运行阶段**:场景加载完成,可以添加图层、监听事件
+3. **销毁阶段**:场景被销毁,释放资源
+
+```
+创建 Scene → loaded 事件 → 添加图层/监听事件 → 销毁 Scene
+```
+
+## 代码示例
+
+### 场景加载事件
+
+#### loaded 事件 - 场景初始化完成
+
+最重要的生命周期事件,所有图层和组件都应该在此事件后添加。
+
+```javascript
+import { Scene } from '@antv/l7';
+import { GaodeMap } from '@antv/l7-maps';
+
+const scene = new Scene({
+ id: 'map',
+ map: new GaodeMap({
+ center: [120.19, 30.26],
+ zoom: 10,
+ }),
+});
+
+// 场景加载完成后执行
+scene.on('loaded', () => {
+ console.log('场景已加载完成,可以添加图层');
+
+ // 在这里添加图层
+ const pointLayer = new PointLayer()
+ .source(data, { parser: { type: 'json', x: 'lng', y: 'lat' } })
+ .shape('circle')
+ .size(10)
+ .color('#5B8FF9');
+
+ scene.addLayer(pointLayer);
+});
+```
+
+#### resize 事件 - 容器大小变化
+
+当地图容器大小改变时触发,适用于响应式布局。
+
+```javascript
+scene.on('resize', () => {
+ console.log('地图容器大小已改变');
+ // 可以在这里重新布局或调整图层
+});
+```
+
+**常见场景**:
+
+```javascript
+// 监听窗口大小变化,自动调整地图
+window.addEventListener('resize', () => {
+ // Scene 会自动触发 resize 事件
+});
+```
+
+### 地图交互事件
+
+#### 缩放事件
+
+```javascript
+// 缩放级别更改后触发
+scene.on('zoomchange', (e) => {
+ console.log('当前缩放级别:', scene.getZoom());
+});
+
+// 缩放开始时触发
+scene.on('zoomstart', () => {
+ console.log('开始缩放');
+});
+
+// 缩放停止时触发
+scene.on('zoomend', () => {
+ console.log('缩放结束');
+});
+```
+
+#### 地图移动事件
+
+```javascript
+// 地图平移时触发
+scene.on('mapmove', () => {
+ console.log('地图正在移动');
+});
+
+// 地图平移开始时触发
+scene.on('movestart', () => {
+ console.log('开始移动');
+});
+
+// 地图移动结束后触发(包括平移和缩放导致的中心点变化)
+scene.on('moveend', () => {
+ const center = scene.getCenter();
+ console.log('移动结束,当前中心点:', center);
+});
+```
+
+#### 拖拽事件
+
+```javascript
+// 开始拖拽地图时触发
+scene.on('dragstart', (e) => {
+ console.log('开始拖拽');
+});
+
+// 拖拽地图过程中触发
+scene.on('dragging', (e) => {
+ console.log('正在拖拽');
+});
+
+// 停止拖拽地图时触发
+scene.on('dragend', (e) => {
+ console.log('拖拽结束');
+});
+```
+
+### 鼠标事件
+
+```javascript
+// 鼠标点击
+scene.on('click', (e) => {
+ console.log('点击位置:', e.lngLat);
+});
+
+// 鼠标双击
+scene.on('dblclick', (e) => {
+ console.log('双击位置:', e.lngLat);
+});
+
+// 鼠标移动
+scene.on('mousemove', (e) => {
+ // 高频事件,注意性能
+});
+
+// 鼠标右键
+scene.on('contextmenu', (e) => {
+ e.preventDefault(); // 阻止默认右键菜单
+ console.log('右键点击位置:', e.lngLat);
+});
+
+// 鼠标进入/离开地图容器
+scene.on('mouseover', () => {
+ console.log('鼠标进入地图');
+});
+
+scene.on('mouseout', () => {
+ console.log('鼠标离开地图');
+});
+
+// 鼠标按下/抬起
+scene.on('mousedown', (e) => {
+ console.log('鼠标按下');
+});
+
+scene.on('mouseup', (e) => {
+ console.log('鼠标抬起');
+});
+
+// 鼠标滚轮
+scene.on('mousewheel', (e) => {
+ console.log('鼠标滚轮缩放');
+});
+```
+
+### 移除事件监听
+
+```javascript
+// 定义事件处理函数
+const handleClick = (e) => {
+ console.log('点击位置:', e.lngLat);
+};
+
+// 绑定事件
+scene.on('click', handleClick);
+
+// 移除事件监听
+scene.off('click', handleClick);
+```
+
+### 场景销毁
+
+#### destroy 方法
+
+离开页面或不再需要地图时,必须调用 destroy 方法释放资源。
+
+```javascript
+// 销毁场景
+scene.destroy();
+```
+
+**React 示例**:
+
+```javascript
+import { useEffect } from 'react';
+import { Scene } from '@antv/l7';
+import { GaodeMap } from '@antv/l7-maps';
+
+function MapComponent() {
+ useEffect(() => {
+ const scene = new Scene({
+ id: 'map',
+ map: new GaodeMap({
+ center: [120.19, 30.26],
+ zoom: 10,
+ }),
+ });
+
+ scene.on('loaded', () => {
+ // 添加图层
+ });
+
+ // 组件卸载时销毁场景
+ return () => {
+ scene.destroy();
+ };
+ }, []);
+
+ return
;
+}
+```
+
+**Vue 示例**:
+
+```javascript
+import { onMounted, onBeforeUnmount } from 'vue';
+import { Scene } from '@antv/l7';
+import { GaodeMap } from '@antv/l7-maps';
+
+export default {
+ setup() {
+ let scene = null;
+
+ onMounted(() => {
+ scene = new Scene({
+ id: 'map',
+ map: new GaodeMap({
+ center: [120.19, 30.26],
+ zoom: 10,
+ }),
+ });
+
+ scene.on('loaded', () => {
+ // 添加图层
+ });
+ });
+
+ onBeforeUnmount(() => {
+ if (scene) {
+ scene.destroy();
+ }
+ });
+
+ return {};
+ },
+};
+```
+
+#### destroy 事件
+
+场景销毁时触发的事件。
+
+```javascript
+scene.on('destroy', () => {
+ console.log('场景已销毁');
+ // 可以在这里执行额外的清理工作
+});
+
+scene.destroy();
+```
+
+### 调试相关事件
+
+#### 开启/关闭实时渲染
+
+L7 默认按需重绘以节省资源。调试时可以开启实时渲染,便于使用 SpectorJS 等工具捕捉帧渲染。
+
+```javascript
+// 开启实时渲染(用于调试)
+scene.startAnimate();
+
+// 停止实时渲染
+scene.stopAnimate();
+```
+
+#### WebGL 上下文丢失
+
+```javascript
+scene.on('webglcontextlost', () => {
+ console.error('WebGL 上下文丢失');
+ // 可以提示用户刷新页面
+});
+```
+
+## 实际应用场景
+
+### 1. 响应式地图
+
+```javascript
+const scene = new Scene({
+ id: 'map',
+ map: new GaodeMap({
+ center: [120.19, 30.26],
+ zoom: 10,
+ }),
+});
+
+scene.on('loaded', () => {
+ addLayers();
+});
+
+// 容器大小变化时重新布局
+scene.on('resize', () => {
+ // 根据新尺寸调整图层或控件
+ updateLayout();
+});
+```
+
+### 2. 缩放级别联动
+
+```javascript
+scene.on('loaded', () => {
+ const layer = new PointLayer().source(data).shape('circle').size(10).color('#5B8FF9');
+
+ scene.addLayer(layer);
+});
+
+// 根据缩放级别调整图层显示
+scene.on('zoomchange', () => {
+ const zoom = scene.getZoom();
+
+ if (zoom < 10) {
+ // 小级别显示聚合数据
+ layer.hide();
+ clusterLayer.show();
+ } else {
+ // 大级别显示详细数据
+ layer.show();
+ clusterLayer.hide();
+ }
+});
+```
+
+### 3. 地图移动加载数据
+
+```javascript
+scene.on('moveend', () => {
+ const bounds = scene.getBounds();
+
+ // 根据可视范围加载数据
+ fetchDataInBounds(bounds).then((data) => {
+ layer.setData(data);
+ });
+});
+```
+
+### 4. 交互提示
+
+```javascript
+scene.on('loaded', () => {
+ const layer = new PointLayer().source(data).shape('circle').size(10).color('#5B8FF9');
+
+ scene.addLayer(layer);
+
+ // 图层点击事件
+ layer.on('click', (e) => {
+ console.log('点击了点:', e.feature.properties);
+ });
+});
+
+// 全局点击事件
+scene.on('click', (e) => {
+ console.log('点击了地图:', e.lngLat);
+});
+```
+
+## 常见问题
+
+### Q: 为什么必须在 loaded 事件后添加图层?
+
+A: 场景需要完成初始化(包括 WebGL 上下文、地图底图等)才能正确渲染图层。在 loaded 之前添加图层可能导致渲染失败。
+
+### Q: 如何避免内存泄漏?
+
+A: 确保在页面/组件卸载时调用 `scene.destroy()`,并移除所有事件监听器。
+
+```javascript
+// ❌ 错误:忘记销毁
+useEffect(() => {
+ const scene = new Scene({...});
+ // 没有返回清理函数
+}, []);
+
+// ✅ 正确:销毁场景
+useEffect(() => {
+ const scene = new Scene({...});
+ return () => scene.destroy();
+}, []);
+```
+
+### Q: 事件监听器会自动移除吗?
+
+A: 调用 `scene.destroy()` 时会自动移除所有事件监听器。但如果需要临时移除某个监听器,应该手动调用 `scene.off()`。
+
+### Q: resize 事件什么时候触发?
+
+A: 当地图容器的 DOM 元素大小改变时触发。通常由以下情况引起:
+
+- 窗口大小改变
+- 父容器大小改变
+- CSS 样式动态修改
+
+### Q: 场景销毁后还能重新使用吗?
+
+A: 不能。销毁后的 Scene 实例无法恢复,需要重新创建新的 Scene 实例。
+
+## 注意事项
+
+⚠️ **性能优化**:避免在高频事件(mousemove、mapmove)中执行复杂计算
+
+⚠️ **内存管理**:确保销毁场景时同时销毁所有图层和组件
+
+⚠️ **事件顺序**:某些事件有先后顺序,如 zoomstart → zoomchange → zoomend
+
+⚠️ **异步操作**:在 loaded 事件中进行异步数据加载时注意错误处理
+
+## 相关技能
+
+- [场景初始化](./scene.md)
+- [场景方法](./scene-methods.md)
+- [事件处理](../interaction/events.md)
+- [点图层](../layers/point.md)
+
+## 在线示例
+
+查看更多示例:[L7 官方示例](https://l7.antv.antgroup.com/examples)
diff --git a/skills/l7/references/core/scene-methods.md b/skills/l7/references/core/scene-methods.md
new file mode 100644
index 0000000..e31afad
--- /dev/null
+++ b/skills/l7/references/core/scene-methods.md
@@ -0,0 +1,827 @@
+---
+skill_id: scene-methods
+skill_name: 场景方法
+category: core
+difficulty: intermediate
+tags: [scene, methods, api, map-control]
+dependencies: [scene-initialization]
+version: 2.x
+---
+
+# 场景方法
+
+## 技能描述
+
+掌握 L7 Scene 提供的各种方法,包括图层管理、控件管理、地图操作、坐标转换、资源管理等核心功能。
+
+## 何时使用
+
+- ✅ 动态添加/移除图层
+- ✅ 添加地图控件(缩放、比例尺等)
+- ✅ 控制地图视角(中心点、缩放、旋转)
+- ✅ 坐标系统转换
+- ✅ 管理全局图片资源
+- ✅ 添加 Popup 和 Marker
+- ✅ 导出地图图片
+
+## 前置条件
+
+- 已完成[场景初始化](./scene.md)
+
+## 图层管理方法
+
+### addLayer(layer): void
+
+将图层添加到场景中。
+
+```javascript
+import { Scene, PointLayer } from '@antv/l7';
+
+const scene = new Scene({...});
+
+scene.on('loaded', () => {
+ const pointLayer = new PointLayer()
+ .source(data, { parser: { type: 'json', x: 'lng', y: 'lat' } })
+ .shape('circle')
+ .size(10)
+ .color('#5B8FF9');
+
+ scene.addLayer(pointLayer);
+});
+```
+
+### getLayers(): ILayer[]
+
+获取所有图层。
+
+```javascript
+const layers = scene.getLayers();
+console.log('图层数量:', layers.length);
+
+layers.forEach((layer) => {
+ console.log('图层ID:', layer.id);
+});
+```
+
+### getLayer(id: string): ILayer
+
+根据图层 ID 获取图层。
+
+```javascript
+const layer = scene.getLayer('layer-id');
+if (layer) {
+ layer.show();
+}
+```
+
+### getLayerByName(name: string): ILayer
+
+根据图层名称获取图层。
+
+```javascript
+const layer = new PointLayer({ name: 'myPointLayer' })
+ .source(data)
+ .shape('circle')
+ .size(10)
+ .color('#5B8FF9');
+
+scene.addLayer(layer);
+
+// 通过名称获取
+const foundLayer = scene.getLayerByName('myPointLayer');
+```
+
+### removeLayer(layer: ILayer): void
+
+移除并销毁图层。
+
+```javascript
+const layer = scene.getLayer('layer-id');
+scene.removeLayer(layer);
+// 图层已被销毁,不能再使用
+```
+
+⚠️ **注意**:移除图层的同时会自动销毁图层,释放资源。
+
+### removeAllLayer(): void
+
+移除并销毁所有图层。
+
+```javascript
+scene.removeAllLayer();
+// 所有图层已被移除和销毁
+```
+
+## 控件管理方法
+
+### addControl(control: IControl): void
+
+添加控件到场景。
+
+```javascript
+import { Scene, Zoom, Scale } from '@antv/l7';
+
+const scene = new Scene({...});
+
+// 添加缩放控件
+const zoomControl = new Zoom({
+ position: 'topright'
+});
+scene.addControl(zoomControl);
+
+// 添加比例尺控件
+const scaleControl = new Scale({
+ position: 'bottomleft'
+});
+scene.addControl(scaleControl);
+```
+
+### removeControl(control: IControl): void
+
+移除控件。
+
+```javascript
+const zoomControl = new Zoom({ position: 'topright' });
+scene.addControl(zoomControl);
+
+// 移除控件
+scene.removeControl(zoomControl);
+```
+
+### getControlByName(name: string): IControl
+
+根据控件名称获取控件。
+
+```javascript
+const zoomControl = new Zoom({
+ name: 'myZoom',
+ position: 'topright',
+});
+scene.addControl(zoomControl);
+
+// 通过名称获取
+const control = scene.getControlByName('myZoom');
+```
+
+## Popup 管理方法
+
+### addPopup(popup: Popup): void
+
+添加 Popup 弹窗。
+
+```javascript
+import { Popup } from '@antv/l7';
+
+const popup = new Popup({
+ offsets: [0, 20],
+ closeButton: true,
+})
+ .setLnglat([120.19, 30.26])
+ .setHTML('这是一个 Popup
');
+
+scene.addPopup(popup);
+```
+
+### removePopup(popup: Popup): void
+
+移除 Popup 弹窗。
+
+```javascript
+scene.removePopup(popup);
+```
+
+## Marker 管理方法
+
+### addMarker(marker: IMarker): void
+
+添加 Marker 标记。
+
+```javascript
+import { Marker } from '@antv/l7';
+
+const el = document.createElement('div');
+el.className = 'marker-custom';
+el.innerHTML = '📍';
+
+const marker = new Marker({ element: el }).setLnglat([120.19, 30.26]);
+
+scene.addMarker(marker);
+```
+
+### addMarkerLayer(layer: IMarkerLayer): void
+
+添加 MarkerLayer 统一管理多个 Marker。
+
+```javascript
+import { MarkerLayer } from '@antv/l7';
+
+const markerLayer = new MarkerLayer();
+
+data.forEach((item) => {
+ const el = document.createElement('div');
+ el.textContent = item.name;
+
+ const marker = new Marker({ element: el }).setLnglat([item.lng, item.lat]);
+
+ markerLayer.addMarker(marker);
+});
+
+scene.addMarkerLayer(markerLayer);
+```
+
+### removeMarkerLayer(layer: IMarkerLayer): void
+
+移除 MarkerLayer。
+
+```javascript
+scene.removeMarkerLayer(markerLayer);
+```
+
+### removeAllMarkers(): void
+
+移除所有 Marker。
+
+```javascript
+scene.removeAllMarkers();
+```
+
+## 地图视角控制方法
+
+### getZoom(): number
+
+获取当前缩放级别。
+
+```javascript
+const zoom = scene.getZoom();
+console.log('当前缩放级别:', zoom);
+```
+
+### setZoom(zoom: number): void
+
+设置缩放级别(0-22)。
+
+```javascript
+scene.setZoom(12);
+```
+
+### zoomIn(): void
+
+地图放大一级。
+
+```javascript
+scene.zoomIn();
+```
+
+### zoomOut(): void
+
+地图缩小一级。
+
+```javascript
+scene.zoomOut();
+```
+
+### getCenter(): ILngLat
+
+获取地图中心点。
+
+```javascript
+const center = scene.getCenter();
+console.log('中心点:', center); // { lng: 120.19, lat: 30.26 }
+```
+
+### setCenter(center: [number, number], options?: ICameraOptions): void
+
+设置地图中心点。
+
+```javascript
+// 基础用法
+scene.setCenter([120.19, 30.26]);
+
+// 带偏移的中心点
+scene.setCenter([120.19, 30.26], {
+ padding: {
+ top: 100,
+ bottom: 50,
+ left: 100,
+ right: 100,
+ },
+});
+
+// 数组形式的 padding
+scene.setCenter([120.19, 30.26], {
+ padding: [100, 50, 100, 100], // top, right, bottom, left
+});
+
+// 单个数值(四边相同)
+scene.setCenter([120.19, 30.26], {
+ padding: 50,
+});
+```
+
+### setZoomAndCenter(zoom: number, center: [number, number]): void
+
+同时设置缩放级别和中心点。
+
+```javascript
+scene.setZoomAndCenter(12, [120.19, 30.26]);
+```
+
+### getPitch(): number
+
+获取地图倾斜角度(0-60)。
+
+```javascript
+const pitch = scene.getPitch();
+console.log('倾斜角度:', pitch);
+```
+
+### setPitch(pitch: number): void
+
+设置地图倾斜角度(用于 3D 效果)。
+
+```javascript
+scene.setPitch(45); // 设置为 45 度倾斜
+```
+
+### setRotation(rotation: number): void
+
+设置地图顺时针旋转角度(0-360)。
+
+```javascript
+scene.setRotation(90); // 旋转 90 度
+```
+
+### panTo(lnglat: [number, number]): void
+
+地图平移到指定经纬度。
+
+```javascript
+scene.panTo([120.19, 30.26]);
+```
+
+### panBy(x: number, y: number): void
+
+以像素为单位平移地图。
+
+```javascript
+// 向右平移 100px,向下平移 50px
+scene.panBy(100, 50);
+
+// 向左平移 100px,向上平移 50px
+scene.panBy(-100, -50);
+```
+
+### fitBounds(bounds: [[number, number], [number, number]], options?: IOptions): void
+
+地图缩放到指定范围。
+
+```javascript
+// 基础用法
+scene.fitBounds([
+ [112, 32], // 西南角 [minLng, minLat]
+ [114, 35], // 东北角 [maxLng, maxLat]
+]);
+
+// 带动画
+scene.fitBounds(
+ [
+ [112, 32],
+ [114, 35],
+ ],
+ { animate: true },
+);
+```
+
+## 地图样式和状态方法
+
+### setMapStyle(style: string): void
+
+设置地图样式。
+
+```javascript
+// L7 内置样式
+scene.setMapStyle('dark'); // 暗色
+scene.setMapStyle('light'); // 亮色
+scene.setMapStyle('normal'); // 正常
+
+// Mapbox 样式
+scene.setMapStyle('mapbox://styles/mapbox/streets-v11');
+
+// 高德样式
+scene.setMapStyle('amap://styles/2a09079c3daac9420ee53b67307a8006?isPublic=true');
+```
+
+### setMapStatus(options: IStatusOptions): void
+
+设置地图交互状态。
+
+```javascript
+scene.setMapStatus({
+ dragEnable: true, // 是否允许拖拽
+ keyboardEnable: true, // 是否允许键盘操作
+ doubleClickZoom: true, // 是否允许双击缩放
+ zoomEnable: true, // 是否允许缩放
+ rotateEnable: true, // 是否允许旋转
+ showIndoorMap: false, // 是否显示室内地图
+ resizeEnable: true, // 是否自动调整大小
+});
+
+// 禁用所有交互
+scene.setMapStatus({
+ dragEnable: false,
+ zoomEnable: false,
+ rotateEnable: false,
+});
+```
+
+## 容器和尺寸方法
+
+### getContainer(): HTMLElement | null
+
+获取地图容器 DOM 元素。
+
+```javascript
+const container = scene.getContainer();
+console.log('容器宽度:', container.offsetWidth);
+console.log('容器高度:', container.offsetHeight);
+```
+
+### getSize(): [number, number]
+
+获取地图容器宽高。
+
+```javascript
+const [width, height] = scene.getSize();
+console.log(`容器尺寸: ${width} x ${height}`);
+```
+
+## 坐标转换方法
+
+### lngLatToContainer(lnglat: [number, number]): IPoint
+
+经纬度转换为容器像素坐标。
+
+```javascript
+const point = scene.lngLatToContainer([120.19, 30.26]);
+console.log('容器坐标:', point); // { x: 256, y: 128 }
+```
+
+### containerToLngLat(point: [number, number]): ILngLat
+
+容器像素坐标转换为经纬度。
+
+```javascript
+const lnglat = scene.containerToLngLat([256, 128]);
+console.log('经纬度:', lnglat); // { lng: 120.19, lat: 30.26 }
+```
+
+### lngLatToPixel(lnglat: [number, number]): IPoint
+
+经纬度转换为屏幕像素坐标。
+
+```javascript
+const pixel = scene.lngLatToPixel([120.19, 30.26]);
+console.log('像素坐标:', pixel); // { x: 512, y: 384 }
+```
+
+### pixelToLngLat(pixel: [number, number]): ILngLat
+
+屏幕像素坐标转换为经纬度。
+
+```javascript
+const lnglat = scene.pixelToLngLat([512, 384]);
+console.log('经纬度:', lnglat); // { lng: 120.19, lat: 30.26 }
+```
+
+**坐标系统说明**:
+
+- **容器坐标**:相对于地图容器左上角的坐标,原点 (0, 0) 在容器左上角
+- **像素坐标**:地图的绝对像素坐标,考虑了地图的缩放和平移
+
+## 全局资源管理方法
+
+### addImage(id: string, img: HTMLImageElement | string | File): void
+
+添加全局图片资源,供图层使用。
+
+```javascript
+// 添加网络图片
+scene.addImage('marker-icon', 'https://example.com/marker.png');
+
+// 添加本地图片元素
+const img = document.getElementById('my-image');
+scene.addImage('custom-icon', img);
+
+// 在图层中使用
+const layer = new PointLayer().source(data).shape('marker-icon').size(20);
+```
+
+### hasImage(id: string): boolean
+
+判断是否已添加某个图片资源。
+
+```javascript
+if (!scene.hasImage('marker-icon')) {
+ scene.addImage('marker-icon', 'https://example.com/marker.png');
+}
+```
+
+### removeImage(id: string): void
+
+删除全局图片资源。
+
+```javascript
+scene.removeImage('marker-icon');
+```
+
+### addFontFace(fontFamily: string, fontPath: string): void
+
+添加字体文件(用于 iconfont)。
+
+```javascript
+const fontFamily = 'iconfont';
+const fontPath = '//at.alicdn.com/t/font_2534097_iiet9d3nekn.woff2?t=1620444089776';
+
+scene.addFontFace(fontFamily, fontPath);
+
+// 在图层中使用
+const layer = new PointLayer().source(data).shape('icon', 'text').style({
+ fontFamily: 'iconfont',
+ iconfont: true,
+});
+```
+
+### addIconFont(name: string, unicode: string): void
+
+添加 iconfont 映射。
+
+```javascript
+scene.addIconFont('home', '');
+scene.addIconFont('location', '');
+
+// 在数据中使用名称
+const data = [
+ { lng: 120, lat: 30, icon: 'home' },
+ { lng: 121, lat: 31, icon: 'location' },
+];
+
+const layer = new PointLayer().source(data).shape('icon', 'text').style({
+ fontFamily: 'iconfont',
+ iconfont: true,
+});
+```
+
+### addIconFonts(options: Array<[string, string]>): void
+
+批量添加 iconfont 映射。
+
+```javascript
+scene.addIconFonts([
+ ['home', ''],
+ ['location', ''],
+ ['star', ''],
+]);
+```
+
+## 静态方法
+
+### Scene.addProtocol(protocol: string, handler: Function)
+
+添加自定义数据协议(用于加载特殊格式瓦片)。
+
+```javascript
+// 自定义协议
+Scene.addProtocol('custom', (params, callback) => {
+ fetch(`https://${params.url.split('://')[1]}`)
+ .then((response) => {
+ if (response.status === 200) {
+ response.arrayBuffer().then((buffer) => {
+ callback(null, buffer, null, null);
+ });
+ } else {
+ callback(new Error(`加载失败: ${response.statusText}`));
+ }
+ })
+ .catch((error) => {
+ callback(new Error(error));
+ });
+
+ return { cancel: () => {} };
+});
+
+// 使用自定义协议
+const source = new Source('custom://your-tile-url/{z}/{x}/{y}', {
+ parser: {
+ type: 'mvt',
+ tileSize: 256,
+ },
+});
+```
+
+**PMTiles 示例**:
+
+```javascript
+import * as pmtiles from 'pmtiles';
+
+const protocol = new pmtiles.Protocol();
+Scene.addProtocol('pmtiles', protocol.tile);
+
+const source = new Source('pmtiles://https://example.com/tiles.pmtiles', {
+ parser: {
+ type: 'mvt',
+ tileSize: 256,
+ maxZoom: 14,
+ },
+});
+```
+
+### Scene.removeProtocol(protocol: string)
+
+删除自定义协议。
+
+```javascript
+Scene.removeProtocol('custom');
+```
+
+## 导出和调试方法
+
+### exportMap(type?: 'png' | 'jpg'): string
+
+导出地图为图片(仅导出可视化层,不包含底图)。
+
+```javascript
+// 导出为 PNG
+const pngDataURL = scene.exportMap('png');
+
+// 导出为 JPG
+const jpgDataURL = scene.exportMap('jpg');
+
+// 下载图片
+const link = document.createElement('a');
+link.download = 'map.png';
+link.href = pngDataURL;
+link.click();
+```
+
+### getPointSizeRange(): Float32Array
+
+获取当前设备支持的 WebGL 点精灵大小范围。
+
+```javascript
+const [minSize, maxSize] = scene.getPointSizeRange();
+console.log(`点大小范围: ${minSize} - ${maxSize}`);
+```
+
+### startAnimate(): void
+
+开启实时渲染(用于调试)。
+
+```javascript
+scene.startAnimate();
+// 便于使用 SpectorJS 等工具捕捉帧渲染
+```
+
+### stopAnimate(): void
+
+停止实时渲染。
+
+```javascript
+scene.stopAnimate();
+```
+
+## 实际应用场景
+
+### 1. 动态切换图层
+
+```javascript
+const layers = {
+ point: new PointLayer()...,
+ line: new LineLayer()...,
+ polygon: new PolygonLayer()...
+};
+
+function showLayer(type) {
+ // 移除所有图层
+ scene.removeAllLayer();
+
+ // 添加指定图层
+ scene.addLayer(layers[type]);
+}
+
+// 切换图层
+showLayer('point');
+```
+
+### 2. 地图导航
+
+```javascript
+function flyTo(city) {
+ const cities = {
+ beijing: [116.404, 39.915],
+ shanghai: [121.473, 31.23],
+ guangzhou: [113.264, 23.129],
+ };
+
+ scene.setCenter(cities[city]);
+ scene.setZoom(12);
+}
+
+// 飞到北京
+flyTo('beijing');
+```
+
+### 3. 数据范围适配
+
+```javascript
+function fitData(data) {
+ const lngs = data.map((d) => d.lng);
+ const lats = data.map((d) => d.lat);
+
+ const bounds = [
+ [Math.min(...lngs), Math.min(...lats)],
+ [Math.max(...lngs), Math.max(...lats)],
+ ];
+
+ scene.fitBounds(bounds);
+}
+```
+
+### 4. 响应式控件
+
+```javascript
+function updateControls() {
+ const [width] = scene.getSize();
+
+ // 移动端隐藏部分控件
+ if (width < 768) {
+ scene.removeControl(zoomControl);
+ } else {
+ scene.addControl(zoomControl);
+ }
+}
+
+scene.on('resize', updateControls);
+```
+
+## 常见问题
+
+### Q: 图层添加后看不到?
+
+A: 检查:
+
+1. 是否在 `loaded` 事件后添加
+2. 图层数据是否正确
+3. 图层样式是否配置
+4. 地图中心和缩放级别是否合适
+
+### Q: 坐标转换结果不准确?
+
+A: 确保在地图加载完成后进行坐标转换,并且使用正确的坐标系统。
+
+### Q: 如何禁用地图交互?
+
+A: 使用 `setMapStatus` 方法:
+
+```javascript
+scene.setMapStatus({
+ dragEnable: false,
+ zoomEnable: false,
+ rotateEnable: false,
+});
+```
+
+### Q: 如何监听地图属性变化?
+
+A: 使用生命周期事件:
+
+```javascript
+scene.on('zoomchange', () => {
+ console.log('缩放级别:', scene.getZoom());
+});
+
+scene.on('moveend', () => {
+ console.log('中心点:', scene.getCenter());
+});
+```
+
+## 注意事项
+
+⚠️ **内存管理**:使用 `removeLayer` 会自动销毁图层,无需手动调用 `layer.destroy()`
+
+⚠️ **坐标系统**:区分容器坐标和像素坐标的使用场景
+
+⚠️ **资源管理**:及时移除不需要的全局资源(图片、字体)
+
+⚠️ **导出限制**:`exportMap` 只能导出 L7 图层,不包含底图
+
+## 相关技能
+
+- [场景初始化](./scene.md)
+- [场景生命周期](./scene-lifecycle.md)
+- [图层管理](../layers/point.md)
+- [交互组件](../interaction/components.md)
+- [事件处理](../interaction/events.md)
+
+## 在线示例
+
+查看更多示例:[L7 官方示例](https://l7.antv.antgroup.com/examples)
diff --git a/skills/l7/references/core/scene.md b/skills/l7/references/core/scene.md
new file mode 100644
index 0000000..dcfa7e4
--- /dev/null
+++ b/skills/l7/references/core/scene.md
@@ -0,0 +1,357 @@
+---
+skill_id: scene-initialization
+skill_name: 场景初始化
+category: core
+difficulty: beginner
+tags: [scene, initialization, map, setup]
+dependencies: []
+version: 2.x
+---
+
+# 场景初始化
+
+## 技能描述
+
+创建 L7 场景对象(Scene),这是使用 L7 的第一步。Scene 是包含地图、图层、组件的全局容器。
+
+## 何时使用
+
+- 开始任何 L7 可视化项目
+- 需要在页面中嵌入地图
+- 需要管理多个图层
+
+## 前置条件
+
+- HTML 页面中存在用于地图渲染的 DOM 容器
+- 已安装 `@antv/l7` 和地图底图库(如 `@antv/l7-maps`)
+
+## 输入参数
+
+### 必需参数
+
+| 参数 | 类型 | 说明 |
+| ----- | --------------------- | ------------------------------------------------ |
+| `id` | string \| HTMLElement | DOM 容器 ID 或 DOM 元素 |
+| `map` | MapInstance | 地图实例 (GaodeMap \| Mapbox \| Maplibre \| Map) |
+
+### 地图配置参数
+
+| 参数 | 类型 | 默认值 | 说明 |
+| --------- | ---------------- | ------- | ----------------------------------------------- |
+| `style` | string | 'light' | 地图样式 (dark \| light \| normal \| satellite) |
+| `center` | [number, number] | [0, 0] | 地图中心点 [经度, 纬度] |
+| `zoom` | number | 0 | 缩放级别 (0-22) |
+| `pitch` | number | 0 | 倾斜角度 (0-60) |
+| `bearing` | number | 0 | 旋转角度 (0-360) |
+| `minZoom` | number | 0 | 最小缩放级别 |
+| `maxZoom` | number | 22 | 最大缩放级别 |
+
+### 场景配置参数
+
+| 参数 | 类型 | 默认值 | 说明 |
+| ----------------------- | ------- | ------------ | ---------------------------- |
+| `logoVisible` | boolean | true | 是否显示 L7 Logo |
+| `logoPosition` | string | 'bottomleft' | Logo 位置 |
+| `antialias` | boolean | true | 是否开启抗锯齿 |
+| `stencil` | boolean | false | 是否开启裁剪(遮罩功能需要) |
+| `preserveDrawingBuffer` | boolean | false | 是否保留缓冲区数据 |
+
+## 输出
+
+返回 `Scene` 实例,可以用于:
+
+- 添加图层: `scene.addLayer(layer)`
+- 添加组件: `scene.addControl(control)`
+- 监听事件: `scene.on('loaded', callback)`
+- 获取地图: `scene.getMapService()`
+
+## 代码示例
+
+### 基础用法 - 高德地图
+
+```javascript
+import { Scene } from '@antv/l7';
+import { GaodeMap } from '@antv/l7-maps';
+
+// 确保 HTML 中有对应的容器
+//
+
+const scene = new Scene({
+ id: 'map',
+ map: new GaodeMap({
+ style: 'dark',
+ center: [120.19382669582967, 30.258134],
+ pitch: 0,
+ zoom: 12,
+ }),
+});
+
+// 等待场景加载完成
+scene.on('loaded', () => {
+ console.log('地图加载完成');
+ // 在这里添加图层
+});
+```
+
+### 使用 Mapbox
+
+```javascript
+import { Scene } from '@antv/l7';
+import { Mapbox } from '@antv/l7-maps';
+
+const scene = new Scene({
+ id: 'map',
+ map: new Mapbox({
+ style: 'mapbox://styles/mapbox/streets-v11',
+ center: [120.19, 30.26],
+ zoom: 10,
+ token: 'your-mapbox-token',
+ }),
+});
+```
+
+### 使用 MapLibre(离线部署)
+
+```javascript
+import { Scene } from '@antv/l7';
+import { Maplibre } from '@antv/l7-maps';
+
+const scene = new Scene({
+ id: 'map',
+ map: new Maplibre({
+ style: 'https://your-server.com/style.json',
+ center: [120.19, 30.26],
+ zoom: 10,
+ }),
+});
+```
+
+### 使用独立地图 Map(无第三方依赖)
+
+Map 是 L7 内置的独立地图引擎,不依赖任何第三方地图服务,适合室内地图、游戏地图等场景。
+
+```javascript
+import { Scene } from '@antv/l7';
+import { Map } from '@antv/l7-maps';
+
+const scene = new Scene({
+ id: 'map',
+ map: new Map({
+ center: [0, 0], // 使用平面坐标
+ zoom: 3,
+ style: 'blank', // 空白背景
+ }),
+});
+
+scene.on('loaded', () => {
+ // Map 使用平面坐标系统,不是经纬度
+ const data = [
+ { x: 100, y: 100, value: 10 },
+ { x: 200, y: 200, value: 20 },
+ ];
+
+ const pointLayer = new PointLayer()
+ .source(data, {
+ parser: {
+ type: 'json',
+ x: 'x',
+ y: 'y',
+ },
+ })
+ .shape('circle')
+ .size(10)
+ .color('#5B8FF9');
+
+ scene.addLayer(pointLayer);
+});
+```
+
+**Map 的特点**:
+
+- ✅ 无需第三方地图服务(高德、Mapbox)Key
+- ✅ 完全离线可用
+- ✅ 使用平面坐标系统
+- ✅ 适合室内地图、游戏地图、抽象数据可视化
+
+### 3D 视角配置
+
+```javascript
+const scene = new Scene({
+ id: 'map',
+ map: new GaodeMap({
+ style: 'dark',
+ center: [120.19, 30.26],
+ zoom: 14,
+ pitch: 45, // 倾斜角度,用于 3D 效果
+ bearing: 30, // 旋转角度
+ }),
+});
+```
+
+### 自定义 Logo 位置
+
+```javascript
+const scene = new Scene({
+ id: 'map',
+ logoVisible: true,
+ logoPosition: 'bottomright', // bottomleft | topright | bottomleft | topleft
+ map: new GaodeMap({
+ style: 'light',
+ center: [120.19, 30.26],
+ zoom: 10,
+ }),
+});
+```
+
+### 开启遮罩功能
+
+```javascript
+const scene = new Scene({
+ id: 'map',
+ stencil: true, // 必须开启才能使用 Mask 功能
+ map: new GaodeMap({
+ style: 'dark',
+ center: [120.19, 30.26],
+ zoom: 10,
+ }),
+});
+```
+
+### 使用 DOM 元素
+
+```javascript
+const mapContainer = document.getElementById('my-map');
+
+const scene = new Scene({
+ id: mapContainer,
+ map: new GaodeMap({
+ center: [120.19, 30.26],
+ zoom: 10,
+ }),
+});
+```
+
+## HTML 页面配置
+
+```html
+
+
+
+
+ L7 地图
+
+
+
+
+
+
+
+```
+
+## 常见问题
+
+**地理地图**(GaodeMap、Mapbox、Maplibre)使用 **WGS84** 坐标系统(经纬度):
+
+- 经度范围: -180 ~ 180
+- 纬度范围: -90 ~ 90
+- 格式: [经度, 纬度] 或 [lng, lat]
+
+中国常用城市坐标参考:
+
+- 北京: [116.404, 39.915]
+- 上海: [121.473, 31.230]
+- 杭州: [120.155, 30.274]
+- 深圳: [114.057, 22.543]
+
+**独立地图**(Map)使用**平面坐标系统**:
+
+- X 轴:横向坐标(任意数值)
+- Y 轴:纵向坐标(任意数值)
+- 格式: [x, y]
+- 适合室内地图、游戏地图等非地理场景
+ document.addEventListener('DOMContentLoaded', () => {
+ const scene = new Scene({
+ id: 'map',
+ map: new GaodeMap({...})
+ });
+ });
+
+````
+
+### 2. 高德地图 Key 配置
+
+高德地图需要申请 Key:
+
+```javascript
+import { GaodeMap } from '@antv/l7-maps';
+
+const scene = new Scene({
+ id: 'map',
+ map: new GaodeMap({
+ style: 'dark',
+ center: [120, 30],
+ zoom: 10,
+ token: 'your-amap-key' // 高德地图 Key
+ })
+});
+````
+
+### 3. 地图未显示
+
+**检查清单**:
+
+- ✅ 容器是否有高度(必须设置具体高度,不能为 0)
+- ✅ 是否正确引入了底图库
+- ✅ 控制台是否有错误信息
+- ✅ 网络是否正常(在线底图需要联网)
+
+### 4. 坐标系统
+
+L7 使用 **WGS84** 坐标系统(经纬度):
+
+- 经度范围: -180 ~ 180
+- 纬度范围: -90 ~ 90
+- 格式: [经度, 纬度] 或 [lng, lat]
+
+中国常用城市坐标参考:
+
+- 北京: [116.404, 39.915]
+- 上海: [121.473, 31.230]
+- 杭州: [120.155, 30.274]
+- 深圳: [114.057, 22.543]
+
+## 场景生命周期事件
+
+```javascript
+// 场景加载完成
+scene.on('loaded', () => {
+ console.log('场景加载完成');
+});
+
+// 场景销毁
+scene.on('destroy', () => {
+ console.log('场景已销毁');
+});
+```
+
+## 相关技能
+
+- [场景生命周期管理](./scene-lifecycle.md)
+- [场景方法](./scene-methods.md)
+- [添加图层](../layers/point.md)
+
+## 下一步
+
+场景初始化完成后,你可以:
+
+1. [添加点图层显示数据](../layers/point.md)
+2. [添加交互控件](../interaction/components.md)
+3. [处理用户交互事件](../interaction/events.md)
diff --git a/skills/l7/references/data/source-csv.md b/skills/l7/references/data/source-csv.md
new file mode 100644
index 0000000..44339cc
--- /dev/null
+++ b/skills/l7/references/data/source-csv.md
@@ -0,0 +1,566 @@
+---
+skill_id: source-csv
+skill_name: CSV 数据源
+category: data
+difficulty: beginner
+tags: [csv, data, source, table, 数据源, 表格]
+dependencies: []
+version: 2.x
+---
+
+# CSV 数据源
+
+## 技能描述
+
+使用 CSV(逗号分隔值)格式的数据作为图层数据源,适合处理表格数据。
+
+## 何时使用
+
+- ✅ 数据来自 Excel、数据库导出
+- ✅ 简单的点位数据
+- ✅ 统计数据、业务数据
+- ✅ 需要在 Excel 中编辑数据
+- ✅ 数据量较大但结构简单
+
+## CSV 格式说明
+
+CSV 是一种简单的文本格式,每行代表一条记录,字段之间用逗号分隔。
+
+### 基本格式
+
+```csv
+lng,lat,name,value,type
+120.19,30.26,杭州,100,city
+121.47,31.23,上海,200,city
+116.40,39.91,北京,300,capital
+```
+
+**格式要求**:
+
+- 第一行为字段名(表头)
+- 每行数据字段数量一致
+- 使用逗号分隔字段
+- 字段值包含逗号时需要用引号包裹
+
+## 代码示例
+
+### 基础用法 - 点数据
+
+```javascript
+import { PointLayer } from '@antv/l7';
+
+const csvData = `lng,lat,name,value,type
+120.19,30.26,杭州,100,city
+121.47,31.23,上海,200,city
+116.40,39.91,北京,300,capital`;
+
+const pointLayer = new PointLayer()
+ .source(csvData, {
+ parser: {
+ type: 'csv',
+ x: 'lng', // 经度字段
+ y: 'lat', // 纬度字段
+ },
+ })
+ .shape('circle')
+ .size('value', [5, 20])
+ .color('type', {
+ city: '#5B8FF9',
+ capital: '#FF6B3B',
+ });
+
+scene.addLayer(pointLayer);
+```
+
+### 从文件加载 CSV
+
+```javascript
+fetch('/data/cities.csv')
+ .then((res) => res.text())
+ .then((csvText) => {
+ const layer = new PointLayer()
+ .source(csvText, {
+ parser: {
+ type: 'csv',
+ x: 'lng',
+ y: 'lat',
+ },
+ })
+ .shape('circle')
+ .size(10)
+ .color('#5B8FF9');
+
+ scene.addLayer(layer);
+ });
+```
+
+### 使用 fetch 异步加载
+
+```javascript
+async function loadCSVData() {
+ const response = await fetch('/data/cities.csv');
+ const csvText = await response.text();
+
+ const layer = new PointLayer()
+ .source(csvText, {
+ parser: {
+ type: 'csv',
+ x: 'lng',
+ y: 'lat',
+ },
+ })
+ .shape('circle')
+ .size(8)
+ .color('#5B8FF9');
+
+ scene.addLayer(layer);
+}
+
+scene.on('loaded', () => {
+ loadCSVData();
+});
+```
+
+### 自定义分隔符
+
+```javascript
+// 使用制表符分隔的 TSV 文件
+const tsvData = `lng\tlat\tname\tvalue
+120.19\t30.26\t杭州\t100
+121.47\t31.23\t上海\t200`;
+
+const layer = new PointLayer()
+ .source(tsvData, {
+ parser: {
+ type: 'csv',
+ x: 'lng',
+ y: 'lat',
+ delimiter: '\t', // 指定分隔符为制表符
+ },
+ })
+ .shape('circle')
+ .size(10)
+ .color('#5B8FF9');
+
+scene.addLayer(layer);
+```
+
+### 处理带引号的字段
+
+```javascript
+const csvData = `lng,lat,name,address,value
+120.19,30.26,杭州,"浙江省,杭州市",100
+121.47,31.23,上海,"上海市,黄浦区",200`;
+
+const layer = new PointLayer()
+ .source(csvData, {
+ parser: {
+ type: 'csv',
+ x: 'lng',
+ y: 'lat',
+ },
+ })
+ .shape('circle')
+ .size(10)
+ .color('#5B8FF9');
+
+scene.addLayer(layer);
+```
+
+### 数据类型转换
+
+```javascript
+const csvData = `lng,lat,name,value,active
+120.19,30.26,杭州,100,true
+121.47,31.23,上海,200,false`;
+
+const layer = new PointLayer()
+ .source(csvData, {
+ parser: {
+ type: 'csv',
+ x: 'lng',
+ y: 'lat',
+ },
+ transforms: [
+ {
+ type: 'map',
+ callback: (item) => {
+ // 转换数据类型
+ item.value = Number(item.value);
+ item.active = item.active === 'true';
+ return item;
+ },
+ },
+ ],
+ })
+ .shape('circle')
+ .size('value', [5, 20])
+ .color('active', ['#999', '#5B8FF9']);
+
+scene.addLayer(layer);
+```
+
+### OD 数据(起点-终点)
+
+```javascript
+const odCsvData = `from_lng,from_lat,to_lng,to_lat,count,type
+120.19,30.26,121.47,31.23,100,train
+120.19,30.26,116.40,39.91,200,plane`;
+
+const arcLayer = new LineLayer()
+ .source(odCsvData, {
+ parser: {
+ type: 'csv',
+ x: 'from_lng',
+ y: 'from_lat',
+ x1: 'to_lng',
+ y1: 'to_lat',
+ },
+ })
+ .shape('arc')
+ .size('count', [1, 5])
+ .color('type', {
+ train: '#5B8FF9',
+ plane: '#FF6B3B',
+ });
+
+scene.addLayer(arcLayer);
+```
+
+## Parser 配置选项
+
+### 必需参数
+
+| 参数 | 类型 | 说明 |
+| ------ | ------ | ----------------------- |
+| `type` | string | 必须设置为 'csv' |
+| `x` | string | 经度字段名(或 X 坐标) |
+| `y` | string | 纬度字段名(或 Y 坐标) |
+
+### 可选参数
+
+| 参数 | 类型 | 默认值 | 说明 |
+| ----------- | ------ | ------ | ----------------------- |
+| `x1` | string | - | 终点经度字段(OD 数据) |
+| `y1` | string | - | 终点纬度字段(OD 数据) |
+| `delimiter` | string | ',' | 字段分隔符 |
+
+## 数据示例
+
+### 点位数据
+
+```csv
+lng,lat,name,type,value,date
+120.19382669582967,30.258134,店铺A,restaurant,100,2024-01-01
+121.473701,31.230416,店铺B,cafe,200,2024-01-02
+116.404,39.915,店铺C,restaurant,150,2024-01-03
+```
+
+### 轨迹数据(路径点)
+
+```csv
+lng,lat,time,speed,status
+120.19,30.26,2024-01-01 10:00:00,60,normal
+120.20,30.27,2024-01-01 10:05:00,55,normal
+120.21,30.28,2024-01-01 10:10:00,45,slow
+```
+
+### 统计数据(区域)
+
+```csv
+region_id,region_name,center_lng,center_lat,population,gdp,area
+330100,杭州市,120.19,30.26,1200,18000,16850
+310100,上海市,121.47,31.23,2400,38000,6340
+```
+
+## 从 Excel 导出 CSV
+
+### Excel 导出步骤
+
+1. 打开 Excel 文件
+2. 点击"文件" → "另存为"
+3. 选择"CSV UTF-8(逗号分隔)(.csv)"
+4. 保存文件
+
+### 注意事项
+
+- ✅ 确保第一行是字段名
+- ✅ 经纬度列名要明确(如 lng, lat)
+- ✅ 使用 UTF-8 编码避免中文乱码
+- ✅ 日期格式要统一
+
+## 常见问题
+
+### 1. 中文乱码
+
+**原因**: CSV 文件编码不是 UTF-8
+
+**解决方案**:
+
+```javascript
+// 在读取时指定编码
+fetch('/data/cities.csv')
+ .then((res) => res.arrayBuffer())
+ .then((buffer) => {
+ const decoder = new TextDecoder('utf-8');
+ const csvText = decoder.decode(buffer);
+
+ layer.source(csvText, {
+ parser: {
+ type: 'csv',
+ x: 'lng',
+ y: 'lat',
+ },
+ });
+ });
+```
+
+或使用工具转换编码:
+
+```bash
+# 使用 iconv 转换编码
+iconv -f GB2312 -t UTF-8 input.csv > output.csv
+```
+
+### 2. 数据不显示
+
+**检查清单**:
+
+- ✅ 字段名是否正确(区分大小写)
+- ✅ 坐标值是否为数字
+- ✅ 是否有空行或格式错误
+- ✅ parser 配置是否正确
+
+```javascript
+// 调试:打印解析后的数据
+layer.on('add', () => {
+ console.log('图层数据:', layer.getSource().data);
+});
+```
+
+### 3. 数字被当作字符串
+
+CSV 中所有值默认都是字符串,需要手动转换:
+
+```javascript
+layer.source(csvData, {
+ parser: {
+ type: 'csv',
+ x: 'lng',
+ y: 'lat',
+ },
+ transforms: [
+ {
+ type: 'map',
+ callback: (item) => {
+ // 转换为数字类型
+ item.value = Number(item.value);
+ item.population = parseInt(item.population);
+ item.price = parseFloat(item.price);
+ return item;
+ },
+ },
+ ],
+});
+```
+
+### 4. 特殊字符处理
+
+包含逗号、引号、换行符的字段需要特殊处理:
+
+```csv
+name,description,value
+"产品A","价格:1,000元",100
+"产品B","说明:包含""引号""",200
+```
+
+## 性能优化
+
+### 1. 大文件处理
+
+```javascript
+// 使用 Web Worker 处理大文件
+const worker = new Worker('csv-parser-worker.js');
+
+worker.postMessage({ csvText: largeCSVData });
+
+worker.onmessage = (e) => {
+ const data = e.data;
+ layer.source(data, {
+ parser: {
+ type: 'json', // Worker 已经解析过,使用 json
+ x: 'lng',
+ y: 'lat',
+ },
+ });
+};
+```
+
+### 2. 数据抽稀
+
+```javascript
+layer.source(csvData, {
+ parser: {
+ type: 'csv',
+ x: 'lng',
+ y: 'lat',
+ },
+ transforms: [
+ {
+ type: 'filter',
+ callback: (item, index) => {
+ // 只显示每 10 条数据
+ return index % 10 === 0;
+ },
+ },
+ ],
+});
+```
+
+## CSV 转 GeoJSON
+
+```javascript
+function csvToGeoJSON(csvText, xField, yField) {
+ const lines = csvText.trim().split('\n');
+ const headers = lines[0].split(',');
+
+ const features = lines.slice(1).map((line) => {
+ const values = line.split(',');
+ const properties = {};
+
+ headers.forEach((header, i) => {
+ properties[header] = values[i];
+ });
+
+ return {
+ type: 'Feature',
+ properties: properties,
+ geometry: {
+ type: 'Point',
+ coordinates: [parseFloat(properties[xField]), parseFloat(properties[yField])],
+ },
+ };
+ });
+
+ return {
+ type: 'FeatureCollection',
+ features: features,
+ };
+}
+
+// 使用
+const csvText = `lng,lat,name,value
+120.19,30.26,杭州,100
+121.47,31.23,上海,200`;
+
+const geojson = csvToGeoJSON(csvText, 'lng', 'lat');
+
+layer.source(geojson, {
+ parser: { type: 'geojson' },
+});
+```
+
+## 使用第三方库
+
+### PapaParse(推荐)
+
+```javascript
+import Papa from 'papaparse';
+
+Papa.parse(csvText, {
+ header: true, // 第一行作为字段名
+ dynamicTyping: true, // 自动类型转换
+ complete: (results) => {
+ layer.source(results.data, {
+ parser: {
+ type: 'json',
+ x: 'lng',
+ y: 'lat',
+ },
+ });
+ },
+});
+```
+
+### D3.js
+
+```javascript
+import * as d3 from 'd3';
+
+d3.csv('/data/cities.csv').then((data) => {
+ layer.source(data, {
+ parser: {
+ type: 'json',
+ x: 'lng',
+ y: 'lat',
+ },
+ });
+});
+```
+
+## 最佳实践
+
+### 1. 字段命名规范
+
+```csv
+# 推荐:使用英文字段名
+lng,lat,name,value,type
+
+# 不推荐:使用中文字段名
+经度,纬度,名称,数值,类型
+```
+
+### 2. 数据验证
+
+```javascript
+function validateCSVData(data) {
+ return data.every((item) => {
+ return (
+ !isNaN(item.lng) &&
+ !isNaN(item.lat) &&
+ item.lng >= -180 &&
+ item.lng <= 180 &&
+ item.lat >= -90 &&
+ item.lat <= 90
+ );
+ });
+}
+```
+
+### 3. 错误处理
+
+```javascript
+fetch('/data/cities.csv')
+ .then((res) => {
+ if (!res.ok) {
+ throw new Error('Failed to load CSV file');
+ }
+ return res.text();
+ })
+ .then((csvText) => {
+ layer.source(csvText, {
+ parser: {
+ type: 'csv',
+ x: 'lng',
+ y: 'lat',
+ },
+ });
+ })
+ .catch((error) => {
+ console.error('Error loading CSV:', error);
+ });
+```
+
+## 相关技能
+
+- [GeoJSON 数据源](./source-geojson.md)
+- [JSON 数据源](./source-json.md)
+- [数据解析配置](./source-parser.md)
+- [点图层](../layers/point.md)
+- [线图层](../layers/line.md)
+
+## 参考资源
+
+- [RFC 4180 - CSV 规范](https://tools.ietf.org/html/rfc4180)
+- [PapaParse - CSV 解析库](https://www.papaparse.com/)
+- [D3.js - CSV 函数](https://github.com/d3/d3-dsv)
diff --git a/skills/l7/references/data/source-geojson.md b/skills/l7/references/data/source-geojson.md
new file mode 100644
index 0000000..3421631
--- /dev/null
+++ b/skills/l7/references/data/source-geojson.md
@@ -0,0 +1,518 @@
+---
+skill_id: source-geojson
+skill_name: GeoJSON 数据源
+category: data
+difficulty: beginner
+tags: [geojson, data, source, geo, 数据源]
+dependencies: []
+version: 2.x
+---
+
+# GeoJSON 数据源
+
+## 技能描述
+
+使用 GeoJSON 格式的地理数据作为图层数据源,这是 L7 最常用的数据格式。
+
+## 何时使用
+
+- ✅ 处理地理空间数据(点、线、面)
+- ✅ 使用标准的地理数据格式
+- ✅ 数据包含几何信息和属性信息
+- ✅ 从 GIS 系统导出的数据
+- ✅ 行政区划、建筑、道路等矢量数据
+
+## GeoJSON 格式说明
+
+GeoJSON 是一种用于编码各种地理数据结构的格式,基于 JSON。
+
+### 基本结构
+
+```json
+{
+ "type": "FeatureCollection",
+ "features": [
+ {
+ "type": "Feature",
+ "properties": {
+ "name": "属性名称",
+ "value": 100
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [120.19, 30.26]
+ }
+ }
+ ]
+}
+```
+
+### 几何类型
+
+| 类型 | 说明 | coordinates 格式 |
+| ----------------- | ---- | -------------------------------------------- |
+| `Point` | 点 | `[lng, lat]` |
+| `LineString` | 线 | `[[lng, lat], [lng, lat], ...]` |
+| `Polygon` | 面 | `[[[lng, lat], [lng, lat], ...]]` |
+| `MultiPoint` | 多点 | `[[lng, lat], [lng, lat], ...]` |
+| `MultiLineString` | 多线 | `[[[lng, lat], ...], [[lng, lat], ...]]` |
+| `MultiPolygon` | 多面 | `[[[[lng, lat], ...]], [[[lng, lat], ...]]]` |
+
+## 代码示例
+
+### 基础用法 - 点数据
+
+```javascript
+import { PointLayer } from '@antv/l7';
+
+const pointData = {
+ type: 'FeatureCollection',
+ features: [
+ {
+ type: 'Feature',
+ properties: {
+ name: '杭州',
+ population: 1200,
+ type: 'city',
+ },
+ geometry: {
+ type: 'Point',
+ coordinates: [120.19, 30.26],
+ },
+ },
+ {
+ type: 'Feature',
+ properties: {
+ name: '上海',
+ population: 2400,
+ type: 'city',
+ },
+ geometry: {
+ type: 'Point',
+ coordinates: [121.47, 31.23],
+ },
+ },
+ ],
+};
+
+const pointLayer = new PointLayer()
+ .source(pointData, {
+ parser: {
+ type: 'geojson',
+ },
+ })
+ .shape('circle')
+ .size('population', [5, 20])
+ .color('type', ['#5B8FF9', '#5AD8A6']);
+
+scene.addLayer(pointLayer);
+```
+
+### 线数据 - LineString
+
+```javascript
+import { LineLayer } from '@antv/l7';
+
+const lineData = {
+ type: 'FeatureCollection',
+ features: [
+ {
+ type: 'Feature',
+ properties: {
+ name: '路线1',
+ type: 'highway',
+ },
+ geometry: {
+ type: 'LineString',
+ coordinates: [
+ [120.19, 30.26],
+ [120.2, 30.27],
+ [120.21, 30.28],
+ ],
+ },
+ },
+ ],
+};
+
+const lineLayer = new LineLayer()
+ .source(lineData, {
+ parser: {
+ type: 'geojson',
+ },
+ })
+ .shape('line')
+ .size(3)
+ .color('type', ['#5B8FF9', '#5AD8A6']);
+
+scene.addLayer(lineLayer);
+```
+
+### 面数据 - Polygon
+
+```javascript
+import { PolygonLayer } from '@antv/l7';
+
+const polygonData = {
+ type: 'FeatureCollection',
+ features: [
+ {
+ type: 'Feature',
+ properties: {
+ name: '浙江省',
+ adcode: '330000',
+ gdp: 82553,
+ },
+ geometry: {
+ type: 'Polygon',
+ coordinates: [
+ [
+ [118.0, 28.0],
+ [122.0, 28.0],
+ [122.0, 31.0],
+ [118.0, 31.0],
+ [118.0, 28.0],
+ ],
+ ],
+ },
+ },
+ ],
+};
+
+const polygonLayer = new PolygonLayer()
+ .source(polygonData, {
+ parser: {
+ type: 'geojson',
+ },
+ })
+ .shape('fill')
+ .color('gdp', ['#FFF5B8', '#FFAB5C', '#FF6B3B', '#CC2B12'])
+ .style({
+ opacity: 0.8,
+ });
+
+scene.addLayer(polygonLayer);
+```
+
+### 从 URL 加载 GeoJSON
+
+```javascript
+const layer = new PolygonLayer();
+
+fetch('https://gw.alipayobjects.com/os/basement_prod/d2e0e930-fd44-4fca-8872-c1037b0fee7b.json')
+ .then((res) => res.json())
+ .then((data) => {
+ layer
+ .source(data, {
+ parser: {
+ type: 'geojson',
+ },
+ })
+ .shape('fill')
+ .color('name', ['#5B8FF9', '#5AD8A6', '#5D7092'])
+ .style({
+ opacity: 0.8,
+ });
+
+ scene.addLayer(layer);
+ });
+```
+
+### 多面 - MultiPolygon
+
+```javascript
+const multiPolygonData = {
+ type: 'FeatureCollection',
+ features: [
+ {
+ type: 'Feature',
+ properties: {
+ name: '浙江省(含岛屿)',
+ },
+ geometry: {
+ type: 'MultiPolygon',
+ coordinates: [
+ // 主陆地
+ [
+ [
+ [118.0, 28.0],
+ [122.0, 28.0],
+ [122.0, 31.0],
+ [118.0, 31.0],
+ [118.0, 28.0],
+ ],
+ ],
+ // 岛屿1
+ [
+ [
+ [122.1, 30.0],
+ [122.2, 30.0],
+ [122.2, 30.1],
+ [122.1, 30.1],
+ [122.1, 30.0],
+ ],
+ ],
+ ],
+ },
+ },
+ ],
+};
+
+const layer = new PolygonLayer()
+ .source(multiPolygonData, {
+ parser: {
+ type: 'geojson',
+ },
+ })
+ .shape('fill')
+ .color('#5B8FF9');
+
+scene.addLayer(layer);
+```
+
+### 带孔洞的面
+
+```javascript
+const polygonWithHole = {
+ type: 'Feature',
+ properties: { name: '带孔洞的面' },
+ geometry: {
+ type: 'Polygon',
+ coordinates: [
+ // 外环(逆时针)
+ [
+ [118.0, 28.0],
+ [122.0, 28.0],
+ [122.0, 31.0],
+ [118.0, 31.0],
+ [118.0, 28.0],
+ ],
+ // 内环/孔洞(顺时针)
+ [
+ [119.0, 29.0],
+ [119.0, 30.0],
+ [121.0, 30.0],
+ [121.0, 29.0],
+ [119.0, 29.0],
+ ],
+ ],
+ },
+};
+```
+
+## 数据转换
+
+### 从普通数组转换为 GeoJSON
+
+```javascript
+// 原始数据
+const rawData = [
+ { lng: 120.19, lat: 30.26, name: '杭州', value: 100 },
+ { lng: 121.47, lat: 31.23, name: '上海', value: 200 },
+];
+
+// 转换为 GeoJSON
+const geojson = {
+ type: 'FeatureCollection',
+ features: rawData.map((item) => ({
+ type: 'Feature',
+ properties: {
+ name: item.name,
+ value: item.value,
+ },
+ geometry: {
+ type: 'Point',
+ coordinates: [item.lng, item.lat],
+ },
+ })),
+};
+
+layer.source(geojson, {
+ parser: { type: 'geojson' },
+});
+```
+
+### 使用工具库转换
+
+```javascript
+// 使用 turf.js
+import * as turf from '@turf/turf';
+
+const point = turf.point([120.19, 30.26], { name: '杭州' });
+const line = turf.lineString(
+ [
+ [120, 30],
+ [121, 31],
+ ],
+ { name: '路线' },
+);
+const polygon = turf.polygon(
+ [
+ [
+ [118, 28],
+ [122, 28],
+ [122, 31],
+ [118, 31],
+ [118, 28],
+ ],
+ ],
+ { name: '区域' },
+);
+
+layer.source(point, {
+ parser: { type: 'geojson' },
+});
+```
+
+## 常见问题
+
+### 1. 数据不显示
+
+**检查清单**:
+
+- ✅ coordinates 格式是否正确(经度在前,纬度在后)
+- ✅ 坐标范围是否合理(经度 -180~180,纬度 -90~90)
+- ✅ GeoJSON 结构是否完整
+- ✅ parser 类型是否设置为 'geojson'
+
+```javascript
+// 正确的格式
+coordinates: [120.19, 30.26]; // [经度, 纬度]
+
+// 错误的格式
+coordinates: [30.26, 120.19]; // [纬度, 经度] ❌
+```
+
+### 2. 面不闭合
+
+多边形的首尾坐标必须相同:
+
+```javascript
+// 正确 - 闭合
+coordinates: [
+ [
+ [118.0, 28.0],
+ [122.0, 28.0],
+ [122.0, 31.0],
+ [118.0, 31.0],
+ [118.0, 28.0], // 与第一个点相同
+ ],
+];
+
+// 错误 - 未闭合
+coordinates: [
+ [
+ [118.0, 28.0],
+ [122.0, 28.0],
+ [122.0, 31.0],
+ [118.0, 31.0], // 缺少闭合点 ❌
+ ],
+];
+```
+
+### 3. 数据格式验证
+
+使用在线工具验证 GeoJSON 格式:
+
+- [geojson.io](http://geojson.io/)
+- [GeoJSONLint](https://geojsonlint.com/)
+
+### 4. 性能优化
+
+对于复杂的 GeoJSON 数据:
+
+```javascript
+// 简化几何形状
+layer.source(data, {
+ parser: {
+ type: 'geojson',
+ },
+ transforms: [
+ {
+ type: 'simplify',
+ tolerance: 0.01, // 简化容差
+ },
+ ],
+});
+```
+
+## GeoJSON 规范
+
+### Feature 必需字段
+
+```javascript
+{
+ "type": "Feature", // 必需
+ "geometry": {}, // 必需
+ "properties": {} // 可选,但通常包含
+}
+```
+
+### FeatureCollection 结构
+
+```javascript
+{
+ "type": "FeatureCollection", // 必需
+ "features": [] // 必需,Feature 数组
+}
+```
+
+### 坐标顺序
+
+- **经度**(Longitude)在前,范围: -180 ~ 180
+- **纬度**(Latitude)在后,范围: -90 ~ 90
+- 格式: `[经度, 纬度]` 或 `[lng, lat]`
+
+### 环绕方向
+
+- **外环**: 逆时针
+- **内环**(孔洞): 顺时针
+
+## 最佳实践
+
+### 1. 数据结构清晰
+
+```javascript
+// 推荐:属性语义化
+{
+ "type": "Feature",
+ "properties": {
+ "id": "330100",
+ "name": "杭州市",
+ "type": "city",
+ "population": 1200,
+ "gdp": 18000
+ },
+ "geometry": {...}
+}
+```
+
+### 2. 合理使用属性
+
+```javascript
+// 在 properties 中存储可视化需要的数据
+layer.color('type', {...})
+layer.size('population', [5, 20])
+```
+
+### 3. 数据分层
+
+```javascript
+// 不同类型的数据使用不同图层
+const cityLayer = new PointLayer().source(cityGeoJSON);
+const provinceLayer = new PolygonLayer().source(provinceGeoJSON);
+```
+
+## 相关技能
+
+- [JSON 数据源](./source-json.md)
+- [CSV 数据源](./source-csv.md)
+- [数据解析配置](./source-parser.md)
+- [点图层](../layers/point.md)
+- [线图层](../layers/line.md)
+- [面图层](../layers/polygon.md)
+
+## 参考资源
+
+- [GeoJSON 规范](https://datatracker.ietf.org/doc/html/rfc7946)
+- [Turf.js - GeoJSON 工具库](https://turfjs.org/)
+- [geojson.io - 在线编辑器](http://geojson.io/)
diff --git a/skills/l7/references/data/source-json.md b/skills/l7/references/data/source-json.md
new file mode 100644
index 0000000..0948ce8
--- /dev/null
+++ b/skills/l7/references/data/source-json.md
@@ -0,0 +1,704 @@
+---
+skill_id: source-json
+skill_name: JSON 数据源
+category: data
+difficulty: beginner
+tags: [json, data, source, object, 数据源]
+dependencies: []
+version: 2.x
+---
+
+# JSON 数据源
+
+## 技能描述
+
+使用 JSON(JavaScript Object Notation)格式的数据作为图层数据源,适合结构化的业务数据。
+
+## 何时使用
+
+- ✅ API 返回的数据
+- ✅ 前端 JavaScript 对象
+- ✅ 业务数据、统计数据
+- ✅ 配置文件数据
+- ✅ 不需要地理拓扑结构的简单数据
+
+## JSON 格式说明
+
+JSON 是一种轻量级的数据交换格式,易于阅读和编写。
+
+### 基本格式
+
+```json
+[
+ {
+ "lng": 120.19,
+ "lat": 30.26,
+ "name": "杭州",
+ "value": 100,
+ "type": "city"
+ },
+ {
+ "lng": 121.47,
+ "lat": 31.23,
+ "name": "上海",
+ "value": 200,
+ "type": "city"
+ }
+]
+```
+
+## 代码示例
+
+### 基础用法 - 点数据
+
+```javascript
+import { PointLayer } from '@antv/l7';
+
+const data = [
+ { lng: 120.19, lat: 30.26, name: '杭州', value: 100, type: 'city' },
+ { lng: 121.47, lat: 31.23, name: '上海', value: 200, type: 'city' },
+ { lng: 116.4, lat: 39.91, name: '北京', value: 300, type: 'capital' },
+];
+
+const pointLayer = new PointLayer()
+ .source(data, {
+ parser: {
+ type: 'json',
+ x: 'lng', // 经度字段
+ y: 'lat', // 纬度字段
+ },
+ })
+ .shape('circle')
+ .size('value', [5, 20])
+ .color('type', {
+ city: '#5B8FF9',
+ capital: '#FF6B3B',
+ });
+
+scene.addLayer(pointLayer);
+```
+
+### 从 API 加载数据
+
+```javascript
+fetch('/api/cities')
+ .then((res) => res.json())
+ .then((data) => {
+ const layer = new PointLayer()
+ .source(data, {
+ parser: {
+ type: 'json',
+ x: 'lng',
+ y: 'lat',
+ },
+ })
+ .shape('circle')
+ .size(10)
+ .color('#5B8FF9');
+
+ scene.addLayer(layer);
+ });
+```
+
+### 异步加载数据
+
+```javascript
+async function loadData() {
+ const response = await fetch('/api/data');
+ const data = await response.json();
+
+ const layer = new PointLayer()
+ .source(data, {
+ parser: {
+ type: 'json',
+ x: 'longitude',
+ y: 'latitude',
+ },
+ })
+ .shape('circle')
+ .size(8)
+ .color('#5B8FF9');
+
+ scene.addLayer(layer);
+}
+
+scene.on('loaded', () => {
+ loadData();
+});
+```
+
+### 嵌套对象数据
+
+```javascript
+const data = [
+ {
+ location: {
+ lng: 120.19,
+ lat: 30.26,
+ },
+ info: {
+ name: '杭州',
+ population: 1200,
+ },
+ metrics: {
+ gdp: 18000,
+ growth: 0.08,
+ },
+ },
+];
+
+const layer = new PointLayer()
+ .source(data, {
+ parser: {
+ type: 'json',
+ x: 'location.lng', // 支持嵌套字段
+ y: 'location.lat',
+ },
+ })
+ .shape('circle')
+ .size('info.population', [5, 20])
+ .color('metrics.growth', ['#FFF5B8', '#FFAB5C', '#FF6B3B']);
+
+scene.addLayer(layer);
+```
+
+### 路径数据(LineString)
+
+```javascript
+const pathData = [
+ {
+ name: '路线1',
+ type: 'route',
+ path: [
+ [120.19, 30.26],
+ [120.2, 30.27],
+ [120.21, 30.28],
+ ],
+ },
+ {
+ name: '路线2',
+ type: 'route',
+ path: [
+ [121.47, 31.23],
+ [121.48, 31.24],
+ [121.49, 31.25],
+ ],
+ },
+];
+
+const lineLayer = new LineLayer()
+ .source(pathData, {
+ parser: {
+ type: 'json',
+ coordinates: 'path', // 指定路径坐标字段
+ },
+ })
+ .shape('line')
+ .size(3)
+ .color('type', ['#5B8FF9', '#5AD8A6']);
+
+scene.addLayer(lineLayer);
+```
+
+### OD 数据(起点-终点)
+
+```javascript
+const odData = [
+ {
+ from: { lng: 120.19, lat: 30.26 },
+ to: { lng: 121.47, lat: 31.23 },
+ count: 100,
+ type: 'migration',
+ },
+ {
+ from: { lng: 120.19, lat: 30.26 },
+ to: { lng: 116.4, lat: 39.91 },
+ count: 200,
+ type: 'migration',
+ },
+];
+
+// 方式1: 使用嵌套字段
+const arcLayer = new LineLayer()
+ .source(odData, {
+ parser: {
+ type: 'json',
+ x: 'from.lng',
+ y: 'from.lat',
+ x1: 'to.lng',
+ y1: 'to.lat',
+ },
+ })
+ .shape('arc')
+ .size('count', [1, 5])
+ .color('#5B8FF9');
+
+// 方式2: 先转换数据结构
+const transformedData = odData.map((item) => ({
+ from_lng: item.from.lng,
+ from_lat: item.from.lat,
+ to_lng: item.to.lng,
+ to_lat: item.to.lat,
+ count: item.count,
+}));
+
+const arcLayer2 = new LineLayer()
+ .source(transformedData, {
+ parser: {
+ type: 'json',
+ x: 'from_lng',
+ y: 'from_lat',
+ x1: 'to_lng',
+ y1: 'to_lat',
+ },
+ })
+ .shape('arc')
+ .size('count', [1, 5])
+ .color('#5B8FF9');
+
+scene.addLayer(arcLayer);
+```
+
+### 使用坐标数组
+
+```javascript
+const data = [
+ {
+ coordinates: [120.19, 30.26],
+ name: '点位1',
+ value: 100,
+ },
+ {
+ coordinates: [121.47, 31.23],
+ name: '点位2',
+ value: 200,
+ },
+];
+
+const layer = new PointLayer()
+ .source(data, {
+ parser: {
+ type: 'json',
+ coordinates: 'coordinates', // 直接使用坐标数组
+ },
+ })
+ .shape('circle')
+ .size(10)
+ .color('#5B8FF9');
+
+scene.addLayer(layer);
+```
+
+## Parser 配置选项
+
+### 点数据配置
+
+| 参数 | 类型 | 说明 |
+| ------------- | ------ | -------------------------- |
+| `type` | string | 必须设置为 'json' |
+| `x` | string | 经度字段名(支持嵌套路径) |
+| `y` | string | 纬度字段名(支持嵌套路径) |
+| `coordinates` | string | 坐标数组字段(替代 x, y) |
+
+### OD 数据配置
+
+| 参数 | 类型 | 说明 |
+| ---- | ------ | -------- |
+| `x` | string | 起点经度 |
+| `y` | string | 起点纬度 |
+| `x1` | string | 终点经度 |
+| `y1` | string | 终点纬度 |
+
+### 路径数据配置
+
+| 参数 | 类型 | 说明 |
+| ------------- | ------ | ---------------- |
+| `coordinates` | string | 路径坐标数组字段 |
+
+## 数据转换
+
+### 自定义数据转换
+
+```javascript
+const rawData = [{ lon: 120.19, lat: 30.26, name: '杭州', val: '100' }];
+
+const layer = new PointLayer()
+ .source(rawData, {
+ parser: {
+ type: 'json',
+ x: 'lon',
+ y: 'lat',
+ },
+ transforms: [
+ {
+ type: 'map',
+ callback: (item) => {
+ // 字段名映射
+ item.value = Number(item.val);
+ delete item.val;
+
+ // 数据清洗
+ if (item.value < 0) {
+ item.value = 0;
+ }
+
+ return item;
+ },
+ },
+ ],
+ })
+ .shape('circle')
+ .size('value', [5, 20])
+ .color('#5B8FF9');
+
+scene.addLayer(layer);
+```
+
+### 过滤数据
+
+```javascript
+const layer = new PointLayer()
+ .source(data, {
+ parser: {
+ type: 'json',
+ x: 'lng',
+ y: 'lat',
+ },
+ transforms: [
+ {
+ type: 'filter',
+ callback: (item) => {
+ // 只显示值大于 100 的数据
+ return item.value > 100;
+ },
+ },
+ ],
+ })
+ .shape('circle')
+ .size(10)
+ .color('#5B8FF9');
+
+scene.addLayer(layer);
+```
+
+### JSON 转 GeoJSON
+
+```javascript
+function jsonToGeoJSON(data, xField, yField) {
+ return {
+ type: 'FeatureCollection',
+ features: data.map((item) => ({
+ type: 'Feature',
+ properties: { ...item },
+ geometry: {
+ type: 'Point',
+ coordinates: [item[xField], item[yField]],
+ },
+ })),
+ };
+}
+
+// 使用
+const jsonData = [{ lng: 120.19, lat: 30.26, name: '杭州', value: 100 }];
+
+const geojson = jsonToGeoJSON(jsonData, 'lng', 'lat');
+
+layer.source(geojson, {
+ parser: { type: 'geojson' },
+});
+```
+
+## 常见问题
+
+### 1. 字段名不匹配
+
+**问题**: 数据字段名与 parser 配置不一致
+
+**解决方案**:
+
+```javascript
+// 检查字段名
+console.log('数据结构:', data[0]);
+
+// 确保字段名正确
+layer.source(data, {
+ parser: {
+ type: 'json',
+ x: 'longitude', // 注意大小写
+ y: 'latitude',
+ },
+});
+```
+
+### 2. 嵌套字段访问
+
+**问题**: 无法访问嵌套对象的字段
+
+**解决方案**:
+
+```javascript
+// 使用点号访问嵌套字段
+layer.source(data, {
+ parser: {
+ type: 'json',
+ x: 'location.coordinates.lng',
+ y: 'location.coordinates.lat',
+ },
+});
+
+// 或者先展平数据
+const flatData = data.map((item) => ({
+ lng: item.location.coordinates.lng,
+ lat: item.location.coordinates.lat,
+ ...item.properties,
+}));
+```
+
+### 3. 数据类型错误
+
+**问题**: 坐标值是字符串而不是数字
+
+**解决方案**:
+
+```javascript
+layer.source(data, {
+ parser: {
+ type: 'json',
+ x: 'lng',
+ y: 'lat',
+ },
+ transforms: [
+ {
+ type: 'map',
+ callback: (item) => {
+ // 转换为数字
+ item.lng = Number(item.lng);
+ item.lat = Number(item.lat);
+ item.value = parseFloat(item.value);
+ return item;
+ },
+ },
+ ],
+});
+```
+
+### 4. 异步数据加载失败
+
+**解决方案**:
+
+```javascript
+async function loadDataSafely() {
+ try {
+ const response = await fetch('/api/data');
+
+ if (!response.ok) {
+ throw new Error(`HTTP error! status: ${response.status}`);
+ }
+
+ const data = await response.json();
+
+ if (!Array.isArray(data) || data.length === 0) {
+ console.warn('No data returned');
+ return;
+ }
+
+ const layer = new PointLayer()
+ .source(data, {
+ parser: {
+ type: 'json',
+ x: 'lng',
+ y: 'lat',
+ },
+ })
+ .shape('circle')
+ .size(10)
+ .color('#5B8FF9');
+
+ scene.addLayer(layer);
+ } catch (error) {
+ console.error('Failed to load data:', error);
+ }
+}
+```
+
+## 性能优化
+
+### 1. 数据分页加载
+
+```javascript
+let currentPage = 0;
+const pageSize = 1000;
+
+async function loadPage(page) {
+ const response = await fetch(`/api/data?page=${page}&size=${pageSize}`);
+ const data = await response.json();
+
+ if (data.length > 0) {
+ const layer = new PointLayer()
+ .source(data, {
+ parser: {
+ type: 'json',
+ x: 'lng',
+ y: 'lat',
+ },
+ })
+ .shape('circle')
+ .size(5)
+ .color('#5B8FF9');
+
+ scene.addLayer(layer);
+ }
+}
+
+// 按需加载
+loadPage(currentPage);
+```
+
+### 2. 数据缓存
+
+```javascript
+const dataCache = new Map();
+
+async function loadDataWithCache(url) {
+ if (dataCache.has(url)) {
+ return dataCache.get(url);
+ }
+
+ const response = await fetch(url);
+ const data = await response.json();
+
+ dataCache.set(url, data);
+ return data;
+}
+```
+
+### 3. 数据压缩
+
+```javascript
+// 服务端返回压缩数据
+async function loadCompressedData() {
+ const response = await fetch('/api/data.gz', {
+ headers: {
+ 'Accept-Encoding': 'gzip',
+ },
+ });
+
+ const data = await response.json();
+ // 使用数据
+}
+```
+
+## 数据验证
+
+```javascript
+function validateData(data) {
+ if (!Array.isArray(data)) {
+ throw new Error('Data must be an array');
+ }
+
+ const errors = [];
+
+ data.forEach((item, index) => {
+ // 检查必需字段
+ if (!item.lng || !item.lat) {
+ errors.push(`Item ${index}: missing lng or lat`);
+ }
+
+ // 检查坐标范围
+ if (item.lng < -180 || item.lng > 180) {
+ errors.push(`Item ${index}: invalid lng ${item.lng}`);
+ }
+
+ if (item.lat < -90 || item.lat > 90) {
+ errors.push(`Item ${index}: invalid lat ${item.lat}`);
+ }
+ });
+
+ if (errors.length > 0) {
+ console.error('Data validation errors:', errors);
+ return false;
+ }
+
+ return true;
+}
+
+// 使用
+if (validateData(data)) {
+ layer.source(data, {
+ parser: {
+ type: 'json',
+ x: 'lng',
+ y: 'lat',
+ },
+ });
+}
+```
+
+## 最佳实践
+
+### 1. 统一数据结构
+
+```javascript
+// 推荐:统一的字段命名
+const data = [
+ { lng: 120.19, lat: 30.26, name: '点1', value: 100 },
+ { lng: 121.47, lat: 31.23, name: '点2', value: 200 },
+];
+
+// 避免:不一致的结构
+const badData = [
+ { longitude: 120.19, latitude: 30.26, title: '点1' },
+ { x: 121.47, y: 31.23, name: '点2' },
+];
+```
+
+### 2. 错误处理
+
+```javascript
+fetch('/api/data')
+ .then((res) => res.json())
+ .then((data) => {
+ if (validateData(data)) {
+ layer.source(data, {
+ parser: {
+ type: 'json',
+ x: 'lng',
+ y: 'lat',
+ },
+ });
+ }
+ })
+ .catch((error) => {
+ console.error('Error:', error);
+ // 显示错误提示
+ });
+```
+
+### 3. 数据更新
+
+```javascript
+// 动态更新数据
+function updateLayerData(newData) {
+ layer.setData(newData);
+}
+
+// 定时更新
+setInterval(async () => {
+ const newData = await fetch('/api/data/latest').then((r) => r.json());
+ updateLayerData(newData);
+}, 5000);
+```
+
+## 相关技能
+
+- [GeoJSON 数据源](./source-geojson.md)
+- [CSV 数据源](./source-csv.md)
+- [数据解析配置](./source-parser.md)
+- [点图层](../layers/point.md)
+- [线图层](../layers/line.md)
+
+## 参考资源
+
+- [JSON 规范](https://www.json.org/)
+- [MDN - JSON](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON)
+- [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API)
diff --git a/skills/l7/references/data/source-mvt.md b/skills/l7/references/data/source-mvt.md
new file mode 100644
index 0000000..27dccdb
--- /dev/null
+++ b/skills/l7/references/data/source-mvt.md
@@ -0,0 +1,574 @@
+---
+skill_id: source-mvt
+skill_name: MVT 数据源
+category: data
+difficulty: intermediate
+tags: [mvt, vector-tile, pbf, tile-source]
+dependencies: []
+version: 2.x
+---
+
+# MVT 数据源
+
+## 技能描述
+
+掌握 L7 中 MVT (Mapbox Vector Tile) 数据源的配置和使用。MVT 是一种高效的矢量瓦片格式,使用 Protocol Buffer 编码,广泛应用于大规模地理数据的可视化。
+
+## 何时使用
+
+- ✅ 需要加载矢量瓦片地图服务
+- ✅ 渲染海量矢量数据(百万级以上)
+- ✅ 需要按需加载地理数据
+- ✅ 使用第三方瓦片服务(Mapbox、阿里云等)
+- ✅ 自建 MVT 瓦片服务
+
+## 前置条件
+
+- 已完成[场景初始化](../core/scene.md)
+- 准备好 MVT 瓦片服务 URL
+
+## 核心概念
+
+### MVT 格式
+
+MVT (Mapbox Vector Tile) 是一种矢量瓦片规范:
+
+- **编码格式**:使用 Protocol Buffer(.pbf 文件)
+- **瓦片结构**:遵循 TMS 或 XYZ 瓦片命名规则
+- **图层组织**:一个瓦片可包含多个图层(layers)
+- **坐标系统**:使用 Web Mercator 投影(EPSG:3857)
+
+### 瓦片 URL 模板
+
+MVT 数据源使用 URL 模板,包含以下占位符:
+
+- `{z}` - 缩放级别(zoom level)
+- `{x}` - 列号(column)
+- `{y}` - 行号(row)
+
+```
+https://example.com/tiles/{z}/{x}/{y}.pbf
+```
+
+## 基础用法
+
+### 单服务器配置
+
+```javascript
+import { Source } from '@antv/l7';
+
+const source = new Source('https://tiles.example.com/{z}/{x}/{y}.pbf', {
+ parser: {
+ type: 'mvt', // 固定为 'mvt'
+ tileSize: 256, // 瓦片大小(像素)
+ maxZoom: 14, // 最大缩放级别
+ minZoom: 0, // 最小缩放级别
+ extent: [
+ // 数据范围 [west, south, east, north]
+ -180, -85.051129, 179, 85.051129,
+ ],
+ },
+});
+
+// 使用数据源
+layer.source(source);
+```
+
+### 多服务器配置
+
+使用大括号语法实现负载均衡:
+
+```javascript
+// 数字范围 {1-4}
+const source1 = new Source('https://tile{1-4}.example.com/tiles/{z}/{x}/{y}.pbf', {
+ parser: { type: 'mvt' },
+});
+
+// 字母范围 {a-d}
+const source2 = new Source('https://tile{a-d}.example.com/tiles/{z}/{x}/{y}.pbf', {
+ parser: { type: 'mvt' },
+});
+
+// 会自动分发到多个域名请求:
+// tile1.example.com, tile2.example.com, tile3.example.com, tile4.example.com
+```
+
+## 配置参数
+
+### parser 参数
+
+| 参数 | 类型 | 默认值 | 说明 |
+| ------------- | ---------------------------------- | -------------------------------------------- | ---------------------- |
+| type | `string` | - | 固定为 `'mvt'` |
+| tileSize | `number` | `256` | 瓦片大小(像素) |
+| minZoom | `number` | `0` | 最小缩放级别 |
+| maxZoom | `number` | `Infinity` | 最大缩放级别 |
+| zoomOffset | `number` | `0` | 缩放级别偏移 |
+| extent | `[number, number, number, number]` | `[-Infinity, -Infinity, Infinity, Infinity]` | 数据边界范围 |
+| getCustomData | `function` | - | 自定义瓦片数据获取方法 |
+
+### tileSize
+
+瓦片的像素大小,常见值:
+
+- `256` - 标准瓦片大小(默认)
+- `512` - 高清瓦片
+- `128` - 低精度瓦片
+
+```javascript
+const source = new Source(url, {
+ parser: {
+ type: 'mvt',
+ tileSize: 512, // 使用 512 像素瓦片
+ },
+});
+```
+
+### minZoom / maxZoom
+
+控制瓦片请求的缩放级别范围:
+
+```javascript
+const source = new Source(url, {
+ parser: {
+ type: 'mvt',
+ minZoom: 3, // 地图缩放级别 < 3 时不请求
+ maxZoom: 14, // 地图缩放级别 > 14 时使用 14 级数据
+ },
+});
+```
+
+**使用场景**:
+
+- `minZoom`:避免在全球视图下请求过多瓦片
+- `maxZoom`:复用较低层级数据,减少请求量
+
+### zoomOffset
+
+瓦片层级偏移量:
+
+```javascript
+const source = new Source(url, {
+ parser: {
+ type: 'mvt',
+ zoomOffset: 1, // 请求比当前 zoom 高一级的瓦片
+ },
+});
+
+// 地图 zoom=10 时,请求 z=11 的瓦片
+```
+
+**使用场景**:
+
+- 提高数据精度(正偏移)
+- 减少瓦片数量(负偏移)
+
+### extent
+
+限制瓦片请求的地理范围:
+
+```javascript
+// 只请求中国范围的瓦片
+const source = new Source(url, {
+ parser: {
+ type: 'mvt',
+ extent: [73.66, 3.86, 135.05, 53.55], // [west, south, east, north]
+ },
+});
+```
+
+**使用场景**:
+
+- 区域性应用(只显示特定国家/地区)
+- 减少不必要的瓦片请求
+- 提升性能
+
+## 高级用法
+
+### 1. 自定义瓦片请求
+
+使用 `getCustomData` 实现自定义请求逻辑:
+
+```javascript
+const source = new Source('https://api.example.com/tiles', {
+ parser: {
+ type: 'mvt',
+ getCustomData: (tile, callback) => {
+ const { x, y, z } = tile;
+
+ // 自定义 URL 和参数
+ const url = `https://api.example.com/tiles?z=${z}&x=${x}&y=${y}&token=YOUR_TOKEN`;
+
+ fetch(url, {
+ headers: {
+ Authorization: 'Bearer YOUR_TOKEN',
+ },
+ })
+ .then((res) => res.arrayBuffer())
+ .then((data) => {
+ callback(null, data); // 成功回调
+ })
+ .catch((err) => {
+ callback(err, null); // 失败回调
+ });
+ },
+ },
+});
+```
+
+**使用场景**:
+
+- 需要鉴权的瓦片服务
+- 动态参数(时间、过滤条件等)
+- 数据加密/解密
+- 自定义缓存策略
+
+### 2. 数据源复用
+
+多个图层共享同一个 MVT 数据源:
+
+```javascript
+const vectorSource = new Source(
+ 'https://ganos.oss-cn-hangzhou.aliyuncs.com/m2/rs_l7/{z}/{x}/{y}.pbf',
+ {
+ parser: {
+ type: 'mvt',
+ maxZoom: 9,
+ },
+ },
+);
+
+// 多个图层复用数据源
+const polygonLayer = new PolygonLayer({
+ sourceLayer: 'regions',
+}).source(vectorSource);
+
+const lineLayer = new LineLayer({
+ sourceLayer: 'boundaries',
+}).source(vectorSource);
+
+// 只会请求一份瓦片数据,高效!
+scene.addLayer(polygonLayer);
+scene.addLayer(lineLayer);
+```
+
+### 3. 多数据源组合
+
+```javascript
+// 全球底图数据
+const globalSource = new Source('https://global.tiles.com/{z}/{x}/{y}.pbf', {
+ parser: {
+ type: 'mvt',
+ maxZoom: 10,
+ },
+});
+
+// 中国详细数据
+const chinaSource = new Source('https://china.tiles.com/{z}/{x}/{y}.pbf', {
+ parser: {
+ type: 'mvt',
+ extent: [73.66, 3.86, 135.05, 53.55],
+ minZoom: 10,
+ },
+});
+
+// 根据缩放级别切换数据源
+scene.on('zoomchange', () => {
+ const zoom = scene.getZoom();
+ if (zoom < 10) {
+ layer.source(globalSource);
+ } else {
+ layer.source(chinaSource);
+ }
+});
+```
+
+## 实际应用场景
+
+### 1. 使用公开瓦片服务
+
+#### 阿里云 Ganos
+
+```javascript
+const source = new Source('https://ganos.oss-cn-hangzhou.aliyuncs.com/m2/rs_l7/{z}/{x}/{y}.pbf', {
+ parser: {
+ type: 'mvt',
+ tileSize: 256,
+ maxZoom: 9,
+ extent: [-180, -85.051129, 179, 85.051129],
+ },
+});
+```
+
+#### Mapbox
+
+```javascript
+const source = new Source(
+ 'https://api.mapbox.com/v4/mapbox.mapbox-streets-v8/{z}/{x}/{y}.mvt?access_token=YOUR_TOKEN',
+ {
+ parser: {
+ type: 'mvt',
+ maxZoom: 14,
+ },
+ },
+);
+```
+
+### 2. 自建瓦片服务
+
+#### MBTiles 文件服务
+
+```javascript
+// 使用 tileserver-gl 等工具提供服务
+const source = new Source('http://localhost:8080/data/china/{z}/{x}/{y}.pbf', {
+ parser: {
+ type: 'mvt',
+ tileSize: 256,
+ maxZoom: 14,
+ },
+});
+```
+
+#### PostGIS + Tegola
+
+```javascript
+const source = new Source('http://tegola.io/maps/osm/{z}/{x}/{y}.pbf', {
+ parser: {
+ type: 'mvt',
+ tileSize: 512,
+ },
+});
+```
+
+### 3. 带鉴权的私有服务
+
+```javascript
+const source = new Source('https://api.example.com/secure-tiles', {
+ parser: {
+ type: 'mvt',
+ getCustomData: (tile, callback) => {
+ const { x, y, z } = tile;
+
+ // 获取 token
+ const token = getAuthToken();
+
+ fetch(`https://api.example.com/secure-tiles/${z}/${x}/${y}.pbf`, {
+ headers: {
+ Authorization: `Bearer ${token}`,
+ 'X-API-Key': 'YOUR_API_KEY',
+ },
+ })
+ .then((res) => {
+ if (!res.ok) {
+ throw new Error(`HTTP ${res.status}: ${res.statusText}`);
+ }
+ return res.arrayBuffer();
+ })
+ .then((data) => callback(null, data))
+ .catch((err) => {
+ console.error('瓦片加载失败:', err);
+ callback(err, null);
+ });
+ },
+ },
+});
+```
+
+### 4. 动态参数(时间序列)
+
+```javascript
+let currentTime = '2024-01-01';
+
+const source = new Source('https://api.example.com/tiles', {
+ parser: {
+ type: 'mvt',
+ getCustomData: (tile, callback) => {
+ const { x, y, z } = tile;
+ const url = `https://api.example.com/tiles/${z}/${x}/${y}.pbf?time=${currentTime}`;
+
+ fetch(url)
+ .then((res) => res.arrayBuffer())
+ .then((data) => callback(null, data))
+ .catch((err) => callback(err, null));
+ },
+ },
+});
+
+// 更新时间参数
+function updateTime(newTime) {
+ currentTime = newTime;
+ layer.source(source); // 重新加载
+ scene.render();
+}
+```
+
+## 性能优化
+
+### 1. 合理设置缩放范围
+
+```javascript
+const source = new Source(url, {
+ parser: {
+ type: 'mvt',
+ minZoom: 5, // 避免全球视图请求过多瓦片
+ maxZoom: 14, // 限制最大精度,复用低层级数据
+ },
+});
+```
+
+### 2. 限制数据范围
+
+```javascript
+// 只请求业务相关区域的瓦片
+const source = new Source(url, {
+ parser: {
+ type: 'mvt',
+ extent: [100, 20, 130, 50], // 东亚地区
+ },
+});
+```
+
+### 3. 使用多服务器
+
+```javascript
+// 利用浏览器并发请求能力
+const source = new Source('https://tile{1-4}.example.com/{z}/{x}/{y}.pbf', {
+ parser: { type: 'mvt' },
+});
+```
+
+### 4. 适当使用 zoomOffset
+
+```javascript
+// 使用低一级的瓦片,减少请求量
+const source = new Source(url, {
+ parser: {
+ type: 'mvt',
+ zoomOffset: -1, // 地图 zoom=10 时使用 z=9 的瓦片
+ },
+});
+```
+
+## 常见问题
+
+### Q: 如何查看瓦片包含的图层?
+
+A: 使用浏览器开发者工具或在线工具:
+
+```javascript
+// 1. 查看网络请求的 PBF 文件
+
+// 2. 使用 TileDebugLayer
+import { TileDebugLayer } from '@antv/l7';
+const debugLayer = new TileDebugLayer();
+scene.addLayer(debugLayer);
+
+// 3. 使用在线工具
+// https://protomaps.github.io/PMTiles/
+// https://mapbox.github.io/vector-tile-spec/
+```
+
+### Q: MVT 和 GeoJSON 有什么区别?
+
+A:
+
+| 对比项 | MVT | GeoJSON |
+| ---------- | ------------------------ | --------------- |
+| 编码格式 | Protocol Buffer (二进制) | JSON (文本) |
+| 文件大小 | 小(通常 < 50KB) | 大(可能数 MB) |
+| 加载方式 | 按需加载(瓦片) | 一次性加载全部 |
+| 适用数据量 | 百万级以上 | 万级以下 |
+| 数据修改 | 需重新生成瓦片 | 可直接修改 |
+
+### Q: 如何调试瓦片加载问题?
+
+A:
+
+1. **检查网络请求**:
+ - 打开浏览器开发者工具 Network 面板
+ - 查看瓦片请求的 URL 是否正确
+ - 检查 HTTP 状态码
+
+2. **使用调试图层**:
+
+```javascript
+import { TileDebugLayer } from '@antv/l7';
+const debugLayer = new TileDebugLayer();
+scene.addLayer(debugLayer);
+```
+
+3. **查看控制台错误**:
+
+```javascript
+scene.on('error', (e) => {
+ console.error('场景错误:', e);
+});
+```
+
+### Q: 瓦片 404 怎么办?
+
+A: 检查以下几点:
+
+1. URL 模板格式是否正确(`{z}/{x}/{y}`)
+2. 是否超出 maxZoom 范围
+3. extent 是否包含当前视野
+4. 服务器是否支持 CORS
+5. 是否需要鉴权
+
+### Q: 如何实现瓦片缓存?
+
+A: 浏览器会自动缓存瓦片(HTTP 缓存),也可以使用 Service Worker:
+
+```javascript
+// 使用 getCustomData 实现自定义缓存
+const cache = new Map();
+
+const source = new Source(url, {
+ parser: {
+ type: 'mvt',
+ getCustomData: (tile, callback) => {
+ const key = `${tile.z}/${tile.x}/${tile.y}`;
+
+ // 从缓存读取
+ if (cache.has(key)) {
+ callback(null, cache.get(key));
+ return;
+ }
+
+ // 请求数据
+ fetch(url)
+ .then((res) => res.arrayBuffer())
+ .then((data) => {
+ cache.set(key, data); // 存入缓存
+ callback(null, data);
+ })
+ .catch((err) => callback(err, null));
+ },
+ },
+});
+```
+
+## 注意事项
+
+⚠️ **坐标系统**:MVT 使用 Web Mercator 投影(EPSG:3857),确保数据匹配
+
+⚠️ **文件格式**:瓦片必须是 Protocol Buffer 格式(.pbf 或 .mvt)
+
+⚠️ **CORS 配置**:跨域请求需要服务器配置 CORS 头
+
+⚠️ **缩放范围**:合理设置 minZoom 和 maxZoom 避免性能问题
+
+⚠️ **数据源复用**:多图层使用同一数据时,务必复用 Source 对象
+
+⚠️ **URL 占位符**:确保使用正确的占位符格式(`{z}/{x}/{y}`,不是 `$z/$x/$y`)
+
+## 相关技能
+
+- [矢量瓦片图层](../layers/tile-vector.md)
+- [GeoJSON 数据源](./source-geojson.md)
+- [场景初始化](../core/scene.md)
+- [性能优化](../performance/optimization.md)
+
+## 在线示例
+
+查看更多示例:[L7 矢量瓦片示例](https://l7.antv.antgroup.com/examples/tile/vector)
diff --git a/skills/l7/references/data/source-parser.md b/skills/l7/references/data/source-parser.md
new file mode 100644
index 0000000..104e04f
--- /dev/null
+++ b/skills/l7/references/data/source-parser.md
@@ -0,0 +1,671 @@
+---
+skill_id: source-parser
+skill_name: 数据解析配置
+category: data
+difficulty: intermediate
+tags: [parser, data, transform, config, 解析器, 数据转换]
+dependencies: [source-geojson, source-csv, source-json]
+version: 2.x
+---
+
+# 数据解析配置
+
+## 技能描述
+
+配置数据解析器(Parser)和数据转换器(Transform),将原始数据转换为 L7 可以使用的格式。
+
+## 何时使用
+
+- ✅ 数据格式需要特殊处理
+- ✅ 需要数据转换和清洗
+- ✅ 字段映射和重命名
+- ✅ 数据过滤和聚合
+- ✅ 自定义数据处理逻辑
+
+## Parser 类型
+
+L7 支持多种数据解析器:
+
+| Parser 类型 | 说明 | 适用场景 |
+| ----------- | ------------- | -------------------- |
+| `geojson` | GeoJSON 格式 | 标准地理数据 |
+| `json` | JSON 对象数组 | 业务数据、API 数据 |
+| `csv` | CSV 文本 | 表格数据、Excel 导出 |
+| `image` | 图片 | 图片图层 |
+| `raster` | 栅格数据 | 遥感影像 |
+| `mvt` | 矢量瓦片 | 大规模矢量数据 |
+
+## 代码示例
+
+### GeoJSON Parser
+
+```javascript
+layer.source(geojsonData, {
+ parser: {
+ type: 'geojson',
+ },
+});
+```
+
+### JSON Parser - 基础配置
+
+```javascript
+const data = [{ lng: 120.19, lat: 30.26, name: '杭州', value: 100 }];
+
+layer.source(data, {
+ parser: {
+ type: 'json',
+ x: 'lng', // 经度字段
+ y: 'lat', // 纬度字段
+ },
+});
+```
+
+### JSON Parser - 嵌套字段
+
+```javascript
+const data = [
+ {
+ location: {
+ coordinates: {
+ lng: 120.19,
+ lat: 30.26,
+ },
+ },
+ info: { name: '杭州' },
+ },
+];
+
+layer.source(data, {
+ parser: {
+ type: 'json',
+ x: 'location.coordinates.lng', // 支持点号访问嵌套字段
+ y: 'location.coordinates.lat',
+ },
+});
+```
+
+### JSON Parser - 坐标数组
+
+```javascript
+const data = [
+ {
+ coordinates: [120.19, 30.26],
+ name: '杭州',
+ },
+];
+
+layer.source(data, {
+ parser: {
+ type: 'json',
+ coordinates: 'coordinates', // 直接使用坐标数组
+ },
+});
+```
+
+### JSON Parser - 路径数据
+
+```javascript
+const pathData = [
+ {
+ name: '路线1',
+ path: [
+ [120.19, 30.26],
+ [120.2, 30.27],
+ [120.21, 30.28],
+ ],
+ },
+];
+
+layer.source(pathData, {
+ parser: {
+ type: 'json',
+ coordinates: 'path', // 路径坐标数组
+ },
+});
+```
+
+### JSON Parser - OD 数据
+
+```javascript
+const odData = [
+ {
+ from_lng: 120.19,
+ from_lat: 30.26,
+ to_lng: 121.47,
+ to_lat: 31.23,
+ count: 100,
+ },
+];
+
+layer.source(odData, {
+ parser: {
+ type: 'json',
+ x: 'from_lng',
+ y: 'from_lat',
+ x1: 'to_lng', // 终点经度
+ y1: 'to_lat', // 终点纬度
+ },
+});
+```
+
+### CSV Parser
+
+```javascript
+const csvData = `lng,lat,name,value
+120.19,30.26,杭州,100
+121.47,31.23,上海,200`;
+
+layer.source(csvData, {
+ parser: {
+ type: 'csv',
+ x: 'lng',
+ y: 'lat',
+ delimiter: ',', // 分隔符,默认为逗号
+ },
+});
+```
+
+### Image Parser
+
+```javascript
+layer.source('https://example.com/image.png', {
+ parser: {
+ type: 'image',
+ extent: [minLng, minLat, maxLng, maxLat], // 图片地理范围
+ },
+});
+```
+
+### Raster Parser
+
+```javascript
+layer.source(rasterImageUrl, {
+ parser: {
+ type: 'rasterImage',
+ extent: [minLng, minLat, maxLng, maxLat],
+ },
+});
+```
+
+### MVT Parser(矢量瓦片)
+
+```javascript
+layer.source('https://tiles.example.com/{z}/{x}/{y}.mvt', {
+ parser: {
+ type: 'mvt',
+ tileSize: 256,
+ maxZoom: 14,
+ extent: [-180, -85.051129, 179, 85.051129],
+ },
+});
+```
+
+## Transforms 数据转换
+
+### Map 转换 - 字段映射
+
+```javascript
+layer.source(data, {
+ parser: {
+ type: 'json',
+ x: 'lng',
+ y: 'lat',
+ },
+ transforms: [
+ {
+ type: 'map',
+ callback: (item) => {
+ // 字段重命名
+ item.value = item.val;
+ delete item.val;
+
+ // 数据类型转换
+ item.value = Number(item.value);
+
+ // 计算新字段
+ item.category = item.value > 100 ? 'high' : 'low';
+
+ return item;
+ },
+ },
+ ],
+});
+```
+
+### Filter 转换 - 数据过滤
+
+```javascript
+layer.source(data, {
+ parser: {
+ type: 'json',
+ x: 'lng',
+ y: 'lat',
+ },
+ transforms: [
+ {
+ type: 'filter',
+ callback: (item) => {
+ // 过滤条件
+ return item.value > 100 && item.type === 'city';
+ },
+ },
+ ],
+});
+```
+
+### 多个转换组合
+
+```javascript
+layer.source(data, {
+ parser: {
+ type: 'json',
+ x: 'lng',
+ y: 'lat',
+ },
+ transforms: [
+ // 1. 先过滤
+ {
+ type: 'filter',
+ callback: (item) => item.value > 0,
+ },
+ // 2. 再转换
+ {
+ type: 'map',
+ callback: (item) => {
+ item.value = Math.sqrt(item.value);
+ item.normalized = item.value / 100;
+ return item;
+ },
+ },
+ ],
+});
+```
+
+## 常见场景
+
+### 场景 1: API 数据适配
+
+```javascript
+// API 返回的数据结构
+const apiData = {
+ code: 200,
+ data: {
+ list: [
+ {
+ id: 1,
+ location: { longitude: 120.19, latitude: 30.26 },
+ metrics: { score: 85, rating: 4.5 },
+ },
+ ],
+ },
+};
+
+// 提取并转换数据
+const data = apiData.data.list;
+
+layer.source(data, {
+ parser: {
+ type: 'json',
+ x: 'location.longitude',
+ y: 'location.latitude',
+ },
+ transforms: [
+ {
+ type: 'map',
+ callback: (item) => {
+ return {
+ id: item.id,
+ lng: item.location.longitude,
+ lat: item.location.latitude,
+ score: item.metrics.score,
+ rating: item.metrics.rating,
+ };
+ },
+ },
+ ],
+});
+```
+
+### 场景 2: 数据聚合
+
+```javascript
+// 对数据进行聚合
+layer.source(data, {
+ parser: {
+ type: 'json',
+ x: 'lng',
+ y: 'lat',
+ },
+ transforms: [
+ {
+ type: 'map',
+ callback: (item, index, arr) => {
+ // 计算该点周围的平均值
+ const nearby = arr.filter(
+ (d) => Math.abs(d.lng - item.lng) < 0.1 && Math.abs(d.lat - item.lat) < 0.1,
+ );
+
+ item.nearbyCount = nearby.length;
+ item.averageValue = nearby.reduce((sum, d) => sum + d.value, 0) / nearby.length;
+
+ return item;
+ },
+ },
+ ],
+});
+```
+
+### 场景 3: 数据标准化
+
+```javascript
+layer.source(data, {
+ parser: {
+ type: 'json',
+ x: 'lng',
+ y: 'lat',
+ },
+ transforms: [
+ {
+ type: 'map',
+ callback: (item, index, arr) => {
+ // 计算最大最小值
+ const values = arr.map((d) => d.value);
+ const min = Math.min(...values);
+ const max = Math.max(...values);
+
+ // 归一化
+ item.normalized = (item.value - min) / (max - min);
+
+ return item;
+ },
+ },
+ ],
+});
+```
+
+### 场景 4: 时间数据处理
+
+```javascript
+layer.source(data, {
+ parser: {
+ type: 'json',
+ x: 'lng',
+ y: 'lat',
+ },
+ transforms: [
+ {
+ type: 'map',
+ callback: (item) => {
+ // 解析时间字符串
+ item.timestamp = new Date(item.dateStr).getTime();
+
+ // 提取时间组件
+ const date = new Date(item.dateStr);
+ item.year = date.getFullYear();
+ item.month = date.getMonth() + 1;
+ item.day = date.getDate();
+ item.hour = date.getHours();
+
+ return item;
+ },
+ },
+ {
+ type: 'filter',
+ callback: (item) => {
+ // 只显示最近7天的数据
+ const now = Date.now();
+ const sevenDaysAgo = now - 7 * 24 * 60 * 60 * 1000;
+ return item.timestamp >= sevenDaysAgo;
+ },
+ },
+ ],
+});
+```
+
+### 场景 5: 坐标系转换
+
+```javascript
+// 假设需要从 GCJ-02 转 WGS84
+function gcj02ToWgs84(lng, lat) {
+ // 坐标转换算法
+ // ...
+ return { lng: newLng, lat: newLat };
+}
+
+layer.source(data, {
+ parser: {
+ type: 'json',
+ x: 'lng',
+ y: 'lat',
+ },
+ transforms: [
+ {
+ type: 'map',
+ callback: (item) => {
+ const { lng, lat } = gcj02ToWgs84(item.lng, item.lat);
+ item.lng = lng;
+ item.lat = lat;
+ return item;
+ },
+ },
+ ],
+});
+```
+
+## 性能优化
+
+### 1. 避免在 transform 中进行重复计算
+
+```javascript
+// ❌ 不推荐:每条数据都计算全局统计
+transforms: [
+ {
+ type: 'map',
+ callback: (item, index, arr) => {
+ const max = Math.max(...arr.map((d) => d.value)); // 重复计算
+ item.normalized = item.value / max;
+ return item;
+ },
+ },
+];
+
+// ✅ 推荐:预先计算
+const max = Math.max(...data.map((d) => d.value));
+
+transforms: [
+ {
+ type: 'map',
+ callback: (item) => {
+ item.normalized = item.value / max;
+ return item;
+ },
+ },
+];
+```
+
+### 2. 合并转换逻辑
+
+```javascript
+// ❌ 不推荐:多次遍历
+transforms: [
+ { type: 'filter', callback: (item) => item.value > 0 },
+ {
+ type: 'map',
+ callback: (item) => {
+ item.doubled = item.value * 2;
+ return item;
+ },
+ },
+ {
+ type: 'map',
+ callback: (item) => {
+ item.category = item.doubled > 100 ? 'high' : 'low';
+ return item;
+ },
+ },
+];
+
+// ✅ 推荐:一次遍历
+transforms: [
+ {
+ type: 'map',
+ callback: (item) => {
+ if (item.value <= 0) return null; // 过滤
+
+ item.doubled = item.value * 2;
+ item.category = item.doubled > 100 ? 'high' : 'low';
+ return item;
+ },
+ },
+ {
+ type: 'filter',
+ callback: (item) => item !== null,
+ },
+];
+```
+
+### 3. 数据量大时使用 Web Worker
+
+```javascript
+// worker.js
+self.onmessage = function (e) {
+ const data = e.data;
+
+ const processed = data.map((item) => {
+ // 复杂的数据处理
+ return processItem(item);
+ });
+
+ self.postMessage(processed);
+};
+
+// 主线程
+const worker = new Worker('worker.js');
+
+worker.postMessage(rawData);
+
+worker.onmessage = function (e) {
+ const processedData = e.data;
+
+ layer.source(processedData, {
+ parser: {
+ type: 'json',
+ x: 'lng',
+ y: 'lat',
+ },
+ });
+};
+```
+
+## 调试技巧
+
+### 1. 打印解析后的数据
+
+```javascript
+layer.source(data, {
+ parser: {
+ type: 'json',
+ x: 'lng',
+ y: 'lat',
+ },
+ transforms: [
+ {
+ type: 'map',
+ callback: (item, index) => {
+ // 打印前几条数据
+ if (index < 3) {
+ console.log('Item', index, item);
+ }
+ return item;
+ },
+ },
+ ],
+});
+
+// 或者在图层添加后查看
+layer.on('add', () => {
+ const source = layer.getSource();
+ console.log('Source data:', source.data.dataArray.slice(0, 5));
+});
+```
+
+### 2. 验证数据格式
+
+```javascript
+transforms: [
+ {
+ type: 'map',
+ callback: (item, index) => {
+ // 数据验证
+ if (isNaN(item.lng) || isNaN(item.lat)) {
+ console.error(`Invalid coordinates at index ${index}:`, item);
+ }
+
+ if (item.lng < -180 || item.lng > 180) {
+ console.warn(`Invalid longitude at index ${index}: ${item.lng}`);
+ }
+
+ return item;
+ },
+ },
+];
+```
+
+## 常见问题
+
+### 1. 数据解析失败
+
+**检查清单**:
+
+- ✅ parser.type 是否正确
+- ✅ 字段名是否正确(区分大小写)
+- ✅ 坐标格式是否正确(经度在前)
+- ✅ 数据是否为空
+
+### 2. Transform 不生效
+
+**原因**: Transform callback 必须返回数据
+
+```javascript
+// ❌ 错误:没有返回值
+transforms: [
+ {
+ type: 'map',
+ callback: (item) => {
+ item.value = item.value * 2;
+ // 忘记 return
+ },
+ },
+];
+
+// ✅ 正确
+transforms: [
+ {
+ type: 'map',
+ callback: (item) => {
+ item.value = item.value * 2;
+ return item; // 必须返回
+ },
+ },
+];
+```
+
+### 3. 性能问题
+
+对于大数据量,避免复杂的 transform 操作,尽量在服务端处理。
+
+## 相关技能
+
+- [GeoJSON 数据源](./source-geojson.md)
+- [CSV 数据源](./source-csv.md)
+- [JSON 数据源](./source-json.md)
+- [性能优化](../performance/optimization.md)
+
+## 最佳实践
+
+1. **预处理优先**: 尽量在服务端或数据加载前完成数据处理
+2. **简化 transform**: Transform 在渲染时执行,保持逻辑简单
+3. **类型转换**: 确保数字字段是 number 类型而非 string
+4. **错误处理**: 添加数据验证,避免坏数据导致渲染失败
+5. **性能监控**: 对大数据量使用 console.time 监控处理时间
diff --git a/skills/l7/references/interaction/components.md b/skills/l7/references/interaction/components.md
new file mode 100644
index 0000000..6a39c56
--- /dev/null
+++ b/skills/l7/references/interaction/components.md
@@ -0,0 +1,212 @@
+# L7 Components Reference
+
+本文档整合了 L7 的所有交互组件使用指南。
+
+## Popup - 信息弹窗
+
+详见 [popup.md](popup.md) - 完整的 Popup 组件使用指南,包括基础用法、高级配置、样式定制等。
+
+### 快速示例
+
+```javascript
+import { Popup } from '@antv/l7';
+
+const popup = new Popup({
+ closeButton: true,
+})
+ .setLnglat([120.19, 30.26])
+ .setHTML('标题
内容
');
+
+scene.addPopup(popup);
+```
+
+### 事件绑定
+
+```javascript
+layer.on('click', (e) => {
+ const popup = new Popup().setLnglat(e.lnglat).setHTML(`${e.feature.properties.name}
`);
+ scene.addPopup(popup);
+});
+```
+
+## Marker - 标注点
+
+自定义 HTML 标注,支持拖拽。
+
+### 基础用法
+
+```javascript
+import { Marker } from '@antv/l7';
+
+const marker = new Marker().setLnglat([120.19, 30.26]);
+
+scene.addMarker(marker);
+```
+
+### 自定义样式
+
+```javascript
+const el = document.createElement('div');
+el.className = 'custom-marker';
+el.innerHTML = '';
+
+const marker = new Marker({
+ element: el,
+}).setLnglat([120.19, 30.26]);
+
+scene.addMarker(marker);
+```
+
+### 拖拽标注
+
+```javascript
+const marker = new Marker({
+ draggable: true,
+}).setLnglat([120.19, 30.26]);
+
+marker.on('dragend', () => {
+ const lnglat = marker.getLnglat();
+ console.log('新位置:', lnglat);
+});
+
+scene.addMarker(marker);
+```
+
+## Controls - 地图控件
+
+L7 提供多种内置控件。
+
+### Zoom Control - 缩放控件
+
+```javascript
+import { Zoom } from '@antv/l7';
+
+const zoom = new Zoom({
+ position: 'topright',
+});
+
+scene.addControl(zoom);
+```
+
+### Scale Control - 比例尺
+
+```javascript
+import { Scale } from '@antv/l7';
+
+const scale = new Scale({
+ position: 'bottomleft',
+});
+
+scene.addControl(scale);
+```
+
+### Layer Control - 图层控制
+
+```javascript
+import { LayerControl } from '@antv/l7';
+
+const layerControl = new LayerControl({
+ layers: [
+ { layer: pointLayer, name: '点图层' },
+ { layer: lineLayer, name: '线图层' },
+ ],
+});
+
+scene.addControl(layerControl);
+```
+
+## Legend - 图例
+
+自定义图例组件。
+
+### 基础图例
+
+```javascript
+import { Legend } from '@antv/l7';
+
+const legend = new Legend({
+ position: 'bottomright',
+ items: [
+ { color: '#5B8FF9', value: '类型A' },
+ { color: '#5AD8A6', value: '类型B' },
+ { color: '#FF6B3B', value: '类型C' },
+ ],
+});
+
+scene.addControl(legend);
+```
+
+### 渐变图例
+
+```javascript
+const legend = new Legend({
+ position: 'bottomright',
+ type: 'gradient',
+ items: [
+ { color: '#FFF5B8', value: '0' },
+ { color: '#FFAB5C', value: '50' },
+ { color: '#FF6B3B', value: '100' },
+ ],
+});
+
+scene.addControl(legend);
+```
+
+## 组件组合使用
+
+实际应用中通常组合使用多个组件:
+
+```javascript
+import { Scene, PointLayer, Popup, Marker, Zoom, Scale } from '@antv/l7';
+
+// 添加控件
+scene.addControl(new Zoom({ position: 'topright' }));
+scene.addControl(new Scale({ position: 'bottomleft' }));
+
+// 添加标注
+const marker = new Marker().setLnglat([120.19, 30.26]);
+scene.addMarker(marker);
+
+// 点击显示 Popup
+layer.on('click', (e) => {
+ const popup = new Popup().setLnglat(e.lnglat).setHTML(`${e.feature.properties.name}
`);
+ scene.addPopup(popup);
+});
+```
+
+## 常见问题
+
+### 1. Popup 位置偏移
+
+使用 `offset` 调整位置:
+
+```javascript
+const popup = new Popup({
+ offset: [0, -10], // x, y 偏移量
+});
+```
+
+### 2. Marker 自定义图标
+
+```javascript
+const el = document.createElement('div');
+el.style.backgroundImage = 'url(icon.png)';
+el.style.width = '32px';
+el.style.height = '32px';
+
+const marker = new Marker({ element: el });
+```
+
+### 3. 控件位置
+
+支持的位置:
+
+- `'topleft'` - 左上角
+- `'topright'` - 右上角
+- `'bottomleft'` - 左下角
+- `'bottomright'` - 右下角
+
+## 相关文档
+
+- [events.md](events.md) - 事件处理详细指南
+- [popup.md](popup.md) - Popup 完整文档
diff --git a/skills/l7/references/interaction/controls.md b/skills/l7/references/interaction/controls.md
new file mode 100644
index 0000000..3fcf6b1
--- /dev/null
+++ b/skills/l7/references/interaction/controls.md
@@ -0,0 +1,674 @@
+---
+skill_id: controls
+skill_name: 地图控件
+category: interaction
+difficulty: intermediate
+tags: [control, zoom, scale, fullscreen, layer-switch]
+dependencies: [scene-initialization]
+version: 2.x
+---
+
+# 地图控件
+
+## 技能描述
+
+掌握 L7 地图控件的使用,通过各种控件实现地图缩放、比例尺显示、全屏、图层切换等交互功能。控件是悬浮在地图四周,对地图和图层进行信息呈现或交互的组件。
+
+## 何时使用
+
+- ✅ 需要地图缩放控制按钮
+- ✅ 需要显示地图比例尺
+- ✅ 需要全屏显示地图
+- ✅ 需要切换不同图层的显示
+- ✅ 需要显示鼠标位置的经纬度
+- ✅ 需要添加自定义 Logo
+
+## 前置条件
+
+- 已完成[场景初始化](../core/scene.md)
+
+## 核心概念
+
+### 控件位置
+
+L7 支持将控件插入到地图的 **8 个位置** 或自定义 DOM:
+
+- `topleft` - 左上角
+- `topright` - 右上角
+- `bottomleft` - 左下角
+- `bottomright` - 右下角
+- `topcenter` - 顶部中心
+- `bottomcenter` - 底部中心
+- `leftcenter` - 左侧中心
+- `rightcenter` - 右侧中心
+
+
+
+### 控件排列
+
+同一位置的多个控件支持横向和纵向排列,通过 `layout` 配置:
+
+```javascript
+const control = new Zoom({
+ position: 'topleft',
+ layout: 'horizontal', // 'horizontal' | 'vertical'
+});
+```
+
+## 基础用法
+
+### 1. 添加控件
+
+```javascript
+import { Scene, Zoom } from '@antv/l7';
+import { GaodeMap } from '@antv/l7-maps';
+
+const scene = new Scene({
+ id: 'map',
+ map: new GaodeMap({
+ center: [120, 30],
+ zoom: 10,
+ }),
+});
+
+scene.on('loaded', () => {
+ // 实例化控件
+ const zoom = new Zoom({
+ position: 'topleft',
+ });
+
+ // 添加控件
+ scene.addControl(zoom);
+});
+```
+
+### 2. 移除控件
+
+```javascript
+// 移除指定控件
+scene.removeControl(zoom);
+
+// 移除所有控件
+scene.removeAllControl();
+```
+
+### 3. 更新控件配置
+
+```javascript
+const zoom = new Zoom({
+ position: 'topleft',
+});
+
+scene.addControl(zoom);
+
+// 更新配置
+zoom.setOptions({
+ position: 'topright',
+ className: 'custom-class',
+});
+```
+
+## 内置控件
+
+### Zoom - 缩放控件
+
+用于地图放大和缩小操作。
+
+
+
+```javascript
+import { Zoom } from '@antv/l7';
+
+const zoom = new Zoom({
+ position: 'topleft',
+ zoomInText: '+', // 放大按钮内容
+ zoomInTitle: '放大', // 放大按钮 title
+ zoomOutText: '-', // 缩小按钮内容
+ zoomOutTitle: '缩小', // 缩小按钮 title
+ showZoom: true, // 显示当前缩放级别数值
+});
+
+scene.addControl(zoom);
+
+// 方法
+zoom.zoomIn(); // 放大
+zoom.zoomOut(); // 缩小
+```
+
+**配置项**:
+
+| 参数 | 类型 | 说明 |
+| ------------ | ------------------- | -------------------------------- |
+| zoomInText | `Element \| string` | 放大按钮的展示内容 |
+| zoomInTitle | `string` | 放大按钮的 title 属性 |
+| zoomOutText | `Element \| string` | 缩小按钮的展示内容 |
+| zoomOutTitle | `string` | 缩小按钮的 title 属性 |
+| showZoom | `boolean` | 是否展示当前缩放级别(向下取整) |
+
+### Scale - 比例尺控件
+
+显示地图比例尺,支持公制和英制。
+
+
+
+```javascript
+import { Scale } from '@antv/l7';
+
+const scale = new Scale({
+ position: 'bottomleft',
+ lockWidth: false, // 是否固定容器宽度
+ maxWidth: 100, // 容器最大宽度(像素)
+ metric: true, // 显示公制(千米)
+ imperial: false, // 显示英制(英里)
+ updateWhenIdle: false, // 是否只在拖拽/缩放结束后更新
+});
+
+scene.addControl(scale);
+```
+
+**配置项**:
+
+| 参数 | 类型 | 默认值 | 说明 |
+| -------------- | --------- | ------- | ---------------------- |
+| lockWidth | `boolean` | `true` | 是否固定容器宽度 |
+| maxWidth | `number` | `100` | 组件容器最大宽度 |
+| metric | `boolean` | `true` | 展示千米格式的比例尺 |
+| imperial | `boolean` | `false` | 展示英里格式的比例尺 |
+| updateWhenIdle | `boolean` | `false` | 是否只在交互结束后更新 |
+
+### Fullscreen - 全屏控件
+
+控制地图区域的全屏显示。
+
+
+
+```javascript
+import { Fullscreen } from '@antv/l7';
+
+const fullscreen = new Fullscreen({
+ position: 'topright',
+ btnText: '全屏', // 按钮文本
+ btnTitle: '全屏', // 按钮 title
+ exitBtnText: '退出', // 退出按钮文本
+ exitTitle: '退出全屏', // 退出按钮 title
+});
+
+scene.addControl(fullscreen);
+
+// 方法
+fullscreen.toggleFullscreen(); // 切换全屏状态
+
+// 事件
+fullscreen.on('fullscreenChange', (isFullscreen) => {
+ console.log('全屏状态:', isFullscreen);
+});
+```
+
+**配置项**:
+
+| 参数 | 类型 | 说明 |
+| ----------- | --------------------------- | ----------------------- |
+| btnIcon | `HTMLElement \| SVGElement` | 全屏按钮图标 |
+| btnText | `string` | 全屏按钮文本 |
+| btnTitle | `string` | 全屏按钮 title 属性 |
+| exitBtnIcon | `HTMLElement \| SVGElement` | 退出全屏按钮图标 |
+| exitBtnText | `string` | 退出全屏按钮文本 |
+| exitTitle | `string` | 退出全屏按钮 title 属性 |
+
+### Logo - 标志控件
+
+在地图上展示 Logo 图片,支持超链接跳转。
+
+
+
+```javascript
+import { Logo } from '@antv/l7';
+
+// 隐藏默认 Logo
+const scene = new Scene({
+ id: 'map',
+ map: new GaodeMap({
+ /* ... */
+ }),
+ logoVisible: false, // 关闭默认 L7 Logo
+});
+
+// 添加自定义 Logo
+const logo = new Logo({
+ position: 'bottomleft',
+ img: 'https://example.com/logo.png', // Logo 图片 URL
+ href: 'https://example.com', // 点击跳转的链接
+});
+
+scene.addControl(logo);
+```
+
+**配置项**:
+
+| 参数 | 类型 | 说明 |
+| ---- | -------- | ------------------------ |
+| img | `string` | Logo 图片 URL |
+| href | `string` | 点击跳转的超链接(可选) |
+
+### LayerSwitch - 图层切换控件
+
+控制图层的显示和隐藏,支持单选和多选模式。
+
+
+
+```javascript
+import { LayerSwitch } from '@antv/l7';
+
+// 创建图层时指定名称
+const layer1 = new PointLayer({
+ name: 'POI图层',
+})
+ .source(data1)
+ .shape('circle')
+ .size(10)
+ .color('#5B8FF9');
+
+const layer2 = new LineLayer({
+ name: '道路图层',
+})
+ .source(data2)
+ .shape('line')
+ .size(2)
+ .color('#5AD8A6');
+
+scene.addLayer(layer1);
+scene.addLayer(layer2);
+
+// 添加图层切换控件
+const layerSwitch = new LayerSwitch({
+ position: 'topright',
+ layers: [layer1, layer2], // 可传入图层实例或图层 ID
+ multiple: true, // 是否多选模式(false 为单选)
+});
+
+scene.addControl(layerSwitch);
+```
+
+**高级配置**:
+
+```javascript
+const layerSwitch = new LayerSwitch({
+ position: 'topright',
+ layers: [
+ {
+ layer: layer1,
+ name: '自定义图层名称', // 覆盖图层默认名称
+ img: 'https://example.com/icon.png', // 图层缩略图
+ },
+ {
+ layer: layer2,
+ name: '道路网络',
+ },
+ ],
+ multiple: true,
+});
+
+scene.addControl(layerSwitch);
+```
+
+**配置项**:
+
+| 参数 | 类型 | 说明 |
+| -------- | -------------------------------------------- | ---------------------------------------- |
+| layers | `Array` | 需要控制的图层数组 |
+| multiple | `boolean` | 是否多选模式(单选时默认显示第一个图层) |
+
+### MouseLocation - 鼠标位置控件
+
+实时显示鼠标在地图上的经纬度坐标。
+
+
+
+```javascript
+import { MouseLocation } from '@antv/l7';
+
+const mouseLocation = new MouseLocation({
+ position: 'bottomright',
+ transform: (position) => {
+ // 可以对坐标进行转换处理
+ const [lng, lat] = position;
+ return [lng.toFixed(6), lat.toFixed(6)];
+ },
+});
+
+scene.addControl(mouseLocation);
+
+// 事件
+mouseLocation.on('locationChange', (position) => {
+ console.log('当前位置:', position);
+});
+```
+
+**配置项**:
+
+| 参数 | 类型 | 说明 |
+| --------- | -------------------------------------------------- | ------------------ |
+| transform | `(position: [number, number]) => [number, number]` | 转换坐标的回调函数 |
+
+## 通用配置
+
+所有控件都支持以下通用配置:
+
+| 参数 | 类型 | 默认值 | 说明 |
+| --------- | ---------------------------- | ------------ | --------------- |
+| position | `string` | `'topright'` | 控件位置 |
+| name | `string` | - | 控件名称 |
+| className | `string` | - | 自定义 CSS 类名 |
+| layout | `'horizontal' \| 'vertical'` | - | 排列方向 |
+
+## 通用方法
+
+所有控件都支持以下方法:
+
+```javascript
+// 显示控件
+control.show();
+
+// 隐藏控件
+control.hide();
+
+// 更新配置
+control.setOptions({ position: 'topleft' });
+
+// 移除控件
+scene.removeControl(control);
+```
+
+## 实际应用场景
+
+### 1. 完整地图控件组合
+
+```javascript
+import { Scene, Zoom, Scale, Fullscreen, Logo } from '@antv/l7';
+
+const scene = new Scene({
+ id: 'map',
+ map: new GaodeMap({
+ /* ... */
+ }),
+ logoVisible: false,
+});
+
+scene.on('loaded', () => {
+ // 左上角:缩放控件
+ const zoom = new Zoom({
+ position: 'topleft',
+ showZoom: true,
+ });
+
+ // 右上角:全屏控件
+ const fullscreen = new Fullscreen({
+ position: 'topright',
+ });
+
+ // 左下角:自定义 Logo
+ const logo = new Logo({
+ position: 'bottomleft',
+ img: 'https://example.com/logo.png',
+ href: 'https://example.com',
+ });
+
+ // 右下角:比例尺
+ const scale = new Scale({
+ position: 'bottomright',
+ metric: true,
+ });
+
+ scene.addControl(zoom);
+ scene.addControl(fullscreen);
+ scene.addControl(logo);
+ scene.addControl(scale);
+});
+```
+
+### 2. 图层组管理
+
+```javascript
+// 创建不同类型的图层
+const baseLayer = new PolygonLayer({
+ name: '行政区划',
+})
+ .source(adminData)
+ .color('#f0f0f0');
+
+const heatLayer = new HeatmapLayer({
+ name: '人口热力图',
+})
+ .source(populationData)
+ .size('value', [0, 1]);
+
+const poiLayer = new PointLayer({
+ name: 'POI点',
+})
+ .source(poiData)
+ .shape('circle')
+ .size(8);
+
+scene.addLayer(baseLayer);
+scene.addLayer(heatLayer);
+scene.addLayer(poiLayer);
+
+// 添加图层切换控件
+const layerSwitch = new LayerSwitch({
+ position: 'topright',
+ layers: [
+ {
+ layer: baseLayer,
+ name: '底图',
+ img: 'base.png',
+ },
+ {
+ layer: heatLayer,
+ name: '热力',
+ img: 'heat.png',
+ },
+ {
+ layer: poiLayer,
+ name: '标注',
+ img: 'poi.png',
+ },
+ ],
+ multiple: true,
+});
+
+scene.addControl(layerSwitch);
+```
+
+### 3. 自定义样式控件
+
+```javascript
+const zoom = new Zoom({
+ position: 'topleft',
+ className: 'custom-zoom',
+ zoomInText: '➕',
+ zoomOutText: '➖',
+});
+
+scene.addControl(zoom);
+
+// CSS 样式
+/*
+.custom-zoom {
+ background: rgba(0, 0, 0, 0.8);
+ border-radius: 8px;
+ padding: 8px;
+}
+
+.custom-zoom button {
+ color: white;
+ font-size: 20px;
+}
+*/
+```
+
+### 4. 响应式控件位置
+
+```javascript
+const zoom = new Zoom({
+ position: window.innerWidth < 768 ? 'bottomleft' : 'topleft',
+});
+
+scene.addControl(zoom);
+
+// 监听窗口大小变化
+window.addEventListener('resize', () => {
+ const newPosition = window.innerWidth < 768 ? 'bottomleft' : 'topleft';
+ zoom.setOptions({ position: newPosition });
+});
+```
+
+### 5. 控件事件监听
+
+```javascript
+const layerSwitch = new LayerSwitch({
+ layers: [layer1, layer2],
+ multiple: false,
+});
+
+// 监听图层切换
+layerSwitch.on('selectChange', (selectedLayers) => {
+ console.log('当前选中的图层:', selectedLayers);
+
+ // 根据选中图层更新其他UI
+ updateLegend(selectedLayers[0]);
+});
+
+const fullscreen = new Fullscreen();
+
+fullscreen.on('fullscreenChange', (isFullscreen) => {
+ if (isFullscreen) {
+ console.log('进入全屏');
+ // 隐藏其他UI元素
+ } else {
+ console.log('退出全屏');
+ // 恢复UI元素
+ }
+});
+
+scene.addControl(layerSwitch);
+scene.addControl(fullscreen);
+```
+
+## 常见问题
+
+### Q: 如何自定义控件位置?
+
+A: 除了使用预设的 8 个位置,还可以通过 CSS 自定义:
+
+```javascript
+const zoom = new Zoom({
+ position: 'topleft',
+ className: 'custom-position',
+});
+
+scene.addControl(zoom);
+
+// CSS
+/*
+.custom-position {
+ position: absolute;
+ top: 100px !important;
+ left: 20px !important;
+}
+*/
+```
+
+### Q: 如何移除默认 Logo?
+
+A: 在 Scene 初始化时配置:
+
+```javascript
+const scene = new Scene({
+ id: 'map',
+ map: new GaodeMap({
+ /* ... */
+ }),
+ logoVisible: false, // 关闭默认 Logo
+});
+```
+
+### Q: LayerSwitch 如何指定默认显示的图层?
+
+A: 单选模式下默认显示第一个图层,多选模式下根据图层初始状态:
+
+```javascript
+// 单选模式:默认显示 layer1
+const layerSwitch = new LayerSwitch({
+ layers: [layer1, layer2, layer3],
+ multiple: false,
+});
+
+// 多选模式:控制图层初始可见性
+layer1.show();
+layer2.hide();
+layer3.show();
+
+const layerSwitch = new LayerSwitch({
+ layers: [layer1, layer2, layer3],
+ multiple: true,
+});
+```
+
+### Q: 如何获取控件实例?
+
+A: 保存实例引用或通过 Scene 获取:
+
+```javascript
+// 方式1:保存引用
+const zoom = new Zoom();
+scene.addControl(zoom);
+
+// 方式2:通过名称获取
+const zoom = new Zoom({ name: 'zoom-control' });
+scene.addControl(zoom);
+
+const control = scene.getControlByName('zoom-control');
+```
+
+### Q: 控件如何实现国际化?
+
+A: 通过配置项传入不同语言文本:
+
+```javascript
+const lang = 'zh-CN'; // 或 'en-US'
+
+const zoom = new Zoom({
+ zoomInTitle: lang === 'zh-CN' ? '放大' : 'Zoom In',
+ zoomOutTitle: lang === 'zh-CN' ? '缩小' : 'Zoom Out',
+});
+
+const fullscreen = new Fullscreen({
+ btnText: lang === 'zh-CN' ? '全屏' : 'Fullscreen',
+ exitBtnText: lang === 'zh-CN' ? '退出' : 'Exit',
+});
+```
+
+## 注意事项
+
+⚠️ **控件顺序**:同一位置的控件按添加顺序排列
+
+⚠️ **图层名称**:LayerSwitch 依赖图层的 `name` 属性,务必在创建图层时指定
+
+⚠️ **移动端适配**:移动端建议将控件放在不遮挡重要内容的位置
+
+⚠️ **控件样式**:可通过 `className` 自定义样式,但需注意不要影响控件功能
+
+⚠️ **性能考虑**:MouseLocation 控件会高频更新,注意 `transform` 回调的性能
+
+⚠️ **全屏 API**:Fullscreen 控件依赖浏览器的 Fullscreen API,部分浏览器可能不支持
+
+## 相关技能
+
+- [场景初始化](../core/scene.md)
+- [场景方法](../core/scene-methods.md)
+- [图层管理](../layers/base-layer.md)
+- [事件处理](./events.md)
+
+## 在线示例
+
+查看更多示例:[L7 控件示例](https://l7.antv.antgroup.com/examples/component/control)
diff --git a/skills/l7/references/interaction/events.md b/skills/l7/references/interaction/events.md
new file mode 100644
index 0000000..7ec909f
--- /dev/null
+++ b/skills/l7/references/interaction/events.md
@@ -0,0 +1,485 @@
+---
+skill_id: event-handling
+skill_name: 事件处理
+category: interaction
+difficulty: beginner
+tags: [event, click, mousemove, interaction]
+dependencies: [scene-initialization]
+version: 2.x
+---
+
+# 事件处理
+
+## 技能描述
+
+为图层添加交互事件监听,响应用户操作(点击、鼠标移动、悬停等)。
+
+## 何时使用
+
+- ✅ 点击要素查看详情
+- ✅ 鼠标悬停高亮显示
+- ✅ 右键显示菜单
+- ✅ 双击缩放定位
+- ✅ 实现自定义交互逻辑
+
+## 支持的事件类型
+
+| 事件 | 说明 | 常用场景 |
+| ------------- | -------- | ------------------ |
+| `click` | 单击 | 查看详情、选中要素 |
+| `dblclick` | 双击 | 缩放定位 |
+| `mousemove` | 鼠标移动 | Tooltip、高亮 |
+| `mouseenter` | 鼠标进入 | 高亮、改变样式 |
+| `mouseleave` | 鼠标离开 | 取消高亮 |
+| `mousedown` | 鼠标按下 | 拖拽开始 |
+| `mouseup` | 鼠标抬起 | 拖拽结束 |
+| `contextmenu` | 右键菜单 | 自定义菜单 |
+
+## 代码示例
+
+### 基础用法 - 点击事件
+
+```javascript
+layer.on('click', (e) => {
+ console.log('点击位置:', e.lngLat);
+ console.log('要素数据:', e.feature);
+ console.log('要素属性:', e.feature.properties);
+});
+```
+
+### 点击显示详情
+
+```javascript
+import { Popup } from '@antv/l7';
+
+layer.on('click', (e) => {
+ const { name, value, category } = e.feature.properties;
+
+ const popup = new Popup({
+ offsets: [0, 10],
+ }).setLnglat(e.lngLat).setHTML(`
+
+
${name}
+
类别: ${category}
+
数值: ${value}
+
+ `);
+
+ scene.addPopup(popup);
+});
+```
+
+### 鼠标悬停高亮
+
+```javascript
+let hoveredFeatureId = null;
+
+// 鼠标进入
+layer.on('mouseenter', (e) => {
+ // 重置之前的高亮
+ if (hoveredFeatureId !== null) {
+ layer.setActive(hoveredFeatureId, false);
+ }
+
+ // 高亮当前要素
+ hoveredFeatureId = e.featureId;
+ layer.setActive(hoveredFeatureId, true);
+
+ // 改变鼠标样式
+ scene.setMapStatus({ dragEnable: false });
+ document.body.style.cursor = 'pointer';
+});
+
+// 鼠标离开
+layer.on('mouseleave', () => {
+ // 取消高亮
+ if (hoveredFeatureId !== null) {
+ layer.setActive(hoveredFeatureId, false);
+ hoveredFeatureId = null;
+ }
+
+ // 恢复鼠标样式
+ scene.setMapStatus({ dragEnable: true });
+ document.body.style.cursor = 'default';
+});
+
+// 配置高亮样式
+layer.style({
+ selectColor: '#FF0000',
+});
+```
+
+### 鼠标移动显示 Tooltip
+
+```javascript
+let popup = null;
+
+layer.on('mousemove', (e) => {
+ const { name, value } = e.feature.properties;
+
+ // 移除旧的 Tooltip
+ if (popup) {
+ popup.remove();
+ }
+
+ // 创建新的 Tooltip
+ popup = new Popup({
+ closeButton: false,
+ closeOnClick: false,
+ anchor: 'bottom',
+ offsets: [0, -10],
+ }).setLnglat(e.lngLat).setHTML(`
+
+ ${name}: ${value}
+
+ `);
+
+ scene.addPopup(popup);
+});
+
+layer.on('mouseleave', () => {
+ if (popup) {
+ popup.remove();
+ popup = null;
+ }
+});
+```
+
+### 右键菜单
+
+```javascript
+layer.on('contextmenu', (e) => {
+ e.originalEvent.preventDefault(); // 阻止默认右键菜单
+
+ const { name, id } = e.feature.properties;
+
+ // 显示自定义菜单
+ showContextMenu(e.x, e.y, [
+ {
+ label: '查看详情',
+ onClick: () => showDetail(id),
+ },
+ {
+ label: '编辑',
+ onClick: () => editFeature(id),
+ },
+ {
+ label: '删除',
+ onClick: () => deleteFeature(id),
+ },
+ ]);
+});
+```
+
+### 双击定位
+
+```javascript
+layer.on('dblclick', (e) => {
+ const { lng, lat } = e.lngLat;
+
+ // 缩放并定位到点击位置
+ scene.setZoomAndCenter(12, [lng, lat], {
+ duration: 1000, // 动画时长 1 秒
+ });
+});
+```
+
+### 点击选中/取消选中
+
+```javascript
+let selectedFeatureId = null;
+
+layer.on('click', (e) => {
+ // 如果点击的是已选中的要素,取消选中
+ if (selectedFeatureId === e.featureId) {
+ layer.setActive(selectedFeatureId, false);
+ selectedFeatureId = null;
+ return;
+ }
+
+ // 取消之前的选中
+ if (selectedFeatureId !== null) {
+ layer.setActive(selectedFeatureId, false);
+ }
+
+ // 选中当前要素
+ selectedFeatureId = e.featureId;
+ layer.setActive(selectedFeatureId, true);
+
+ console.log('选中:', e.feature.properties.name);
+});
+```
+
+### 多选功能
+
+```javascript
+const selectedFeatures = new Set();
+
+layer.on('click', (e) => {
+ const featureId = e.featureId;
+
+ if (e.originalEvent.ctrlKey || e.originalEvent.metaKey) {
+ // 按住 Ctrl/Cmd 多选
+ if (selectedFeatures.has(featureId)) {
+ selectedFeatures.delete(featureId);
+ layer.setActive(featureId, false);
+ } else {
+ selectedFeatures.add(featureId);
+ layer.setActive(featureId, true);
+ }
+ } else {
+ // 单选,清除其他选中
+ selectedFeatures.forEach((id) => {
+ layer.setActive(id, false);
+ });
+ selectedFeatures.clear();
+
+ selectedFeatures.add(featureId);
+ layer.setActive(featureId, true);
+ }
+
+ console.log('已选中:', selectedFeatures.size, '个要素');
+});
+```
+
+### 拖拽事件
+
+```javascript
+let isDragging = false;
+let dragStartPos = null;
+
+layer.on('mousedown', (e) => {
+ isDragging = true;
+ dragStartPos = e.lngLat;
+ console.log('开始拖拽');
+});
+
+layer.on('mousemove', (e) => {
+ if (isDragging) {
+ console.log('拖拽中:', e.lngLat);
+ }
+});
+
+layer.on('mouseup', (e) => {
+ if (isDragging) {
+ isDragging = false;
+ console.log('结束拖拽');
+ console.log('拖拽距离:', calculateDistance(dragStartPos, e.lngLat));
+ }
+});
+```
+
+### 节流优化 - 高频事件
+
+```javascript
+import { throttle } from 'lodash';
+
+// 使用节流优化 mousemove 事件
+const handleMouseMove = throttle((e) => {
+ console.log('鼠标移动:', e.lngLat);
+ // 更新 UI
+}, 100); // 每 100ms 最多执行一次
+
+layer.on('mousemove', handleMouseMove);
+```
+
+## 事件对象属性
+
+### 事件对象 (e)
+
+| 属性 | 类型 | 说明 |
+| --------------- | ---------------- | ------------ |
+| `lngLat` | {lng, lat} | 地理坐标 |
+| `x` | number | 屏幕 X 坐标 |
+| `y` | number | 屏幕 Y 坐标 |
+| `feature` | Object | 要素对象 |
+| `featureId` | string \| number | 要素 ID |
+| `originalEvent` | Event | 原生事件对象 |
+
+### feature 对象
+
+```javascript
+layer.on('click', (e) => {
+ console.log(e.feature);
+ // {
+ // type: 'Feature',
+ // properties: { name: '杭州', value: 100 },
+ // geometry: { type: 'Point', coordinates: [120, 30] }
+ // }
+});
+```
+
+## 移除事件监听
+
+```javascript
+// 添加事件监听
+const handleClick = (e) => {
+ console.log('点击');
+};
+
+layer.on('click', handleClick);
+
+// 移除特定事件监听
+layer.off('click', handleClick);
+
+// 移除所有事件监听
+layer.off('click');
+```
+
+## 场景事件
+
+除了图层事件,Scene 也支持事件监听:
+
+```javascript
+// 场景加载完成
+scene.on('loaded', () => {
+ console.log('场景加载完成');
+});
+
+// 地图移动
+scene.on('mapmove', () => {
+ console.log('地图移动');
+});
+
+// 地图缩放
+scene.on('zoom', (e) => {
+ console.log('缩放级别:', e.zoom);
+});
+
+// 地图点击(未点击到图层)
+scene.on('click', (e) => {
+ console.log('点击地图:', e.lngLat);
+});
+```
+
+## 常见问题
+
+### 1. 事件不触发
+
+**检查清单**:
+
+- ✅ 图层是否已添加到场景
+- ✅ 图层是否可见
+- ✅ 事件名称是否正确
+- ✅ 是否有其他图层遮挡
+
+```javascript
+// 调试代码
+console.log('图层是否可见:', layer.isVisible());
+console.log('图层层级:', layer.zIndex);
+```
+
+### 2. 事件触发多次
+
+**原因**: 可能添加了多个相同的监听器
+
+**解决方案**:
+
+```javascript
+// 移除旧的监听器
+layer.off('click');
+
+// 添加新的监听器
+layer.on('click', handler);
+```
+
+### 3. 性能问题 - mousemove 卡顿
+
+**原因**: mousemove 事件频率太高
+
+**解决方案**: 使用节流
+
+```javascript
+import { throttle } from 'lodash';
+
+const handler = throttle((e) => {
+ // 处理逻辑
+}, 100);
+
+layer.on('mousemove', handler);
+```
+
+### 4. 阻止事件冒泡
+
+```javascript
+layer.on('click', (e) => {
+ e.originalEvent.stopPropagation(); // 阻止冒泡
+
+ // 你的逻辑
+});
+```
+
+## 高级用法
+
+### 事件委托 - 统一处理
+
+```javascript
+class LayerEventManager {
+ constructor(layers) {
+ this.layers = layers;
+ this.selectedFeatures = new Map();
+ }
+
+ bindClickEvents() {
+ this.layers.forEach((layer) => {
+ layer.on('click', (e) => {
+ this.handleClick(layer, e);
+ });
+ });
+ }
+
+ handleClick(layer, e) {
+ console.log(`图层 ${layer.id} 被点击`);
+ console.log('要素:', e.feature.properties);
+
+ // 统一的选中逻辑
+ this.selectFeature(layer, e.featureId);
+ }
+
+ selectFeature(layer, featureId) {
+ // 清除其他图层的选中
+ this.clearAllSelections();
+
+ // 选中当前要素
+ layer.setActive(featureId, true);
+ this.selectedFeatures.set(layer.id, featureId);
+ }
+
+ clearAllSelections() {
+ this.selectedFeatures.forEach((featureId, layerId) => {
+ const layer = this.layers.find((l) => l.id === layerId);
+ if (layer) {
+ layer.setActive(featureId, false);
+ }
+ });
+ this.selectedFeatures.clear();
+ }
+}
+
+// 使用
+const manager = new LayerEventManager([layer1, layer2, layer3]);
+manager.bindClickEvents();
+```
+
+### 条件事件触发
+
+```javascript
+layer.on('click', (e) => {
+ // 只处理特定类型的要素
+ if (e.feature.properties.type === 'important') {
+ handleImportantFeature(e);
+ } else {
+ handleNormalFeature(e);
+ }
+});
+```
+
+## 相关技能
+
+- [场景初始化](../core/scene.md)
+- [弹窗组件](./popup.md)
+- [标注组件](./components.md)
+
+## 在线示例
+
+查看更多示例: [L7 官方示例 - 交互](https://l7.antv.antgroup.com/examples/interaction/select)
diff --git a/skills/l7/references/interaction/popup.md b/skills/l7/references/interaction/popup.md
new file mode 100644
index 0000000..95acb33
--- /dev/null
+++ b/skills/l7/references/interaction/popup.md
@@ -0,0 +1,454 @@
+---
+skill_id: popup
+skill_name: 弹窗组件
+category: components
+difficulty: beginner
+tags: [popup, tooltip, info, interaction]
+dependencies: [scene-initialization]
+version: 2.x
+---
+
+# 弹窗组件
+
+## 技能描述
+
+在地图上显示信息弹窗,用于展示详细信息、提示文本等。
+
+## 何时使用
+
+- ✅ 点击要素显示详情
+- ✅ 鼠标悬停显示 Tooltip
+- ✅ 显示固定位置的说明信息
+- ✅ 展示表格、图表等复杂内容
+- ✅ 自定义交互提示
+
+## 前置条件
+
+- 已完成[场景初始化](../core/scene.md)
+
+## 代码示例
+
+### 基础用法 - 简单弹窗
+
+```javascript
+import { Popup } from '@antv/l7';
+
+const popup = new Popup({
+ offsets: [0, 10], // 偏移量 [x, y]
+ closeButton: true, // 显示关闭按钮
+})
+ .setLnglat([120.19, 30.26])
+ .setHTML('这是一个弹窗
');
+
+scene.addPopup(popup);
+```
+
+### 点击图层显示弹窗
+
+```javascript
+import { PointLayer } from '@antv/l7';
+import { Popup } from '@antv/l7';
+
+const pointLayer = new PointLayer()
+ .source(data, {
+ parser: {
+ type: 'json',
+ x: 'lng',
+ y: 'lat',
+ },
+ })
+ .shape('circle')
+ .size(10)
+ .color('#5B8FF9');
+
+scene.addLayer(pointLayer);
+
+// 点击显示弹窗
+pointLayer.on('click', (e) => {
+ const { name, value, category } = e.feature.properties;
+
+ const popup = new Popup({
+ offsets: [0, 10],
+ }).setLnglat(e.lngLat).setHTML(`
+
+
${name}
+
类别: ${category}
+
数值: ${value}
+
+ `);
+
+ scene.addPopup(popup);
+});
+```
+
+### 鼠标悬停显示 Tooltip
+
+```javascript
+let popup = null;
+
+layer.on('mousemove', (e) => {
+ const { name, value } = e.feature.properties;
+
+ // 移除旧弹窗
+ if (popup) {
+ popup.remove();
+ }
+
+ // 创建新弹窗
+ popup = new Popup({
+ closeButton: false,
+ closeOnClick: false,
+ })
+ .setLnglat(e.lngLat)
+ .setHTML(`${name}: ${value}
`);
+
+ scene.addPopup(popup);
+});
+
+layer.on('mouseleave', () => {
+ if (popup) {
+ popup.remove();
+ popup = null;
+ }
+});
+```
+
+### 自定义样式的弹窗
+
+```javascript
+const popup = new Popup({
+ offsets: [0, 10],
+ closeButton: true,
+ className: 'custom-popup', // 自定义 CSS 类名
+}).setLnglat([120.19, 30.26]).setHTML(`
+
+ `);
+
+scene.addPopup(popup);
+```
+
+对应 CSS:
+
+```css
+.custom-popup {
+ max-width: 300px;
+}
+
+.custom-popup .popup-content {
+ padding: 15px;
+ background: #fff;
+ border-radius: 8px;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
+}
+
+.custom-popup .popup-title {
+ font-size: 16px;
+ font-weight: bold;
+ margin-bottom: 10px;
+ color: #333;
+}
+
+.custom-popup .popup-body {
+ font-size: 14px;
+ color: #666;
+}
+
+.custom-popup .popup-body p {
+ margin: 5px 0;
+}
+```
+
+### 使用 DOM 元素创建弹窗
+
+```javascript
+const el = document.createElement('div');
+el.className = 'my-popup';
+el.innerHTML = `
+ 自定义内容
+
+`;
+
+const popup = new Popup({
+ offsets: [0, 10],
+})
+ .setLnglat([120.19, 30.26])
+ .setDOMContent(el);
+
+scene.addPopup(popup);
+```
+
+### 显示图表的弹窗
+
+```javascript
+import { Popup } from '@antv/l7';
+import { Chart } from '@antv/g2';
+
+layer.on('click', (e) => {
+ const { name, data } = e.feature.properties;
+
+ // 创建容器
+ const container = document.createElement('div');
+ container.style.width = '400px';
+ container.style.height = '300px';
+
+ const popup = new Popup({
+ offsets: [0, 10],
+ closeButton: true,
+ })
+ .setLnglat(e.lngLat)
+ .setDOMContent(container);
+
+ scene.addPopup(popup);
+
+ // 在弹窗中渲染图表
+ const chart = new Chart({
+ container: container,
+ autoFit: true,
+ });
+
+ chart.data(data);
+ chart.interval().position('month*value');
+ chart.render();
+});
+```
+
+### 只允许显示一个弹窗
+
+```javascript
+let currentPopup = null;
+
+layer.on('click', (e) => {
+ // 关闭之前的弹窗
+ if (currentPopup) {
+ currentPopup.remove();
+ }
+
+ // 创建新弹窗
+ currentPopup = new Popup({
+ offsets: [0, 10],
+ })
+ .setLnglat(e.lngLat)
+ .setHTML(`${e.feature.properties.name}
`);
+
+ scene.addPopup(currentPopup);
+});
+```
+
+### 固定位置的说明弹窗
+
+```javascript
+// 在地图右上角显示固定说明
+const infoPopup = new Popup({
+ anchor: 'top-left',
+ closeButton: false,
+ closeOnClick: false,
+}).setLnglat([120.25, 30.3]).setHTML(`
+
+
数据说明
+
数据来源: XXX
+
更新时间: 2024-01-01
+
+ `);
+
+scene.addPopup(infoPopup);
+```
+
+## 配置参数
+
+### 构造函数参数
+
+| 参数 | 类型 | 默认值 | 说明 |
+| ----------------- | ---------------- | -------- | ---------------- |
+| `closeButton` | boolean | true | 是否显示关闭按钮 |
+| `closeOnClick` | boolean | true | 点击地图是否关闭 |
+| `closeOnEsc` | boolean | true | 按 ESC 是否关闭 |
+| `maxWidth` | string | '240px' | 最大宽度 |
+| `offsets` | [number, number] | [0, 0] | 偏移量 [x, y] |
+| `anchor` | string | 'bottom' | 锚点位置 |
+| `className` | string | '' | 自定义 CSS 类名 |
+| `stopPropagation` | boolean | true | 是否阻止事件传播 |
+
+### 锚点位置 (anchor)
+
+- `center`: 中心
+- `top`: 上方
+- `bottom`: 下方
+- `left`: 左侧
+- `right`: 右侧
+- `top-left`: 左上
+- `top-right`: 右上
+- `bottom-left`: 左下
+- `bottom-right`: 右下
+
+```javascript
+const popup = new Popup({
+ anchor: 'top', // 弹窗出现在点的上方
+});
+```
+
+## 实例方法
+
+| 方法 | 说明 | 示例 |
+| ------------------- | -------------- | ---------------------------------- |
+| `setLnglat(lnglat)` | 设置位置 | `popup.setLnglat([120, 30])` |
+| `setHTML(html)` | 设置 HTML 内容 | `popup.setHTML('内容
')` |
+| `setText(text)` | 设置纯文本内容 | `popup.setText('文本内容')` |
+| `setDOMContent(el)` | 设置 DOM 元素 | `popup.setDOMContent(element)` |
+| `remove()` | 移除弹窗 | `popup.remove()` |
+| `isOpen()` | 是否打开 | `popup.isOpen()` |
+| `addTo(scene)` | 添加到场景 | `popup.addTo(scene)` |
+
+## 常见问题
+
+### 1. 弹窗位置不对
+
+**原因**: 偏移量设置不合适
+
+**解决方案**:
+
+```javascript
+const popup = new Popup({
+ offsets: [0, -20], // 向上偏移 20 像素
+ anchor: 'bottom', // 设置锚点位置
+});
+```
+
+### 2. 弹窗内容被截断
+
+**原因**: 宽度限制
+
+**解决方案**:
+
+```javascript
+const popup = new Popup({
+ maxWidth: '400px' // 增加最大宽度
+});
+
+// 或使用 CSS
+.l7-popup {
+ max-width: 400px !important;
+}
+```
+
+### 3. 弹窗事件冲突
+
+**问题**: 点击弹窗内的按钮触发了地图事件
+
+**解决方案**:
+
+```javascript
+const popup = new Popup({
+ stopPropagation: true, // 阻止事件传播
+});
+```
+
+### 4. 多个弹窗管理
+
+**问题**: 同时显示多个弹窗,需要统一管理
+
+**解决方案**:
+
+```javascript
+class PopupManager {
+ constructor() {
+ this.popups = [];
+ }
+
+ add(popup) {
+ this.popups.push(popup);
+ }
+
+ removeAll() {
+ this.popups.forEach((popup) => popup.remove());
+ this.popups = [];
+ }
+
+ removeLast() {
+ const popup = this.popups.pop();
+ if (popup) {
+ popup.remove();
+ }
+ }
+}
+
+const manager = new PopupManager();
+
+layer.on('click', (e) => {
+ const popup = new Popup().setLnglat(e.lngLat).setHTML(`${e.feature.properties.name}
`);
+
+ scene.addPopup(popup);
+ manager.add(popup);
+});
+
+// 清除所有弹窗
+manager.removeAll();
+```
+
+## 高级用法
+
+### 响应式弹窗
+
+```javascript
+function createResponsivePopup(data, lnglat) {
+ const isMobile = window.innerWidth < 768;
+
+ const popup = new Popup({
+ maxWidth: isMobile ? '200px' : '400px',
+ offsets: isMobile ? [0, 5] : [0, 10],
+ }).setLnglat(lnglat).setHTML(`
+
+
${data.name}
+
${data.value}
+
+ `);
+
+ return popup;
+}
+```
+
+### 带加载状态的弹窗
+
+```javascript
+layer.on('click', async (e) => {
+ // 显示加载状态
+ const popup = new Popup({
+ offsets: [0, 10],
+ })
+ .setLnglat(e.lngLat)
+ .setHTML('加载中...
');
+
+ scene.addPopup(popup);
+
+ // 异步加载数据
+ try {
+ const data = await fetchDetailData(e.feature.properties.id);
+
+ // 更新内容
+ popup.setHTML(`
+
+
${data.name}
+
${data.description}
+
+ `);
+ } catch (error) {
+ popup.setHTML('加载失败
');
+ }
+});
+```
+
+## 相关技能
+
+- [场景初始化](../core/scene.md)
+- [事件处理](./events.md)
+- [标注组件](./components.md)
+- [点图层](../layers/point.md)
+
+## 在线示例
+
+查看更多示例: [L7 官方示例 - Popup](https://l7.antv.antgroup.com/examples/component/popup)
diff --git a/skills/l7/references/layers/base-layer.md b/skills/l7/references/layers/base-layer.md
new file mode 100644
index 0000000..531d9a4
--- /dev/null
+++ b/skills/l7/references/layers/base-layer.md
@@ -0,0 +1,698 @@
+---
+skill_id: layer-common-api
+skill_name: 图层通用方法和事件
+category: layers
+difficulty: beginner
+tags:
+ [
+ layer-api,
+ layer-methods,
+ layer-events,
+ layer-common,
+ show,
+ hide,
+ visible,
+ setIndex,
+ fitBounds,
+ zoom,
+ click,
+ mousemove,
+ mouseout,
+ hover,
+ contextmenu,
+ source,
+ scale,
+ filter,
+ event,
+ active,
+ select,
+ 通用方法,
+ 图层控制,
+ 鼠标事件,
+ 数据方法,
+ ]
+type: reference
+dependencies: [scene-initialization]
+applies_to: [point, line, polygon, heatmap, image, raster, tile-vector]
+related_skills: [point, line, polygon, heatmap, events]
+version: 2.x
+---
+
+# 图层通用方法和事件
+
+## 技能描述
+
+掌握 L7 所有图层通用的方法和事件。本文档是参考手册,详细说明了 PointLayer、LineLayer、PolygonLayer 等所有图层类型共享的核心 API,包括显示控制、数据管理、事件监听等能力。
+
+> 💡 **使用提示**:这是通用 API 参考文档。使用具体图层时,请查看对应的图层文档(如 [点图层](./point.md)、[线图层](./line.md)),它们会介绍图层特有功能并引用本文档的通用能力。
+
+## 何时使用
+
+- ✅ 需要控制图层的显示和隐藏
+- ✅ 需要调整图层的绘制顺序
+- ✅ 需要监听图层的鼠标事件
+- ✅ 需要动态更新图层数据或样式
+- ✅ 需要获取图层的状态和属性
+- ✅ 需要聚合数据或使用数据转换
+
+## 前置条件
+
+- 已完成[场景初始化](../core/scene.md)
+- 了解基本的图层类型([点图层](./point.md)、[线图层](./line.md)等)
+
+## 核心概念
+
+### 图层语法
+
+L7 图层遵循图形语法,提供链式调用API:
+
+```javascript
+const layer = new PointLayer(options)
+ .source(data, config) // 设置数据源
+ .scale(field, scaleConfig) // 设置数据映射
+ .filter(callback) // 数据过滤
+ .shape(field, values) // 设置形状
+ .color(field, colors) // 设置颜色
+ .size(field, sizes) // 设置大小
+ .texture(field, textures) // 设置纹理
+ .animate(options) // 设置动画
+ .active(options) // 设置高亮
+ .select(options) // 设置选中
+ .style(options); // 设置样式
+
+scene.addLayer(layer);
+```
+
+## 图层控制方法
+
+### show(): void
+
+显示图层。
+
+```javascript
+layer.show();
+```
+
+### hide(): void
+
+隐藏图层。
+
+```javascript
+layer.hide();
+```
+
+**示例:切换图层显示**
+
+```javascript
+let isVisible = true;
+
+function toggleLayer() {
+ if (isVisible) {
+ layer.hide();
+ } else {
+ layer.show();
+ }
+ isVisible = !isVisible;
+}
+
+// 按钮点击事件
+document.getElementById('toggle-btn').addEventListener('click', toggleLayer);
+```
+
+### isVisible(): boolean
+
+检查图层是否可见。
+
+```javascript
+if (layer.isVisible()) {
+ console.log('图层可见');
+} else {
+ console.log('图层隐藏');
+}
+```
+
+### setIndex(zIndex: number): void
+
+设置图层绘制顺序,数值越大越在上层。
+
+```javascript
+// 设置图层在最上层
+layer.setIndex(999);
+
+// 设置图层在底层
+layer.setIndex(1);
+```
+
+**示例:图层分层管理**
+
+```javascript
+// 底层:区域底色
+polygonLayer.setIndex(1);
+
+// 中层:道路
+lineLayer.setIndex(2);
+
+// 顶层:POI 标注
+pointLayer.setIndex(3);
+```
+
+### fitBounds(fitBoundsOptions?: IFitBoundsOptions): void
+
+缩放地图至图层数据范围。
+
+```javascript
+// 基础用法
+layer.fitBounds();
+
+// 带参数
+layer.fitBounds({
+ padding: 50, // 边距(像素)
+});
+```
+
+**示例:加载数据后自动适配范围**
+
+```javascript
+const layer = new PointLayer()
+ .source(data, { parser: { type: 'json', x: 'lng', y: 'lat' } })
+ .shape('circle')
+ .size(10)
+ .color('#5B8FF9');
+
+scene.addLayer(layer);
+
+scene.on('loaded', () => {
+ // 自动缩放到数据范围
+ layer.fitBounds();
+});
+```
+
+### setMinZoom(zoom: number): void
+
+设置图层最小缩放等级(小于此等级时不显示)。
+
+```javascript
+// 地图缩放级别小于 10 时不显示该图层
+layer.setMinZoom(10);
+```
+
+### setMaxZoom(zoom: number): void
+
+设置图层最大缩放等级(大于此等级时不显示)。
+
+```javascript
+// 地图缩放级别大于 18 时不显示该图层
+layer.setMaxZoom(18);
+```
+
+**示例:根据缩放级别切换图层**
+
+```javascript
+// 小级别显示聚合数据
+clusterLayer.setMaxZoom(12);
+
+// 大级别显示详细数据
+detailLayer.setMinZoom(12);
+
+scene.on('zoomchange', () => {
+ const zoom = scene.getZoom();
+ console.log(`当前缩放级别: ${zoom}`);
+});
+```
+
+## 数据方法
+
+### source(data, config): Layer
+
+设置图层数据源和解析配置。
+
+```javascript
+layer.source(data, {
+ parser: {
+ type: 'json', // 数据类型: json | geojson | csv
+ x: 'lng', // 经度字段
+ y: 'lat', // 纬度字段
+ },
+ transforms: [
+ // 数据转换(可选)
+ {
+ type: 'map',
+ callback: (item) => {
+ item.value = item.value * 100;
+ return item;
+ },
+ },
+ ],
+});
+```
+
+**支持的数据格式**:
+
+- GeoJSON - [详见文档](../data/source-geojson.md)
+- JSON - [详见文档](../data/source-json.md)
+- CSV - [详见文档](../data/source-csv.md)
+
+### scale(field, scaleOptions): Layer
+
+设置数据字段的映射规则。
+
+```javascript
+layer.scale('value', {
+ type: 'linear', // scale 类型
+ domain: [0, 100], // 数据值域
+});
+```
+
+**Scale 类型**:
+
+| 类型 | 适用数据 | 说明 |
+| --------- | -------- | ---------------- |
+| linear | 连续数值 | 线性映射 |
+| log | 连续数值 | 对数映射 |
+| pow | 连续数值 | 幂次映射 |
+| quantize | 连续数值 | 等间距分类 |
+| quantile | 连续数值 | 分位数分类 |
+| threshold | 连续数值 | 自定义阈值分类 |
+| diverging | 连续数值 | 发散分类(双色) |
+| cat | 分类数据 | 类别映射 |
+| identity | 任意 | 值即映射结果 |
+
+**示例**:
+
+```javascript
+// 线性映射
+layer.scale('population', {
+ type: 'linear',
+ domain: [0, 10000000],
+});
+
+// 分类映射
+layer.scale('category', {
+ type: 'cat',
+ domain: ['A', 'B', 'C'],
+});
+
+// 阈值分类
+layer.scale('aqi', {
+ type: 'threshold',
+ domain: [50, 100, 150, 200, 300], // 5个阈值,需要6个颜色
+});
+```
+
+详细说明参见 [视觉映射](../visual/mapping.md)。
+
+### filter(callback): Layer
+
+数据过滤,返回 true 的数据会被显示。
+
+```javascript
+// 只显示值大于 100 的数据
+layer.filter((feature) => {
+ return feature.value > 100;
+});
+
+// 根据类型过滤
+layer.filter((feature) => {
+ return ['A', 'B'].includes(feature.type);
+});
+```
+
+### getScale(scaleName: string): IScale
+
+获取指定字段的 scale 实例。
+
+```javascript
+const valueScale = layer.getScale('value');
+console.log('值域:', valueScale.domain);
+console.log('映射范围:', valueScale.range);
+```
+
+## 数据聚合方法
+
+### cluster 聚合配置
+
+使用聚合功能时,可通过以下方法获取聚合数据:
+
+#### getClusters(zoom: number): IFeatureCollection
+
+获取指定缩放等级的聚合数据。
+
+```javascript
+const source = layer.getSource();
+const clusters = source.getClusters(10); // 获取 zoom=10 的聚合数据
+console.log('聚合节点数量:', clusters.features.length);
+```
+
+#### getClustersLeaves(id: string): IFeatureCollection
+
+获取聚合节点包含的原始数据。
+
+```javascript
+const source = layer.getSource();
+
+layer.on('click', (e) => {
+ if (e.feature.cluster) {
+ // 获取聚合节点的原始数据
+ const leaves = source.getClustersLeaves(e.feature.cluster_id);
+ console.log('该聚合包含数据:', leaves);
+ }
+});
+```
+
+## 鼠标事件
+
+所有图层支持的鼠标事件:
+
+### 基础鼠标事件
+
+```javascript
+// 点击事件
+layer.on('click', (e) => {
+ console.log('点击位置:', e.lngLat);
+ console.log('点击要素:', e.feature);
+});
+
+// 双击事件
+layer.on('dblclick', (e) => {
+ console.log('双击要素:', e.feature);
+});
+
+// 鼠标移动
+layer.on('mousemove', (e) => {
+ // 高频事件,注意性能
+});
+
+// 鼠标移出
+layer.on('mouseout', (e) => {
+ console.log('鼠标移出');
+});
+
+// 鼠标按下
+layer.on('mousedown', (e) => {
+ console.log('鼠标按下');
+});
+
+// 鼠标抬起
+layer.on('mouseup', (e) => {
+ console.log('鼠标抬起');
+});
+
+// 右键菜单
+layer.on('contextmenu', (e) => {
+ e.preventDefault(); // 阻止默认菜单
+ console.log('右键点击:', e.lngLat);
+});
+```
+
+### 未拾取事件(Unpick Events)
+
+当鼠标操作未选中图层要素时触发:
+
+```javascript
+// 点击图层外
+layer.on('unclick', (e) => {
+ console.log('点击了图层外的区域');
+});
+
+// 图层外移动
+layer.on('unmousemove', (e) => {
+ // 鼠标在图层外移动
+});
+
+// 图层外鼠标抬起
+layer.on('unmouseup', (e) => {});
+
+// 图层外鼠标按下
+layer.on('unmousedown', (e) => {});
+
+// 图层外右键
+layer.on('uncontextmenu', (e) => {});
+
+// 所有未拾取事件
+layer.on('unpick', (e) => {
+ console.log('所有图层外的操作');
+});
+```
+
+### 移动端事件
+
+```javascript
+// 触摸开始
+layer.on('touchstart', (e) => {
+ console.log('触摸开始');
+});
+
+// 触摸结束
+layer.on('touchend', (e) => {
+ console.log('触摸结束');
+});
+```
+
+### 事件参数
+
+所有鼠标事件回调参数包含:
+
+```typescript
+interface ILayerMouseEvent {
+ x: number; // 鼠标在地图位置 x 坐标
+ y: number; // 鼠标在地图位置 y 坐标
+ type: string; // 事件类型
+ lngLat: ILngLat; // 经纬度 { lng, lat }
+ feature: any; // 选中的要素数据
+ featureId: number | null; // 要素 ID
+}
+```
+
+## 实际应用场景
+
+### 1. 图层高亮和弹窗
+
+```javascript
+const layer = new PointLayer()
+ .source(data, { parser: { type: 'json', x: 'lng', y: 'lat' } })
+ .shape('circle')
+ .size(10)
+ .color('#5B8FF9')
+ .active(true); // 开启高亮
+
+// 鼠标移入显示信息
+layer.on('mousemove', (e) => {
+ const { feature } = e;
+ const popup = new Popup({
+ offsets: [0, 20],
+ }).setLnglat(e.lngLat).setHTML(`
+
+
${feature.name}
+
值: ${feature.value}
+
+ `);
+ scene.addPopup(popup);
+});
+
+// 鼠标移出关闭弹窗
+layer.on('mouseout', () => {
+ scene.removeAllPopup();
+});
+```
+
+### 2. 点击查看详情
+
+```javascript
+layer.on('click', (e) => {
+ const { feature } = e;
+
+ // 显示详情面板
+ showDetailPanel({
+ name: feature.name,
+ address: feature.address,
+ phone: feature.phone,
+ });
+
+ // 高亮选中的点
+ layer.select(true);
+});
+```
+
+### 3. 根据缩放级别切换图层
+
+```javascript
+// 创建两个图层
+const clusterLayer = new PointLayer({ name: 'cluster' })
+ .source(clusterData)
+ .shape('circle')
+ .size('count', [20, 50])
+ .color('count', ['#ffffcc', '#800026'])
+ .setMaxZoom(12); // 小于 12 级显示
+
+const detailLayer = new PointLayer({ name: 'detail' })
+ .source(detailData)
+ .shape('circle')
+ .size(8)
+ .color('#5B8FF9')
+ .setMinZoom(12) // 大于 12 级显示
+ .hide(); // 初始隐藏
+
+scene.addLayer(clusterLayer);
+scene.addLayer(detailLayer);
+
+// 监听缩放变化
+scene.on('zoomchange', () => {
+ const zoom = scene.getZoom();
+ if (zoom >= 12) {
+ clusterLayer.hide();
+ detailLayer.show();
+ } else {
+ clusterLayer.show();
+ detailLayer.hide();
+ }
+});
+```
+
+### 4. 数据过滤和更新
+
+```javascript
+const layer = new PointLayer().source(data).shape('circle').size(10).color('category', colorMap);
+
+// 动态过滤数据
+function filterByCategory(categories) {
+ layer.filter((feature) => {
+ return categories.includes(feature.category);
+ });
+ scene.render(); // 触发重绘
+}
+
+// 过滤按钮
+document.getElementById('filter-A').addEventListener('click', () => {
+ filterByCategory(['A', 'B']);
+});
+```
+
+### 5. 聚合数据展示
+
+```javascript
+const layer = new PointLayer()
+ .source(data, {
+ parser: { type: 'json', x: 'lng', y: 'lat' },
+ cluster: true,
+ clusterOption: {
+ radius: 40,
+ minZoom: 0,
+ maxZoom: 16,
+ },
+ })
+ .shape('circle')
+ .size('point_count', [20, 60])
+ .color('point_count', ['#ffffcc', '#800026']);
+
+// 点击聚合节点查看包含的数据
+layer.on('click', (e) => {
+ const source = layer.getSource();
+
+ if (e.feature.cluster) {
+ // 获取聚合节点的原始数据
+ const leaves = source.getClustersLeaves(e.feature.cluster_id);
+ console.log('包含的数据:', leaves);
+
+ // 显示列表
+ showDataList(leaves);
+ }
+});
+```
+
+## 常见问题
+
+### Q: 如何获取图层对象?
+
+A: 通过 Scene 的方法获取:
+
+```javascript
+// 通过 ID
+const layer = scene.getLayer('layer-id');
+
+// 通过名称
+const layer = scene.getLayerByName('my-layer');
+
+// 获取所有图层
+const layers = scene.getLayers();
+```
+
+### Q: 图层顺序如何控制?
+
+A: 使用 `setIndex()` 方法,数值越大越在上层:
+
+```javascript
+bottomLayer.setIndex(1);
+middleLayer.setIndex(2);
+topLayer.setIndex(3);
+```
+
+### Q: 如何监听图层加载完成?
+
+A: 图层添加到 Scene 后会自动加载,监听 Scene 的 `loaded` 事件:
+
+```javascript
+scene.on('loaded', () => {
+ console.log('所有图层加载完成');
+});
+```
+
+### Q: 鼠标事件中如何获取原始数据?
+
+A: 通过事件参数的 `feature` 属性:
+
+```javascript
+layer.on('click', (e) => {
+ console.log('原始数据:', e.feature.properties);
+ console.log('坐标:', e.feature.geometry.coordinates);
+});
+```
+
+### Q: 如何移除事件监听?
+
+A: 使用 `off()` 方法:
+
+```javascript
+const handleClick = (e) => {
+ console.log('点击:', e);
+};
+
+// 绑定事件
+layer.on('click', handleClick);
+
+// 移除事件
+layer.off('click', handleClick);
+```
+
+### Q: filter 过滤后如何重置?
+
+A: 传入返回 true 的函数即可:
+
+```javascript
+// 重置过滤(显示所有数据)
+layer.filter(() => true);
+scene.render();
+```
+
+## 注意事项
+
+⚠️ **事件顺序**:先添加图层到 Scene,再绑定事件
+
+⚠️ **性能优化**:`mousemove` 是高频事件,避免在回调中执行复杂计算
+
+⚠️ **图层销毁**:使用 `scene.removeLayer(layer)` 会自动销毁图层并移除事件
+
+⚠️ **坐标系统**:确保数据坐标系统与地图匹配
+
+⚠️ **Scale 配置**:scale 要在 color/size 之前调用
+
+## 相关技能
+
+- [点图层](./point.md)
+- [线图层](./line.md)
+- [面图层](./polygon.md)
+- [视觉映射](../visual/mapping.md)
+- [事件处理](../interaction/events.md)
+
+## 在线示例
+
+查看更多示例:[L7 官方示例](https://l7.antv.antgroup.com/examples)
diff --git a/skills/l7/references/layers/heatmap.md b/skills/l7/references/layers/heatmap.md
new file mode 100644
index 0000000..cffcc32
--- /dev/null
+++ b/skills/l7/references/layers/heatmap.md
@@ -0,0 +1,465 @@
+---
+skill_id: heatmap-layer
+skill_name: 热力图层
+category: layers
+difficulty: beginner
+tags: [heatmap, layer, density, heat-visualization, intensity]
+dependencies: [scene-initialization, source-geojson]
+version: 2.x
+---
+
+# 热力图层
+
+## 技能描述
+
+使用颜色梯度展示地理空间数据的密度或强度分布,适合表现数据的聚集程度和热度区域。
+
+## 何时使用
+
+- ✅ 显示数据密度分布(人口密度、POI 密度)
+- ✅ 展示热点区域(犯罪热点、事故高发区)
+- ✅ 可视化强度分布(温度分布、污染程度)
+- ✅ 分析空间聚集模式(用户活跃区域、订单集中区)
+- ✅ 网格热力图(蜂窝热力图、方格热力图)
+
+## 前置条件
+
+- 已完成[场景初始化](../core/scene.md)
+- 准备好点位数据(包含经纬度和权重值)
+
+## 输入参数
+
+### 数据格式
+
+```typescript
+interface HeatmapData {
+ lng: number; // 经度
+ lat: number; // 纬度
+ value?: number; // 权重值(可选,默认为 1)
+ [key: string]: any;
+}
+```
+
+### 图层配置
+
+| 方法 | 参数 | 说明 |
+| ---------------------- | -------------------------------- | --------------------------------------- |
+| `source(data, config)` | data: 数据数组 | 设置数据源 |
+| `shape(type)` | type: 形状类型 | heatmap \| heatmap3D \| hexagon \| grid |
+| `size(field, range)` | field: 聚合字段, range: 映射范围 | 设置热力大小 |
+| `color(colors)` | colors: 颜色数组 | 设置颜色渐变 |
+| `style(config)` | config: 样式对象 | 设置样式参数 |
+
+## 输出
+
+返回 `HeatmapLayer` 实例
+
+## 通用方法
+
+热力图层继承了所有图层的通用能力:
+
+### 显示控制
+
+```javascript
+// 显示/隐藏图层
+heatmapLayer.show();
+heatmapLayer.hide();
+
+// 设置图层顺序(热力图通常在上层)
+heatmapLayer.setIndex(10);
+```
+
+### 事件监听
+
+```javascript
+// 点击热力区域
+heatmapLayer.on('click', (e) => {
+ console.log('点击位置:', e.lngLat);
+ console.log('热力值:', e.feature);
+});
+```
+
+### 缩放范围
+
+```javascript
+// 只在特定缩放级别显示热力图
+heatmapLayer.setMinZoom(10); // 小于 10 级不显示
+heatmapLayer.setMaxZoom(18); // 大于 18 级不显示
+```
+
+> 📖 **完整文档**:查看 [图层通用方法和事件](./layer-common-api.md) 了解所有通用 API。
+
+---
+
+## 代码示例
+
+### 基础用法 - 经典热力图
+
+```javascript
+import { HeatmapLayer } from '@antv/l7';
+
+const data = [
+ { lng: 120.19, lat: 30.26, value: 100 },
+ { lng: 120.2, lat: 30.27, value: 200 },
+ { lng: 120.21, lat: 30.28, value: 150 },
+ { lng: 120.19, lat: 30.29, value: 300 },
+];
+
+scene.on('loaded', () => {
+ const heatmapLayer = new HeatmapLayer()
+ .source(data, {
+ parser: {
+ type: 'json',
+ x: 'lng',
+ y: 'lat',
+ },
+ })
+ .shape('heatmap')
+ .size('value', [0, 1])
+ .style({
+ intensity: 3, // 热力强度
+ radius: 20, // 热力半径
+ opacity: 1.0, // 透明度
+ rampColors: {
+ colors: ['#2E8AE6', '#69D1AB', '#DAF291', '#FFE234', '#FF7C6A', '#FF4818'],
+ positions: [0, 0.2, 0.4, 0.6, 0.8, 1.0],
+ },
+ });
+
+ scene.addLayer(heatmapLayer);
+});
+```
+
+### 3D 热力图
+
+```javascript
+const heatmap3DLayer = new HeatmapLayer()
+ .source(data, {
+ parser: {
+ type: 'json',
+ x: 'lng',
+ y: 'lat',
+ },
+ transforms: [
+ {
+ type: 'hexagon',
+ size: 1000, // 六边形网格大小
+ field: 'value',
+ method: 'sum', // 聚合方式:sum | max | min | mean
+ },
+ ],
+ })
+ .shape('heatmap3D')
+ .size('sum', [0, 600]) // 3D 高度映射
+ .color('sum', [
+ '#0B1678',
+ '#1E3EAD',
+ '#2E8AE6',
+ '#69D1AB',
+ '#DAF291',
+ '#FFE234',
+ '#FF7C6A',
+ '#FF4818',
+ ])
+ .style({
+ coverage: 0.9, // 覆盖度
+ angle: 0, // 旋转角度
+ opacity: 1.0,
+ });
+
+scene.addLayer(heatmap3DLayer);
+```
+
+### 蜂窝热力图
+
+```javascript
+const hexagonLayer = new HeatmapLayer()
+ .source(data, {
+ parser: {
+ type: 'json',
+ x: 'lng',
+ y: 'lat',
+ },
+ transforms: [
+ {
+ type: 'hexagon',
+ size: 500, // 六边形大小(米)
+ field: 'value',
+ method: 'sum',
+ },
+ ],
+ })
+ .shape('hexagon')
+ .color('sum', ['#ffffcc', '#c7e9b4', '#7fcdbb', '#41b6c4', '#2c7fb8', '#253494'])
+ .style({
+ coverage: 0.8,
+ angle: 0,
+ });
+
+scene.addLayer(hexagonLayer);
+```
+
+### 方格热力图
+
+```javascript
+const gridLayer = new HeatmapLayer()
+ .source(data, {
+ parser: {
+ type: 'json',
+ x: 'lng',
+ y: 'lat',
+ },
+ transforms: [
+ {
+ type: 'grid',
+ size: 1000, // 方格大小(米)
+ field: 'value',
+ method: 'sum',
+ },
+ ],
+ })
+ .shape('square')
+ .color('sum', ['#feedde', '#fdd0a2', '#fdae6b', '#fd8d3c', '#e6550d', '#a63603'])
+ .style({
+ coverage: 1, // 完全覆盖
+ });
+
+scene.addLayer(gridLayer);
+```
+
+### 自定义配置 - 高级用法
+
+```javascript
+const advancedHeatmap = new HeatmapLayer({
+ name: 'custom-heatmap',
+ blend: 'additive', // 混合模式
+})
+ .source(data, {
+ parser: {
+ type: 'json',
+ x: 'lng',
+ y: 'lat',
+ },
+ })
+ .shape('heatmap')
+ .size('value', (value) => {
+ // 自定义大小映射函数
+ return Math.sqrt(value) / 100;
+ })
+ .style({
+ intensity: 2,
+ radius: 25,
+ opacity: 0.8,
+ rampColors: {
+ colors: [
+ 'rgba(33,102,172,0)',
+ 'rgb(103,169,207)',
+ 'rgb(209,229,240)',
+ 'rgb(253,219,199)',
+ 'rgb(239,138,98)',
+ 'rgb(178,24,43)',
+ ],
+ positions: [0, 0.2, 0.4, 0.6, 0.8, 1.0],
+ },
+ });
+
+scene.addLayer(advancedHeatmap);
+```
+
+## 样式配置详解
+
+### 经典热力图样式
+
+```javascript
+{
+ intensity: 3, // 热力强度,值越大颜色越浓,范围 1-10
+ radius: 20, // 热力半径(像素),影响扩散范围
+ opacity: 1.0, // 整体透明度,0-1
+ rampColors: {
+ colors: [...], // 颜色数组,从低到高
+ positions: [...] // 颜色位置,0-1,需与 colors 对应
+ }
+}
+```
+
+### 3D/网格热力图样式
+
+```javascript
+{
+ coverage: 0.9, // 覆盖度,0-1,控制网格间距
+ angle: 0, // 旋转角度,0-360
+ opacity: 1.0 // 透明度
+}
+```
+
+## 常见场景
+
+### 1. 人口密度分析
+
+```javascript
+const populationHeatmap = new HeatmapLayer()
+ .source(populationData, {
+ parser: { type: 'json', x: 'lng', y: 'lat' },
+ })
+ .shape('heatmap')
+ .size('population', [0, 1])
+ .style({
+ intensity: 3,
+ radius: 30,
+ rampColors: {
+ colors: [
+ '#FFFFCC',
+ '#FFEDA0',
+ '#FED976',
+ '#FEB24C',
+ '#FD8D3C',
+ '#FC4E2A',
+ '#E31A1C',
+ '#BD0026',
+ '#800026',
+ ],
+ positions: [0, 0.1, 0.2, 0.3, 0.4, 0.6, 0.7, 0.85, 1.0],
+ },
+ });
+```
+
+### 2. 实时订单热力
+
+```javascript
+const orderHeatmap = new HeatmapLayer()
+ .source(orderData, {
+ parser: { type: 'json', x: 'lng', y: 'lat' },
+ })
+ .shape('heatmap3D')
+ .size('order_count', [0, 500])
+ .color('order_count', ['#440154', '#3b528b', '#21908c', '#5dc863', '#fde725'])
+ .style({
+ coverage: 0.8,
+ opacity: 0.8,
+ });
+
+// 定时更新数据
+setInterval(() => {
+ fetch('/api/realtime-orders')
+ .then((res) => res.json())
+ .then((newData) => {
+ orderHeatmap.setData(newData);
+ });
+}, 5000);
+```
+
+### 3. 交通事故热点
+
+```javascript
+const accidentHeatmap = new HeatmapLayer()
+ .source(accidentData, {
+ parser: { type: 'json', x: 'lng', y: 'lat' },
+ transforms: [
+ {
+ type: 'hexagon',
+ size: 500,
+ field: 'severity',
+ method: 'max',
+ },
+ ],
+ })
+ .shape('hexagon')
+ .color('max', [
+ '#f7fbff',
+ '#deebf7',
+ '#c6dbef',
+ '#9ecae1',
+ '#6baed6',
+ '#4292c6',
+ '#2171b5',
+ '#084594',
+ ]);
+```
+
+## 性能优化
+
+### 1. 数据量优化
+
+```javascript
+// 大数据量时使用网格聚合
+const layer = new HeatmapLayer()
+ .source(bigData, {
+ parser: { type: 'json', x: 'lng', y: 'lat' },
+ transforms: [
+ {
+ type: 'grid',
+ size: 2000, // 增大网格减少计算量
+ field: 'value',
+ method: 'sum',
+ },
+ ],
+ })
+ .shape('square');
+```
+
+### 2. 降低渲染精度
+
+```javascript
+layer.style({
+ radius: 15, // 减小半径
+ intensity: 2, // 降低强度
+ opacity: 0.8,
+});
+```
+
+### 3. 使用抽样
+
+```javascript
+// 数据抽样,适合超大数据集
+const sampledData = data.filter((_, index) => index % 10 === 0);
+
+layer.source(sampledData, {
+ /* config */
+});
+```
+
+## 注意事项
+
+⚠️ **数据量**:经典热力图建议数据量在 10 万以内,超过建议使用网格热力图
+
+⚠️ **颜色位置**:`rampColors.positions` 必须是递增的 0-1 数组,长度需与 colors 对应
+
+⚠️ **半径设置**:`radius` 过大会导致性能下降,建议 10-50 像素
+
+⚠️ **权重字段**:如果数据没有权重字段,系统会为每个点赋予默认权重 1
+
+⚠️ **坐标系统**:确保经纬度数据正确,经度范围 -180~180,纬度范围 -90~90
+
+## 常见问题
+
+### Q: 热力图颜色不明显?
+
+A: 增加 `intensity` 参数或增大 `radius` 值
+
+### Q: 3D 热力图高度不明显?
+
+A: 调整 `size()` 方法的映射范围,如 `[0, 1000]`
+
+### Q: 网格热力图出现空洞?
+
+A: 增大 `coverage` 参数至 1.0,或减小网格 `size`
+
+### Q: 数据更新后热力图不刷新?
+
+A: 使用 `layer.setData(newData)` 或 `scene.render()`
+
+### Q: 热力图边界被裁切?
+
+A: 调整地图的 `padding` 或使用 `fitBounds()` 方法
+
+## 相关技能
+
+- [图层通用方法和事件](./layer-common-api.md)
+- [点图层](./point.md)
+- [面图层](./polygon.md)
+- [数据映射](../visual/mapping.md)
+- [数据聚合](../data/source-parser.md)
+
+## 在线示例
+
+查看更多示例: [L7 官方示例 - 热力图](https://l7.antv.antgroup.com/examples/heatmap/heatmap)
diff --git a/skills/l7/references/layers/image.md b/skills/l7/references/layers/image.md
new file mode 100644
index 0000000..28aae28
--- /dev/null
+++ b/skills/l7/references/layers/image.md
@@ -0,0 +1,577 @@
+---
+skill_id: image-layer
+skill_name: 图片图层
+category: layers
+difficulty: beginner
+tags: [image, layer, raster, overlay, photo]
+dependencies: [scene-initialization]
+version: 2.x
+---
+
+# 图片图层
+
+## 技能描述
+
+在地图上叠加显示图片,支持卫星图、航拍图、扫描地图、历史地图等图片的精确定位和显示。
+
+## 何时使用
+
+- ✅ 显示卫星遥感图片(卫星影像、航拍照片)
+- ✅ 叠加历史地图(古地图对比、历史影像)
+- ✅ 显示扫描文档(建筑平面图、工程图纸)
+- ✅ 展示分析结果图(热力分析图、风险区域图)
+- ✅ 自定义底图(特殊区域地图、室内地图)
+
+## 前置条件
+
+- 已完成[场景初始化](../core/scene.md)
+- 准备好图片 URL 或 Base64 数据
+- 确定图片的地理边界坐标(四个角点)
+
+## 输入参数
+
+### 数据格式
+
+```typescript
+interface ImageData {
+ extent: [number, number, number, number]; // [minLng, minLat, maxLng, maxLat]
+ // 或使用四个角点
+ coordinates: [
+ [number, number], // 左上角 [lng, lat]
+ [number, number], // 右上角 [lng, lat]
+ [number, number], // 右下角 [lng, lat]
+ [number, number], // 左下角 [lng, lat]
+ ];
+}
+```
+
+### 图层配置
+
+| 方法 | 参数 | 说明 |
+| --------------------- | --------------------------- | -------------- |
+| `source(url, config)` | url: 图片地址, config: 配置 | 设置图片数据源 |
+| `shape(type)` | type: 形状类型 | image(默认) |
+| `style(config)` | config: 样式对象 | 设置样式 |
+
+## 输出
+
+返回 `ImageLayer` 实例
+
+## 通用方法
+
+图片图层继承了所有图层的通用能力:
+
+### 显示控制
+
+```javascript
+// 显示/隐藏图层
+imageLayer.show();
+imageLayer.hide();
+
+// 检查可见性
+if (imageLayer.isVisible()) {
+ console.log('图片可见');
+}
+
+// 设置图层顺序
+imageLayer.setIndex(5);
+```
+
+### 事件监听
+
+```javascript
+// 点击图片区域
+imageLayer.on('click', (e) => {
+ console.log('点击位置:', e.lngLat);
+});
+```
+
+### 缩放和范围
+
+```javascript
+// 缩放到图片范围
+imageLayer.fitBounds();
+
+// 设置显示的缩放范围
+imageLayer.setMinZoom(8);
+imageLayer.setMaxZoom(16);
+```
+
+> 📖 **完整文档**:查看 [图层通用方法和事件](./layer-common-api.md) 了解所有通用 API。
+
+---
+
+## 代码示例
+
+### 基础用法 - extent 方式
+
+```javascript
+import { ImageLayer } from '@antv/l7';
+
+scene.on('loaded', () => {
+ const imageLayer = new ImageLayer()
+ .source('https://gw.alipayobjects.com/zos/rmsportal/FnHFeFklTzKDdUESRNDv.jpg', {
+ parser: {
+ type: 'image',
+ extent: [121.168, 30.2828, 121.384, 30.4219], // [西, 南, 东, 北]
+ },
+ })
+ .style({
+ opacity: 1.0,
+ });
+
+ scene.addLayer(imageLayer);
+});
+```
+
+### 四角点定位方式
+
+```javascript
+const imageLayer = new ImageLayer().source('https://example.com/aerial-photo.jpg', {
+ parser: {
+ type: 'image',
+ coordinates: [
+ [121.168, 30.4219], // 左上角 [经度, 纬度]
+ [121.384, 30.4219], // 右上角
+ [121.384, 30.2828], // 右下角
+ [121.168, 30.2828], // 左下角
+ ],
+ },
+});
+
+scene.addLayer(imageLayer);
+```
+
+### Base64 图片
+
+```javascript
+const base64Image = 'data:image/png;base64,iVBORw0KGgoAAAANS...';
+
+const imageLayer = new ImageLayer()
+ .source(base64Image, {
+ parser: {
+ type: 'image',
+ extent: [120.0, 30.0, 121.0, 31.0],
+ },
+ })
+ .style({
+ opacity: 0.8,
+ });
+
+scene.addLayer(imageLayer);
+```
+
+### 多张图片叠加
+
+```javascript
+const images = [
+ {
+ url: 'https://example.com/layer1.png',
+ extent: [121.0, 30.0, 122.0, 31.0],
+ opacity: 0.5,
+ },
+ {
+ url: 'https://example.com/layer2.png',
+ extent: [121.0, 30.0, 122.0, 31.0],
+ opacity: 0.7,
+ },
+];
+
+images.forEach((img, index) => {
+ const layer = new ImageLayer({ name: `image-${index}` })
+ .source(img.url, {
+ parser: {
+ type: 'image',
+ extent: img.extent,
+ },
+ })
+ .style({
+ opacity: img.opacity,
+ });
+
+ scene.addLayer(layer);
+});
+```
+
+### 动态更新图片
+
+```javascript
+const imageLayer = new ImageLayer().source('https://example.com/image1.jpg', {
+ parser: {
+ type: 'image',
+ extent: [121.0, 30.0, 122.0, 31.0],
+ },
+});
+
+scene.addLayer(imageLayer);
+
+// 切换图片
+function updateImage(newUrl) {
+ imageLayer.setData(newUrl, {
+ parser: {
+ type: 'image',
+ extent: [121.0, 30.0, 122.0, 31.0],
+ },
+ });
+}
+
+// 使用示例
+updateImage('https://example.com/image2.jpg');
+```
+
+### 带交互的图片图层
+
+```javascript
+const imageLayer = new ImageLayer()
+ .source('https://example.com/floor-plan.png', {
+ parser: {
+ type: 'image',
+ extent: [121.4737, 31.2304, 121.4837, 31.2404],
+ },
+ })
+ .style({
+ opacity: 0.8,
+ });
+
+// 点击事件
+imageLayer.on('click', (e) => {
+ console.log('点击位置:', e.lngLat);
+
+ // 显示信息弹窗
+ const popup = new L7.Popup({
+ offsets: [0, 0],
+ closeButton: false,
+ })
+ .setLnglat(e.lngLat)
+ .setHTML(`坐标: ${e.lngLat.lng.toFixed(4)}, ${e.lngLat.lat.toFixed(4)}
`)
+ .addTo(scene);
+});
+
+// 鼠标悬停改变透明度
+imageLayer.on('mousemove', () => {
+ imageLayer.style({ opacity: 1.0 });
+ scene.render();
+});
+
+imageLayer.on('mouseout', () => {
+ imageLayer.style({ opacity: 0.8 });
+ scene.render();
+});
+
+scene.addLayer(imageLayer);
+```
+
+## 样式配置详解
+
+### 基础样式
+
+```javascript
+{
+ opacity: 1.0, // 透明度,0-1,默认 1.0
+ clampLow: true, // 是否裁剪低于最小值的部分
+ clampHigh: true, // 是否裁剪高于最大值的部分
+ domain: [0, 1], // 数据值域范围
+ rampColors: { // 颜色映射(用于灰度图)
+ colors: [...],
+ positions: [...]
+ }
+}
+```
+
+## 常见场景
+
+### 1. 卫星影像叠加
+
+```javascript
+// 叠加高清卫星图
+const satelliteLayer = new ImageLayer()
+ .source('https://api.example.com/satellite/tile.jpg', {
+ parser: {
+ type: 'image',
+ extent: [116.3, 39.9, 116.5, 40.1], // 北京区域
+ },
+ })
+ .style({
+ opacity: 0.8,
+ });
+
+scene.addLayer(satelliteLayer);
+
+// 添加滑动条控制透明度
+const slider = document.getElementById('opacity-slider');
+slider.addEventListener('input', (e) => {
+ const opacity = parseFloat(e.target.value);
+ satelliteLayer.style({ opacity });
+ scene.render();
+});
+```
+
+### 2. 历史地图对比
+
+```javascript
+let showHistorical = false;
+
+// 当前地图(默认显示)
+const currentMap = new ImageLayer({ name: 'current' })
+ .source('https://example.com/current-map.jpg', {
+ parser: {
+ type: 'image',
+ extent: [121.0, 30.0, 122.0, 31.0],
+ },
+ })
+ .style({ opacity: 1.0 });
+
+// 历史地图(初始隐藏)
+const historicalMap = new ImageLayer({ name: 'historical' })
+ .source('https://example.com/historical-map.jpg', {
+ parser: {
+ type: 'image',
+ extent: [121.0, 30.0, 122.0, 31.0],
+ },
+ })
+ .style({ opacity: 0.0 });
+
+scene.addLayer(currentMap);
+scene.addLayer(historicalMap);
+
+// 切换按钮
+document.getElementById('toggle-btn').addEventListener('click', () => {
+ showHistorical = !showHistorical;
+
+ currentMap.style({ opacity: showHistorical ? 0 : 1 });
+ historicalMap.style({ opacity: showHistorical ? 1 : 0 });
+ scene.render();
+});
+```
+
+### 3. 建筑平面图
+
+```javascript
+// 室内平面图
+const floorPlan = new ImageLayer()
+ .source('https://example.com/floor-1.png', {
+ parser: {
+ type: 'image',
+ coordinates: [
+ [121.4737, 31.2404], // 左上
+ [121.4837, 31.2404], // 右上
+ [121.4837, 31.2304], // 右下
+ [121.4737, 31.2304], // 左下
+ ],
+ },
+ })
+ .style({
+ opacity: 0.9,
+ });
+
+scene.addLayer(floorPlan);
+
+// 楼层切换
+const floors = {
+ '1F': 'https://example.com/floor-1.png',
+ '2F': 'https://example.com/floor-2.png',
+ '3F': 'https://example.com/floor-3.png',
+};
+
+function switchFloor(floorName) {
+ floorPlan.setData(floors[floorName], {
+ parser: {
+ type: 'image',
+ coordinates: [
+ [121.4737, 31.2404],
+ [121.4837, 31.2404],
+ [121.4837, 31.2304],
+ [121.4737, 31.2304],
+ ],
+ },
+ });
+}
+```
+
+### 4. 雷达气象图
+
+```javascript
+// 实时更新的雷达图
+class WeatherRadar {
+ constructor(scene) {
+ this.scene = scene;
+ this.layer = null;
+ this.updateInterval = null;
+ }
+
+ start() {
+ this.layer = new ImageLayer()
+ .source('', {
+ parser: {
+ type: 'image',
+ extent: [115.0, 28.0, 125.0, 38.0], // 覆盖区域
+ },
+ })
+ .style({
+ opacity: 0.7,
+ });
+
+ this.scene.addLayer(this.layer);
+ this.update();
+
+ // 每5分钟更新一次
+ this.updateInterval = setInterval(
+ () => {
+ this.update();
+ },
+ 5 * 60 * 1000,
+ );
+ }
+
+ async update() {
+ try {
+ const response = await fetch('/api/weather/radar/latest');
+ const data = await response.json();
+
+ this.layer.setData(data.imageUrl, {
+ parser: {
+ type: 'image',
+ extent: [115.0, 28.0, 125.0, 38.0],
+ },
+ });
+ } catch (error) {
+ console.error('更新雷达图失败:', error);
+ }
+ }
+
+ stop() {
+ if (this.updateInterval) {
+ clearInterval(this.updateInterval);
+ }
+ if (this.layer) {
+ this.scene.removeLayer(this.layer);
+ }
+ }
+}
+
+// 使用
+const radar = new WeatherRadar(scene);
+radar.start();
+```
+
+## 性能优化
+
+### 1. 图片尺寸优化
+
+```javascript
+// 使用合适分辨率的图片
+// 根据显示区域大小选择图片
+const mapWidth = scene.getSize().width;
+const mapHeight = scene.getSize().height;
+
+// 推荐图片分辨率不超过显示区域的 2 倍
+const recommendedWidth = mapWidth * 2;
+const recommendedHeight = mapHeight * 2;
+
+// 使用 CDN 图片服务调整尺寸
+const imageUrl = `https://cdn.example.com/image.jpg?w=${recommendedWidth}&h=${recommendedHeight}`;
+```
+
+### 2. 懒加载
+
+```javascript
+// 只在需要时加载图片图层
+let imageLayer = null;
+
+function showImageLayer() {
+ if (!imageLayer) {
+ imageLayer = new ImageLayer().source('https://example.com/large-image.jpg', {
+ parser: {
+ type: 'image',
+ extent: [121.0, 30.0, 122.0, 31.0],
+ },
+ });
+
+ scene.addLayer(imageLayer);
+ } else {
+ imageLayer.show();
+ }
+}
+
+function hideImageLayer() {
+ if (imageLayer) {
+ imageLayer.hide();
+ }
+}
+```
+
+### 3. 使用 WebP 格式
+
+```javascript
+// WebP 格式可减少 25-35% 的文件大小
+const imageLayer = new ImageLayer().source('https://example.com/image.webp', {
+ parser: {
+ type: 'image',
+ extent: [121.0, 30.0, 122.0, 31.0],
+ },
+});
+```
+
+## 注意事项
+
+⚠️ **坐标顺序**:extent 格式为 `[minLng, minLat, maxLng, maxLat]`(西南东北)
+
+⚠️ **图片大小**:建议单张图片不超过 5MB,过大会影响加载速度
+
+⚠️ **跨域问题**:确保图片服务器配置了正确的 CORS 头
+
+⚠️ **坐标精度**:确保图片边界坐标准确,否则会出现偏移或变形
+
+⚠️ **透明度**:PNG 格式支持透明度,JPG 不支持
+
+⚠️ **更新性能**:频繁更新图片会影响性能,建议控制更新频率
+
+## 常见问题
+
+### Q: 图片无法显示?
+
+A: 检查:1) 图片 URL 是否可访问;2) CORS 配置;3) extent 坐标是否正确;4) 图片格式是否支持
+
+### Q: 图片位置偏移?
+
+A: 检查 extent 或 coordinates 的坐标是否准确,注意经纬度顺序
+
+### Q: 图片模糊?
+
+A: 使用更高分辨率的图片,或使用原始尺寸而非缩放后的图片
+
+### Q: 图片加载慢?
+
+A: 1) 压缩图片大小;2) 使用 CDN;3) 使用 WebP 格式;4) 预加载图片
+
+### Q: 如何实现图片淡入效果?
+
+A: 创建图层时设置 opacity: 0,然后逐渐增加到 1
+
+```javascript
+const layer = new ImageLayer().style({ opacity: 0 });
+scene.addLayer(layer);
+
+let opacity = 0;
+const fadeIn = setInterval(() => {
+ opacity += 0.05;
+ if (opacity >= 1) {
+ opacity = 1;
+ clearInterval(fadeIn);
+ }
+ layer.style({ opacity });
+ scene.render();
+}, 50);
+```
+
+## 相关技能
+
+- [图层通用方法和事件](./layer-common-api.md)
+- [场景初始化](../core/scene.md)
+- [栅格图层](raster.md)
+- [瓦片图层](raster.md)
+- [事件处理](../interaction/events.md)
+
+## 在线示例
+
+查看更多示例: [L7 官方示例 - 图片图层](https://l7.antv.antgroup.com/examples/raster/image)
diff --git a/skills/l7/references/layers/line.md b/skills/l7/references/layers/line.md
new file mode 100644
index 0000000..ee7660a
--- /dev/null
+++ b/skills/l7/references/layers/line.md
@@ -0,0 +1,582 @@
+---
+skill_id: line-layer
+skill_name: 线图层
+category: layers
+difficulty: beginner
+tags: [line, path, arc, route, trajectory]
+dependencies: [scene-initialization]
+version: 2.x
+---
+
+# 线图层
+
+## 技能描述
+
+在地图上绘制线状地理要素,支持路径线、弧线、3D 弧线等多种形式。
+
+## 何时使用
+
+- ✅ 显示道路、河流等线性要素
+- ✅ 显示轨迹路径(车辆、人员移动)
+- ✅ 显示 OD 流向(人口迁徙、物流)
+- ✅ 显示航线、航路
+- ✅ 显示行政区划边界
+- ✅ 显示等值线
+
+## 前置条件
+
+- 已完成[场景初始化](../core/scene.md)
+- 准备好线段数据
+
+## 线类型
+
+| 类型 | 说明 | 适用场景 |
+| ------------- | -------- | ---------------- |
+| `line` | 基础直线 | 道路、河流、边界 |
+| `arc` | 2D 弧线 | 短距离流向 |
+| `arc3d` | 3D 弧线 | 长距离流向、航线 |
+| `greatcircle` | 大圆航线 | 跨越半球的航线 |
+| `wall` | 墙/幕墙 | 3D 围栏效果 |
+
+## 通用方法
+
+线图层继承了所有图层的通用能力,以下是最常用的方法:
+
+### 显示控制
+
+```javascript
+// 显示/隐藏图层
+lineLayer.show();
+lineLayer.hide();
+
+// 设置图层绘制顺序
+lineLayer.setIndex(5);
+
+// 适配到数据范围
+lineLayer.fitBounds();
+```
+
+### 事件监听
+
+```javascript
+// 点击线段
+lineLayer.on('click', (e) => {
+ console.log('线段数据:', e.feature);
+});
+
+// 鼠标悬停高亮
+lineLayer.on('mousemove', (e) => {
+ lineLayer.setActive(e.feature.id);
+});
+```
+
+### 数据过滤
+
+```javascript
+// 只显示特定类型的线
+lineLayer.filter((feature) => {
+ return ['高速公路', '国道'].includes(feature.type);
+});
+```
+
+> 📖 **完整文档**:查看 [图层通用方法和事件](./layer-common-api.md) 了解所有通用 API。
+
+---
+
+## 代码示例
+
+### 基础用法 - 路径线
+
+```javascript
+import { LineLayer } from '@antv/l7';
+
+const data = [
+ {
+ coordinates: [
+ [120.19, 30.26],
+ [120.2, 30.27],
+ [120.21, 30.28],
+ ],
+ name: '路线1',
+ type: 'A',
+ },
+];
+
+scene.on('loaded', () => {
+ const lineLayer = new LineLayer()
+ .source(data, {
+ parser: {
+ type: 'json',
+ coordinates: 'coordinates',
+ },
+ })
+ .shape('line')
+ .size(3)
+ .color('#5B8FF9')
+ .style({
+ opacity: 0.8,
+ });
+
+ scene.addLayer(lineLayer);
+});
+```
+
+### 多条线段
+
+```javascript
+const lines = [
+ {
+ coordinates: [
+ [120.19, 30.26],
+ [120.2, 30.27],
+ ],
+ name: '线路1',
+ value: 100,
+ },
+ {
+ coordinates: [
+ [120.21, 30.28],
+ [120.22, 30.29],
+ ],
+ name: '线路2',
+ value: 200,
+ },
+];
+
+const lineLayer = new LineLayer()
+ .source(lines, {
+ parser: {
+ type: 'json',
+ coordinates: 'coordinates',
+ },
+ })
+ .shape('line')
+ .size('value', [2, 10]) // 根据数值映射宽度
+ .color('name', ['#5B8FF9', '#5AD8A6'])
+ .style({
+ opacity: 0.8,
+ });
+
+scene.addLayer(lineLayer);
+```
+
+### 虚线样式
+
+```javascript
+const lineLayer = new LineLayer()
+ .source(data, {
+ parser: {
+ type: 'json',
+ coordinates: 'coordinates',
+ },
+ })
+ .shape('line')
+ .size(2)
+ .color('#5B8FF9')
+ .style({
+ lineType: 'dash', // 虚线类型: solid | dash
+ dashArray: [5, 5], // 虚线间隔
+ opacity: 0.8,
+ });
+
+scene.addLayer(lineLayer);
+```
+
+### 2D 弧线 - OD 流向
+
+```javascript
+const odData = [
+ {
+ from_lng: 120.19,
+ from_lat: 30.26,
+ to_lng: 121.47,
+ to_lat: 31.23,
+ value: 100,
+ },
+];
+
+const arcLayer = new LineLayer()
+ .source(odData, {
+ parser: {
+ type: 'json',
+ x: 'from_lng',
+ y: 'from_lat',
+ x1: 'to_lng',
+ y1: 'to_lat',
+ },
+ })
+ .shape('arc')
+ .size('value', [1, 5])
+ .color('value', ['#5B8FF9', '#5AD8A6', '#FF6B3B'])
+ .style({
+ opacity: 0.8,
+ });
+
+scene.addLayer(arcLayer);
+```
+
+### 3D 弧线 - 城市迁徙
+
+```javascript
+const migrationLayer = new LineLayer()
+ .source(migrationData, {
+ parser: {
+ type: 'json',
+ x: 'from_lng',
+ y: 'from_lat',
+ x1: 'to_lng',
+ y1: 'to_lat',
+ },
+ })
+ .shape('arc3d')
+ .size('count', [1, 5])
+ .color('count', ['#5B8FF9', '#5AD8A6', '#FF6B3B', '#CF1D49'])
+ .style({
+ opacity: 0.8,
+ sourceColor: '#5B8FF9', // 起点颜色
+ targetColor: '#CF1D49', // 终点颜色
+ });
+
+scene.addLayer(migrationLayer);
+```
+
+### 大圆航线
+
+```javascript
+const flightLayer = new LineLayer()
+ .source(flightData, {
+ parser: {
+ type: 'json',
+ x: 'from_lng',
+ y: 'from_lat',
+ x1: 'to_lng',
+ y1: 'to_lat',
+ },
+ })
+ .shape('greatcircle') // 大圆航线,地球表面最短路径
+ .size(2)
+ .color('#6495ED')
+ .style({
+ opacity: 0.6,
+ });
+
+scene.addLayer(flightLayer);
+```
+
+### 带动画的轨迹
+
+```javascript
+const trajectoryLayer = new LineLayer()
+ .source(pathData, {
+ parser: {
+ type: 'json',
+ coordinates: 'path',
+ },
+ })
+ .shape('line')
+ .size(3)
+ .color('#6495ED')
+ .animate({
+ enable: true,
+ interval: 0.2, // 动画间隔
+ duration: 5, // 动画持续时间
+ trailLength: 0.2, // 轨迹长度比例
+ })
+ .style({
+ opacity: 0.8,
+ });
+
+scene.addLayer(trajectoryLayer);
+```
+
+### 渐变色线条
+
+```javascript
+const gradientLineLayer = new LineLayer()
+ .source(data, {
+ parser: {
+ type: 'json',
+ coordinates: 'coordinates',
+ },
+ })
+ .shape('line')
+ .size(4)
+ .color('#5B8FF9')
+ .style({
+ opacity: 0.8,
+ lineGradient: true, // 开启渐变
+ sourceColor: '#5B8FF9', // 起始颜色
+ targetColor: '#CF1D49', // 结束颜色
+ });
+
+scene.addLayer(gradientLineLayer);
+```
+
+### 3D 墙效果
+
+```javascript
+const wallLayer = new LineLayer()
+ .source(data, {
+ parser: {
+ type: 'json',
+ coordinates: 'coordinates',
+ },
+ })
+ .shape('wall')
+ .size('height', [10, 100]) // 墙的高度
+ .color('#5B8FF9')
+ .style({
+ opacity: 0.6,
+ });
+
+scene.addLayer(wallLayer);
+```
+
+## 数据格式要求
+
+### 路径线数据格式
+
+```json
+[
+ {
+ "coordinates": [
+ [120.19, 30.26],
+ [120.2, 30.27],
+ [120.21, 30.28]
+ ],
+ "name": "路线1",
+ "type": "A",
+ "value": 100
+ }
+]
+```
+
+### OD 数据格式
+
+```json
+[
+ {
+ "from_lng": 120.19,
+ "from_lat": 30.26,
+ "to_lng": 121.47,
+ "to_lat": 31.23,
+ "value": 100,
+ "category": "A"
+ }
+]
+```
+
+### GeoJSON 线数据
+
+```json
+{
+ "type": "FeatureCollection",
+ "features": [
+ {
+ "type": "Feature",
+ "properties": {
+ "name": "线路1",
+ "type": "A"
+ },
+ "geometry": {
+ "type": "LineString",
+ "coordinates": [
+ [120.19, 30.26],
+ [120.2, 30.27],
+ [120.21, 30.28]
+ ]
+ }
+ }
+ ]
+}
+```
+
+使用 GeoJSON:
+
+```javascript
+lineLayer.source(geojsonData, {
+ parser: {
+ type: 'geojson',
+ },
+});
+```
+
+## 样式配置详解
+
+### 基础样式
+
+```javascript
+layer.style({
+ opacity: 0.8, // 透明度
+ lineType: 'solid', // 线类型: solid | dash
+ dashArray: [5, 5], // 虚线配置 [实线长度, 间隔长度]
+});
+```
+
+### 渐变样式
+
+```javascript
+layer.style({
+ lineGradient: true, // 开启渐变
+ sourceColor: '#5B8FF9', // 起点颜色
+ targetColor: '#CF1D49', // 终点颜色
+});
+```
+
+### 动画配置
+
+```javascript
+layer.animate({
+ enable: true, // 开启动画
+ interval: 0.2, // 动画间隔,数值越小速度越快
+ duration: 5, // 动画持续时间(秒)
+ trailLength: 0.2, // 轨迹长度比例 0-1
+});
+```
+
+## 常见问题
+
+### 1. 线不显示
+
+**检查清单**:
+
+- ✅ 坐标数据是否正确(至少 2 个点)
+- ✅ 线的宽度是否太小
+- ✅ 颜色是否与背景相同
+- ✅ 坐标是否在地图视野内
+
+```javascript
+// 调试代码
+console.log('数据:', data);
+layer.size(10); // 加粗线条
+layer.color('#FF0000'); // 使用明显颜色
+```
+
+### 2. 弧线方向错误
+
+OD 数据的起点和终点要明确:
+
+```javascript
+// 正确的配置
+.source(data, {
+ parser: {
+ type: 'json',
+ x: 'from_lng', // 起点经度
+ y: 'from_lat', // 起点纬度
+ x1: 'to_lng', // 终点经度
+ y1: 'to_lat' // 终点纬度
+ }
+})
+```
+
+### 3. 动画不流畅
+
+**优化方案**:
+
+```javascript
+// 1. 调整动画参数
+layer.animate({
+ enable: true,
+ interval: 0.1, // 减小间隔
+ duration: 3, // 缩短持续时间
+ trailLength: 0.1, // 缩短轨迹长度
+});
+
+// 2. 减少数据量
+const simplifiedData = data.filter((d, i) => i % 5 === 0);
+```
+
+### 4. 3D 弧线看不到
+
+需要设置地图倾斜角度:
+
+```javascript
+const scene = new Scene({
+ id: 'map',
+ map: new GaodeMap({
+ pitch: 45, // 设置倾斜角度
+ style: 'dark',
+ center: [120, 30],
+ zoom: 5,
+ }),
+});
+```
+
+## 高级用法
+
+### 多图层组合 - 线 + 端点
+
+```javascript
+// 1. 绘制线
+const lineLayer = new LineLayer()
+ .source(odData, {
+ parser: {
+ type: 'json',
+ x: 'from_lng',
+ y: 'from_lat',
+ x1: 'to_lng',
+ y1: 'to_lat',
+ },
+ })
+ .shape('arc')
+ .size(2)
+ .color('#6495ED');
+
+// 2. 绘制起点
+const startPoints = odData.map((d) => ({
+ lng: d.from_lng,
+ lat: d.from_lat,
+}));
+
+const startLayer = new PointLayer()
+ .source(startPoints, {
+ parser: { type: 'json', x: 'lng', y: 'lat' },
+ })
+ .shape('circle')
+ .size(5)
+ .color('#00FF00');
+
+// 3. 绘制终点
+const endPoints = odData.map((d) => ({
+ lng: d.to_lng,
+ lat: d.to_lat,
+}));
+
+const endLayer = new PointLayer()
+ .source(endPoints, {
+ parser: { type: 'json', x: 'lng', y: 'lat' },
+ })
+ .shape('circle')
+ .size(5)
+ .color('#FF0000');
+
+scene.addLayer(lineLayer);
+scene.addLayer(startLayer);
+scene.addLayer(endLayer);
+```
+
+### 动态更新线数据
+
+```javascript
+// 更新数据
+const newData = [...];
+layer.setData(newData);
+
+// 更新样式
+layer.size(5);
+layer.color('#FF0000');
+scene.render();
+```
+
+## 相关技能
+
+- [图层通用方法和事件](./layer-common-api.md)
+- [场景初始化](../core/scene.md)
+- [点图层](./point.md)
+- [轨迹动画](../animation/layer-animation.md)
+- [颜色映射](../visual/mapping.md)
+- [事件交互](../interaction/events.md)
+
+## 在线示例
+
+查看更多示例: [L7 官方示例 - 线图层](https://l7.antv.antgroup.com/examples/line/path)
diff --git a/skills/l7/references/layers/point.md b/skills/l7/references/layers/point.md
new file mode 100644
index 0000000..fa4b532
--- /dev/null
+++ b/skills/l7/references/layers/point.md
@@ -0,0 +1,530 @@
+---
+skill_id: point-layer
+skill_name: 点图层
+category: layers
+difficulty: beginner
+tags: [point, layer, scatter, bubble, visualization]
+dependencies: [scene-initialization, source-geojson]
+version: 2.x
+---
+
+# 点图层
+
+## 技能描述
+
+在地图上绘制点状地理要素,支持气泡图、散点图、符号地图等多种形式。
+
+## 何时使用
+
+- ✅ 显示 POI 位置(餐厅、商店、景点等)
+- ✅ 显示事件发生点(地震、案件、事故)
+- ✅ 显示采样点位置(气象站、监测点)
+- ✅ 散点图可视化(人口分布、经济指标)
+- ✅ 3D 柱状图(城市数据对比)
+
+## 前置条件
+
+- 已完成[场景初始化](../core/scene.md)
+- 准备好点位数据(包含经纬度)
+
+## 输入参数
+
+### 数据格式
+
+```typescript
+interface PointData {
+ lng: number; // 经度
+ lat: number; // 纬度
+ [key: string]: any; // 其他属性
+}
+```
+
+### 图层配置
+
+| 方法 | 参数 | 说明 |
+| ---------------------- | --------------------- | ------------------------------------------------------- |
+| `source(data, config)` | data: 数据数组 | 设置数据源 |
+| `shape(type)` | type: 形状类型 | circle \| square \| hexagon \| triangle \| text \| icon |
+| `size(value)` | value: 数字或字段映射 | 设置点大小 |
+| `color(value)` | value: 颜色或字段映射 | 设置点颜色 |
+| `style(config)` | config: 样式对象 | 设置样式 |
+
+## 输出
+
+返回 `PointLayer` 实例
+
+## 通用方法
+
+点图层继承了所有图层的通用能力,以下是最常用的方法:
+
+### 显示控制
+
+```javascript
+// 显示/隐藏图层
+pointLayer.show();
+pointLayer.hide();
+
+// 检查可见性
+if (pointLayer.isVisible()) {
+ console.log('图层可见');
+}
+
+// 设置图层绘制顺序(数值越大越在上层)
+pointLayer.setIndex(10);
+```
+
+### 事件监听
+
+```javascript
+// 点击事件
+pointLayer.on('click', (e) => {
+ console.log('点击的点:', e.feature);
+ console.log('经纬度:', e.lngLat);
+});
+
+// 鼠标悬停
+pointLayer.on('mousemove', (e) => {
+ // 显示 tooltip
+});
+
+// 鼠标移出
+pointLayer.on('mouseout', () => {
+ // 隐藏 tooltip
+});
+```
+
+### 数据更新
+
+```javascript
+// 数据过滤
+pointLayer.filter((feature) => {
+ return feature.value > 100;
+});
+
+// 适配到数据范围
+pointLayer.fitBounds();
+
+// 设置缩放范围
+pointLayer.setMinZoom(10); // zoom < 10 时不显示
+pointLayer.setMaxZoom(18); // zoom > 18 时不显示
+```
+
+> 📖 **完整文档**:查看 [图层通用方法和事件](./layer-common-api.md) 了解所有通用 API,包括 source、scale、所有事件类型、聚合方法等。
+
+---
+
+## 代码示例
+
+### 基础用法 - 简单散点图
+
+```javascript
+import { PointLayer } from '@antv/l7';
+
+const data = [
+ { lng: 120.19, lat: 30.26, name: '点位1', value: 100 },
+ { lng: 120.2, lat: 30.27, name: '点位2', value: 200 },
+ { lng: 120.21, lat: 30.28, name: '点位3', value: 300 },
+];
+
+scene.on('loaded', () => {
+ const pointLayer = new PointLayer()
+ .source(data, {
+ parser: {
+ type: 'json',
+ x: 'lng',
+ y: 'lat',
+ },
+ })
+ .shape('circle')
+ .size(10)
+ .color('#5B8FF9')
+ .style({
+ opacity: 0.8,
+ });
+
+ scene.addLayer(pointLayer);
+});
+```
+
+### 数据驱动 - 颜色和大小映射
+
+```javascript
+const pointLayer = new PointLayer()
+ .source(data, {
+ parser: {
+ type: 'json',
+ x: 'lng',
+ y: 'lat',
+ },
+ })
+ .shape('circle')
+ .size('value', [5, 20]) // 根据 value 字段映射大小
+ .color('category', {
+ // 根据 category 字段映射颜色
+ A: '#5B8FF9',
+ B: '#5AD8A6',
+ C: '#5D7092',
+ })
+ .style({
+ opacity: 0.8,
+ strokeWidth: 2,
+ stroke: '#fff',
+ });
+
+scene.addLayer(pointLayer);
+```
+
+### 气泡图 - 面积映射
+
+```javascript
+const bubbleLayer = new PointLayer()
+ .source(data, {
+ parser: {
+ type: 'json',
+ x: 'lng',
+ y: 'lat',
+ },
+ })
+ .shape('circle')
+ .size('population', (value) => Math.sqrt(value)) // 使用回调函数
+ .color('gdp', ['#FFF5B8', '#FFAB5C', '#FF6B3B', '#CC2B12'])
+ .style({
+ opacity: 0.6,
+ strokeWidth: 1,
+ stroke: '#fff',
+ });
+
+scene.addLayer(bubbleLayer);
+```
+
+### 不同形状的点
+
+```javascript
+// 圆形
+const circleLayer = new PointLayer().source(data).shape('circle').size(10).color('#5B8FF9');
+
+// 方形
+const squareLayer = new PointLayer().source(data).shape('square').size(10).color('#5AD8A6');
+
+// 三角形
+const triangleLayer = new PointLayer().source(data).shape('triangle').size(10).color('#5D7092');
+
+// 六边形
+const hexagonLayer = new PointLayer().source(data).shape('hexagon').size(10).color('#FF6B3B');
+```
+
+### 文本标注
+
+```javascript
+const textLayer = new PointLayer()
+ .source(data, {
+ parser: {
+ type: 'json',
+ x: 'lng',
+ y: 'lat',
+ },
+ })
+ .shape('name', 'text') // 使用 name 字段作为文本
+ .size(14)
+ .color('#000')
+ .style({
+ textAnchor: 'center', // 文本对齐方式
+ textOffset: [0, 20], // 文本偏移
+ fontWeight: 'bold',
+ stroke: '#fff',
+ strokeWidth: 2,
+ });
+
+scene.addLayer(textLayer);
+```
+
+### 图标点
+
+```javascript
+const iconLayer = new PointLayer()
+ .source(data, {
+ parser: {
+ type: 'json',
+ x: 'lng',
+ y: 'lat',
+ },
+ })
+ .shape('icon', 'image')
+ .size(20)
+ .style({
+ icon: 'https://example.com/icon.png',
+ });
+
+scene.addLayer(iconLayer);
+```
+
+### 3D 柱状图
+
+```javascript
+const columnLayer = new PointLayer()
+ .source(data, {
+ parser: {
+ type: 'json',
+ x: 'lng',
+ y: 'lat',
+ },
+ })
+ .shape('cylinder') // 3D 柱状
+ .size('value', [10, 100]) // 映射高度
+ .color('type', ['#5B8FF9', '#5AD8A6', '#5D7092'])
+ .style({
+ opacity: 0.8,
+ });
+
+scene.addLayer(columnLayer);
+```
+
+### 组合使用 - 点 + 文本
+
+```javascript
+// 底层点
+const pointLayer = new PointLayer()
+ .source(data, {
+ parser: { type: 'json', x: 'lng', y: 'lat' },
+ })
+ .shape('circle')
+ .size(15)
+ .color('#5B8FF9')
+ .style({
+ opacity: 0.8,
+ strokeWidth: 2,
+ stroke: '#fff',
+ });
+
+// 顶层文本
+const textLayer = new PointLayer()
+ .source(data, {
+ parser: { type: 'json', x: 'lng', y: 'lat' },
+ })
+ .shape('name', 'text')
+ .size(12)
+ .color('#fff')
+ .style({
+ textAnchor: 'center',
+ textOffset: [0, 0],
+ });
+
+scene.addLayer(pointLayer);
+scene.addLayer(textLayer);
+```
+
+## 样式配置详解
+
+### 基础样式
+
+```javascript
+layer.style({
+ opacity: 0.8, // 透明度 0-1
+ strokeWidth: 2, // 描边宽度
+ stroke: '#fff', // 描边颜色
+ strokeOpacity: 1, // 描边透明度
+});
+```
+
+### 文本样式
+
+```javascript
+layer.style({
+ textAnchor: 'center', // 对齐: center | left | right | top | bottom
+ textOffset: [0, 20], // 偏移 [x, y]
+ spacing: 2, // 字符间距
+ padding: [5, 5], // 内边距
+ fontFamily: 'sans-serif',
+ fontSize: 12,
+ fontWeight: 'normal', // normal | bold
+ textAllowOverlap: true, // 是否允许文本重叠
+ stroke: '#fff', // 文本描边
+ strokeWidth: 2,
+ strokeOpacity: 1,
+});
+```
+
+## 数据格式要求
+
+### GeoJSON 格式
+
+```json
+{
+ "type": "FeatureCollection",
+ "features": [
+ {
+ "type": "Feature",
+ "properties": {
+ "name": "点位1",
+ "value": 100,
+ "category": "A"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [120.19, 30.26]
+ }
+ }
+ ]
+}
+```
+
+使用 GeoJSON:
+
+```javascript
+layer.source(geojsonData, {
+ parser: {
+ type: 'geojson',
+ },
+});
+```
+
+### JSON 格式
+
+```json
+[
+ {
+ "lng": 120.19,
+ "lat": 30.26,
+ "name": "点位1",
+ "value": 100,
+ "category": "A"
+ }
+]
+```
+
+### CSV 格式
+
+```csv
+lng,lat,name,value,category
+120.19,30.26,点位1,100,A
+120.20,30.27,点位2,200,B
+120.21,30.28,点位3,300,C
+```
+
+使用 CSV:
+
+```javascript
+layer.source(csvData, {
+ parser: {
+ type: 'csv',
+ x: 'lng',
+ y: 'lat',
+ },
+});
+```
+
+## 常见问题
+
+### 1. 点不显示
+
+**原因分析**:
+
+- 坐标不在地图视野范围内
+- 点太小看不见
+- 颜色与背景相同
+- 数据格式错误
+
+**解决方案**:
+
+```javascript
+// 1. 检查数据坐标
+console.log(data);
+
+// 2. 增大点的大小
+layer.size(20);
+
+// 3. 使用明显的颜色
+layer.color('#FF0000');
+
+// 4. 检查地图中心和缩放级别
+scene.setCenter([120.19, 30.26]);
+scene.setZoom(12);
+
+// 5. 使用 fitBounds 自动调整视野
+const bounds = [
+ [minLng, minLat],
+ [maxLng, maxLat],
+];
+scene.fitBounds(bounds);
+```
+
+### 2. 大数据量性能问题
+
+**优化方案**:
+
+```javascript
+// 1. 数据抽稀
+layer.source(data.filter((d, i) => i % 10 === 0));
+
+// 2. 根据缩放级别显示
+layer.setMinZoom(10); // 只在 zoom >= 10 时显示
+
+// 3. 使用聚合
+// 参考: ../performance/optimization.md
+```
+
+### 3. 点的大小不一致
+
+**问题**: 不同缩放级别下点的大小变化
+
+**解决方案**:
+
+```javascript
+// 使用单位配置
+layer.size(10).style({
+ unit: 'meter', // 使用地理单位,点会随缩放变化
+});
+
+// 或使用像素单位(默认)
+layer.size(10).style({
+ unit: 'pixel', // 使用像素单位,点大小固定
+});
+```
+
+## 高级用法
+
+### 动态更新数据
+
+```javascript
+// 更新数据
+const newData = [...];
+layer.setData(newData);
+
+// 只更新样式
+layer.color('#FF0000');
+layer.size(20);
+scene.render();
+```
+
+### 图层控制
+
+```javascript
+// 显示/隐藏
+layer.show();
+layer.hide();
+
+// 销毁图层
+layer.destroy();
+scene.removeLayer(layer);
+
+// 设置层级
+layer.setIndex(10);
+
+// 设置显示范围
+layer.setMinZoom(5);
+layer.setMaxZoom(15);
+```
+
+## 相关技能
+
+- [图层通用方法和事件](./layer-common-api.md)
+- [场景初始化](../core/scene.md)
+- [GeoJSON 数据处理](../data/source-geojson.md)
+- [颜色映射](../visual/mapping.md)
+- [大小映射](../visual/mapping.md)
+- [事件交互](../interaction/events.md)
+- [添加弹窗](../interaction/popup.md)
+
+## 在线示例
+
+查看更多在线示例: [L7 官方示例 - 点图层](https://l7.antv.antgroup.com/examples/point/scatter)
diff --git a/skills/l7/references/layers/polygon.md b/skills/l7/references/layers/polygon.md
new file mode 100644
index 0000000..83db372
--- /dev/null
+++ b/skills/l7/references/layers/polygon.md
@@ -0,0 +1,524 @@
+---
+skill_id: polygon-layer
+skill_name: 面图层
+category: layers
+difficulty: beginner
+tags: [polygon, fill, extrude, 3d, choropleth]
+dependencies: [scene-initialization]
+version: 2.x
+---
+
+# 面图层
+
+## 技能描述
+
+在地图上绘制面状地理要素,支持填充图、3D 挤出、等值图等多种形式。
+
+## 何时使用
+
+- ✅ 显示行政区划(省、市、区)
+- ✅ 显示建筑轮廓(2D/3D)
+- ✅ 显示土地利用分类
+- ✅ 显示等值区域(人口密度、经济指标)
+- ✅ 显示湖泊、公园等区域
+- ✅ 制作热力分区图
+
+## 前置条件
+
+- 已完成[场景初始化](../core/scene.md)
+- 准备好面数据(通常是 GeoJSON 格式)
+
+## 面类型
+
+| 类型 | 说明 | 适用场景 |
+| --------- | -------- | ------------------ |
+| `fill` | 2D 填充 | 行政区划、土地分类 |
+| `extrude` | 3D 挤出 | 建筑、人口柱状图 |
+| `water` | 水面效果 | 湖泊、海洋 |
+| `ocean` | 海洋效果 | 全球海洋 |
+
+## 通用方法
+
+面图层继承了所有图层的通用能力,以下是最常用的方法:
+
+### 显示控制
+
+```javascript
+// 显示/隐藏图层
+polygonLayer.show();
+polygonLayer.hide();
+
+// 设置图层顺序(面图层通常在底层)
+polygonLayer.setIndex(1);
+
+// 缩放到图层范围
+polygonLayer.fitBounds();
+```
+
+### 事件监听
+
+```javascript
+// 点击区域
+polygonLayer.on('click', (e) => {
+ console.log('区域名称:', e.feature.properties.name);
+ console.log('区域数据:', e.feature);
+});
+
+// 鼠标悬停高亮
+polygonLayer.on('mousemove', (e) => {
+ // 高亮当前区域
+});
+
+polygonLayer.on('mouseout', () => {
+ // 取消高亮
+});
+```
+
+### 数据过滤
+
+```javascript
+// 只显示特定省份
+polygonLayer.filter((feature) => {
+ return ['浙江省', '江苏省', '上海市'].includes(feature.name);
+});
+```
+
+> 📖 **完整文档**:查看 [图层通用方法和事件](./layer-common-api.md) 了解所有通用 API。
+
+---
+
+## 代码示例
+
+### 基础用法 - 区域填充
+
+```javascript
+import { PolygonLayer } from '@antv/l7';
+
+scene.on('loaded', () => {
+ fetch('https://gw.alipayobjects.com/os/basement_prod/d2e0e930-fd44-4fca-8872-c1037b0fee7b.json')
+ .then((res) => res.json())
+ .then((data) => {
+ const polygonLayer = new PolygonLayer().source(data).shape('fill').color('#5B8FF9').style({
+ opacity: 0.6,
+ });
+
+ scene.addLayer(polygonLayer);
+ });
+});
+```
+
+### 数据驱动 - 等值图
+
+```javascript
+const provinceLayer = new PolygonLayer()
+ .source(provinceData)
+ .shape('fill')
+ .color('gdp', ['#FFF5B8', '#FFAB5C', '#FF6B3B', '#CC2B12']) // GDP 分级着色
+ .style({
+ opacity: 0.8,
+ });
+
+scene.addLayer(provinceLayer);
+```
+
+### 自定义颜色映射
+
+```javascript
+const landUseLayer = new PolygonLayer()
+ .source(landUseData)
+ .shape('fill')
+ .color('type', {
+ 住宅: '#5B8FF9',
+ 商业: '#5AD8A6',
+ 工业: '#5D7092',
+ 绿地: '#61DDAA',
+ 水域: '#65789B',
+ })
+ .style({
+ opacity: 0.7,
+ });
+
+scene.addLayer(landUseLayer);
+```
+
+### 带描边的填充图
+
+```javascript
+// 填充层
+const fillLayer = new PolygonLayer()
+ .source(data)
+ .shape('fill')
+ .color('value', ['#FFF5B8', '#FFAB5C', '#FF6B3B'])
+ .style({
+ opacity: 0.8,
+ });
+
+// 描边层
+const lineLayer = new LineLayer().source(data).shape('line').size(1).color('#fff').style({
+ opacity: 0.6,
+});
+
+scene.addLayer(fillLayer);
+scene.addLayer(lineLayer);
+```
+
+### 3D 建筑 - 挤出效果
+
+```javascript
+const buildingLayer = new PolygonLayer()
+ .source(buildingData)
+ .shape('extrude')
+ .size('height') // 根据 height 字段设置高度
+ .color('type', {
+ 住宅: '#5B8FF9',
+ 商业: '#5AD8A6',
+ 办公: '#5D7092',
+ })
+ .style({
+ opacity: 0.8,
+ });
+
+scene.addLayer(buildingLayer);
+
+// 确保场景有倾斜角度才能看到 3D 效果
+scene.setPitch(45);
+```
+
+### 3D 人口柱状图
+
+```javascript
+const populationLayer = new PolygonLayer()
+ .source(districtData)
+ .shape('extrude')
+ .size('population', [0, 50000]) // 人口映射到高度
+ .color('population', ['#FFF5B8', '#FFAB5C', '#FF6B3B', '#CC2B12'])
+ .style({
+ opacity: 0.8,
+ });
+
+scene.addLayer(populationLayer);
+```
+
+### 水面效果
+
+```javascript
+const lakeLayer = new PolygonLayer()
+ .source(lakeData)
+ .shape('water')
+ .color('#6495ED')
+ .style({
+ opacity: 0.8,
+ speed: 0.5, // 水波速度
+ })
+ .animate(true);
+
+scene.addLayer(lakeLayer);
+```
+
+### 海洋效果
+
+```javascript
+const oceanLayer = new PolygonLayer()
+ .source(oceanData)
+ .shape('ocean')
+ .color('#284AC9')
+ .style({
+ opacity: 0.8,
+ })
+ .animate(true);
+
+scene.addLayer(oceanLayer);
+```
+
+## 数据格式要求
+
+### GeoJSON 格式(推荐)
+
+```json
+{
+ "type": "FeatureCollection",
+ "features": [
+ {
+ "type": "Feature",
+ "properties": {
+ "name": "浙江省",
+ "adcode": "330000",
+ "gdp": 82553,
+ "population": 6540
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [120.19, 30.26],
+ [120.2, 30.27],
+ [120.21, 30.26],
+ [120.19, 30.26]
+ ]
+ ]
+ }
+ }
+ ]
+}
+```
+
+使用 GeoJSON:
+
+```javascript
+layer.source(geojsonData, {
+ parser: {
+ type: 'geojson',
+ },
+});
+```
+
+### MultiPolygon(多面)
+
+```json
+{
+ "type": "Feature",
+ "properties": {
+ "name": "浙江省(含岛屿)"
+ },
+ "geometry": {
+ "type": "MultiPolygon",
+ "coordinates": [
+ [
+ [
+ [120.19, 30.26],
+ [120.2, 30.27],
+ [120.21, 30.26],
+ [120.19, 30.26]
+ ]
+ ],
+ [
+ [
+ [122.1, 30.0],
+ [122.11, 30.01],
+ [122.12, 30.0],
+ [122.1, 30.0]
+ ]
+ ]
+ ]
+ }
+}
+```
+
+### 3D 建筑数据格式
+
+```json
+{
+ "type": "FeatureCollection",
+ "features": [
+ {
+ "type": "Feature",
+ "properties": {
+ "name": "大厦A",
+ "height": 150,
+ "floors": 30,
+ "type": "商业"
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [...]
+ }
+ }
+ ]
+}
+```
+
+## 样式配置详解
+
+### 2D 填充样式
+
+```javascript
+layer.style({
+ opacity: 0.8, // 透明度
+ stroke: '#fff', // 描边颜色(需要配合 lineLayer)
+ strokeWidth: 1, // 描边宽度
+});
+```
+
+### 3D 挤出样式
+
+```javascript
+layer.style({
+ opacity: 0.8,
+ extrudeBase: 0, // 挤出基准高度
+ pickLight: true, // 是否接受光照
+});
+```
+
+### 水面样式
+
+```javascript
+layer.style({
+ opacity: 0.8,
+ speed: 0.5, // 水波动画速度
+});
+```
+
+## 常见问题
+
+### 1. 面不显示
+
+**检查清单**:
+
+- ✅ GeoJSON 数据格式是否正确
+- ✅ 坐标顺序是否正确(经度在前,纬度在后)
+- ✅ 多边形是否闭合(首尾坐标相同)
+- ✅ 颜色是否与背景相同
+- ✅ opacity 是否为 0
+
+```javascript
+// 调试代码
+console.log('数据:', data);
+layer.color('#FF0000'); // 使用明显颜色
+layer.style({ opacity: 1 });
+```
+
+### 2. 3D 效果看不到
+
+需要设置地图倾斜:
+
+```javascript
+// 创建场景时设置
+const scene = new Scene({
+ id: 'map',
+ map: new GaodeMap({
+ pitch: 45, // 倾斜角度
+ center: [120, 30],
+ zoom: 12,
+ }),
+});
+
+// 或运行时设置
+scene.setPitch(45);
+```
+
+### 3. 数据加载慢
+
+**优化方案**:
+
+```javascript
+// 1. 简化几何形状
+layer.source(data, {
+ parser: {
+ type: 'geojson',
+ },
+ transforms: [
+ {
+ type: 'simplify',
+ tolerance: 0.01, // 简化容差
+ },
+ ],
+});
+
+// 2. 根据缩放级别显示
+layer.setMinZoom(5);
+layer.setMaxZoom(15);
+```
+
+### 4. 描边效果不明显
+
+需要单独创建线图层:
+
+```javascript
+// 填充层
+const fillLayer = new PolygonLayer()
+ .source(data)
+ .shape('fill')
+ .color('#5B8FF9')
+ .style({ opacity: 0.6 });
+
+// 线图层
+const lineLayer = new LineLayer()
+ .source(data)
+ .shape('line')
+ .size(2)
+ .color('#fff')
+ .style({ opacity: 1 });
+
+scene.addLayer(fillLayer);
+scene.addLayer(lineLayer);
+```
+
+## 高级用法
+
+### 分级设色图(Choropleth)
+
+```javascript
+// 配置比例尺
+layer
+ .source(data)
+ .shape('fill')
+ .color('value', ['#FFF5B8', '#FFAB5C', '#FF6B3B', '#CC2B12'])
+ .scale('value', {
+ type: 'quantize', // 分段类型
+ domain: [0, 1000],
+ })
+ .style({
+ opacity: 0.8,
+ });
+
+// 添加图例
+import { Legend } from '@antv/l7';
+
+const legend = new Legend({
+ position: 'bottomright',
+ items: [
+ { value: '0-250', color: '#FFF5B8' },
+ { value: '250-500', color: '#FFAB5C' },
+ { value: '500-750', color: '#FF6B3B' },
+ { value: '750-1000', color: '#CC2B12' },
+ ],
+});
+
+scene.addControl(legend);
+```
+
+### 动态更新数据
+
+```javascript
+// 更新数据
+const newData = {...};
+layer.setData(newData);
+
+// 更新样式
+layer.color('newField', ['#5B8FF9', '#5AD8A6']);
+scene.render();
+```
+
+### 高亮选中区域
+
+```javascript
+layer.on('click', (e) => {
+ // 重置之前的高亮
+ if (layer.selectedFeatureId) {
+ layer.setActive(layer.selectedFeatureId, false);
+ }
+
+ // 高亮当前选中
+ layer.setActive(e.featureId, true);
+ layer.selectedFeatureId = e.featureId;
+});
+
+// 配置高亮样式
+layer.style({
+ selectColor: '#FF0000',
+});
+```
+
+## 相关技能
+
+- [图层通用方法和事件](./layer-common-api.md)
+- [场景初始化](../core/scene.md)
+- [线图层(描边)](./line.md)
+- [颜色映射](../visual/mapping.md)
+- [事件交互](../interaction/events.md)
+- [添加弹窗](../interaction/popup.md)
+- [添加图例](../interaction/components.md)
+
+## 在线示例
+
+查看更多示例: [L7 官方示例 - 面图层](https://l7.antv.antgroup.com/examples/polygon/fill)
diff --git a/skills/l7/references/layers/raster.md b/skills/l7/references/layers/raster.md
new file mode 100644
index 0000000..cd77965
--- /dev/null
+++ b/skills/l7/references/layers/raster.md
@@ -0,0 +1,605 @@
+---
+skill_id: raster-layer
+skill_name: 栅格瓦片图层
+category: layers
+difficulty: beginner
+tags: [raster, layer, tile, xyz, tms, imagery]
+dependencies: [scene-initialization]
+version: 2.x
+---
+
+# 栅格瓦片图层
+
+## 技能描述
+
+加载和显示第三方图片瓦片服务,支持 XYZ、TMS 等标准瓦片协议,可用于叠加卫星影像、地形图、专题图等栅格数据。
+
+## 何时使用
+
+- ✅ 叠加第三方卫星影像(天地图、OpenStreetMap)
+- ✅ 显示专题地图图层(气象云图、热力分析)
+- ✅ 加载自定义瓦片服务(地形渲染、历史地图)
+- ✅ 多源数据叠加(多个瓦片图层组合)
+- ✅ 替换或补充默认底图
+
+## 前置条件
+
+- 已完成[场景初始化](../core/scene.md)
+- 准备好瓦片服务 URL(XYZ 或 TMS 格式)
+- 了解瓦片服务的坐标系和缩放级别范围
+
+## 输入参数
+
+### 瓦片 URL 格式
+
+```typescript
+// XYZ 格式 (最常用)
+'https://tile.server.com/{z}/{x}/{y}.png';
+
+// TMS 格式 (Y轴反转)
+'https://tile.server.com/{z}/{x}/{-y}.png';
+
+// 多子域
+'https://{s}.tile.server.com/{z}/{x}/{y}.png';
+
+// 带参数
+'https://tile.server.com/{z}/{x}/{y}.png?token=YOUR_TOKEN';
+```
+
+### 图层配置
+
+| 方法 | 参数 | 说明 |
+| --------------------- | -------------------------- | -------------- |
+| `source(url, config)` | url: 瓦片URL, config: 配置 | 设置瓦片数据源 |
+| `style(config)` | config: 样式对象 | 设置样式参数 |
+
+### 配置参数
+
+```typescript
+{
+ parser: {
+ type: 'rasterTile',
+ tileSize: 256, // 瓦片大小,默认 256
+ minZoom: 0, // 最小缩放级别
+ maxZoom: 18, // 最大缩放级别
+ zoomOffset: 0, // 缩放偏移量
+ updateStrategy: 'overlap' // 更新策略: overlap | replace
+ }
+}
+```
+
+## 输出
+
+返回 `RasterLayer` 实例
+
+## 代码示例
+
+### 基础用法 - OpenStreetMap 瓦片
+
+```javascript
+import { RasterLayer } from '@antv/l7';
+
+scene.on('loaded', () => {
+ const osmLayer = new RasterLayer()
+ .source('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
+ parser: {
+ type: 'rasterTile',
+ tileSize: 256,
+ minZoom: 0,
+ maxZoom: 18,
+ },
+ })
+ .style({
+ opacity: 1.0,
+ });
+
+ scene.addLayer(osmLayer);
+});
+```
+
+### 天地图影像服务
+
+```javascript
+const TOKEN = 'YOUR_TIANDITU_TOKEN';
+
+const tdtImageLayer = new RasterLayer({
+ name: 'tianditu-imagery',
+ zIndex: 1,
+}).source(`https://t{s}.tianditu.gov.cn/DataServer?T=img_w&x={x}&y={y}&l={z}&tk=${TOKEN}`, {
+ parser: {
+ type: 'rasterTile',
+ tileSize: 256,
+ minZoom: 0,
+ maxZoom: 18,
+ subdomains: ['0', '1', '2', '3', '4', '5', '6', '7'],
+ },
+});
+
+// 叠加天地图注记
+const tdtLabelLayer = new RasterLayer({
+ name: 'tianditu-labels',
+ zIndex: 2,
+}).source(`https://t{s}.tianditu.gov.cn/DataServer?T=cia_w&x={x}&y={y}&l={z}&tk=${TOKEN}`, {
+ parser: {
+ type: 'rasterTile',
+ tileSize: 256,
+ minZoom: 0,
+ maxZoom: 18,
+ subdomains: ['0', '1', '2', '3', '4', '5', '6', '7'],
+ },
+});
+
+scene.addLayer(tdtImageLayer);
+scene.addLayer(tdtLabelLayer);
+```
+
+### Mapbox 卫星图层
+
+```javascript
+const MAPBOX_TOKEN = 'YOUR_MAPBOX_TOKEN';
+
+const satelliteLayer = new RasterLayer()
+ .source(
+ `https://api.mapbox.com/v4/mapbox.satellite/{z}/{x}/{y}.png?access_token=${MAPBOX_TOKEN}`,
+ {
+ parser: {
+ type: 'rasterTile',
+ tileSize: 256,
+ minZoom: 0,
+ maxZoom: 22,
+ },
+ },
+ )
+ .style({
+ opacity: 0.8,
+ });
+
+scene.addLayer(satelliteLayer);
+```
+
+### TMS 格式瓦片
+
+```javascript
+// TMS 格式的 Y 轴是反转的
+const tmsLayer = new RasterLayer().source('https://tile.server.com/{z}/{x}/{-y}.png', {
+ parser: {
+ type: 'rasterTile',
+ tileSize: 256,
+ minZoom: 0,
+ maxZoom: 16,
+ },
+});
+
+scene.addLayer(tmsLayer);
+```
+
+### 自定义瓦片服务
+
+```javascript
+const customTileLayer = new RasterLayer({
+ name: 'custom-tiles',
+})
+ .source('https://your-tile-server.com/tiles/{z}/{x}/{y}.png', {
+ parser: {
+ type: 'rasterTile',
+ tileSize: 512, // 512x512 瓦片
+ minZoom: 0,
+ maxZoom: 14,
+ zoomOffset: 0,
+ updateStrategy: 'overlap',
+ },
+ })
+ .style({
+ opacity: 1.0,
+ });
+
+scene.addLayer(customTileLayer);
+```
+
+### 多子域配置
+
+```javascript
+// 提高并发加载速度
+const tileLayer = new RasterLayer().source('https://{s}.example.com/{z}/{x}/{y}.png', {
+ parser: {
+ type: 'rasterTile',
+ tileSize: 256,
+ subdomains: ['a', 'b', 'c', 'd'], // 4个子域轮流请求
+ },
+});
+```
+
+### 带认证的瓦片服务
+
+```javascript
+const securedTileLayer = new RasterLayer().source(
+ 'https://secure-tiles.example.com/{z}/{x}/{y}.png?apikey=YOUR_API_KEY',
+ {
+ parser: {
+ type: 'rasterTile',
+ tileSize: 256,
+ maxZoom: 18,
+ },
+ },
+);
+
+scene.addLayer(securedTileLayer);
+```
+
+### 图层控制 - 显示/隐藏
+
+```javascript
+const rasterLayer = new RasterLayer().source(tileUrl, config);
+scene.addLayer(rasterLayer);
+
+// 隐藏图层
+rasterLayer.hide();
+
+// 显示图层
+rasterLayer.show();
+
+// 切换可见性
+function toggleLayer() {
+ const isVisible = rasterLayer.isVisible();
+ if (isVisible) {
+ rasterLayer.hide();
+ } else {
+ rasterLayer.show();
+ }
+}
+```
+
+### 透明度动态调整
+
+```javascript
+const rasterLayer = new RasterLayer().source(tileUrl, config);
+scene.addLayer(rasterLayer);
+
+// 创建透明度滑块
+const slider = document.getElementById('opacity-slider');
+slider.addEventListener('input', (e) => {
+ const opacity = parseFloat(e.target.value);
+ rasterLayer.style({ opacity });
+ scene.render();
+});
+```
+
+## 样式配置详解
+
+```javascript
+{
+ opacity: 1.0, // 透明度,0-1,默认 1.0
+ // 暂不支持更多样式配置
+}
+```
+
+## 常见场景
+
+### 1. 叠加气象云图
+
+```javascript
+class WeatherTileLayer {
+ constructor(scene) {
+ this.scene = scene;
+ this.layer = null;
+ this.updateInterval = null;
+ }
+
+ start() {
+ // 创建气象瓦片图层
+ this.layer = new RasterLayer({
+ name: 'weather-radar',
+ })
+ .source('https://weather.example.com/radar/{z}/{x}/{y}.png?time=' + Date.now(), {
+ parser: {
+ type: 'rasterTile',
+ tileSize: 256,
+ minZoom: 1,
+ maxZoom: 10,
+ },
+ })
+ .style({
+ opacity: 0.6,
+ });
+
+ this.scene.addLayer(this.layer);
+
+ // 每10分钟更新一次
+ this.updateInterval = setInterval(
+ () => {
+ this.update();
+ },
+ 10 * 60 * 1000,
+ );
+ }
+
+ update() {
+ const timestamp = Date.now();
+ this.layer.setData(`https://weather.example.com/radar/{z}/{x}/{y}.png?time=${timestamp}`, {
+ parser: {
+ type: 'rasterTile',
+ tileSize: 256,
+ minZoom: 1,
+ maxZoom: 10,
+ },
+ });
+ }
+
+ stop() {
+ if (this.updateInterval) {
+ clearInterval(this.updateInterval);
+ }
+ if (this.layer) {
+ this.scene.removeLayer(this.layer);
+ }
+ }
+}
+
+// 使用
+const weatherLayer = new WeatherTileLayer(scene);
+weatherLayer.start();
+```
+
+### 2. 多图层切换
+
+```javascript
+const layers = {
+ satellite: {
+ name: '卫星影像',
+ url: 'https://tile.server.com/satellite/{z}/{x}/{y}.jpg',
+ layer: null,
+ },
+ street: {
+ name: '街道地图',
+ url: 'https://tile.server.com/street/{z}/{x}/{y}.png',
+ layer: null,
+ },
+ terrain: {
+ name: '地形图',
+ url: 'https://tile.server.com/terrain/{z}/{x}/{y}.png',
+ layer: null,
+ },
+};
+
+// 初始化所有图层
+Object.keys(layers).forEach((key) => {
+ const config = layers[key];
+ config.layer = new RasterLayer({ name: key })
+ .source(config.url, {
+ parser: {
+ type: 'rasterTile',
+ tileSize: 256,
+ },
+ })
+ .style({
+ opacity: key === 'satellite' ? 1.0 : 0, // 默认显示卫星图
+ });
+
+ scene.addLayer(config.layer);
+});
+
+// 切换图层
+function switchLayer(layerKey) {
+ Object.keys(layers).forEach((key) => {
+ const opacity = key === layerKey ? 1.0 : 0;
+ layers[key].layer.style({ opacity });
+ });
+ scene.render();
+}
+
+// UI 控制
+document.querySelectorAll('.layer-switch').forEach((btn) => {
+ btn.addEventListener('click', (e) => {
+ const layerKey = e.target.dataset.layer;
+ switchLayer(layerKey);
+ });
+});
+```
+
+### 3. 历史影像对比
+
+```javascript
+// 对比两个不同时期的影像
+const layer2020 = new RasterLayer({ name: 'image-2020' })
+ .source('https://tiles.example.com/2020/{z}/{x}/{y}.png', {
+ parser: { type: 'rasterTile', tileSize: 256 },
+ })
+ .style({ opacity: 1.0 });
+
+const layer2025 = new RasterLayer({ name: 'image-2025' })
+ .source('https://tiles.example.com/2025/{z}/{x}/{y}.png', {
+ parser: { type: 'rasterTile', tileSize: 256 },
+ })
+ .style({ opacity: 0.0 });
+
+scene.addLayer(layer2020);
+scene.addLayer(layer2025);
+
+// 卷帘对比控制
+const compareSlider = document.getElementById('compare-slider');
+compareSlider.addEventListener('input', (e) => {
+ const value = parseFloat(e.target.value); // 0-1
+ layer2020.style({ opacity: 1 - value });
+ layer2025.style({ opacity: value });
+ scene.render();
+});
+```
+
+### 4. 叠加专题图层
+
+```javascript
+// 基础底图
+const baseLayer = new RasterLayer({ name: 'base', zIndex: 1 }).source(
+ 'https://tiles.example.com/base/{z}/{x}/{y}.png',
+ {
+ parser: { type: 'rasterTile', tileSize: 256 },
+ },
+);
+
+// 专题图层 - 人口密度
+const populationLayer = new RasterLayer({ name: 'population', zIndex: 2 })
+ .source('https://tiles.example.com/population/{z}/{x}/{y}.png', {
+ parser: { type: 'rasterTile', tileSize: 256 },
+ })
+ .style({ opacity: 0.6 });
+
+// 专题图层 - 交通路网
+const trafficLayer = new RasterLayer({ name: 'traffic', zIndex: 3 })
+ .source('https://tiles.example.com/traffic/{z}/{x}/{y}.png', {
+ parser: { type: 'rasterTile', tileSize: 256 },
+ })
+ .style({ opacity: 0.5 });
+
+scene.addLayer(baseLayer);
+scene.addLayer(populationLayer);
+scene.addLayer(trafficLayer);
+
+// 图层控制面板
+function toggleThematicLayer(layerName, visible) {
+ const layers = { population: populationLayer, traffic: trafficLayer };
+ if (layers[layerName]) {
+ visible ? layers[layerName].show() : layers[layerName].hide();
+ scene.render();
+ }
+}
+```
+
+## 性能优化
+
+### 1. 设置合理的缩放级别
+
+```javascript
+const tileLayer = new RasterLayer().source(tileUrl, {
+ parser: {
+ type: 'rasterTile',
+ tileSize: 256,
+ minZoom: 3, // 避免加载过大范围的瓦片
+ maxZoom: 16, // 避免加载过精细的瓦片
+ },
+});
+```
+
+### 2. 使用多子域提高并发
+
+```javascript
+const tileLayer = new RasterLayer().source('https://{s}.tile.server.com/{z}/{x}/{y}.png', {
+ parser: {
+ type: 'rasterTile',
+ tileSize: 256,
+ subdomains: ['a', 'b', 'c', 'd'], // 多个子域并发请求
+ },
+});
+```
+
+### 3. 图层按需加载
+
+```javascript
+let rasterLayer = null;
+
+function showRasterLayer() {
+ if (!rasterLayer) {
+ rasterLayer = new RasterLayer().source(tileUrl, config);
+ scene.addLayer(rasterLayer);
+ } else {
+ rasterLayer.show();
+ }
+}
+
+function hideRasterLayer() {
+ if (rasterLayer) {
+ rasterLayer.hide(); // 隐藏而不是销毁,保留缓存
+ }
+}
+```
+
+### 4. 使用较大瓦片尺寸
+
+```javascript
+// 512x512 瓦片可减少请求数量
+const tileLayer = new RasterLayer().source(tileUrl, {
+ parser: {
+ type: 'rasterTile',
+ tileSize: 512, // 使用 512 而不是 256
+ maxZoom: 16,
+ },
+});
+```
+
+## 注意事项
+
+⚠️ **瓦片 URL 格式**:确保使用正确的占位符 `{z}`、`{x}`、`{y}`,TMS 格式使用 `{-y}`
+
+⚠️ **跨域问题**:确保瓦片服务器配置了 CORS 头,或使用代理
+
+⚠️ **缩放级别**:不同瓦片服务支持的缩放级别不同,设置合适的 minZoom/maxZoom
+
+⚠️ **瓦片尺寸**:大多数服务使用 256x256,部分使用 512x512,需与服务匹配
+
+⚠️ **坐标系**:确保瓦片服务使用 Web Mercator (EPSG:3857) 坐标系
+
+⚠️ **API Key**:使用第三方服务时注意 API Key 的有效性和配额限制
+
+⚠️ **性能**:过多图层会影响性能,建议不超过 3-5 个栅格图层同时显示
+
+## 常见问题
+
+### Q: 瓦片无法显示?
+
+A: 检查:1) URL 格式是否正确;2) CORS 配置;3) API Key 是否有效;4) 网络连接;5) 缩放级别范围
+
+### Q: 瓦片加载很慢?
+
+A: 1) 使用多子域配置;2) 使用 CDN;3) 设置合理的缩放级别范围;4) 考虑使用更大的瓦片尺寸
+
+### Q: 瓦片位置偏移?
+
+A: 检查:1) 坐标系是否匹配(Web Mercator);2) 是否需要使用 TMS 格式({-y});3) zoomOffset 设置
+
+### Q: 如何判断使用 XYZ 还是 TMS?
+
+A: 如果瓦片 Y 轴从上到下递增用 XYZ(`{y}`),从下到上递增用 TMS(`{-y}`)
+
+### Q: 瓦片有缝隙?
+
+A: 1) 检查 tileSize 设置是否与服务匹配;2) 可能是网络加载延迟,等待加载完成
+
+### Q: 如何实现瓦片预加载?
+
+A: L7 会自动缓存已加载的瓦片,可通过调整地图视角提前触发瓦片加载
+
+## 瓦片服务示例
+
+### 国内常用服务
+
+```javascript
+// 天地图 (需要申请 token)
+'https://t{s}.tianditu.gov.cn/DataServer?T=vec_w&x={x}&y={y}&l={z}&tk=YOUR_TOKEN';
+
+// 高德地图 (供参考,建议使用官方 SDK)
+'https://wprd0{s}.is.autonavi.com/appmaptile?x={x}&y={y}&z={z}&style=7';
+
+// OpenStreetMap
+'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png';
+```
+
+### 国际常用服务
+
+```javascript
+// Mapbox
+'https://api.mapbox.com/v4/mapbox.satellite/{z}/{x}/{y}.png?access_token=YOUR_TOKEN';
+
+// CartoDB
+'https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png';
+
+// Stamen
+'https://stamen-tiles-{s}.a.ssl.fastly.net/terrain/{z}/{x}/{y}.png';
+```
+
+## 相关技能
+
+- [场景初始化](../core/scene.md)
+- [图片图层](./image.md)
+
+## 在线示例
+
+查看更多示例: [L7 官方示例 - 栅格图层](https://l7.antv.antgroup.com/examples/raster/raster)
diff --git a/skills/l7/references/layers/tile-vector.md b/skills/l7/references/layers/tile-vector.md
new file mode 100644
index 0000000..6941a3a
--- /dev/null
+++ b/skills/l7/references/layers/tile-vector.md
@@ -0,0 +1,608 @@
+---
+skill_id: tile-vector
+skill_name: 矢量瓦片图层
+category: layers
+difficulty: advanced
+tags: [vector-tile, mvt, big-data, tile-layer]
+dependencies: [layer-common-api, source-mvt]
+version: 2.x
+---
+
+# 矢量瓦片图层
+
+## 技能描述
+
+掌握 L7 矢量瓦片图层的使用,通过瓦片化加载实现海量地理数据的高效渲染。矢量瓦片将大范围的矢量数据切分成小块,按需加载和渲染,显著降低数据传输量和渲染压力。
+
+## 何时使用
+
+- ✅ 需要渲染超大数据量的矢量数据(百万级以上)
+- ✅ 需要支持不同缩放级别的详细程度(LOD)
+- ✅ 需要减少初始加载时间,按需加载数据
+- ✅ 需要在低带宽环境下流畅显示地图
+- ✅ 使用 Mapbox Vector Tile (MVT) 标准的瓦片服务
+
+## 前置条件
+
+- 已完成[场景初始化](../core/scene.md)
+- 了解[MVT 数据源](../data/source-mvt.md)
+- 准备好矢量瓦片服务(MVT 格式)
+
+## 核心概念
+
+### 什么是矢量瓦片
+
+矢量瓦片是将地理矢量数据按照瓦片金字塔结构切分的数据格式:
+
+- **瓦片金字塔**:不同缩放级别(zoom)对应不同精度的数据
+- **按需加载**:只加载当前视野范围内的瓦片
+- **矢量格式**:保留矢量数据,可在客户端动态样式化
+- **标准格式**:通常使用 MVT (Mapbox Vector Tile) 或 PBF 格式
+
+### 矢量瓦片 vs 栅格瓦片
+
+| 对比项 | 矢量瓦片 | 栅格瓦片 |
+| ---------- | ------------------ | ---------------- |
+| 数据格式 | 矢量数据(点线面) | 图片(PNG/JPG) |
+| 文件大小 | 小(通常 < 50KB) | 较大(10-100KB) |
+| 样式灵活性 | 可动态改变样式 | 样式固定 |
+| 高清屏支持 | 完美支持 | 需要 @2x 图 |
+| 数据映射 | 支持数据驱动样式 | 不支持 |
+| 交互性 | 可点击、查询 | 不可交互 |
+
+## 支持的图层类型
+
+L7 矢量瓦片支持以下图层类型:
+
+| 图层类型 | 描述 | 使用场景 |
+| -------------- | ------------ | ------------------ |
+| PointLayer | 矢量点瓦片 | POI、定位点、标注 |
+| LineLayer | 矢量线瓦片 | 道路、边界、管线 |
+| PolygonLayer | 矢量面瓦片 | 行政区、建筑、地块 |
+| MaskLayer | 矢量掩模瓦片 | 配合栅格图层使用 |
+| TileDebugLayer | 瓦片调试图层 | 查看瓦片加载状态 |
+
+## 基础用法
+
+### 1. 点图层
+
+```javascript
+import { Scene, PointLayer, Source } from '@antv/l7';
+
+const scene = new Scene({
+ id: 'map',
+ map: new GaodeMap({
+ center: [120, 30],
+ zoom: 10,
+ }),
+});
+
+// 创建 MVT 数据源
+const vectorSource = new Source(
+ 'https://ganos.oss-cn-hangzhou.aliyuncs.com/m2/rs_l7/{z}/{x}/{y}.pbf',
+ {
+ parser: {
+ type: 'mvt',
+ tileSize: 256,
+ maxZoom: 14,
+ extent: [-180, -85.051129, 179, 85.051129],
+ },
+ },
+);
+
+// 创建点图层
+const pointLayer = new PointLayer({
+ featureId: 'id', // 要素唯一标识字段
+ sourceLayer: 'points', // MVT 数据源中的图层名称
+})
+ .source(vectorSource)
+ .shape('circle')
+ .size(8)
+ .color('category', ['#5B8FF9', '#5AD8A6', '#5D7092']);
+
+scene.on('loaded', () => {
+ scene.addLayer(pointLayer);
+});
+```
+
+### 2. 线图层
+
+```javascript
+import { LineLayer } from '@antv/l7';
+
+const lineLayer = new LineLayer({
+ featureId: 'id',
+ sourceLayer: 'roads', // 道路图层
+})
+ .source(vectorSource)
+ .shape('line')
+ .size('type', (type) => {
+ // 根据道路类型设置宽度
+ const widthMap = {
+ highway: 5,
+ main: 3,
+ secondary: 2,
+ default: 1,
+ };
+ return widthMap[type] || widthMap.default;
+ })
+ .color('type', {
+ highway: '#FF6B6B',
+ main: '#4ECDC4',
+ secondary: '#95E1D3',
+ default: '#CCCCCC',
+ });
+
+scene.addLayer(lineLayer);
+```
+
+### 3. 面图层
+
+```javascript
+import { PolygonLayer } from '@antv/l7';
+
+const polygonLayer = new PolygonLayer({
+ featureId: 'adcode',
+ sourceLayer: 'regions', // 行政区图层
+})
+ .source(vectorSource)
+ .shape('fill')
+ .color('population', ['#FFF5EB', '#FEE6CE', '#FDD0A2', '#FDAE6B', '#FD8D3C', '#E6550D'])
+ .style({
+ opacity: 0.8,
+ });
+
+scene.addLayer(polygonLayer);
+```
+
+## 配置选项
+
+### 图层构造参数
+
+```typescript
+interface IVectorTileLayerOptions {
+ featureId?: string; // 要素唯一标识字段(用于高亮和选中)
+ sourceLayer: string; // MVT 数据源中的图层名称
+ // ... 其他基础图层参数
+}
+```
+
+### Source 配置
+
+详见 [MVT 数据源](../data/source-mvt.md)
+
+```javascript
+const source = new Source(url, {
+ parser: {
+ type: 'mvt', // 固定为 'mvt'
+ tileSize: 256, // 瓦片大小,默认 256
+ minZoom: 0, // 最小缩放级别
+ maxZoom: 14, // 最大缩放级别
+ zoomOffset: 0, // 层级偏移
+ extent: [
+ // 数据边界
+ -180, -85.051129, 179, 85.051129,
+ ],
+ },
+});
+```
+
+## 高级用法
+
+### 1. 数据驱动样式
+
+矢量瓦片支持完整的数据映射能力:
+
+```javascript
+const layer = new PolygonLayer({
+ sourceLayer: 'buildings',
+})
+ .source(vectorSource)
+ .shape('fill')
+ .color('height', ['#ffffcc', '#c7e9b4', '#7fcdbb', '#41b6c4', '#2c7fb8', '#253494'])
+ .style({
+ opacity: 0.9,
+ })
+ .scale('height', {
+ type: 'quantile', // 分位数分类
+ });
+```
+
+### 2. 多图层数据源复用
+
+```javascript
+// 创建共享数据源
+const vectorSource = new Source('https://tiles.example.com/{z}/{x}/{y}.pbf', {
+ parser: {
+ type: 'mvt',
+ maxZoom: 14,
+ },
+});
+
+// 多个图层复用同一数据源
+const buildingLayer = new PolygonLayer({
+ sourceLayer: 'buildings',
+})
+ .source(vectorSource)
+ .color('#5B8FF9');
+
+const roadLayer = new LineLayer({
+ sourceLayer: 'roads',
+})
+ .source(vectorSource)
+ .color('#5AD8A6');
+
+// 只会请求一份瓦片数据
+scene.addLayer(buildingLayer);
+scene.addLayer(roadLayer);
+```
+
+### 3. 配合掩模图层
+
+矢量掩模图层常用于裁剪栅格图层:
+
+```javascript
+import { MaskLayer, RasterLayer } from '@antv/l7';
+
+// 创建掩模图层
+const maskLayer = new MaskLayer({
+ sourceLayer: 'boundary',
+}).source(vectorSource);
+
+// 创建栅格图层
+const rasterLayer = new RasterLayer({
+ mask: true, // 启用掩模
+}).source(rasterUrl, {
+ parser: { type: 'rasterTile' },
+});
+
+scene.addLayer(maskLayer);
+scene.addLayer(rasterLayer);
+```
+
+### 4. 瓦片调试
+
+使用调试图层查看瓦片加载状态:
+
+```javascript
+import { TileDebugLayer } from '@antv/l7';
+
+const debugLayer = new TileDebugLayer();
+scene.addLayer(debugLayer);
+
+// 调试图层会显示:
+// - 瓦片边界
+// - 瓦片坐标 (z/x/y)
+// - 加载状态
+```
+
+### 5. 交互事件
+
+矢量瓦片支持完整的交互能力:
+
+```javascript
+// 鼠标悬停高亮
+layer.on('mousemove', (e) => {
+ if (e.feature) {
+ layer.setActive(e.feature.id);
+ }
+});
+
+// 点击显示详情
+layer.on('click', (e) => {
+ const { feature } = e;
+ const popup = new Popup().setLnglat(e.lngLat).setHTML(`
+ ${feature.name}
+ 人口: ${feature.population}
+ `);
+ scene.addPopup(popup);
+});
+```
+
+> 💡 矢量瓦片图层同样支持所有通用图层方法(show/hide/setIndex 等)和事件,详见[图层通用方法和事件](./layer-common-api.md)。
+
+````
+
+## 实际应用场景
+
+### 1. 全国行政区可视化
+
+```javascript
+const chinaLayer = new PolygonLayer({
+ featureId: 'adcode',
+ sourceLayer: 'admin_bounds',
+})
+ .source(
+ 'https://tiles.example.com/china/{z}/{x}/{y}.pbf',
+ {
+ parser: {
+ type: 'mvt',
+ maxZoom: 12,
+ },
+ }
+ )
+ .shape('fill')
+ .color('gdp', [
+ '#f7fcf5',
+ '#c7e9c0',
+ '#74c476',
+ '#238b45',
+ '#00441b',
+ ])
+ .style({
+ opacity: 0.8,
+ })
+ .scale('gdp', {
+ type: 'quantile',
+ });
+
+// 边界线图层
+const boundaryLayer = new LineLayer({
+ sourceLayer: 'admin_bounds',
+})
+ .source(chinaLayer.getSource())
+ .shape('line')
+ .size(1)
+ .color('#333');
+
+scene.addLayer(chinaLayer);
+scene.addLayer(boundaryLayer);
+
+// 适配到数据范围
+scene.on('loaded', () => {
+ chinaLayer.fitBounds();
+});
+````
+
+### 2. 城市道路网络
+
+```javascript
+// 不同等级道路使用不同样式
+const highwayLayer = new LineLayer({
+ sourceLayer: 'roads',
+ zIndex: 3,
+})
+ .source(vectorSource)
+ .filter((feature) => feature.type === 'highway')
+ .shape('line')
+ .size(4)
+ .color('#FF6B6B')
+ .style({
+ opacity: 0.9,
+ });
+
+const mainRoadLayer = new LineLayer({
+ sourceLayer: 'roads',
+ zIndex: 2,
+})
+ .source(vectorSource)
+ .filter((feature) => feature.type === 'main')
+ .shape('line')
+ .size(2)
+ .color('#4ECDC4');
+
+const secondaryRoadLayer = new LineLayer({
+ sourceLayer: 'roads',
+ zIndex: 1,
+})
+ .source(vectorSource)
+ .filter((feature) => feature.type === 'secondary')
+ .shape('line')
+ .size(1)
+ .color('#95E1D3');
+
+scene.addLayer(highwayLayer);
+scene.addLayer(mainRoadLayer);
+scene.addLayer(secondaryRoadLayer);
+```
+
+### 3. POI 密度热力
+
+```javascript
+const poiLayer = new PointLayer({
+ sourceLayer: 'pois',
+})
+ .source(vectorSource)
+ .shape('circle')
+ .size('category', (type) => {
+ const sizeMap = {
+ restaurant: 10,
+ shop: 8,
+ bank: 12,
+ default: 6,
+ };
+ return sizeMap[type] || sizeMap.default;
+ })
+ .color('category', {
+ restaurant: '#FF6B6B',
+ shop: '#4ECDC4',
+ bank: '#FFD93D',
+ default: '#95E1D3',
+ })
+ .style({
+ opacity: 0.8,
+ });
+
+// 根据缩放级别控制显示
+poiLayer.setMinZoom(12); // 地图缩放到 12 级以上才显示
+```
+
+### 4. 建筑 3D 效果
+
+```javascript
+const buildingLayer = new PolygonLayer({
+ sourceLayer: 'buildings',
+})
+ .source(vectorSource)
+ .shape('extrude') // 拉伸成 3D
+ .size('height', (h) => h * 0.3) // 根据建筑高度拉伸
+ .color('usage', {
+ residential: '#5B8FF9',
+ commercial: '#5AD8A6',
+ industrial: '#5D7092',
+ })
+ .style({
+ opacity: 0.9,
+ pickingBuffer: 5,
+ });
+
+scene.addLayer(buildingLayer);
+```
+
+## 性能优化
+
+### 1. 控制瓦片范围
+
+```javascript
+const source = new Source(url, {
+ parser: {
+ type: 'mvt',
+ // 限制请求范围(中国区域)
+ extent: [73.66, 3.86, 135.05, 53.55],
+ // 限制最大层级(避免请求过细瓦片)
+ maxZoom: 14,
+ },
+});
+```
+
+### 2. 合理设置缩放范围
+
+```javascript
+// 避免在小缩放级别显示过多细节
+layer.setMinZoom(10);
+
+// 避免在大缩放级别请求过多瓦片
+layer.setMaxZoom(16);
+```
+
+### 3. 数据源复用
+
+```javascript
+// ✅ 推荐:多图层共享数据源
+const source = new Source(url, options);
+layer1.source(source);
+layer2.source(source);
+
+// ❌ 避免:重复创建数据源
+layer1.source(url, options);
+layer2.source(url, options); // 会重复请求
+```
+
+### 4. 按需加载图层
+
+```javascript
+// 根据缩放级别动态切换图层
+scene.on('zoomchange', () => {
+ const zoom = scene.getZoom();
+
+ if (zoom < 12) {
+ // 小级别:显示概览数据
+ summaryLayer.show();
+ detailLayer.hide();
+ } else {
+ // 大级别:显示详细数据
+ summaryLayer.hide();
+ detailLayer.show();
+ }
+});
+```
+
+## 常见问题
+
+### Q: 如何指定 sourceLayer?
+
+A: sourceLayer 是 MVT 数据源中包含的图层名称,需要查看瓦片数据结构确定。可以使用调试工具查看:
+
+```javascript
+const debugLayer = new TileDebugLayer();
+scene.addLayer(debugLayer);
+
+// 或查看网络请求的 PBF 文件内容
+```
+
+### Q: 瓦片不显示怎么办?
+
+A: 检查以下几点:
+
+1. URL 模板是否正确(包含 {z}/{x}/{y})
+2. sourceLayer 名称是否正确
+3. extent 范围是否包含当前视野
+4. maxZoom/minZoom 是否合理
+5. 网络请求是否成功(查看 Network 面板)
+
+### Q: 矢量瓦片和 GeoJSON 如何选择?
+
+A:
+
+| 场景 | 推荐方案 |
+| ------------- | -------- |
+| 数据量 < 1万 | GeoJSON |
+| 数据量 > 10万 | 矢量瓦片 |
+| 需要全局分析 | GeoJSON |
+| 只需可视化 | 矢量瓦片 |
+| 需要离线使用 | GeoJSON |
+| 需要按需加载 | 矢量瓦片 |
+
+### Q: 如何自定义瓦片请求?
+
+A: 使用 `getCustomData` 方法:
+
+```javascript
+const source = new Source(url, {
+ parser: {
+ type: 'mvt',
+ getCustomData: (tile, callback) => {
+ const { x, y, z } = tile;
+ const customUrl = `https://api.example.com/tile?z=${z}&x=${x}&y=${y}&token=xxx`;
+
+ fetch(customUrl)
+ .then((res) => res.arrayBuffer())
+ .then((data) => callback(null, data))
+ .catch((err) => callback(err, null));
+ },
+ },
+});
+```
+
+### Q: 如何处理不同缩放级别的数据差异?
+
+A: 可以根据 zoom 动态调整样式:
+
+```javascript
+scene.on('zoomchange', () => {
+ const zoom = scene.getZoom();
+
+ if (zoom < 10) {
+ layer.size(4); // 小缩放级别用小尺寸
+ } else if (zoom < 14) {
+ layer.size(6);
+ } else {
+ layer.size(8); // 大缩放级别用大尺寸
+ }
+});
+```
+
+## 注意事项
+
+⚠️ **数据格式**:确保瓦片服务返回标准的 MVT/PBF 格式
+
+⚠️ **坐标系统**:MVT 使用 Web Mercator 投影(EPSG:3857)
+
+⚠️ **图层名称**:sourceLayer 必须与 MVT 数据中的图层名称完全匹配
+
+⚠️ **缩放范围**:合理设置 minZoom 和 maxZoom 避免性能问题
+
+⚠️ **数据边界**:extent 参数用于限制请求范围,提升性能
+
+⚠️ **复用数据源**:多个图层使用同一瓦片数据时,务必复用 Source 对象
+
+## 相关技能
+
+- [图层通用方法和事件](./layer-common-api.md)
+- [MVT 数据源](../data/source-mvt.md)
+- [栅格瓦片图层](./tile-raster.md)
+- [性能优化](../performance/optimization.md)
+
+## 在线示例
+
+查看更多示例:[L7 矢量瓦片示例](https://l7.antv.antgroup.com/examples/tile/vector)
diff --git a/skills/l7/references/performance/optimization.md b/skills/l7/references/performance/optimization.md
new file mode 100644
index 0000000..115be01
--- /dev/null
+++ b/skills/l7/references/performance/optimization.md
@@ -0,0 +1,375 @@
+# Performance Optimization Guide
+
+L7 性能优化完整指南。
+
+## 数据优化
+
+### 1. 数据过滤
+
+```javascript
+// 在 source 时过滤
+layer.source(data, {
+ parser: { type: 'json', x: 'lng', y: 'lat' },
+ transforms: [
+ {
+ type: 'filter',
+ callback: (item) => {
+ // 只显示值大于阈值的数据
+ return item.value > 100;
+ },
+ },
+ ],
+});
+
+// 按视窗范围过滤
+scene.on('mapmove', () => {
+ const bounds = scene.getBounds();
+ const filteredData = data.filter(
+ (item) =>
+ item.lng >= bounds[0][0] &&
+ item.lng <= bounds[1][0] &&
+ item.lat >= bounds[0][1] &&
+ item.lat <= bounds[1][1],
+ );
+ layer.setData(filteredData);
+});
+```
+
+### 2. 数据聚合
+
+```javascript
+// 使用聚合图层
+import { HeatmapLayer } from '@antv/l7';
+
+// 替代大量点的 PointLayer
+const heatmap = new HeatmapLayer()
+ .source(massiveData, {
+ parser: { type: 'json', x: 'lng', y: 'lat' },
+ transforms: [
+ {
+ type: 'hexagon', // 六边形聚合
+ size: 500, // 聚合大小
+ field: 'value',
+ method: 'sum',
+ },
+ ],
+ })
+ .size('sum', [10, 50])
+ .color('sum', ['#ffffb2', '#fd8d3c', '#f03b20', '#bd0026']);
+
+scene.addLayer(heatmap);
+```
+
+### 3. 数据抽稀
+
+```javascript
+// 按比例抽样
+const sampledData = data.filter((_, index) => index % 10 === 0);
+
+// 按值抽样(只显示重要数据)
+const sampledData = data.sort((a, b) => b.value - a.value).slice(0, 1000); // 只显示前 1000 个重要点
+```
+
+### 4. 简化几何形状
+
+```javascript
+import * as turf from '@turf/turf';
+
+// 简化多边形
+const simplified = turf.simplify(polygonData, {
+ tolerance: 0.01, // 简化容差
+ highQuality: false, // 快速简化
+});
+
+layer.source(simplified, {
+ parser: { type: 'geojson' },
+});
+```
+
+## 图层管理
+
+### 1. 按需显示图层
+
+```javascript
+// 根据缩放级别显示不同细节
+scene.on('zoom', () => {
+ const zoom = scene.getZoom();
+
+ if (zoom < 10) {
+ // 低缩放:显示汇总数据
+ provinceLayer.show();
+ cityLayer.hide();
+ districtLayer.hide();
+ } else if (zoom < 13) {
+ // 中缩放:显示城市数据
+ provinceLayer.hide();
+ cityLayer.show();
+ districtLayer.hide();
+ } else {
+ // 高缩放:显示详细数据
+ provinceLayer.hide();
+ cityLayer.hide();
+ districtLayer.show();
+ }
+});
+```
+
+### 2. 合并图层
+
+```javascript
+// ❌ 低效:多个小图层
+categories.forEach((cat) => {
+ const layer = new PointLayer().source(data.filter((d) => d.category === cat));
+ scene.addLayer(layer);
+});
+
+// ✅ 高效:合并为一个图层
+const layer = new PointLayer().source(data).color('category', categoryColors);
+
+scene.addLayer(layer);
+```
+
+### 3. 移除不需要的图层
+
+```javascript
+// 移除图层释放资源
+scene.removeLayer(layer);
+
+// 清空所有图层
+scene.removeAllLayer();
+```
+
+## 渲染优化
+
+### 1. 降低渲染频率
+
+```javascript
+// 使用防抖
+import { debounce } from 'lodash';
+
+const updateLayer = debounce(() => {
+ layer.setData(newData);
+}, 300);
+
+// 在快速交互时使用
+inputElement.addEventListener('input', updateLayer);
+```
+
+### 2. 关闭不必要的特性
+
+```javascript
+layer.source(data).shape('circle').size(5).color('#5B8FF9').style({
+ opacity: 1.0, // 完全不透明,避免混合计算
+ blend: 'normal', // 普通混合模式
+});
+```
+
+### 3. 使用 GPU 加速
+
+```javascript
+// L7 默认使用 WebGL,确保使用 GPU 友好的配置
+
+// ✅ 固定大小(GPU 友好)
+layer.size(5);
+
+// ⚠️ 动态大小(需要更多计算)
+layer.size('value', [5, 20]);
+```
+
+## 瓦片服务
+
+对于海量数据,使用瓦片服务:
+
+```javascript
+import { PointLayer } from '@antv/l7';
+
+// 矢量瓦片
+const tileLayer = new PointLayer()
+ .source('https://tiles.example.com/{z}/{x}/{y}.mvt', {
+ parser: {
+ type: 'mvt',
+ tileSize: 256,
+ maxZoom: 14,
+ },
+ })
+ .shape('circle')
+ .size(5)
+ .color('#5B8FF9');
+
+scene.addLayer(tileLayer);
+```
+
+## 内存管理
+
+### 1. 及时清理资源
+
+```javascript
+// 移除图层前清理事件监听
+layer.off('click');
+layer.off('mousemove');
+
+// 移除图层
+scene.removeLayer(layer);
+
+// 销毁场景
+scene.destroy();
+```
+
+### 2. 避免内存泄漏
+
+```javascript
+// ❌ 可能导致内存泄漏
+scene.on('mapmove', () => {
+ const newLayer = new PointLayer(); // 每次都创建新图层
+ scene.addLayer(newLayer);
+});
+
+// ✅ 复用图层
+const layer = new PointLayer();
+scene.addLayer(layer);
+
+scene.on('mapmove', () => {
+ layer.setData(newData); // 更新数据而不是创建新图层
+});
+```
+
+### 3. 分批加载数据
+
+```javascript
+// 分批加载大数据
+async function loadDataInBatches(dataUrl, batchSize = 1000) {
+ let offset = 0;
+ let hasMore = true;
+
+ while (hasMore) {
+ const response = await fetch(`${dataUrl}?offset=${offset}&limit=${batchSize}`);
+ const batch = await response.json();
+
+ if (batch.length === 0) {
+ hasMore = false;
+ } else {
+ // 添加到图层
+ const currentData = layer.getSource().data;
+ layer.setData([...currentData, ...batch]);
+
+ offset += batchSize;
+ }
+ }
+}
+```
+
+## 性能监控
+
+### 1. FPS 监控
+
+```javascript
+let frameCount = 0;
+let lastTime = Date.now();
+
+scene.on('afterrender', () => {
+ frameCount++;
+ const now = Date.now();
+
+ if (now - lastTime >= 1000) {
+ const fps = frameCount;
+ console.log('FPS:', fps);
+
+ frameCount = 0;
+ lastTime = now;
+ }
+});
+```
+
+### 2. 渲染时间
+
+```javascript
+console.time('render');
+
+scene.on('loaded', () => {
+ layer.source(data).shape('circle').size(5).color('#5B8FF9');
+
+ scene.addLayer(layer);
+
+ console.timeEnd('render');
+});
+```
+
+### 3. 数据量统计
+
+```javascript
+// 图层数量
+console.log('图层数:', scene.getLayers().length);
+
+// 每个图层的数据量
+scene.getLayers().forEach((layer) => {
+ const source = layer.getSource();
+ console.log(layer.name, '数据量:', source.data.dataArray.length);
+});
+```
+
+## 最佳实践
+
+### 1. 数据量建议
+
+| 数据量 | 推荐方案 |
+| ------------ | --------------- |
+| < 1万 | 直接渲染 |
+| 1万 - 10万 | 数据过滤 + 抽稀 |
+| 10万 - 100万 | 聚合 + 瓦片 |
+| > 100万 | 瓦片服务 |
+
+### 2. 图层数量建议
+
+- 尽量控制在 **10 个图层以内**
+- 超过 20 个图层考虑合并或按需加载
+- 使用图层分组管理
+
+### 3. 事件监听优化
+
+```javascript
+// ❌ 为每个要素绑定事件
+data.forEach((item) => {
+ // 创建单独图层并绑定事件
+});
+
+// ✅ 在图层级别绑定事件
+layer.on('click', (e) => {
+ const feature = e.feature;
+ // 处理点击
+});
+```
+
+### 4. 样式更新优化
+
+```javascript
+// ❌ 频繁更新样式
+slider.addEventListener('input', (e) => {
+ layer.size(e.target.value);
+ scene.render();
+});
+
+// ✅ 防抖处理
+const updateSize = debounce((value) => {
+ layer.size(value);
+}, 100);
+
+slider.addEventListener('input', (e) => {
+ updateSize(e.target.value);
+});
+```
+
+## 性能检查清单
+
+- [ ] 数据量是否合理?考虑过滤或聚合
+- [ ] 是否有不必要的图层?考虑合并
+- [ ] 是否按需显示图层?根据缩放级别控制
+- [ ] 是否有内存泄漏?检查事件监听和图层清理
+- [ ] 样式更新是否频繁?使用防抖或节流
+- [ ] 是否使用了瓦片服务?对于海量数据考虑瓦片
+- [ ] 几何形状是否过于复杂?考虑简化
+- [ ] 监控 FPS 是否正常?目标 ≥ 30 FPS
+
+## 相关文档
+
+- [data/source-parser.md](../data/source-parser.md) - 数据过滤和转换
+- [layers/heatmap.md](../layers/heatmap.md) - 热力图聚合
diff --git a/skills/l7/references/visual/mapping.md b/skills/l7/references/visual/mapping.md
new file mode 100644
index 0000000..3f05c16
--- /dev/null
+++ b/skills/l7/references/visual/mapping.md
@@ -0,0 +1,547 @@
+---
+skill_id: visual-mapping
+skill_name: 视觉映射
+category: visual
+difficulty: intermediate
+tags: [color, size, shape, scale, mapping, visual-encoding]
+dependencies: [scene-initialization, point-layer]
+version: 2.x
+---
+
+# 视觉映射
+
+## 技能描述
+
+将数据字段映射到图层的视觉属性(颜色、大小、形状等),实现数据的可视化编码。
+
+## 何时使用
+
+- ✅ 根据数值字段设置不同颜色(热力分布、分级着色)
+- ✅ 根据数据值调整图形大小(气泡图、比例符号)
+- ✅ 使用不同形状表示分类数据(POI 类型、事件类别)
+- ✅ 多维数据可视化(颜色+大小+形状组合)
+- ✅ 动态数据范围映射(自适应色阶)
+
+## 前置条件
+
+- 已完成[场景初始化](../core/scene.md)
+- 已创建图层([点图层](../layers/point.md)、[线图层](../layers/line.md)等)
+- 数据中包含用于映射的字段
+
+## 核心概念
+
+### 映射类型
+
+| 映射方式 | 数据类型 | 适用场景 | 示例 |
+| -------- | --------- | ---------- | --------------------------------------------- |
+| 固定值 | - | 统一样式 | `.color('#5B8FF9')` |
+| 字段映射 | 连续/分类 | 数据驱动 | `.color('temperature', [...])` |
+| 回调函数 | 任意 | 自定义逻辑 | `.color(d => d.value > 100 ? 'red' : 'blue')` |
+
+## 代码示例
+
+### 颜色映射 (color)
+
+#### 1. 固定颜色
+
+```javascript
+const layer = new PointLayer()
+ .source(data, { parser: { type: 'json', x: 'lng', y: 'lat' } })
+ .shape('circle')
+ .size(10)
+ .color('#5B8FF9'); // 所有点使用相同颜色
+```
+
+#### 2. 字段映射 - 连续数值
+
+```javascript
+// 根据温度值映射颜色(蓝色到红色渐变)
+const layer = new PointLayer()
+ .source(data, { parser: { type: 'json', x: 'lng', y: 'lat' } })
+ .shape('circle')
+ .size(10)
+ .color('temperature', [
+ '#313695', // 低温
+ '#4575b4',
+ '#74add1',
+ '#abd9e9',
+ '#e0f3f8',
+ '#ffffbf',
+ '#fee090',
+ '#fdae61',
+ '#f46d43',
+ '#d73027',
+ '#a50026', // 高温
+ ]);
+```
+
+#### 3. 字段映射 - 分类数据
+
+```javascript
+// 根据类型字段映射不同颜色
+const layer = new PointLayer()
+ .source(data, { parser: { type: 'json', x: 'lng', y: 'lat' } })
+ .shape('circle')
+ .size(10)
+ .color('type', ['#FF6B6B', '#4ECDC4', '#95E1D3', '#F38181', '#CCCCCC']);
+
+// 如果需要固定 domain 顺序,可以设置 scale
+layer
+ .scale('type', {
+ type: 'cat',
+ domain: ['餐饮', '购物', '娱乐', '酒店', '其他'],
+ })
+ .color('type', ['#FF6B6B', '#4ECDC4', '#95E1D3', '#F38181', '#CCCCCC']);
+```
+
+#### 4. 回调函数映射
+
+```javascript
+const layer = new PointLayer()
+ .source(data, { parser: { type: 'json', x: 'lng', y: 'lat' } })
+ .shape('circle')
+ .size(10)
+ .color((feature) => {
+ const value = feature.value;
+ if (value > 1000) return '#d73027';
+ if (value > 500) return '#fdae61';
+ if (value > 100) return '#fee090';
+ return '#abd9e9';
+ });
+```
+
+### 大小映射 (size)
+
+#### 1. 固定大小
+
+```javascript
+const layer = new PointLayer()
+ .source(data, { parser: { type: 'json', x: 'lng', y: 'lat' } })
+ .shape('circle')
+ .size(15) // 所有点大小为 15
+ .color('#5B8FF9');
+```
+
+#### 2. 字段映射 - 数值范围
+
+```javascript
+// 根据人口数量映射点的大小
+const layer = new PointLayer()
+ .source(data, { parser: { type: 'json', x: 'lng', y: 'lat' } })
+ .shape('circle')
+ .size('population', [5, 50]) // 人口越多,点越大(5-50像素)
+ .color('#5B8FF9');
+```
+
+#### 3. 字段映射 - 3D 高度
+
+```javascript
+// 3D 柱状图:根据数值映射高度
+const layer = new PointLayer()
+ .source(data, { parser: { type: 'json', x: 'lng', y: 'lat' } })
+ .shape('cylinder')
+ .size('value', [0, 1000]) // 映射到 0-1000 米高度
+ .color('value', ['#ffffcc', '#800026'])
+ .style({
+ opacity: 0.8,
+ });
+```
+
+#### 4. 回调函数映射
+
+```javascript
+const layer = new PointLayer()
+ .source(data, { parser: { type: 'json', x: 'lng', y: 'lat' } })
+ .shape('circle')
+ .size((feature) => {
+ // 根据业务逻辑自定义大小
+ const value = feature.sales;
+ return Math.sqrt(value) / 10; // 平方根缩放
+ })
+ .color('#5B8FF9');
+```
+
+### 形状映射 (shape)
+
+#### 1. 固定形状
+
+```javascript
+const layer = new PointLayer()
+ .source(data, { parser: { type: 'json', x: 'lng', y: 'lat' } })
+ .shape('circle') // 圆形
+ .size(10)
+ .color('#5B8FF9');
+```
+
+#### 2. 字段映射 - 分类形状
+
+```javascript
+// 根据类型使用不同形状
+const layer = new PointLayer()
+ .source(data, { parser: { type: 'json', x: 'lng', y: 'lat' } })
+ .shape('category', {
+ A类: 'circle',
+ B类: 'triangle',
+ C类: 'square',
+ D类: 'hexagon',
+ })
+ .size(15)
+ .color('category', ['#FF6B6B', '#4ECDC4', '#95E1D3', '#F38181']);
+```
+
+#### 3. 图标映射
+
+```javascript
+// 使用自定义图标
+const layer = new PointLayer()
+ .source(data, { parser: { type: 'json', x: 'lng', y: 'lat' } })
+ .shape('icon', 'category') // 根据 category 字段映射图标
+ .size(20)
+ .style({
+ A类: 'restaurant',
+ B类: 'shop',
+ C类: 'hotel',
+ });
+```
+
+### 多维映射组合
+
+```javascript
+// 同时映射颜色、大小、形状
+const layer = new PointLayer()
+ .source(data, { parser: { type: 'json', x: 'lng', y: 'lat' } })
+ .shape('type', {
+ 高风险: 'triangle',
+ 中风险: 'square',
+ 低风险: 'circle',
+ })
+ .size('impact', [5, 30]) // 影响程度控制大小
+ .color('severity', ['#fee5d9', '#fcae91', '#fb6a4a', '#de2d26', '#a50f15'])
+ .style({
+ opacity: 0.8,
+ strokeWidth: 2,
+ stroke: '#fff',
+ });
+```
+
+## 色阶配置
+
+### 内置色阶
+
+```javascript
+// ColorBrewer 色阶
+import { scales } from '@antv/l7';
+
+layer.color('value', scales.quantize.Blues); // 蓝色系
+layer.color('value', scales.quantize.Reds); // 红色系
+layer.color('value', scales.quantize.Greens); // 绿色系
+layer.color('value', scales.diverging.RdYlBu); // 红-黄-蓝发散色
+```
+
+### 自定义色阶
+
+```javascript
+// 线性插值色阶
+const colorScale = [
+ '#f7fbff',
+ '#deebf7',
+ '#c6dbef',
+ '#9ecae1',
+ '#6baed6',
+ '#4292c6',
+ '#2171b5',
+ '#08519c',
+ '#08306b',
+];
+
+// threshold 类型示例
+layer
+ .scale('value', {
+ type: 'threshold',
+ domain: [100, 500, 1000, 5000], // N个断点对应 N+1 个颜色
+ })
+ .color('value', ['#ffffcc', '#c7e9b4', '#7fcdbb', '#41b6c4', '#2c7fb8', '#253494']);
+```
+
+## 高级用法
+
+### 动态更新映射
+
+```javascript
+const layer = new PointLayer()
+ .source(data, { parser: { type: 'json', x: 'lng', y: 'lat' } })
+ .shape('circle')
+ .size(10)
+ .color('value', ['#ffffcc', '#800026']);
+
+// 切换到另一个字段
+function switchField(fieldName) {
+ layer.color(fieldName, ['#fee5d9', '#a50f15']);
+ scene.render();
+}
+
+// 更新颜色范围
+function updateColors(colors) {
+ layer.color('value', colors);
+ scene.render();
+}
+```
+
+### Scale 配置
+
+L7 使用 `scale()` 方法设置数据字段的映射方式,将地图数据值(数字、日期、类别等)转换为视觉属性(颜色、大小、形状等)。
+
+```javascript
+// 先配置 scale,再进行 color/size 映射
+layer
+ .scale('mag', {
+ type: 'linear',
+ domain: [1, 50],
+ })
+ .color('id', ['#f00', '#ff0'])
+ .size('mag', [1, 80]);
+```
+
+### Scale 配置参数
+
+```typescript
+interface IScaleConfig {
+ type:
+ | 'linear'
+ | 'log'
+ | 'pow'
+ | 'quantile'
+ | 'quantize'
+ | 'threshold'
+ | 'cat'
+ | 'time'
+ | 'sequential'
+ | 'diverging'
+ | 'identity';
+ domain?: any[]; // 数据值域范围
+ range?: any[]; // 视觉值映射范围(通常使用 color/size 方法设置)
+ unknown?: string; // 未知值的默认映射
+ nice?: boolean; // 是否优化边界值
+ clamp?: boolean; // 是否限制在范围内
+}
+```
+
+### Scale 使用规则
+
+| Scale 类型 | domain 设置 | 说明 |
+| ---------- | ----------- | --------------------------------- |
+| linear | 可选 | 不设置会自动计算 min、max |
+| quantize | 可选 | 不设置会自动计算 min、max |
+| quantile | 不可设置 | 必须自动计算,需要全量数据 |
+| threshold | 必须设置 | N 个断点需要 N+1 个颜色 |
+| diverging | 可选 | 不设置会自动计算 min、middle、max |
+| cat | 可选 | 不设置会自动获取唯一值 |
+
+### 使用示例
+
+```javascript
+// 组合使用 scale 和映射
+const layer = new PointLayer()
+ .source(data, {
+ parser: { type: 'json', x: 'lng', y: 'lat' },
+ })
+ .scale('population', {
+ type: 'linear',
+ domain: [0, 10000000],
+ })
+ .scale('category', {
+ type: 'cat',
+ })
+ .size('population', [5, 50])
+ .color('category', ['#FF6B6B', '#4ECDC4', '#95E1D3']);
+```
+
+## 常见场景
+
+### 1. 人口密度分级着色
+
+```javascript
+const populationLayer = new PolygonLayer()
+ .source(districtData, { parser: { type: 'geojson' } })
+ .shape('fill')
+ .scale('population_density', {
+ type: 'quantile', // 分位数映射
+ })
+ .color('population_density', [
+ '#f7fbff',
+ '#deebf7',
+ '#c6dbef',
+ '#9ecae1',
+ '#6baed6',
+ '#4292c6',
+ '#2171b5',
+ '#084594',
+ ])
+ .style({
+ opacity: 0.8,
+ });
+```
+
+### 2. 气泡图(双变量映射)
+
+```javascript
+// 颜色表示类型,大小表示数量
+const bubbleLayer = new PointLayer()
+ .source(data, { parser: { type: 'json', x: 'lng', y: 'lat' } })
+ .shape('circle')
+ .size('amount', [10, 80]) // 交易金额
+ .color('category', ['#FF6B6B', '#4ECDC4', '#95E1D3', '#F38181']) // 类别
+ .style({
+ opacity: 0.6,
+ strokeWidth: 2,
+ stroke: '#fff',
+ });
+```
+
+### 3. 热力强度可视化
+
+```javascript
+const heatLayer = new PointLayer()
+ .source(eventData, { parser: { type: 'json', x: 'lng', y: 'lat' } })
+ .shape('circle')
+ .size(15)
+ .color('intensity', ['#440154', '#31688e', '#35b779', '#fde724'])
+ .style({
+ opacity: 0.8,
+ });
+```
+
+### 4. 三维数据可视化
+
+```javascript
+// 建筑高度 + 颜色表示用途
+const buildingLayer = new PolygonLayer()
+ .source(buildingData, { parser: { type: 'geojson' } })
+ .shape('extrude')
+ .size('height', [10, 500]) // 建筑高度
+ .scale('usage', {
+ type: 'cat',
+ domain: ['住宅', '商业', '办公', '工业', '其他'],
+ })
+ .color('usage', ['#FFE5B4', '#FF6B6B', '#4ECDC4', '#95E1D3', '#CCCCCC'])
+ .style({
+ opacity: 0.8,
+ });
+```
+
+## 性能优化
+
+### 1. 避免频繁更新
+
+```javascript
+// ❌ 不好的做法:频繁重新映射
+data.forEach((item) => {
+ layer.color('value', colors);
+ scene.render();
+});
+
+// ✅ 好的做法:批量更新
+layer.color('value', colors);
+scene.render();
+```
+
+### 简化映射逻辑
+
+```javascript
+// ❌ 复杂的回调函数会影响性能
+layer.color((d) => {
+ // 大量计算...
+ return complexCalculation(d);
+});
+
+// ✅ 预处理数据,使用 scale 配置
+data.forEach((d) => (d.colorCategory = complexCalculation(d)));
+layer.scale('colorCategory', { type: 'cat' }).color('colorCategory', colors);
+```
+
+### 3. 使用合适的 Scale 类型
+
+```javascript
+// 大数据量时,quantize 比 quantile 快(不需要排序数据)
+layer
+ .scale('value', {
+ type: 'quantize',
+ domain: [0, 100],
+ })
+ .color('value', colorScale);
+```
+
+## 注意事项
+
+⚠️ **数据范围**:确保映射字段在数据中存在且值有效
+
+⚠️ **颜色数量**:色阶颜色数量建议 5-10 个,过多难以区分
+
+⚠️ **可访问性**:考虑色盲友好的配色方案,避免红绿搭配
+
+⚠️ **数据类型**:连续数据用渐变色,分类数据用区分色
+
+⚠️ **Scale 类型**:根据数据分布选择合适的 scale 类型
+
+⚠️ **默认值**:回调函数要处理空值情况,返回默认颜色
+
+## 常见问题
+
+### Q: 为什么颜色没有显示?
+
+A: 检查:1) 字段名是否正确;2) 数据中是否有该字段;3) 颜色数组是否有效
+
+### Q: 如何实现数据驱动的透明度?
+
+A: 在 `style()` 中使用回调函数:
+
+```javascript
+layer.style({
+ opacity: (feature) => feature.value / 100,
+});
+```
+
+### Q: 映射后颜色分布不均匀?
+
+A: 尝试不同的 scale 类型:
+
+```javascript
+// 使用 quantile 而不是 linear
+layer.scale('value', { type: 'quantile' }).color('value', colors);
+
+// 或手动指定 domain 范围
+layer
+ .scale('value', {
+ type: 'linear',
+ domain: [0, 1000], // 明确数据范围
+ })
+ .color('value', colors);
+```
+
+### Q: 如何自定义图例?
+
+A: 参考 [components.md](../interaction/components.md) 中的图例组件
+
+### Q: 能否同时映射多个字段到一个属性?
+
+A: 使用回调函数自定义逻辑:
+
+```javascript
+layer.color((d) => {
+ if (d.type === 'A' && d.value > 100) return 'red';
+ if (d.type === 'B') return 'blue';
+ return 'gray';
+});
+```
+
+## 相关技能
+
+- [样式配置](./style.md)
+- [点图层](../layers/point.md)
+- [线图层](../layers/line.md)
+- [面图层](../layers/polygon.md)
+- [组件](../interaction/components.md)
+
+## 在线示例
+
+查看更多示例: [L7 官方示例 - 数据映射](https://l7.antv.antgroup.com/examples)
diff --git a/skills/l7/references/visual/style.md b/skills/l7/references/visual/style.md
new file mode 100644
index 0000000..f983ac3
--- /dev/null
+++ b/skills/l7/references/visual/style.md
@@ -0,0 +1,581 @@
+---
+skill_id: layer-style
+skill_name: 图层样式配置
+category: visual
+difficulty: beginner
+tags: [style, opacity, stroke, texture, blend, animation]
+dependencies: [scene-initialization]
+version: 2.x
+---
+
+# 图层样式配置
+
+## 技能描述
+
+配置图层的视觉样式属性,包括透明度、描边、纹理、混合模式等,提升可视化效果。
+
+## 何时使用
+
+- ✅ 调整图层透明度(叠加图层、突出重点)
+- ✅ 添加描边效果(边界清晰、视觉层次)
+- ✅ 使用纹理填充(特殊视觉效果)
+- ✅ 设置混合模式(光晕效果、数据叠加)
+- ✅ 配置动画效果(动态展示、吸引注意)
+
+## 前置条件
+
+- 已完成[场景初始化](../core/scene.md)
+- 已创建图层实例
+
+## 代码示例
+
+### 基础样式配置
+
+```javascript
+import { PointLayer } from '@antv/l7';
+
+const layer = new PointLayer()
+ .source(data, { parser: { type: 'json', x: 'lng', y: 'lat' } })
+ .shape('circle')
+ .size(10)
+ .color('#5B8FF9')
+ .style({
+ opacity: 0.8, // 透明度
+ strokeWidth: 2, // 描边宽度
+ stroke: '#fff', // 描边颜色
+ strokeOpacity: 1.0, // 描边透明度
+ });
+
+scene.addLayer(layer);
+```
+
+## 样式属性详解
+
+### 1. 透明度 (Opacity)
+
+#### 整体透明度
+
+```javascript
+layer.style({
+ opacity: 0.8, // 0 完全透明,1 完全不透明
+});
+```
+
+#### 动态透明度(数据驱动)
+
+```javascript
+layer.style({
+ opacity: (feature) => {
+ return feature.value / 100; // 根据数值动态调整
+ },
+});
+```
+
+### 2. 描边样式 (Stroke)
+
+#### 基础描边
+
+```javascript
+// 点图层描边
+const pointLayer = new PointLayer()
+ .source(data, { parser: { type: 'json', x: 'lng', y: 'lat' } })
+ .shape('circle')
+ .size(15)
+ .color('#5B8FF9')
+ .style({
+ strokeWidth: 2, // 描边宽度(像素)
+ stroke: '#ffffff', // 描边颜色
+ strokeOpacity: 1.0, // 描边透明度
+ });
+
+// 面图层描边
+const polygonLayer = new PolygonLayer()
+ .source(data, { parser: { type: 'geojson' } })
+ .shape('fill')
+ .color('#5B8FF9')
+ .style({
+ opacity: 0.6,
+ strokeWidth: 1,
+ stroke: '#333',
+ strokeOpacity: 0.8,
+ });
+
+// 线图层样式
+const lineLayer = new LineLayer()
+ .source(data, { parser: { type: 'geojson' } })
+ .shape('line')
+ .color('#5B8FF9')
+ .size(2)
+ .style({
+ opacity: 0.8,
+ lineType: 'solid', // solid | dash
+ });
+```
+
+#### 虚线样式
+
+```javascript
+const lineLayer = new LineLayer()
+ .source(data, { parser: { type: 'geojson' } })
+ .shape('line')
+ .color('#FF6B6B')
+ .size(3)
+ .style({
+ lineType: 'dash',
+ dashArray: [5, 5], // [实线长度, 间隔长度]
+ });
+```
+
+### 3. 混合模式 (Blend)
+
+```javascript
+const layer = new PointLayer({
+ blend: 'additive', // normal | additive | subtractive | max
+})
+ .source(data, { parser: { type: 'json', x: 'lng', y: 'lat' } })
+ .shape('circle')
+ .size(20)
+ .color('#FF6B6B')
+ .style({
+ opacity: 0.6,
+ });
+```
+
+#### 混合模式说明
+
+| 模式 | 效果 | 适用场景 |
+| ------------- | -------- | ------------------ |
+| `normal` | 默认混合 | 常规图层 |
+| `additive` | 叠加变亮 | 热力效果、光晕效果 |
+| `subtractive` | 叠加变暗 | 阴影效果 |
+| `max` | 取最大值 | 密度分析 |
+
+### 4. 纹理填充
+
+```javascript
+// 使用图片纹理
+const polygonLayer = new PolygonLayer()
+ .source(data, { parser: { type: 'geojson' } })
+ .shape('fill')
+ .style({
+ texture: 'https://example.com/pattern.png',
+ textureBlend: 'normal',
+ opacity: 0.8,
+ });
+```
+
+### 5. 3D 效果样式
+
+#### 3D 柱状图
+
+```javascript
+const layer = new PointLayer()
+ .source(data, { parser: { type: 'json', x: 'lng', y: 'lat' } })
+ .shape('cylinder')
+ .size('value', [0, 500]) // 高度
+ .color('value', ['#ffffcc', '#800026'])
+ .style({
+ opacity: 0.8,
+ coverage: 0.8, // 覆盖度(柱子粗细)
+ angle: 0, // 旋转角度
+ });
+```
+
+#### 3D 建筑
+
+```javascript
+const buildingLayer = new PolygonLayer()
+ .source(buildingData, { parser: { type: 'geojson' } })
+ .shape('extrude')
+ .size('height')
+ .color('#5B8FF9')
+ .style({
+ opacity: 0.8,
+ pickingBuffer: 5, // 拾取缓冲区
+ heightfixed: true, // 高度固定
+ });
+```
+
+### 6. 光晕效果
+
+```javascript
+const layer = new PointLayer({
+ blend: 'additive',
+})
+ .source(data, { parser: { type: 'json', x: 'lng', y: 'lat' } })
+ .shape('circle')
+ .size(30)
+ .color('#FF6B6B')
+ .style({
+ opacity: 0.3,
+ blur: 0.9, // 模糊程度,0-1
+ });
+```
+
+## 图层级别配置
+
+### zIndex (层叠顺序)
+
+```javascript
+const baseLayer = new PolygonLayer({ zIndex: 1 }).source(data).shape('fill').color('#f0f0f0');
+
+const highlightLayer = new PolygonLayer({ zIndex: 2 })
+ .source(highlightData)
+ .shape('fill')
+ .color('#FF6B6B');
+
+scene.addLayer(baseLayer);
+scene.addLayer(highlightLayer); // 显示在 baseLayer 上方
+```
+
+### 图层可见性
+
+```javascript
+// 隐藏/显示图层
+layer.hide();
+layer.show();
+
+// 检查可见性
+const isVisible = layer.isVisible();
+
+// 切换可见性
+layer.toggleVisible();
+```
+
+### 图层拾取控制
+
+```javascript
+layer.style({
+ pickingBuffer: 5, // 拾取缓冲区大小(像素),增大可提高点击精度
+});
+```
+
+## 常见场景
+
+### 1. 半透明图层叠加
+
+```javascript
+// 底层:行政区划
+const districtLayer = new PolygonLayer({ zIndex: 1 })
+ .source(districtData, { parser: { type: 'geojson' } })
+ .shape('fill')
+ .color('name', ['#FEF0D9', '#FDD49E', '#FDBB84', '#FC8D59'])
+ .style({
+ opacity: 0.6,
+ });
+
+// 上层:道路网络
+const roadLayer = new LineLayer({ zIndex: 2 })
+ .source(roadData, { parser: { type: 'geojson' } })
+ .shape('line')
+ .color('#333333')
+ .size(2)
+ .style({
+ opacity: 0.8,
+ });
+
+scene.addLayer(districtLayer);
+scene.addLayer(roadLayer);
+```
+
+### 2. 高亮选中效果
+
+```javascript
+const normalLayer = new PolygonLayer()
+ .source(data, { parser: { type: 'geojson' } })
+ .shape('fill')
+ .color('#5B8FF9')
+ .style({
+ opacity: 0.6,
+ strokeWidth: 1,
+ stroke: '#fff',
+ });
+
+const highlightLayer = new PolygonLayer()
+ .source([], { parser: { type: 'geojson' } })
+ .shape('fill')
+ .color('#FF6B6B')
+ .style({
+ opacity: 0.8,
+ strokeWidth: 3,
+ stroke: '#fff',
+ });
+
+scene.addLayer(normalLayer);
+scene.addLayer(highlightLayer);
+
+// 点击高亮
+normalLayer.on('click', (e) => {
+ highlightLayer.setData(e.feature);
+});
+```
+
+### 3. 热力光晕效果
+
+```javascript
+const heatLayer = new PointLayer({
+ blend: 'additive',
+ zIndex: 2,
+})
+ .source(data, { parser: { type: 'json', x: 'lng', y: 'lat' } })
+ .shape('circle')
+ .size('value', [20, 100])
+ .color('value', ['#440154', '#31688e', '#35b779', '#fde724'])
+ .style({
+ opacity: 0.5,
+ blur: 0.8,
+ });
+```
+
+### 4. 动画样式
+
+```javascript
+// 呼吸动画
+const layer = new PointLayer()
+ .source(data, { parser: { type: 'json', x: 'lng', y: 'lat' } })
+ .shape('circle')
+ .size(15)
+ .color('#FF6B6B')
+ .animate(true); // 开启动画
+
+// 自定义动画
+layer.animate({
+ enable: true,
+ type: 'wave', // wave | ripple
+ duration: 2000, // 动画时长(毫秒)
+ speed: 0.5, // 动画速度
+ rings: 3, // 波纹圈数
+});
+```
+
+### 5. 边界强调
+
+```javascript
+const boundaryLayer = new LineLayer()
+ .source(boundaryData, { parser: { type: 'geojson' } })
+ .shape('line')
+ .color('#333')
+ .size(3)
+ .style({
+ opacity: 1.0,
+ lineType: 'solid',
+ });
+
+// 添加外发光效果
+const glowLayer = new LineLayer({
+ blend: 'additive',
+})
+ .source(boundaryData, { parser: { type: 'geojson' } })
+ .shape('line')
+ .color('#FF6B6B')
+ .size(8)
+ .style({
+ opacity: 0.3,
+ blur: 0.8,
+ });
+
+scene.addLayer(glowLayer);
+scene.addLayer(boundaryLayer);
+```
+
+## 样式动态更新
+
+### 运行时更新
+
+```javascript
+const layer = new PointLayer()
+ .source(data, { parser: { type: 'json', x: 'lng', y: 'lat' } })
+ .shape('circle')
+ .size(10)
+ .color('#5B8FF9')
+ .style({
+ opacity: 0.8,
+ });
+
+scene.addLayer(layer);
+
+// 更新样式
+function updateOpacity(value) {
+ layer.style({ opacity: value });
+ scene.render();
+}
+
+// UI 滑块控制
+document.getElementById('opacity-slider').addEventListener('input', (e) => {
+ updateOpacity(parseFloat(e.target.value));
+});
+```
+
+### 批量样式更新
+
+```javascript
+function updateLayerStyle(options) {
+ layer.style({
+ opacity: options.opacity || 0.8,
+ strokeWidth: options.strokeWidth || 2,
+ stroke: options.stroke || '#fff',
+ });
+ scene.render();
+}
+
+// 使用
+updateLayerStyle({
+ opacity: 0.6,
+ strokeWidth: 3,
+ stroke: '#333',
+});
+```
+
+## 性能优化
+
+### 1. 避免频繁样式更新
+
+```javascript
+// ❌ 不好的做法
+setInterval(() => {
+ layer.style({ opacity: Math.random() });
+ scene.render();
+}, 16);
+
+// ✅ 好的做法:使用动画
+layer.animate({
+ enable: true,
+ interval: 0.5,
+ duration: 2000,
+});
+```
+
+### 2. 简化样式计算
+
+```javascript
+// ❌ 复杂的回调函数
+layer.style({
+ opacity: (feature) => {
+ // 大量计算...
+ return complexCalculation(feature);
+ },
+});
+
+// ✅ 预处理数据
+data.forEach((d) => (d.opacityValue = complexCalculation(d)));
+layer.color('opacityValue', [0.1, 1.0]);
+```
+
+### 3. 合理使用混合模式
+
+```javascript
+// additive 模式会增加渲染负担,仅在需要时使用
+const layer = new PointLayer({
+ blend: 'normal', // 默认模式性能最好
+});
+```
+
+## 注意事项
+
+⚠️ **透明度范围**:opacity 值范围 0-1,超出会被裁剪
+
+⚠️ **描边性能**:大量数据时描边会影响性能,考虑简化
+
+⚠️ **混合模式**:additive 模式对性能有影响,谨慎使用
+
+⚠️ **样式更新**:更新样式后需调用 `scene.render()` 才能生效
+
+⚠️ **zIndex 范围**:建议使用 0-999,过大可能导致问题
+
+⚠️ **3D 效果**:3D 图层的渲染成本高于 2D 图层
+
+## 常见问题
+
+### Q: 修改样式后不生效?
+
+A: 确保调用了 `scene.render()` 触发重绘
+
+### Q: 描边看不清?
+
+A: 增大 `strokeWidth`,或使用对比色作为描边颜色
+
+### Q: 如何实现闪烁效果?
+
+A: 使用 `animate()` 方法或自定义定时器更新透明度
+
+### Q: 图层顺序错乱?
+
+A: 设置合适的 `zIndex` 值,数值越大越在上层
+
+### Q: 如何实现阴影效果?
+
+A: 创建两个图层,下层使用 `subtractive` 混合模式
+
+### Q: 纹理图片不显示?
+
+A: 检查图片 URL 是否可访问,是否有 CORS 问题
+
+## 完整示例
+
+### 多层次可视化
+
+```javascript
+// 1. 底图图层
+const baseLayer = new PolygonLayer({ zIndex: 1 })
+ .source(baseData, { parser: { type: 'geojson' } })
+ .shape('fill')
+ .color('#f5f5f5')
+ .style({
+ opacity: 0.8,
+ });
+
+// 2. 数据填充层
+const dataLayer = new PolygonLayer({ zIndex: 2 })
+ .source(valueData, { parser: { type: 'geojson' } })
+ .shape('fill')
+ .color('value', ['#ffffcc', '#c7e9b4', '#7fcdbb', '#1d91c0', '#0c2c84'])
+ .style({
+ opacity: 0.7,
+ strokeWidth: 0.5,
+ stroke: '#fff',
+ });
+
+// 3. 高亮热点层(光晕效果)
+const hotspotLayer = new PointLayer({
+ blend: 'additive',
+ zIndex: 3,
+})
+ .source(hotspots, { parser: { type: 'json', x: 'lng', y: 'lat' } })
+ .shape('circle')
+ .size(50)
+ .color('#FF6B6B')
+ .style({
+ opacity: 0.4,
+ blur: 0.9,
+ });
+
+// 4. 标注层
+const labelLayer = new PointLayer({ zIndex: 4 })
+ .source(labels, { parser: { type: 'json', x: 'lng', y: 'lat' } })
+ .shape('name', 'text')
+ .size(14)
+ .color('#333')
+ .style({
+ textAnchor: 'center',
+ textOffset: [0, 0],
+ strokeWidth: 2,
+ stroke: '#fff',
+ });
+
+scene.addLayer(baseLayer);
+scene.addLayer(dataLayer);
+scene.addLayer(hotspotLayer);
+scene.addLayer(labelLayer);
+```
+
+## 相关技能
+
+- [视觉映射](./mapping.md)
+- [图层动画](../animation/layer-animation.md)
+- [点图层](../layers/point.md)
+- [线图层](../layers/line.md)
+- [面图层](../layers/polygon.md)
+
+## 在线示例
+
+查看更多示例: [L7 官方示例](https://l7.antv.antgroup.com/examples)