D3 Horizontal Bar Chart

In the horizontal bar, when creating rectangle band for each domain input, the x value for all rectangle is zero. As all the rectangle starting at same x that is zero with varying value in the y-axis.

When compare rectangle value between horizontal and vertical we can see in code below

Note: While applying attribute value for width and y-axis, we have to wrap these value with scale value of x and y.

Important function to note

    1. Scale value for x and y
      var x = d3.scaleLinear().rangeRound([0, width]);
      var y = d3.scaleBand().rangeRound([height, 0]).padding(0.2);
    2. Axix value for x and y
      svg.append("g")
      .attr("transform", "translate(0," + height + ")")
      .call(d3.axisBottom(x));
      
      svg.append("g")
      .call(d3.axisLeft(y));

Add the following code in horizontal-bar.html

<!DOCTYPE html>
<meta charset="utf-8">
<style> /* set the CSS */

.bar { fill: steelblue; }

</style>
<body>
	
<!-- load the d3.js library -->    	
<script src="//d3js.org/d3.v4.min.js"></script>
<script>
    //set up svg using margin conventions - we'll need plenty of room on the left for labels

var margin = {
    top: 15,
    right: 25,
    bottom: 30,
    left: 80
};

var width = 960 - margin.left - margin.right,
    height = 500 - margin.top - margin.bottom;

var x = d3.scaleLinear().rangeRound([0, width]);
var y = d3.scaleBand().rangeRound([height, 0]).padding(0.2);
    
    //Append the svg to body  
   
var svg = d3.select("body").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 + ")");
 
//Get or load the data 
d3.csv('sales.csv', function(err, data){
        if(err) throw err;

        //parse the data 

        data.forEach(function(d){
            d.sale = +d.sale;
        });
        
        x.domain([0, d3.max(data, function(d) { return d.sales; })]);
        y.domain(data.map(function(d) { return d.name; }));
        
        //Create or append rectangel for graph

      svg.selectAll(".bar")
          .data(data)
        .enter()
          .append("rect")
          .attr("class", "bar")
          .attr("x", 0)
          .attr("width", function(d) { return x(d.sales);})
          .attr("y", function(d) { return y(d.name); })
          .attr("height", y.bandwidth());

        
        // Add the x Axis

         svg.append("g")
            .attr("transform", "translate(0," + height + ")")
            .call(d3.axisBottom(x));

         svg.append("g")
            .call(d3.axisLeft(y));
    });
        
    </script>
</body>
</html>

 

Add the following code in sales.csv

name,sales
Sharma,33
Raj,12
Ajay,41
Arjun,16
Shihab,59
Royan,38
Max,21
Tenzin,25
Kumar,30
Lisa,47
Tom,5
Jerry,20
John,13
Tamo,29

 

 

Axes

In D3, it’s worth remembering that a scale is a function that maps an input range to an
output domain, whereas an axis is merely a visual representation of a scale. The axis component renders human-readable reference marks for scales. When creating a graph we have to make the line for x and y-axis, adding tick on the line and add the label text on the graph. All these activities are handled automatically by the axis generator in D3. We don’t have to build this tedious task from the scratch.

As we can see in above graph for making x-axis, the axis generator will create a line for x-axis through path function, axis generator will create one group where all the line or path, tick point, and text label are a group and all these are added to our SVG to display.

The D3 support four type of axis orientation.

  1. d3.axisTop:A horizontal axis with labels placed on the top of the axis.
  2. d3.axisBottom:A horizontal axis with labels placed on the bottom of the axis.
  3. d3.axisLeft:A Vertical axis with labels placed on the left-hand of the axis.
  4. d3.axisRight:A Vertical axis with labels placed on the right-hand of the axis.

Syntax:

var axis = d3.axisLeft(scale);
Where scale is domain and range mapping variable.

D3 Arc Generator for Pie and Donut Chart

The D3 path has lots of generators, the arc generator is most common generator beside the line.  The D3 arc generator is a lot more versatile than the simple SVG circle element.  The arc generator can create circle or pie, annulus or donut, circle sector and annulus sector.

The D3.arc can draw Circular or annular sectors, as in a pie or donut chart.

Syntax var arc2 = d3.arc()

The D3 arc has two optional fields: startingAngle and endAngle. When we have zero value for

var arc = d3.arc()
    .innerRadius(0)
    .outerRadius(100)
    .startAngle(0)
    .endAngle(Math.PI / 2);

If the arc generator has a context, then the arc is rendered to this context as a sequence of path method calls and this function returns void. Otherwise, a path data string is returned.

Here we will create pie and donut chart on Election result of BJP seat win.

