multichart model that can handle Stacked, Line, and Bar charts across 2 axes

master-patched
Steven Noble 12 years ago
parent c09378e963
commit 06139a64cd

@ -0,0 +1,104 @@
<!DOCTYPE html>
<meta charset="utf-8">
<link href="../src/nv.d3.css" rel="stylesheet" type="text/css">
<style>
body {
overflow-y:scroll;
}
text {
font: 12px sans-serif;
}
#chart1 svg {
height: 500px;
margin: 10px;
min-width: 100px;
min-height: 100px;
/*
Minimum height and width is a good idea to prevent negative SVG dimensions...
For example width should be =< margin.left + margin.right + 1,
of course 1 pixel for the entire chart would not be very useful, BUT should not have errors
*/
}
</style>
<body>
<div id="chart1">
<svg> </svg>
</div>
<script src="../lib/d3.v2.js"></script>
<script src="../nv.d3.js"></script>
<script src="../src/tooltip.js"></script>
<script src="../src/models/legend.js"></script>
<script src="../src/models/axis.js"></script>
<script src="../src/models/scatter.js"></script>
<script src="../src/models/line.js"></script>
<script src="../src/models/multiBar.js"></script>
<script src="../src/models/multiChart.js"></script>
<script src="stream_layers.js"></script>
<script>
var testdata = stream_layers(3,10+Math.random()*100,.1).map(function(data, i) {
return {
key: 'Stream' + i,
values: data
};
});
testdata[0].type = "area"
testdata[0].yAxis = 1
testdata[1].type = "line"
testdata[1].yAxis = 2
testdata[2].type = "line"
testdata[2].yAxis = 2
/*
//For testing single data point
var testdata = [
{
"key" : "Quantity" ,
"bar": true,
"values" : [ [ 1136005200000 , 1271000.0] ]
} ,
{
"key" : "Price" ,
"values" : [ [ 1136005200000 , 71.89] ]
}
].map(function(series) {
series.values = series.values.map(function(d) { return {x: d[0], y: d[1] } });
return series;
});
*/
nv.addGraph(function() {
var chart = nv.models.multiChart()
.margin({top: 30, right: 60, bottom: 50, left: 70})
.color(d3.scale.category10().range());
chart.xAxis
.tickFormat(d3.format(',f'));
chart.yLinesAxis1
.tickFormat(d3.format(',.1f'));
chart.yLinesAxis2
.tickFormat(d3.format(',.1f'));
d3.select('#chart1 svg')
.datum(testdata)
.transition().duration(500).call(chart);
return chart;
});
</script>

