更新面积图

时间:2016-08-26 21:41:53

标签: d3.js

我有一个区域图表,我想将其纳入更新模式,以便我可以看到图表实时更新。我已经能够用条形图来做到这一点,但是面积图更适合我最终拥有的数据点数量。

我最好的尝试如下。更新过程分为四个部分,用于两个行选择和两个区域选择。这对我来说似乎很复杂,但这是我能够让事情看起来有效的唯一方法。问题是当我添加数据时,图表在浏览器中很快就开始表现得很差,这暗示着我做错了。此外,元素不透明度与早期静态图形不同,并且添加新数据之前出现的图形部分在我添加新数据后会缩小到一个小峰值。

将更新模式合并到按日期排序的区域图表中的正确方法是什么?



//////////////////////////////////////////////////
// initial data
//////////////////////////////////////////////////
var tdata = [

{
    "property":"humidity",
    "date":"2016-06-28 05:47:10",
    "value": 40, "unit": "\%"
},
{
    "property":"humidity",
    "date":"2016-06-28 05:47:20",
    "value": 35, "unit": "\%"
},
{
    "property":"humidity",
    "date":"2016-06-28 05:47:30",
    "value": 36, "unit": "\%"
} ,
{
    "property":"humidity",
    "date":"2016-06-28 05:47:40",
    "value": 40, "unit": "\%"
},
{
    "property":"temperature",
    "date":"2016-06-28 05:47:15",
    "value": 75, "unit": "F"
} ,

{
    "property":"temperature",
    "date":"2016-06-28 05:47:25",
    "value": 70, "unit": "F"
} ,

{
    "property":"temperature",
    "date":"2016-06-28 05:47:35",
    "value": 72, "unit": "F"
},

{
    "property":"temperature",
    "date":"2016-06-28 05:47:45",
    "value": 75, "unit": "F"
} , 

];
///////////////////////////////////////////////////////// 
// d3 code:
/////////////////////////////////////////////////////////

    var canvas = d3.select('#disp')
      .append('svg')
      .attr('width', 1200)
      .attr('height', 200);

    var x = d3.scale.linear().range([0,700]);

    var y = d3.scale.linear()
      .domain([0, 100])
      .range([200, 0]);

    var line = d3.svg.line()
     // .interpolate("cardinal")
      .x(function(d) {
        return x(d.date);
      })
      .y(function(d) {
        return y(d.value);
      });

    var area = d3.svg.area()
      //.interpolate("cardinal")
      .x(line.x())
      .y1(line.y())
      .y0(y(0));

var parseDate = d3.time.format("%Y-%m-%d %H:%M:%S").parse;

 var lines = canvas.selectAll('path');

//////////////////// update function:

function update(dataset){
// parse new date strings
 dataset.forEach(function(d) { 
  if(typeof(d.date) === "string"){ d.date = parseDate(d.date); }
});
// sort by date
dataset = dataset.sort(sortByDateAscending);
// update domain
x.domain(d3.extent(dataset, function (d) { return d.date; }));
 
// (1) make temperature line selection, update 
lines = canvas.selectAll('.tline').data(dataset);
    lines
      .attr('class', 'tline')
      .attr("d", function(d) {
        return line(dataset.filter(function(d){ return d.property == 'temperature'}));
      })
      .style("stroke", function(d){ return 'black'; });
 // append new data to selection     
    lines
      .enter().append("path")
      .attr('class', 'tline')
      .attr("d", function(d) {
        return line(dataset.filter(function(d){ return d.property == 'temperature'}));
      })
      .style("stroke", function(d) {
        return 'black';
      });
// remove 
    lines.exit().remove();
// (2) make temperature area selection, update
lines = canvas.selectAll('.tarea').data(dataset);
      lines
        .attr("class", "tarea")
        .style('fill', 'red')
        .style('opacity', '0.3')
        .attr("d", function(d) {
          return area(dataset.filter(function(d){ return d.property == 'temperature'}));
        });
 // append new data to selection     
      lines
        .enter().append("path")
        .attr("class", "tarea")
        .style('fill', 'red')
        .style('opacity', '0.3')
        .attr("d", function(d) {
          return area(dataset.filter(function(d){ return d.property == 'temperature'}));
        });
//remove
      lines.exit().remove();
 // (3) make humidity line selection, update
 lines = canvas.selectAll('.hline').data(dataset);
    lines
      .attr('class', 'hline')
      .attr("d", function(d) {
        return line(dataset.filter(function(d){ return d.property == 'humidity'}));
      })
      .style("stroke", function(d){ return 'black'; });
 // append new data to selection     
    lines
      .enter().append("path")
      .attr('class', 'hline')
      .attr("d", function(d) {
        return line(dataset.filter(function(d){ return d.property == 'humidity'}));
      })
      .style("stroke", function(d) {
        return 'black';
      });
// remove
    lines.exit().remove();
// (4) make humidity area selection, update
lines = canvas.selectAll('.harea').data(dataset);
      lines
        .attr("class", "harea")
        .style('fill', 'steelblue')
        .style('opacity', '0.3')
        .attr("d", function(d) {
          return area(dataset.filter(function(d){ return d.property == 'humidity'}));
        });
// append new data to selection
      lines
        .enter().append("path")
        .attr("class", "harea")
        .style('fill', 'steelblue')
        .style('opacity', '0.3')
        .attr("d", function(d) {
          return area(dataset.filter(function(d){ return d.property == 'humidity'}));
        });
// remove
      lines.exit().remove();     


  }


  function sortByDateAscending(a, b) {
    return Date.parse(a.date) - Date.parse(b.date);
};
//////////////////////////////////////////////////////////////
// main code:
//////////////////////////////////////////////////////////////
addObjs(tdata)
addObjs(tdata)
addObjs(tdata)
addObjs(tdata)
addObjs(tdata)
addObjs(tdata)
addObjs(tdata)
addObjs(tdata)
addObjs(tdata)

