// ***************************************** // reusable multiple slopegraph chart // ***************************************** (function() { 'use strict'; d3.eesur.slopegraph_v2 = function module() { // input vars for getter setters var w = 200, // width of the set h = 600, margin = {top: 40, bottom: 40, left: 80, right: 80}, gutter = 50, strokeColour = 'black', // key data values (in order) keyValues = [], // key value (used for ref/titles) keyName = '', format = d3.format(''), sets; var dispatch = d3.dispatch('_hover'); var svg, yScale; function exports(_selection) { _selection.each(function(data) { var allValues = [], maxValue; // format/clean data data.forEach(function(d) { _.times(keyValues.length, function (n) { d[keyValues[n]] = +d[keyValues[n]]; allValues.push(d[keyValues[n]]); }); }); // create max value so scale is consistent maxValue = _.max(allValues); // adapt the size against number of sets w = w * keyValues.length; // have reference for number of sets sets = keyValues.length -1; // use same scale for both sides yScale = d3.scale.linear() .domain([0, maxValue]) .range([h - margin.top, margin.bottom]); // clean start d3.select(this).select('svg').remove(); svg = d3.select(this).append('svg') .attr({ width: w, height: h }); render(data, 0); }); } // recursive function to apply each set // then the start and end labels (as only needed once) function render (data, n) { if (n < keyValues.length-1 ) { lines(data, n); middleLabels(data, n); return render(data, n+1); } else { startLabels(data); endLabels(data); return n; } } // render connecting lines function lines(data, n) { var lines = svg.selectAll('.s-line-' + n) .data(data); lines.enter().append('line'); lines.attr({ x1: function () { if (n === 0) { return margin.left; } else { return ((w / sets) * n) + margin.left/2; } }, y1: function(d) { return yScale(d[keyValues[n]]); }, x2: function () { if (n === sets-1) { return w - margin.right; } else { return ((w / sets) * (n+1)) - gutter; } }, y2: function(d) { return yScale(d[keyValues[n+1]]); }, stroke: strokeColour, 'stroke-width': 1, class: function (d, i) { return 'elm s-line-' + n + ' sel-' + i; } }) .on('mouseover', dispatch._hover); // lines.exit().remove(); } // middle labels in-between sets function middleLabels(data, n) { if (n !== sets-1) { var middleLabels = svg.selectAll('.m-labels-' + n) .data(data); middleLabels.enter().append('text') .attr({ class: function (d, i) { return 'labels m-labels-' + n + ' elm ' + 'sel-' + i; }, x: ((w / sets) * (n+1)) + 15, y: function(d) { return yScale(d[keyValues[n+1]]) + 4; }, }) .text(function (d) { return format(d[keyValues[n+1]]); }) .style('text-anchor','end') .on('mouseover', dispatch._hover); // title svg.append('text') .attr({ class: 's-title', x: ((w / sets) * (n+1)), y: margin.top/2 }) .text(keyValues[n+1] + ' ↓') .style('text-anchor','end'); } } // start labels applied left of chart sets function startLabels(data) { var startLabels = svg.selectAll('.l-labels') .data(data); startLabels.enter().append('text') .attr({ class: function (d, i) { return 'labels l-labels elm ' + 'sel-' + i; }, x: margin.left - 3, y: function(d) { return yScale(d[keyValues[0]]) + 4; } }) .text(function (d) { return d[keyName] + ' ' + format(d[keyValues[0]]); }) .style('text-anchor','end') .on('mouseover', dispatch._hover); // title svg.append('text') .attr({ class: 's-title', x: margin.left - 3, y: margin.top/2 }) .text(keyValues[0] + ' ↓') .style('text-anchor','end'); } // end labels applied right of chart sets function endLabels(data) { var i = keyValues.length-1; var endLabels = svg.selectAll('r.labels') .data(data); endLabels.enter().append('text') .attr({ class: function (d, i) { return 'labels r-labels elm ' + 'sel-' + i; }, x: w - margin.right + 3, y: function(d) { return yScale(d[keyValues[i]]) + 4; }, }) .text(function (d) { return d[keyName] + ' ' + format(d[keyValues[i]]); }) .style('text-anchor','start') .on('mouseover', dispatch._hover); // title svg.append('text') .attr({ class: 's-title', x: w - margin.right + 3, y: margin.top/2 }) .text('↓ ' + keyValues[i]) .style('text-anchor','start'); } // getter/setters for overrides exports.w = function(value) { if (!arguments.length) return w; w = value; return this; }; exports.h = function(value) { if (!arguments.length) return h; h = value; return this; }; exports.margin = function(value) { if (!arguments.length) return margin; margin = value; return this; }; exports.gutter = function(value) { if (!arguments.length) return gutter; gutter = value; return this; }; exports.format = function(value) { if (!arguments.length) return format; format = value; return this; }; exports.strokeColour = function(value) { if (!arguments.length) return strokeColour; strokeColour = value; return this; }; exports.keyValues = function(value) { if (!arguments.length) return keyValues; keyValues = value; return this; }; exports.keyName = function(value) { if (!arguments.length) return keyName; keyName = value; return this; }; d3.rebind(exports, dispatch, 'on'); return exports; }; }());