@ -0,0 +1,299 @@
nv.models.multiChart = function() {
//============================================================
// Public Variables with Default Settings
//------------------------------------------------------------
var margin = {top: 30, right: 20, bottom: 50, left: 60},
color = d3.scale.category20().range(),
width = null,
height = null,
showLegend = true,
tooltips = true,
tooltip = function(key, x, y, e, graph) {
return '<h3>' + key + '</h3>' +
'<p>' + y + ' at ' + x + '</p>'
},
x, y; //can be accessed via chart.lines.[x/y]Scale()
//============================================================
// Private Variables
//------------------------------------------------------------
var x = d3.scale.linear(),
lines1 = nv.models.line(),
yLines1 = lines1.yScale(),
lines2 = nv.models.line(),
yLines2 = lines2.yScale(),
bars1 = nv.models.multiBar().stacked(false),
yBars1 = bars1.yScale(),
bars2 = nv.models.multiBar().stacked(false),
yBars2 = bars2.yScale(),
stack1 = nv.models.stackedArea(),
yStack1 = stack1.yScale(),
stack2 = nv.models.stackedArea(),
yStack2 = stack2.yScale(),
xAxis = nv.models.axis().scale(x).orient('bottom').tickPadding(5),
yLinesAxis1 = nv.models.axis().scale(yLines1).orient('left'),
yBarsAxis1 = nv.models.axis().scale(yBars1).orient('left'),
yStackAxis1 = nv.models.axis().scale(yStack1).orient('left'),
yLinesAxis2 = nv.models.axis().scale(yLines2).orient('right'),
yBarsAxis2 = nv.models.axis().scale(yBars2).orient('right'),
yStackAxis2 = nv.models.axis().scale(yStack2).orient('right'),
legend = nv.models.legend().height(30),
dispatch = d3.dispatch('tooltipShow', 'tooltipHide');
function chart(selection) {
selection.each(function(data) {
var container = d3.select(this),
that = this;
var availableWidth = (width || parseInt(container.style('width')) || 960)
- margin.left - margin.right,
availableHeight = (height || parseInt(container.style('height')) || 400)
- margin.top - margin.bottom;
var dataLines1 = data.filter(function(d) {return !d.disabled && d.type == 'line' && d.yAxis == 1})
var dataLines2 = data.filter(function(d) {return !d.disabled && d.type == 'line' && d.yAxis == 2})
var dataBars1 = data.filter(function(d) {return !d.disabled && d.type == 'bar' && d.yAxis == 1})
var dataBars2 = data.filter(function(d) {return !d.disabled && d.type == 'bar' && d.yAxis == 2})
var dataStack1 = data.filter(function(d) {return !d.disabled && d.type == 'area' && d.yAxis == 1})
var dataStack2 = data.filter(function(d) {return !d.disabled && d.type == 'area' && d.yAxis == 2})
var series1 = data.filter(function(d) {return !d.disabled && d.yAxis == 1})
.map(function(d) {
return d.values.map(function(d,i) {
return { x: d.x, y: d.y }
})
})
var series2 = data.filter(function(d) {return !d.disabled && d.yAxis == 2})
.map(function(d) {
return d.values.map(function(d,i) {
return { x: d.x, y: d.y }
})
})
x .domain(d3.extent(d3.merge(series1.concat(series2)), function(d) { return d.x } ))
.range([0, availableWidth]);
var wrap = container.selectAll('g.wrap.multiChart').data([data]);
var gEnter = wrap.enter().append('g').attr('class', 'wrap nvd3 multiChart').append('g');
gEnter.append('g').attr('class', 'x axis');
gEnter.append('g').attr('class', 'y1 axis');
gEnter.append('g').attr('class', 'y2 axis');
gEnter.append('g').attr('class', 'lines1Wrap');
gEnter.append('g').attr('class', 'lines2Wrap');
gEnter.append('g').attr('class', 'bars1Wrap');
gEnter.append('g').attr('class', 'bars2Wrap');
gEnter.append('g').attr('class', 'stack1Wrap');
gEnter.append('g').attr('class', 'stack2Wrap');
var g = wrap.select('g');
lines1
.width(availableWidth)
.height(availableHeight)
.interpolate("monotone")
.color(data.map(function(d,i) {
return d.color || color[i % color.length];
}).filter(function(d,i) { return !data[i].disabled && data[i].yAxis == 1 && data[i].type == 'line'}));
lines2
.width(availableWidth)
.height(availableHeight)
.interpolate("monotone")
.color(data.map(function(d,i) {
return d.color || color[i % color.length];
}).filter(function(d,i) { return !data[i].disabled && data[i].yAxis == 2 && data[i].type == 'line'}));
bars1
.width(availableWidth)
.height(availableHeight)
.color(data.map(function(d,i) {
return d.color || color[i % color.length];
}).filter(function(d,i) { return !data[i].disabled && data[i].yAxis == 1 && data[i].type == 'bar'}));
bars2
.width(availableWidth)
.height(availableHeight)
.color(data.map(function(d,i) {
return d.color || color[i % color.length];
}).filter(function(d,i) { return !data[i].disabled && data[i].yAxis == 2}));
stack1
.width(availableWidth)
.height(availableHeight)
.color(data.map(function(d,i) {
return d.color || color[i % color.length];
}).filter(function(d,i) { return !data[i].disabled && data[i].yAxis == 1}));
stack2
.width(availableWidth)
.height(availableHeight)
.color(data.map(function(d,i) {
return d.color || color[i % color.length];
}).filter(function(d,i) { return !data[i].disabled && data[i].yAxis == 2}));
g.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
var lines1Wrap = g.select('.lines1Wrap')
.datum(dataLines1)
var bars1Wrap = g.select('.bars1Wrap')
.datum(dataBars1)
var stack1Wrap = g.select('.stack1Wrap')
.datum(dataStack1)
var lines2Wrap = g.select('.lines2Wrap')
.datum(dataLines2)
var bars2Wrap = g.select('.bars2Wrap')
.datum(dataBars2)
var stack2Wrap = g.select('.stack2Wrap')
.datum(dataStack2)
var extraValue1 = []
var extraValue2 = []
if(dataLines1.length){d3.transition(lines1Wrap).call(lines1);}
if(dataBars1.length){d3.transition(bars1Wrap).call(bars1);}
if(dataStack1.length){
d3.transition(stack1Wrap).call(stack1);
extraValue1.push({x:0, y:0})
}
if(dataLines2.length){d3.transition(lines2Wrap).call(lines2);}
if(dataBars2.length){d3.transition(bars2Wrap).call(bars2);}
if(dataStack2.length){
d3.transition(stack2Wrap).call(stack2);
extraValue2.push({x:0, y:0})
}
yLinesAxis1.domain(d3.extent(d3.merge(series1), function(d) { return d.y } ))
yBarsAxis1.domain(d3.extent(d3.merge(series1), function(d) { return d.y } ))
yStackAxis1.domain(d3.extent(d3.merge(series1).concat(extraValue1), function(d) { return d.y } ))
yLinesAxis2.domain(d3.extent(d3.merge(series2), function(d) { return d.y } ))
yBarsAxis2.domain(d3.extent(d3.merge(series2), function(d) { return d.y } ))
yStackAxis2.domain(d3.extent(d3.merge(series2).concat(extraValue2), function(d) { return d.y } ))
xAxis
.ticks( availableWidth / 100 )
.tickSize(-availableHeight, 0);
g.select('.x.axis')
.attr('transform', 'translate(0,' + availableHeight + ')');
d3.transition(g.select('.x.axis'))
.call(xAxis);
yLinesAxis1
.ticks( availableHeight / 36 )
.tickSize( -availableWidth, 0);
// d3.transition(g.select('.y1.axis'))
// .call(yLinesAxis1);
d3.transition(g.select('.y1.axis'))
.call(yStackAxis1);
yLinesAxis2
.ticks( availableHeight / 36 )
.tickSize( -availableWidth, 0);
d3.transition(g.select('.y2.axis'))
.call(yLinesAxis2);
g.select('.y2.axis')
.style('opacity', series2.length ? 1 : 0)
.attr('transform', 'translate(' + x.range()[1] + ',0)');
});
chart.update = function() { chart(selection) };
chart.container = this;
return chart;
}
//============================================================
// Global getters and setters
//------------------------------------------------------------
chart.dispatch = dispatch;
chart.lines1 = lines1;
chart.lines2 = lines2;
chart.bars1 = bars1;
chart.bars2 = bars2;
chart.stack1 = stack1;
chart.stack2 = stack2;
chart.xAxis = xAxis;
chart.yLinesAxis1 = yStackAxis1;
chart.yLinesAxis2 = yLinesAxis2;
d3.rebind(chart, lines1, 'defined', 'isArea', 'x', 'y', 'size', 'xScale', 'yScale', 'xDomain', 'yDomain', 'forceX', 'forceY', 'interactive', 'clipEdge', 'clipVoronoi', 'id', 'interpolate');
chart.x = function(_) {
if (!arguments.length) return getX;
getX = _;
lines1.x(_);
bars1.x(_);
return chart;
};
chart.y = function(_) {
if (!arguments.length) return getY;
getY = _;
lines1.y(_);
bars1.y(_);
return chart;
};
chart.margin = function(_) {
if (!arguments.length) return margin;
margin = _;
return chart;
};
chart.width = function(_) {
if (!arguments.length) return width;
width = _;
return chart;
};
chart.height = function(_) {
if (!arguments.length) return height;
height = _;
return chart;
};
chart.color = function(_) {
if (!arguments.length) return color;
color = _;
legend.color(_);
return chart;
};
// chart.showLegend = function(_) {
// if (!arguments.length) return showLegend;
// showLegend = _;
// return chart;
// };
// chart.tooltips = function(_) {
// if (!arguments.length) return tooltips;
// tooltips = _;
// return chart;
// };
// chart.tooltipContent = function(_) {
// if (!arguments.length) return tooltip;
// tooltip = _;
// return chart;
// };
return chart;
}
Loading…
Cancel
Save