简介
基于 WebGL 的开源大规模地理空间数据可化视分析开发框架。
L7 中的 L 代表 Location,7 代表世界七大洲。专注数据可视化表达,通过颜色、大小、纹理,方向,体积等视觉变量设置实现从数据到信息清晰,有效的表达。
L7 能够满足常见的地图图表,BI 系统的可视化分析、以及 GIS,交通,电力,国土,农业,城市等领域的空间信息管理,分析等应用系统开发需求。
教程
特点
- 数据驱动可视化展示
数据驱动,灵活数据映射,从数到形,支持丰富的地图可视化类型,更好洞察数据。
- 2D,3D 一体化的海量数据高性能渲染
海量空间数据实时,可交互,动态渲染,
- 简单灵活的数据接入
支持 CSV,JSON,GeoJSON 等数据格式接入,可以根据需求自定义数据格式,无需复杂的空间数据转换。
- 多地图底图支持,支持离线内网部署
屏蔽不同底图之间的差异,用户只需要关注数据层表达,交互。高德地图国内合法合规的地理底图,Mapbox 满足国际化业务需求。
快速上手
安装
- npm引入
| |
| npm install --save @antv/l7 |
| |
| npm install @antv/l7-maps |
| |
| npm install @antv/l7-draw |
使用:
| <script src="./node_modules/@antv/l7/dist/l7.js"></script> |
| |
| const scene = new L7.Scene({ |
| id: 'map', |
| map: new L7.GaodeMap({ |
| style: 'dark', |
| center: [110.770672, 34.159869], |
| pitch: 45, |
| }), |
| }); |
模块内:
| import { Scene } from '@antv/l7'; |
| import { GaodeMap } from '@antv/l7-maps'; |
- cnd引入
| |
| <script src="https://unpkg.com/@antv/l7"></script> |
| |
| <script src="https://unpkg.com/@antv/l7@2.0.11"></script> |
| |
| <script src="https://unpkg.com/@antv/l7-draw@3.0.28/dist/l7-draw.min.js"></script> |
| const { Scene, GaodeMap } = L7 |
| const scene = new Scene({ |
| id: 'map', |
| map: new GaodeMap({ |
| style: 'dark', |
| center: [104.288144, 31.239692], |
| zoom: 4.4, |
| }), |
| }) |
初始化地图
- 引入L7相关的库文件
- 准备地图容器
- 创建场景实例
| |
| <script src="https://unpkg.com/@antv/l7"></script> |
| |
| <body> |
| |
| <div id="map"></div> |
| |
| <script> |
| |
| const { Scene, GaodeMap } = L7 |
| const scene = new Scene({ |
| id: 'map', |
| map: new GaodeMap({ |
| style: 'dark', |
| center: [104.288144, 31.239692], |
| zoom: 4.4, |
| }), |
| }) |
| </script> |
| </body> |
- 场景Scene: 包含地图, 控件, 组件, 加载资源的全局对象. 通过scene可以获取到操作地图需要的所有内容
- 地图: 支持
Mapbox
和Gaode
地图
添加图层
- 面图层
| const { Scene, GaodeMap, PolygonLayer } = L7 |
| |
| const scene = new Scene({ |
| id: 'map', |
| map: new GaodeMap({ |
| style: 'normal', |
| center: [114.3, 30.5], |
| zoom: 5, |
| |
| }), |
| }) |
| |
| fetch('./data/province.json') |
| .then((res) => res.json()) |
| .then((data) => { |
| |
| const chinaPolygonLayer = new PolygonLayer({ |
| autoFit: true, |
| }) |
| .source(data) |
| .color('name', [ |
| 'rgb(239,243,255)', |
| 'rgb(189,215,231)', |
| 'rgb(107,174,214)', |
| 'rgb(49,130,189)', |
| 'rgb(8,81,156)', |
| ]) |
| |
| |
| scene.addLayer(chinaPolygonLayer) |
| }) |
- 线图层
| |
| const { Scene, GaodeMap, PolygonLayer, LineLayer } = L7 |
| const scene = new Scene({ |
| id: 'map', |
| map: new GaodeMap({ |
| style: 'dark', |
| center: [104.288144, 31.239692], |
| zoom: 4.4, |
| }), |
| }) |
| |
| scene.on('loaded', () => { |
| fetch('./data/province.json') |
| .then((res) => res.json()) |
| .then((data) => { |
| const chinaPolygonLayer = new PolygonLayer({ |
| autoFit: true, |
| }) |
| .source(data) |
| .color('name', [ |
| 'rgb(239,243,255)', |
| 'rgb(189,215,231)', |
| 'rgb(107,174,214)', |
| 'rgb(49,130,189)', |
| 'rgb(8,81,156)', |
| ]) |
| scene.addLayer(chinaPolygonLayer) |
| |
| const layer2 = new LineLayer({ |
| zIndex: 2, |
| }) |
| .source(data) |
| .color('rgb(93,112,146)') |
| .size(0.6) |
| .style({ |
| opacity: 1, |
| }) |
| scene.addLayer(layer2) |
| }) |
| }) |
- 标注文本
| fetch( |
| "https://gw.alipayobjects.com/os/bmw-prod/c4a6aa9d-8923-4193-a695-455fd8f6638c.json" // 标注数据 |
| ) |
| .then(res => res.json()) |
| .then(data => { |
| const labelLayer = new PointLayer({ |
| zIndex: 5 |
| }) |
| .source(data, { |
| parser: { |
| type: "json", |
| coordinates: "center" |
| } |
| }) |
| .color("#fff") |
| .shape("name", "text") |
| .size(12) |
| .style({ |
| opacity: 1, |
| stroke: "#fff", |
| strokeWidth: 0, |
| padding: [5, 5], |
| textAllowOverlap: false |
| }) |
| |
| scene.addLayer(labelLayer) |
| }) |
L7组件
L7 中的 Component 主要包含以下三种类型:
- Control 控件类型:指的是悬停在地图四周,可以对地图以及图层等元素进行信息呈现或交互的组件。
- Popup 气泡类型:用于在地图上指定经纬度位置展示气泡,气泡内容完全交由开发者自定义。
- Marker 类型:与 Popup 相似,不同的是 Marker 展示的内容不会在气泡内展示,而是完全交由开发者自定义。
Control
- Logo
| const { Scene, GaodeMap, Logo } = L7 |
| |
| const scene = new Scene({ |
| id: 'map', |
| map: new GaodeMap({ |
| style: 'dark', |
| center: [104.288144, 31.239692], |
| zoom: 4.4, |
| logo: true, |
| }), |
| logoVisible: false, |
| }) |
| |
| |
| scene.on('loaded', () => { |
| const logo = new Logo({ |
| |
| img: 'https://img.gejiba.com/images/dfdb6db1623eb881e724f58d9a366af8.png', |
| |
| href: 'http://www.x-zd.com/', |
| }) |
| scene.addControl(logo) |
| }) |
- Zoom
| const { Zoom } = L7; |
| scene.on('loaded', () => { |
| const zoom = new Zoom({ |
| position: 'bottomright', |
| zoomInTitle: '放大', |
| zoomOutTitle: '缩小', |
| }); |
| scene.addControl(zoom); |
| }); |
- Scale
| const { Scale } = L7; |
| scene.on('loaded', () => { |
| const scale = new Scale({ |
| position: 'bottomleft', |
| metric: true, |
| imperial: false, |
| maxWidth: 100, |
| }) |
| scene.addControl(scale) |
| }) |
- Fullscreen
| const { Fullscreen } = L7 |
| scene.on('loaded', () => { |
| const fullscreen = new Fullscreen({ |
| btnText: '全屏', |
| exitBtnText: '退出全屏', |
| }); |
| scene.addControl(fullscreen) |
| }); |
- ExportImage
| const { ExportImage } = L7 |
| scene.on('loaded', () => { |
| const exportImage = new ExportImage({ |
| onExport: (base64) => { |
| alert(base64) |
| }, |
| }) |
| scene.addControl(exportImage) |
| }) |
- 图层切换
| const { PointLayer, LayerSwitch } = L7 |
| scene.on('loaded', () => { |
| const layer = new PointLayer({ |
| name: '点图层', |
| }) |
| layer |
| .source({ |
| type: 'FeatureCollection', |
| features: [ |
| { |
| type: 'Feature', |
| geometry: { |
| type: 'Point', |
| coordinates: [114.3, 30.5], |
| }, |
| }, |
| ], |
| }) |
| .shape('circle') |
| .color('#333') |
| .style({ |
| opacity: 0.5, |
| strokeWidth: 1, |
| }) |
| .size(20) |
| .active(true) |
| scene.addLayer(layer) |
| |
| const layerSwitch = new LayerSwitch({ |
| layers: [layer], |
| }) |
| scene.addControl(layerSwitch) |
| }) |
- MouseLocation
| const { MouseLocation } = L7 |
| scene.on('loaded', () => { |
| const mouseLocation = new MouseLocation({ |
| transform: (position) => { |
| return position |
| }, |
| }) |
| scene.addControl(mouseLocation) |
| }) |
- 地图主题
| const { MapTheme } = L7; |
| scene.on('loaded', () => { |
| const mapTheme = new MapTheme({}); |
| scene.addControl(mapTheme); |
| }); |
Marker
- 基本示例
| <div id="map"></div> |
| <script> |
| const { Scene, GaodeMap, Marker } = L7 |
| |
| const scene = new Scene({ |
| id: 'map', |
| map: new GaodeMap({ |
| style: 'normal', |
| center: [114.3, 30.5], |
| zoom: 5, |
| }), |
| }) |
| |
| scene.on('loaded', () => { |
| const marker = new Marker() |
| |
| marker.setLnglat([114.3, 30.5]) |
| |
| scene.addMarker(marker) |
| }) |
| </script> |
| </body> |
| </html> |
- marker结合popup
| <script> |
| const { Scene, GaodeMap, Marker, Popup } = L7 |
| |
| const scene = new Scene({ |
| id: 'map', |
| map: new GaodeMap({ |
| style: 'normal', |
| center: [114.3, 30.5], |
| zoom: 5, |
| }), |
| }) |
| |
| scene.on('loaded', () => { |
| const marker = new Marker() |
| marker.setLnglat([114.3, 30.5]) |
| |
| const popup = new Popup({ |
| offsets: [0, 50], |
| }).setText('武汉') |
| marker.setPopup(popup) |
| |
| scene.addMarker(marker) |
| }) |
| </script> |
- 自定义marker
| #marker { |
| text-align: center; |
| background-color: #652e8092; |
| line-height: 50px; |
| color: #fff; |
| width: 50px; |
| height: 50px; |
| border-radius: 50%; |
| cursor: pointer; |
| } |
| |
| const element = document.createElement('div') |
| element.id = 'marker' |
| |
| const marker = new Marker({ |
| element: element, |
| }) |
| marker.setLnglat([114.3, 30.5]) |
| scene.addMarker(marker) |
- Marker图层
| <style> |
| * { |
| margin: 0; |
| padding: 0; |
| } |
| .marker { |
| text-align: center; |
| background-color: #652e8092; |
| line-height: 50px; |
| color: #fff; |
| width: 50px; |
| height: 50px; |
| border-radius: 50%; |
| cursor: pointer; |
| } |
| </style> |
| </head> |
| <body> |
| <div id="map"></div> |
| |
| <script> |
| const { Scene, GaodeMap, Marker, MarkerLayer } = L7 |
| |
| const scene = new Scene({ |
| id: 'map', |
| map: new GaodeMap({ |
| style: 'normal', |
| center: [114.3, 30.5], |
| zoom: 5, |
| }), |
| }) |
| |
| |
| const data = [ |
| { |
| lng: 114.3, |
| lat: 30.5, |
| name: '武汉', |
| }, |
| { |
| lng: 112.59, |
| lat: 28.12, |
| name: '长沙', |
| }, |
| ] |
| scene.on('loaded', () => { |
| |
| const markerLayer = new MarkerLayer({}) |
| for (const city of data) { |
| const { lng, lat, name } = city |
| |
| const element = document.createElement('div') |
| element.className = 'marker' |
| element.innerHTML = name |
| |
| const marker = new Marker({ |
| element: element, |
| }) |
| marker.setLnglat([lng, lat]) |
| markerLayer.addMarker(marker) |
| } |
| |
| |
| scene.addMarkerLayer(markerLayer) |
| }) |
| </script> |
| </body> |
| </html> |
| |
图层
点图层
- 符号点
| const { Scene, GaodeMap, PointLayer } = L7 |
| |
| const scene = new Scene({ |
| id: 'map', |
| map: new GaodeMap({ |
| style: 'normal', |
| center: [114.3, 30.5], |
| zoom: 10, |
| }), |
| }) |
| |
| const data = [ |
| { |
| lng: 114.3, |
| lat: 30.5, |
| name: '白沙洲', |
| }, |
| { |
| lng: 114.4, |
| lat: 30.45, |
| name: '中南路', |
| }, |
| ] |
| |
| scene.on('loaded', () => { |
| scene.addImage('police', './images/avatar.png') |
| const policeLayer = new PointLayer() |
| .source(data, { |
| parser: { |
| type: 'json', |
| x: 'lng', |
| y: 'lat', |
| }, |
| }) |
| .shape('police') |
| .size(16) |
| scene.addLayer(policeLayer) |
| }) |
- 圆环动图
| const data = { |
| type: 'FeatureCollection', |
| features: [ |
| { |
| type: 'Feature', |
| geometry: { |
| type: 'Point', |
| coordinates: [114.3, 30.5], |
| }, |
| }, |
| ], |
| } |
| |
| scene.on('loaded', () => { |
| const pointLayer = new PointLayer() |
| .source(data) |
| .animate(true) |
| .color('#ff0000') |
| .shape('circle') |
| .size(100) |
| .active(true) |
| scene.addLayer(pointLayer) |
| }) |
- 雷达图
| scene.on('loaded', () => { |
| const pointLayer = new PointLayer() |
| .source(data) |
| .animate(true) |
| .color('#ff0000') |
| .shape('radar') |
| .size(100) |
| .active(true) |
| scene.addLayer(pointLayer) |
| }) |
线图层
- 流线动图
| const { Scene, GaodeMap, LineLayer } = L7 |
| const scene = new Scene({ |
| id: 'map', |
| map: new GaodeMap({ |
| style: 'normal', |
| center: [114.3, 30.5], |
| zoom: 10, |
| }), |
| }) |
| |
| const data = { |
| type: 'FeatureCollection', |
| features: [ |
| { |
| type: 'Feature', |
| geometry: { |
| type: 'LineString', |
| coordinates: [ |
| [114.292975, 30.488542], |
| [114.29292, 30.493947], |
| [114.293779, 30.498206], |
| [114.299404, 30.498046], |
| [114.302326, 30.497993], |
| ], |
| }, |
| }, |
| ], |
| } |
| |
| scene.on('loaded', () => { |
| const layer = new LineLayer() |
| .source(data) |
| .shape('line') |
| .size(2) |
| .color('#f00') |
| .animate({ |
| trailLength: 1, |
| interval: 0.5, |
| duration: 2, |
| }) |
| scene.addLayer(layer) |
| }) |
- 3D弧线图
| scene.on('loaded', () => { |
| scene.setRotation(30) |
| const layer = new LineLayer({ |
| blend: 'normal', |
| }) |
| .source(data) |
| .shape('arc3d') |
| .size(2) |
| .color('#f00') |
| .animate({ |
| trailLength: 1, |
| interval: 0.5, |
| duration: 2, |
| }) |
| scene.addLayer(layer) |
| }) |
面图层
- 深圳智慧城市
| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8" /> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
| <title>Document</title> |
| |
| <link |
| href="https://api.mapbox.com/mapbox-gl-js/v2.9.1/mapbox-gl.css" |
| rel="stylesheet" |
| /> |
| <script src="https://api.mapbox.com/mapbox-gl-js/v2.9.1/mapbox-gl.js"></script> |
| <script src="https://unpkg.com/@antv/l7"></script> |
| <style> |
| * { |
| margin: 0; |
| padding: 0; |
| } |
| body { |
| overflow: hidden; |
| } |
| #map { |
| height: 100vh; |
| } |
| </style> |
| </head> |
| <body> |
| <div id="map"></div> |
| |
| <script> |
| |
| mapboxgl.accessToken = '' |
| const map = new mapboxgl.Map({ |
| container: 'map', |
| style: 'mapbox://styles/mapbox/dark-v10', |
| pitch: 50, |
| center: [114.050008, 22.529272], |
| zoom: 14, |
| }) |
| |
| |
| const { Scene, Mapbox, PolygonLayer } = L7 |
| const scene = new Scene({ |
| id: 'map', |
| map: new Mapbox({ |
| mapInstance: map, |
| }), |
| }) |
| |
| scene.on('loaded', () => { |
| fetch('https://gw.alipayobjects.com/os/basement_prod/972566c5-a2b9-4a7e-8da1-bae9d0eb0117.json' |
| ) |
| .then((res) => res.json()) |
| .then((data) => { |
| |
| const layer = new PolygonLayer() |
| .source(data) |
| .shape('extrude') |
| .size('h10', [100, 120, 160, 200, 260, 500]) |
| .color('h10', [ |
| '#816CAD', |
| '#A67FB5', |
| '#C997C7', |
| '#DEB8D4', |
| '#F5D4E6', |
| '#FAE4F1', |
| '#FFF3FC', |
| ]) |
| .active(true) |
| scene.addLayer(layer) |
| }) |
| }) |
| </script> |
| </body> |
| </html> |
- 水体
| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8" /> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
| <title>Document</title> |
| |
| <script src="https://unpkg.com/@antv/l7"></script> |
| <style> |
| * { |
| margin: 0; |
| padding: 0; |
| } |
| </style> |
| </head> |
| <body> |
| |
| <div id="map"></div> |
| |
| |
| <script> |
| const { Scene, GaodeMap, PolygonLayer } = L7 |
| const scene = new Scene({ |
| id: 'map', |
| map: new GaodeMap({ |
| style: 'normal', |
| center: [120.144689, 30.250433], |
| zoom: 14, |
| token: 'bda17043c3dfbbbfd24900155b3fa909', |
| pitch: 45, |
| }), |
| }) |
| scene.on('loaded', () => { |
| fetch( |
| 'https://gw.alipayobjects.com/os/bmw-prod/67130c6c-7f49-4680-915c-54e69730861d.json' |
| ) |
| .then((data) => data.json()) |
| .then((data) => { |
| const { lakeData } = data |
| const lakeLayer = new PolygonLayer() |
| .source(lakeData) |
| .shape('ocean') |
| .color('#1E90FF') |
| .style({ speed: 0.4 }) |
| .animate(true) |
| scene.addLayer(lakeLayer) |
| }) |
| }) |
| </script> |
| </body> |
| </html> |
图形绘制
绘制点
DrawPoint
| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8" /> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
| <title>Document</title> |
| |
| <script src="https://unpkg.com/@antv/l7"></script> |
| <script src="./node_modules/@antv/l7-draw/dist/l7-draw.js"></script> |
| <style> |
| * { |
| margin: 0; |
| padding: 0; |
| } |
| .btn { |
| padding: 5px; |
| } |
| .enable { |
| position: absolute; |
| top: 0; |
| left: 0; |
| z-index: 99; |
| } |
| .exit { |
| position: absolute; |
| top: 0; |
| left: 100px; |
| z-index: 99; |
| } |
| </style> |
| </head> |
| <body> |
| <button onclick="addPoint()" class="btn enable">开启点绘制</button> |
| <button onclick="exit()" class="btn exit">退出</button> |
| <div id="map"></div> |
| |
| <script> |
| const { Scene, GaodeMap } = L7 |
| const { DrawPoint, DrawEvent } = L7.Draw |
| const scene = new Scene({ |
| id: 'map', |
| map: new GaodeMap({ |
| style: 'normal', |
| center: [114.3, 30.5], |
| zoom: 10, |
| }), |
| }) |
| |
| let draw = null |
| scene.on('loaded', () => { |
| |
| draw = new DrawPoint(scene, {}) |
| }) |
| |
| function addPoint() { |
| draw.enable() |
| } |
| function exit() { |
| draw.disable() |
| } |
| </script> |
| </body> |
| </html> |
绘制线
DrawLine
| const { Scene, GaodeMap } = L7 |
| const { DrawLine, DrawEvent } = L7.Draw |
| |
| const scene = new Scene({ |
| id: 'map', |
| map: new GaodeMap({ |
| style: 'normal', |
| center: [114.3, 30.5], |
| zoom: 10, |
| }), |
| }) |
| scene.on('loaded', () => { |
| const drawLine = new DrawLine(scene, { |
| |
| distanceOptions: { |
| showTotalDistance: false, |
| showDashDistance: true, |
| format: (meters) => { |
| if (meters >= 1000) { |
| return +(meters / 1000).toFixed(2) + 'km' |
| } else { |
| return +meters.toFixed(2) + 'm' |
| } |
| }, |
| }, |
| }) |
| drawLine.enable() |
| }) |
绘制面
DrawPolygon
| const { DrawPolygon, DrawEvent } = L7.Draw |
| |
| scene.on('loaded', () => { |
| const drawPolygon = new DrawPolygon(scene, { |
| areaOptions: { |
| format: (squareMeters) => { |
| return squareMeters > 1000000 |
| ? `${+(squareMeters / 1000000).toFixed(2)}km²` |
| : `${+squareMeters.toFixed(2)}m²` |
| }, |
| }, |
| }) |
| drawPolygon.enable() |
| }) |
绘制矩形
DrawRect
| const { DrawEvent, DrawRect } = L7.Draw |
| scene.on('loaded', () => { |
| const drawRect = new DrawRect(scene, {}); |
| drawRect.enable(); |
| |
| drawRect.on(DrawEvent.Change, (allFeatures) => { |
| console.log(allFeatures); |
| }); |
| }); |
绘制图
DrawCircle
| const { DrawEvent, DrawCircle } = L7.Draw |
| scene.on('loaded', () => { |
| const drawCircle = new DrawCircle(scene, {}); |
| drawCircle.enable(); |
| |
| drawCircle.on(DrawEvent.Change, (allFeatures) => { |
| console.log(allFeatures); |
| }); |
| }); |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理