下拉更改时画笔范围/宽度(dc.js,moment.js,d3.js,crossfilter)

时间:2016-12-01 19:30:09

标签: momentjs dc.js

我必须创建一个固定范围值来自下拉(3,6,12,24小时)的画笔。尽量将画笔的效果作为画笔范围http://jsfiddle.net/9yccpjbu/,而不是按钮来使用下拉菜单(例如此处http://jsfiddle.net/gordonwoodhull/400wd2nd/16/)。 我们也欢迎修复按钮。

下拉

1)下拉列表仅在第一次点击时更改显示的堆栈(条形码:g.chart-body- g.stack),以显示来自投放的正确值 - 下:

enter image description here

当从下拉列表中点击另一个值时,它会以白色添加值而不渲染堆栈(在图片中从下拉列表中选择6的结果(灰色条)和3(白色):

enter image description here

2)点击后,下拉画笔范围(rect.extent)从左侧开始    g.chart-body-g.stack 。 (图中红色边框):

enter image description here

如果没有点击下拉列表,从timeSlider中选择画笔效果很好,看起来像这样:

enter image description here

3)点击下拉列表后,显示"全部重置" link is 0 (在堆栈上移动rect.extent显示所选记录的数量)。

4)"全部重置"链接不会重置图表**并显示下拉列表中选择的项目数

按钮:

4)单击时,它们会显示g.chart-body-g.stack的预期图形和维度上的互补rect.extent(stack + rect.extent = dimesion):

enter image description here

当我从

更改范围时
filterDimension.filterRange([start, start.add('hours,amountHours).hours()]) 

filterDimension.filterRange([start.add('hours', amountHours).hours(), end])

g.chart-body-g.stack和rect.extent被连接 - 但是在范围的互补范围内,我需要显示来自下拉列表的刷子范围(我需要的范围(灰色)+范围显示(蓝色带红色边框)=域名):

enter image description here

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();

非常感谢任何帮助!

1 个答案:

答案 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();
}

你小提琴的叉子:http://jsfiddle.net/gordonwoodhull/ewmrmu83/9/