1. 前言

这是一篇由浅入深的AntV L7的学习笔记总结,记述了从了解到使用的一些过程

本文所使用的数据(包括数据处理过程)和代码均有详细描述,所有案例均可复现,甚至大部分代码可直接使用

如果喜欢分页阅读,可以参考下列目录:

因笔者水平有限,如有错误,敬请指正

2. 概述

L7 地理空间数据可视分析引擎是一种基于 WebGL 技术的地理空间数据可视化引擎,可以用于实现各种地理空间数据可视化应用。L7 引擎支持多种数据源和数据格式,包括 GeoJSON、CSV等,可以快速加载和渲染大规模地理空间数据。L7 引擎还提供了丰富的可视化效果和交互功能,包括热力图、等高线图、鼠标交互等,可以帮助用户更好地理解和分析地理空间数据

L7 官网:蚂蚁地理空间数据可视化 | AntV (antgroup.com)

L7 GitHub 仓库:antvis/L7: Large-scale WebGL-powered Geospatial Data Visualization analysis engine (github.com)

L7 官方教程:简介 | L7 (antgroup.com)

L7 官方示例:所有图表 | L7 (antgroup.com)

L7 API文档:场景 Scene | L7 (antgroup.com)

3. 快速入门

3.1 入门示例

L7的主要特点是使用WebGL绘制地图数据,此处主要描述的是L7作为前端GIS库基础功能使用

通过CDN的方式可以快速引入L7:

  1. <script src = 'https://unpkg.com/@antv/l7'></script>

通过NPM的方式引入L7可参考:

  1. npm install @antv/l7

简单起见,下面使用CDN方式引入

3.1.1 加载地图

加载地图:

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  7. <title>Document</title>
  8. <script src='https://unpkg.com/@antv/l7'></script>
  9. </head>
  10. <body>
  11. <div id="map"></div>
  12. <script>
  13. const scene = new L7.Scene({
  14. id: 'map',
  15. map: new L7.GaodeMap({
  16. style: 'dark',
  17. center: [110.770672, 34.159869]
  18. }),
  19. });
  20. </script>
  21. </body>
  22. </html>

结果如下:

L7内置了高德底图API,测试环境下可以直接使用

3.1.2 加载底图

通常,使用栅格瓦片作为底图,L7 的栅格图层支持加载 TMSWMSWMTS 等多种格式的图片瓦片

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  7. <title>Document</title>
  8. <script src='https://unpkg.com/@antv/l7'></script>
  9. <style>
  10. body,
  11. #map {
  12. height: 100vh;
  13. width: 100vw;
  14. margin: 0;
  15. }
  16. </style>
  17. </head>
  18. <body>
  19. <div id="map"></div>
  20. <script>
  21. const scene = new L7.Scene({
  22. id: 'map',
  23. map: new L7.Map({
  24. center: [110.770672, 34.159869],
  25. zoom: 4
  26. }),
  27. });
  28. const url1 =
  29. 'https://t0.tianditu.gov.cn/img_w/wmts?tk=b72aa81ac2b3cae941d1eb213499e15e&';
  30. const layer1 = new L7.RasterLayer({
  31. zIndex: 1,
  32. }).source(url1, {
  33. parser: {
  34. type: 'rasterTile',
  35. tileSize: 256,
  36. wmtsOptions: {
  37. layer: 'img',
  38. tileMatrixset: 'w',
  39. format: 'tiles',
  40. },
  41. },
  42. });
  43. scene.on('loaded', () => {
  44. scene.addLayer(layer1);
  45. });
  46. </script>
  47. </body>
  48. </html>
  • 注意:L7目前只支持 3857 坐标系

结果如下:

3.1.3 加载矢量数据

L7支持 GeoJSON数据

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  7. <title>Document</title>
  8. <script src='https://unpkg.com/@antv/l7'></script>
  9. <style>
  10. body,
  11. #map {
  12. height: 100vh;
  13. width: 100vw;
  14. margin: 0;
  15. }
  16. </style>
  17. </head>
  18. <body>
  19. <div id="map"></div>
  20. <script>
  21. const scene = new L7.Scene({
  22. id: 'map',
  23. map: new L7.GaodeMap({
  24. center: [116.3956, 39.9392],
  25. zoom: 10,
  26. style: 'dark'
  27. })
  28. });
  29. scene.on('loaded', () => {
  30. fetch(
  31. 'https://gw.alipayobjects.com/os/basement_prod/0d2f0113-f48b-4db9-8adc-a3937243d5a3.json'
  32. )
  33. .then(res => res.json())
  34. .then(data => {
  35. const layer = new L7.LineLayer({})
  36. .source(data)
  37. scene.addLayer(layer);
  38. });
  39. });
  40. </script>
  41. </body>
  42. </html>

结果如下:

3.1.4 Marker标注

Marker标注是地图上用来标记信息的常用组件

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  7. <title>Document</title>
  8. <script src='https://unpkg.com/@antv/l7'></script>
  9. </head>
  10. <body>
  11. <div id="map"></div>
  12. <script>
  13. const scene = new L7.Scene({
  14. id: 'map',
  15. map: new L7.GaodeMap({
  16. style: 'light',
  17. center: [110.770672, 34.159869]
  18. }),
  19. });
  20. const marker = new L7.Marker({
  21. color: '#f00'
  22. }).setLnglat([110.770672, 34.159869]);
  23. scene.addMarker(marker);
  24. </script>
  25. </body>
  26. </html>