In this example, we need our data in excel format. The data.csv contain the state and seat information as

state,count
Himachal,80
UP,80
Bihar,25
Goa,20
Punjab,30
Rajasthan,50

Step 1: Add the following code in index.html

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>Pie Charts</title>
<link rel="stylesheet" type="text/css" href="css/style.css">
        
 <script src="//d3js.org/d3.v4.min.js" charset="utf-8"></script>
        
    </head>
    <body>
        <h2>Pie Chart and Donut Chart on BJP Election</h2>
        
        <script type="text/javascript" src="js/pie.js"></script>
        
    </body>
    
</html

Step 2: Add the following code in style.css

.arc text {
  font: 10px sans-serif;
  text-anchor: middle;
}

.arc path {
  stroke: #fff;
}

.arc2 text {
  font: 10px sans-serif;
  text-anchor: middle;
}

.arc2 path {
  stroke: #fff;
}

 

Step 3: Add the following code in pie.js file

Abstract for what we are doing.

  1. First set the width and height for our SVG element.
  2.  Create two arcs, one for pie and one for a donut. The arc of innerRadius zero for pie chart.
  3. Generate pie chart and donut chart 
    // generate pie chart and donut chart
    var pie = d3.pie()
        .sort(null)
        .value(function(d) { return d.count; });
  4. Define two SVG elements to the body element.
  5. Import the data and
    d3.csv("data.csv", function(error, data) {    

    parse each element using data.forEach 

// margin
var margin = {top: 20, right: 20, bottom: 20, left: 20},
    width = 300 - margin.right - margin.left,
    height = 300 - margin.top - margin.bottom,
    radius = width/2;

// color range
var color = d3.scaleOrdinal()
    .range(["#BBDEFB", "#90CAF9", "#64B5F6", "#42A5F5", "#2196F3", "#1E88E5", "#1976D2"]);

// pie chart arc. Need to create arcs before generating pie
var arc = d3.arc()
    .outerRadius(radius - 10)
    .innerRadius(0);

// donut chart arc
var arc2 = d3.arc()
    .outerRadius(radius - 10)
    .innerRadius(radius - 70);


// arc for the labels position
var labelArc = d3.arc()
    .outerRadius(radius - 50)
    .innerRadius(radius - 50);

// arc for the labels position for %
var labelPer = d3.arc()
    .outerRadius(radius - 20)
    .innerRadius(radius - 20);


// generate pie chart and donut chart
var pie = d3.pie()
    .sort(null)
    .value(function(d) { return d.count; });

// define the svg for pie chart
var svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height)
  .append("g")
    .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");

// define the svg donut chart
var svg2 = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height)
  .append("g")
    .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");

// import data 
d3.csv("data.csv", function(error, data) {
  if (error) throw error;
    
    // parse data
    data.forEach(function(d) {
        d.count = +d.count;
        d.state = d.state;
    })

  // "g element is a container used to group other SVG elements"
  var g = svg.selectAll(".arc")
      .data(pie(data))
    .enter().append("g")
      .attr("class", "arc");

  // append path 
  g.append("path")
      .attr("d", arc)
      .style("fill", function(d) { return color(d.data.state); })
    // transition 
    .transition()
      .ease(d3.easeLinear)
      .duration(2000)
      .attrTween("d", tweenPie);
        
  // append text
  g.append("text")
    .transition()
      .ease(d3.easeLinear)
      .duration(2000)
    .attr("transform", function(d) { return "translate(" + labelArc.centroid(d) + ")"; })
      .attr("dy", ".35em")
      .text(function(d) { return d.data.state; });
    
      // append text
  g.append("text")
    .transition()
      .ease(d3.easeLinear)
      .duration(2000)
    .attr("transform", function(d) { return "translate(" + labelPer.centroid(d) + ")"; })
      .attr("dy", ".55em")
      .text(function(d) { return d.data.count + "%"; });
    

    // "g element is a container used to group other SVG elements"
  var g2 = svg2.selectAll(".arc2")
      .data(pie(data))
    .enter().append("g")
      .attr("class", "arc2");

   // append path 
  g2.append("path")
      .attr("d", arc2)
      .style("fill", function(d) { return color(d.data.state); })
    .transition()
      .ease(d3.easeLinear)
      .duration(2000)
      .attrTween("d", tweenDonut);
        
   // append text
  g2.append("text")
    .transition()
      .ease(d3.easeLinear)
      .duration(2000)
    .attr("transform", function(d) { return "translate(" + labelArc.centroid(d) + ")"; })
      .attr("dy", ".35em")
      .text(function(d) { return d.data.state; });
    
});