update(tdata);  

function btnfunc(){
addObjs(tdata)
update(tdata);  
}
// function to add new objects to dataset incremented by date
function addObjs(arr) {
var z = 1;
var h = JSON.parse(JSON.stringify(arr[arr.length-2])); 
var t = JSON.parse(JSON.stringify(arr[arr.length-1])); 

h.property = 'humidity'; h.unit = '\%';
t.property = 'temperature'; t.unit = 'F';

var time = dparse(t.date);
time+= z;
h.date = timeString(time.toString());
h.value = 15 + Math.floor(Math.random()*31);
time+= z;
t.date = timeString(time.toString());
t.value = 65 + Math.floor(Math.random()*28);

arr.push(h);
arr.push(t);

function timeString(ins) {
    return  ins.slice(0,4) + '-' + ins.slice(4,6) + '-' + ins.slice(6,8) + ' '
         +  ins.slice(8,10) + ':'  +  ins.slice(10,12) + ':'  +  ins.slice(12);
}
function dparse(date){
var d = date.substr(0,10).replace(/-/g , '');
var t = date.substr(11,18).replace(/:/g , '');
var i = parseInt(d+t);
return i;
}
    
}

.axis path,
.axis line {
  fill: none;
  stroke: #000;
  shape-rendering: crispEdges;
}

.area {
  fill: steelblue;
}
 body {
      font: 10px sans-serif;
    }
    
    .axis path,
    .axis line {
      fill: none;
      shape-rendering: crispEdges;
    }
    
    .tline {
      fill: none;
      stroke-width: 1px;
      
    }
     .hline {
      fill: none;
      stroke-width: 1px;
      
    }   

<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div id="disp"></div> 
<button onclick="btnfunc()">add</button>
&#13;
&#13;
&#13;

1 个答案:

答案 0 :(得分:0)

问题在于(在区域图表中)我不应该在更新功能中添加任何内容。

由于只有一条连续的路径,所以不需要像在条形图中那样添加任何新的东西来反映新数据。只需更新路径的数据属性即可,因为路径已存在于图形范围内。

我将初始追加放在init函数中。

域更新仍然表现出破坏init数据的奇怪行为,但除此之外它的工作原理应该如下:

//////////////////////////////////////////////
// initial data
//////////////////////////////////////////////
var tdata = [

{
    "property":"humidity",
    "date":"2016-06-28 05:47:10",
    "value": 40, "unit": "\%"
},
{
    "property":"humidity",
    "date":"2016-06-28 05:47:20",
    "value": 35, "unit": "\%"
},
{
    "property":"humidity",
    "date":"2016-06-28 05:47:30",
    "value": 36, "unit": "\%"
} ,
{
    "property":"humidity",
    "date":"2016-06-28 05:47:40",
    "value": 40, "unit": "\%"
},
{
    "property":"temperature",
    "date":"2016-06-28 05:47:15",
    "value": 75, "unit": "F"
} ,

{
    "property":"temperature",
    "date":"2016-06-28 05:47:25",
    "value": 70, "unit": "F"
} ,

{
    "property":"temperature",
    "date":"2016-06-28 05:47:35",
    "value": 72, "unit": "F"
},

{
    "property":"temperature",
    "date":"2016-06-28 05:47:45",
    "value": 75, "unit": "F"
} , 

];

///////////////////////////////////////////////
// d3 code
///////////////////////////////////////////////

    var canvas = d3.select('#disp')
      .append('svg')
      .attr('width', 800)
      .attr('height', 200);

    var x = d3.scale.linear().range([0, 800]);

    var y = d3.scale.linear()
      .domain([0, 100])
      .range([200, 0]);

    var line = d3.svg.line()
     // .interpolate("cardinal")
      .x(function(d) {
        return x(d.date);
      })
      .y(function(d) {
        return y(d.value);
      });

    var area = d3.svg.area()
      //.interpolate("cardinal")
      .x(line.x())
      .y1(line.y())
      .y0(y(0));

