block by emeeks 280cb0607c68faf30bb5

d3.layout.timeline with dates

Full Screen

An example of d3.layout.timeline that shows wars the United States has been involved in during its first 100 years (from Wikipedia). Entries are formatted like:

name,start,end,sphere
"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"

By default, d3.layout.timeline expects dates and start and end as the notation for the location of the start and end data, so you can see there aren’t many settings for the layout.

index.html

<html xmlns="//www.w3.org/1999/xhtml">
<head>
  <title>Timeline Using Dates</title>
  <meta charset="utf-8" />
    <style type="text/css">
      svg {
        height: 1100px;
        width: 1100px;
      }
      div.viz {
        height: 1000px;
        width: 1000px;
      }
      </style>
<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>
</head>
<body>
<div id="viz">
  <svg style="background:white;" height=1100 width=1100>
  </svg>
</div>
<footer>
</footer>
<script>

var timeline = d3.layout.timeline()
  .size([1000,300]);

colorScale = d3.scale.ordinal()
  .domain(["European","Native","Colonial","Latin America","Internal"])
  .range(["#96abb1", "#313746", "#b0909d", "#687a97", "#292014"]);

d3.csv("wars.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", 30)
  .attr("width", function (d) {return d.end - d.start})
  .style("fill", function (d) {return colorScale(d.sphere)})
  .style("stroke", "black")
  .style("stroke-width", 1)
})

</script>
</body>
</html>

d3.layout.timeline.js

(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;
}
})();

wars.csv

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"