在下拉列表更改时刷 extent/width(dc.js、moment.js、d3.js、crossfilter)
Brush extent/width on dropdown change (dc.js,moment.js,d3.js ,crossfilter)
我必须创建一个具有来自下拉列表的固定范围值的画笔(3、6、12、24 小时)。尝试获得画笔的影响作为画笔范围 http://jsfiddle.net/9yccpjbu/, but instead of buttons to use the drop-down (like here http://jsfiddle.net/gordonwoodhull/400wd2nd/16/)。
也欢迎对按钮进行修复。
下拉菜单
1) 下拉列表仅在第一次单击时更改显示的堆栈(条:g.chart-正文- g.stack),到显示来自下拉列表的正确值:
当从下拉列表中单击另一个值时,它会添加白色的值而不渲染堆栈(在图片中,从下拉列表中选择 6(灰色条)而不是 3(白色)的结果:
2) 点击后下拉笔刷范围(rect.extent)与
断开连接
g.chart-正文- g.stack。 (图中红框):
.
在不点击下拉菜单的情况下,从 timeSlider 中选择画笔效果很好,看起来像这样:
3) 点击下拉后"Reset All"link中显示的选中记录数为0(移动rect.extent堆栈显示所选记录的数量)。
4) "Reset All" link 不重置 图形** 并显示下拉菜单选择的项目数
按钮:
4) 单击时,它们会显示预期的 g.chart-body- g.stack 和维度上的互补 rect.extent (stack+rect.extent= dimesion) :
当我从
更改范围时
filterDimension.filterRange([start, start.add('hours,amountHours).hours()])
到
filterDimension.filterRange([start.add('hours', amountHours).hours(), end])
g.chart-body- g.stack 和 rect.extent 连接在一起 - 但在范围的互补范围内,我需要显示来自下拉列表的画笔范围(我需要的范围(灰色)+ 显示的范围(蓝色带红色边框)= 域:
5) "Reset All" link 似乎重置了图表,但在单击 "Reset all" 时显示了错误的数字。
我的fiddle:https://jsfiddle.net/dani2011/upa3eowb/
JAVASCRIPT
'use strict';
// chart objects
var bitChart = dc.compositeChart("#bitrate-move-chart");//Before dynamic Y-Axis nonzero_min used var bitChart = dc.lineChart('#bitrate-move-chart');
var bitChart2 = dc.compositeChart("#bitrate-move-chart2");//Before dynamic Y-Axis nonzero_min used var bitChart = dc.lineChart('#bitrate-move-chart');
var timeSlider = dc.barChart('#bitrate-timeSlider-chart');
var bitCount = dc.dataCount('.dc-data-count');
var bitrateTable = dc.dataTable('.dc-data-table');
//Creating dynamic Y axis with min/max ticks' values depending on min/max of data - copied from http://jsfiddle.net/gordonwoodhull/7anae5c5/1/...
// 15 Min Interval - copied from https://github.com/mbostock/d3/blob/master/src/time/interval.js.....
// generalization of d3.time.minute copied from- https://github.com/mbostock/d3/blob/master/src/time/minute.js....
//bitchart
var min15 = n_minutes_interval(15);
//bitchart_2
var min15_2 = n_minutes_interval(15);
//timeSlider
var min15_3 = n_minutes_interval(15);
//### Load data
//var data = d3.csv.parse(d3.select("pre#data").text());
d3.csv('CHANNEL_CLUSTER_BITRATE_takeThis.csv', function (data) {
// Format CSV data
var dateFormat = d3.time.format('%Y/%m/%d/%H:%M');
var numberFormat = d3.format('.2');
data.forEach(function (d) {
d.bitdate = new Date(d.DATETIME); //d.DATETIME = dateFormat.parse(d.DATETIME);
d.hours = d3.time.hours(d.bitdate);
d.BITRATE = +d.BITRATE.match(/\d+/); //d.BITRATE = +d.BITRATE;
});
//### Crossfilter Dimensions
var crossFilteredData = crossfilter(data);
var all = crossFilteredData.groupAll();
// Dimension by full date
//bitChart
var dateDimension = crossFilteredData.dimension(function (d) {
return d.bitdate;
});
//bitChart2
var dateDimension2 = crossFilteredData.dimension(function (d) {
return d.bitdate;
});
//timeSlider
var dateDimension3 = crossFilteredData.dimension(function (d) {
return d.bitdate;
});
//dropDown
var filterDimension = crossFilteredData.dimension(function (d) {
return d.bitdate;
});
//### Crossfiltered Groups
//timeSlider
var minIntervalWeekBitrateGroup3 = dateDimension3.group(min15_3).reduceSum(function (d) {
return 10 // +d.BITRATE
});
//Group bitrate per week, 15 minInterval - maintain running tallies
//bitChart
var bitrateWeekMinIntervalGroupMove = dateDimension.group(min15).reduce(
/* callback for when data is added to the current filter results */
function (p, v) {
++p.count;
p.BITRATE = +v.BITRATE;
p.total += +v.BITRATE;
p.avg = p.count ? Math.round(p.total / p.count) : 0;
return p;
},
/* callback for when data is removed from the current filter results */
function (p, v) {
--p.count;
p.BITRATE = +v.BITRATE;
p.total -= +v.BITRATE;
p.avg = p.count ? Math.round(p.total / p.count) : 0;
return p;
},
/* initialize p */
function () {
return {
count: 0,
bitrate: 0,
total: 0,
avg: 0
};
}
);
//bitChart2
var bitrateWeekMinIntervalGroupMove2 = dateDimension2.group(min15_2).reduce(
/* callback for when data is added to the current filter results */
function (p, v) {
++p.count;
p.BITRATE = +v.BITRATE;
p.total += +v.BITRATE;
p.avg = p.count ? Math.round(p.total / p.count) : 0;
return p;
},
/* callback for when data is removed from the current filter results */
function (p, v) {
--p.count;
p.BITRATE = +v.BITRATE;
p.total -= +v.BITRATE;
p.avg = p.count ? Math.round(p.total / p.count) : 0;
return p;
},
/* initialize p */
function () {
return {
count: 0,
bitrate: 0,
total: 0,
avg: 0
};
}
);
//domain limits
var minDate = dateDimension.bottom(1)[0].DATETIME;
var maxDate = dateDimension.top(1)[0].DATETIME;
var start = moment(new Date(minDate));
var end = moment(new Date(maxDate));
//max line
var maxbit = d3.max(data, function (d) { return
+d["BITRATE"]; });
//dropdown / buttons copied from http://jsfiddle.net/gordonwoodhull/400wd2nd/16/ , http://jsfiddle.net/9yccpjbu/
var btns = d3.select(".buttons-container").selectAll("button").data(["3 Hours", "6 Hours", "12 Hours", "24 Hours"]);
btns = btns.enter().append("button")
.attr("class", "btn btn-sm btn-success")
// fill the buttons with the year from the data assigned to them
btns.each(function (d) {
this.innerText = d;
})
btns.on("click", drawBrush)
function drawBrush() {
if (this.innerText === "Brush Extent") { }
if (this.innerText === "3 Hours") { addHours(3); }
if (this.innerText === "6 Hours") { addHours(6); }
if (this.innerText === "12 Hours") { addHours(12); }
if (this.innerText === "24 Hours") { addHours(24); }
timeSlider.filter(null);
timeSlider.filter(dc.filters.RangedFilter( start, end);
// timeSlider.x.domain(brush.empty() ?
timeSlider.x.domain() : brush.extent());
dc.redrawAll();
}
function addHours(amountHours) {
filterDimension.filterRange([start, start.add('hours', amountHours).hours()]);
//filterDimension.filterRange([start.add('hours', amountHours).hours(), end]);
dc.redrawAll();
}
function brushed() {
timeSlider.x.domain(brush.empty()) ?
timeSlider.x.domain() : brush.extent());
}
function fixed_now() {
return new Date(minDate)
}
d3.select('#hoursDropDown').on('change', function() {
filterDimension.filterRange([start, start.add(this.value,
'hours').hours()]);
dc.redrawAll();
timeSlider.filter(null);//filterAll()
timeSlider.filter(dc.filters.RangedFilter(new
Date(start), new Date(end)));
dc.redrawAll();
// timeSlider.x.domain(brush.empty() ?
// timeSlider.x.domain() : brush.extent());
//var start = moment(new Date(minDate));
// var end = moment(new Date(maxDate));
// filterDimension.filterRange([start,
//start.add(this.value, 'hours').hours()]);
// dc.redrawAll();
});
//###Graphs
bitChart /* dc.lineChart('#bitrate-move-chart', 'chartGroup') */
.xUnits(min15.range) //.xUnits(d3.time.weeks)//.round(d3.time.week) //.round(d3.time.minute)//d3.time.month.round)
.x(d3.time.scale().domain([new Date(minDate), new Date(maxDate)]))
.yAxisPadding('5%')
.elasticY(true)
//Specify a "range chart" to link its brush extent with the zoom of the current "focus chart".
.rangeChart(timeSlider)
.width(450)
.height(200)
.transitionDuration(500)
.margins({ top: 30, right: 50, bottom: 25, left: 50, padding: 1 })
.mouseZoomable(true)
.brushOn(false)
.renderHorizontalGridLines(true)
.legend(dc.legend().x(800).y(10).itemHeight(13).gap(5))
//Render max bitrate horizontal line copied from bar-extra-line.html
.yAxisLabel("Total Bitrate per 15 minutes")
.renderlet(function (chart) {
chart.svg().selectAll('.chart-body').attr('clip-path', null)
})
.on('renderlet', function (chart) {
var left_y = 10, right_y = 70; // use real statistics here!
var extra_data = [{ x: chart.x().range()[0], y: chart.y()(left_y) }, { x: chart.x().range()[1], y: chart.y()(right_y) }];
var line = d3.svg.line()
.x(function (d) { return d.x; })
.y(function (d) { return maxbit; })
.interpolate('linear');
var chartBody = chart.select('g.chart-body');
var path = chartBody.selectAll('path.extra').data([extra_data]);
path.enter().append('path').attr({
class: 'extra',
stroke: 'red',
id: 'extra-line'
});
path.attr('d', line);
// Label the max line
var text = chartBody.selectAll('text.extra-label').data([0]);
text.enter().append('text')
.attr('text-anchor', 'middle')
.append('textPath').attr({
class: 'extra-label',
'xlink:href': '#extra-line',
startOffset: '50%'
})
.text('Total Bitrate Max Value');
})
// .ordinalColors('red')
// Title can be called by any stack layer.
.title(function (d) {
var value = d.value.total ? d.value.total : d.value;
if (isNaN(value)) {
value = 0;
}
return dateFormat(d.key) + ' \n Total Bit:' + numberFormat(value)
})
//Creating dynamic Y axis with min max ticks' values depending on min max of data - copied from http://jsfiddle.net/gordonwoodhull/7anae5c5/1/
.compose([
nonzero_min(dc.lineChart(bitChart)
.dimension(min15)
.colors('blue')
.group(bitrateWeekMinIntervalGroupMove, 'Bitrate Total')
.valueAccessor(function (d) {
return d.value.total;
})
// .dashStyle([2,2])
.interpolate('step-after')
.renderArea(false)
.brushOn(false)
.renderDataPoints(false)
.clipPadding(10)),
])
bitChart.render();
//bitchart2
bitChart2 /* dc.lineChart('#bitrate-move-chart', 'chartGroup') */
.xUnits(min15_2.range) //.xUnits(d3.time.weeks)//.round(d3.time.week) //.round(d3.time.minute)//d3.time.month.round)
.x(d3.time.scale().domain([new Date(minDate), new Date(maxDate)]))
.yAxisPadding('5%')
.elasticY(true)
//Specify a "range chart" to link its brush extent with the zoom of the current "focus chart".
.rangeChart(timeSlider)
.width(450)
.height(200)
.transitionDuration(500)
.margins({ top: 30, right: 50, bottom: 25, left: 50, padding: 1 })
.mouseZoomable(true)
.brushOn(false)
.renderHorizontalGridLines(true)
.legend(dc.legend().x(800).y(10).itemHeight(13).gap(5))
//Render max bitrate horizontal line copied from bar-extra-line.html
.yAxisLabel("Total Bitrate per 15 minutes")
.renderlet(function (chart) {
chart.svg().selectAll('.chart-body').attr('clip-path', null)
})
.on('renderlet', function (chart) {
var left_y = 10, right_y = 70; // use real statistics here!
var extra_data = [{ x: chart.x().range()[0], y: chart.y()(left_y) }, { x: chart.x().range()[1], y: chart.y()(right_y) }];
var line = d3.svg.line()
.x(function (d) { return d.x; })
.y(function (d) { return maxbit; })
.interpolate('linear');
var chartBody = chart.select('g.chart-body');
var path = chartBody.selectAll('path.extra').data([extra_data]);
path.enter().append('path').attr({
class: 'extra',
stroke: 'red',
id: 'extra-line'
});
path.attr('d', line);
// Label the max line
var text = chartBody.selectAll('text.extra-label').data([0]);
text.enter().append('text')
.attr('text-anchor', 'middle')
.append('textPath').attr({
class: 'extra-label',
'xlink:href': '#extra-line',
startOffset: '50%'
})
.text('Total Bitrate Max Value');
})
// .ordinalColors('red')
// Title can be called by any stack layer.
.title(function (d) {
var value = d.value.total ? d.value.total : d.value;
if (isNaN(value)) {
value = 0;
}
return dateFormat(d.key) + ' \n Total Bit:' + numberFormat(value)
})
//Creating dynamic Y axis with min max ticks' values depending on min max of data - copied from http://jsfiddle.net/gordonwoodhull/7anae5c5/1/
.compose([
nonzero_min(dc.lineChart(bitChart2)
.dimension(min15_2)
.colors('blue')
.group(bitrateWeekMinIntervalGroupMove2, 'Bitrate Total')
.valueAccessor(function (d) {
return d.value.total;
})
//.dashStyle([2,2])
.interpolate('step-after')
.renderArea(false)
.brushOn(false)
.renderDataPoints(false)
.clipPadding(10)),
])
bitChart2.render();
//#### Range Chart
// Since this bar chart is specified as "range chart" for the area chart, its brush extent will always match the zoom of the area chart.
timeSlider
.dimension(dateDimension3)//.dimension(min15)//.dimension(weekDim)// //
.group(minIntervalWeekBitrateGroup3)
// .x(d3.time.scale().range([0, brushContainer.select("rect").attr("width")]).domain([new Date(dateDimension3.bottom(1)[0].DATETIME), new Date(dateDimension3.top(1)[0].DATETIME)]))
.x(d3.time.scale().domain([new Date(dateDimension3.bottom(1)[0].DATETIME), new Date(dateDimension3.top(1)[0].DATETIME)]))
.round(dc.round.floor) //(d3.time.month.round)
.xUnits(min15_3.range)//.xUnits(d3.time.week) //.xUnits(d3.time.minute) //.xUnits(d3.time.months)
.width(990) /* dc.barChart('#bitrate-timeSlider-chart', 'chartGroup'); */
.height(40)
.margins({ top: 0, right: 50, bottom: 20, left: 40 })
// .centerBar(true)
.gap(1)
.mouseZoomable(true)
//#### Data Count dateformat.parse(d.time);
bitCount /* dc.dataCount('.dc-data-count', 'chartGroup'); */
.dimension(crossFilteredData)
.group(all)
.html({
some: '<strong>%filter-count</strong> records selected out of <strong>%total-count</strong> records' +
' | <a href=\'javascript:dc.filterAll(); dc.renderAll();\'>Reset All</a>',
all: ' All records selected. Please click on the graph to apply filters.'
});
//#### Data Table
bitrateTable /* dc.dataTable('.dc-data-table', 'chartGroup') */
.dimension(dateDimension) // .dimension(dateDimension)
// Data table does not use crossfilter group but rather a closure as a grouping function
.group(function (d) {
var format = d3.format('02d');
return d.bitdate.getFullYear() + '/' + format((d.bitdate.getMonth() + 1));
})
.sortBy(function (d) { return d.bitdate; })
// (_optional_) max number of records to be shown, `default = 25`
.size(13)
.columns([
'DATETIME',
'CHANNEL_ID',
'BITRATE'
])
// (_optional_) custom renderlet to post-process chart using [D3](http://d3js.org)
.on('renderlet', function (table) {
table.selectAll('.dc-table-group').classed('info', true);
});
//#### Rendering
//Render all charts on the page
dc.renderAll();
//#### Versions
//Determine the current version of dc with `dc.version`
d3.selectAll('#version').text(dc.version);
// Determine latest stable version in the repo via Github API
d3.json('https://api.github.com/repos/dc-js/dc.js/releases/latest', function (error, latestRelease) {
/*jshint camelcase: false */
d3.selectAll('#latest').text(latestRelease.tag_name); /* jscs:disable */
});
});
//d3.select('#myDropDown2').on('change', function () {
// var nd = new Date(minDate);
// nd.setDate(nd.getDate() + +this.value);
// var start1 = moment(new Date(d.key));
//var nd = new Date(minDate);
//nd.setDate(nd.getDate());
// filterDimension.filterRange([nd, nd.setDate(nd.getDate() + +this.value)]);
// dc.redrawAll();
//});
//var brushContainer = d3.select("svg");
//alert( brushContainer.select("rect").attr("width"));
// start.max(new Date(minDate), new Date(maxDate));
// moment.max(start, end);
//if ((start.add(this.value, 'hours').hours()).getTime()>= end)
//{
// filterDimension.filterRange([start, end]);
//}
//else
//{
//filterDimension.filterRange([start, start.add(this.value, 'hours').hours()]);
//}
// timeSlider.filter(null);
//d3.select("svg").select("rect").enter().append('rect').attr({ width: ''+ this.value +'' });
// timeSlider.x.domain(brush.empty() ? timeSlider.x.domain() : brush.extent(0,this.value));
// focus.select("timeSlider.area").attr("d", area);
// focus.select("timeSlider.x.axis").call(xAxis);
// }
// dc.renderAll();
任何帮助将不胜感激!
这里有很多多余的代码。如果某些东西不起作用,您应该在尝试下一步之前真正将其删除。 :)
所以,这真的很简单。
首先,您通常应该通过图表设置过滤器,而不是直接在交叉过滤器维度上设置过滤器。 Crossfilter 不提供任何 getter,因此需要告知图表在何处显示画笔。并且只有在没有显示时间过滤器的图表时才需要单独的 filterDimension
。设置这个额外的维度意味着当图表被重置时,仍然有额外的过滤发生,所以重置不会发生。
为了使按钮与下拉菜单一样工作,我们只对两者使用 addHours
。我们还可以使用 replaceFilter
,这比 .filter(null)
然后设置另一个过滤器要快一点:
function drawBrush() {
if (this.innerText === "Brush Extent") { }
if (this.innerText === "3 Hours") { addHours(3); }
if (this.innerText === "6 Hours") { addHours(6); }
if (this.innerText === "12 Hours") { addHours(12); }
if (this.innerText === "24 Hours") { addHours(24); }
}
d3.select('#hoursDropDown').on('change', function() {
addHours(this.value);
});
function addHours(amountHours) {
timeSlider.replaceFilter(...);
dc.redrawAll();
}
现在 ...
中发生了什么?我认为这是让这件事变得如此混乱的另一部分,我花了一段时间才弄明白。您可能希望 moment.js 使用没有副作用的函数式样式,但实际上,它每次都在修改日期对象。所以如果你打电话给
start.add(amountHours, 'hours')
多次,它每次都会向 start
添加更多小时!相反,我们可以 clone start
before modifying it:
moment(start).add(amountHours, 'hours')
(注意:我还颠倒了参数的顺序,因为当时正在抱怨一个已弃用的接口。)
这里的另一个问题是您不想调用 moment.hours()
- 它只会获取小时数、整数而不是有效的日期对象。
将所有这些放在一起,就是:
function addHours(amountHours) {
timeSlider.replaceFilter(dc.filters.RangedFilter(start, moment(start).add(amountHours, 'hours')));
dc.redrawAll();
}
你的 fiddle 的分支:http://jsfiddle.net/gordonwoodhull/ewmrmu83/9/
我必须创建一个具有来自下拉列表的固定范围值的画笔(3、6、12、24 小时)。尝试获得画笔的影响作为画笔范围 http://jsfiddle.net/9yccpjbu/, but instead of buttons to use the drop-down (like here http://jsfiddle.net/gordonwoodhull/400wd2nd/16/)。 也欢迎对按钮进行修复。
下拉菜单
1) 下拉列表仅在第一次单击时更改显示的堆栈(条:g.chart-正文- g.stack),到显示来自下拉列表的正确值:
当从下拉列表中单击另一个值时,它会添加白色的值而不渲染堆栈(在图片中,从下拉列表中选择 6(灰色条)而不是 3(白色)的结果:
2) 点击后下拉笔刷范围(rect.extent)与
断开连接
g.chart-正文- g.stack。 (图中红框):
在不点击下拉菜单的情况下,从 timeSlider 中选择画笔效果很好,看起来像这样:
3) 点击下拉后"Reset All"link中显示的选中记录数为0(移动rect.extent堆栈显示所选记录的数量)。
4) "Reset All" link 不重置 图形** 并显示下拉菜单选择的项目数
按钮:
4) 单击时,它们会显示预期的 g.chart-body- g.stack 和维度上的互补 rect.extent (stack+rect.extent= dimesion) :
当我从
更改范围时filterDimension.filterRange([start, start.add('hours,amountHours).hours()])
到
filterDimension.filterRange([start.add('hours', amountHours).hours(), end])
g.chart-body- g.stack 和 rect.extent 连接在一起 - 但在范围的互补范围内,我需要显示来自下拉列表的画笔范围(我需要的范围(灰色)+ 显示的范围(蓝色带红色边框)= 域:
5) "Reset All" link 似乎重置了图表,但在单击 "Reset all" 时显示了错误的数字。
我的fiddle:https://jsfiddle.net/dani2011/upa3eowb/
JAVASCRIPT
'use strict';
// chart objects
var bitChart = dc.compositeChart("#bitrate-move-chart");//Before dynamic Y-Axis nonzero_min used var bitChart = dc.lineChart('#bitrate-move-chart');
var bitChart2 = dc.compositeChart("#bitrate-move-chart2");//Before dynamic Y-Axis nonzero_min used var bitChart = dc.lineChart('#bitrate-move-chart');
var timeSlider = dc.barChart('#bitrate-timeSlider-chart');
var bitCount = dc.dataCount('.dc-data-count');
var bitrateTable = dc.dataTable('.dc-data-table');
//Creating dynamic Y axis with min/max ticks' values depending on min/max of data - copied from http://jsfiddle.net/gordonwoodhull/7anae5c5/1/...
// 15 Min Interval - copied from https://github.com/mbostock/d3/blob/master/src/time/interval.js.....
// generalization of d3.time.minute copied from- https://github.com/mbostock/d3/blob/master/src/time/minute.js....
//bitchart
var min15 = n_minutes_interval(15);
//bitchart_2
var min15_2 = n_minutes_interval(15);
//timeSlider
var min15_3 = n_minutes_interval(15);
//### Load data
//var data = d3.csv.parse(d3.select("pre#data").text());
d3.csv('CHANNEL_CLUSTER_BITRATE_takeThis.csv', function (data) {
// Format CSV data
var dateFormat = d3.time.format('%Y/%m/%d/%H:%M');
var numberFormat = d3.format('.2');
data.forEach(function (d) {
d.bitdate = new Date(d.DATETIME); //d.DATETIME = dateFormat.parse(d.DATETIME);
d.hours = d3.time.hours(d.bitdate);
d.BITRATE = +d.BITRATE.match(/\d+/); //d.BITRATE = +d.BITRATE;
});
//### Crossfilter Dimensions
var crossFilteredData = crossfilter(data);
var all = crossFilteredData.groupAll();
// Dimension by full date
//bitChart
var dateDimension = crossFilteredData.dimension(function (d) {
return d.bitdate;
});
//bitChart2
var dateDimension2 = crossFilteredData.dimension(function (d) {
return d.bitdate;
});
//timeSlider
var dateDimension3 = crossFilteredData.dimension(function (d) {
return d.bitdate;
});
//dropDown
var filterDimension = crossFilteredData.dimension(function (d) {
return d.bitdate;
});
//### Crossfiltered Groups
//timeSlider
var minIntervalWeekBitrateGroup3 = dateDimension3.group(min15_3).reduceSum(function (d) {
return 10 // +d.BITRATE
});
//Group bitrate per week, 15 minInterval - maintain running tallies
//bitChart
var bitrateWeekMinIntervalGroupMove = dateDimension.group(min15).reduce(
/* callback for when data is added to the current filter results */
function (p, v) {
++p.count;
p.BITRATE = +v.BITRATE;
p.total += +v.BITRATE;
p.avg = p.count ? Math.round(p.total / p.count) : 0;
return p;
},
/* callback for when data is removed from the current filter results */
function (p, v) {
--p.count;
p.BITRATE = +v.BITRATE;
p.total -= +v.BITRATE;
p.avg = p.count ? Math.round(p.total / p.count) : 0;
return p;
},
/* initialize p */
function () {
return {
count: 0,
bitrate: 0,
total: 0,
avg: 0
};
}
);
//bitChart2
var bitrateWeekMinIntervalGroupMove2 = dateDimension2.group(min15_2).reduce(
/* callback for when data is added to the current filter results */
function (p, v) {
++p.count;
p.BITRATE = +v.BITRATE;
p.total += +v.BITRATE;
p.avg = p.count ? Math.round(p.total / p.count) : 0;
return p;
},
/* callback for when data is removed from the current filter results */
function (p, v) {
--p.count;
p.BITRATE = +v.BITRATE;
p.total -= +v.BITRATE;
p.avg = p.count ? Math.round(p.total / p.count) : 0;
return p;
},
/* initialize p */
function () {
return {
count: 0,
bitrate: 0,
total: 0,
avg: 0
};
}
);
//domain limits
var minDate = dateDimension.bottom(1)[0].DATETIME;
var maxDate = dateDimension.top(1)[0].DATETIME;
var start = moment(new Date(minDate));
var end = moment(new Date(maxDate));
//max line
var maxbit = d3.max(data, function (d) { return
+d["BITRATE"]; });
//dropdown / buttons copied from http://jsfiddle.net/gordonwoodhull/400wd2nd/16/ , http://jsfiddle.net/9yccpjbu/
var btns = d3.select(".buttons-container").selectAll("button").data(["3 Hours", "6 Hours", "12 Hours", "24 Hours"]);
btns = btns.enter().append("button")
.attr("class", "btn btn-sm btn-success")
// fill the buttons with the year from the data assigned to them
btns.each(function (d) {
this.innerText = d;
})
btns.on("click", drawBrush)
function drawBrush() {
if (this.innerText === "Brush Extent") { }
if (this.innerText === "3 Hours") { addHours(3); }
if (this.innerText === "6 Hours") { addHours(6); }
if (this.innerText === "12 Hours") { addHours(12); }
if (this.innerText === "24 Hours") { addHours(24); }
timeSlider.filter(null);
timeSlider.filter(dc.filters.RangedFilter( start, end);
// timeSlider.x.domain(brush.empty() ?
timeSlider.x.domain() : brush.extent());
dc.redrawAll();
}
function addHours(amountHours) {
filterDimension.filterRange([start, start.add('hours', amountHours).hours()]);
//filterDimension.filterRange([start.add('hours', amountHours).hours(), end]);
dc.redrawAll();
}
function brushed() {
timeSlider.x.domain(brush.empty()) ?
timeSlider.x.domain() : brush.extent());
}
function fixed_now() {
return new Date(minDate)
}
d3.select('#hoursDropDown').on('change', function() {
filterDimension.filterRange([start, start.add(this.value,
'hours').hours()]);
dc.redrawAll();
timeSlider.filter(null);//filterAll()
timeSlider.filter(dc.filters.RangedFilter(new
Date(start), new Date(end)));
dc.redrawAll();
// timeSlider.x.domain(brush.empty() ?
// timeSlider.x.domain() : brush.extent());
//var start = moment(new Date(minDate));
// var end = moment(new Date(maxDate));
// filterDimension.filterRange([start,
//start.add(this.value, 'hours').hours()]);
// dc.redrawAll();
});
//###Graphs
bitChart /* dc.lineChart('#bitrate-move-chart', 'chartGroup') */
.xUnits(min15.range) //.xUnits(d3.time.weeks)//.round(d3.time.week) //.round(d3.time.minute)//d3.time.month.round)
.x(d3.time.scale().domain([new Date(minDate), new Date(maxDate)]))
.yAxisPadding('5%')
.elasticY(true)
//Specify a "range chart" to link its brush extent with the zoom of the current "focus chart".
.rangeChart(timeSlider)
.width(450)
.height(200)
.transitionDuration(500)
.margins({ top: 30, right: 50, bottom: 25, left: 50, padding: 1 })
.mouseZoomable(true)
.brushOn(false)
.renderHorizontalGridLines(true)
.legend(dc.legend().x(800).y(10).itemHeight(13).gap(5))
//Render max bitrate horizontal line copied from bar-extra-line.html
.yAxisLabel("Total Bitrate per 15 minutes")
.renderlet(function (chart) {
chart.svg().selectAll('.chart-body').attr('clip-path', null)
})
.on('renderlet', function (chart) {
var left_y = 10, right_y = 70; // use real statistics here!
var extra_data = [{ x: chart.x().range()[0], y: chart.y()(left_y) }, { x: chart.x().range()[1], y: chart.y()(right_y) }];
var line = d3.svg.line()
.x(function (d) { return d.x; })
.y(function (d) { return maxbit; })
.interpolate('linear');
var chartBody = chart.select('g.chart-body');
var path = chartBody.selectAll('path.extra').data([extra_data]);
path.enter().append('path').attr({
class: 'extra',
stroke: 'red',
id: 'extra-line'
});
path.attr('d', line);
// Label the max line
var text = chartBody.selectAll('text.extra-label').data([0]);
text.enter().append('text')
.attr('text-anchor', 'middle')
.append('textPath').attr({
class: 'extra-label',
'xlink:href': '#extra-line',
startOffset: '50%'
})
.text('Total Bitrate Max Value');
})
// .ordinalColors('red')
// Title can be called by any stack layer.
.title(function (d) {
var value = d.value.total ? d.value.total : d.value;
if (isNaN(value)) {
value = 0;
}
return dateFormat(d.key) + ' \n Total Bit:' + numberFormat(value)
})
//Creating dynamic Y axis with min max ticks' values depending on min max of data - copied from http://jsfiddle.net/gordonwoodhull/7anae5c5/1/
.compose([
nonzero_min(dc.lineChart(bitChart)
.dimension(min15)
.colors('blue')
.group(bitrateWeekMinIntervalGroupMove, 'Bitrate Total')
.valueAccessor(function (d) {
return d.value.total;
})
// .dashStyle([2,2])
.interpolate('step-after')
.renderArea(false)
.brushOn(false)
.renderDataPoints(false)
.clipPadding(10)),
])
bitChart.render();
//bitchart2
bitChart2 /* dc.lineChart('#bitrate-move-chart', 'chartGroup') */
.xUnits(min15_2.range) //.xUnits(d3.time.weeks)//.round(d3.time.week) //.round(d3.time.minute)//d3.time.month.round)
.x(d3.time.scale().domain([new Date(minDate), new Date(maxDate)]))
.yAxisPadding('5%')
.elasticY(true)
//Specify a "range chart" to link its brush extent with the zoom of the current "focus chart".
.rangeChart(timeSlider)
.width(450)
.height(200)
.transitionDuration(500)
.margins({ top: 30, right: 50, bottom: 25, left: 50, padding: 1 })
.mouseZoomable(true)
.brushOn(false)
.renderHorizontalGridLines(true)
.legend(dc.legend().x(800).y(10).itemHeight(13).gap(5))
//Render max bitrate horizontal line copied from bar-extra-line.html
.yAxisLabel("Total Bitrate per 15 minutes")
.renderlet(function (chart) {
chart.svg().selectAll('.chart-body').attr('clip-path', null)
})
.on('renderlet', function (chart) {
var left_y = 10, right_y = 70; // use real statistics here!
var extra_data = [{ x: chart.x().range()[0], y: chart.y()(left_y) }, { x: chart.x().range()[1], y: chart.y()(right_y) }];
var line = d3.svg.line()
.x(function (d) { return d.x; })
.y(function (d) { return maxbit; })
.interpolate('linear');
var chartBody = chart.select('g.chart-body');
var path = chartBody.selectAll('path.extra').data([extra_data]);
path.enter().append('path').attr({
class: 'extra',
stroke: 'red',
id: 'extra-line'
});
path.attr('d', line);
// Label the max line
var text = chartBody.selectAll('text.extra-label').data([0]);
text.enter().append('text')
.attr('text-anchor', 'middle')
.append('textPath').attr({
class: 'extra-label',
'xlink:href': '#extra-line',
startOffset: '50%'
})
.text('Total Bitrate Max Value');
})
// .ordinalColors('red')
// Title can be called by any stack layer.
.title(function (d) {
var value = d.value.total ? d.value.total : d.value;
if (isNaN(value)) {
value = 0;
}
return dateFormat(d.key) + ' \n Total Bit:' + numberFormat(value)
})
//Creating dynamic Y axis with min max ticks' values depending on min max of data - copied from http://jsfiddle.net/gordonwoodhull/7anae5c5/1/
.compose([
nonzero_min(dc.lineChart(bitChart2)
.dimension(min15_2)
.colors('blue')
.group(bitrateWeekMinIntervalGroupMove2, 'Bitrate Total')
.valueAccessor(function (d) {
return d.value.total;
})
//.dashStyle([2,2])
.interpolate('step-after')
.renderArea(false)
.brushOn(false)
.renderDataPoints(false)
.clipPadding(10)),
])
bitChart2.render();
//#### Range Chart
// Since this bar chart is specified as "range chart" for the area chart, its brush extent will always match the zoom of the area chart.
timeSlider
.dimension(dateDimension3)//.dimension(min15)//.dimension(weekDim)// //
.group(minIntervalWeekBitrateGroup3)
// .x(d3.time.scale().range([0, brushContainer.select("rect").attr("width")]).domain([new Date(dateDimension3.bottom(1)[0].DATETIME), new Date(dateDimension3.top(1)[0].DATETIME)]))
.x(d3.time.scale().domain([new Date(dateDimension3.bottom(1)[0].DATETIME), new Date(dateDimension3.top(1)[0].DATETIME)]))
.round(dc.round.floor) //(d3.time.month.round)
.xUnits(min15_3.range)//.xUnits(d3.time.week) //.xUnits(d3.time.minute) //.xUnits(d3.time.months)
.width(990) /* dc.barChart('#bitrate-timeSlider-chart', 'chartGroup'); */
.height(40)
.margins({ top: 0, right: 50, bottom: 20, left: 40 })
// .centerBar(true)
.gap(1)
.mouseZoomable(true)
//#### Data Count dateformat.parse(d.time);
bitCount /* dc.dataCount('.dc-data-count', 'chartGroup'); */
.dimension(crossFilteredData)
.group(all)
.html({
some: '<strong>%filter-count</strong> records selected out of <strong>%total-count</strong> records' +
' | <a href=\'javascript:dc.filterAll(); dc.renderAll();\'>Reset All</a>',
all: ' All records selected. Please click on the graph to apply filters.'
});
//#### Data Table
bitrateTable /* dc.dataTable('.dc-data-table', 'chartGroup') */
.dimension(dateDimension) // .dimension(dateDimension)
// Data table does not use crossfilter group but rather a closure as a grouping function
.group(function (d) {
var format = d3.format('02d');
return d.bitdate.getFullYear() + '/' + format((d.bitdate.getMonth() + 1));
})
.sortBy(function (d) { return d.bitdate; })
// (_optional_) max number of records to be shown, `default = 25`
.size(13)
.columns([
'DATETIME',
'CHANNEL_ID',
'BITRATE'
])
// (_optional_) custom renderlet to post-process chart using [D3](http://d3js.org)
.on('renderlet', function (table) {
table.selectAll('.dc-table-group').classed('info', true);
});
//#### Rendering
//Render all charts on the page
dc.renderAll();
//#### Versions
//Determine the current version of dc with `dc.version`
d3.selectAll('#version').text(dc.version);
// Determine latest stable version in the repo via Github API
d3.json('https://api.github.com/repos/dc-js/dc.js/releases/latest', function (error, latestRelease) {
/*jshint camelcase: false */
d3.selectAll('#latest').text(latestRelease.tag_name); /* jscs:disable */
});
});
//d3.select('#myDropDown2').on('change', function () {
// var nd = new Date(minDate);
// nd.setDate(nd.getDate() + +this.value);
// var start1 = moment(new Date(d.key));
//var nd = new Date(minDate);
//nd.setDate(nd.getDate());
// filterDimension.filterRange([nd, nd.setDate(nd.getDate() + +this.value)]);
// dc.redrawAll();
//});
//var brushContainer = d3.select("svg");
//alert( brushContainer.select("rect").attr("width"));
// start.max(new Date(minDate), new Date(maxDate));
// moment.max(start, end);
//if ((start.add(this.value, 'hours').hours()).getTime()>= end)
//{
// filterDimension.filterRange([start, end]);
//}
//else
//{
//filterDimension.filterRange([start, start.add(this.value, 'hours').hours()]);
//}
// timeSlider.filter(null);
//d3.select("svg").select("rect").enter().append('rect').attr({ width: ''+ this.value +'' });
// timeSlider.x.domain(brush.empty() ? timeSlider.x.domain() : brush.extent(0,this.value));
// focus.select("timeSlider.area").attr("d", area);
// focus.select("timeSlider.x.axis").call(xAxis);
// }
// dc.renderAll();
任何帮助将不胜感激!
这里有很多多余的代码。如果某些东西不起作用,您应该在尝试下一步之前真正将其删除。 :)
所以,这真的很简单。
首先,您通常应该通过图表设置过滤器,而不是直接在交叉过滤器维度上设置过滤器。 Crossfilter 不提供任何 getter,因此需要告知图表在何处显示画笔。并且只有在没有显示时间过滤器的图表时才需要单独的 filterDimension
。设置这个额外的维度意味着当图表被重置时,仍然有额外的过滤发生,所以重置不会发生。
为了使按钮与下拉菜单一样工作,我们只对两者使用 addHours
。我们还可以使用 replaceFilter
,这比 .filter(null)
然后设置另一个过滤器要快一点:
function drawBrush() {
if (this.innerText === "Brush Extent") { }
if (this.innerText === "3 Hours") { addHours(3); }
if (this.innerText === "6 Hours") { addHours(6); }
if (this.innerText === "12 Hours") { addHours(12); }
if (this.innerText === "24 Hours") { addHours(24); }
}
d3.select('#hoursDropDown').on('change', function() {
addHours(this.value);
});
function addHours(amountHours) {
timeSlider.replaceFilter(...);
dc.redrawAll();
}
现在 ...
中发生了什么?我认为这是让这件事变得如此混乱的另一部分,我花了一段时间才弄明白。您可能希望 moment.js 使用没有副作用的函数式样式,但实际上,它每次都在修改日期对象。所以如果你打电话给
start.add(amountHours, 'hours')
多次,它每次都会向 start
添加更多小时!相反,我们可以 clone start
before modifying it:
moment(start).add(amountHours, 'hours')
(注意:我还颠倒了参数的顺序,因为当时正在抱怨一个已弃用的接口。)
这里的另一个问题是您不想调用 moment.hours()
- 它只会获取小时数、整数而不是有效的日期对象。
将所有这些放在一起,就是:
function addHours(amountHours) {
timeSlider.replaceFilter(dc.filters.RangedFilter(start, moment(start).add(amountHours, 'hours')));
dc.redrawAll();
}
你的 fiddle 的分支:http://jsfiddle.net/gordonwoodhull/ewmrmu83/9/