// Helper function for animation of pie chart and donut chart
function tweenPie(b) {
  b.innerRadius = 0;
  var i = d3.interpolate({startAngle: 0, endAngle: 0}, b);
  return function(t) { return arc(i(t)); };
}

function tweenDonut(b) {
  b.innerRadius = 0;
  var i = d3.interpolate({startAngle: 0, endAngle: 0}, b);
  return function(t) { return arc2(i(t)); };
}

 

D3 Income Chart

D3 is open source library for displaying powerful visualization on the web with help of javascript, HTML, and CSS. D3.js is a powerful library with many uses. This example specifically focuses on the map making the ability of D3.js and demonstrates how to make a web map using TopoJSON formatted geographic data, as well as introduce you to the wide range of projections that can be used to display a map in D3.js.

In this example, we are displaying the poverty and income based map in New York state. For this project, we first need the two data source and one ToponJson data.

  1. income.csv: Containing ID and Income
  2. poverty.csv: Containing ID and Poverty
  3. ny-quantize-topo.Json: Topjson data on New York

Step for creating New York Income Map in D3
Step 1. First, we need use scaleThreshold function to map input to output domain. Example as

var thresholdScale = d3.scaleThreshold()
    .domain([0, 50, 100])
    .range(['#ccc', 'lightblue', 'orange', '#ccc']);

In our example, we have to set color domain for each county in New York

// color 
var income_domain = [0, 10000, 50000, 70000, 80000, 150000, 290000, 360000]
var income_color = d3.scaleThreshold()
    .domain(income_domain)
    .range(d3.schemeGreens[7]);

d3.schemeGreens[7] is function from d3-scale-chromatic.v1.min.js for making chromatic color in our map. 

Step 2: We need to assign our data from file to variable and set it with d3.map function. The D3.map function map dictionary like structure that provides key-value storage. Here we are linking Id with Income.

// incomeData 
var incomeData = d3.map();
// povertyData 
var povertyData = d3.map();

Step 3: Load the TopoJSON data through d3.queue and defer functions. In our case, we will asynchronously load income.csv, poverty.CSV and TopoJSON data. Once we finishing loading all the data we will call the callback function.

In Step 3, we will have to follow sub-step, as

3.1 Read all the topojson data file and parse data to a variable and assign Geometry type and data.  Where the data, we are getting from the callback function.

    // new york data from  topojson
    var new_york = topojson.feature(data, {
        type: "GeometryCollection",
        geometries: data.objects.ny.geometries
    });

3.2 Assign the projection type and path

    // projection and path
    var projection = d3.geoAlbersUsa()
        .fitExtent([[20, 20], [460, 580]], new_york);;

    var geoPath = d3.geoPath()
        .projection(projection);

Where 20, 20 is padding and 460 and 580 is width and height. The d3.geoPath() is going to be the workhorse of our geographic drawings. It’s similar to the SVG path generators, except it draws geographic data and is smart enough to decide whether to draw a line or an area.

D3 gives us three tools for geographic data:
1. Paths produce the final pixels
2. Projections turn sphere coordinates into Cartesian coordinates
3. Streams speed things up

3.3 Draw the New York map and bind income and poverty separately and assign both to separate SVG in HTML file.

Full code for ny.js


// color 
var income_domain = [0, 10000, 50000, 70000, 80000, 150000, 290000, 360000]
var income_color = d3.scaleThreshold()
    .domain(income_domain)
    .range(d3.schemeGreens[7]);

var poverty_domain = [0, 10, 20, 30, 40, 50, 60, 70, 80, 90]
var poverty_color = d3.scaleThreshold()
    .domain(poverty_domain)
    .range(d3.schemeReds[4]);

// incomeData 
var incomeData = d3.map();

// povertyData 
var povertyData = d3.map();


// asynchronous tasks, load topojson maps and data
d3.queue()
    .defer(d3.json, "data/ny-quantize-topo.json")
    .defer(d3.csv, "data/income.csv", function(d) { 
        if (isNaN(d.income)) {
            incomeData.set(d.id, 0); 
        } else {
            incomeData.set(d.id, +d.income); 
        }
        
    })
    .defer(d3.csv, "data/poverty.csv", function(d) {
        if (d.poverty == '-') {
            povertyData.set(d.id, 0);
        } else {
            povertyData.set(d.id, +d.poverty); 
        }
        
    })
    .await(ready);



