模拟实时动态趋势

主要用到echart插件实现趋势图绘制,为了模拟实时效果,并且保证趋势图匀速移动,实现主要思想如下:

1.为保证匀速移动,趋势图的移动以前端时间间隔为准,时间间隔到了,趋势图就移动一次,不管后台数据正常返回与否,有后台返回的数据则显示正常数据,没有正常数据则前端自行填补数据。

2.趋势图x轴显示的时间,是第一次和后台交互,从时间戳解析出来的。最新的时间,就是取第一次请求数据中后台最新的时间,以后的时间,由前端自行计算nextTime,所以第一次与后台的协定,特别重要。

3.初始化整个趋势图后,以后一份份数据请求。后台以“尽最大努力”发送数据位目标,前端indexRight记录最新最近的正常返回数据。

4.cacheData数组用来缓存后台数据,生成趋势图时,参考缓存数据,并通过一些“填空补缺”的机制,绘制趋势图。

5.趋势图为了保证轴两端都有时间,对xAxis 的interval设置函数实现。

趋势图部分:

  1 var trendChart = null;
  2 var initTrendFlag = false;
  3 /**echart反复创建**/
  4 function createTrend(convertedData) {
  5     if(convertedData && convertedData[0] && convertedData[1]){
  6         $("#trendContent").attr(‘class‘,‘trendContentStyle‘);
  7         var title = "";
  8         var historyOption =    {
  9             title : {
 10                 text: title
 11             },
 12             tooltip : {
 13                 trigger: ‘axis‘,
 14                 formatter:"{b}<br>{c}"
 15             },
 16             legend: {
 17                 show:false,
 18                 data:[‘‘]
 19             },
 20             dataZoom : {
 21                 show : false,
 22                 start : 0,
 23                 end : 100
 24             },
 25             xAxis : [
 26                 {
 27                     type : ‘category‘,
 28                     boundaryGap : false,
 29                     axisLabel: {
 30                         show: true,
 31                         rotate: 0,
 32                         interval: function(index,data){
 33                             if(index == 0|| index == showNum-1 || index % 40 == 0){
 34                                 return true;
 35                             }else
 36                             {
 37                                 return false;
 38                             }
 39                         },
 40                         onGap: true
 41                     },
 42                     data : convertedData[0] //[0,0,0,0,0,0,0,0,0,0]
 43                 }
 44             ],
 45             yAxis : [
 46                 {
 47                     show:false,
 48                     type : ‘value‘,
 49                     scale: true,
 50                     name : ‘价格‘,
 51                     boundaryGap: [0.2, 0.2],
 52                     min:0
 53                 }
 54             ],
 55             series : [
 56                 {
 57                     itemStyle: {normal: {areaStyle: {type: ‘default‘,color:‘#D8E2F7‘}}},
 58                     symbol:‘circle‘,
 59                     symbolSize:0.02,
 60                     name:‘‘,
 61                     smooth:true,
 62                     type:‘line‘,
 63                     data:convertedData[1] //[0,0,0,0,0,0,0,0,0,0]
 64                 }
 65             ]
 66         };
 67             //切换到有数据清空下,重新set整个option
 68
 69             trendChart = echarts.init(document.getElementById(‘trendContent‘));
 70             trendChart.setOption(historyOption);
 71     }else{
 72        if(trendChart && trendChart.clear){
 73            trendChart.clear();//从有数据切换到无数据
 74        }
 75         $("#trendContent").attr(‘class‘,‘trendContentStyle nodata‘);
 76     }
 77 }
 78
 79 function initTrend(poiid){
 80     cacheData = [];//切换地区,清空上一次缓存数据
 81     initTrendFlag = false;//初始化标识为false
 82     var url = "/lte/realtimeheatmap/poilinkchart";
 83     var para = {
 84         province:condition.provincecode,
 85         city:condition.citycode,
 86 //        poiid:‘g20atblc‘,
 87         poiid:poiid,
 88         time:""
 89     };
 90 //"/lte/realtimeheatmap/poilinkchart?province=330000&city=330100&poiid=g20atblc&time=2016-07-10+14:50:00"
 91     $.ajax({
 92         type: "GET",
 93         contentType: "application/json",
 94         url: url,
 95         data: para,
 96         async: false, //需要同步;初始化成功后,趋势图再做其他
 97         success: function (data) {
 98             //初始化成功,改变indexNow;缓存、更新cacheData,不绘制趋势图
 99 //            showNum = data.length;//第一次初始化,数据就有可能缺失
100             if(!data || data.length <1){
101                 createTrend(null);
102                 return;
103             }
104             var newestTime = data[data.length-1][0];
105             if(indexRight < newestTime){
106                 indexRight = newestTime;//第一次初始化,indexRight = "" ,一定会成功赋值的
107             }
108             indexNow = indexRight;
109             indexTail = getNextTimeStamp(indexNow,timeInterval,-showNum + 1);//显示[indexTail,indexNow]showNum个数据
110             initCache(data);//用第一次数据,初始化规整的cacheData
111             var convertedData = convertTrendData();//结合cacheData中数据,生成趋势图所需数据
112             createTrend(convertedData);
113             initTrendFlag = true;
114             requestTrendData();//初始化之后,立马发起一次请求
115         },
116         error: function (XMLHttpRequest, textStatus, errorThrown) {
117             createTrend(null);
118             initTrendFlag = false;
119         }
120     });
121 }
122
123 /**不断定时调用,发请求,请求最新的数据,来生成趋势图**/
124 function requestTrendData() {
125     if(!cacheData || cacheData.length < 1) return;//初始化失败
126 //    var indexTail = getNextTimeStamp(indexNow,timeInterval,-showNum);
127     if(indexRight < indexTail){
128         indexRight = indexTail;//整个趋势图显示数据都是前端补齐的
129     }else if(indexRight > indexNow){
130         indexRight = indexNow;//某一次最新数据超过indexNow
131     }
132     var url = "/lte/realtimeheatmap/poilinkchart";
133     //"/poilinkchart?province=330000&city=330100&poiid=g20atblc&time=2016-07-10+14:50:00"
134     var para = {
135         province:condition.provincecode,
136         city:condition.citycode,
137         poiid:‘g20atblc‘,
138         time:indexRight
139     };
140
141     $.ajax({
142         type: "GET",
143         contentType: "application/json",
144         url: url,
145         data: para,
146         //async: false, //需要异步;不过,存在几个ajax返回同事addData去修改cacheData的情况?
147         success: function (data) {
148             /**拿到数据,填装数组,绘制趋势图,更新indexRight**/
149             if(!data || data.length <1){
150                 //查无数据,返回 []
151                 return;
152             }
153             var newestTime = data[data.length-1][0];
154             if(indexRight < newestTime){
155                 indexRight = newestTime;//indexRight只右移;不再次查询之前的
156             }
157             addResponseData(data);    //更新cacheData;不绘制趋势图;更新indexTail、indexNow
158 //            var convertedData = convertTrendData(cacheData);
159 //            createTrend(convertedData);
160         },
161         error: function (XMLHttpRequest, textStatus, errorThrown) {
162              /**ajax失败,添加测试数据;真实情况ajax失败直接不予处理,时间间隔到了,前端就走**/
163          //   addResponseData([[getNextTimeStamp(indexNow,timeInterval,1),Math.random()*100]]);
164         }
165     });
166 }

缓存数据部分:

/**趋势图只有第一次初始化成功后,才开始持续走动;在初始化成功的ajax回调里面调用其他函数**/

//记录收到的最新一次的  真实后台数据的时间戳 ,超期了,等于indexTail
// 为了填补(连续缺数据的情况),发给后台;如果30分钟的到了,25分钟的还是前端填补的,那直接忽略
var indexRight = "";//在有新数据来到的时候更新
var indexNow = "";//指向趋势图  显示的  最新时间戳;只在前端时间间隔到了的时候更新
var indexTail = "";//指向趋势图 显示的 最早时间戳;只在前端时间间隔到了的时候更新
var timeInterval = 1;//时间间隔,五分钟,可配置
var showNum = 481;//前后端提前协议好,因为第一次初始化的时候,数据长度就不确定;这样保证indexNow和indexTail间距固定
//存储ajax返回的趋势图数据;不存储伪造数据
var cacheData = [];//cacheData的第一个数据cacheData[0][0],不一定对齐显示的最左侧数据indexTail,最后一个数据,也不一定对齐indexNow(可能缓存未来的)
//cacheData[0] = [];//先初始化第一个元素为一个空的数组

//var mapCachedata = {};//日期-数据,key-value形式,更好填充?
/**构造一个ajax返回的趋势图样例数据**/
/**先假设趋势图显示10分钟数据,时间间隔为1分钟**/
 var initData = [
    ["2016-07-01 08:30:00",50],
    ["2016-07-01 08:31:00",40],
    ["2016-07-01 08:32:00",70],
    ["2016-07-01 08:33:00",55],
    ["2016-07-01 08:34:00",55],
    ["2016-07-01 08:35:00",25],
    ["2016-07-01 08:36:00",155],
    ["2016-07-01 08:37:00",15],
    ["2016-07-01 08:38:00",95],
    ["2016-07-01 08:39:00",52]
];

/**第一次收到数据后,形成规则的cacheData数组**/
function initCache(resData){
    var timeStamp = indexTail;
    var searchData = null;
    var flag = false;
    for(var k=0;k<showNum;k++){
        cacheData[k] = [];
        cacheData[k][0] = timeStamp;
        cacheData[k][1] = 0;
        //遍历resData上的数据,如果ajax返回数据里面有,直接填上,如果没有填补之前的
        searchData = getSearchData(timeStamp,resData);
        if(searchData != null){
            flag = true;//一旦有一条有一条效数据,后续空缺填补之前的
            cacheData[k][1] = searchData;
        }else if(flag){
            //前段自行填补
           cacheData[k][1] = cacheData[k-1][1];
        }
        timeStamp = getNextTimeStamp(timeStamp,timeInterval,1);
    }
}

/**需要把cacheData从稀疏数组,转化为稠密数组吗?
 * cacheData前段,在初始化正确后,就一直会保证是非稀疏的
 * cacheData[i]为undefined的空位不能剔除掉,有可能有数据会来填补
 * **/
function fillUpCache() {
    if (!cacheData || !cacheData[0] || !cacheData[0][0]) {
        {
            //cacheData第一个数据为空时;修改头部为indexTail,0
            cacheData = [];
            cacheData[0] = [];
            cacheData[0][0] = indexTail;
            cacheData[0][1] = 0;
//         cacheData.unshift([indexTail,0]);//在头部插入一个数据;这样会多
        }

        for (var i = 1; i < cacheData.length; i++) {
            /**这样只能保证数组为没有返回的数据预留了空位的情况下的填充;如果第一次返回数据就是间断的**/
            if (cacheData[i] == undefined) {
                cacheData[i] = [];
                cacheData[i][0] = getNextTimeStamp(cacheData[i - 1][0], timeInterval, 1);//要确保第一个非空;i从1开始
                cacheData[i][1] = cacheData[i - 1][1];
            }
        }
    }
}
/**从cacheData取数据给趋势图加载;
 * 不存在则伪造;填补cacheData
 * 行列转换,转化时间戳为显示时间形式**/
function convertTrendData(){
    var time = [];
    var ueNum = [];
    var timeStamp = indexTail;
    var searchData = null;
    for(var k=0;k<showNum;k++){
        var timeArr = timeStamp.split(" ");
        if(timeArr && timeArr[1]){
            var lastTime = timeStamp.split(" ")[1];
            if(lastTime[0] == ‘0‘){
                lastTime = lastTime.substring(1);
            }
            time[k] = lastTime;
        }else{
            time[k] = timeStamp;//时间戳格式有误;直接赋值整个时间戳
        }
        ueNum[k] = 0;
        //遍历cacheData上的数据,如果缓存里面有,直接填上,如果没有填补之前的
        searchData = getSearchData(timeStamp,cacheData);//缓存上基本有序,按理说,可以优化搜索,不应该每次都去遍历所有
        if(searchData != null){
            ueNum[k] = searchData;
        }else{
            if(k>0){
                ueNum[k] = ueNum[k-1];//不是第一个数据,就取前一个数据的
                addResponseData([[timeStamp,ueNum[k]]]);//伪造的数据,也必须纳入缓存
            }else{
                ueNum[k] = 0;//第一个数据,没有历史,直接给0;第一次初始化cacheData成功后,不会搜索不到
            }
        }
        timeStamp = getNextTimeStamp(timeStamp,timeInterval,1);
    }
        return [time,ueNum];

}
/**时间到了,填入之前的数据;并且从头到尾检查数组是否有空缺,有空缺填补上一个**/

/**填装数据,有ajax返回数据需要填充,时间间隔 到了也需要填充;添加新数据到尾部,drop头数据**/

/**ajax返回的数据,是合理时间点,有效数据;存在三种情况:填补(更新)之前数据,填充当前最新数据,或者缓存未来数据**/
function addResponseData(reqData){
        /**返回数据是“连续时间点”的**/
    var time = null;
    var data = null;
    var insertIndex = null;
    if(reqData && reqData.length > 0){
        for(var i=0;i<reqData.length;i++){
            if(reqData[i] && reqData[i][0] && reqData[i][1]){
                time = reqData[i][0];
                data = reqData[i][1];
                insertIndex = getIndexByTime(time);
                if(insertIndex !== null){
                    cacheData[insertIndex] = [];
                    cacheData[insertIndex][0] = time;
                    cacheData[insertIndex][1] = data;
                }
            }
        }
        /**添加新数据后,暂时不挪动indexNow等;每次都在前端时钟结束,调用move方法移动**/

    }
}

/**前端计时器到时间间隔,坐标轴移动一个单位,绘制趋势图**/
function trendMove(){
    indexNow = getNextTimeStamp(indexNow,timeInterval,1);
    indexTail = getNextTimeStamp(indexTail,timeInterval,1);
    if(cacheData && cacheData[0] && cacheData[0][0]){
        //如果cacheData首个元素为 undefined 或者[] 则无法删除数据了;
        /** 在第一次初始化的时候,如果首个元素正常,后续每挪动一步,必有填补,cacheData在indexTail-indexNow之间的区段是非稀疏的数组
         * 首个元素就不可能为空了**/
        while(getNextTimeStamp(indexTail,timeInterval,-1) > cacheData[0][0]){
            cacheData.shift();//删除超期数据
        }
    }
    var convertedData = convertTrendData();//结合cacheData中数据,生成趋势图所需数据
    createTrend(convertedData);
}

/**初始化的时候,后台需要返回时间戳,例如2016-07-08 7:25   **/
function getNextTimeStamp(currentTime,timeInterval,num){
    var d = new Date(currentTime);
    d.setMinutes(d.getMinutes() + timeInterval * num);
    var nextStr = getTimeStamp(d)[0];//暂时先用完整时间"2016-07-08 7:25"
    return nextStr;
}

/**根据给定时间戳,查找数据应该填入cacheData位置;更新index;跳跃式更新,数组中间的是undefined,数组长度会变长+N**/
function getIndexByTime(timeStamp){
    if(timeStamp < indexTail){
        //过期数据
        return null;
    }else if(timeStamp <= indexNow){
        //趋势图正在显示的历史时间
        //折半查找不行,因为可能中间的数据是undefined 无法确定是在哪段
        for(var tail = cacheData.length - 1;tail >=0;tail--){
            if(cacheData[tail] && cacheData[tail][0] && cacheData[tail][0] === timeStamp){
                return tail;
            }
        }
        return null;//数据异常,没匹配上各个时间点,一般不应该出现
    }else{
        //未来数据
        var tempTimeStamp = indexNow;
        var tempIndex = cacheData.length - 1;
        while(timeStamp > tempTimeStamp){
            tempTimeStamp = getNextTimeStamp(tempTimeStamp,timeInterval,1);//后移一个间隔
            tempIndex ++;
            if(timeStamp === tempTimeStamp){
                return tempIndex;
            }
        }
        /**数据间隔应该可以逐步+1 timeInterval吻合,一般不应该返回null;除非本身cacheData时间点“不连续”**/
        return null;
    }
}

/**为填充趋势图数据,在cacheData中查找数据**/
function getSearchData(timeStamp,cacheData){
    //折半查找行不通,虽然有序,但是cacheData可能是稀疏数组
//    if(!cacheData || !cacheData.length) return null;
    for(var index=0;index<cacheData.length;index++){
        if(cacheData[index] && cacheData[index][0] && cacheData[index][0] === timeStamp && cacheData[index][1] != undefined){
            return cacheData[index][1];//"1a3"这种非法数据没有排除
        }
    }
    return null;//数据没有缓存在cacheData中

}

//将日期对象转化为标准的时间戳以及趋势图横坐标显示形式:["2016-07-08 7:25","7:25"]
function getTimeStamp(dateObj){
    var strYear = dateObj.getFullYear();
    var strMonth = dateObj.getMonth() + 1;
    if (strMonth < 10) {
        strMonth = ‘0‘ + strMonth;
    }
    var strDay = dateObj.getDate();
    if (strDay < 10) {
        strDay = ‘0‘ + strDay;
    }
    var strHour = dateObj.getHours();
    if(strHour < 10){
        strHour = "0" + strHour;
    }
    var strMinute = dateObj.getMinutes();
    if(strMinute < 10){
        strMinute = ‘0‘ + strMinute;
    }

    var last = strHour + ":" + strMinute + ":00";
    var all = strYear + ‘-‘ + strMonth + ‘-‘ + strDay + " " + last;
    return [all,last];
}
时间: 2024-10-03 00:06:28

模拟实时动态趋势的相关文章

现新趋势Tableau Desktop v9.3 Professional Win32_64 2CD+Flaresim.v5.0.2

现新趋势Tableau Desktop v9.3 Professional Win32_64 2CD+Flaresim.v5.0.2    Tableau桌面是一种产品,每个人都可以提出新问题.发现趋势,发现机会,让数据指导决策的信心. 你的数据中隐藏了多少机会?它的设计,以支持人们怎么想.拖放画布上.利用你的自然能力迅速发现视 觉模式.体验内置的速度和易用性分析解决方案.画面10-100X比现有解决方案更快.无论是在电子表格. SQL数据库.Hadoop,或云,你可以连接到任何数据,任何地方.

.NET微信模拟登录及{base_resp:{ret:-4,err_msg:nvalid referrer}}的解决办法

12年的时候写了些关于微信开发的内容,当时看好这个东西,可惜当时腾讯开放的权限太少,之后的一年多时间没有太关注了. 现在又要重新开始微信开发的阵容了,微信只是个入口,微网站才是趋势. 我是个水货,所以写的都是比较入门的,给初学者点启发用的. 这里有3个文件,一个页面展示(不贴代码了,就两个文本框和提交按钮)和后台代码,一个方法类,一个实体类 后台代码 ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 protected void btnConfirm_Click(

DirectX 因素:模拟合成器的仿真

Charles Petzold 下载代码示例 大约在 50 年前,一名物理学家和工程师名叫罗伯特 · 穆格电子音乐合成器功能创建的颇不寻常:器官型键盘. 一些电子音乐的作曲家轻视这种平淡和老式的控制设备,同时其他作曲家 - - 和特别是表演者 - - 对此发展表示欢迎. 1960 年代末,由温卡洛斯的 Switched-On 巴赫已成为最畅销的古典专辑的所有时间,和 Moog 合成器进入了主流. 早期的 Moog 合成器是模块化的编程与跳线. 1970 年,然而,Minimoog 被释放 - -

模拟恶劣网络环境常用的几种解决方案

一.利用Fiddler模拟恶劣网络环境   在解决日常的支持需求中,经常会遇到一些用户反馈一些无法简单复现的bug,有很大一部分的bug是由于用户自身的网络环境波动,或者是本身网络环境就较为恶劣,而服务在面对这种恶劣的网络环境的健壮性不够,导致会出现一些意想不到的bug.而在正常的开发自测过程中很难去营造出这种恶劣的网络环境,使得这些bug较难被提前发现和修复.另外一些服务在恶劣网络环境下虽然不会出现不可用的情况,但是用户体检很差,为了优化这个情况下的用户体验,也需要去在本地模拟这种环境来进行调

atitit。企业组织与软件project的策略 战略 趋势 原则 attilax 大总结

atitit. 企业组织与软件project的策略 战略 趋势 原则 attilax 大总结 1. 战略规划,适当的过度设计 1 2. 跨平台化 1 3. 可扩展性高于一切 1 4. 界面html5化 2 5. web界面spa ajax化 2 6. Vm 平台化 2 7. 插件化 2 8. 自包括.容器化隔离. .iframe 3 9. 延迟初始化 cfg  bat化.. 3 10. #--------------------------------other 3 11. 全球化(国际化) 3

明年大数据行业的趋势会是哪些?

在即将过去的2016年,大数据技术在不断的发展,新霸哥预计到明年很多的主流公司会采用大数据和物联网.新霸哥发现自助式数据分析的普及,加上云计算和Hadoop的广泛采用,目前正在整个行业带来变化,越来越多的公司会抓住这一形势,或者无视变化.因此面临险境.实际上,工具仍在出现,而Hadoop平台承诺的还没有达到公司缺少不了它的地步. 深度学习 深度学习是一套基于神经网络的机器学习技术,深度学习仍在发展之中,不过在解决业务问题方面显示出大有潜力.深度学习让计算机能够从大量非结构化数据和二进制数据中找出

python构建模拟模型——网站独立访问用户数量

背景:发现一个有趣的现象,即一些用户在每一月都仅仅访问网站一次,我们想要了解这些人数量的变化趋势. 建立数学模型:简化问题,根据瓮模型推导出公式(具体推导见<数据之魅>,有时间再补充...):n(t)=N(1-e^((-k/N)*t)),其中,t代表一个月中的第t天,N代表潜在的总的访问人数,k为根据网站日志计算的每日平均访问量,n(t)代表第t天为止,访问此网站的用户总人数. python模拟,并和分析的模型作比较: import math import random as rnd impo

编程模拟自然(七):力学矢量与牛顿定律

序 旧书有云:古者十日并出,草木焦槁,一曰后羿者射日,太阳里之九鸟皆死,救苍生于涂炭. 传闻后羿所用箭矢加持了力学模拟系统,可实现深空精确制导,才得以击落太阳. ... “星星挂在天边,就像梦想遥不可现...”元哼唧着. “猿叔你在哼什么曲子啊?” “...星星消失在天边,就像诺言来不及实现...”元对无名儿捂着双耳选择无视. “咳咳,我这是在朗诵诗人小刚的诗歌呢!” ... 第零章 “星星挂在天...你爸曾经射落过太阳?”元刚要继续深情朗诵,似乎想到了什么. “是的,听娘说爹地最厉害了.” “

httpclient4.3.x模拟post及get请求

在web开发中,我们经常需要模拟post及get请求,现在网上比较多的是使用httpclient3.x,然而httpclient4.x已经发布好几年了,而且4.x之后改名为HttpComponents,显然是今后的趋势.Apache HttpComponents4.x中的HttpClient是一个很好的工具,它符合HTTP1.1规范,是基于HttpCore类包的实现.但是HttpComponents4.x较之前httpclient3.x的API变化比较大,已经分为HttpClient,HttpC