var parseDate = d3.time.format("%Y-%m-%d %H:%M:%S").parse;

    var lines = canvas.selectAll('.property');

    var lE;

///////////////////////////// init function
function init(dataset){
 dataset.forEach(function(d) { 
  if(typeof(d.date) === "string"){ d.date = parseDate(d.date); }
});

dataset = dataset.sort(sortByDateAscending);

x.domain(d3.extent(dataset, function (d) { return d.date; }));

lines = canvas.selectAll('.property')
      .data(dataset, function(d) {
        return d.property;
      });

lE = lines.enter()
      .append('g')
      .attr('class', 'property');
  
     lE.append("path")
      .attr('class', 'tline')
      .attr("d", function(d) {
        return line(dataset.filter(function(d){ return d.property == 'temperature'}));
     })
      .style("stroke", function(d) {
        return 'black';
      })

    lE.append("path")
      .attr('class', 'hline')
      .attr("d", function(d) {
        return line(dataset.filter(function(d){ return d.property == 'humidity'}));
      })
      .style("stroke", function(d) {
        return 'black';
      })

      lE.append("path")
        .attr("class", "tarea")
        .style('fill', 'red')
        .style('opacity', '0.3')
        .attr("d", function(d) {
          return area(dataset.filter(function(d){ return d.property == 'temperature'}));
        });
      
      lE.append("path")
        .attr("class", "harea")
        .style('fill', 'steelblue')
        .style('opacity', '0.9')
        .attr("d", function(d) {
          return area(dataset.filter(function(d){ return d.property == 'humidity'}));
        });

}

///////////////////////////// update function
function update(dataset){

 dataset.forEach(function(d) { 
  if(typeof(d.date) === "string"){ d.date = parseDate(d.date); }
});

dataset = dataset.sort(sortByDateAscending);

x.domain(d3.extent(dataset, function (d) { return d.date; }));

    lE.select(".tarea")
    // .transition().duration(500)
     .attr("d", function(d) {
          return area(dataset.filter(function(d){ return d.property == 'temperature'}));
        });

    lE.select(".harea")
    // .transition().duration(500)
     .attr("d", function(d) {
          return area(dataset.filter(function(d){ return d.property == 'humidity'}));
        });

    lE.select(".tline")
   //  .transition().duration(500)
     .attr("d", function(d) {
          return area(dataset.filter(function(d){ return d.property == 'temperature'}));
        });


    lE.select(".hline")
   //  .transition().duration(500)
     .attr("d", function(d) {
          return area(dataset.filter(function(d){ return d.property == 'humidity'}));
        });

 }

  function sortByDateAscending(a, b) {
    return Date.parse(a.date) - Date.parse(b.date);
};

////////////////////////////////////////////////////////
// main code
////////////////////////////////////////////////////////

var anim = false;
addObjs(tdata)
addObjs(tdata)
addObjs(tdata)
addObjs(tdata)
addObjs(tdata)
addObjs(tdata)
addObjs(tdata)
addObjs(tdata)
addObjs(tdata)

init(tdata);	

function btnfunc(){
addObjs(tdata)
update(tdata);	
}

function rmvfunc(){
tdata.pop(); tdata.pop();
update(tdata);	
}

function shiftfunc(){
addObjs(tdata)
tdata.shift(); tdata.shift();
update(tdata);	
}

function animfunc() {
	anim = !anim; 
}

window.setInterval(function(){ if(anim){btnfunc();} }, 1000);

	
function addObjs(arr) {
var z = 1;
var h = JSON.parse(JSON.stringify(arr[arr.length-2])); 
var t = JSON.parse(JSON.stringify(arr[arr.length-1])); 

h.property = 'humidity'; h.unit = '\%';
t.property = 'temperature'; t.unit = 'F';

var time = dparse(t.date);
time+= z;
h.date = timeString(time.toString());
h.value = 15 + Math.floor(Math.random()*31);
time+= z;
t.date = timeString(time.toString());
t.value = 65 + Math.floor(Math.random()*28);

arr.push(h);
arr.push(t);

function timeString(ins) {
	return  ins.slice(0,4) + '-' + ins.slice(4,6) + '-' + ins.slice(6,8) + ' '
         +  ins.slice(8,10) + ':'  +  ins.slice(10,12) + ':'  +  ins.slice(12);
}
function dparse(date){
var d = date.substr(0,10).replace(/-/g , '');
var t = date.substr(11,18).replace(/:/g , '');
var i = parseInt(d+t);
return i;
}
	
}
.axis path,
.axis line {
  fill: none;
  shape-rendering: crispEdges;
}

.tline {
  fill: none;
  stroke-width: 1px;

}
.hline {
  fill: none;
  stroke-width: 1px;

}   
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div id="disp"></div> 
<button onclick="btnfunc()">add</button>
<button onclick="shiftfunc()">shift</button>
<button onclick="rmvfunc()">remove</button>
<button onclick="animfunc()">anim on/off</button>
<script src="data.js"></script>
<script src="graph2.js"></script>
<script src="main.js"></script>