介绍
有朋友反馈说最近分享的内容不太好理解,那么今天来分享个早前开发的图层制作过程,基于GIS数据代码生成流动的车行线,上手很简单。下面我将在实现思路、具体开发、数据来源这几方面逐步讲解,希望读者能从中获取对数据可视化开发的兴趣。文中使用到高德地图JSAPI、three.js和一些GIS数据处理工具。
实现思路
-
这是我们通常能够获取到的数据,从geoJSON数据通常没有真正的曲线,得到通常是基于关键节点生成的线段几何体,只要线越密集,看上去就越平滑,所消耗的资源也就越多。
-
在空间几何中,线本身是没有宽度这个概念的,我们能看到不同粗细程度到线,其本质其实是一连串拼合的三角面带,这些面始终朝着相机,这样的话才保证我们在不同角度看到的始终是"均匀圆润"的线。
-
给这些几何赋予纯色材质,整条线看着像一个整体。
-
根据具体场景给几何体赋予贴图,我们知道如果用相机长曝光模式去拍摄夜晚车辆移动,能够看到一条从亮到暗的轨迹,因为给线图层也赋予这样一层贴图。
-
通过调整贴图的X轴偏移让线条动起来。
-
增加后期辉光效果,给图层赋予光的质感。
实现步骤
-
编写构造函数,声明基础的参数。这里Layer为基类,封装了在高德地图上创建glCustomLayer图,管理图层,逐帧函数等基础操作。
jsxclass FlowlineLayer extends Layer { // 图层数据 _data = [] // 线材质 _material = null /** * @param {Object} config * @param {geoJSON} config.data 路径数据 * @param {Number} [config.lineWidth=20.0] 路径线条宽度 * @param {Number} [config.altitude=0.0] 整体海拔高度 * @param {Array} [config.zooms=[5,14]] 显示区间 * @param {Boolean} [config.animate=true] 是否显示动画 * @param {Number} [config.speed=1.0] 流动速度系数 * @param {String} [config.uvMapURL='./static/texture/road_bg1.png'] 线条纹理URL */ constructor (config) { const conf = { data: null, speed: 1, animate: true, lineWidth: 20.0, altitude: 0.0, uvMapURL: './static/texture/road_bg1.png', ...config } super(conf) this.initData(conf.data) } }
-
将Polyline的geoJSON数据并转为空间坐标
jsxinitData (data) { const arr = data?.features.map(item => { return item?.geometry?.coordinates[0] }) // 经纬度转为空间坐标 const res = this.customCoords.lngLatsToCoords(arr) this._data = res }
-
根据数据创建几何体,并设置材质。事实上我们得到的是一条没有宽度的线的数据,而最终的展示的是平行于地面的一条平面带,这里我们使用插件meshline,它提供的MeshLineGeometry能够自动帮我们生成所需的几何体。
jsximport { MeshLineGeometry, MeshLineMaterial } from '../plugins/meshline.js' //... initLines () { const { scene } = this //材质纹理 const texture = new THREE.TextureLoader().load(this.mergeSourceURL(this._conf.uvMapURL)) texture.wrapS = THREE.RepeatWrapping texture.wrapT = THREE.RepeatWrapping const material = new MeshLineMaterial({ lineWidth: this._conf.lineWidth, sizeAttenuation: 1, useMap: 1, opacity: 1, map: texture, transparent: true, depthTest: true }) this._material = material //生成网格体 const multiLine = [...this._data] multiLine.forEach(lineData => { const points = [] lineData.forEach(([x, y]) => { points.push(new THREE.Vector3(x, y, this._conf.altitude)) }) const line = new MeshLineGeometry() line.setPoints(points) const mesh = new THREE.Mesh(line, material) scene.add(mesh) }) }
-
在requestAnimationFrame逐帧修改纹理的x偏移量,形成动画效果
jsx// 更新纹理偏移量 update () { if (!this._isAnimate) { return } if (this._material?.uniforms?.offset) { this._material.uniforms.offset.value.x -= 0.01 * this._conf.speed } }
-
添加后期效果,这块不作展开,有兴趣可以看 这里
jsxconst layer = new FlowlineLayer({ id: 'trafficLayer', //... }) layer.on('complete',(layer)=>{ new EffectLayer({ layer, style:{ strength: 1.0, //发光强度 radius: 0.5 //泛光半径 } }) })
路径数据从哪来
-
通过地图引擎的路径导航功能获取路径,如下图所示,通过指定起点、终点、经过点可以从API中获取规划好的路径数据,适合在地图上能查得到的部分道路
-
自行绘制,如果需要展示的路径还处于规划中,或者属于园区内部道路数据,在官方地图上无法获取,则需要人工使用工具做绘制,这里推荐使用QGIS绘制矢量图层。
-
如果是涉及范围或数据量较大的路径,则需要通过某些途径导出了,通过OSM可以无套路导出所需的各种点线面数据,缺点就是有些数据实在太老旧了。在国内我们使用水经注之类的GIS数据下载器获取最新最全的数据,当然,优质的数据都是收费的。