2017-05-27 406 views
2

將垂直線添加到特定點處的折線圖在三條相似折線圖(相同數據集)之間建立垂直線有些困難。d3.js通過索引

瓦爾testdate1包含日期,這是在該圖表(該lc1.on(renderlet)函數的頂部)發現在索引POS 4(在應用於LineChart第四「circle.dot」)。

我該如何在該位置向圖表添加垂直線? (我希望在所有三個折線圖上的相同位置添加垂直線,但我希望這三個線圖都是相同的程序)。

此外,出於某種原因,我無法在該位置獲得圓形作爲紅色或綠色點的樣式,儘管它在this SO answer by davcs86中有效 - 但在他的示例中,我們使用x軸(日期),並且這段代碼在alldotsN集合中使用索引號 - 無法看到我在這裏做錯了什麼。

jsFiddle to experiment with

var chartHeight = 250; 
 
var chartWidth = 500; 
 
var myCSV = [ \t 
 
    {"shift":"1","date":"01/01/2016/08/00/00/+0500","car":"178","truck":"125","bike":"317","moto":"237"}, 
 
    {"shift":"2","date":"01/01/2016/17/00/00/+0500","car":"125","truck":"189","bike":"125","moto":"273"}, 
 
    {"shift":"3","date":"02/01/2016/08/00/00/-0500","car":"140","truck":"219","bike":"328","moto":"1252"}, 
 
    {"shift":"4","date":"02/01/2016/17/00/00/+0500","car":"222","truck":"290","bike":"432","moto":"378"}, 
 
    {"shift":"5","date":"03/01/2016/08/00/00/+0500","car":"200","truck":"250","bike":"420","moto":"319"}, 
 
    {"shift":"6","date":"03/01/2016/17/00/00/+0500","car":"230","truck":"220","bike":"310","moto":"413"}, 
 
    {"shift":"7","date":"04/01/2016/08/00/00/+0500","car":"155","truck":"177","bike":"377","moto":"180"}, 
 
    {"shift":"8","date":"04/01/2016/17/00/00/+0500","car":"179","truck":"203","bike":"405","moto":"222"}, 
 
    {"shift":"9","date":"05/01/2016/08/00/00/+0500","car":"208","truck":"185","bike":"360","moto":"195"}, 
 
    {"shift":"10","date":"05/01/2016/17/00/00/+0500","car":"150","truck":"290","bike":"315","moto":"280"}, 
 
    {"shift":"11","date":"06/01/2016/08/00/00/+0500","car":"200","truck":"220","bike":"350","moto":"205"}, 
 
    {"shift":"12","date":"06/01/2016/17/00/00/+0500","car":"230","truck":"170","bike":"390","moto":"400"} 
 
]; 
 
var testdate1 = +new Date('Sun Jan 03 2016 08:00:00 GMT-0500 (Eastern Standard Time)'); 
 
lc1 = dc.lineChart("#line1"); 
 
lc2 = dc.lineChart("#line2"); 
 
lc3 = dc.lineChart("#line3"); 
 

 
var dateFormat = d3.time.format("%d/%m/%Y/%H/%M/%S/%Z"); 
 
myCSV.forEach(function (d) { 
 
\t d.date = dateFormat.parse(d.date); 
 
\t d.car = +d.car; 
 
\t d.bike = +d.bike; 
 
\t d.moto = +d.moto; 
 
}); 
 

 
var facts = crossfilter(myCSV); 
 

 
var dateDim = facts.dimension(function (d) {return d.date}); 
 
var carDim = facts.dimension(function (d) {return d['car']}); 
 
var dgCar = dateDim.group().reduceSum(function (d) {return d['car']}); 
 
var bikeDim = facts.dimension(function (d) {return d['bike']}); 
 
var dgBike = dateDim.group().reduceSum(function (d) {return d['bike']}); 
 
var motoDim = facts.dimension(function (d) {return d['moto']}); 
 
var dgMoto = dateDim.group().reduceSum(function (d) {return d['moto']}); 
 

 
var minDate = myCSV[0].date; //new Date ("2016-01-01T08:00:00.000Z"); 
 
var maxDate = myCSV[myCSV.length-1].date; \t //new Date ("2016-01-06T17:00:00.000Z"); \t 
 

 
lc1 
 
    .renderArea(false) 
 
    .width(chartWidth) 
 
    .height(chartHeight) 
 
    .dimension(dateDim) 
 
    .group(dgCar) 
 
    .defined(function(d) {if(d.y !==null) {return d.y;}}) 
 
    .transitionDuration(1000) 
 
    .margins({top: 30, right: 20, bottom: 35, left: 60}) 
 
    .yAxisLabel('Cars') 
 
    .renderHorizontalGridLines(true) 
 
    .brushOn(false) 
 
    .x(d3.time.scale().domain([minDate,maxDate])); 
 
