我必须创建一个固定范围值来自下拉(3,6,12,24小时)的画笔。尽量将画笔的效果作为画笔范围http://jsfiddle.net/9yccpjbu/,而不是按钮来使用下拉菜单(例如此处http://jsfiddle.net/gordonwoodhull/400wd2nd/16/)。 我们也欢迎修复按钮。
下拉
1)下拉列表仅在第一次点击时更改显示的堆栈(条形码:g.chart-body- g.stack),以显示来自投放的正确值 - 下:
当从下拉列表中点击另一个值时,它会以白色添加值而不渲染堆栈(在图片中从下拉列表中选择6的结果(灰色条)和3(白色):
2)点击后,下拉画笔范围(rect.extent)从左侧开始 g.chart-body-g.stack 。 (图中红色边框):
如果没有点击下拉列表,从timeSlider中选择画笔效果很好,看起来像这样:
3)点击下拉列表后,显示"全部重置" link is 0 (在堆栈上移动rect.extent显示所选记录的数量)。
4)"全部重置"链接不会重置图表**并显示下拉列表中选择的项目数
按钮:
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)"全部重置"链接似乎重置图表,但是当"重置所有"时显示错误的数字。点击。
我的小提琴: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();
非常感谢任何帮助!
答案 0 :(得分:1)
这里有很多多余的代码。如果某些东西不起作用,你应该在尝试下一件事之前将其删除。 :)
所以,这真的很简单。
首先,您通常应该通过图表设置过滤器,而不是直接在crossfilter维度上设置过滤器。 Crossfilter不提供任何吸气剂,因此需要告知图表显示刷子的位置。如果没有显示时间过滤器的图表,则只需要单独的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();
}