diff --git a/examples/multiBarChart.html b/examples/multiBarChart.html index f214a6957..427194cd2 100644 --- a/examples/multiBarChart.html +++ b/examples/multiBarChart.html @@ -98,4 +98,4 @@ - \ No newline at end of file + diff --git a/examples/multiChart2.html b/examples/multiChart2.html new file mode 100644 index 000000000..e4550b2b2 --- /dev/null +++ b/examples/multiChart2.html @@ -0,0 +1,386 @@ + + + + + + + + + + + + + +
+ +
+ + + + diff --git a/examples/site.html b/examples/site.html index ddb920c64..1f4972763 100644 --- a/examples/site.html +++ b/examples/site.html @@ -116,6 +116,7 @@

MultiBarChart2 | MultiBarHorizontalChart | MultiChart | + MultiChart2 | DonutCharts | PieCharts | MonitoringCharts | diff --git a/src/models/historicalBar.js b/src/models/historicalBar.js index d21c532c7..9ddf613a9 100644 --- a/src/models/historicalBar.js +++ b/src/models/historicalBar.js @@ -41,14 +41,30 @@ nv.models.historicalBar = function() { nv.utils.initSVG(container); // Setup Scales - x.domain(xDomain || d3.extent(data[0].values.map(getX).concat(forceX) )); + // remap and flatten the data for use in calculating the scales' domains + var seriesData = (xDomain && yDomain) ? [] : // if we know xDomain and yDomain, no need to calculate + data.map(function(d, idx) { + return d.values.map(function(d,i) { + return { x: getX(d,i), y: getY(d,i), y0: d.y0, y1: d.y1, idx:idx } + }) + }); + + var _getX = function (d) { return d.x; }; + var _getY = function (d) { return d.y; }; + var merged = d3.merge(seriesData); + + var dataValuesLength = data[0] ? data[0].values.length : 0; + // Setup Scales + x.domain(xDomain || d3.extent(merged.map(_getX).concat(forceX) ).filter(function (el) { return el !== undefined })); + + // TODO: multichart with historicalBar will throw when data is empty if (padData) - x.range(xRange || [availableWidth * .5 / data[0].values.length, availableWidth * (data[0].values.length - .5) / data[0].values.length ]); + x.range(xRange || [availableWidth * .5 / dataValuesLength, availableWidth * (dataValuesLength - .5) / dataValuesLength ]); else x.range(xRange || [0, availableWidth]); - y.domain(yDomain || d3.extent(data[0].values.map(getY).concat(forceY) )) + y.domain(yDomain || d3.extent(merged.map(_getY).concat(forceY) )) .range(yRange || [availableHeight, 0]); // If scale's domain don't have a range, slightly adjust to make one... so a chart can show a single data point @@ -63,7 +79,7 @@ nv.models.historicalBar = function() { : y.domain([-1,1]); // Setup containers and skeleton of chart - var wrap = container.selectAll('g.nv-wrap.nv-historicalBar-' + id).data([data[0].values]); + var wrap = container.selectAll('g.nv-wrap.nv-historicalBar-' + id).data([data]); var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-historicalBar-' + id); var defsEnter = wrapEnter.append('defs'); var gEnter = wrapEnter.append('g'); @@ -93,12 +109,12 @@ nv.models.historicalBar = function() { g.attr('clip-path', clipEdge ? 'url(#nv-chart-clip-path-' + id + ')' : ''); var bars = wrap.select('.nv-bars').selectAll('.nv-bar') - .data(function(d) { return d }, function(d,i) {return getX(d,i)}); + .data(function(d) { return d[0] ? d[0].values: []; }); bars.exit().remove(); bars.enter().append('rect') .attr('x', 0 ) - .attr('y', function(d,i) { return nv.utils.NaNtoZero(y(Math.max(0, getY(d,i)))) }) + .attr('y', function(d,i) { return nv.utils.NaNtoZero(y(Math.max(0, getY(d,i)))) }) .attr('height', function(d,i) { return nv.utils.NaNtoZero(Math.abs(y(getY(d,i)) - y(0))) }) .attr('transform', function(d,i) { return 'translate(' + (x(getX(d,i)) - availableWidth / data[0].values.length * .45) + ',0)'; }) .on('mouseover', function(d,i) { @@ -153,10 +169,9 @@ nv.models.historicalBar = function() { bars .attr('fill', function(d,i) { return color(d, i); }) .attr('class', function(d,i,j) { return (getY(d,i) < 0 ? 'nv-bar negative' : 'nv-bar positive') + ' nv-bar-' + j + '-' + i }) - .watchTransition(renderWatch, 'bars') - .attr('transform', function(d,i) { return 'translate(' + (x(getX(d,i)) - availableWidth / data[0].values.length * .45) + ',0)'; }) + .attr('transform', function(d,i) { return 'translate(' + (x(getX(d,i)) - availableWidth / dataValuesLength * .45) + ',0)'; }) //TODO: better width calculations that don't assume always uniform data spacing;w - .attr('width', (availableWidth / data[0].values.length) * .9 ); + .attr('width', (availableWidth / dataValuesLength) * .9 ); bars.watchTransition(renderWatch, 'bars') .attr('y', function(d,i) { diff --git a/src/models/multiChart.js b/src/models/multiChart.js index c6499f7ef..66990149c 100644 --- a/src/models/multiChart.js +++ b/src/models/multiChart.js @@ -1,4 +1,4 @@ -nv.models.multiChart = function() { +nv.models.multiChart = function () { "use strict"; //============================================================ @@ -38,8 +38,10 @@ nv.models.multiChart = function() { scatters1 = nv.models.scatter().yScale(yScale1).duration(duration), scatters2 = nv.models.scatter().yScale(yScale2).duration(duration), - bars1 = nv.models.multiBar().stacked(false).yScale(yScale1).duration(duration), - bars2 = nv.models.multiBar().stacked(false).yScale(yScale2).duration(duration), + barsModel, + + bars1 = getBarModel(yScale1), + bars2 = getBarModel(yScale2), stack1 = nv.models.stackedArea().yScale(yScale1).duration(duration), stack2 = nv.models.stackedArea().yScale(yScale2).duration(duration), @@ -54,6 +56,18 @@ nv.models.multiChart = function() { var charts = [lines1, lines2, scatters1, scatters2, bars1, bars2, stack1, stack2]; + function getBarModel(yScale) { + var model; + + if (typeof barsModel === 'function') { + model = barsModel(); + } else { + model = nv.models.multiBar().stacked(false).duration(duration); + } + + return model.yScale(yScale); + } + function chart(selection) { selection.each(function(data) { var container = d3.select(this), @@ -66,6 +80,11 @@ nv.models.multiChart = function() { var availableWidth = nv.utils.availableWidth(width, container, margin), availableHeight = nv.utils.availableHeight(height, container, margin); + charts.forEach(function (chart) { + chart.x(getX); + chart.y(getY); + }); + var dataLines1 = data.filter(function(d) {return d.type == 'line' && d.yAxis == 1}); var dataLines2 = data.filter(function(d) {return d.type == 'line' && d.yAxis == 2}); var dataScatters1 = data.filter(function(d) {return d.type == 'scatter' && d.yAxis == 1}); @@ -75,6 +94,15 @@ nv.models.multiChart = function() { var dataStack1 = data.filter(function(d) {return d.type == 'area' && d.yAxis == 1}); var dataStack2 = data.filter(function(d) {return d.type == 'area' && d.yAxis == 2}); + // fixes tooltips for bar models other than multibar + data.forEach(function(series, i) { + series.values.forEach(function(point) { + point.series = i; + point.key = series.key; + }); + }); + + // TODO: duplicate in linePlusBarChart.js# // Display noData message if there's nothing to show. if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) { nv.utils.noData(chart, container); @@ -516,9 +544,20 @@ nv.models.multiChart = function() { chart._options = Object.create({}, { // simple options, just get/set the necessary values + barsModel: {get: function(){return barsModel}, set: function(_){ + var bars1Idx = charts.indexOf(bars1), + bars2Idx = charts.indexOf(bars2); + + barsModel = _; + bars1 = getBarModel(yScale1); + bars2 = getBarModel(yScale2); + charts.splice(bars1Idx, bars1Idx + 1, bars1); + charts.splice(bars2Idx, bars2Idx + 1, bars2); + }}, width: {get: function(){return width;}, set: function(_){width=_;}}, height: {get: function(){return height;}, set: function(_){height=_;}}, showLegend: {get: function(){return showLegend;}, set: function(_){showLegend=_;}}, + xScale: {get: function(){return x;}, set: function(_){ x = _; xAxis.scale(x); }}, yDomain1: {get: function(){return yDomain1;}, set: function(_){yDomain1=_;}}, yDomain2: {get: function(){return yDomain2;}, set: function(_){yDomain2=_;}}, noData: {get: function(){return noData;}, set: function(_){noData=_;}}, @@ -540,25 +579,9 @@ nv.models.multiChart = function() { }}, x: {get: function(){return getX;}, set: function(_){ getX = _; - lines1.x(_); - lines2.x(_); - scatters1.x(_); - scatters2.x(_); - bars1.x(_); - bars2.x(_); - stack1.x(_); - stack2.x(_); }}, y: {get: function(){return getY;}, set: function(_){ getY = _; - lines1.y(_); - lines2.y(_); - scatters1.y(_); - scatters2.y(_); - stack1.y(_); - stack2.y(_); - bars1.y(_); - bars2.y(_); }}, useVoronoi: {get: function(){return useVoronoi;}, set: function(_){ useVoronoi=_;