// callback function  
function ready(error, data) {

    if (error) throw error;

    // new york data from  topojson
    var new_york = topojson.feature(data, {
        type: "GeometryCollection",
        geometries: data.objects.ny.geometries
    });

    // projection and path
    var projection = d3.geoAlbersUsa()
        .fitExtent([[20, 20], [460, 580]], new_york);;

    var geoPath = d3.geoPath()
        .projection(projection);

    // draw new york map and bind income data
    d3.select("svg.income").selectAll("path")
        .data(new_york.features)
        .enter()
        .append("path")
        .attr("d", geoPath)
        .attr("fill", "white")
        .transition().duration(2000)
        .delay(function(d, i) {
            return i * 5; 
        })
        .ease(d3.easeLinear)
        .attr("fill", function(d) { 
            var value = incomeData.get(d.properties.GEOID);
            return (value != 0 ? income_color(value) : "lightblue");  

        })
        .attr("class", "counties-income");
    
    // title
    d3.select("svg.income").selectAll("path")
        .append("title")
        .text(function(d) {
            return d.income = incomeData.get(d.properties.GEOID);
        });

    // draw new york map and bind poverty data
    d3.select("svg.poverty").selectAll("path")
        .data(new_york.features)
        .enter()
        .append("path")
        .attr("d", geoPath)
        .attr("fill", "white")
        .transition().duration(2000)
        .delay(function(d, i) {
            return i * 5; 
        })
        .ease(d3.easeLinear)
        .attr("fill", function(d) { 
            var value = povertyData.get(d.properties.GEOID);
            return (value != 0 ? poverty_color(value) : "lightblue");  

        })
        .attr("class", "counties-poverty");
        
    // title
    d3.select("svg.poverty").selectAll("path")
        .append("title")
        .text(function(d) {
            return d.income = incomeData.get(d.properties.GEOID);
        });
}

 

Add the following code in index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>D3js New York</title>

    <!-- bulma css-->
    <link rel="stylesheet"  type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.3.2/css/bulma.min.css">

    <!-- CSS stylesheet -->
    <link rel="stylesheet" type="text/css" href="stylesheet.css">

    <!-- D3.js CDN source -->
    <script src="https://d3js.org/d3.v4.min.js" charset="utf-8"></script>
    <script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script>
    <script src="https://d3js.org/topojson.v2.min.js"></script>
  </head>
  <body>
    <div class="container">
        <!-- Title -->
        <h1 class="title has-text-centered">New York</h1>
        <h2 class="subtitle has-text-centered">Income vs Poverty</h2>

        <div class="columns">
          <div class="column is-half">
            <svg class="income" width="480" height="600"></svg>
          </div>
           <div class="column is-half">
            <svg class="poverty" width="480" height="600"></svg>
          </div>
        </div>


        <!-- Your D3 code for ny maps -->
        <script type="text/javascript" src="js/ny.js"></script>
        

        <!-- Info -->
        <p class="subtitle is-5 has-text-centered">Created by: Venkata Karthik Thota</p>
    </div>
  </body>
</html>

Add following code in style.css

svg {
    margin-left: auto;
    margin-right: auto;
    display: block;
}

Introduction to D3 scale

As a data visualization developer, one of the most important tasks is perform over and over is to convert data or input domain to our visual domain. Scales are mainly used for transforming data values to visual variables such as position, length, and color.

The scale not only for mapping of domain input to the range to produce visual output on the screen, but also to serve as fundamental building blocks for many other constructs such as transition and axes.

Axis in D3
In D3 we can draw axis manually and automatically, we can use D3 built in axis generator that we draw axis line, tick mark and label dynamically from data domain and spacing everything need before us. The D3 arrange all this element in the group. If we update our data domain, the axis will automatically update with new map value.

To built axis, we need to understand the concept of scale.

The Scale in D3
In D3 scale are JavaScript functions that perform data transform.
The scale function map an abstract input value domain to output value or range.  The scale function can convert a number’s of quantitative input domain to a continuous output range. This abstract domain can contain numerical and non-numerical like the mass of gold.

The scale input i.e is domain and output i.e is the range. The scale typically used to transform (or ‘map’) data values into visual variables (such as position, length, and color). When we are drawing a shape by our data values, we added the scaling factor to our data value, so that shape can visible on the screen. As data value, sometimes many be too large and some too small, so we have mapped it into the visual domain that is output graphics that can feet into our screen.

The range is D3 output for our domain input and always in the pixel so it can not be negative.  The range describes height or width available for our web page output value.

Most of the chart had two axises or ranges one for x-axis and one for the y-axis. The x-axis in most time is the date and we have to map the domain to range value for the y-axis.  In above graph tell us that y-axis is 200px in height and we can say that domain of 50 is plotted as 100px in the y-axis. Similarly for domain for 20 as 0px and domain value of 80 as 200px.