lc1.yAxis().ticks(5); 
 
lc1.xAxis().ticks(3); 
 

 
lc2 
 
.renderArea(false) 
 
.width(chartWidth) 
 
.height(chartHeight) 
 
.dimension(dateDim) 
 
.group(dgBike) 
 
.defined(function(d) {if(d.y !==null) {return d.y;}}) 
 
.transitionDuration(1000) 
 
.margins({top: 30, right: 20, bottom: 35, left: 60}) 
 
.yAxisLabel('Bikes') 
 
.renderHorizontalGridLines(true) 
 
.brushOn(false) 
 
.x(d3.time.scale().domain([minDate,maxDate])); 
 
lc2.yAxis().ticks(5); 
 
lc2.xAxis().ticks(3); 
 

 
lc3 
 
.renderArea(false) 
 
.width(chartHeight) 
 
.height(250) 
 
.dimension(dateDim) 
 
.group(dgMoto) 
 
.defined(function(d) {if(d.y !==null) {return d.y;}}) 
 
.transitionDuration(1000) 
 
.margins({top: 30, right: 20, bottom: 35, left: 60}) 
 
.yAxisLabel('Motos') 
 
.renderHorizontalGridLines(true) 
 
.brushOn(false) 
 
.x(d3.time.scale().domain([minDate,maxDate])); 
 
lc3.yAxis().ticks(5); 
 
lc3.xAxis().ticks(3); 
 

 

 
lc1.on('renderlet', function(lc1) { 
 
    \t var thespot; 
 
    \t var allDots1 = lc1.selectAll('circle.dot'); 
 
    allDots1.filter(function(d,i){ //d==datum (obj), i==index (of datapoint on line) 
 
    \t \t if (+d.x===testdate1) thespot = i; 
 
\t }); 
 
    console.log('found spot: ' +thespot); //== 4th position on line 
 
    //display red circle - NOT WORKING 
 
    alldots1.filter((d,i) => i === thespot).classed('reddot',true); 
 
    alldots2.filter((d,i) => i === thespot).classed('greendot',true); 
 
    alldots3.filter((d,i) => i === thespot).classed('greendot',true); 
 
    //display vertical line on all 3 graphs at same point - NOT WORKING 
 
    alldots1 
 
    \t .filter((d,i) => +i === +thespot) 
 
    \t .append('line') 
 
     .attr('x1', +testdate1) 
 
     .attr('y1', chartHeight - margins.top) 
 
     .attr('x2', +testdate1) 
 
     .attr('y2', 0 + margins.top) 
 
    \t \t .style("stroke-width", 2) 
 
    \t \t .style("stroke", "red") 
 
    \t \t .style("fill", "none"); 
 
    alldots2 
 
    \t .filter((d,i) => +i === +thespot) 
 
    \t .append('line') 
 
     .attr('x1', +testdate1) 
 
     .attr('y1', chartHeight - margins.top) 
 
     .attr('x2', +testdate1) 
 
     .attr('y2', 0 + margins.top) 
 
    \t \t .style("stroke-width", 2) 
 
    \t \t .style("stroke", "red") 
 
    \t \t .style("fill", "none"); 
 
    alldots3 
 
    \t .filter((d,i) => +i === +thespot) 
 
    \t .append('line') 
 
     .attr('x1', +testdate1) 
 
     .attr('y1', chartHeight - margins.top) 
 
     .attr('x2', +testdate1) 
 
     .attr('y2', 0 + margins.top) 
 
    \t \t .style("stroke-width", 2) 
 
    \t \t .style("stroke", "red") 
 
    \t \t .style("fill", "none"); 
 
});//END lc1.renderlet 
 

 
dc.renderAll(); 
 
dc.redrawAll();
.reddot { 
 
    stroke: red !important; 
 
    fill: red !important; 
 
    fill-opacity: 1 !important; 
 
} 
 

 
.greendot { 
 
    stroke: green; 
 
    fill: green; 
 
    fill-opacity: 1 !important; 
 
}
<script src="//cdnjs.cloudflare.com/ajax/libs/crossfilter/1.3.1/crossfilter.min.js"></script> 
 
<script src="//cdnjs.cloudflare.com/ajax/libs/d3/3.3.3/d3.min.js"></script> 
 