结果如下:

3.1.5 Popup弹窗

Popup弹窗是地图上用来显示信息的常用组件

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  7. <title>Document</title>
  8. <script src='https://unpkg.com/@antv/l7'></script>
  9. </head>
  10. <body>
  11. <div id="map"></div>
  12. <script>
  13. const scene = new L7.Scene({
  14. id: 'map',
  15. map: new L7.GaodeMap({
  16. style: 'light',
  17. center: [110.770672, 34.159869]
  18. }),
  19. });
  20. const popup = new L7.Popup({
  21. title: '自定义标题',
  22. html: '<p>Popup 示例的自定义内容</p>',
  23. lngLat: {
  24. lng: 110.770672,
  25. lat: 34.159869,
  26. },
  27. });
  28. scene.addPopup(popup);
  29. </script>
  30. </body>
  31. </html>

结果如下:

3.1.6 事件监听

L7支持事件鼠标点击、双击等事件监听

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  7. <title>Document</title>
  8. <script src='https://unpkg.com/@antv/l7'></script>
  9. </head>
  10. <body>
  11. <div id="map"></div>
  12. <script>
  13. const scene = new L7.Scene({
  14. id: 'map',
  15. map: new L7.GaodeMap({
  16. style: 'light',
  17. center: [110.770672, 34.159869]
  18. }),
  19. });
  20. setTimeout(() => {
  21. scene.on('click', (e) => {
  22. console.log(e) // 鼠标左键点击事件
  23. const marker = new L7.Marker({
  24. color: '#f00'
  25. }).setLnglat([e.lnglat.lng, e.lnglat.lat]);
  26. scene.addMarker(marker);
  27. });
  28. }, 1000);
  29. // scene.on('click', (e) => {console.log(e)}); // 直接监听click事件会报错
  30. </script>
  31. </body>
  32. </html>

结果如下:

3.1.7 地图控件

在使用地图时,常常需要使用缩放、比例尺等控件

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  7. <title>Document</title>
  8. <script src='https://unpkg.com/@antv/l7'></script>
  9. <style>
  10. body,
  11. #map {
  12. height: 100vh;
  13. width: 100vw;
  14. margin: 0;
  15. }
  16. </style>
  17. </head>
  18. <body>
  19. <div id="map"></div>
  20. <script>
  21. const scene = new L7.Scene({
  22. id: 'map',
  23. map: new L7.GaodeMap({
  24. center: [116.3956, 39.9392],
  25. zoom: 10,
  26. style: 'light'
  27. })
  28. });
  29. scene.on('loaded', () => {
  30. const zoom = new L7.Zoom();
  31. scene.addControl(zoom);
  32. const scale = new L7.Scale();
  33. scene.addControl(scale);
  34. });
  35. </script>
  36. </body>
  37. </html>

结果如下:

3.1.8 基础功能小结

综上基础功能案例,不难发现L7满足GIS前端库的基本要求,和经典的GIS前端库(如,Leaflet等)相比,还具有开箱即用、API友好、绘制效果好等优势

3.2 整体架构

L7的整体架构大致总结如下:

graph LR;
Map地图底图 --> Scene
高德Gaode --> Map地图底图
Mapbox --> Map地图底图
Layer --> Scene
组件Component --> Scene
Control --> 组件Component
Popup --> 组件Component
Marker标注 --> 组件Component
线图层LineLayer --> Layer
点图层PointLayer --> Layer
面图层PolygonLayer --> Layer
热力图层HeatMapLayer --> Layer
其他图层Other --> Layer
数据源Source --> 面图层PolygonLayer
配置项Options --> 面图层PolygonLayer
样式Style --> 面图层PolygonLayer
动画Animate --> 面图层PolygonLayer

4. 使用前端框架

Vue是常用的前端框架,TypeScript(简称TS) 是 JavaScript 的超集,可以提高代码的可维护性和可读性

下面记述基于Vite、Vue3和TypeScript搭建L7开发环境并示例

4.1 环境安装

这里使用Vue 官方的项目脚手架工具创建Vue开发环境(其他方式也可,如直接使用Vite创建)

在CMD(或Shell)中,切换到存放代码的目录,并执行:

  1. npm init vue@latest

接着选择一系列创建选项,通常默认即可:

  1. Need to install the following packages:
  2. create-vue@3.6.4
  3. Ok to proceed? (y) y
  4. Vue.js - The Progressive JavaScript Framework
  5. Project name: ... L7Test
  6. Package name: ... l7test
  7. Add TypeScript? ... No / Yes
  8. Add JSX Support? ... No / Yes
  9. Add Vue Router for Single Page Application development? ... No / Yes
  10. Add Pinia for state management? ... No / Yes
  11. Add Vitest for Unit Testing? ... No / Yes
  12. Add an End-to-End Testing Solution? » No
  13. Add ESLint for code quality? ... No / Yes
  14. Scaffolding project in E:\Code\test\L7Test...
  15. Done. Now run:
  16. cd L7Test
  17. npm install
  18. npm run dev

