Skip to content

Instantly share code, notes, and snippets.

@kylelk
Forked from tsenga/README.markdown
Created January 27, 2014 06:41
Show Gist options
  • Save kylelk/8644091 to your computer and use it in GitHub Desktop.
Save kylelk/8644091 to your computer and use it in GitHub Desktop.

An experiment within the Visualisation Exploration series.

Exploring, techniques and practices in the field of data visualisation.

Key history of this experiment:

  • D3, the data driven visualisation enabler.
  • Initial prototype written in handcrafted SVG with handcrafted data
  • Creation of sample .json dataset
  • Migration to systematic data treatment using D3
  • Conversion to reusable charts convention

Possible future directions:

  • Pulling out style treatment into configuration and .css
  • Enabling configuration of left and right gutter spacing
  • Distance between events proportional to time between events
  • Auto-sizing of event boxes, depending on data to be presented
  • Auto-packing of event boxes, to maximise space
d3.json("./data/history.json", function(json) {
if (json === null) return; // parse problem, nothing to do here
// setup data for chart
json.events.forEach(function(p, i) {
p.date = +p.date; // coerce into right type
});
json.events.sort(function(a,b) { return a.date < b.date ? -1 : a.date > b.date ? 1 : 0; });
// instantiate the chart
var chart = timelineChart();
chart.title(function(d) { return d.name; }) // accessor for event title
.date(function(d) { return d.date; }) // accessor for event date
.details(function(d) { return d.party; })
.width(600); // width of chart
// join and render
d3.select("#chart").datum(json.events).call(chart);
});
{
"events" : [
{ "date" : "2010", "name" : "David Cameron", "party" : "Conservative" },
{ "date" : "2007", "name" : "Gordon Brown", "party" : "Labour" },
{ "date" : "1997", "name" : "Tony Blair", "party" : "Labour" },
{ "date" : "1990", "name" : "John Major", "party" : "Conservative" },
{ "date" : "1979", "name" : "Margaret Thatcher", "party" : "Conservative" },
{ "date" : "1976", "name" : "James Callaghan", "party" : "Labour" },
{ "date" : "1974", "name" : "Harold Wilson", "party" : "Labour" },
{ "date" : "1970", "name" : "Edward Heath", "party" : "Conservative" }
]
}
<HTML>
<head>
<title>D3 Timeline demonstration</title>
<script src="http://d3js.org/d3.v2.min.js"></script>
<script src="timeline.js"></script>
</head>
<body>
<h1>Vis Exploration - D3 - Timeline</h1>
<div id="chart"></div>
</body>
<script src="example.js"></script>
</HTML>
function timelineChart() {
var _title = function(value) { return value; };
var _date = function(value) { return value; };
var _details = null;
var _entryHeight = 50; // spacing between each entry
var _entryGap = 10; // gap above the start of each entry
var _width = 0; // default is set later
// left & right margins of each column, including the middle line (midMargin)
// all derived from _width - see my.width() below
var _midMargin = 0;
var _leftColMarginL = 0, _leftColMarginR = 0;
var _rightColMarginL = 0, _rightColMarginR = 0;
function my(selection) {
selection.each(function(d, i) {
// generate chart here; 'd' is the data and 'this' is the element
// establish base SVG frame
var svgBase = d3.select(this).append("svg:svg")
.attr("width", _rightColMarginR + 5)
.attr("height", (d.length + 1.5) * _entryHeight);
// draw mid-line - use number of events to determine length of the line
svgBase.append("line")
.attr("x1", _midMargin)
.attr("y1", 0)
.attr("x2", _midMargin)
.attr("y2", (d.length + 1.5) * _entryHeight)
.attr("stroke", "#999999")
.attr("stroke-width", 5);
// now bind data and draw entries
var entryBase = svgBase.selectAll(".entry")
.data(d)
.enter()
.append("g");
entryBase.append("circle")
.attr("cx", _midMargin)
.attr("cy", function(d, i) { return i * _entryHeight + _entryGap + 25; })
.attr("r", "5")
.attr("style", "fill:#999999; stroke:#ffffff; stroke-width:3")
// draw box around each event, factoring in left or right column-ness
entryBase.append("polygon")
.attr("points", function(d, i) {
var yTop = i * _entryHeight + _entryGap;
// polygon has notch on right or left depending on odd/even of index
return (i%2 == 0) ?
_leftColMarginL + "," + yTop
+ " " + _leftColMarginR + "," + yTop
+ " " + _leftColMarginR + "," + (yTop + 20)
+ " " + (_leftColMarginR+5) + "," + (yTop + 25)
+ " " + _leftColMarginR + "," + (yTop + 30)
+ " " + _leftColMarginR + "," + (yTop + 85)
+ " " + _leftColMarginL + "," + (yTop + 85)
:
_rightColMarginR + "," + yTop
+ " " + _rightColMarginL + "," + yTop
+ " " + _rightColMarginL + "," + (yTop + 20)
+ " " + (_rightColMarginL-5) + "," + (yTop + 25)
+ " " + _rightColMarginL + "," + (yTop + 30)
+ " " + _rightColMarginL + "," + (yTop + 85)
+ " " + _rightColMarginR + "," + (yTop + 85);
})
.attr("style", "fill:#eeeeee; stroke:#999999; stroke-width:1")
.on("mouseover", function() {d3.select(this).style("fill", "aliceblue").style("stroke", "#0000ff");})
.on("mouseout", function() {d3.select(this).style("fill", "#eeeeee").style("stroke", "#999999"); });
var textBase = entryBase.append("text")
// text is written in to left or right column, depending on odd/even of index
.attr("x", function(d,i) { return ((i%2 == 0) ? _leftColMarginL : _rightColMarginL) + 4; })
// set height & style of text block
.attr("y", function(d, i) { return i * _entryHeight + _entryGap + 14; })
.attr("style", "font-size: 12px; font-family: Arial");
// lay out text block - date followed by event title
textBase.append("tspan")
.attr("style", "stroke-width:1; stroke:#000000; kerning:1.2")
.text(function(d) { return _date(d); })
textBase.append("tspan")
.attr("x", function(d, i) { return ((i%2 == 0) ? _leftColMarginL : _rightColMarginL) + 4; })
.attr("dy", 14)
.text(function(d) { return _title(d); } );
if (_details)
textBase.append("tspan")
.attr("x", function(d, i) { return ((i%2 == 0) ? _leftColMarginL : _rightColMarginL) + 4; })
.attr("dy", 14)
.text(function(d) { return _details(d); } );
});
}
my.width = function(value) {
if (!arguments.length) return _width;
_width = value;
_midMargin = _width/2;
_leftColMarginR = _midMargin - 13;
_rightColMarginL = _midMargin + 13;
_leftColMarginL = 5;
_rightColMarginR = _width - 5;
return my;
}
// configuration accessors and setters
my.entryHeight = function(value) {
if (!arguments.length) return _entryHeight;
_entryHeight = value;
return my;
}
my.entryGap = function(value) {
if (!arguments.length) return _entryGap;
_entryGap = value;
return my;
}
my.details = function(value) {
if (!arguments.length) return _details;
_details = value;
return my;
}
my.title = function(value) {
if (!arguments.length) return _title;
_title = value;
return my;
}
my.date = function(value) {
if (!arguments.length) return _date;
_date = value;
return my;
}
my.width(600);
return my;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment