
Antv L7
- 27 installs
- 4k repo stars
- Updated July 30, 2026
- antvis/l7
AntV L7 (antv-l7) is a skill that guides building interactive WebGL maps and geospatial visualizations with the AntV L7 library.
About
A reference guide for AntV L7, a WebGL-based geospatial data visualization library. A developer uses it to create interactive maps, plot geographic data as points, lines, polygons, and heatmaps, and build location-based dashboards. It documents scene setup, data parsing for GeoJSON/CSV/JSON, layer types, interactions, animations, and performance tuning for large datasets, with reference files loaded on demand.
- Guide for building interactive WebGL maps with the AntV L7 geospatial visualization library
- Covers point, line, polygon, heatmap, image, and raster/tile layers plus interactions and animations
- Integrates with AMap (GaodeMap), Mapbox, Maplibre, or standalone L7 Map
Antv L7 by the numbers
- 27 all-time installs (skills.sh)
- Ranked #1,483 of 2,245 Frontend Development skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
antv-l7 capabilities & compatibility
- Capabilities
- map visualization · geospatial layers · data visualization
- Use cases
- frontend · data analysis · ui design
- Pricing
- Free
What antv-l7 says it does
Comprehensive guide for AntV L7 geospatial visualization library.
Integrate maps with AMap (GaodeMap), Mapbox, Maplibre, or standalone L7 Map
Optimize performance for large-scale geographic datasets
npx skills add https://github.com/antvis/l7 --skill antv-l7Add your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 27 |
|---|---|
| repo stars | ★ 4k |
| Last updated | July 30, 2026 |
| Repository | antvis/l7 ↗ |
What it does
Build interactive WebGL maps and geospatial dashboards with AntV L7 from GeoJSON, CSV, or JSON data.
Who is it for?
Interactive maps, geographic data visualization, and location-based data dashboards.
When should I use this skill?
When a developer needs interactive maps, geographic data layers, or location dashboards with AntV L7.
What you get
An interactive L7 map with the right layers, data parsing, interactions, and performance settings.
- A configured Scene with map layers, interactions, and performance tuning
By the numbers
- documents 5-step core workflow (Scene, data, layers, interaction, performance)
- covers 8+ layer types (point, line, polygon, heatmap, image, raster, tile-raster, tile-vector)
Files
AntV L7 Geospatial Visualization
AntV L7 是基于 WebGL 的大规模地理空间数据可视化引擎,支持多种地图底图和丰富的可视化图层类型。
Quick Start
创建最简单的 L7 地图应用:
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 转换
- [source-raster.md](references/data/source-raster.md) - 栅格数据源(GeoTIFF、多波段、遥感影像)
图层类型 (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) - 栅格图层:单张栅格图片
- [tile-raster.md](references/layers/tile-raster.md) - 栅格瓦片图层:TMS/WMS/WMTS 瓦片服务
- [base-layer.md](references/layers/base-layer.md) - 图层通用方法和事件
- [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) - 点击、悬停、选中事件
- [popup.md](references/interaction/popup.md) - Popup 弹窗组件
- [layer-popup.md](references/interaction/layer-popup.md) - LayerPopup 图层弹窗
- [components.md](references/interaction/components.md) - Marker、MarkerLayer、Controls、Legend
- [marker-layer.md](references/interaction/marker-layer.md) - MarkerLayer 标注图层
- [controls.md](references/interaction/controls.md) - 地图控件(Zoom、Scale、Fullscreen 等)
动画效果 (references/animation/)
- [layer-animation.md](references/animation/layer-animation.md) - 图层动画、轨迹动画
性能优化 (references/performance/)
- [optimization.md](references/performance/optimization.md) - 数据过滤、聚合、图层管理
使用指南
按用户需求选择文档
| 用户请求示例 | 加载的文档 |
|---|---|
| "创建一个地图" | core/scene.md, core/map-types.md |
| "显示点位数据" | layers/point.md, data/geojson.md |
| "绘制路径" | layers/line.md |
| "热力图" | layers/heatmap.md |
| "加载卫星影像" | layers/tile-raster.md, data/source-raster.md |
| "添加点击事件" | interaction/events.md |
| "显示弹窗" | interaction/popup.md, interaction/layer-popup.md |
| "添加标注" | interaction/components.md, interaction/marker-layer.md |
| "添加控件" | interaction/controls.md |
技能组合模式
复杂需求需要组合多个技能:
城市可视化 = scene + polygon + point + events + popup
轨迹动画 = scene + line + animation
热力分析 = scene + heatmap + data/json依赖检查
使用 metadata/skill-dependency.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. 错误处理: 添加事件监听和数据验证
快速参考
常用导入
// 核心
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)
坐标格式
[经度, 纬度]; // [120.19, 30.26]
// 经度: -180 ~ 180
// 纬度: -90 ~ 90元数据
- skill-dependency.json - 技能依赖关系图
- skill-tags.json - 中英文标签检索
- version-compatibility.json - 版本兼容性信息
查看 index.md 获取完整技能列表和导航。
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.mdskills/references/layers/tile-raster.md
3. 数据源补充
官方文档位置: site/docs/api/source/
缺失内容:
- ❌ MVT(Mapbox Vector Tile)- 矢量瓦片数据源
- ❌ Image 数据源
- ❌ Raster 数据源
- ❌ RasterTile 数据源
- ❌ NDI 数据源(遥感影像)
- ❌ RGB 数据源(遥感影像)
推荐新增文档:
skills/references/data/source-mvt.mdskills/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% 以上。
L7 技能索引(Skill Index)
从这里开始,快速查找和组合 L7 技能文档
📚 按领域查找
1. 核心功能 Core
- scene.md - Scene 初始化、生命周期、方法
- map-types.md - 地图类型配置
2. 数据处理 Data
- geojson.md - GeoJSON 格式和解析
- csv.md - CSV 数据加载
- json.md - JSON 数据源
- parser.md - 数据解析配置
- source-raster.md - 栅格数据源(GeoTIFF、多波段、遥感影像)
3. 图层类型 Layers
- point.md - 点图层
- line.md - 线图层
- polygon.md - 面图层
- heatmap.md - 热力图
- image.md - 图片图层
- raster.md - 栅格图层
- tile-raster.md - 栅格瓦片图层(TMS/WMS/WMTS)
- base-layer.md - 图层通用方法和事件
- other-layers.md - 其他图层
4. 视觉映射 Visual
- mapping.md - 颜色、大小、形状映射
- style.md - 样式配置
5. 交互组件 Interaction
- events.md - 事件处理
- popup.md - Popup 弹窗
- layer-popup.md - LayerPopup 图层弹窗
- components.md - Marker、MarkerLayer、Controls、Legend
- marker-layer.md - MarkerLayer 标注图层
- controls.md - 地图控件(Zoom、Scale、Fullscreen 等)
6. 动画效果 Animation
- layer-animation.md - 图层动画、轨迹动画
7. 性能优化 Performance
- optimization.md - 数据过滤、聚合、图层管理
🎯 按场景查找
| 用户需求 | 推荐文档 | 难度 |
|---|---|---|
| 创建地图 | scene.md + map-types.md | ⭐ |
| 显示点位 | point.md + geojson.md | ⭐ |
| 绘制路径 | line.md | ⭐ |
| 区域填充 | polygon.md | ⭐ |
| 热力图 | heatmap.md | ⭐⭐ |
| 加载卫星影像 | tile-raster.md + source-raster.md | ⭐⭐ |
| 点击事件 | events.md | ⭐⭐ |
| 显示弹窗 | layer-popup.md | ⭐⭐ |
| 添加标注 | marker-layer.md | ⭐⭐ |
| 添加控件 | controls.md | ⭐⭐ |
| 轨迹动画 | layer-animation.md | ⭐⭐ |
| 性能优化 | optimization.md | ⭐⭐⭐ |
---
📖 相关资源
- 主文档:SKILL.md - 快速入门与概览
- 依赖关系:skill-dependency.json
- 标签检索:skill-tags.json
- 版本兼容性:version-compatibility.json
{
"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": []
}
}
{
"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": ["问题", "样式", "不生效"]
}
{
"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 支持"
]
}
L7 Skill Library
为 AntV L7 地理空间可视化引擎设计的结构化技能知识库,遵循 skill-creator 最佳实践。
🎯 设计原则
基于 skill-creator 的最佳实践:
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 文件遵循统一结构:
# 标题
## 目录 (对于 >100 行的文件)
## 快速示例
## 详细配置
## 使用场景
## 常见问题
## 相关文档🔍 检索策略
按标签检索
使用 metadata/skill-tags.json:
{
"point-layer": ["point", "scatter", "bubble", "点", "散点", "气泡"],
"scene-initialization": ["scene", "map", "init", "场景", "地图", "初始化"]
}按依赖检索
使用 metadata/skill-dependency.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 模型代码生成,建议配合向量检索系统使用以获得最佳效果。
Layer Animation Guide
L7 图层动画和轨迹动画完整指南。
图层动画
数据更新动画
// 基础数据更新
layer.setData(newData);
// 带动画的数据更新
layer.animate(true); // 开启动画
layer.setData(newData);属性动画
// 大小动画
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 轨迹动画
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 运动点
// 运动的点
const pointLayer = new PointLayer()
.source(pathData)
.shape('circle')
.size(10)
.color('#FF6B3B')
.animate({
enable: true,
interval: 0.5,
duration: 4,
});组合使用:路径 + 运动点
// 路径
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 弧线动画
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);时序数据动画
时间轴控制
// 带时间戳的数据
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);相机动画
飞行动画
// 飞到指定位置
scene.flyTo({
center: [120.19, 30.26],
zoom: 12,
pitch: 45,
bearing: 30,
duration: 2000, // 动画时长(毫秒)
});环绕动画
let bearing = 0;
function rotate() {
bearing = (bearing + 0.5) % 360;
scene.setBearing(bearing);
requestAnimationFrame(rotate);
}
rotate();动画控制
开始/暂停/重置
// 开始动画
layer.animate(true);
// 暂停动画
layer.animate(false);
// 重置并重新开始
layer.animate(false);
layer.animate(true);动画事件
layer.on('animatestart', () => {
console.log('动画开始');
});
layer.on('animateend', () => {
console.log('动画结束');
});性能优化
1. 降低数据密度
// 对复杂路径进行简化
import * as turf from '@turf/turf';
const simplified = turf.simplify(pathData, {
tolerance: 0.01,
highQuality: false,
});2. 控制动画数量
// 限制同时播放的动画数量
const MAX_ANIMATIONS = 10;
if (scene.getLayers().filter((l) => l.isAnimating()).length < MAX_ANIMATIONS) {
layer.animate(true);
}3. 使用 requestAnimationFrame
function animate() {
// 更新状态
updateData();
requestAnimationFrame(animate);
}
animate();完整示例:出租车轨迹
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 - LineLayer 详细配置
- point.md - PointLayer 详细配置
- events.md - 事件处理
地图引擎配置
技能描述
L7 支持多种地图引擎作为底图,包括高德地图、Mapbox、MapLibre 以及独立的 Map 引擎。选择合适的地图引擎是创建 L7 可视化项目的第一步。
何时使用
- 🗺️ 高德地图(GaodeMap):国内项目,需要国内地图服务和 POI 数据
- 🌍 Mapbox:国际项目,需要精美的国际地图样式
- 🆓 MapLibre:开源项目,离线部署,自定义地图服务
- 📐 Map(独立引擎):室内地图、游戏地图、不需要地理底图的场景
前置条件
- 已安装
@antv/l7 - 已安装对应的地图库(如
@antv/l7-maps)
地图引擎对比
| 特性 | GaodeMap | Mapbox | MapLibre | Map |
|---|---|---|---|---|
| 国内服务 | ✅ 优秀 | ❌ 需翻墙 | ✅ 可用 | ✅ 不依赖 |
| 国际服务 | ❌ 较弱 | ✅ 优秀 | ✅ 优秀 | ✅ 不依赖 |
| 需要 Token | ✅ 是 | ✅ 是 | ❌ 否 | ❌ 否 |
| 离线部署 | ❌ 否 | ❌ 否 | ✅ 是 | ✅ 是 |
| POI 数据 | ✅ 丰富 | ✅ 有 | ❌ 无 | ❌ 无 |
| 自定义样式 | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| 坐标系统 | 经纬度 | 经纬度 | 经纬度 | 平面坐标 |
| 适用场景 | 国内地理 | 国际地理 | 自建服务 | 非地理场景 |
代码示例
1. 高德地图(GaodeMap)- 推荐国内使用
高德地图是国内最常用的地图服务,提供丰富的 POI 数据和路网信息。
基础用法
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. 访问 高德开放平台 2. 注册账号并创建应用 3. 获取 Web 端(JS API)Key 4. 在代码中配置 token 参数
⚠️ 注意:虽然 token 是可选的,但建议配置以避免服务限制。
地图样式
高德地图支持多种内置样式:
// 暗色主题(适合数据可视化)
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 视角配置
const scene = new Scene({
id: 'map',
map: new GaodeMap({
style: 'dark',
center: [120.19, 30.26],
zoom: 14,
pitch: 45, // 倾斜角度,用于 3D 效果
bearing: 30, // 旋转角度
}),
});传入已有高德地图实例
如果项目中已经创建了高德地图实例,可以直接传入:
// 先创建高德地图实例
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 模式)
使用高德地图插件
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 内置的独立地图引擎,完全不依赖第三方地图服务,适合室内地图、游戏地图等场景。
基础用法
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
- 需要自己提供底图(或使用空白背景)
添加自定义瓦片底图
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);
});室内地图示例
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(国际项目)
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 官网 2. 注册账号 3. 在 Account 页面获取 Access Token 4. 配置 token 参数
4. MapLibre(开源方案)
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
✅ 优势:
- 国内服务稳定快速
- 丰富的 POI 和路网数据
- 支持多种地图样式
- 完善的中文文档
❌ 限制:
- 需要申请 Token
- 国际数据相对较弱无底图场景推荐:Map
✅ 优势:
- 完全离线可用
- 不需要任何 Token
- 使用灵活的平面坐标
- 可自定义底图
❌ 限制:
- 没有内置地理数据
- 需要自己处理坐标系统国际项目推荐:Mapbox 或 MapLibre
✅ Mapbox 优势:
- 精美的地图样式
- 全球数据完善
- 强大的自定义能力
✅ MapLibre 优势:
- 完全开源免费
- 不需要 Token
- 支持离线部署实际应用场景
1. 国内城市可视化(使用 GaodeMap)
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)
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)
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) 方法:
// 切换为暗色主题
scene.setMapStyle('dark');
// 切换为自定义样式
scene.setMapStyle('amap://styles/your-style-id');注意事项
⚠️ Token 安全:不要在公开的代码仓库中暴露 Token,建议使用环境变量
⚠️ 坐标系统:确保数据坐标系统与地图引擎匹配
⚠️ 网络依赖:GaodeMap、Mapbox 需要网络连接;Map 可完全离线
⚠️ 性能考虑:Map 引擎在大数据量时性能更好,因为没有底图渲染开销
相关技能
- 场景初始化
- 场景生命周期
- 场景方法
- 点图层
在线示例
场景生命周期管理
技能描述
管理 L7 场景的完整生命周期,包括场景加载、运行时事件监听、以及场景销毁。理解和正确使用生命周期事件对于构建稳定的地图应用至关重要。
何时使用
- ✅ 需要在场景加载完成后执行初始化操作
- ✅ 监听地图的缩放、平移等交互事件
- ✅ 监听容器大小变化,实现响应式布局
- ✅ 页面卸载或组件销毁时清理资源
- ✅ 调试时需要开启/关闭实时渲染
前置条件
- 已完成场景初始化
核心概念
生命周期阶段
L7 场景的生命周期分为三个主要阶段:
1. 初始化阶段:创建 Scene 实例 2. 运行阶段:场景加载完成,可以添加图层、监听事件 3. 销毁阶段:场景被销毁,释放资源
创建 Scene → loaded 事件 → 添加图层/监听事件 → 销毁 Scene代码示例
场景加载事件
loaded 事件 - 场景初始化完成
最重要的生命周期事件,所有图层和组件都应该在此事件后添加。
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 事件 - 容器大小变化
当地图容器大小改变时触发,适用于响应式布局。
scene.on('resize', () => {
console.log('地图容器大小已改变');
// 可以在这里重新布局或调整图层
});常见场景:
// 监听窗口大小变化,自动调整地图
window.addEventListener('resize', () => {
// Scene 会自动触发 resize 事件
});地图交互事件
缩放事件
// 缩放级别更改后触发
scene.on('zoomchange', (e) => {
console.log('当前缩放级别:', scene.getZoom());
});
// 缩放开始时触发
scene.on('zoomstart', () => {
console.log('开始缩放');
});
// 缩放停止时触发
scene.on('zoomend', () => {
console.log('缩放结束');
});地图移动事件
// 地图平移时触发
scene.on('mapmove', () => {
console.log('地图正在移动');
});
// 地图平移开始时触发
scene.on('movestart', () => {
console.log('开始移动');
});
// 地图移动结束后触发(包括平移和缩放导致的中心点变化)
scene.on('moveend', () => {
const center = scene.getCenter();
console.log('移动结束,当前中心点:', center);
});拖拽事件
// 开始拖拽地图时触发
scene.on('dragstart', (e) => {
console.log('开始拖拽');
});
// 拖拽地图过程中触发
scene.on('dragging', (e) => {
console.log('正在拖拽');
});
// 停止拖拽地图时触发
scene.on('dragend', (e) => {
console.log('拖拽结束');
});鼠标事件
// 鼠标点击
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('鼠标滚轮缩放');
});移除事件监听
// 定义事件处理函数
const handleClick = (e) => {
console.log('点击位置:', e.lngLat);
};
// 绑定事件
scene.on('click', handleClick);
// 移除事件监听
scene.off('click', handleClick);场景销毁
destroy 方法
离开页面或不再需要地图时,必须调用 destroy 方法释放资源。
// 销毁场景
scene.destroy();React 示例:
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 <div id="map" style={{ height: '500px' }} />;
}Vue 示例:
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 事件
场景销毁时触发的事件。
scene.on('destroy', () => {
console.log('场景已销毁');
// 可以在这里执行额外的清理工作
});
scene.destroy();调试相关事件
开启/关闭实时渲染
L7 默认按需重绘以节省资源。调试时可以开启实时渲染,便于使用 SpectorJS 等工具捕捉帧渲染。
// 开启实时渲染(用于调试)
scene.startAnimate();
// 停止实时渲染
scene.stopAnimate();WebGL 上下文丢失
scene.on('webglcontextlost', () => {
console.error('WebGL 上下文丢失');
// 可以提示用户刷新页面
});实际应用场景
1. 响应式地图
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. 缩放级别联动
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. 地图移动加载数据
scene.on('moveend', () => {
const bounds = scene.getBounds();
// 根据可视范围加载数据
fetchDataInBounds(bounds).then((data) => {
layer.setData(data);
});
});4. 交互提示
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(),并移除所有事件监听器。
// ❌ 错误:忘记销毁
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 事件中进行异步数据加载时注意错误处理
相关技能
- 场景初始化
- 场景方法
- 事件处理
- 点图层
在线示例
查看更多示例:L7 官方示例
场景方法
技能描述
掌握 L7 Scene 提供的各种方法,包括图层管理、控件管理、地图操作、坐标转换、资源管理等核心功能。
何时使用
- ✅ 动态添加/移除图层
- ✅ 添加地图控件(缩放、比例尺等)
- ✅ 控制地图视角(中心点、缩放、旋转)
- ✅ 坐标系统转换
- ✅ 管理全局图片资源
- ✅ 添加 Popup 和 Marker
- ✅ 导出地图图片
前置条件
- 已完成场景初始化
图层管理方法
addLayer(layer): void
将图层添加到场景中。
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[]
获取所有图层。
const layers = scene.getLayers();
console.log('图层数量:', layers.length);
layers.forEach((layer) => {
console.log('图层ID:', layer.id);
});getLayer(id: string): ILayer
根据图层 ID 获取图层。
const layer = scene.getLayer('layer-id');
if (layer) {
layer.show();
}getLayerByName(name: string): ILayer
根据图层名称获取图层。
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
移除并销毁图层。
const layer = scene.getLayer('layer-id');
scene.removeLayer(layer);
// 图层已被销毁,不能再使用⚠️ 注意:移除图层的同时会自动销毁图层,释放资源。
removeAllLayer(): void
移除并销毁所有图层。
scene.removeAllLayer();
// 所有图层已被移除和销毁控件管理方法
addControl(control: IControl): void
添加控件到场景。
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
移除控件。
const zoomControl = new Zoom({ position: 'topright' });
scene.addControl(zoomControl);
// 移除控件
scene.removeControl(zoomControl);getControlByName(name: string): IControl
根据控件名称获取控件。
const zoomControl = new Zoom({
name: 'myZoom',
position: 'topright',
});
scene.addControl(zoomControl);
// 通过名称获取
const control = scene.getControlByName('myZoom');Popup 管理方法
addPopup(popup: Popup): void
添加 Popup 弹窗。
import { Popup } from '@antv/l7';
const popup = new Popup({
offsets: [0, 20],
closeButton: true,
})
.setLnglat([120.19, 30.26])
.setHTML('<div>这是一个 Popup</div>');
scene.addPopup(popup);removePopup(popup: Popup): void
移除 Popup 弹窗。
scene.removePopup(popup);Marker 管理方法
addMarker(marker: IMarker): void
添加 Marker 标记。
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。
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。
scene.removeMarkerLayer(markerLayer);removeAllMarkers(): void
移除所有 Marker。
scene.removeAllMarkers();地图视角控制方法
getZoom(): number
获取当前缩放级别。
const zoom = scene.getZoom();
console.log('当前缩放级别:', zoom);setZoom(zoom: number): void
设置缩放级别(0-22)。
scene.setZoom(12);zoomIn(): void
地图放大一级。
scene.zoomIn();zoomOut(): void
地图缩小一级。
scene.zoomOut();getCenter(): ILngLat
获取地图中心点。
const center = scene.getCenter();
console.log('中心点:', center); // { lng: 120.19, lat: 30.26 }setCenter(center: [number, number], options?: ICameraOptions): void
设置地图中心点。
// 基础用法
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
同时设置缩放级别和中心点。
scene.setZoomAndCenter(12, [120.19, 30.26]);getPitch(): number
获取地图倾斜角度(0-60)。
const pitch = scene.getPitch();
console.log('倾斜角度:', pitch);setPitch(pitch: number): void
设置地图倾斜角度(用于 3D 效果)。
scene.setPitch(45); // 设置为 45 度倾斜setRotation(rotation: number): void
设置地图顺时针旋转角度(0-360)。
scene.setRotation(90); // 旋转 90 度panTo(lnglat: [number, number]): void
地图平移到指定经纬度。
scene.panTo([120.19, 30.26]);panBy(x: number, y: number): void
以像素为单位平移地图。
// 向右平移 100px,向下平移 50px
scene.panBy(100, 50);
// 向左平移 100px,向上平移 50px
scene.panBy(-100, -50);fitBounds(bounds: [[number, number], [number, number]], options?: IOptions): void
地图缩放到指定范围。
// 基础用法
scene.fitBounds([
[112, 32], // 西南角 [minLng, minLat]
[114, 35], // 东北角 [maxLng, maxLat]
]);
// 带动画
scene.fitBounds(
[
[112, 32],
[114, 35],
],
{ animate: true },
);地图样式和状态方法
setMapStyle(style: string): void
设置地图样式。
// 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
设置地图交互状态。
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 元素。
const container = scene.getContainer();
console.log('容器宽度:', container.offsetWidth);
console.log('容器高度:', container.offsetHeight);getSize(): [number, number]
获取地图容器宽高。
const [width, height] = scene.getSize();
console.log(`容器尺寸: ${width} x ${height}`);坐标转换方法
lngLatToContainer(lnglat: [number, number]): IPoint
经纬度转换为容器像素坐标。
const point = scene.lngLatToContainer([120.19, 30.26]);
console.log('容器坐标:', point); // { x: 256, y: 128 }containerToLngLat(point: [number, number]): ILngLat
容器像素坐标转换为经纬度。
const lnglat = scene.containerToLngLat([256, 128]);
console.log('经纬度:', lnglat); // { lng: 120.19, lat: 30.26 }lngLatToPixel(lnglat: [number, number]): IPoint
经纬度转换为屏幕像素坐标。
const pixel = scene.lngLatToPixel([120.19, 30.26]);
console.log('像素坐标:', pixel); // { x: 512, y: 384 }pixelToLngLat(pixel: [number, number]): ILngLat
屏幕像素坐标转换为经纬度。
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
添加全局图片资源,供图层使用。
// 添加网络图片
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
判断是否已添加某个图片资源。
if (!scene.hasImage('marker-icon')) {
scene.addImage('marker-icon', 'https://example.com/marker.png');
}removeImage(id: string): void
删除全局图片资源。
scene.removeImage('marker-icon');addFontFace(fontFamily: string, fontPath: string): void
添加字体文件(用于 iconfont)。
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 映射。
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 映射。
scene.addIconFonts([
['home', ''],
['location', ''],
['star', ''],
]);静态方法
Scene.addProtocol(protocol: string, handler: Function)
添加自定义数据协议(用于加载特殊格式瓦片)。
// 自定义协议
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 示例:
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)
删除自定义协议。
Scene.removeProtocol('custom');导出和调试方法
exportMap(type?: 'png' | 'jpg'): string
导出地图为图片(仅导出可视化层,不包含底图)。
// 导出为 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 点精灵大小范围。
const [minSize, maxSize] = scene.getPointSizeRange();
console.log(`点大小范围: ${minSize} - ${maxSize}`);startAnimate(): void
开启实时渲染(用于调试)。
scene.startAnimate();
// 便于使用 SpectorJS 等工具捕捉帧渲染stopAnimate(): void
停止实时渲染。
scene.stopAnimate();实际应用场景
1. 动态切换图层
const layers = {
point: new PointLayer()...,
line: new LineLayer()...,
polygon: new PolygonLayer()...
};
function showLayer(type) {
// 移除所有图层
scene.removeAllLayer();
// 添加指定图层
scene.addLayer(layers[type]);
}
// 切换图层
showLayer('point');2. 地图导航
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. 数据范围适配
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. 响应式控件
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 方法:
scene.setMapStatus({
dragEnable: false,
zoomEnable: false,
rotateEnable: false,
});Q: 如何监听地图属性变化?
A: 使用生命周期事件:
scene.on('zoomchange', () => {
console.log('缩放级别:', scene.getZoom());
});
scene.on('moveend', () => {
console.log('中心点:', scene.getCenter());
});注意事项
⚠️ 内存管理:使用 removeLayer 会自动销毁图层,无需手动调用 layer.destroy()
⚠️ 坐标系统:区分容器坐标和像素坐标的使用场景
⚠️ 资源管理:及时移除不需要的全局资源(图片、字体)
⚠️ 导出限制:exportMap 只能导出 L7 图层,不包含底图
相关技能
- 场景初始化
- 场景生命周期
- 图层管理
- 交互组件
- 事件处理
在线示例
查看更多示例:L7 官方示例
场景初始化
技能描述
创建 L7 场景对象(Scene),这是使用 L7 的第一步。Scene 是包含地图、图层、组件的全局容器。
何时使用
- 开始任何 L7 可视化项目
- 需要在页面中嵌入地图
- 需要管理多个图层
前置条件
- HTML 页面中存在用于地图渲染的 DOM 容器
- 已安装
@antv/l7和地图底图库(如@antv/l7-maps)
输入参数
必需参数
| 参数 | 类型 | 说明 |
|---|---|---|
id | string \ | HTMLElement |
map | MapInstance | 地图实例 (GaodeMap \ |
地图配置参数
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
style | string | 'light' | 地图样式 (dark \ |
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()
代码示例
基础用法 - 高德地图
import { Scene } from '@antv/l7';
import { GaodeMap } from '@antv/l7-maps';
// 确保 HTML 中有对应的容器
// <div id="map"></div>
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
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(离线部署)
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 内置的独立地图引擎,不依赖任何第三方地图服务,适合室内地图、游戏地图等场景。
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 视角配置
const scene = new Scene({
id: 'map',
map: new GaodeMap({
style: 'dark',
center: [120.19, 30.26],
zoom: 14,
pitch: 45, // 倾斜角度,用于 3D 效果
bearing: 30, // 旋转角度
}),
});自定义 Logo 位置
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,
}),
});开启遮罩功能
const scene = new Scene({
id: 'map',
stencil: true, // 必须开启才能使用 Mask 功能
map: new GaodeMap({
style: 'dark',
center: [120.19, 30.26],
zoom: 10,
}),
});使用 DOM 元素
const mapContainer = document.getElementById('my-map');
const scene = new Scene({
id: mapContainer,
map: new GaodeMap({
center: [120.19, 30.26],
zoom: 10,
}),
});HTML 页面配置
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>L7 地图</title>
<style>
#map {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
</style>
</head>
<body>
<div id="map"></div>
<script src="your-script.js"></script>
</body>
</html>纯 HTML CDN 加载方案
直接通过 CDN 引入 L7,无需构建工具,适合快速原型开发和简单应用。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>L7 CDN 示例</title>
<style>
* {
margin: 0;
padding: 0;
}
html,
body {
width: 100%;
height: 100%;
}
#map {
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<div id="map"></div>
<!-- 引入 L7 库(自动获取最新版本) -->
<script src="https://gw.alipayobjects.com/render/p/hitu_npm/@antv/l7/2.23.2/dist/l7.js"></script>
<script>
// L7 全局变量已自动注册,可以直接访问
const { Scene, GaodeMap, PointLayer } = window.L7;
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('场景加载完成');
// 示例:添加点图层
const data = [
{ lng: 120.19, lat: 30.26, value: 10 },
{ lng: 120.2, lat: 30.27, value: 20 },
];
const pointLayer = new PointLayer()
.source(data, {
parser: {
type: 'json',
x: 'lng',
y: 'lat',
},
})
.shape('circle')
.size(8)
.color('#1890ff');
scene.addLayer(pointLayer);
});
</script>
</body>
</html>CDN 方案的优点:
- ✅ 无需 npm 或构建工具
- ✅ 快速集成到现有项目
- ✅ 适合简单的数据可视化需求
- ✅ CDN 提供的版本已包含常用的地图和图层
使用 CDN 时的注意事项:
- L7 对象挂载在全局
window.L7下 - CDN 已自动拉取最新版本,生产环境建议锁定具体版本号
- 某些高级功能(如自定义着色器)可能需要额外配置
常见问题
地理地图(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:
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]
场景生命周期事件
// 场景加载完成
scene.on('loaded', () => {
console.log('场景加载完成');
});
// 场景销毁
scene.on('destroy', () => {
console.log('场景已销毁');
});相关技能
- 场景生命周期管理
- 场景方法
- 添加图层
下一步
场景初始化完成后,你可以:
1. 添加点图层显示数据 2. 添加交互控件 3. 处理用户交互事件
CSV 数据源
技能描述
使用 CSV(逗号分隔值)格式的数据作为图层数据源,适合处理表格数据。
何时使用
- ✅ 数据来自 Excel、数据库导出
- ✅ 简单的点位数据
- ✅ 统计数据、业务数据
- ✅ 需要在 Excel 中编辑数据
- ✅ 数据量较大但结构简单
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格式要求:
- 第一行为字段名(表头)
- 每行数据字段数量一致
- 使用逗号分隔字段
- 字段值包含逗号时需要用引号包裹
代码示例
基础用法 - 点数据
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
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 异步加载
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();
});自定义分隔符
// 使用制表符分隔的 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);处理带引号的字段
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);数据类型转换
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 数据(起点-终点)
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 | ',' | 字段分隔符 |
数据示例
点位数据
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轨迹数据(路径点)
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统计数据(区域)
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
解决方案:
// 在读取时指定编码
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',
},
});
});或使用工具转换编码:
# 使用 iconv 转换编码
iconv -f GB2312 -t UTF-8 input.csv > output.csv2. 数据不显示
检查清单:
- ✅ 字段名是否正确(区分大小写)
- ✅ 坐标值是否为数字
- ✅ 是否有空行或格式错误
- ✅ parser 配置是否正确
// 调试:打印解析后的数据
layer.on('add', () => {
console.log('图层数据:', layer.getSource().data);
});3. 数字被当作字符串
CSV 中所有值默认都是字符串,需要手动转换:
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. 特殊字符处理
包含逗号、引号、换行符的字段需要特殊处理:
name,description,value
"产品A","价格:1,000元",100
"产品B","说明:包含""引号""",200性能优化
1. 大文件处理
// 使用 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. 数据抽稀
layer.source(csvData, {
parser: {
type: 'csv',
x: 'lng',
y: 'lat',
},
transforms: [
{
type: 'filter',
callback: (item, index) => {
// 只显示每 10 条数据
return index % 10 === 0;
},
},
],
});CSV 转 GeoJSON
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(推荐)
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
import * as d3 from 'd3';
d3.csv('/data/cities.csv').then((data) => {
layer.source(data, {
parser: {
type: 'json',
x: 'lng',
y: 'lat',
},
});
});最佳实践
1. 字段命名规范
# 推荐:使用英文字段名
lng,lat,name,value,type
# 不推荐:使用中文字段名
经度,纬度,名称,数值,类型2. 数据验证
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. 错误处理
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 数据源
- JSON 数据源
- 数据解析配置
- 点图层
- 线图层
参考资源
GeoJSON 数据源
技能描述
使用 GeoJSON 格式的地理数据作为图层数据源,这是 L7 最常用的数据格式。
何时使用
- ✅ 处理地理空间数据(点、线、面)
- ✅ 使用标准的地理数据格式
- ✅ 数据包含几何信息和属性信息
- ✅ 从 GIS 系统导出的数据
- ✅ 行政区划、建筑、道路等矢量数据
GeoJSON 格式说明
GeoJSON 是一种用于编码各种地理数据结构的格式,基于 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], ...]]] |
代码示例
基础用法 - 点数据
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
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
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
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
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);带孔洞的面
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
// 原始数据
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' },
});使用工具库转换
// 使用 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'
// 正确的格式
coordinates: [120.19, 30.26]; // [经度, 纬度]
// 错误的格式
coordinates: [30.26, 120.19]; // [纬度, 经度] ❌2. 面不闭合
多边形的首尾坐标必须相同:
// 正确 - 闭合
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 格式:
4. 性能优化
对于复杂的 GeoJSON 数据:
// 简化几何形状
layer.source(data, {
parser: {
type: 'geojson',
},
transforms: [
{
type: 'simplify',
tolerance: 0.01, // 简化容差
},
],
});GeoJSON 规范
Feature 必需字段
{
"type": "Feature", // 必需
"geometry": {}, // 必需
"properties": {} // 可选,但通常包含
}FeatureCollection 结构
{
"type": "FeatureCollection", // 必需
"features": [] // 必需,Feature 数组
}坐标顺序
- 经度(Longitude)在前,范围: -180 ~ 180
- 纬度(Latitude)在后,范围: -90 ~ 90
- 格式:
[经度, 纬度]或[lng, lat]
环绕方向
- 外环: 逆时针
- 内环(孔洞): 顺时针
最佳实践
1. 数据结构清晰
// 推荐:属性语义化
{
"type": "Feature",
"properties": {
"id": "330100",
"name": "杭州市",
"type": "city",
"population": 1200,
"gdp": 18000
},
"geometry": {...}
}2. 合理使用属性
// 在 properties 中存储可视化需要的数据
layer.color('type', {...})
layer.size('population', [5, 20])3. 数据分层
// 不同类型的数据使用不同图层
const cityLayer = new PointLayer().source(cityGeoJSON);
const provinceLayer = new PolygonLayer().source(provinceGeoJSON);相关技能
- JSON 数据源
- CSV 数据源
- 数据解析配置
- 点图层
- 线图层
- 面图层
参考资源
JSON 数据源
技能描述
使用 JSON(JavaScript Object Notation)格式的数据作为图层数据源,适合结构化的业务数据。
何时使用
- ✅ API 返回的数据
- ✅ 前端 JavaScript 对象
- ✅ 业务数据、统计数据
- ✅ 配置文件数据
- ✅ 不需要地理拓扑结构的简单数据
JSON 格式说明
JSON 是一种轻量级的数据交换格式,易于阅读和编写。
基本格式
[
{
"lng": 120.19,
"lat": 30.26,
"name": "杭州",
"value": 100,
"type": "city"
},
{
"lng": 121.47,
"lat": 31.23,
"name": "上海",
"value": 200,
"type": "city"
}
]代码示例
基础用法 - 点数据
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 加载数据
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);
});异步加载数据
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();
});嵌套对象数据
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)
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 数据(起点-终点)
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);使用坐标数组
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 | 路径坐标数组字段 |
数据转换
自定义数据转换
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);过滤数据
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
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 配置不一致
解决方案:
// 检查字段名
console.log('数据结构:', data[0]);
// 确保字段名正确
layer.source(data, {
parser: {
type: 'json',
x: 'longitude', // 注意大小写
y: 'latitude',
},
});2. 嵌套字段访问
问题: 无法访问嵌套对象的字段
解决方案:
// 使用点号访问嵌套字段
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. 数据类型错误
问题: 坐标值是字符串而不是数字
解决方案:
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. 异步数据加载失败
解决方案:
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. 数据分页加载
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. 数据缓存
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. 数据压缩
// 服务端返回压缩数据
async function loadCompressedData() {
const response = await fetch('/api/data.gz', {
headers: {
'Accept-Encoding': 'gzip',
},
});
const data = await response.json();
// 使用数据
}数据验证
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. 统一数据结构
// 推荐:统一的字段命名
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. 错误处理
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. 数据更新
// 动态更新数据
function updateLayerData(newData) {
layer.setData(newData);
}
// 定时更新
setInterval(async () => {
const newData = await fetch('/api/data/latest').then((r) => r.json());
updateLayerData(newData);
}, 5000);相关技能
- GeoJSON 数据源
- CSV 数据源
- 数据解析配置
- 点图层
- 线图层
参考资源
L7 Components Reference
本文档整合了 L7 的所有交互组件使用指南。
Popup - 信息弹窗
详见 popup.md - 完整的 Popup 组件使用指南,包括基础用法、高级配置、样式定制等。
快速示例
import { Popup } from '@antv/l7';
const popup = new Popup({
closeButton: true,
})
.setLnglat([120.19, 30.26])
.setHTML('<h3>标题</h3><p>内容</p>');
scene.addPopup(popup);事件绑定
layer.on('click', (e) => {
const popup = new Popup().setLnglat(e.lnglat).setHTML(`<div>${e.feature.properties.name}</div>`);
scene.addPopup(popup);
});Marker - 标注点
自定义 HTML 标注,支持拖拽。
基础用法
import { Marker } from '@antv/l7';
const marker = new Marker().setLnglat([120.19, 30.26]);
scene.addMarker(marker);自定义样式
const el = document.createElement('div');
el.className = 'custom-marker';
el.innerHTML = '<i class="icon"></i>';
const marker = new Marker({
element: el,
}).setLnglat([120.19, 30.26]);
scene.addMarker(marker);拖拽标注
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 - 缩放控件
import { Zoom } from '@antv/l7';
const zoom = new Zoom({
position: 'topright',
});
scene.addControl(zoom);Scale Control - 比例尺
import { Scale } from '@antv/l7';
const scale = new Scale({
position: 'bottomleft',
});
scene.addControl(scale);Layer Control - 图层控制
import { LayerControl } from '@antv/l7';
const layerControl = new LayerControl({
layers: [
{ layer: pointLayer, name: '点图层' },
{ layer: lineLayer, name: '线图层' },
],
});
scene.addControl(layerControl);Legend - 图例
自定义图例组件。
基础图例
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);渐变图例
const legend = new Legend({
position: 'bottomright',
type: 'gradient',
items: [
{ color: '#FFF5B8', value: '0' },
{ color: '#FFAB5C', value: '50' },
{ color: '#FF6B3B', value: '100' },
],
});
scene.addControl(legend);组件组合使用
实际应用中通常组合使用多个组件:
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(`<h3>${e.feature.properties.name}</h3>`);
scene.addPopup(popup);
});常见问题
1. Popup 位置偏移
使用 offset 调整位置:
const popup = new Popup({
offset: [0, -10], // x, y 偏移量
});2. Marker 自定义图标
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 - 事件处理详细指南
- popup.md - Popup 完整文档
Related skills
FAQ
Which base maps does AntV L7 support?
It integrates with AMap (GaodeMap), Mapbox, Maplibre, or a standalone L7 Map.
What data formats can L7 parse?
GeoJSON, CSV, JSON, and raster sources such as GeoTIFF.