block by HarryStevens 6fb5a3c76246f542fecede554325cadf

Playfair Import-Export Chart

Full Screen

A line chart written in D3, based on William Playfair’s iconic import-export charts, using data from India’s Export Import Data Bank.

Libraries: d3.js, jQuery.

index.html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"//www.w3.org/TR/html4/loose.dtd">
<html xmlns="//www.w3.org/1999/xhtml">
	<head>
		<!-- meta -->
		<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
		<title></title>

		<!-- fonts -->
		<link href="https://fonts.googleapis.com/css?family=IM+Fell+English" rel="stylesheet">
		<link href="https://fonts.googleapis.com/css?family=IM+Fell+DW+Pica+SC" rel="stylesheet">

		<!-- css -->
		<link rel="stylesheet" href="styles.css"

	</head>
	<body>
		<div class="title">CHART of all the IMPORTS and EXPORTS to and from INDIA<br/>From the Year 1996 to 2015 by H. Stevens</div>
		<div class="chart"></div>
		<div class="title sub">The Divisions at the Bottom, expreſs <b>YEARS</b>, &amp; those on the Right hand, LAKHS of 2015 RUPEES</div>
		<div class="source left">A D3 chart based on <a href="https://commons.wikimedia.org/wiki/File:1786_Playfair_-_1_Chart_of_all_the_import_and_exports_to_and_from_England_from_the_year_1700_to_1782.jpg">the work of William Playfair</a><br />Source: <a href="//www.commerce.nic.in/eidb/">Export Import Data Bank, Indian Commerce Department</a></div>
		<div class="source right">Published as the Act directs. 28.<sup>th</sup> Aug.<sup>st</sup> 2016</div>



		<!-- js -->
		<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script>
		<script src="https://d3js.org/d3.v4.min.js"></script>
		<script src="scripts.js"></script>

	</body>
</html>

scripts.js

// a function to convert all the numbers into numbers
function types(d){
  // loop through all the years in the data
  for (var i = 1996;i<2016;i++){
    var name = 'y'+i+'to'+(i+1);
    d[name] = +(d[name]);
  }
  return d;
}

$(document).ready(function(){

  // define margins and whatnot
  var margin = {top: 0, right: 2, bottom: 30, left: 0},
      width = 780 - margin.left - margin.right,
      height = 390 - margin.top - margin.bottom;

  // time formatter
  var parseTime = d3.timeParse("%d-%b-%y");

  // x scale
  var x = d3.scaleTime()
      .range([0,width]);

  // y scale
  var y = d3.scaleLinear()
      .range([height,0]);

  // x axis
  var xAxis = d3.axisBottom().scale(x).tickSizeInner(-height).tickSizeOuter(0).tickPadding(8);

  // y axis
  var yAxis = d3.axisRight().scale(y).tickSizeInner(-width).tickSizeOuter(0).tickPadding(8);

  // line for exports
  var lineExport = d3.line()
    .x(function(d){ return x(d.date); })
    .y(function(d){ return y(d.export); });

  // line for imports
  var lineImport = d3.line()
    .x(function(d){ return x(d.date); })
    .y(function(d){ return y(d.import); });

  // areas
  var areaAboveExport = d3.area()
    .x(lineExport.x())
    .y0(lineExport.y())
    .y1(0);

  var areaBelowExport = d3.area()
    .x(lineExport.x())
    .y0(lineExport.y())
    .y1(height);

  var areaAboveImport = d3.area()
    .x(lineImport.x())
    .y0(lineImport.y())
    .y1(0);

  var areaBelowImport = d3.area()
    .x(lineImport.x())
    .y0(lineImport.y())
    .y1(height);


  // create an svg element, append it to the .chart div, and append a g to it
  var svg = d3.select('.chart').append('svg')
      .attr('width', width + margin.left + margin.right)
      .attr('height', height + margin.top + margin.bottom)
    .append('g')
      .attr('transform','translate(' + margin.left + ',' + margin.top + ')');

  d3.tsv('total_real.tsv', type, function(error,data){
    if (error) throw error;

    //extent of the x domain
    //the really enormous numbers are to add padding
    x.domain([(d3.min(data, function(d){ return d.date-8000000000; })),(d3.max(data, function(d){ return d.date-(-86890000000); }))]);

    // y domain. setting the max to export now, and adding some padding
    y.domain([0,(d3.max(data, function(d){ return d.import+d.import*.2; }))]);

    // append x axis to the svg element
    svg.append('g')
        .attr('class', 'x axis')
        .attr('transform', 'translate(0,' + height +')')
        .call(xAxis)
      .append('text')
        .attr('class','x axis label')
        .attr('x',width-margin.right)
        .attr('y',15)
        .style('text-anchor','end')
        .text('Years');

    // set the first year of the x axis a little to the left
    $('.x.axis g.tick:first-of-type text').attr('x',8.5);

    // append y axis to the svg element
    svg.append('g')
        .attr('class', 'y axis')
        .attr('transform','translate(' + width + ',0)')
        .call(yAxis)
      .append('text')
        .attr('x', -34)
        .attr('y', 6)
        .attr('dy', '.71em')
        .attr('class', 'y axis label')
        .style('text-anchor', 'end')
        .text('Lakhs');

    var bottomY = $('.y.axis .tick:first-of-type text').text();
    $('.y.axis .tick:first-of-type text').html(bottomY+' &#8377;')

    // y axis ticks placement
    $('.y.axis .tick text').attr('x',-60).attr('y',-6);

    // append a path, or a line, to the svg element, for Imports
    svg.append('defs').append('path')
        .datum(data)
        .attr('id', 'lineimports')
        .attr('class', 'line imports')
        .attr('transform','translate(0,-2)')
        .attr('d', lineImport);

    svg.append('text')
        .attr('class','line-text')
        .attr('id','imports-text')
        .attr('transform','translate(0,-5)')
        .attr('word-spacing',25)
      .append('textPath')
        .attr('xlink:href','#lineimports')
        .html('&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Line Representing Imports into India')

    svg.append('use')
        .attr('id', 'exports-line')
        .attr('xlink:href', '#lineimports');

    // append a line that's the same as imports, but just a single line
    svg.append('path')
        .datum(data)
        .attr('class', 'line-thin')
        .attr('d', lineImport);

    // append a path, or a line, to the svg element, for EXPORTS
    svg.append('defs').append('path')
        .datum(data)
        .attr('id', 'lineexports')
        .attr('class', 'line exports')
        .attr('transform','translate(0,2)')
        .attr('d', lineExport);

    svg.append('text')
        .attr('class','line-text')
        .attr('id','exports-text')
        .attr('transform','translate(0,12)')
        .attr('word-spacing',50)
      .append('textPath')
        .attr('xlink:href','#lineexports')
        .html('&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Line Representing Exports');

    svg.append('use')
        .attr('id', 'exports-line')
        .attr('xlink:href', '#lineexports');

    // append a line that's the same as exports, but just a single line
    svg.append('path')
        .datum(data)
        .attr('class', 'line-thin')
        .attr('d', lineExport);

    // define areas
    var defs = svg.append('defs');

    defs.append('clipPath')
        .attr('id','clip-import')
      .append('path')
        .datum(data)
        .attr('d',areaAboveImport);

    defs.append('clipPath')
        .attr('id','clip-export')
      .append('path')
        .datum(data)
        .attr('d',areaAboveExport);

    // IMPORT IS ABOVE EXPORT
    svg.append('path')
        .datum(data)
        .attr('d', areaBelowImport)
        .attr('class','surplus import')
        .attr('clip-path', 'url(#clip-export)')

    svg.append('text')
        .attr('class','line-text')
        .attr('id','area-text')
        .attr('transform','translate(0,30)')
        .attr('word-spacing',25)
      .append('textPath')
        .attr('xlink:href','#lineimports')
        .html('&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;BALANCE against INDIA')


    // EXPORT IS ABOVE IMPORT
    svg.append('path')
        .datum(data)
        .attr('class','surplus export')
        .attr('d', areaBelowExport)
        .attr('clip-path', 'url(#clip-import)');

    // place the shaded area below the axis lines
    $('svg g:first-of-type').before($('.import.surplus')).before($('.export.surplus'));
  });// end tsv

  // set the data types
  function type(d) {
    d.date = parseTime(d.date);
    d.export = +d.export;
    d.import = +d.import;
    return d;
  }

});// end document ready