然后根据提示依次执行命令:

  1. cd L7Test
  1. npm install
  1. npm run dev

一个Vue模板就搭建完成:

使用VS Code(其他也可)打开刚刚创建的项目,删除掉src/components下的默认文件,并清除App.vue的默认内容:

在Terminal(CMD或Shell也可)中安装L7:

  1. npm install @antv/l7

App.vue中编写代码加载L7地图:

  1. <script setup lang="ts">
  2. import { Scene } from "@antv/l7";
  3. import { GaodeMap } from "@antv/l7-maps";
  4. const scene = new Scene({
  5. id: "map",
  6. map: new GaodeMap({
  7. center: [120.19382669582967, 30.258134],
  8. pitch: 0,
  9. style: "dark",
  10. zoom: 10,
  11. }),
  12. });
  13. </script>
  14. <template>
  15. <div id="map"></div>
  16. </template>
  17. <style scoped>
  18. #map {
  19. height: 100%;
  20. width: 100%;
  21. }
  22. </style>
  • 注:L7使用TS编写,使用TS有良好的代码提示和检测功能

结果如下:

至此环境安装完成

4.2 示例

官方示例:所有图表 | L7 (antgroup.com),基本演示了绝大部分的图表,并且示例代码和上述App.vue中的script标签下差不多,可以直接复制使用,如下图所示:

直接将示例中的代码复制到App.vuescript中即可运行:

  1. <script setup lang="ts">
  2. import { Scene, PointLayer } from '@antv/l7';
  3. import { GaodeMap } from '@antv/l7-maps';
  4. const scene = new Scene({
  5. id: 'map',
  6. map: new GaodeMap({
  7. style: 'dark',
  8. center: [ 121.417463, 31.215175 ],
  9. zoom: 11
  10. })
  11. });
  12. scene.on('loaded', () => {
  13. fetch('https://gw.alipayobjects.com/os/rmsportal/BElVQFEFvpAKzddxFZxJ.txt')
  14. .then(res => res.text())
  15. .then(data => {
  16. const pointLayer = new PointLayer({})
  17. .source(data, {
  18. parser: {
  19. type: 'csv',
  20. y: 'lat',
  21. x: 'lng'
  22. }
  23. })
  24. .size(0.5)
  25. .color('#080298');
  26. scene.addLayer(pointLayer);
  27. });
  28. });
  29. </script>
  30. <template>
  31. <div id="map"></div>
  32. </template>
  33. <style scoped>
  34. #map {
  35. height: 100%;
  36. width: 100%;
  37. }
  38. </style>

结果如下:

具体函数API的使用,可以查阅API手册:场景 Scene | L7 (antgroup.com)

官网示例图表,均可用上述方式移植到项目中使用

5. 案例练习

5.1 案例一:全球AQI数据获取与L7可视化

下面记述使用L7对全球AQI数据进行可视化

5.1.1 数据获取

全球AQI数据可从这个网站获取:World's Air Pollution: Real-time Air Quality Index (waqi.info)

进入这个网站后打开控制台,刷新网页重新加载,找到000.json

000.json上右键并在新标签页中打开

在新标签页中右键并另存为

即可获得JSON数据

5.1.2 L7可视化

可参考官方散点图样例:简单点 | L7 (antgroup.com)

5.1.2.1 加载底图

加载高德地图

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  7. <title>Document</title>
  8. <script src='https://unpkg.com/@antv/l7'></script>
  9. <style>
  10. body,
  11. #map {
  12. height: 100vh;
  13. width: 100vw;
  14. margin: 0;
  15. }
  16. </style>
  17. </head>
  18. <body>
  19. <div id="map"></div>
  20. <script>
  21. const scene = new L7.Scene({
  22. id: 'map',
  23. map: new L7.GaodeMap({
  24. center: [116.3956, 39.9392],
  25. zoom: 2,
  26. style: 'light'
  27. })
  28. });
  29. </script>
  30. </body>
  31. </html>

5.1.2.2 加载数据并解析

根据数据内容,将经纬度数组转置以符合L7的数据格式:

  1. scene.on('loaded', () => {
  2. fetch('./000.json')
  3. .then(res => res.json())
  4. .then(data => {
  5. data = data.stations
  6. data.forEach(item => {
  7. item.g.reverse()
  8. })
  9. console.log(data);
  10. })
  11. });

5.1.2.3 绘制样式

绘制点图层,并设置样式:

  1. const layer = new L7.PointLayer()
  2. .source(data, {
  3. parser: {
  4. type: 'json',
  5. coordinates: 'g'
  6. }
  7. })
  8. .shape('circle')
  9. .color('a', (value) => {
  10. // 大于0小于50的绿色
  11. if (value > 0 && value < 50) {
  12. return '#00ff00'
  13. } else if (value > 50 && value < 100) {
  14. // 大于50小于100的蓝色
  15. return '#0000ff'
  16. } else if (value > 100) {
  17. // 大于100的红色
  18. return '#ff0000'
  19. }
  20. })
  21. .size('a', (value) => {
  22. // 根据value值设置点的大小
  23. let a = value / 100 + 2;
  24. return a;
  25. })
  26. .active(true);
  27. scene.addLayer(layer);

5.1.2.4 完整代码