<script src="//dc-js.github.io/dc.js/js/dc.js"></script> 
 

 
<link href="//dc-js.github.io/dc.js/css/dc.css" rel="stylesheet" /> 
 

 

 
<svg id="line1"></svg> 
 
<svg id="line2"></svg> 
 
<svg id="line3"></svg>

+0

一些第一想法:是否(renderlet)可以運行嗎?沒有在我的iPad上。因此,也沒有你期望的東西。圓形格式對我來說看起來沒問題,應該可以工作。追加行看起來不正確。你不應該將該行添加到svg嗎?它現在的方式將被添加到選定的圓圈內。 – Florian

回答

3

它具有通過使用dc.js API更簡單的(和可重複使用的)的解決方案:

/* draw vertical lines code */ 
var line = d3.svg.line().interpolate('linear'); 
function draw_verticals(chart, points){ 
    // merge 
    var selection = chart.g() 
     .select('g.chart-body') 
     .selectAll('path.horizontal') 
     .data(points) 
    // append 
    selection.enter() 
     .append('path') 
     .attr('class', 'horizontal reddot') 
     .attr('d', function(d) { 
     var x = chart.x()(d); 
     return line([ 
      [x, chart.y().range()[0]], 
      [x, chart.y().range()[1]] 
     ]); 
     }); 
    // remove 
    selection.exit().remove(); 
} 
/* ends here */ 

與此,只通過該圖表,在要繪製垂直點的數組線,例如

draw_verticals(lc1, [testdate1, testdate2]); 
draw_verticals(lc2, [testdate1, testdate2]); 
draw_verticals(lc3, [testdate1, testdate2]); 

Working demo

Reference

Reference's source code

附加

對於用刷子一起使用,您必須

1)對於每個圖表中刪除動畫之前的線(S),與

lcX.on('pretransition', function(c){ 
    draw_verticals(c, []); 
}); 

2)重畫線(S)在leaflet事件

lcX.on('renderlet', function(c) { 
    var thespot; 
    var allDots = c.selectAll('circle.dot'); 
    allDots.filter(function(d, i) { //d==datum (obj), i==index (of datapoint on line) 
    if (+d.x === testdate1) thespot = i; 
    }); 
    // fixed `alldots1` to `allDots1`, now the red point renders correctly 
    allDots.filter((d, i) => i === thespot).classed('reddot', true); 
    draw_verticals(c, (thespot?[testdate1]:[])); 
}); 
+1

太棒了!但是有一個問題 - 它不隨刷子移動。如果使用畫筆放大時間窗口,數據點會展開,並且pinkdot會移動,但垂直線會保留在原來的位置。對此有何建議? – crashwap

+1

(我正在jsFiddle演示臺上工作) – crashwap

+0

好的,當你完成時發給我jsfiddle。 – davcs86

2
var xScale = d3.time.scale().domain([minDate,maxDate]).range([screenMinX, screenMaxX]); 

var vertLineXCoord = xScale(myCSV[pointYouWantToDrawLineAt].car); // or bike, or moto 

vertLineXCoord應該是你的垂直線的x座標。

[screenMinX, screenMaxX]是圖中最左邊和最右邊的點的像素座標。有關尺度如何工作的更多解釋,請參見here

+0

計算完此座標後,如何將其添加到折線圖? – 1252748

2

腳本中的錯字:alldots1應爲allDots1。糾正後,出現紅點。

3

http://jsfiddle.net/v6ehemnq/1/

下面是我從其他兩個答案納入變化的總結。

首先,我使紅點正確渲染。

// fixed `alldots1` to `allDots1`, now the red point renders correctly 
allDots1.filter((d, i) => i === thespot).classed('reddot', true); 

然後,我通過觀察screenMinX和screenMaxX所在的位置來畫線。您可能想要根據您的需求使用更精確的測量。

var screenMinX = margins.left; // left most pixel coordinate of graph 
var screenMaxX = margins.left + 408; // right most pixel coordinate of graph 
var xScale = d3.time.scale().domain([minDate, maxDate]).range([screenMinX, screenMaxX]); 
var vertLineXCoord = xScale(testdate1); 

d3.select('#line1') 
    .append('line') 
    .attr('x1', vertLineXCoord) 
    .attr('y1', chartHeight - margins.top) 
    .attr('x2', vertLineXCoord) 
    .attr('y2', 0 + margins.top) 
    .style("stroke-width", 2) 
    .style("stroke", "red") 
    .style("fill", "none"); 

注意var margins從來沒有存在過,所以我創造了它,讓margins.leftmargins.top可用。

+0

總結得很好。 – Florian