Note: The browser draw the graphic element from the top down and we read the data chart from bottom up. So we have to invert our y value position. For this reason, we normally have to match the minimum value of the domain against the maximum value of range and vice versa.

D3 scale types
D3 has around 12 different scale types (scaleLinear, scalePow, scaleQuantise, scaleOrdinal etc.) and broadly speaking they can be classified into 3 groups:

  1. scales with continuous input and continuous output
  2. scales with continuous input and discrete output
  3. scales with discrete input and discrete output

Creating the linear scale
The D3 scale can be continues, ordinal, Quantize or sequential, we want to most common type of continues scale which is numeric linear scale.    Adding the D3 linear scale example as

var dataArray = [25,27,28,30,35,40,45,56,77,80,94,107,100,120,140,156,170]

var height = 200;
var width = 500;

var margin = { left:50, right:50, top:40, bottom:0 };

var y = d3.scaleLinear()
			.domain([0, 170])
			.range([height,0]);

var yAxis = d3.axisLeft(y);

console.log(y(0));  //op 200
console.log(y(85));  //op 100
console.log(y(170));  // op 0

var area = d3.area()
				.x(function(d,i){ return i*20; })
				.y0(height)
				.y1(function(d){ return y(d); });

var svg = d3.select("body").append("svg").attr("height", "100%").attr("width","100%");
var chartGroup = svg.append("g").attr("transform","translate("+margin.left+", "+margin.top+")");

chartGroup.append("path").attr("d",area(dataArray));
chartGroup.append("g")
		.attr("class","axis y")
		.call(yAxis);

At the highlight, we have to define the type of scale we want, domain as input and range as output. Both domain and range are arrays, both have min and max value. In the domain, we have a minimum as 25 and maximum as 170. In the range array minimum is zero and the maximum value is height. Read the above highlight Note again.  We can see that in the console when the input value is 0 then corresponding output value that is y is 200 and when are input value is 170 and corresponding y value will be 0.

At line number 10 of code we have to declare axis generator, 
var yAxis = d3.axisLeft(y);
The generator will not run until we call it. There are 4 types of the axis in D3 axisLeft, axisRight, axisTop, axisBottom. This will tell where to put the label relative to the line, should label popup at the left side of the line or right side of the line and same goes for top and bottom.
To add the axis we need to add the group element and then call the axis on that group. D3 then add all the elements into the group for us.
We will create one group and add both axis and chart into that group. We will create a chartGroup to add both axis and chart element. The output of our above D3 code.

 

Javascript Variable Scope

When we are learning the programming language, each language has its own variable scope.  In the Javascript, both function and object are also variables.  We can declare a variable in many locations inside, outside the function and object. Based on the location of variable it has the scope.

By the scope, we mean the lifetime of a variable i.e where the variable is visible and available for us to use in our code. In JavaScript, we have not many but just a couple of different types of scopes. In the Javascript we have

  1. Local Variable
  2. Global Variable

ES6 has introduce two new keywords for declaring variables,

  1. let
  2. const

The scope of variables declared using var is within the function it is defined and global if it is not defined inside any function, while the scope of let is restricted to within the enclosing block it was declared in and global if it is not defined inside any enclosing block.

Const keyword :
The working of const is the same as that of let, except that variables declared using const cannot be changed (reassigned). Hence, const is used for constants. However, an entire constant cannot be reassigned but their properties can be changed. For example:

const a = 5;
// this will not work
a = 7; 
const b = {
	a: 1,
	b: 2
};
 // this will not work
 
b = { a: 2, b: 2 };

// this will work since only a property of b is changed
b.a = 2; 

Local variable: When a variable is declared inside the function it has local scope. The local variable only visible and accessed within the function where it was declared. The local variable dies when function finished its task.

Global Variable: When the variable is declared outside of the function it becomes global variable and can be accessed from anywhere in the code.

The Javascript does not implement block scoping. We can achieve block with help of let keyword. In the example here we can access variable i and inFor outside the for loop block.

function scopeTest() {
   for (var i = 0; i <= 5; i++){
     inFor = i; 
   }
  console.log("Value of inFor" +inFor);  
  console.log("Value of i" +i);
}

The output of above code inFor as 5 and i as 6.  In the new version of Javascript allow us to use new keyword called let to achieve block level in the loop. Variables declared with let inside a block scope are only accessible inside that scope. If we use let to declare the i variable in a for loop, that variable will only be available inside the for a loop.

function scopeTest() {
   for (let i = 0; i <= 5; i++){
        inFor = i; 
   }
   console.log("Value of inFor" +inFor);  
   console.log("Value of i" +i);
}