完整代码如下:

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  7. <title>Document</title>
  8. <script src='https://unpkg.com/@antv/l7'></script>
  9. <style>
  10. body,
  11. #map {
  12. height: 100vh;
  13. width: 100vw;
  14. margin: 0;
  15. }
  16. </style>
  17. </head>
  18. <body>
  19. <div id="map"></div>
  20. <script>
  21. const scene = new L7.Scene({
  22. id: 'map',
  23. map: new L7.GaodeMap({
  24. center: [116.3956, 39.9392],
  25. zoom: 2,
  26. style: 'light'
  27. })
  28. });
  29. scene.on('loaded', () => {
  30. fetch('./000.json')
  31. .then(res => res.json())
  32. .then(data => {
  33. data = data.stations
  34. data.forEach(item => {
  35. item.g.reverse()
  36. })
  37. // console.log(data);
  38. const layer = new L7.PointLayer()
  39. .source(data, {
  40. parser: {
  41. type: 'json',
  42. coordinates: 'g'
  43. }
  44. })
  45. .shape('circle')
  46. .color('a', (value) => {
  47. // 大于0小于50的绿色
  48. if (value > 0 && value < 50) {
  49. return '#00ff00'
  50. } else if (value > 50 && value < 100) {
  51. // 大于50小于100的蓝色
  52. return '#0000ff'
  53. } else if (value > 100) {
  54. // 大于100的红色
  55. return '#ff0000'
  56. }
  57. })
  58. .size('a', (value) => {
  59. // 根据value值设置点的大小
  60. let a = value / 100 + 2;
  61. return a;
  62. })
  63. .active(true);
  64. scene.addLayer(layer);
  65. });
  66. });
  67. </script>
  68. </body>
  69. </html>

5.2 案例二:路网数据获取与L7可视化

下面记述使用L7对路网数据进行可视化

5.2.1 数据获取

路网数据可以从以下网站下载,数据来源自OSM:Index of /extracts (openstreetmap.fr)

其中,中国的路网数据可以从这个下载:Index of /extracts/asia/china (openstreetmap.fr)

笔者这里下载上海的数据:http://download.openstreetmap.fr/extracts/asia/china/shanghai.osm.pbf

下载好以后可以直接拖入QGIS中:

  • 注:这里加载了Lines,数据量较大,绘制卡顿。且路网质量也欠佳

在图层上右键选择导出

导出为GeoJSON

导出的数据尺寸为84 MB,至此数据获取完成

5.2.2 L7可视化

可参考官方路网图样例:路径地图 | L7 (antgroup.com)

5.2.2.1 加载底图

加载高德地图

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  7. <title>Document</title>
  8. <script src='https://unpkg.com/@antv/l7'></script>
  9. <style>
  10. body,
  11. #map {
  12. height: 100vh;
  13. width: 100vw;
  14. margin: 0;
  15. }
  16. </style>
  17. </head>
  18. <body>
  19. <div id="map"></div>
  20. <script>
  21. const scene = new L7.Scene({
  22. id: 'map',
  23. map: new L7.GaodeMap({
  24. center: [116.3956, 39.9392],
  25. zoom: 2,
  26. style: 'light'
  27. })
  28. });
  29. </script>
  30. </body>
  31. </html>

5.2.2.2 加载数据并解析

加载数据,L7对于GeoJSON无需额外设置解析器:

  1. scene.on('loaded', () => {
  2. fetch('./lines.geojson')
  3. .then(res => res.json())
  4. .then(data => {
  5. console.log(data);
  6. })
  7. });

5.2.2.3 绘制样式

绘制线图层,并设置样式:

  1. const layer = new L7.LineLayer({
  2. zIndex: 2,
  3. })
  4. .source(data)
  5. .size(0.5)
  6. .active(true)
  7. .color('highway', (type) => {
  8. switch (type) {
  9. case "bridleway" :
  10. return '#c81841';
  11. case "bus_guideway" :
  12. return '#39a0cc';
  13. case "bus_stop" :
  14. return '#0d70cc';
  15. case "busway" :
  16. return '#d385dd';
  17. case "construction" :
  18. return '#30e5dc';
  19. case "corridor" :
  20. return '#ca6166';
  21. case "cycleway" :
  22. return '#c94534';
  23. case "elevator" :
  24. return '#c3ee79';
  25. case "footway" :
  26. return '#df7f53';
  27. case "living_street" :
  28. return '#0d2dce';
  29. case "motorway" :
  30. return '#c8659f';
  31. case "motorway_link" :
  32. return '#15d066';
  33. case "path" :
  34. return '#cab646';
  35. case "pedestrian" :
  36. return '#2ddb95';
  37. case "planned" :
  38. return '#36cd25';
  39. case "platform" :
  40. return '#d99b1f';
  41. case "primary" :
  42. return '#75cc53';
  43. case "primary_link" :
  44. return '#e31eb2';
  45. case "proposed" :
  46. return '#4e2bec';
  47. case "raceway" :
  48. return '#c8721c';
  49. case "residential" :
  50. return '#6ced77';
  51. case "road" :
  52. return '#57e079';
  53. case "secondary" :
  54. return '#2063e9';
  55. case "secondary_link" :
  56. return '#7aec1d';
  57. case "service" :
  58. return '#58d9ed';
  59. case "services" :
  60. return '#1fe3b9';
  61. case "steps" :
  62. return '#e010d2';
  63. case "tertiary" :
  64. return '#adca37';
  65. case "tertiary_link" :
  66. return '#d0d32e';
  67. case "track" :
  68. return '#e04684';
  69. case "trunk" :
  70. return '#b232e4';
  71. case "trunk_link" :
  72. return '#822dcd';
  73. case "unclassified" :
  74. return '#a686de';
  75. case "null" :
  76. return '#1f1bef';
  77. case "" :
  78. return '#beb297';
  79. default:
  80. return '#beb297';
  81. }
  82. });
  83. scene.addLayer(layer);
  84. });

