Lifestyle & Tools
#web
antv-l7
基于 WebGL 的大规模地理空间数据可视化引擎。适用于: (1) 创建交互式 WebGL 地图应用 (2) 可视化地理空间数据(点、线、面、热力图) (3) 构建位置数据驾驶舱 (4) 添加地图图层、交互和动画效果 (5) 处理并展示 GeoJSON、CSV 等空间数据
DeepseekModel
Curated skill
Quality Excellent · 90
v1.0.0
Get
https://deepseekmodel.com/api/download.php?id=antvis-l7-skills-l7-single-skill-md&format=skill
Download .skill
Standard format with system_prompt and model_config, ready for any agent framework
The actual content of the system_prompt field in the .skill file.
name antv-l7 title AntV L7 地理空间可视化 description 基于 WebGL 的大规模地理空间数据可视化引擎。适用于: (1) 创建交互式 WebGL 地图应用 (2) 可视化地理空间数据(点、线、面、热力图) (3) 构建位置数据驾驶舱 (4) 添加地图图层、交互和动画效果 (5) 处理并展示 GeoJSON、CSV 等空间数据 license MIT AntV L7 地理空间可视化 AntV L7 是基于 WebGL 的大规模地理空间数据可视化引擎,支持多种地图底图和丰富的可视化图层类型。 ⚠️ 重要约束 作用域声明:以下约束仅适用于 WeaveFox(本仓库)环境。 在 WeaveFox 环境下,高德地图配置禁止传入 token 参数。 在 WeaveFox 项目中,L7 已配置默认 token。在该环境下, GaodeMap 配置中 不要 设置 token 字段,否则会导致地图加载失败。 // ✅ 正确 - 不传 token new GaodeMap ({ center : [ 120.19 , 30.26 ], zoom : 10 , style : 'light' , }); // ❌ 错误 - 禁止传入 token new GaodeMap ({ center : [ 120.19 , 30.26 ], zoom : 10 , style : 'light' , token : 'xxx' , // 不要这样做! }); 选型指南 简单点位展示(< 100 个点) 优先使用 Marker ,而非 PointLayer: import { Marker , Popup } from '@antv/l7' ; let currentPopup = null ; // ✅ 推荐 - 少量点位使用 Marker data. forEach ( ( point ) => { const marker = new Marker ({ color : '#5B8FF9' , draggable : false , }). setLnglat ([point. lng , point. lat ]); marker. on ( 'click' , () => { // 统一单实例 popup:先移除旧 popup,再创建新 popup if (currentPopup) scene. removePopup (currentPopup); currentPopup = new Popup ({ anchor : 'bottom' , className : 'outing-popup' , lngLat : { lng : point. lng , lat : point. lat }, html : `<div> ${point.name} </div>` , }); scene. addPopup (currentPopup); }); // 必须使用 scene.addMarker,否则地图缩放/平移时 Marker 不会更新位置 scene. addMarker (marker); }); Marker vs PointLayer 对比: 特性 Marker PointLayer 适用场景 < 100 个点位 > 100 个点位/大规模数据 性能 DOM 渲染,轻量 WebGL 渲染,高性能 交互 原生 DOM 事件,灵活 需绑定图层事件 自定义 支持任意 HTML/CSS 受限于 L7 样式配置 Popup 内置支持 需额外配置 大规模数据可视化(> 100 个点) 使用 PointLayer 进行 WebGL 渲染: import { PointLayer } from '@antv/l7' ; // ✅ 推荐 - 大量点位使用 PointLayer const pointLayer = new PointLayer () . source (data, { parser : { type : 'json' , x : 'lng' , y : 'lat' }, }) . shape ( 'circle' ) . size ( 10 ) . color ( '#5B8FF9' ); scene. addLayer (pointLayer); React 最佳实践 核心原则 单次初始化 :地图场景只初始化一次 图层分离 :数据更新时只更新图层,不重建地图 完整清理 :组件卸载时清理所有资源 错误处理 :捕获初始化异常 硬性规则(防止重复初始化) 以下规则为强约束,生成 React + L7 代码时必须遵守: 初始化 effect 必须空依赖 :创建 new Scene() 的 useEffect 依赖必须是 [] 。 禁止在初始化 effect 依赖中放业务状态 :例如 selectedId 、筛选条件、分页参数。 禁止在初始化 effect 依赖中放易变回调 :例如来自父组件的 onSelect 、 onClick 、 onChange 。 状态更新走独立 effect :选中态、数据源、样式变化只能更新图层/Marker,不得 destroy + new Scene 。 最新值使用 ref : loaded 回调里若需要最新 props,使用 latestXxxRef.current ,不要依赖闭包旧值。 清理只在卸载时执行 : scene.destroy() 只能放在初始化 effect 的 cleanup 中,不能由状态变化触发。 严格区分初始化与更新职责 :初始化负责 Scene/Layer 首建,更新负责 setData 、更新 Marker 或样式。 Popup 锚点默认规则 :点位/Marker 场景默认使用 anchor: 'bottom' ,让弹窗显示在点位上方。 Popup 方案必须单一 :统一使用 scene.addPopup(...) ,禁止与 marker.setPopup(...) 、 popup.addTo(scene) 混用。 Popup 必须单实例 :创建新 popup 前先 scene.removePopup(oldPopup) ,避免页面出现多个弹窗。 Marker 形态优先尖角样式 :优先使用“气泡主体 + 尖角(pointer)”图标,尖角必须指向真实经纬度点。 Marker 锚点必须与尖角一致 :尖角在元素底部时, anchor 必须使用 'bottom' ,禁止使用 'center' 导致点位偏移。 Marker 文案对齐规则 :文字描述必须在 Marker 主体内水平居中,主体中心与实际点位保持视觉对称。 多行文案稳定规则 :文案允许换行时,主体必须设置 max-width 与 word-break ,且尖角保持 margin: 0 auto 居中,防止尖角偏离点位。 应用 UI 层级规则 :地图应用的信息面板、Sidebar、模态框等非地图组件必须设置 z-index >= 1000 ,确保显示在地图 logo 和其他地图固定元素之上。 Popup 关闭按钮规则 :创建 Popup 时必须启用 closeButton: true ,允许用户点击关闭按钮关闭弹窗,提升交互体验。 Popup 相对位置规则 :Popup 必须显示在 Marker/点位上方,需使用 offset: [0, -40] 向上移动,避免与 Marker 气泡重叠遮挡。 反例(禁止): // 会导致 selectedId 变化时反复销毁并重建 Scene useEffect ( () => { const scene = new Scene (...); return () => scene. destroy (); }, [selectedId, onSelectLocation]); 正例(推荐): // 只初始化一次 useEffect ( () => { const scene = new Scene (...); return () => scene. destroy (); }, []); // 状态变化只更新图层,不重建 Scene useEffect ( () => { pointLayerRef. current ?. setData (nextData); }, [nextData]); 说明:React 18 开发环境 StrictMode 会对 mount 做额外检查,可能看到两次初始化日志。只要初始化 effect 为空依赖且职责正确,生产环境不会因此重复重建。 Marker 样式约束(尖角指向点位) 当使用自定义 Marker DOM 时,默认采用“主体 + 尖角”结构: function createMarkerElement ( point : { name: string ; color: string } ) { const el = document . createElement ( 'div' ); el. style . cssText = 'position: relative; transform: translateY(-8px);' ; el. innerHTML = ` <div style=" min-width: 88px; padding: 6px 10px; border-radius: 999px; background: ${point.color} ; color: #fff; font-size: 12px; font-weight: 600; text-align: center; line-height: 1.2; box-shadow: 0 4px 12px rgba(0,0,0,0.2); position: relative; max-width: 160px; white-space: normal; word-break: break-word; "> ${point.name} </div> <div style=" width: 0; height: 0; border-left: 6px solid transparent; border-right: 6px solid transparent; border-top: 8px solid ${point.color} ; margin: 0 auto; "></div> ` ; return el; } const marker = new Marker ({ element : createMarkerElement (point), anchor : 'bottom' , }). setLnglat ([point. lng , point. lat ]); 约束说明: 尖角是点位指示器,必须位于元素底部中心。 文案容器使用 text-align: center ,并保持左右内边距对称。 多行文案时必须限制最大宽度并允许换行,避免单行过长导致主体偏斜。 为避免遮挡点位,元素整体可上移少量像素(例如 translateY(-8px) )。 使用 Marker(< 100 个点) 'use client' ; import { useEffect, useRef, useCallback } from 'react' ; import { Scene , Marker , Popup } from '@antv/l7' ; import { GaodeMap } from '@antv/l7-maps' ; interface Location { id : string ; lng : number ; lat : number ; name : string ; } export function MarkerMap ( { locations }: { locations: Location[] } ) { const mapContainerRef = useRef< HTMLDivElement >( null ); const sceneRef = useRef< Scene | null >( null ); const markersRef = useRef< Marker []>([]); const popupRef = useRef< Popup | null >( null ); const isInitializedRef = useRef ( false ); // 创建/更新 Markers const updateMarkers = useCallback ( ( scene : Scene , data : Location [] ) => { // 1. 清理旧 marker(避免重复渲染和内存泄漏) markersRef. current . forEach ( marker => { marker. remove (); }); markersRef. current = []; // 2. 创建新 marker data. forEach ( point => { const marker = new Marker ({ color : '#5B8FF9' }) . setLnglat ([point. lng , point. lat ]); marker. on ( 'click' , () => { if (popupRef. current ) { scene. removePopup (popupRef. current ); } popupRef. current = new Popup ({ anchor : 'bottom' , closeButton : true , offset : [ 0 , - 40 ], lngLat : { lng : point. lng , lat : point. lat }, html : `<div> ${point.name} </div>` , }); scene. addPopup (popupRef. current ); }); // 必须使用 scene.addMarker,否则地图缩放/平移时 Marker 不会联动 scene. addMarker (marker); markersRef. current . push (marker); }); }, []); useEffect ( () => { if (!mapContainerRef. current || isInitializedRef. current ) return ; const scene = new Scene ({ id : mapContainerRef. current , map : new GaodeMap ({ center : [ 105 , 35 ], zoom : 4 , style : 'light' , }), }); sceneRef. current = scene; isInitializedRef. current = true ; scene. on ( 'loaded' , () => { updateMarkers (scene, locations); }); return () => { if (sceneRef. current ) { if (popupRef. current ) { sceneRef. current . removePopup (popupRef. current ); popupRef. current = null ; } markersRef. current . forEach ( marker => marker. remove ()); markersRef. current = []; sceneRef. current . destroy (); sceneRef. current = null ; isInitializedRef. current = false ; } }; }, []); // 数据更新时重建 markers useEffect ( () => { if (isInitializedRef. current && sceneRef. current ) { updateMarkers (sceneRef. current , locations); } }, [locations]); return ( < div ref = {mapContainerRef} style = {{ width: ' 100 %', height: ' min ( 70vh , 600px )', minHeight: ' 360px ' }} /> ); } Marker 关键注意点: markersRef 保存实例数组,用于数据更新时清理旧 marker scene.addMarker(marker) 必须用它, .addTo(scene) 不会注册相机事件,导致 marker 与地图不同步 Popup 统一使用 scene.addPopup(...) ,不要和 marker.setPopup(...) 混用 点位弹窗默认 anchor: 'bottom' ,确保弹窗显示在 marker 上方 组件卸载时遍历 markersRef.current 逐一 remove() 使用 PointLayer(> 100 个点) 'use client' ; import { useEffect, useRef, useCallback } from 'react' ; import { Scene , PointLayer , Popup } from '@antv/l7' ; import { GaodeMap } from '@antv/l7-maps' ; interface Location { id : string ; lng : number ; lat : number ; name : string ; color : string ; } interface MapProps { locations : Location []; onLocationClick ?: ( location : Location ) => void ; } export function TravelMap ( { locations, onLocationClick }: MapProps ) { const mapContainerRef = useRef< HTMLDivElement >( null ); const sceneRef = useRef< Scene | null >( null ); const pointLayerRef = useRef< PointLayer | null >( null ); const popupRef = useRef< Popup | null >( null ); const isInitializedRef = useRef ( false ); const latestLocationsRef = useRef< Location []>(locations); // 保持最新数据,避免 scene loaded 回调拿到旧闭包数据 useEffect ( () => { latestLocationsRef. current = locations; }, [locations]); // 初始化地图(只执行一次) const initMap = useCallback ( () => { if (!mapContainerRef. current || isInitializedRef. current ) return ; try { const scene = new Scene ({ id : mapContainerRef. current , map : new GaodeMap ({ center : [ 105 , 35 ], zoom : 4 , style : 'light' , }), }); sceneRef. current = scene; isInitializedRef. current = true ; return scene; } catch (error) { console . error ( '地图初始化失败:' , error); throw error; } }, []); // 创建图层 const createLayer = useCallback ( ( scene : Scene , data : Location [] ) => { const pointData = data. map ( loc => ({ lng : loc. lng , lat : loc. lat , name : loc. name , color : loc. color , id : loc. id , })); const pointLayer = new PointLayer () . source (pointData, { parser : { type : 'json' , x : 'lng' , y : 'lat' }, }) . shape ( 'circle' ) . size ( 20 ) . color ( 'color' , ( color : string ) => color) . style ({ opacity : 0.9 , strokeWidth : 2 , stroke : '#ffffff' , });
Keywords that activate this skill. Click one to copy it.
This skill does not provide trigger words.
The downloaded .skill package contains the following fields.
| Field | Description |
|---|---|
| format | Format tag (skill/v1) |
| skill_id | Unique skill ID |
| name | Skill name |
| version | Version |
| description | Description |
| category | Categories (array) |
| trigger_words | Trigger words |
| tags | Tags |
| source | Source |
| source_url | Source URL (this page) |
| exported_at | Exported at (set per download) |
| system_prompt | System prompt body |
| model_config | Model config: provider / model / temperature / max_tokens / top_p |
| examples | Examples |
| install_guide | Import guide for Coze / Dify / Claude / custom frameworks |
The same skill can be exported in different platform formats.