For above code output value of inFor is 5 and we can’t access the i value outside so it will generate output message as Uncaught ReferenceError: i is not defined.

Static Variable scoping:
Here we are learning static variable scope in Javascript with D3 library.  Add the following code in our filename.js file.

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
    <title>Functional Javascript</title>
    <link rel="stylesheet" type="text/css" href="css/style.css"/>
    <script type="text/javascript" src="node_modules/d3/build/d3.js"></script>
</head>

<body>

<script type="text/javascript">
    function simpleFunObject(spec) {
        var instance = {}; 
        var headline, description; 

        instance.render = function () {
            var div = d3.select('body').append("div");

            div.append("h3").text(headline); 

//Adding box class and color to parent div 
            div.attr("class", "box") 
               .attr("style", "color:" + spec.color) 
               .append("p")
                   .text(description); 

            return instance; 
        };

        instance.headline = function (h) {
            if (!arguments.length) return headline; 
            headline = h;
            return instance; 
        };

        instance.description = function (d) {
            if (!arguments.length) return description;
            description = d;
            return instance;
        };

        return instance; 
    }

    var testScope = simpleFunObject({color: "#718024"})
            .headline("Javascirpt static variable scope")
            .description("This is a example of simple functional object in javascript.");
    testScope.render();
</script>

</body>
</html>

In above code, all three variable instance, headline, description are private variable belongs to the simplFunObject function object.  The function simpleFunObject is a functional object and we can assign it variable and called render() method. Render refers to show output in the browser and utilize the document object to manipulate DOM elements.

In the Javascript Scoping is static, it is determined by the nesting of syntactic constructs. The variable scoping is actually determined by a simple static scoping rule. This rule can follow as, whenever the runtime searches for a variable reference, the search will be performed first locally. When a variable declaration is not found in second highlight headline in code, search will perform first in local then it search continue toward the parent object, if still not found, then this process will continue recursively to next parent, so on and so forth, till it reaches a global variable definition, if it is still not found then a reference error will be generated for this variable.

D3 module in Node JS

The D3 js open source and functional programming of Javascript.   The d3.js is JavaScript function programming that gives all the visualization support we want to have almost all in functional form. Function in Javascript are objects. Like any other object in Javascript, the function object is just a collection of name and value pair. The only difference between regular object and function object is that function can be invoked and additionally, function object has two hidden properties, function context, and a function code.  

The D3 is self-sufficient, it has no dependency on any other Javascript library except what browser already provide. The D3 is modular based, we can add whole D3 or part of D3 submodule in our web the node apps.  Eg, we can use D3 submodule like d3-selection as dependency in our node apps

Syntax for adding whole d3 module : npm install –save d3

The D3 can be run directly in local machine, by opening HTML page with the D3 script embedded to view D3 visualization.  For the security reason, it is recommended to add local HTTP server so that we can be accessed HTML page and data from the server.

D3 Bar Chart

When we are making a bar chart, we need scaleBand() function to create a band for each domain in a bar chart. In the above bar chart picture, we had use scaleBand() on the x-axis to create separate rectangle or band for each salesman.  Here we have used discrete domain value as salesman name as domain input and we used the continuous value from zero to width for range value. The scaleBand will calculate inner padding and outer padding for our band on a bar chart.

In D3 version 4, some of the function had replaced as

  1. v4’s .scaleBand() replaced v3’s .scale.ordinal().
  2. v4’s .rangeRound() and .padding() replaced v3’s rangeRoundBands().

 

scaleBand function: 
Band scales are like ordinal scales except the output range is continuous and numeric. The scaleBand have a discrete domain and will generate the range of continue value or uniform bands for each domain. Band scales are typically used for bar charts with an ordinal or categorical dimension.
When creating bar charts scaleBand helps to determine the geometry of the bars, taking into account padding between each bar. The domain is specified as an array of values or the discrete value  (one value for each band) and the range as the minimum and maximum extents of the bands plus padding (e.g. the total width of the bar chart).

Case 1: scaleBand without domain value.
x = d3.scaleBand() .range([0, width]);
Constructs a new band scale with the empty domain, the unit range [0, 1], no padding, no rounding and center alignment. We can assign the domain later by calling

 x.domain(data.map(function(d) { return d.salesperson; }));

Case 2: scaleBand without domain value.

var bandScale = d3.scaleBand()
  .domain(['Mon', 'Tue', 'Wed', 'Thu', 'Fri'])
  .range([0, 200]);

bandScale('Mon'); // returns 0
bandScale('Tue'); // returns 40
bandScale('Fri'); // returns 160