5.2.2.4 完整代码

完整代码如下:

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  7. <title>Document</title>
  8. <script src='https://unpkg.com/@antv/l7'></script>
  9. <style>
  10. body,
  11. #map {
  12. height: 100vh;
  13. width: 100vw;
  14. margin: 0;
  15. }
  16. </style>
  17. </head>
  18. <body>
  19. <div id="map"></div>
  20. <script>
  21. const scene = new L7.Scene({
  22. id: 'map',
  23. map: new L7.GaodeMap({
  24. center: [116.3956, 39.9392],
  25. zoom: 2,
  26. style: 'dark'
  27. })
  28. });
  29. scene.on('loaded', () => {
  30. fetch('./lines.geojson')
  31. .then(res => res.json())
  32. .then(data => {
  33. const layer = new L7.LineLayer({
  34. zIndex: 2,
  35. })
  36. .source(data)
  37. .size(0.5)
  38. .active(true)
  39. .color('highway', (type) => {
  40. switch (type) {
  41. case "bridleway" :
  42. return '#c81841';
  43. case "bus_guideway" :
  44. return '#39a0cc';
  45. case "bus_stop" :
  46. return '#0d70cc';
  47. case "busway" :
  48. return '#d385dd';
  49. case "construction" :
  50. return '#30e5dc';
  51. case "corridor" :
  52. return '#ca6166';
  53. case "cycleway" :
  54. return '#c94534';
  55. case "elevator" :
  56. return '#c3ee79';
  57. case "footway" :
  58. return '#df7f53';
  59. case "living_street" :
  60. return '#0d2dce';
  61. case "motorway" :
  62. return '#c8659f';
  63. case "motorway_link" :
  64. return '#15d066';
  65. case "path" :
  66. return '#cab646';
  67. case "pedestrian" :
  68. return '#2ddb95';
  69. case "planned" :
  70. return '#36cd25';
  71. case "platform" :
  72. return '#d99b1f';
  73. case "primary" :
  74. return '#75cc53';
  75. case "primary_link" :
  76. return '#e31eb2';
  77. case "proposed" :
  78. return '#4e2bec';
  79. case "raceway" :
  80. return '#c8721c';
  81. case "residential" :
  82. return '#6ced77';
  83. case "road" :
  84. return '#57e079';
  85. case "secondary" :
  86. return '#2063e9';
  87. case "secondary_link" :
  88. return '#7aec1d';
  89. case "service" :
  90. return '#58d9ed';
  91. case "services" :
  92. return '#1fe3b9';
  93. case "steps" :
  94. return '#e010d2';
  95. case "tertiary" :
  96. return '#adca37';
  97. case "tertiary_link" :
  98. return '#d0d32e';
  99. case "track" :
  100. return '#e04684';
  101. case "trunk" :
  102. return '#b232e4';
  103. case "trunk_link" :
  104. return '#822dcd';
  105. case "unclassified" :
  106. return '#a686de';
  107. case "null" :
  108. return '#1f1bef';
  109. case "" :
  110. return '#beb297';
  111. default:
  112. return '#beb297';
  113. }
  114. });
  115. scene.addLayer(layer);
  116. });
  117. });
  118. </script>
  119. </body>
  120. </html>

6. 综合案例:基于众源轨迹数据的三维路网生成与L7可视化

下面记述使用L7对长沙岳麓山景点游客轨迹数据进行可视化并构建三维路网的综合案例

6.1 数据获取

路网数据可以从以下网站下载,数据来源自六只脚:六只脚_GPS轨迹记录_户外自助游_自助游线路 (foooooot.com)

具体的轨迹获取教程可以参考:GPS地图生成03之数据获取 - 当时明月在曾照彩云归 - 博客园 (cnblogs.com)

下载好以后可以在QGIS中利用加载XY文件的方式加载all.csv文件,并设置OSM底图,预览GPS轨迹:

  • 注:数据量较大,绘制卡顿

数据文件尺寸为47.2 M,有892152条轨迹数据,至此数据获取完成