styles.css

body {
  font-family: 'IM Fell English', serif;
  font-size: 16px;
  width: 780px;
  margin: 0 auto;
  display: table;
}

/*TITLES*/
.title {
  margin-top: 20px;
  width: 100%;
  text-align: center;
  font-style: italic;
}
.title.sub {
  margin-top: 0px;
}
.source {
  font-size: .8em;
  font-style: italic;
}
.source.left {
  float: left;
}
.source.right {
  float: right;
}

/*CHART*/
.chart {
  margin-top: 20px;
  border-top: 2px solid #000;
}

/*LINES*/
.line {
  fill: none;
  stroke-width: .2em;
}
.line-thin {
  fill: none;
  stroke-width: .05em;
  stroke: #000;
}
.line.exports {
  stroke: #b13737;
}
.line.imports {
  stroke: #e5bd10;
}
.line-text {
  font-size: .8em;
}
#exports-line {
  fill: none;
  stroke: #000;
}

/*AXES*/
.axis path,
.axis line {
  fill: none;
  stroke: #888;
  shape-rendering: crispEdges;
}
.axis path {
  stroke: #000;
  stroke-width: .2em;
}
.x.axis path {
  display: none;
}
.x.axis g.tick:first-of-type line,.y.axis g.tick:first-of-type line {
  stroke: #000;
  stroke-width:.2em;
}
.axis.label {
  fill: #000;
  font-size: 1em;
  font-family: 'IM Fell English', serif;
}
.tick text {
  font-family: 'IM Fell English', serif;
}

/*AREA*/
.surplus {
  fill-opacity: .5;
}
.surplus.import {
  fill: #b3e6ff;
}
.surplus.export {
  fill: #fdd8d6;
}
#area-text {
  font-family: 'IM Fell DW Pica SC', serif;
}

total_real.tsv

date	export	import
1-Apr-96	41823925.48	48899720.99
1-Apr-97	42661639.71	50878174.02
1-Apr-98	41227180.05	52607895.99
1-Apr-99	45155981.31	60994547.19
1-Apr-00	54557029.8	61873898.82
1-Apr-01	53299582.1	62525927.17
1-Apr-02	62763768.89	73112644
1-Apr-03	69527918.09	85108515.37
1-Apr-04	85577410.99	114242714.2
1-Apr-05	99499092.78	143969139.9
1-Apr-06	117214752.5	172303793.4
1-Apr-07	126581658.9	195376157.2
1-Apr-08	148813644.7	243275092.4
1-Apr-09	132748781.3	214106480.4
1-Apr-10	165996781.9	245786174.9
1-Apr-11	194972599.5	311946610.9
1-Apr-12	199386830.7	325637758.1
1-Apr-13	209551219.2	298697729.3
1-Apr-14	199116583.3	287394090.3
1-Apr-15	171461770.1	248800746.7