energy spline stack

This commit is contained in:
Eric Hulburd
2016-02-15 16:09:40 -06:00
parent 1758fce5a4
commit 9ea4a8ebe9
7 changed files with 106 additions and 141 deletions

4
client/d3/base.js vendored
View File

@@ -3,7 +3,7 @@ import extend from 'extend';
const DEFAULTS = {
outer_width: 500,
outer_height: 300,
margin: {top: 0, left: 70, bottom: 50, right: 20},
margin: {top: 30, left: 70, bottom: 50, right: 20},
domain_ticks: 10,
range_ticks: 8,
container: "container",
@@ -12,7 +12,7 @@ const DEFAULTS = {
domain_attr: undefined,
range_attr: undefined,
titleize: function(series, datum){
var s = datum ? datum.name : series.title;
var s = datum ? datum.title : series.title;
if (!s) return '';
var words = s.split(' '),
array = [];

View File

@@ -1,42 +1,10 @@
import extend from 'extend';
const DEFAULTS = {
outer_width: 500,
outer_height: 300,
margin: {top: 0, left: 70, bottom: 50, right: 20},
domain_ticks: 10,
range_ticks: 8,
container: "container",
time_series: true,
range_label: "range",
domain_attr: null,
range_attr: 'y',
titleize: function(series, datum){
var s = datum ? datum.name : series.name,
words = s.split(' '),
array = [];
for (var i=0; i<words.length; ++i) {
array.push(words[i].charAt(0).toUpperCase() + words[i].toLowerCase().slice(1));
}
return array.join(' ');
},
toClass: function(series){
return series ? series.title.toLowerCase().replace(/\s+/g, '-') : "";
}
}
import Chart from './../base';
// inspired by https://gist.github.com/mbostock/4b66c0d9be9a0d56484e
class LineChart {
constructor(options){
var line_chart = this;
line_chart = extend(line_chart, DEFAULTS, options);
line_chart.height = line_chart.outer_height - line_chart.margin.top - line_chart.margin.bottom;
line_chart.width = line_chart.outer_width - line_chart.margin.left - line_chart.margin.right;
line_chart.init();
}
class LineChart extends Chart {
get chart_options(){
return {
@@ -44,27 +12,6 @@ class LineChart {
};
}
init(){
var line_chart = this;
line_chart.svg = d3.select(line_chart.container).append("svg")
.attr("width", line_chart.outer_width)
.attr("height", line_chart.outer_height)
.append("g")
.attr("transform", "translate(" + line_chart.margin.left + "," + line_chart.margin.top + ")");
// function that draws the lines.
line_chart.line = d3.svg.line()
.interpolate(line_chart.chart_options.interpolation)
.x(function(d){ return x(d[line_chart.domain_attr]); })
.y(function(d){ return y(d[line_chart.range_attr]); });
// function that returns unique color based on series_title.
line_chart.color = d3.scale.category20();
line_chart.defineAxes();
}
defineAxes(){
var line_chart = this;
@@ -72,7 +19,8 @@ class LineChart {
.range([line_chart.height, 0]);
line_chart.y_axis = d3.svg.axis()
.scale(line_chart.y_scale)
.orient("left");
.orient("left")
.outerTickSize(1);
if (line_chart.time_series){
line_chart.x_scale = d3.time.scale()
@@ -84,24 +32,39 @@ class LineChart {
line_chart.x_axis = d3.svg.axis()
.scale(line_chart.x_scale)
.orient("bottom");
.orient("bottom")
.outerTickSize(0)
//line_chart.x_axis.tickFormat(d3.time.format('%b %d at %H'))
//line_chart.x_axis.ticks(d3.time.hour, 12);
// append axes
line_chart.svg.append("g")
.attr("class", "d3-chart-range d3-chart-axis")
.attr("transform", "translate(0, " + (line_chart.height - line_chart.margin.top) + ")");
.attr("class", "d3-chart-range d3-chart-axis");
line_chart.svg.append("g")
.attr("class", "d3-chart-domain d3-chart-axis");
.attr("class", "d3-chart-domain d3-chart-axis")
.attr("transform", "translate(0, " + (line_chart.height) + ")");
}
afterAxes(){
var line_chart = this;
// function that draws the lines.
line_chart.line = d3.svg.line()
.interpolate(line_chart.chart_options.interpolation)
.x(function(d){ return line_chart.x_scale(d[line_chart.domain_attr]); })
.y(function(d){ return line_chart.y_scale(d[line_chart.range_attr]); });
// function that returns unique color based on series_title.
line_chart.color = d3.scale.category20();
}
serializeData(data){
var line_chart = this,
serialized_data = {
series: [],
range_min: -Infinity,
range_max: Infinity,
domain_min: -Infinity,
domain_max: Infinity,
range_min: Infinity,
range_max: -Infinity,
domain_min: Infinity,
domain_max: -Infinity,
};
data.forEach(function(data_set){
@@ -131,7 +94,7 @@ class LineChart {
bar_chart.svg.select(".d3-chart-range.d3-chart-axis")
.call(bar_chart.y_axis);
bar_chart.x_scale.domain([Math.min(data.domain_min), data.domain_max]);
bar_chart.x_scale.domain([data.domain_max, Math.min(data.domain_min)]);
bar_chart.svg.select(".d3-chart-domain.d3-chart-axis").call(bar_chart.x_axis);
// draw lines

View File

@@ -5,85 +5,78 @@ const INTERPOLATION = 'cardinal';
// inspired by https://bl.ocks.org/mbostock/3885211
class SplineStackChart extends LineChart {
init(){
var spline_stack = this;
spline_stack.svg = d3.select(spline_stack.container).append("svg")
.attr("width", spline_stack.outer_width)
.attr("height", spline_stack.outer_height)
.append("g")
.attr("transform", "translate(" + spline_stack.margin.left + "," + spline_stack.margin.top + ")");
spline_stack.area = d3.svg.area.interpolate(INTERPOLATION)
.x(function(d, i) { return spline_stack.x_scale(d.x); })
.y0(function(d) { return spline_stack.y_scale(d.y0); })
.y1(function(d) { return spline_stack.y_scale(d.y0 + d.y); });
spline_stack.stack = d3.layout.stack()
.values(function(d) { return d.values; });
// function that returns unique color based on series_title.
spline_stack.color = d3.scale.category20();
spline_stack.defineAxes();
get chart_options(){
return Object.assign(LineChart.DEFAULTS, {
interpolation: INTERPOLATION
});
}
get chart_options(){
return {
interpolation: INTERPOLATION
};
afterAxes(){
var spline_stack = this;
spline_stack.fnArea = d3.svg.area()
.x(function(d, i) { return spline_stack.x_scale(d.x); })
.y0(function(d) { return spline_stack.y_scale(d.y0); })
.y1(function(d) { return spline_stack.y_scale(d.y0 + d.y); })
.interpolate(spline_stack.interpolation);
spline_stack.fnStack = d3.layout.stack()
.values(function(d) { return d.values; });
// function that returns unique color based on series_title.
spline_stack.fnColor = d3.scale.category20();
}
serializeData(data){
var spline_stack = this,
serialized_data = {
range_min: -Infinity,
range_max: Infinity,
domain_min: -Infinity,
domain_max: Infinity };
series: [] };
data.series.forEach(function(series, i){
series.css_class = series.css_class || spline_stack.toClass ? spline_stack.toClass(series) : "";
series.title = series.title || spline_stack.toClass ? spline_stack.titleize(series) : "";
series.xy_values = series.values.map((value)=>{
series_data.range_min = Math.min(series_data.range_min, value[line_chart.range_attr]);
series_data.range_max = Math.max(series_data.range_max, value[line_chart.range_attr]);
series_data.domain_min = Math.min(series_data.domain_min, value[line_chart.domain_attr]);
series_data.domain_max = Math.max(series_data.domain_max, value[line_chart.domain_attr]);
return {x: value[spline_stack.domain_attr], y: value[spline_stack.range_attr]};
});
if (spline_stack.domain_attr !== 'x' && spline_stack.range_attr !== 'y'){
series.values = series.values.map((value)=>{
return {x: value[spline_stack.domain_attr], y: value[spline_stack.range_attr], series: series};
});
}
serialized_data.series.push(series);
});
serialized_data.series = spline_stack.fnStack(serialized_data.series);
// assume all series have same domain, use first series to establish extent.
serialized_data.domain_extent = d3.extent(serialized_data.series[0].values.map((value)=>{ return value.x; }));
// final series will have the highest y values.
serialized_data.range_max = d3.max(serialized_data.series[serialized_data.series.length - 1].values.map((value)=>{ return value.y0 + value.y; }))
return serialized_data;
};
drawData(data){
var spline_stack = this;
data = spline_stack.serialize_data;
data = spline_stack.serializeData(data);
// calibrate axes.
bar_chart.y_scale.domain([Math.min(0, data.range_min), data.range_max]);
bar_chart.svg.select(".d3-chart-range.d3-chart-axis")
.call(bar_chart.y_axis);
spline_stack.y_scale.domain([0, data.range_max]);
spline_stack.svg.select(".d3-chart-range.d3-chart-axis")
.call(spline_stack.y_axis);
bar_chart.x_scale.domain([Math.min(data.domain_min), data.domain_max]);
bar_chart.svg.select(".d3-chart-domain.d3-chart-axis").call(bar_chart.x_axis);
spline_stack.x_scale.domain(data.domain_extent);
spline_stack.svg.select(".d3-chart-domain.d3-chart-axis").call(spline_stack.x_axis);
var stack_data = spline_stack.stack(data.series),
stack = svg.selectAll(".d3-chart-spline-stack")
.data(stack_data);
[stack.enter().append("g"), stack.transition()].forEach((groups)=>{
applyData(groups);
var stack = spline_stack.svg.selectAll(".d3-chart-spline-stack")
.data(data.series);
[stack.enter().append("path"), stack.transition()].forEach((paths)=>{
spline_stack.applyData(paths);
});
stack.exit().remove();
}
applyData(groups){
applyData(paths){
var spline_stack = this;
groups
paths
.attr("class", function(series){"d3-chart-spline-stack " + series.css_class;})
.append("path")
.attr("d", function(series){ return spline_stack.area(series.xy_values); })
.style("fill", function(series){ return spline_stack.color(series.name); });
.attr("d", function(series){ return spline_stack.fnArea(series.values); })
.style("fill", function(series){ return spline_stack.fnColor(series.title); });
}
}
export default SplineStackChart;