Here scaleBand will split the range into n bands (where n is the number of values in the domain array) and compute the positions and widths of the bands taking into account any specified padding.

The width of each band can be accessed using .bandwidth():
bandScale.bandwidth(); // returns 40

Two types of padding may be configured:

  • The paddingInner which specifies (as a percentage of the bandwidth) the amount of padding between each band
  • The paddingOuter which specifies (as a percentage of the bandwidth) the amount of padding before the first band and after the last band

bandScale.paddingInner(0.05);

 

d3.scaleLinear():
The mapping performed is linear in that the output range is calculated using a linear function of the input domain.
Linear scales are a good default choice for continuous quantitative data because they preserve proportional differences. Each range value y can be expressed as a function of the domain value
x: y = mx + b.

Here we will create simple bar chart

Add the following code in index.html

<style>
.bar { fill: steelblue; }<br />
</style>
<!-- load the d3.js library --> 
<script src="//d3js.org/d3.v4.min.js"></script> 
<script>
// set the dimensions and margins of the graph
var margin = {top: 20, right: 20, bottom: 30, left: 40},
    width = 760 - margin.left - margin.right,
    height = 400 - margin.top - margin.bottom;

// set the ranges
var x = d3.scaleBand()
          .range([0, width])
          .padding(0.1);
    
var y = d3.scaleLinear()
          .range([height, 0]);
          
// append the svg object to the body of the page
// append a 'group' element to 'svg'
// moves the 'group' element to the top left margin

var svg = d3.select("body").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 + ")");

// get the data
d3.csv("sales.csv", function(error, data) {
  if (error) throw error;

  // format the data
  
  data.forEach(function(d) {     
    d.sales = +d.sales;
  });

  // Scale the range of the data in the domains
  
  x.domain(data.map(function(d) { return d.salesperson; }));
  y.domain([0, d3.max(data, function(d) { return d.sales; })]);

  
  // append the rectangles for the bar chart
  
  svg.selectAll(".bar")
      .data(data)
    .enter().append("rect")
      .attr("class", "bar")
      .attr("x", function(d) { return x(d.salesperson); })
      .attr("width", x.bandwidth())
      .attr("y", function(d) { return y(d.sales); })
      .attr("height", function(d) { return height - y(d.sales); });

	  
  // add the x Axis
  
  svg.append("g")
      .attr("transform", "translate(0," + height + ")")
      .call(d3.axisBottom(x));

	  
  // add the y Axis
  
  svg.append("g")
      .call(d3.axisLeft(y));

});
</script>

Add following code in sales.csv

salesperson,sales
Sharma,33
Raj,12
Ajay,41
Arjun,16
Shihab,59
Royan,38
Max,21
Tenzin,25
Kumar,30
Lisa,47
Tom,5
Jerry,20
John,13
Tamo,29

Note: Important code in above code example

1.  Finding SVG element position in our page.

Here the translate will find the x and y position of svg element in our page and will assign x=40 and y=2 as we already assign
margin.left as 40 and margin.right as 20
var svg = d3.select("body").append("svg") ...
  .append("g")
    .attr("transform", 
          "translate(" + margin.left + "," + margin.top + ")"); 

2. We are making band or rectangle for each domain. As for rectangle in SVG we have 4 value required as x,y positon, width and height.

 

 

D3 interpolate type and SVG Group Element

We can’t add text to a rectangle or any shape to any other shape in the SVG. In standard HTML we can put almost or any element inside another element, eg we can add table an element inside the div element.

There are some exception element in SVG, eg TSPAN can be added inside the text element. The SVG group element allows us to add anything inside the group include group inside another group. The SVG group element can be used to group another SVG element together and it acts as a container for another SVG element.  By nature group, an element is not shown in the web browser, as itself can’t be style. But group element has the tangible effect, the group content can be conceptual move, rotate its content.

The group element allows us to join another element together, turn then in pink or move to new pixel and they allow us to tight organize the elements in the DOM. In the example below, we had used different interpolate available in D3 and we had created 6 group element and each group is having path information and six dots as the circle on the path.

var dataArray = [{x:5,y:5},{x:10,y:15},{x:20,y:7},{x:30,y:18},{x:40,y:10}];
var interpolateTypes = [d3.curveLinear,d3.curveNatural,d3.curveStep,d3.curveBasis,d3.curveBundle,d3.curveCardinal];

var svg = d3.select("body").append("svg").attr("height","100%").attr("width","100%");


