A very simple example of d3.layout.timeline that takes a csv like this:
s,e
1,7
1,6
10,15
7,8
4,5
5,6
And places it into a few lanes to be drawn on-screen.
A very simple example of d3.layout.timeline that takes a csv like this:
s,e
1,7
1,6
10,15
7,8
4,5
5,6
And places it into a few lanes to be drawn on-screen.
| (function() { | |
| d3.layout.timeline = function() { | |
| var timelines = []; | |
| var dateAccessor = function (d) {return new Date(d)}; | |
| var processedTimelines = []; | |
| var startAccessor = function (d) {return d.start}; | |
| var endAccessor = function (d) {return d.end}; | |
| var size = [500,100]; | |
| var timelineExtent = [-Infinity, Infinity]; | |
| var setExtent = []; | |
| var displayScale = d3.scale.linear(); | |
| var swimlanes = []; | |
| var padding = 0; | |
| var fixedExtent = false; | |
| var maximumHeight = Infinity; | |
| function processTimelines() { | |
| timelines.forEach(function (band) { | |
| var projectedBand = {}; | |
| for (var x in band) { | |
| if (band.hasOwnProperty(x)) { | |
| projectedBand[x] = band[x]; | |
| } | |
| } | |
| projectedBand.start = dateAccessor(startAccessor(band)); | |
| projectedBand.end = dateAccessor(endAccessor(band)); | |
| projectedBand.lane = 0; | |
| processedTimelines.push(projectedBand); | |
| }); | |
| } | |
| function projectTimelines() { | |
| if (fixedExtent === false) { | |
| var minStart = d3.min(processedTimelines, function (d) {return d.start}); | |
| var maxEnd = d3.max(processedTimelines, function (d) {return d.end}); | |
| timelineExtent = [minStart,maxEnd]; | |
| } | |
| else { | |
| timelineExtent = [dateAccessor(setExtent[0]), dateAccessor(setExtent[1])]; | |
| } | |
| displayScale.domain(timelineExtent).range([0,size[0]]); | |
| processedTimelines.forEach(function (band) { | |
| band.originalStart = band.start; | |
| band.originalEnd = band.end; | |
| band.start = displayScale(band.start); | |
| band.end = displayScale(band.end); | |
| }); | |
| } | |
| function fitsIn(lane, band) { | |
| if (lane.end < band.start || lane.start > band.end) { | |
| return true; | |
| } | |
| var filteredLane = lane.filter(function (d) {return d.start <= band.end && d.end >= band.start}); | |
| if (filteredLane.length === 0) { | |
| return true; | |
| } | |
| return false; | |
| } | |
| function findlane(band) { | |
| //make the first array | |
| if (swimlanes[0] === undefined) { | |
| swimlanes[0] = [band]; | |
| return; | |
| } | |
| var l = swimlanes.length - 1; | |
| var x = 0; | |
| while (x <= l) { | |
| if (fitsIn(swimlanes[x], band)) { | |
| swimlanes[x].push(band); | |
| return; | |
| } | |
| x++; | |
| } | |
| swimlanes[x] = [band]; | |
| return; | |
| } | |
| function timeline(data) { | |
| if (!arguments.length) return timeline; | |
| timelines = data; | |
| processedTimelines = []; | |
| swimlanes = []; | |
| processTimelines(); | |
| projectTimelines(); | |
| processedTimelines.forEach(function (band) { | |
| findlane(band); | |
| }); | |
| var height = size[1] / swimlanes.length; | |
| height = Math.min(height, maximumHeight); | |
| swimlanes.forEach(function (lane, i) { | |
| lane.forEach(function (band) { | |
| band.y = i * (height); | |
| band.dy = height - padding; | |
| band.lane = i; | |
| }); | |
| }); | |
| return processedTimelines; | |
| } | |
| timeline.dateFormat = function (_x) { | |
| if (!arguments.length) return dateAccessor; | |
| dateAccessor = _x; | |
| return timeline; | |
| } | |
| timeline.bandStart = function (_x) { | |
| if (!arguments.length) return startAccessor; | |
| startAccessor = _x; | |
| return timeline; | |
| } | |
| timeline.bandEnd = function (_x) { | |
| if (!arguments.length) return endAccessor; | |
| endAccessor = _x; | |
| return timeline; | |
| } | |
| timeline.size = function (_x) { | |
| if (!arguments.length) return size; | |
| size = _x; | |
| return timeline; | |
| } | |
| timeline.padding = function (_x) { | |
| if (!arguments.length) return padding; | |
| padding = _x; | |
| return timeline; | |
| } | |
| timeline.extent = function (_x) { | |
| if (!arguments.length) return timelineExtent; | |
| fixedExtent = true; | |
| setExtent = _x; | |
| if (_x.length === 0) { | |
| fixedExtent = false; | |
| } | |
| return timeline; | |
| } | |
| timeline.maxBandHeight = function (_x) { | |
| if (!arguments.length) return maximumHeight; | |
| maximumHeight = _x; | |
| return timeline; | |
| } | |
| return timeline; | |
| } | |
| })(); |
| <html xmlns="http://www.w3.org/1999/xhtml"> | |
| <head> | |
| <title>Timeline with Integers</title> | |
| <meta charset="utf-8" /> | |
| <style type="text/css"> | |
| svg { | |
| height: 1100px; | |
| width: 1100px; | |
| } | |
| div.viz { | |
| height: 1000px; | |
| width: 1000px; | |
| } | |
| </style> | |
| </head> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.16/d3.min.js" charset="utf-8" type="text/javascript"></script> | |
| <script src="d3.layout.timeline.js" charset="utf-8" type="text/javascript"></script> | |
| <script> | |
| var timeline = d3.layout.timeline() | |
| .size([1000,300]) | |
| .bandStart(function (d) {return d.s}) | |
| .bandEnd(function (d) {return d.e}) | |
| .dateFormat(function (d) {return parseInt(d)}) | |
| d3.csv("int_bands.csv", function (csv) { | |
| timelineBands = timeline(csv); | |
| d3.select("svg").selectAll("rect") | |
| .data(timelineBands) | |
| .enter() | |
| .append("rect") | |
| .attr("x", function (d) {return d.start}) | |
| .attr("y", function (d) {return d.y}) | |
| .attr("height", function (d) {return d.dy}) | |
| .attr("width", function (d) {return d.end - d.start}) | |
| .style("fill", "#687a97") | |
| .style("stroke", "black") | |
| }) | |
| </script> | |
| <body> | |
| <div id="viz"> | |
| <svg style="background:white;" height=1100 width=1100> | |
| </svg> | |
| </div> | |
| <footer> | |
| </footer> | |
| </body> | |
| </html> |
| s | e | |
|---|---|---|
| 1 | 7 | |
| 1 | 6 | |
| 10 | 15 | |
| 7 | 8 | |
| 4 | 5 | |
| 5 | 6 | |
| 8 | 9 | |
| 3 | 10 | |
| 10 | 15 | |
| 4 | 8 | |
| 10 | 12 | |
| 3 | 8 | |
| 5 | 12 | |
| 4 | 12 |