数据文件内容示例为:

  1. id,lng,lat,ele,track,time
  2. 0,112.938652777778,28.1828777777778,52.5862003780718,1448263,1511330079
  3. 1,112.936425,28.1837833333333,63.8200589970501,1448263,1511330738
  4. 2,112.936280555556,28.1837833333333,64.1105651105651,1448263,1511330800
  5. 3,112.93595,28.18385,65.3336643495531,1448263,1511331332
  6. 4,112.935691666667,28.1839333333333,66.9243986254296,1448263,1511331794
  7. 5,112.932275,28.1840388888889,80.1450980392157,1448263,1511335690
  8. 6,112.929519444444,28.1849583333333,179.382636655949,1448263,1511336583
  9. 7,112.929244444444,28.1849777777778,185.630363036304,1448263,1511336714
  10. 8,112.928458333333,28.1860111111111,228.579710144928,1448263,1511337087
  11. 9,112.931161111111,28.1911555555556,285.57196969697,1448263,1511339434
  12. 10,112.931288888889,28.1910944444444,288.503448275862,1448263,1511339460
  13. ......

6.2 L7可视化

可参考官方亮度图样例:亮度图 | L7 (antgroup.com)

6.2.1 加载底图

加载Mapbox地图

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  7. <title>Document</title>
  8. <script src='https://api.mapbox.com/mapbox-gl-js/v2.15.0/mapbox-gl.js'></script>
  9. <link href='https://api.mapbox.com/mapbox-gl-js/v2.15.0/mapbox-gl.css' rel='stylesheet' />
  10. <script src='https://unpkg.com/@antv/l7'></script>
  11. </head>
  12. <body>
  13. <div id="map"></div>
  14. <script>
  15. const scene = new L7.Scene({
  16. id: 'map',
  17. map: new L7.Mapbox({
  18. style: 'dark',
  19. center: [112.9448, 28.1708],
  20. zoom: 12,
  21. token: 'pk.eyJ1IjoieWFuZ2ppYW4iLCJhIjoiY2phaG1neno0MXFkNDMzbWhwNWw0bWM4aiJ9.CFmrh0LVWAbmVeed-Xr7wA'
  22. }),
  23. });
  24. </script>
  25. </body>
  26. </html>

6.2.2 加载数据并解析

加载数据,L7内置CSV格式解析器,只需指定字段名即可:

  1. scene.on('loaded', () => {
  2. fetch('./all.csv')
  3. .then(res => res.text())
  4. .then(data => {
  5. const pointLayer = new L7.PointLayer({})
  6. .source(data, {
  7. parser: {
  8. type: 'csv',
  9. y: 'lat',
  10. x: 'lng'
  11. }
  12. })
  13. scene.addLayer(pointLayer);
  14. });
  15. });

6.2.3 绘制样式

绘制点图层,并设置样式:

  1. scene.on('loaded', () => {
  2. fetch('./all.csv')
  3. .then(res => res.text())
  4. .then(data => {
  5. const pointLayer = new L7.PointLayer({})
  6. .source(data, {
  7. parser: {
  8. type: 'csv',
  9. y: 'lat',
  10. x: 'lng'
  11. }
  12. })
  13. .size(0.5)
  14. .color('#080298');
  15. scene.addLayer(pointLayer);
  16. });
  17. });

从图中可以看出主要的路线,即高亮部分

6.2.4 路网叠加

以下对景点及附近的路网数据可视化

路网数据获取与可视化步骤可参考:「AntV」路网数据获取与L7可视化 - 当时明月在曾照彩云归 - 博客园 (cnblogs.com)

叠加图层:

可以看到,在景区的轨迹信息比路网信息更为完善和直观

6.2.5 路网提取

从上图不难看出,虽然有的地方在路网上不显示,这些道路可能只是景区小道,但它确实也是一种特殊的道路,尤其是对于行人、旅行者而言

当足够数量的轨迹点显示在一起时,这些亮度图似乎就是路网图,更进一步的,可以从这些轨迹数据中提取去路网

这里不做具体研究阐述,可以参考下列文章:

这里使用的是《Map Inference in the Face of Noise and Disparity》中所提出的算法进行提取路网,并提取GPS轨迹数据中的三维信息,构建为三维路网

《Map Inference in the Face of Noise and Disparity》中所提出的算法源码可以在以下地址下载:

另外,还有多种路网提取算法,可以在下列网站中查看并使用:

6.2.6 二维路网可视化

经过一系列的数据预处理、路网提取、数据后处理,从上述的轨迹数据得到了以下路网数据:

其中,海拔渐变色带由低到高为:

由图中可以看出岳麓山景点路网数据的相对高度

绘制的代码如下:

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  7. <title>Document</title>
  8. <script src='https://api.mapbox.com/mapbox-gl-js/v2.15.0/mapbox-gl.js'></script>
  9. <link href='https://api.mapbox.com/mapbox-gl-js/v2.15.0/mapbox-gl.css' rel='stylesheet' />
  10. <script src='https://unpkg.com/@antv/l7'></script>
  11. <style>
  12. body,
  13. #map {
  14. height: 100vh;
  15. width: 100vw;
  16. margin: 0;
  17. }
  18. </style>
  19. </head>
  20. <body>
  21. <div id="map"></div>
  22. <script>
  23. const scene = new L7.Scene({
  24. id: 'map',
  25. map: new L7.Mapbox({
  26. style: 'dark',
  27. center: [112.9448, 28.1708],
  28. zoom: 12,
  29. token: 'pk.eyJ1IjoieWFuZ2ppYW4iLCJhIjoiY2phaG1neno0MXFkNDMzbWhwNWw0bWM4aiJ9.CFmrh0LVWAbmVeed-Xr7wA'
  30. })
  31. });
  32. scene.on('loaded', () => {
  33. fetch('./Yuelushan.geojson')
  34. .then(res => res.json())
  35. .then(data => {
  36. const layer = new L7.LineLayer({
  37. zIndex: 2,
  38. })
  39. .source(data)
  40. .size(0.5)
  41. .active(true)
  42. .color('z1', ['#0b8040', '#f2b90c', '#751304', '#8c644c', '#b8b8b8']);
  43. scene.addLayer(layer);
  44. });
  45. });
  46. </script>
  47. </body>

