Skip to content

Instantly share code, notes, and snippets.

@eesur
Created December 12, 2015 16:37
Show Gist options
  • Save eesur/a4679ee453aa9357977c to your computer and use it in GitHub Desktop.
Save eesur/a4679ee453aa9357977c to your computer and use it in GitHub Desktop.
d3 | Reusable slopegraph

Reusable slopegraph inspired/adapted from Ben Van Dyke's example into a reusable chart.

This slopegraph example shows the number of personal computers installed in a country per household. This includes desktop PCs and laptops, but excludes smartphones and terminals connected to mainframe computers. All figures are calculated using total number of Personal Computers and the Total Number of Households.

data source

// *****************************************
// reusable slopegraph chart
// *****************************************
(function() {
'use strict';
d3.eesur.slopegraph = function module() {
// input vars for getter setters
var w = 600,
h = 800,
margin = {top: 40, bottom: 40, left: 100, right: 100},
strokeColour = 'black',
// key data values start for left(axis) and end for right(axis)
keyValueStart = '',
keyValueEnd = '',
// key value (used for ref/titles)
keyName = '',
format = d3.format('');
var dispatch = d3.dispatch('_hover');
function exports(_selection) {
_selection.each(function(data) {
// format/clean data
data.forEach(function(d) {
d[keyValueStart] = +d[keyValueStart];
d[keyValueEnd] = +d[keyValueEnd];
});
// get max value(s) for y scale
var keyValueStartMax = d3.max(data, function (d) { return d[keyValueStart]; } );
var keyValueEndMax = d3.max(data, function (d) { return d[keyValueEnd]; } );
// use same scale for both sides
var yScale = d3.scale.linear()
.domain([0, d3.max([keyValueStartMax, keyValueEndMax])])
.range([h - margin.top, margin.bottom]);
var svg = d3.select(this).append('svg')
.attr({
width: w,
height: h
});
var lines = svg.selectAll('line')
.data(data);
lines.enter().append('line')
.attr({
x1: margin.left,
x2: w - margin.right,
y1: function(d) { return yScale(d[keyValueStart]); },
y2: function(d) { return yScale(d[keyValueEnd]); },
stroke: strokeColour,
'stroke-width': 1,
class: function (d, i) { return 's-line elm ' + 'sel-' + i; }
})
.on('mouseover', dispatch._hover);
var rightLabels = svg.selectAll('.labels')
.data(data);
rightLabels.enter().append('text')
.attr({
class: function (d, i) { return 'r-labels elm ' + 'sel-' + i; },
x: w - margin.right + 3,
y: function(d) { return yScale(d[keyValueEnd]) + 4; },
})
.text(function (d) {
return d[keyName] + ' ' + format(d[keyValueEnd]);
})
.style('text-anchor','start')
.on('mouseover', dispatch._hover);
var leftLabels = svg.selectAll('.left-labels')
.data(data);
leftLabels.enter().append('text')
.attr({
class: function (d, i) { return 'l-labels elm ' + 'sel-' + i; },
x: margin.left - 3,
y: function(d) { return yScale(d[keyValueStart]) + 4; }
})
.text(function (d) {
return d[keyName] + ' ' + format(d[keyValueStart]);
})
.style('text-anchor','end')
.on('mouseover', dispatch._hover);
var leftTitle = svg.append('text')
.attr({
class: 's-title',
x: margin.left - 3,
y: margin.top/2
})
.text(keyValueStart + ' ↓')
.style('text-anchor','end');
var rightTitle = svg.append('text')
.attr({
class: 's-title',
x: w - margin.right + 3,
y: margin.top/2
})
.text('↓ ' + keyValueEnd)
.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.strokeColour = function(value) {
if (!arguments.length) return strokeColour;
strokeColour = value;
return this;
};
exports.keyValueStart = function(value) {
if (!arguments.length) return keyValueStart;
keyValueStart = value;
return this;
};
exports.keyValueEnd = function(value) {
if (!arguments.length) return keyValueEnd;
keyValueEnd = value;
return this;
};
exports.keyName = function(value) {
if (!arguments.length) return keyName;
keyName = value;
return this;
};
exports.format = function(value) {
if (!arguments.length) return format;
format = value;
return this;
};
d3.rebind(exports, dispatch, 'on');
return exports;
};
}());
[
{
"2000": "1.56",
"2012": 3,
"country": "US"
},
{
"2000": 0.74,
"2012": 1.79,
"country": "Germany"
},
{
"2000": 0.75,
"2012": 2.22,
"country": "UK"
},
{
"2000": 0.09,
"2012": 0.67,
"country": "China"
},
{
"2000": 1.02,
"2012": 2.1,
"country": "Japan"
},
{
"2000": 0.02,
"2012": 0.44,
"country": "India"
},
{
"2000": 0.04,
"2012": 0.33,
"country": "Indonesia"
},
{
"2000": 0.26,
"2012": 1.57,
"country": "Mexico"
},
{
"2000": 0.02,
"2012": 0.15,
"country": "Kenya"
}
]
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>d3 | reusable slopegraph</title>
<meta name="author" content="Sundar Singh | eesur.com">
<link rel="stylesheet" href="main.css">
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
</head>
<body>
<header>
<h1>Reusable slopegraph</h1>
<p>Slopegraph example showing number of personal computers installed in a country per household.</p>
</header>
<section id="slopegraph"></section>
<footer>
<button id="reset">reset</button>
<nav id='nav-alt'></nav>
</footer>
<!-- namespace -->
<script> d3.eesur = {}; </script>
<!-- reusable slopegraph -->
<script src="d3_code_slopegraph.js"></script>
<script>
// render slopegraph chart
(function() {
// create chart
var slopegraph = d3.eesur.slopegraph()
// .margin({top: 20, bottom: 20, left: 100, right:100})
.strokeColour('#130C0E')
.keyName('country')
.keyValueStart('2000')
.keyValueEnd('2012')
.h(500)
// .format(d3.format('04d'))
.on('_hover', function (d, i) {
highlightLine(d, i);
});
d3.json('data.json', function(error, data) {
if (error) throw error;
// render chart
d3.select('#slopegraph')
.datum(data)
.call(slopegraph);
// alternative navigation
navAlt(data);
});
// reset button listener
d3.select('#reset')
.on('click', function () {
d3.selectAll('.elm').transition().style('opacity', 1);
d3.selectAll('.s-line').style('stroke', '#130C0E');
});
// navigation
function navAlt(data) {
d3.select('#nav-alt').append('ul')
.selectAll('li')
.data(data)
.enter().append('li')
.on('click', function (d, i) {
highlightLine(d, i);
})
.text(function (d) { return d['country']; });
}
// highlight line and fade other lines
function highlightLine(d, i) {
d3.selectAll('.elm').transition().style('opacity', 0.2);
d3.selectAll('.sel-' + i).transition().style('opacity', 1);
d3.selectAll('.s-line').style('stroke', '#FDBB30');
d3.selectAll('.s-line.sel-' + i).style('stroke', '#130C0E');
}
// just for blocks viewer size
d3.select(self.frameElement).style('height', '800px');
}());
</script>
</body>
</html>
@import url(http://fonts.googleapis.com/css?family=Source+Code+Pro:400,600);
body {
position: relative;
color: #130C0E;
background-color: #eee;
padding: 20px;
font-family: "Source Code Pro", Consolas, monaco, monospace;
line-height: 1.5;
font-weight: 400;
width: 600px;
margin: 0 auto;
}
p {
padding-top: 0;
margin-top: 0;
font-size: 13px;
max-width: 400px;
}
h1 {
font-size: 18px;
font-weight: 400;
}
#slopegraph {
min-height: 400px;
padding: 20px 0;
}
footer {
position: relative;
}
.l-labels, .r-labels {
font-size: 11px;
}
#reset {
font-size: 11px;
background: #130C0E;
color: #7AC143;
position: absolute;
border: none;
outline:none;
padding: 5px 8px;
letter-spacing: 1px;
left: 0;
top: 2px;
}
#reset:hover {
background: #7AC143;
color: #130C0E;
}
#nav-alt ul {
list-style: none;
margin-left: 15px;
}
#nav-alt ul li {
display: inline-block;
padding: 4px 8px;
background: #FDBB30;
cursor: pointer;
font-size: 11px;
margin-right: 1px;
}
#nav-alt ul li:hover {
background: #7AC143;
}
text.s-title {
fill: #130C0E;
letter-spacing: 2px;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment