数据可视化在滴滴的应用
桔妹导读:现代的数据可视化产品相较于之前的仪表盘应用,在数据方面呈现更加生动、数据实时性高、交互更为友好、效果更加震撼等特点,越来越多的人倾向于通过各类可视化产品使静态的数据“活”起来。基于此背景,我们结合滴滴的各业务线发展,打造了本文介绍的数据可视化大屏产品。
1.
前言
随着技术的发展,更多的人不满足于使用基础的图表来展示数据,如何让数据更直观、更炫酷的展示成为了大家的追求。为了能更好的展示滴滴各业务线的发展,本文介绍的数据可视化大屏从城市、全国和全球维度展示了业务线的实时信息,下面将从基础地图的构建、轨迹、气泡、热力、飞线、散点六个方面来讲述一下开发中遇到的难题及解决方案。
1)地图的一次性加载,考虑到易用性和维护性,需自研一套地图框架;
2)大量数据涉及到的性能问题,包含数据的计算、传输和实时渲染;
3)数据业务方较多,接口稳定性和维护性问题;
4)可视化还原度;
1)自研地图框架map3;
2)将数据计算移到后端,对数据进行压缩,同时在大量数据的传输方面采用ArrayBuffer。尽量减轻GPU和CPU的压力,提高页面流畅度。
3)因为数据大屏对数据的依赖性较强,为了保障展示的稳定性,对数据采取了缓存兜底方案;
4)通过开发调试面板,降低与设计师沟通的成本,为提高可视化还原度提供了便利。
const map3 = new Map3({
container: string | HTMLElement;
style: Style;
center?: number[];
pitch?: number;
bearing?: number;
zoom?: number;
controller?: boolean;
hash?: boolean;
});
最后说下项目中对geobuf的改进。主要包含以下两点:
1)因为项目中绘制的区域相对较小,例如杭州,范围在东经118°21′-120°30′,北纬29°11′-30°33′ °,这样对于每一条LineString或者polygon的首个点来说,存储全部的数值也存在一些浪费,于是我们在计算前加入了一个减法计算,将所有的经纬度减去一组数值,例如上面的例子中,减去[120, 30],这样数值将变为[[7045, 20248], [1, -22], [-1, -9]...],首个点的数值更小了,所需要的字节数也更少了(这一步虽然减小了一点数据量,但失去了灵活性,需要权衡利弊)。
2)地图绘制都存在投影计算,我们将投影计算放在了encode的代码中,浏览器接收到数据decode后可以直接使用,这步可以减小浏览器的计算,节省cpu和计算时间的开支。
▍4.可视化还原
图3.4 数据大屏设计稿
这部分主要介绍的是上图长轨迹的实时绘制,同时这也是影响性能的很重要的一部分。正式展开叙述之前先看下需求是怎样的。如动图中所示,需要获取实时轨迹数据在前端进行展示,轨迹需要流动起来,且在地图视野拉近(近看城市)时运动变慢、轨迹变细,在地图视野拉远时(俯视北京全城)运动变快、轨迹变粗。效果要反映真实的订单情况,所以数据需要实时更新。下面从几个重要的点来展开介绍。
上图展示了着色器的相关限制,与纹理相关的部分限制了片元着色器的纹理单元数量不超过16个,这就意味着如果我们将每个气泡单独绘制在一个canvas上,至多只能绘制16种类型的气泡,当然这对于我们当前的业务场景也是够用的,但是容量天花板相对较低。假设我们一个纹理单元绘制两种气泡,最多可绘制32种,以此类推。同时需要注意一下单个纹理单元的最大限制是16384,假设一张图片的大小是200*200,那么一个纹理单元最多容纳6561张图片。如果我们绘制的范围超过该限制,即要添加新的纹理单元,所以要注意边界判断。
时间范围
可以展示过去一分钟、十分钟、一小时等任意时间间隔内的热力情况。
半径大小
可以以任意距离为单位,该范围内的订单数对应的色阶上的颜色即为该范围的热力图颜色。 色阶
热力图的颜色是可调的。
▍2.映射热力图
▍3.调试工具
在调试过程中,因为灯光和屏幕的分辨率等因素导致最终在大屏上展示的效果和在台式机上的效果还是有一些差别的,加之设计师侧也不方便准确的描述差异,例如红色淡一些这些想象空间很大的描述,所以沟通之后开放了上图所示的半径和四种颜色的调试接口,方便设计师现场实时查看热力图的改动效果。
为了加快调参时热力图的响应,同时减少计算量,我们对原本的热力图代码做了一些修改。原来代码里和重绘有关的方法是repaint方法,但是调用该方法触发的操作是全部重新绘制,包括灰度图、线性色谱和热力图等。当我们只改变颜色时,是线性色谱发生改变从而着色时影响最终的热力图,灰度图并没有改变,所以我们添加了recolor方法,调试颜色时不会重绘灰度图。但是,如果改变了半径范围,还是需要调用原本的repaint方法,全部重新绘制。
▍4.参数解读
7.
飞线
1)取点
2)纹理映射
上一步我们获取的100个点是描绘一条完整曲线的全部点,但是从图7.1(b)中可以看出,飞线在飞的过程中展示的是完整的纹理,但是飞线长度只占总长度的1/3。如果按照正常的映射的话应该是完整的纹理对应完整的曲线,所以在飞的过程中应该只展示对应的纹理部分。那么如何实现动图中的效果呢?
第一反应就是改变顶点坐标,例如第一次render第0~30个点的范围,第二次render第10~40个点的范围,以此类推。效果确实是实现了,但是性能上有问题,由于频繁的更换顶点导致了页面的卡顿。
想到的第二种方案就是100个点全部绘制,在render的过程中z不断变换顶点对应的uv坐标。例如开始是第0~30个对应的uv坐标从0~1,下一次是第10~40个点对应的uv坐标从0~1,以此类推可以满足1/3长度的飞线展示完整的纹理,且不断移动。在这里还有个优化,一开始是在每次render的时候去遍历uv数组更改每个点对应的值,但是遍历数组也是耗性能的,特别是当数据量大的时候,后来是将一个常数传进了shader中用于计算每个点此时的uv坐标,每次render都会改变这个数,同时同步到shader中。
8.
散点 平时我们用静态的散点比较多,这种实现很简单。但当展示的信息量不是很丰满时就显得页面有些空洞,这时候就需要添加动态呼吸的散点,让页面看起来更热闹。我们在散点大小变化的过程中对应的改变其透明度,产生渐隐渐现的呼吸效果,同时我们可以随机点出现的位置,让效果看起来更灵动。
9.
结语 以上就是在全业务之城开发过程中的一些总结和思考,日后在不断开发数据可视化大屏的同时,我们也会加速沉淀通用组件库和搭建平台。 ·················END················· 推荐阅读
欢迎长按扫码关注「数据管道」
推荐阅读
欢迎长按扫码关注「数据管道」