L7结合Turf.js实现空间分析与数据可视化
1. 概述
AntV L7 是蚂蚁集团 AntV 数据可视化团队推出的基于 WebGL 的开源大规模地理空间数据可视分析引擎,其特点是通过简单的代码进行配置,即可在前端网页中绘制精美的地图以及相关的图表,并且基于 WebGL 的渲染方式使得 L7 在大数据渲染时具有较为流畅体验
提到地理数据,就不得不说 GIS(Geographic Information System,地理理信息系统),它是一种用于捕捉、存储、管理、分析和展示地理空间数据的技术,通过 GIS ,用户可以将地理空间数据与属性数据相结合,进行复杂的空间分析、制作地图和展示地理信息,GIS 的核心包括地理数据的收集和整合、空间分析和地图制作
L7 专注于地理数据可视化,即地图制作,如果能有一个前端空间分析工具与 L7 结合,丰富 GIS 的核心,会极大地利好前端开发者和用户
Turf.js 就是一个经典地空间分析库,提供了许多用于处理地理空间数据的函数和算法,其基于JavaScript编写,可以用于浏览器端和Node.js环境
所以,将 Turf.js 与 L7 结合,就可以实现在前端进行空间分析与空间可视化,拓展 L7 的使用方向
以下章节,笔者首先记述 Turf.js 与 L7 的快速入门使用,然后记录一些实践案例
(笔者注:为了代码易于复现与使用,本文采用原始HTML的方式编写代码)
2. L7 的快速入门
第一步,使用CDN加载 L7
<! --引入最新版的L7,笔者使用时为2.20.5--> <script src = 'https://unpkg.com/@antv/l7'></script>
- CDN 引用的方式,是在使用时通过 L7 命名空间获取所有对象并初始化,如 L7.scene、L7.GaodeMap
第二步,创建一个 DIV 容器并设置CSS样式
<div id="map"></div>
第三步,加载高德底图
<script> const scene = new L7.Scene({ id: 'map', map: new L7.GaodeMap({ center: [116.3956, 39.9392], zoom: 10, mapStyle: 'amap://styles/darkblue' }) }); </script>
此时的完整代码如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <script src='https://unpkg.com/@antv/l7'></script> <style> body, #map { height: 100vh; width: 100vw; margin: 0; } </style> </head> <body> <div id="map"></div> <script> const scene = new L7.Scene({ id: 'map', map: new L7.GaodeMap({ center: [116.3956, 39.9392], zoom: 10, mapStyle: 'amap://styles/darkblue' }) }); </script> </body> </html>
结果图如下:
第四步,加载GeoJSON矢量数据并设置样式
- L7 对于GeoJSON有很高的支持度,默认就支持GeoJSON数据
- 数据是北京地铁线路
// 加载底图之后 scene.on('loaded', () => { fetch( 'https://gw.alipayobjects.com/os/basement_prod/0d2f0113-f48b-4db9-8adc-a3937243d5a3.json' ) .then(res => res.json()) .then(data => { const layer = new L7.LineLayer({}) .source(data) .size(4) .shape('line') .color('标准名称', [ '#5B8FF9', '#5CCEA1', '#F6BD16' ]) .style({ borderWidth: 0.4, borderColor: '#fff' }); scene.addLayer(layer); }); });
此时的完整代码如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <script src='https://unpkg.com/@antv/l7'></script> <style> body, #map { height: 100vh; width: 100vw; margin: 0; } </style> </head> <body> <div id="map"></div> <script> const scene = new L7.Scene({ id: 'map', map: new L7.GaodeMap({ center: [116.3956, 39.9392], zoom: 10, mapStyle: 'amap://styles/darkblue' }) }); scene.on('loaded', () => { fetch( 'https://gw.alipayobjects.com/os/basement_prod/0d2f0113-f48b-4db9-8adc-a3937243d5a3.json' ) .then(res => res.json()) .then(data => { const layer = new L7.LineLayer({}) .source(data) .size(4) .shape('line') .color('标准名称', ['#5B8FF9', '#5CCEA1', '#F6BD16']) .style({ borderWidth: 0.4, borderColor: '#fff' }); scene.addLayer(layer); }); }); </script> </body> </html>
结果图如下:
由上述代码不难看出,使用 L7 进行可视化,只需要使用极少的代码,就可配置成精美的地图图表
文章篇幅有限,更多的样式配置信息,更详尽的API文档,请翻阅 官方文档 和 官方示例
3. Turf.js 的快速入门
第一步, 引入 Turf.js 的 CDN
<script src='https://unpkg.com/@turf/turf@6/turf.min.js'></script>
- CDN 的方式,通过 turf 命名空间访问 Turf.js 的函数,如 turf.bbox 等
第二步,引入GeoJSON数据
- 数据和上面一样,是北京地铁数据
<script> fetch('https://gw.alipayobjects.com/os/basement_prod/0d2f0113-f48b-4db9-8adc-a3937243d5a3.json') .then(res => res.json()) .then(data => { console.log(data) // JSON对象 }); </script>
第三步,进行空间分析得到结果GeoJSON
- 这里示例为求地理包围盒 BBOX
<script> fetch('https://gw.alipayobjects.com/os/basement_prod/0d2f0113-f48b-4db9-8adc-a3937243d5a3.json') .then(res => res.json()) .then(data => { const bbox = turf.bbox(data); const bboxPolygon = turf.bboxPolygon(bbox); console.log(bboxPolygon); // 下面是地理包围盒的坐标数据 // 0: 116.10214436813241 // 1: 39.6703694682177 // 2: 116.68907341874268 // 3: 40.20693349910422 }); </script>
此时的完整代码为:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <script src='https://unpkg.com/@turf/turf@6/turf.min.js'></script> <script> fetch('https://gw.alipayobjects.com/os/basement_prod/0d2f0113-f48b-4db9-8adc-a3937243d5a3.json') .then(res => res.json()) .then(data => { const bbox = turf.bbox(data); const bboxPolygon = turf.bboxPolygon(bbox); console.log(bboxPolygon); }); </script> </body> </html>
上述 Turf.js 的代码可以看到,Turf.js 接收 GeoJSON 数据,输出 GeoJSON 结果数据
更多的空间分析函数与API可参考 Turf.js官方文档
Turf.js 专注于空间分析,L7 专注于数据可视化,并且两者对于 GeoJSON 格式有着最高优先度的支持,因此,可以将地理数据在 Turf.js 中进行处理,然后直接输出结果到 L7 中进行可视化,实现数据分析与数据可视化的连接
4. 案例一:寻找地铁站的换乘点
4.1 分析场景与数据
地铁换乘点,通常在不同地铁线路之间的交叉处,所以寻找换乘点,通常就是计算地铁线路的交叉处
如何计算交叉点呢?用空间分析的视角,就是进行求交运算
从 Turf.js 的官方文档中,可以找到计算线相交的函数lineIntersect
上述的北京地铁线路数据示例如下:
{ "type": "FeatureCollection", "crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } }, "features": [ { "type": "Feature", "properties": { "标准名称": "地铁二号线", "分类代码": 430101, "数据来源": "正射影像", "现状时间": "2010/08/14", "备注": null, "SHAPE_LENG": 23177.0298819, "Shape_Le_1": 23177.0298784, "Shape_Le_2": 30241.8106532 }, "geometry": { "type": "MultiLineString", "coordinates": [ [ [ 116.38050072430798, 39.94888011518406 ], [ 116.38714780612922, 39.94892587302933 ], // ...
根据 Turf.js 的示例与文档,结合这份数据示例的类型为 FeatureCollection ,需要将里面的每个线路 MultiLineString 进行两两求交运算,最后将结果在 L7 中以点的形式绘制出来
4.2 进行空间分析
将 FeatureCollection 的每个 MultiLineString 进行两两求交运算得到交点的点集:
<script src='https://unpkg.com/@turf/turf@6/turf.min.js'></script> <script> fetch('https://gw.alipayobjects.com/os/basement_prod/0d2f0113-f48b-4db9-8adc-a3937243d5a3.json') .then(res => res.json()) .then(data => { const lines = data; // 新建一个Point类型的FeatureCollection用来存储结果 const points = turf.featureCollection([]); for (let i = 0; i < lines.features.length; i++) { for (let j = i + 1; j < lines.features.length; j++) { const intersections = turf.lineIntersect(lines.features[i], lines.features[j]); if (intersections) { if (intersections.features.length > 0) { // 将交点添加到points中 intersections.features.forEach(item => { points.features.push(item); }); } } } } console.log(points); // 得到四十个交点的JSON对象 }); </script>
4.3 数据可视化
使用 L7 绘制结果代码很简单:
const pointLayer = new L7.PointLayer({}) .source(points) .shape('circle') .size(6) .color('#f00') .style({ stroke: '#fff', strokeWidth: 2 }); scene.addLayer(pointLayer);
最后,地铁路线与路线交点的完整代码如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <script src='https://unpkg.com/@antv/l7'></script> <script src='https://unpkg.com/@turf/turf@6/turf.min.js'></script> <style> body, #map { height: 100vh; width: 100vw; margin: 0; } </style> </head> <body> <div id="map"></div> <script> const scene = new L7.Scene({ id: 'map', map: new L7.GaodeMap({ center: [116.3956, 39.9392], zoom: 10, mapStyle: 'amap://styles/darkblue' }) }); scene.on('loaded', () => { fetch( 'https://gw.alipayobjects.com/os/basement_prod/0d2f0113-f48b-4db9-8adc-a3937243d5a3.json' ) .then(res => res.json()) .then(data => { const layer = new L7.LineLayer({}) .source(data) .size(4) .shape('line') .color('标准名称', ['#5B8FF9', '#5CCEA1', '#F6BD16']) .style({ borderWidth: 0.4, borderColor: '#fff' }); scene.addLayer(layer); const lines = data; // 新建一个Point类型的FeatureCollection const points = turf.featureCollection([]); for (let i = 0; i < lines.features.length; i++) { for (let j = i + 1; j < lines.features.length; j++) { const intersections = turf.lineIntersect(lines.features[i], lines.features[j]); if (intersections) { if (intersections.features.length > 0) { // 将交点添加到points中 intersections.features.forEach(item => { points.features.push(item); }); } } } } const pointLayer = new L7.PointLayer({}) .source(points) .shape('circle') .size(6) .color('#f00') .style({ stroke: '#fff', strokeWidth: 2 }); scene.addLayer(pointLayer); }); }); </script> </body> </html>
结果图如下(红点为换乘点):
5. 案例二:计算地铁线路的服务范围
5.1 分析场景与数据
居民乘坐地铁,通常是前往最近的地铁站,地铁线路的服务范围其实就是地铁线路附近的区域
如果求地铁线路附近的区域呢?从空间分析的角度来说,就是进行缓冲区分析
那地铁线路的服务半径一般是多少呢?ChatGPT提示笔者是1-2公里,笔者这里就取1.5公里作为缓冲半径
地铁线路数据还是使用上述的那份北京地铁线路数据
从 Turf.js 的官方文档中,可以找到计算缓冲区的函数buffer
根据 Turf.js 的示例与文档,结合这份数据示例的类型为 FeatureCollection ,所以只需要将数据传入 buffer 函数即可得到结果
5.2 进行空间分析
调用 turf.buffer 计算缓冲区:
<script src='https://unpkg.com/@turf/turf@6/turf.min.js'></script> <script> fetch('https://gw.alipayobjects.com/os/basement_prod/0d2f0113-f48b-4db9-8adc-a3937243d5a3.json') .then(res => res.json()) .then(data => { const lines = data; const buffered = turf.buffer(data, 1500, { units: 'meters' }); console.log(buffered); // 缓冲区的JSON对象,Polygon }); </script>
5.3 数据可视化
L7 进行可视化还是一如既往的简单
地铁路线与缓冲区分析完整代码如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <script src='https://unpkg.com/@antv/l7'></script> <script src='https://unpkg.com/@turf/turf@6/turf.min.js'></script> <style> body, #map { height: 100vh; width: 100vw; margin: 0; } </style> </head> <body> <div id="map"></div> <script> const scene = new L7.Scene({ id: 'map', map: new L7.GaodeMap({ center: [116.3956, 39.9392], zoom: 10, mapStyle: 'amap://styles/darkblue' }) }); scene.on('loaded', () => { fetch( 'https://gw.alipayobjects.com/os/basement_prod/0d2f0113-f48b-4db9-8adc-a3937243d5a3.json' ) .then(res => res.json()) .then(data => { const layer = new L7.LineLayer({}) .source(data) .size(4) .shape('line') .color('标准名称', ['#5B8FF9', '#5CCEA1', '#F6BD16']) .style({ borderWidth: 0.4, borderColor: '#fff' }); const lines = data; const buffered = turf.buffer(data, 1500, { units: 'meters' }); const polygonLayer = new L7.PolygonLayer({}) .source(buffered) .shape('fill') .color('#fff') .style({ opacity: 0.2 }); scene.addLayer(polygonLayer); scene.addLayer(layer); }); }); </script> </body> </html>
最后结果图如下:
6. 总结
经过上述两个小案例,可以说使用 L7 结合 Turf.js 进行简单的空间分析与可视化简直是易如反掌,尤其是对于前端开发人员来说,有时简单的空间分析功能在客户端完成即可,而不必进行后端开发
L7 具有强大的制图能力,在结合Turf.js后拥有了更广泛的使用场景
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律