for(var p=0; p<6; p++){
	var line = d3.line()
					.x(function(d,i){ return d.x*6; })
					.y(function(d,i){ return d.y*4; })
					.curve(interpolateTypes[p]);
	
	var shiftX = p*250;
	var shiftY = 0;
	
	var chartGroup = svg.append("g").attr("transform", "translate("+shiftX+",0)");

	chartGroup.append("path")
		  .attr("fill","none")
		  .attr("stroke","blue")
		  .attr("d",line(dataArray));
		  
	chartGroup.selectAll("circle.grp"+p)
		.data(dataArray)
		.enter().append("circle")
				.attr("class", function(d,i){ return "grp"+i; })
				.attr("cx",function(d,i){ return d.x*6; })
				.attr("cy",function(d,i){ return d.y*4; })
				.attr("r","2");
				
}

In the above example, we have created 6 charts with 6 dot indicating data points and each chart we have path added to it with different curve setting or interpolation methods.

  1. Create 1st line path and added 6 dots as the circle.
  2. Added all element with path and dot are group into 6 group element. As we can easily reposition group rather than reposition each individual element in the group.

We can see DOM structure of our above code.

Path in D3js

The path is the essential in SVG and can be used to make the advanced graphic shape. The path can be straight, wiggle, can be open and or closed as the chart. The SVG path is capable of drawing any shape like a circle, rectangle, ellipse, straight line, curves and so many.

What to learning here

  • Generator for creating shape.
  • Different types of interpolation,
  • Drawing area Chart

To create path we need.

To create path we need one mandatory attribute called data as in line we have four mandatory attributes as x,y1,x2,y2.  Syntax for drawing line as

Path = “Mx,y  Lx,y  Lx,y ………..Lx,y or Path = “Mx,y  Lx,y  Lx,y ………..Lx,yZ or
Path = “Mx,y  Cx,y  Cx,y ………..Cx,y

<svg height="200" width="400">
  <path d="M100 0 L55 200 L125 200 Z" />
</svg>

Note:
1. M -> Moveto , L -> Lineto (Draw straight line), H -> Horizontal lineto, V -> Vertical lineto, Z -> Closed the path back to first. C-> Curveto, S -> SmoothCurveto, Q -> Quadratic Bezier Curve, T -> Smooth quadratic Bézier curveto, A -> Elliptical Arc

2. All above character are case sensitive, as UpperCase for Absolute positioning of x,y and lowercase for relative positioning of x,y.

D3 Path Generator

The generator is both object and function. The D3 has more than 20 generators and still in growing in number, we have a line, area, arc, chord and so many. We can use D3 generator to create a different shape like the chart. Some of D3 generator are
d3.svg.line()
d3.svg.area()
d3.svg.arc()
d3.chord()

Create path using line generator

In code bellow we have dataArray is data for line or path, it has five objects containing new x and y line position.

var dataArray = [{x:5,y:5},{x:10,y:15},{x:20,y:7},{x:30,y:18},{x:40,y:10}];

var svg = d3.select("body").append("svg").attr("height","100%").attr("width","100%");

var line = d3.line()
				.x(function(d,i) { return d.x*6; })
				.y(function(d,i) { return d.y*4; });
				
svg.append("path")
	.attr("fill", "none")
	.attr("stroke","blue")
	.attr("d", line(dataArray));

The highlighted line is line generator and its output line or path element for us and it appears to line with 4 section.

The SVG line and D3 line are different, as SVG line can only be straight. The SVG line is an element and D3 line is a generator. It will not generate line element, it will generate path element. In above code, we supply the generator with an array containing 5 data points. The x,y will pass 5 different data point and calculate x, y value 5 times. The D3 will take 5 coordinate to make a line for us. By default path had fill attribute in line and we had set the fill attribute to none in our code so that we can see all line.

Drawing an area chart.

In the second example, we will create a chart with help of area generator. The, unlike the line generator the area generator, provides the shape in closed form. In the area generator, we need two y value. The one value will set the location of the upper line and the second one will set the location of the lower line as follow.

In our example in area generator y1 can represent data point on the chart, y1 is the active variable and drawing the upper boundary of the area chart. The y1 change with every data points and y0 will remain always constant in our case we set to height that is 200.

var dataArray = [25,27,28,30,35,40,45,56,77,80,94,107,100,120,140,156,166]
var dataYears = ['2000','2001','2002','2003','2004','2005','2006','2007','2008','2009','2010','2011','2012','2013','2014','2015','2016'];

var height = 200;
var width = 500;

var area = d3.area()
				.x(function(d,i){ return i*20; })
				.y0(height)
				.y1(function(d){ return height - d; });

var svg = d3.select("body").append("svg").attr("height", "100%").attr("width","100%");
svg.append("path").attr("d",area(dataArray));

The output of above code as follow

D3 Interpolate