[置顶] (2)基于Echarts插件的多维数据可视化设计和实现
2017-09-06 10:06
996 查看
前言:本文针对多维离散数据和多维连续数据,分别构建多维可视化图表。由于离散和连续图表展示的效果差别大,故多维图表的可视化,分为两大部分进行设计和实现。
总体设计思想:主要是采用类似树形图结合柱状图、线图、散点图、面积图。树形图主要展示的是维度的值,和各个维度值之间的关系。如图(1)所示。第一条数据:[“企业”, “个人政府征订”,”奉化站”,”2013-07”, 6500],在图表中与这条数据相关的内容,除了第一根蓝色方形,还包括红线相连的名称(企业,个人政府征订,奉化站)。如果把图中所有的名称相互连接起来,像图中红线一样,是不是感觉像一棵树?特别要注意的是(企业-个人政府征订-奉化站) 可以看成一个分组,在这个分组里可能有不止一个值,它分布着多条数据。
图1:多维数据展示
总体实现方式:要实现如图1所示的效果,可以采用Echarts插件的各种组件经过一系列的计算和整合,当然其中肯定涉及插件若干接口的改造和新增。最终生成的图表才能展示出我们需要的效果。
多维离散型数据的设计思路:
多维离散型数据的定义:所有的维度都是离散的,不存在任何连续型维度。对于多维离散型数据,由于在每一条数据相互之间在轴上并没直接关系,关系同属于一个分组。对于同一个度量,各个分组展示出来的图表类型应该是一致的。(特殊情况,对于三个维度的数据,类目气泡图也是可以展示的)
基于Echarts插件的多维离散型数据可视化实现
一、问题阐释
1. 基于Echarts插件,要实现多维数据可视化,是否有官方接口,如果有官方接口不就轻松搞定?
通过查看Echarts的文档,发现确实有一个图表类型,可以展示多维数据,如图2所示,但是这种展示方式,貌似并不能适合所有多维数据,而且可视化的效果有点差强人意。
图2 Echarts的多维数据展示
2. 如果要实现我们设计的树形加普通图表的展示方式,如何实现树形?如何进行分组?每一个分组高和宽占用整个图表高和宽多少?每一个分组由于是同一个度量,是不是应该共享同一个度量轴?
对于这些问题,暂时不做回答。接下来,我会手把手教大家如何解决这些问题,最终实现整个多维图表。
二 多维数据的结构
要实现整个设计,一共需要三个数据结果,我把它们取名:data,measure, dimension。具体的数据例子,如下所示,data:是一个二级数组[[d1,..,dn,m1,..,mn],…,[d1,..,dn,m1,..,mn]] (dn:表示第n个维度的信息,而mn表示n个度量的信息),它存储着图表要显示的多维数据详情,每一条数据时一个数组,数组的前几位是维度的值,后几位是度量的值。measure:是一个数组,它存储着度量字段的相关信息,数组内每条数据时一个js对象,对象的key属性,是度量的关键字,用于区分其他度量;alias属性时度量的别名,用于显示度量的中文名称;state属性表示度量所对应的图表类型。dimension :也是一个数组,构造跟measure差不多,公共的属性表示的含义和measure是一样的,continuity字段表示的是维度的连续性,’11’表示是连续的。colType:表示维度的数据类型。如果是离散的,维度的数据类型一般都是字符串型的。
三、Echarts接口的扩展和新增
为了解决(1)提出的问题,在Echarts的接口一共需要扩展一个接口,新增2个接口。
扩展的接口主要是:
Axis.prototype.dataToCoord;该接口主要负责计算名称或者叫标签的坐标值;在Echarts中标签值叫label,而字符串的label一般用category离散的轴表示,每一个label之间的距离是等距的。这个与我们要求是有出入的,因为在多维图表中,每一个label之间并不一定是等距的,所以我们要定距给其一个值,使之满足我们的要求。而这个值的计算是在Echarts插件外部进行的,由函数calLabelProperty完成。
calLabelProperty函数的功能,主要是计算跟label定位和显示相关的三个属性值,line:表示两个label之间的分割线;area表示每条label信息在图表中所占的宽度;curBit表示每条label在图表上的位置。代码中normalizeData变量保存着每一个label在一条轴上的比率值,介于0和1之间。
新增的接口主要是:label之间的分割线、悬浮label所占区域时候区域背景色改变的变化接口。
新增接口的定义在AxisBuilder构造函数所属区域中builders.axisLabel函数中。
第二个重要的接口新增是buildSplitLine函数,主要负责构建label之间的分隔线。具体代码和注释如下所示:
这几个接口具体定义的位置,请参考我详细的源码。
源码下载地址:http://download.csdn.net/download/mulumeng981/9985030 (基于Echarts最新版3.7.1)
如果你注意到一个不准确或似乎不太正确的地方,请让我知道。谢谢!
总体设计思想:主要是采用类似树形图结合柱状图、线图、散点图、面积图。树形图主要展示的是维度的值,和各个维度值之间的关系。如图(1)所示。第一条数据:[“企业”, “个人政府征订”,”奉化站”,”2013-07”, 6500],在图表中与这条数据相关的内容,除了第一根蓝色方形,还包括红线相连的名称(企业,个人政府征订,奉化站)。如果把图中所有的名称相互连接起来,像图中红线一样,是不是感觉像一棵树?特别要注意的是(企业-个人政府征订-奉化站) 可以看成一个分组,在这个分组里可能有不止一个值,它分布着多条数据。
图1:多维数据展示
总体实现方式:要实现如图1所示的效果,可以采用Echarts插件的各种组件经过一系列的计算和整合,当然其中肯定涉及插件若干接口的改造和新增。最终生成的图表才能展示出我们需要的效果。
多维离散型数据的设计思路:
多维离散型数据的定义:所有的维度都是离散的,不存在任何连续型维度。对于多维离散型数据,由于在每一条数据相互之间在轴上并没直接关系,关系同属于一个分组。对于同一个度量,各个分组展示出来的图表类型应该是一致的。(特殊情况,对于三个维度的数据,类目气泡图也是可以展示的)
基于Echarts插件的多维离散型数据可视化实现
一、问题阐释
1. 基于Echarts插件,要实现多维数据可视化,是否有官方接口,如果有官方接口不就轻松搞定?
通过查看Echarts的文档,发现确实有一个图表类型,可以展示多维数据,如图2所示,但是这种展示方式,貌似并不能适合所有多维数据,而且可视化的效果有点差强人意。
图2 Echarts的多维数据展示
2. 如果要实现我们设计的树形加普通图表的展示方式,如何实现树形?如何进行分组?每一个分组高和宽占用整个图表高和宽多少?每一个分组由于是同一个度量,是不是应该共享同一个度量轴?
对于这些问题,暂时不做回答。接下来,我会手把手教大家如何解决这些问题,最终实现整个多维图表。
二 多维数据的结构
要实现整个设计,一共需要三个数据结果,我把它们取名:data,measure, dimension。具体的数据例子,如下所示,data:是一个二级数组[[d1,..,dn,m1,..,mn],…,[d1,..,dn,m1,..,mn]] (dn:表示第n个维度的信息,而mn表示n个度量的信息),它存储着图表要显示的多维数据详情,每一条数据时一个数组,数组的前几位是维度的值,后几位是度量的值。measure:是一个数组,它存储着度量字段的相关信息,数组内每条数据时一个js对象,对象的key属性,是度量的关键字,用于区分其他度量;alias属性时度量的别名,用于显示度量的中文名称;state属性表示度量所对应的图表类型。dimension :也是一个数组,构造跟measure差不多,公共的属性表示的含义和measure是一样的,continuity字段表示的是维度的连续性,’11’表示是连续的。colType:表示维度的数据类型。如果是离散的,维度的数据类型一般都是字符串型的。
var data = [ [ "企业", "余姚站", "2013-05", 0.015 ], ... [ "普通客户", "仓基站", "2013-01", 0.019 ] ]; var dimension = [ { "key":"cust_type", "alias":"cust_type", "continuity":"00", "colType":"string" }, .... { "key":"Month", "alias":"Month", "continuity":"11", "colType":"date" } ]; var measure = [ { "key":"huan_bi", "alias":"总和_huan_bi", "state":"bar" }, ... { "key":"tong_bi", "alias":"总和_tong_bi", "state":"bar" } ];
三、Echarts接口的扩展和新增
为了解决(1)提出的问题,在Echarts的接口一共需要扩展一个接口,新增2个接口。
扩展的接口主要是:
Axis.prototype.dataToCoord;该接口主要负责计算名称或者叫标签的坐标值;在Echarts中标签值叫label,而字符串的label一般用category离散的轴表示,每一个label之间的距离是等距的。这个与我们要求是有出入的,因为在多维图表中,每一个label之间并不一定是等距的,所以我们要定距给其一个值,使之满足我们的要求。而这个值的计算是在Echarts插件外部进行的,由函数calLabelProperty完成。
calLabelProperty函数的功能,主要是计算跟label定位和显示相关的三个属性值,line:表示两个label之间的分割线;area表示每条label信息在图表中所占的宽度;curBit表示每条label在图表上的位置。代码中normalizeData变量保存着每一个label在一条轴上的比率值,介于0和1之间。
dataToCoord: function (data, clamp) { var extent = this._extent; var scale = this.scale; var normalizeData = this.model.get('normalizeData'); //沈才良 @face if (normalizeData) { data = normalizeData[data]; } else { data = scale.normalize(data); } if (this.onBand && scale.type === 'ordinal') { extent = extent.slice(); fixExtentWithBands(extent, scale.count()); } return linearMap(data, normalizedExtent, extent, clamp); },
calLabelProperty: function(chartLabelData, isContinue) { var categoryLabel = []; var sum = 0, labelArray, percentArray, areaArray; var curSum = 0, curBit = 0; var curBitArray = []; var classNum = chartLabelData.length; for (var i = 0; i < classNum; i++) { //如果图表是离散的,则是label的总和 sum = isContinue ? chartLabelData[classNum - 1].labelData.length : chartLabelData[i].sum; //当前的统计和 curSum = 0; categoryLabel[i] = {}; /* labelArray: 保存每一个维度上的label lineArray: 保存各个label之间的分隔线的位置 curBitArray: 保存各个label的位置 areaArray: 保存各个label在图表上所占的区域宽 */ labelArray = categoryLabel[i].label = []; lineArray = categoryLabel[i].line = []; curBitArray = categoryLabel[i].percent = []; areaArray = categoryLabel[i].area = []; //求出每一个类别子项的显示位置的值 for(var j = 0, dataItem; j < chartLabelData[i].labelData.length; j++) { dataItem = chartLabelData[i].labelData[j]; //连续的时候,离散的最后一个维度计算curSum等于 j + 1 if (isContinue && i == (classNum - 1)) { curSum = j + 1; } else { curSum += dataItem.count; } //删除label里面的换行符号 labelArray.push((dataItem.labelName + '').replace(/\r\n/g, ' ')); lineArray.push(curSum / sum); } //每一个维度的第一个分组信息计算,跟其他分组不一样 curBitArray[0] = lineArray[0] / 2; areaArray[0] = lineArray[0]; for ( j = 1; j < lineArray.length; j++) { areaArray.push(lineArray[j] - lineArray[j-1]); curBitArray.push(lineArray[j - 1] + (lineArray[j] - lineArray[j - 1]) / 2); } } return categoryLabel; },
新增的接口主要是:label之间的分割线、悬浮label所占区域时候区域背景色改变的变化接口。
新增接口的定义在AxisBuilder构造函数所属区域中builders.axisLabel函数中。
//添加标签的hover事件 buildHoverRect({ group: this.group, pos: pos, textEl: textEl, axisModel: axisModel, contentBit: areaData[index] }); /** * 当用户悬浮在label上面 * 则显示整个label分组所占的空间 * 由于zrender默认的hide和show函数 * 当数据超过100多时,性能差,故采用 * 设置opacity达到显示和隐藏的效果 */ textEl.on('mouseover', function (event) { this.hoverRect.setStyle('opacity', 0.24); }); /** * opacity = 0表示隐藏该label有颜色背景 * textEl:表示每一个label在图表上的实体 */ textEl.on('mouseout', function (event) { this.hoverRect.setStyle('opacity', 0); }); /** * 生成hover时候出现的矩形和背景 * @param {Object} params: userful params to build hover rect * @param {Number} contentBit: 每一个label所在的区域长度,[0,1]之间 * @param {Array.<Number>} pos: label的坐标值,二维的 */ function buildHoverRect(params) { var axisModel = params.axisModel; var coordinateRect = axisModel.coordinateSystem && axisModel.coordinateSystem._rect; var extent = axisModel.axis.getExtent(); var axisWidth = extent[1] - extent[0]; b9c3 //得到显示区域的宽度大小 var contentWidth = Math.ceil(axisWidth * params.contentBit); var rect = new graphic.Rect({ shape: { x: params.pos[0] + coordinateRect.x - contentWidth / 2, y: params.pos[1] + coordinateRect.y - 7, height: 28, width: contentWidth }, z: 2, style: { fill:'green', opacity: 0, lineWidth: 1 }, silent: false }); //添加到group中,以便echarts统一绘制 params.group.add(rect); //方便隐藏和显示 params.textEl.hoverRect = rect; }
第二个重要的接口新增是buildSplitLine函数,主要负责构建label之间的分隔线。具体代码和注释如下所示:
/** * 生成label之间的分割线,方便用户识别不同的label分组 * {Array} group:分组组件,生成的line只有放入这个组件中,才能统一绘制 * {Object} axisModel: 坐标轴的model模型,主要保存与坐标轴相关的信息 */ function buildSplitLine(group, axisModel) { var axisType = axisModel.mainType; var show = axisModel.get('classSplitLine.show'); if (axisType != "singleAxis" || show !== true) { return; } //获取分割线的所需的数据,由外部接口calLabelProperty计算出 var data = axisModel.get('classSplitLine.data'); var coordinateRect = axisModel.coordinateSystem && axisModel.coordinateSystem._rect; var extent = axisModel.axis.getExtent(); var axisWidth = extent[1] - extent[0]; var point1 = []; var point2 = []; for (var i = 0 ; i < data.length - 1; i++) { //求出分隔线两个端点的坐标值 point1[0] = data[i] * axisWidth + coordinateRect.x; point1[1] = coordinateRect.y; point2[0] = data[i] * axisWidth + coordinateRect.x; point2[1] = coordinateRect.y + 26; var line = new graphic.Line(graphic.subPixelOptimizeLine({ shape: { x1: point1[0], y1: point1[1], x2: point2[0], y2: point2[1] }, style: { stroke: "#d3d3d3", color: '#666', // lineDash: [4, 4], opacity: 0.5, lineWidth: 1 }, z2: 3, silent: true })); group.add(line); } }
这几个接口具体定义的位置,请参考我详细的源码。
源码下载地址:http://download.csdn.net/download/mulumeng981/9985030 (基于Echarts最新版3.7.1)
如果你注意到一个不准确或似乎不太正确的地方,请让我知道。谢谢!
相关文章推荐
- (3)基于Echarts插件的多维数据可视化设计和实现
- [置顶] 基于Echarts插件的省市区多级地图下钻和返回功能实现
- DTCC 2013:基于SQL Server的大数据解决方案设计及实现
- 基于STK的导弹飞行数据快速可视化仿真实现
- 数据插入一种基于CAS的无锁并发HashTable设计及C代码实现
- 基于Windows CE的数据采集与控制系统设计与实现
- CSDN开源夏令营 基于Compiz的switcher插件设计与实现之compiz特效插件介绍及特效实现
- CSDN开源夏令营 基于Compiz的switcher插件设计与实现之编译compiz源码
- CSDN开源夏令营 基于Compiz的switcher插件设计与实现之熟悉switcher的功能(ccsm)
- 简单易学多维数据可视化R实现:神奇的卡通脸谱图Chernoff faces
- 基于销售数据的决策支持系统的设计与实现源码论文
- 快速基于echarts的大数据可视化
- 基于UDP的可靠性数据传输服务——设计与实现
- 基于BBB的4轮移动轮式机器人系统设计与实现(八)-- SIM908 GPS 数据获取
- 基于ini配置文件实现多维数组数据的按行存储和读取
- ECharts-基于Canvas,纯Javascript图表库,提供直观,生动,可交互,可个性化定制的数据可视化图表
- 基于jquery的finkyUI插件与Ajax实现页面数据加载功能
- 基于MFC的插件应用程序设计与实现
- 内测“百度图说” 基于Web的可视化数据分享平台,嵌入ECharts
- 基于java数据采集串口通讯的设计和实现