Skip to content

Instantly share code, notes, and snippets.

@emeeks
Last active June 19, 2023 20:42
Show Gist options
  • Save emeeks/3184af35f4937d878ac0 to your computer and use it in GitHub Desktop.
Save emeeks/3184af35f4937d878ac0 to your computer and use it in GitHub Desktop.
d3.layout.timeline categorized timelines

An example of d3.layout.timeline that shows wars the United States has been involved in during its first 100 years (from Wikipedia). Events have been categorized and rendered in separate lanes for colonial wars, native wars, internal wars, wars involving European powers, and wars in Latin America.

(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>Categorized Timeline Using Dates</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>
types = ["European","Native","Colonial","Latin America","Internal"];
colorScale = d3.scale.ordinal()
.domain(types)
.range(["#96abb1", "#313746", "#b0909d", "#687a97", "#292014"]);
d3.csv("wars.csv", function (csv) {
var timeline = d3.layout.timeline()
.size([500,80])
.extent(["7/4/1776", "12/31/1876"])
.padding(3)
.maxBandHeight(20);
types.forEach(function (type, i) {
onlyThisType = csv.filter(function(d) {return d.sphere === type});
theseBands = timeline(onlyThisType);
d3.select("svg").append("g")
.attr("transform", "translate(100," + (35 + (i * 90)) + ")")
.selectAll("rect")
.data(theseBands)
.enter()
.append("rect")
.attr("rx", 2)
.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", function (d) {return colorScale(d.sphere)})
.style("stroke", "black")
.style("stroke-width", 1);
d3.select("svg").append("text")
.text(type)
.attr("y", 50 + (i * 90))
.attr("x", 20)
})
})
</script>
<body>
<div id="viz">
<svg style="background:white;" height=1100 width=1100>
</svg>
</div>
<footer>
</footer>
</body>
</html>
name start end sphere
American Revolutionary War 4/19/1775 9/3/1783 European
Cherokee–American wars 01/01/1776 12/31/1795 Native
Northwest Indian War 01/01/1785 12/31/1795 Native
Whiskey Rebellion 01/01/1795 12/31/1795 Internal
Quasi-War 01/01/1798 12/31/1800 European
First Barbary War 5/10/1801 6/10/1805 Colonial
Tecumseh's War 01/01/1811 12/31/1811 Native
War of 1812 6/18/1812 2/18/1815 European
Red Stick War 01/01/1813 8/31/1814 Native
Second Barbary War 06/17/1815 06/19/1815 Colonial
First Seminole War 01/01/1817 12/31/1818 Native
Texas/Indian Wars 01/01/1820 12/31/1875 Native
Arikara War 01/01/1823 12/31/1823 Native
Aegean Sea Anti/Piracy Operations 01/01/1825 12/31/1828 Colonial
Winnebago War 01/01/1827 12/31/1827 Native
First Sumatran expedition 01/01/1832 12/31/1832 Colonial
Black Hawk War 01/01/1832 12/31/1832 Native
Second Seminole War 01/01/1835 12/31/1842 Native
Patriot War 01/01/1838 12/31/1838 European
United States Exploring Expedition 01/01/1838 12/31/1842 Colonial
Second Sumatran expedition 01/01/1838 12/31/1838 Colonial
Mexican–American War 01/01/1846 12/31/1848 Latin America
Cayuse War 01/01/1847 12/31/1855 Native
Taiping Rebellion 01/01/1850 12/31/1864 Colonial
Apache Wars 01/01/1851 12/31/1900 Native
Bombardment of Greytown 01/01/1854 12/31/1854 European
Puget Sound War 01/01/1855 12/31/1856 Native
First Fiji Expedition 01/01/1855 12/31/1855 Colonial
Rogue River Wars 01/01/1855 12/31/1856 Native
Third Seminole War 01/01/1855 12/31/1858 Native
Yakima War 01/01/1855 12/31/1858 Native
Filibuster War 01/01/1856 12/31/1857 Latin America
Second Opium War 01/01/1856 12/31/1859 Colonial
Utah War 01/01/1857 12/31/1858 Internal
Navajo Wars 01/01/1858 12/31/1866 Native
Second Fiji Expedition 01/01/1858 12/31/1858 Colonial
First and Second Cortina War 01/01/1859 12/31/1861 Latin America
Paiute War 01/01/1860 12/31/1860 Native
Reform War 01/01/1860 12/31/1860 Latin America
American Civil War 01/01/1861 12/31/1865 Internal
Bombardment of Qui Nhơn 01/01/1861 12/31/1861 Colonial
Yavapai Wars 01/01/1861 12/31/1875 Native
Dakota War of 1862 01/01/1862 12/31/1862 Native
Colorado War 01/01/1863 12/31/1865 Native
Shimonoseki War 01/01/1863 12/31/1864 Colonial
Snake War 01/01/1864 12/31/1868 Native
Powder River War 01/01/1865 12/31/1865 Native
Red Cloud's War 01/01/1866 12/31/1868 Native
Siege of Mexico City 01/01/1867 12/31/1867 Latin America
Formosa Expedition 01/01/1867 12/31/1867 Colonial
Comanche Campaign 01/01/1867 12/31/1875 Native
United States expedition to Korea 01/01/1871 12/31/1871 Colonial
Modoc War 01/01/1872 12/31/1873 Native
Red River War 01/01/1874 12/31/1875 Native
Las Cuevas War 01/01/1875 12/31/1875 Latin America
Great Sioux War of 1876 01/01/1876 12/31/1877 Native
Buffalo Hunters' War 01/01/1876 12/31/1877 Native
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment