From 715feda0510c262c4bc150aeda06fe37c74a9134 Mon Sep 17 00:00:00 2001 From: Eric Hulburd Date: Thu, 11 Feb 2016 19:52:12 -0600 Subject: [PATCH] create spline stack --- client/d3/bar.d3.js | 1 - client/d3/bar/base.js | 86 ++++++++++++++++++ client/d3/bar/horizontal.js | 76 ++++++++++++++++ client/d3/bar/vertical.js | 76 ++++++++++++++++ client/d3/line/line.js | 153 +++++++++++++++++++++++++++++++++ client/d3/line/spline.js | 6 ++ client/d3/line/spline_stack.js | 85 ++++++++++++++++++ 7 files changed, 482 insertions(+), 1 deletion(-) delete mode 100644 client/d3/bar.d3.js create mode 100644 client/d3/bar/base.js create mode 100644 client/d3/bar/horizontal.js create mode 100644 client/d3/bar/vertical.js create mode 100644 client/d3/line/line.js create mode 100644 client/d3/line/spline.js create mode 100644 client/d3/line/spline_stack.js diff --git a/client/d3/bar.d3.js b/client/d3/bar.d3.js deleted file mode 100644 index ed39498..0000000 --- a/client/d3/bar.d3.js +++ /dev/null @@ -1 +0,0 @@ -bar.d3.js diff --git a/client/d3/bar/base.js b/client/d3/bar/base.js new file mode 100644 index 0000000..8e356ad --- /dev/null +++ b/client/d3/bar/base.js @@ -0,0 +1,86 @@ +import extend from 'extend'; + +// This class is inspired by // http://bl.ocks.org/mbostock/3885304. + +const DEFAULTS = { + outer_width: 500, + outer_height: 300, + margin: {top: 0, left: 70, bottom: 50, right: 20}, + horizontal: true, + range_ticks: 10, + container: "container", + range_label: "range", + titleize: function(series, datum){ + var s = datum ? datum.name : series.name, + words = s.split(' '); + var array = []; + for (var i=0; i{ return d.name; })); + bar_chart.svg.select(".d3-chart-domain.d3-chart-axis") + .call(bar_chart.x_axis) + .selectAll("text") + .attr("transform", function(){ + var elem = this, + bbox = elem.getBBox(), + middleX = bbox.x + (bbox.width / 2), + middleY = bbox.y + (bbox.height / 2); + return "rotate(-30,"+middleX + "," + middleY+")"; + }); + + bar_chart.y_scale.domain([data.min, data.max]); + bar_chart.svg.select(".d3-chart-range.d3-chart-axis").call(bar_chart.y_axis); + + data.series.forEach(function(series){ + var bars = bar_chart.svg.selectAll(".d3-chart-rect.d3-chart-bar." + series.css_class) + .data(series.values); + bar_chart.applyData(series, bars.enter().append("rect")); + bar_chart.applyData(series, bars.transition()); + bars.exit().remove(); + }); + } + + // helper method for drawData + applyData(series, elements){ + var bar_chart = this, + series_class = "d3-chart-bar " + series.css_class; + elements + .attr("class", function(d){ return series_class + " " + d.css_class; }) + .attr("title", function(d){ return d.title; }) + .attr("width", function(d) { return bar_chart.x_scale.rangeBand(); }) + .attr("x", function(d) { return bar_chart.x_scale(series.name); }) + .attr("height", return bar_chart.y_scale(d.value)) + .attr("y", function(d) { return bar_chart.y_scale(d.cummulative); }) + .attr("opacity", function(d) { return d.opacity; }); + } + +} + +export default VerticalBarChart; diff --git a/client/d3/line/line.js b/client/d3/line/line.js new file mode 100644 index 0000000..418a7d8 --- /dev/null +++ b/client/d3/line/line.js @@ -0,0 +1,153 @@ +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: + titleize: function(series, datum){ + var s = datum ? datum.name : series.name, + words = s.split(' '), + array = []; + for (var i=0; i{ + 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]); + }); + serialized_data.series.push(series); + }); + return serialized_data; + }; + + drawData(data){ + var line_chart = this; + data = line_chart.serialize_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); + + 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); + + // draw lines + var line = g.selectAll(".d3-chart-series") + .data(data.series); + + [line.enter().append('g'), line.transition()].forEach((groups)=>{ + line_chart.applyData(groups); + }); + line.exit().remove(); + } + + applyData(groups){ + var line_chart = this; + groups + .attr('class', function(series){ return "d3-chart-line " + series.css_class; ) + .attr("title", function(series){ return series.title; }) + .append("path") + .attr("d", function(series){ return line_chart.line(series.values); }) + .style("stroke", function(series){ return line_chart.color(series.title); }); + } + +} + +export default LineChart; diff --git a/client/d3/line/spline.js b/client/d3/line/spline.js new file mode 100644 index 0000000..e6dd7cb --- /dev/null +++ b/client/d3/line/spline.js @@ -0,0 +1,6 @@ +import LineChart from './line'; + +class SplineChart extends LineChart { + + +} diff --git a/client/d3/line/spline_stack.js b/client/d3/line/spline_stack.js new file mode 100644 index 0000000..4f2716b --- /dev/null +++ b/client/d3/line/spline_stack.js @@ -0,0 +1,85 @@ +import LineChart from './line'; + +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(); + } + + serializeData(data){ + var spline_stack = this, + serialized_data = { + range_min: -Infinity, + range_max: Infinity, + domain_min: -Infinity, + domain_max: Infinity + }; + + 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]}; + }); + serialized_data.series.push(series); + }); + return serialized_data; + }; + + drawData(data){ + var spline_stack = this; + data = spline_stack.serialize_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); + + 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); + + 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); + }); + stack.exit().remove(); + } + + applyData(groups){ + var spline_stack = this; + groups + .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); }); + } +}