6.2.7 三维路网可视化

路网数据的格式如下:

  1. {
  2. "type": "FeatureCollection",
  3. "name": "Yuelushan",
  4. "crs": {
  5. "type": "name",
  6. "properties": {
  7. "name": "urn:ogc:def:crs:OGC:1.3:CRS84"
  8. }
  9. },
  10. "features": [
  11. {
  12. "type": "Feature",
  13. "properties": {
  14. "id": 0,
  15. "z1": 74.72296906,
  16. "z2": 74.72296906
  17. },
  18. "geometry": {
  19. "type": "MultiLineString",
  20. "coordinates": [
  21. [
  22. [
  23. 112.918952568856085,
  24. 28.182139289691705,
  25. 74.72296906
  26. ],
  27. [
  28. 112.918964283127451,
  29. 28.182139382193309,
  30. 74.72296906
  31. ]
  32. ]
  33. ]
  34. }
  35. },
  36. ......

由示例数据可知,每个点坐标是由经纬度和高程组成,而L7默认支持路网数据高程显示:

6.2.8 坡度信息图

利用高程信息,可以制作出坡度信息图

利用坡度信息,可以帮助行人选择道路,实现代码如下:

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  7. <title>Document</title>
  8. <script src='https://api.mapbox.com/mapbox-gl-js/v2.15.0/mapbox-gl.js'></script>
  9. <link href='https://api.mapbox.com/mapbox-gl-js/v2.15.0/mapbox-gl.css' rel='stylesheet' />
  10. <script src='https://unpkg.com/@antv/l7'></script>
  11. <style>
  12. body,
  13. #map {
  14. height: 100vh;
  15. width: 100vw;
  16. margin: 0;
  17. }
  18. </style>
  19. </head>
  20. <body>
  21. <div id="map"></div>
  22. <script>
  23. const scene = new L7.Scene({
  24. id: 'map',
  25. map: new L7.Mapbox({
  26. style: 'dark',
  27. center: [112.9448, 28.1708],
  28. zoom: 12,
  29. token: 'pk.eyJ1IjoieWFuZ2ppYW4iLCJhIjoiY2phaG1neno0MXFkNDMzbWhwNWw0bWM4aiJ9.CFmrh0LVWAbmVeed-Xr7wA'
  30. })
  31. });
  32. scene.on('loaded', () => {
  33. fetch('./Yuelushan.geojson')
  34. .then(res => res.json())
  35. .then(data => {
  36. data.features.forEach(element => {
  37. element.properties.slope = Math.abs(element.properties.z1 - element.properties.z2);
  38. });
  39. const layer = new L7.LineLayer({
  40. zIndex: 2,
  41. })
  42. .source(data)
  43. .size(1)
  44. .active(true)
  45. .color('slope', ['#0b8040', '#f2b90c', '#751304', '#8c644c', '#b8b8b8']);
  46. scene.addLayer(layer);
  47. });
  48. });
  49. </script>
  50. </body>

注:此处坡度计算并不准确,只是大致示意

7. 总结与感悟

笔者使用过多个Web端的GIS开源库,包括OpenLayers、Leaflet、Mapbox、Cesium等,也使用过ECharts等图表库,相较而言,L7开箱即用的特点十分突出,基于WebGL的渲染方式在面对地理大数据时也能获得不错的体验,甚至在上述的案例中,绘制轨迹数据的流畅感比C++写的桌面端软件的QGIS更好

不足之处是,和老牌的GIS开源库相比,专业性和可配置性还是有所欠缺,三维方面也有待完善。当然,目前看来,L7的主要应用场景的地理数据可视化,如果是侧重地理可视化的项目与应用场景,L7是个非常不错的选择

8. 参考资料

[1] 简介 | L7 (antgroup.com)

[2] 所有图表 | L7 (antgroup.com)

[3] 场景 Scene | L7 (antgroup.com)

[4] Map Inference in the Face of Noise and Disparity

[5] haidaoxiaofei/mapinference-gis12-upgrade (github.com)

「AntV」L7地理可视化:从入门到实践的更多相关文章

  1. 「前端」尚妆 UI 组件库工程实践(weex vue)

    本文来自尚妆前端团队南洋 发表于尚妆github博客,欢迎订阅! 前言 尚妆大前端团队使用 weex 进行三端统一开发有一段时间了,截止本文发表「达人店」APP大部分页面都已经用 weex 进行了重构 ...

  2. 「设计模式」JavaScript - 设计模式之单例模式与场景实践

    单例介绍 上次总结了设计模式中的module模式,可能没有真真正正的使用在场景中,发现效果并不好,想要使用起来却不那么得心应手, 所以这次我打算换一种方式~~从简单的场景中来看单例模式, 因为Java ...

  3. Distill详述「可微图像参数化」:神经网络可视化和风格迁移利器!

    近日,期刊平台 Distill 发布了谷歌研究人员的一篇文章,介绍一个适用于神经网络可视化和风格迁移的强大工具:可微图像参数化.这篇文章从多个方面介绍了该工具. 图像分类神经网络拥有卓越的图像生成能力 ...

  4. 「工具」Dubbo可视化测试工具的设计和实现

    「工具」Dubbo可视化测试工具的设计和实现 学习了:https://blog.csdn.net/qq355667166/article/details/78914453

  5. 「福利」Java Swing 编写的可视化算法工程,包含树、图和排序

    之前在整理<学习排序算法,结合这个方法太容易理解了>这篇文章时,发现了一个用 Java Swing 编写的可视化算法工程,真心不错!包含了常用数据结构和算法的动态演示,先来张图感受下: 可 ...

  6. spring cloud 入门,看一个微服务框架的「五脏六腑」

    Spring Cloud 是一个基于 Spring Boot 实现的微服务框架,它包含了实现微服务架构所需的各种组件. 注:Spring Boot 简单理解就是简化 Spring 项目的搭建.配置.组 ...

  7. 【入门必看】不理解「对象」?很可能有致命bug:简单的Python例子告诉你

    简介:越来越多的人要在学习工作中用到『编程』这个工具了,其中很大一部分人用的是Python.大部分人只是做做简单的科研计算.绘图.办公自动化或者爬虫,但-- 这就不需要理解「指针与面向对象」了吗? 在 ...

  8. Note -「多项式」基础模板(FFT/NTT/多模 NTT)光速入门

      进阶篇戳这里. 目录 何为「多项式」 基本概念 系数表示法 & 点值表示法 傅里叶(Fourier)变换 概述 前置知识 - 复数 单位根 快速傅里叶正变换(FFT) 快速傅里叶逆变换(I ...

  9. 一个「学渣」从零开始的Web前端自学之路

    从 13 年专科毕业开始,一路跌跌撞撞走了很多弯路,做过餐厅服务员,进过工厂干过流水线,做过客服,干过电话销售可以说经历相当的“丰富”. 最后的机缘巧合下,走上了前端开发之路,作为一个非计算机专业且低 ...

  10. 「Azure」数据分析师有理由爱Azure之一-Azure能带给我们什么?

    前面我们以相同的方式从数据分析师的视角介绍了Sqlserver,本系列亦同样地延续下去,同样是挖掘数据分析师值得使用的Azure云平台的功能.因云平台功能太多,笔者所接触的面也十分有限,有更专业的读者 ...

随机推荐

  1. 简单部署halo博客

    第一步,购买服务器,安装宝塔linux面板. 第二步,在宝塔linux面板的软件商店安装docker管理器 第三步,配置阿里云镜像加速 修改镜像加速 vim /etc/docker/daemon.js ...

  2. 第四部分:Spdlog日志库的核心组件分析-logger

    Spdlog是一个快速且可扩展的C++日志库,它支持多线程和异步日志记录.在本文中,我们将分析Spdlog日志库的核心代码,探究其实现原理和代码结构. Spdlog的基本架构 上一篇文章介绍了spdl ...

  3. 西瓜视频的li绑定容器 踏坑之旅

    一定要绑定key,不然会出现一个li里面渲染出两个video标签

  4. 深入理解 python 虚拟机:字节码灵魂——Code obejct

    深入理解 python 虚拟机:字节码灵魂--Code obejct 在本篇文章当中主要给大家深入介绍在 cpython 当中非常重要的一个数据结构 code object! 在上一篇文章 深入理解 ...

  5. 京东LBS推荐算法实践

    作者:京东零售 郑书剑 1.推荐LBS业务介绍 1.1 业务场景 现有的同城购业务围绕京东即时零售能力搭建了到店.到家两种业务场景.同城业务与现有业务进行互补,利用高频,时效性快的特点,可以有效提升主 ...

  6. [数据库/MySQL]数据类型:enum 枚举类型

    1 需求描述 场景 性别(gender) :男 / 女 / 保密 2 基本语法 enum(枚举值 1,枚举值 2...); 枚举值列表在 255 个以内,使用 1 个字节来存储 枚举值列表超过 255 ...

  7. stm32报错

    1. declaration may not appear after executable statement in block 关于编译错误的小伙伴:error: #268: declaratio ...

  8. PHP大文件分割上传 PHP分片上传

    这篇文章主要为大家详细介绍了PHP大文件分割上传,PHP分片上传,具有一定的参考价值,感兴趣的小伙伴们可以参考一下 服务端为什么不能直接传大文件?跟php.ini里面的几个配置有关 upload_ma ...

  9. mysql安装my.cnf配置

    进入my.cnf文件//乱码修改 设置编码 character_set_server = utf8mb4 //编码 collation-server = utf8mb4_general_ci //连接 ...

  10. mybatis xml 中 大于、小于、等于 写法

    在 *.xml 中使用常规的 < > = <= >= 会与xml的语法存在冲突 方法一:使用xml 原生转义的方式进行转义 字符名称 sql符号 转义字符 大于号 > & ...