Plotting on the web (with Javascript D3)

We have seen how to plot a bar chart with Python, now let’s see how it can be done for the web so that it can be displayed in any (recent) browser as it is the quickest way to reach a global audience (regardless of the operating system and device type.

We will use the same data and chart as in the Python example.

To plot it, I will use the D3 library, probably the most used javascript library for web visualisation.
This small tutorial assumes that you know Javascript and the W3C DOM.
Download the library from its web site and then import it by adding the following in the HTML head part:

script src="d3/d3.v3.js" type="text/javascript"

The actual Javascript code to generate the chart will go in the body part; let’s start by declaring the data set, a dictionary with name of country and its number of WHC sites:

var whcTop10 = [
   {name: "Italy", value: 50},
   {name: "China", value: 47},
   {name: "Spain", value: 44},
   {name: "Germany", value: 39},
   {name: "France", value: 39},
   {name: "Mexico", value: 32},
   {name: "India", value: 32},
   {name: "UK", value: 28},
   {name: "Russia", value: 26},
   {name: "USA", value: 22}
];

Now let’s create an SVG element (using SVG is more reliable, visually consistent, and faster ) and attach it to the page body:

      // Create the SVG element and store it in myChart
var myChart = d3.select("body").append("svg");

d3.select(“body”) finds the body element in the DOM and hands off a reference to the next step in the chain (append) that is used to create a new SVG empty element and to append it into the DOM.

Finally, the reference to the DOM element is stored in a new variable myChart. This reference saved in a variable is not strictly needed but it allows not to search for that SVG each time—as in d3.select()—but to write only myChart, for example, to define the chart size (think about it as a canvas where to draw):

myChart.attr("width", 500)
       .attr("height", 200);

An even better version is to put the width and height values into variables at the top of your code:

    // Width and height of the canvas (SVG element)
 var w = 500;
 var h = 200;
 myChart.attr("width", w)
        .attr("height", h);

Now we are ready to read the data and then to iterate through each data point, creating an SVG rectangle element for each one:

var myBars = chart.selectAll("rect")
                         .data(whcTop10)
                         .enter()
                         .append("rect")

The d3 function selectAll() will return empty references to all rectangles (which don’t exist yet), while data() binds the data set to the elements we’re going to create, enter() returns a placeholder reference to the new element, and append() finally adds a rectangle (there are a number of visual elements that you can include between those SVG tags, including rect to draw a rectangle or there is circle, ellipse, line, text, and path) to the DOM.
Again, a new variable – myBars –  store references to all of the rectangles / bars.

If you run this, it will just display an empty space (size 500 x 200). We need to set a couple more of attributes, such as the rectangles’ sizes and positions.

Now let’s see how we can create and display these bars: this is achieved by using the function attr() on myBars (similar as before, on the variable myChart) that is used to set the attributes of an element.
Specifically the attributes “x” and “y” (the position inside the SVG element) and the attributes “width” and “height” (the size) need to be set.

var barPadding = 1; // space between bars
var maxValueInDataset = max(whcTop10);
var ratioHeight = h / maxValueInDataset;  

myBars.attr("x", function(d, i) {
                     return i * (w / whcTop10.length);
                 })
      .attr("y", function(d) {
                     return h - (d.value * ratioHeight); //Height minus data value
                 })
      .attr("width", w / whcTop10.length - barPadding)
      .attr("height", function(d) {
                        return d.value * ratioHeight;
                      });

Notice that to display the bars separate among them, a variable barPadding is used that is set to the number of pixels for the space (in this case barPadding = 1).

The functions are called with two arguments: the data (d) and the index (i). Note that our dataset contains both names and values, so we must refer to the value as d.value

attr(“x”) : this is the x position; through an anonymous function it gets assigned a dynamic value that corresponds to each value’s position in the data set. And the bars are so spaced that they can fit the space allocated for the SVG element (that’s the variable w, while the function length returns the total number of data entries in the data set whcTop10).

attr(“width”): this is how wide the bars are. You can just set a constant value, such as 20 (it’s in pixels). Or (like here) you can set the bar widths to be proportional so they get narrower when more data entries are present, or wider when there are fewer values: the width will now be set as the ratio of the SVG width and the data entries, minus a padding value.

attr(“height”): this is how high are the bars. And of course this should be proportional to the data set values. This is obtained by using again an anonymous function that sets it simply to the parameter d. To make the bars well spaced, d is multiplied to a ratio that is the one between the SVG element’s height and the maximum value in the data set.

attr(“y”): this is the y position (the top of the bars); to make the bars starting from the x axes, we use an anonymous function returning the height of the SVG element minus the data entry value.

Here is how it looks like:

Screen Shot 2015-04-22 at 20.27.55That’s nice but the chart looks quite bare …

To add a colour to all the bars you can apply the attribute “fill”:

  // set bar colour = blue
myBars.attr("fill", "blue");

Note: of course you can achieve the same using the CSS and is actually a better way; just define a class in CSS and refer to it in the javascript code.

Instead of passing a fixed colour, you can also pass a function, for example to have different colours according to the value (the height of the bar); actually a bad idea from a visualisation prospective, but it could be used to set a different colour for a specific bar, for example for the one with the highest value:

 myBars.attr("fill", function(d,i) {
                        if (i == countryWithHighestValue) {
                            return "green"
                        } else { 
                            return "blue" 
                        }
                   });

Labels: you can add text elements to an SVG element.
The code is similar as used to create the rect elements:

  // Create the frequency labels above the rectangles as text elements 
 myChart.selectAll("text")
        .data(whcTop10)
        .enter()
        .append("text")
        .text(function(d) {
             return d.value;
          })
        .attr("text-anchor", "middle") // center them in middle of bar
        .attr("x", function(d, i) {
             return i * (w / whcTop10.length) + (w / whcTop10.length - barPadding) / 2;
          })
        .attr("y", function(d) {
             return h - (d.value * 4) + 14;  // some padding as magic numbers...
          })
        .attr("fill", "white");  // set colour = white

text():  this is the function to add a text element, in this case the label. The function just returns the data value and that is what is displayed.

attr(): as for the rect element, you can set the coordinates with “x” and “y” (so that each label will be inside the related bar) and to style the text too, for example to set its colour to white. If you wish you can also set the font, its size and so on.

Here is the result:

Screen Shot 2015-05-15 at 12.28.36

But the real power of D3 library lies in its interactivity.

Let’s add a mouseover effect that displays the country name as a tooltip when the user goes over a bar with the mouse:

   // change colour and display country name when mouse is over
 myBars.on("mouseover", function(d,i) { 
             d3.select(myBars[0][i])
               .style("fill", "red") // new colour is red
               .append("title").text(function(d) { // title is browser tooltip
                     return "This country is " + d.name;
                 })
           });

To do this is simply adding a function for the standard effect on(“mouseover”): you select the bar via the parameter i, change the fill style to apply a new colour (red in this case) and append the tooltip based on the d parameter.

When the user moves the mouse out, you need to change back the colour:

    // change colour back when mouse is out
 myBars.on("mouseout", function(d,i) { 
              d3.select(myBars[0][i])
                .style("fill", "blue"); // back to blue
           });

And here is the final result:

Screen Shot 2015-05-15 at 14.25.58

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s