D3 Tips and Tricks cover page
D3 Tips and Tricks

D3 Tips and Tricks

Table of Contents

Acknowledgements

First and foremost I would like to express my thanks to Mike Bostock, the driving force behind d3.js. His efforts are tireless and his altruism in making his work open and available to the masses is inspiring.

Mike has worked with a crew of like-minded individuals in bringing D3 to the World. Vadim Ogievetsky and Jeffrey Heer share honours for the work on D3: Data-Driven Documents and while there has been a cast of over 40 people contributing to the D3 code base, Jason Davies stands out as the man who has provided a generous portion especially in the area of mapping.

Nick Zhu has created a fantastic resource in dc.js (which is built on top of d3.js and crossfilter) and has been kind enough to provide good advice and permission to include some of his work in the dc.js section.

Advice given by Christophe Viau has been a great help in getting me settled into the on-line world and his energy in managing and directing the D3 community is amazing.

Mike Dewar (Getting Started with D3), Scott Murray (Interactive Data Visualization for the Web) and Sebastian Gutierrez (dashingd3js.com) lead the pack for providing high quality reference material for learning D3. Many thanks gentlemen.

I am particularly grateful for the assistance given by Filiep Spyckerelle and Robin Bennett who selflessly donated their time and expertise in proofreading above and beyond the call of duty (where this document contains any errors, they are most certainly mine). Big thanks go out to the D3 community. Whether providing advice on Google Groups or Stack Overflow, contributing examples on bl.ocks.org or just giving back in the form of time and effort to similar work. Well done all.

Lastly, I want to pay homage to Leanpub who have made the publishing of this document possible. They offer an outstanding service for self-publishing and have made the task of providing and distributing content achievable.

Make sure you get the most up to date copy of D3 Tips and Tricks

If you’ve received a copy of this book from any location other than Leanpub then it’s possible that you haven’t got the latest version. Go to https://leanpub.com/D3-Tips-and-Tricks and download the most recent version. After all, it won’t cost you anything :-). If you find some value in the work, please consider contributing 99 cents when you download it so that Leanpub get something for hosting the book (and I’ll think of you fondly while I have a beer :-D).

What is d3.js?

d3.js (hereafter abridged as D3) is “a JavaScript library for manipulating documents based on data”.

But that description doesn’t do it justice.

D3 is all about helping you to take information and make it more accessible to others via a web browser.

It’s a JavaScript library. That means that it’s a tool that can be used in conjunction with other tools to get a job done. Those other tools are mainly HTML and CSS (amongst others) but you don’t need to know too much about either to use D3 (although it will help :-)).

It’s an open framework, which means that there are no hidden mysteries about how it does its magic and it allows others to contribute to a constant cycle of improvement.

It’s built to leverage web standards which means that modern browsers don’t have to do anything special to use D3, they just have to support the framework that the Internet has adopted for ease of use.

The beauty of D3 is that it allows you to associate data and what appears on the screen in a way that directly links the two. Change the data and you change the object on the screen. D3’s trick is to let you set what appears on the screen. A circle, a line, a point on a map, a graph, a bouncing ball, a gradient (and way, way more). Once the data and the object are linked the possibilities are endless.

It won`t do everything for you in your quest to create the perfect visualization, but it does give you the ability to achieve that goal.

It bridges the gap between the static display of data and the desire of people to mess about with it. That applies equally to the developer who wants to show something cool and to the end user who wants to be able to explore information interactively.

It was (and still is being) developed by Mike Bostock who has not just spent time writing the code, but writing the documentation for D3 as well. There is an extensive community of supporters who also contribute to the code, provide technical support online and generally have fun creating amazing visualizations. Their contributions are extraordinary (you only have to look at the work of Jason Davies to be amazed).

Introduction

I never set out to write treatise on D3…

I am a simple user of this extraordinary framework and when I say simple, I really mean I had no idea how to get it to do anything when I started; I needed to do a lot of searching and learned by trial-and-error (emphasis on the errors which were entirely mine). The one thing that I did know was that the example graphics shown by Mike Bostock and others were the sort of graphical goodness that I wanted to play with.

So to get from the point of having no skills whatsoever to the point where I could begin to code up something to display data in a way I wanted, I had to capture the information as I went. The really cool thing about this sort of process is that it doesn’t need to occur all at once. You can start with no knowledge whatsoever (or pretty close) and by standing on the shoulders of other’s work, you can add building blocks to improve what you’re seeing and then change the blocks to adapt and improve.

For example (and this is pretty much how it started). I wanted to draw a line graph, so I imported an example and then got it running locally on my computer. Then I worked out how to change the example data for my data. Then I worked out how to move the Y axis from the right to the left. Then how to make the axis labels larger, change the tick size, make the lines fatter, change the colour, add a label, fill the area under the graph, put the graph in the centre of the page, add a glow to the text to help it stand out, put it in a framework (bootstrap), add buttons to change data sets, animate the transitions between data sets, update the data automatically when it changed, add a pan and zoom feature, turn parts of the graph into hyperlinks to move to other graphs… And then I started on bar graphs :-).

The point to take away from all of this is that any one graph is just a collection of lots of blocks of code, each block designed to carry out a specific function. Pick the blocks you want and implement them.

I found it was much simpler to work on one thing (block) at a time, and this helped greatly to reduce the uncertainty factor when things didn’t work as anticipated. I’m not going to pretend that everything I’ve done while trying to build graphs employs the most elegant or efficient mechanism, but in the end, if it all works on the screen, I walk away happy :-). That’s not to say I have deliberately ignored any best practices – I just never knew what they were. Likewise, wherever possible, I have tried to make things as extensible as possible.

You will find that I have typically eschewed a simple “Do this approach” for more of a story telling exercise. This means that some explanations are longer and more flowery than might be to everyone’s liking, but there you go, try to be brave :-)

I’m sure most authors try to be as accessible as possible. I’d like to do the same, but be warned… There’s a good chance that if you ask me a technical question I may not know the answer. So please be gentle with your emails :-).

Email: d3noobmail+contact@gmail.com

What do you need to get started?

Let’s be frank. My grandmother will never put together a graphic using D3.

However, that doesn’t mean that it’s beyond those with a little computer savy and a willingness to have a play. Remember failure is your friend (I am fairly sure that I am also related by blood). Just learn from your mistakes and it’ll all work out.

So, here in no particular order is a list of good things to know. None of which are essential, but any one (or more) of which will make your life slightly easier.

  • HyperText Markup Language (HTML)
  • JavaScript
  • Cascading Style Sheets (CSS)
  • Web Servers
  • PHP

HTML

This stands for HyperText Markup Language and is the stuff that web pages are made of. Check out the definition and other information on Wikipedia for a great overview. Just remember that all you’re going to use HTML for is to hold the code that you will use to present your information. This will be as a .html (or .htm) file and they can be pretty simple (we’ll look at some in a moment).

JavaScript

JavaScript is what’s called a ‘scripting language’. It is the code that will be contained inside the HTML file that will make D3 do all its fanciness. In fact, D3 is a JavaScript Library, it’s the native language for using D3.

Knowing a little bit about this would be really good, but to be perfectly honest, I didn’t know anything about it before I started. I read a book along the way (JavaScript: The Missing Manual from O’Reilly) and that helped with context, but the examples that are available for D3 graphics are understandable, and with a bit of trial and error, you can figure out what’s going on.

In fact, most of what this collection of information’s about is providing examples and explanations for the JavaScript components of D3.

Cascading Style Sheets (CSS)

Cascading Style Sheets (everyone appears to call them ‘Style Sheets’ or ‘CSS’) is a language used to describe the formatting (or “look and feel”) of a document written in a markup language. The job of CSS is to make the presentation of the components you will draw with D3 simpler by assigning specific styles to specific objects. One of the cool things about CSS is that it is an enormously flexible and efficient method for making everything on the screen look more consistent and when you want to change the format of something you can just change the CSS component and the whole look and feel of your graphics will change.

The wonderful World of Cascading Style Sheets
The wonderful World of Cascading Style Sheets

Web Servers

Ok, this can go one of two ways. If you have access to a web server and know where to put the files so that you can access them with your browser, you’re on fire. If you’re not quite sure, read on…

A web server will allow you to access your HTML files and will provide the structure that allows it to be displayed on a web browser. There are some simple instructions on the main D3 wiki page for setting up a local server. Or you might have access to a remote one and be able to upload your files. However, for a little more functionality and a whole lot of ease of use, I can thoroughly recommend WampServer as a free and simple way to set up a local web server that includes PHP and a MySQL database (more on those later). Go to the WampServer web page (http://www.wampserver.com/en/) and see if it suits you.

Throughout this document I will be describing the files and how they’re laid out in a way that has suited my efforts while using WAMP, but they will work equally well on a remote server. I will explain a little more about how I arrange the files later in the ‘Getting D3’ section.

WAMP = Windows + Apache + MySQL + PHP
WAMP = Windows + Apache + MySQL + PHP

There are other options of course. You could host code on GitHub and present the resulting graphics on bl.ocks.org. This is a great way to make sure that your code is available for peer review and sharing with the wider community.

One such alternative option that I have recently started playing with is Plunker (http://plnkr.co/) This is a lightweight collaborative online editing tool. It’s so cool I wrote a special section for it which you can find later in this document. This is definitely worth trying if you want to use something simple without a great deal of overhead. If you like what you see, perhaps consider an alternative that provides a greater degree of capability if you go on to greater d3.js things.

PHP

PHP is a scripting language for the web. That is to say that it is a programming language which is executed when you load web pages and it helps web pages do dynamic things.

You might think that this sounds familiar and that JavaScript does the same thing. But not quite.

JavaScript is designed so that it travels with the web page when it is downloaded by a browser (the client). However, PHP is executed remotely on the server that supplies the web page. This might sound a bit redundant, but it’s a big deal. This means that the PHP which is executed doesn’t form part of the web page, but it can form the web page. The implication here is that the web page you are viewing can be altered by the PHP code that runs on a remote server. This is the dynamic aspect of it.

In practice, PHP could be analogous to the glue that binds web pages together. Allowing different portions of the web page to respond to directions from the end user.

It is widely recognised not only as a relatively simple language to learn, but also as a fairly powerful one. At the same time it comes into criticism for being somewhat fragmented and sometimes contradictory or confusing. But in spite of any perceived shortcomings, it is a very widely used and implemented language and one for which there is no obvious better option.

Other Useful Stuff

Text Editor

A good text editor for writing up your code will be a real boost. Don’t make the fatal mistake of using an office word processor or similar. THEY WILL DOOM YOU TO A LIFE OF MISERY. They add in crazy stuff that you can’t even see and never save the files in a way that can be used properly.

Preferably, you should get an editor that will provide some assistance in the form of syntax highlighting which is where the editor knows what language you are writing in (JavaScript for example) and highlights the text in a way that helps you read it. For example, it will change text that might appear as this;

// Get the data
d3.tsv("data/data.tsv", function(error, data) {
data.forEach(function(d) {
    d.date = parseDate(d.date);
    d.close = +d.close;
});

Into something like this;

// Get the data
d3.tsv("data/data.tsv", function(error, data) {
data.forEach(function(d) {
    d.date = parseDate(d.date);
    d.close = +d.close;
});

Infinitely easier to use. Trust me.

There are plenty of editors that will do the trick. I have a preference for Geany, mainly because it’s what I started with and it grew on me :-).

Getting D3

Luckily this is pretty easy.

Go to the D3 repository on github and download the entire repository by clicking on the ‘ZIP’ button.

Download the repository as a zip file
Download the repository as a zip file

What you do with it from here depends on how you’re hosting your graphs. If you’re working on them on your local PC, then you will want to have the d3.js file in the path that can be seen by the browser. Again, I would recommend WAMP (a local web server) to access your files locally. If you’re using WAMP, then you just have to make sure that it knows to use a directory that will contain the d3 directory and you will be away.

The following image is intended to provide a very crude overview of how you can set up the directories.

A potential directory structure for your files
A potential directory structure for your files
  • webserver: Use this as your ‘base’ directory where you put your files that you create. That way when you open your browser you point to this directory and it allows you to access the files like a normal web site.
  • d3: This would be your unzipped d3 directory. It contains all the examples and more importantly the d3.v3.js file that you need to get things going. You will notice in the code examples that follow there is a line like the following;
    <script type="text/javascript" src="d3/d3.v3.js"></script>.
    This tells your browser that from the file it is running (one of the graph html files) if it goes into the ‘d3’ folder it will find the d3.v3.js file that it can load.
  • data: I use this directory to hold any data files that I would use for processing. For example, you will see the following line in the code examples that follow d3.tsv("data/data.tsv", function(error, data) {. Again, that’s telling the browser to go into the ‘data’ directory and to load the ‘data.tsv’ file.
  • js: Often you will find that you will want to include other JavaScript libraries to load. This is a good place to put them.

Where to get information on d3.js

D3 has made huge advances in providing an extensible and practical framework for manipulating data as web objects. At the same time there has been significant increase in information available for people to use it. The following is a far from exhaustive list of sources, but from my own experience it represents a useful subset of knowledge.

d3js.org

d3js.org would be the first port of call for people wanting to know something about d3.js.

From the overview on the main page you can access a dizzying array of examples that have been provided by the founder of d3 (Mike Bostock) and a host of additional developers, artists, coders and anyone who has something to add to the sum knowledge of cool things that can be done with d3.

There is a link to a documentation page that serves as a portal to the ever important API reference, contributed tutorials and other valuable links (some of which I will mention in paragraphs ahead).

The last major link is to the Github repository where you can download d3.js itself.

It is difficult to overstate the volume of available information that can be accessed from d3js.org. It stands alone as the one location that anyone interested in D3 should visit.

Google Groups

There is a Google Group dedicated to discussions on d3.js.

In theory this forum is for discussions on topics including visualization design, API design, requesting new features, etc. With a specific direction made in the main header that “If you want help using D3, please use the d3.js tag on Stack Overflow!”.

In practice however, it would appear that a sizeable proportion of the posts there are technical assistance requests of one type or another. Having said that this means that if you’re having a problem, there could already be a solution posted there. However, if at all possible the intention is certainly that people use Stack Overflow, so this should be the first port of call for those types of inquiry.

So, by all means add this group as a favourite and this will provide you with the opportunity to receive emailed summaries of postings or just an opportunity to easily browse recent goings-on.

Stack Overflow

Stack Overflow is a question and answer site whose stated desire is “to build a library of detailed answers to every question about programming”. Ambitious. So how are they doing? Actually really well. Stack overflow is a fantastic place to get help and information. It’s also a great place to help people out if you have some knowledge on a topic.

They have a funny scheme for rewarding users that encourages providing good answers based on readers voting. It’s a great example of gamification working well. If you want to know a little more about how it works, check out this page; http://stackoverflow.com/about.

They have a d3.js tag (http://stackoverflow.com/questions/tagged/d3.js) and like Google Groups there is a running list of different topics that are an excellent source of information.

Github

Github is predominantly a code repository and version control site. It is highly regarded for its technical acumen and provides a fantastic service that is broadly used for many purposes. Not the least of which is hosting the code (and the wiki) for d3.js.

Whilst not strictly a site that specialises in providing a Q & A function, there is a significant number of repositories (825 at last count) which mention d3.js. With the help from an astute search phrase, there is potentially a solution to be found there.

The other associated feature of Github is Gist. Gist is a pastebin service (a place where you can copy and past code) that can provide a ‘wiki like’ feature for individual repositories and web pages that can be edited through a Git repository. Gist plays a role in providing the hub for the bl.ocks.org example hosting service set up by Mike Bostock.

For a new user, Github / Gist can be slightly daunting. It’s an area where you almost need to know what’s going on to know before you dive in. This is certainly true if you want to make use of its incredible features that are available for hosting code. However, if you want to browse other peoples code it’s an easier introduction. Have a look through what’s available and if you feel so inclined, I recommend that you learn enough to use their service. It’s time well spent.

bl.ocks.org

bl.ocks.org is a viewer for code examples which are hosted on Gist. You are able to load your code into Gist, and then from bl.ocks.org you can view them.

This is a really great way for people to provide examples of their work and there are many who do. However, it’s slightly tricky to know what is there. There is a current project being championed by Christophe Viau and others to provide better access to a range of D3 documentation. The early indications are that it will provide a fantastic method of accessing examples and information. Watch that space.

I would describe the process of getting your own code hosted and displaying as something that will be slightly challenging for people who are not familiar with Github / Gist, but again, in terms of visibility of the code and providing an external hosting solution, it is excellent and well worth the time to get to grips with.

Twitter

Twitter provides a great alerting service to inform a large disparate group of people about stuff.

It’s certainly a great way to keep in touch on an hour by hour basis with people who are involved with d3.js and this can be accomplished in a couple of ways. First, find as many people from the various D3 sites around the web who you consider to be influential in areas you want to follow (different aspects such as development, practical output, educational etc) and follow them. Even better, I found it useful to find a small subset who I considered to be influential people and I noted who they followed. It’s a bit ‘stalky’ if you’re unfamiliar with it, but the end result should be a useful collection of people with something useful to say.

Books

There are only a couple of books that have been released so far on d3.js.

There is “Getting Started with D3” by Mike Dewar (O’Reilly Media, June 2012). This will take you through a good set of exercises to develop your D3 skills and is accompanied by downloadable examples.

There is “Interactive Data Visualization for the Web” by Scott Murray, (O’Reilly Media, November 2012). Currently this has only been released as an ebook, but is scheduled to be released in print form in 2013. The book is based on his great set of on-line tutorials (http://alignedleft.com/tutorials/).

Of course, there is the original paper that launched D3 “D3: Data-Driven Documents” by Michael Bostock, Vadim Ogievetsky and Jeffrey Heer (IEEE Trans. Visualization & Comp. Graphics (Proc. InfoVis), 2011)

Starting with a basic graph

I’ll start by providing the full code for a simple graph and then we can go through it piece by piece (The full code for this example is also in the appendices as ‘Simple Graph’.).

Here’s what the basic graph looks like;

Basic Graph
Basic Graph

And here’s the code that makes it happen;

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

body { font: 12px Arial;}

path { 
    stroke: steelblue;
    stroke-width: 2;
    fill: none;
}

.axis path,
.axis line {
    fill: none;
    stroke: grey;
    stroke-width: 1;
    shape-rendering: crispEdges;
}

</style>
<body>

<!-- load the d3.js library -->    
<script src="http://d3js.org/d3.v3.min.js"></script>

<script>

// Set the dimensions of the canvas / graph
var margin = {top: 30, right: 20, bottom: 30, left: 50},
    width = 600 - margin.left - margin.right,
    height = 270 - margin.top - margin.bottom;

// Parse the date / time
var parseDate = d3.time.format("%d-%b-%y").parse;

// Set the ranges
var x = d3.time.scale().range([0, width]);
var y = d3.scale.linear().range([height, 0]);

// Define the axes
var xAxis = d3.svg.axis().scale(x)
    .orient("bottom").ticks(5);

var yAxis = d3.svg.axis().scale(y)
    .orient("left").ticks(5);

// Define the line
var valueline = d3.svg.line()
    .x(function(d) { return x(d.date); })
    .y(function(d) { return y(d.close); });
    
// Adds the svg canvas
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("data/data.csv", function(error, data) {
    data.forEach(function(d) {
        d.date = parseDate(d.date);
        d.close = +d.close;
    });

    // Scale the range of the data
    x.domain(d3.extent(data, function(d) { return d.date; }));
    y.domain([0, d3.max(data, function(d) { return d.close; })]);

    // Add the valueline path.
    svg.append("path")
        .attr("class", "line")
        .attr("d", valueline(data));

    // Add the X Axis
    svg.append("g")
        .attr("class", "x axis")
        .attr("transform", "translate(0," + height + ")")
        .call(xAxis);

    // Add the Y Axis
    svg.append("g")
        .attr("class", "y axis")
        .call(yAxis);

});

</script>
</body>

The full code for this example can be found on github, in the appendices of this book or in the code samples bundled with this book (simple-graph.html and data.csv). A live example can be found on bl.ocks.org

Once we’ve finished explaining these parts, we’ll start looking at what we need to add in and adjust so that we can incorporate other useful functions that are completely reusable in other diagrams as well.

The end point being something hideous like the following;

Graph with lots of 'tricks' incorperated
Graph with lots of ‘tricks’ incorperated

I say hideous since the graph is not intended to win any beauty prizes, but there are several components to it which some people may find useful (gridlines, area fill, axis label, drop shadow for text, title, text formatting).

So, we can break the file down into component parts. I’m going to play kind of fast and loose here, but never fear, it’ll all make sense.

HTML

Here’s the HTML portions of the code;

<!DOCTYPE html>
<meta charset="utf-8">
<style>

    The CSS is in here

</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>

<script>

    The D3 JavaScript code is here

</script>
</body>

Compare it with the full code. It kind of looks like a wrapping for the CSS and JavaScript. You can see that it really doesn’t boil down to much at all (that doesn’t mean it’s not important).

There are plenty of good options for adding additional HTML stuff into this very basic part for the file, but for what we’re going to be doing, we really don’t need to bother too much.

One thing probably worth mentioning is the line;

<script src="http://d3js.org/d3.v3.min.js"></script>

That’s the line that identifies the file that needs to be loaded to get D3 up and running. In this case the file is sourced from the official d3.js repository on the internet (that way we are using the most up to date version). The D3 file is actually called d3.v3.min.js which may come as a bit of a surprise. That tells us that this is version 3 of the d3.js file (the v3 part) which is an indication that it is separate from the v2 release, which was superseded in late 2012. The other point to note is that this version of d3.js is the minimised version (hence min). This means that any extraneous information has been removed from the file to make it quicker to load.

Later when doing things like implementing integration with bootstrap (a pretty layout framework) we will be doing a great deal more, but for now, that’s the basics done.

The two parts that we left out are the CSS and the D3 JavaScript.

CSS

The CSS is as follows;

body { font: 12px Arial;}

path { 
    stroke: steelblue;
    stroke-width: 2;
    fill: none;
}

.axis path,
.axis line {
    fill: none;
    stroke: grey;
    stroke-width: 1;
    shape-rendering: crispEdges;
}

Cascading Style Sheets give you control over the look / feel / presentation of web content. The idea is to define a set of properties to objects in the web page.

They are made up of ‘rules’. Each rule has a ‘selector’ and a ‘declaration’ and each declaration has a property and a value (or a group of properties and values).

For instance in the example code for this web page we have the following rule;

body { font: 12px Arial;} 

body is the selector. This tells you that on the web page, this rule will apply to the ‘body’ of the page. This actually applies to all the portions of the web page that are contained in the ‘body’ portion of the HTML code (everything between <body> and </body> in the HTML bit). { font: 12px Arial;} is the declaration portion of the rule. It only has the one declaration which is the bit that is in between the curly braces. So font: 12px Arial; is the declaration. The property is font: and the value is 12px Arial;. This tells the web page that the font that appears in the body of the web page will be in 12 px Arial.

Sure enough if we look at the axes of the graph…

x Axis with 12px Arial
x Axis with 12px Arial

We see that the font might actually be 12px Arial!

Let’s try a test. I will change the Rule to the following;

body { font: 16px Arial;}

and the result is…

x Axis with 16px Arial
x Axis with 16px Arial

Ahh…. 16px of goodness!

And now we change it to…

body { font: 16px times;}

and we get…

x Axis with Times font
x Axis with Times font

Hmm… Times font…. I think we can safely say that this has had the desired effect.

So what else is there?

What about the bit that’s like;

path { 
    stroke: steelblue;
    stroke-width: 2;
    fill: none;
}

Well, the whole thing is one rule, ‘path’ is the selector. In this case, ‘path’ is referring to a line in the D3 drawing nomenclature.

For that selector there are three declarations. They give values for the properties of ‘stroke’ (in this case colour), ‘stroke-width’ (the width of the line) and ‘fill’ (we can fill a path with a block of colour).

So let’s change things :-)

path { 
    stroke: red;
    stroke-width: 5;
    fill: yes;
}
Filling of a path
Filling of a path

Wow! The line is now red, it looks about 5 pixels wide and it’s tried to fill the area (roughly defined by the curve) with a black colour.

It ain’t pretty, but it certainly did change. In fact if we go;

    fill: blue;

We’ll get…

Filling of a path with added blue!
Filling of a path with added blue!

So the ‘fill’ property looks pretty flexible. And so does CSS.

D3 JavaScript

The D3 JavaScript part of the code is as follows;

var margin = {top: 30, right: 20, bottom: 30, left: 50},
    width = 600 - margin.left - margin.right,
    height = 270 - margin.top - margin.bottom;

var parseDate = d3.time.format("%d-%b-%y").parse;

var x = d3.time.scale().range([0, width]);
var y = d3.scale.linear().range([height, 0]);

var xAxis = d3.svg.axis().scale(x)
    .orient("bottom").ticks(5);

var yAxis = d3.svg.axis().scale(y)
    .orient("left").ticks(5);

var valueline = d3.svg.line()
    .x(function(d) { return x(d.date); })
    .y(function(d) { return y(d.close); });
    
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("data.csv", function(error, data) {
    data.forEach(function(d) {
        d.date = parseDate(d.date);
        d.close = +d.close;
    });

    // Scale the range of the data
    x.domain(d3.extent(data, function(d) { return d.date; }));
    y.domain([0, d3.max(data, function(d) { return d.close; })]);

    svg.append("path")		// Add the valueline path.
        .attr("class", "line")
        .attr("d", valueline(data));

    svg.append("g")			// Add the X Axis
        .attr("class", "x axis")
        .attr("transform", "translate(0," + height + ")")
        .call(xAxis);

    svg.append("g")			// Add the Y Axis
        .attr("class", "y axis")
        .call(yAxis);

});

Again there’s quite a bit of detail in the code, but it’s not so long that you can’t work out what’s doing what.

Let’s examine the blocks bit by bit to get a feel for it.

Setting up the margins and the graph area.

The part of the code responsible for defining the canvas (or the area where the graph and associated bits and pieces is placed ) is this part.

var margin = {top: 30, right: 20, bottom: 30, left: 50},
    width = 600 - margin.left - margin.right,
    height = 270 - margin.top - margin.bottom;

This is really (really) well explained on Mike Bostock’s page on margin conventions here http://bl.ocks.org/3019563, but at the risk of confusing you here’s my crude take on it.

The first line defines the four margins which surround the block where the graph (as an object) is positioned.

var margin = {top: 30, right: 20, bottom: 30, left: 50},

So there will be a border of 30 pixels at the top, 20 at the right and 30 and 50 at the bottom and left respectively. Now the cool thing about how these are set up is that they use an array to define everything. That means if you want to do calculations in the JavaScript later, you don’t need to put the numbers in, you just use the variable that has been set up. In this case margin.right = 20!

So when we go to the next line;

    width = 600 - margin.left - margin.right,

the width of the inner block of the canvas where the graph will be drawn is 600 pixels – margin.left – margin.right or 600-50-20 or 530 pixels wide. Of course now you have another variable ‘width’ that we can use later in the code.

Obviously the same treatment is given to height.

Another cool thing about all of this is that just because you appear to have defined separate areas for the graph and the margins, the whole area in there is available for use. It just makes it really useful to have areas designated for the axis labels and graph labels without having to juggle them and the graph proper at the same time.

So, let’s have a play and change some values.

var margin = {top: 80, right: 20, bottom: 80, left: 50},
    width = 400 - margin.left - margin.right,
    height = 270 - margin.top  margin.bottom;
The effect of changing the margins
The effect of changing the margins

Here we’ve made the graph narrower (400 pixels) but retained the left / right margins and increased the top bottom margins while maintaining the overall height of the canvas. The really cool thing that you can tell from this is that while we shrank the dimensions of the area that we had to draw the graph in, it was still able to dynamically adapt the axes and line to fit properly. That is the really cool part of this whole business. D3 is running in the background looking after the drawing of the objects, while you get to concentrate on how the data looks without too much maths!

Getting the Data

We’re going to jump forward a little bit here to the bit of the JavaScript code that loads the data for the graph.

I’m going to go out of the sequence of the code here, because if you know what the data is that you’re using, it will make explaining some of the other functions that are coming up much easier.

The section that grabs the data is this bit.

d3.csv("data.csv", function(error, data) {
    data.forEach(function(d) {
        d.date = parseDate(d.date);
        d.close = +d.close;
    });

In fact it’s a combination of a few bits and another piece that isn’t shown!, But let’s take it one step at a time :-)

There’s lots of different ways that we can get data into our web page to turn into graphics. And the method that you’ll want to use will probably depend more on the format that the data is in than the mechanism you want to use for importing.

For instance, if it’s only a few points of data we could include the information directly in the JavaScript.

That would make it look something like;

var data = [
    {date:"1-May-12",close:"58.13"},
    {date:"30-Apr-12",close:"53.98"},
    {date:"27-Apr-12",close:"67.00"},
    {date:"26-Apr-12",close:"89.70"},
    {date:"25-Apr-12",close:"99.00"}
];

The format of the data shown above is called JSON (JavaScript Object Notation) and it’s a great way to include data since it’s easy for humans to read what’s in there and it’s easy for computers to parse the data out. For a brief overview of JSON there is a separate section in the “Assorted Tips and Tricks Chapter” that may assist.

But if you’ve got a fair bit of data or if the data you want to include is dynamic and could be changing from one moment to the next, you’ll want to load it from an external source. That’s when we call on D3’s ‘Request’ functions.

The different types of data that can be requested by D3 are;

  • text: A plain old piece of text that has options to be encoded in a particular way (see the D3 API).
  • json: This is the afore mentioned JavaScript Object Notation.
  • xml: Extensible Markup Language is a language that is widely used for encoding documents in a human readable forrm.
  • html: HyperText Markup Language is the language used for displaying web pages.
  • csv: Comma Separated Values is a widely used format for storing data where plain text information is separated by (wait for it) commas.
  • tsv: Tab Separated Values is a widely used format for storing data where plain text information is separated by a tab-stop character.

Details on these ingestion methods and the formats for the requests are well explained on the D3 Wiki page. In this particular script we will look at the csv request method.

Back to our request…

d3.csv("data.csv", function(error, data) {
    data.forEach(function(d) {
        d.date = parseDate(d.date);
        d.close = +d.close;
    });

The first line of that piece of code invokes the d3.csv request (d3.csv) and then the function is pointed to the data file that should be loaded (data.csv). This is referred to as the ‘url’ (unique resource locator) of the file. In this case the file is stored locally (in the same directory as the simple-graph.html file), but the url could just as easily point to a file somewhere on the Internet.

The format of the data in the data.csv file looks a bit like this;

date,close
1-May-12,58.13
30-Apr-12,53.98
27-Apr-12,67.00
26-Apr-12,89.70
25-Apr-12,99.00

(although the file is longer (about 26 data points)). The ‘date’ and the ‘close’ heading labels are separated by a comma as are each subsequent date and number. Hence the ‘comma separated values’ :-).

The next part is part of the coolness of JavaScript. With the request made and the file requested, the script is told to carry out a function on the data (which will now be called ‘data’).

function(error, data) {

There are actually more things that get acted on as part of the function call, but the one we will consider here is contained in the following lines;

    data.forEach(function(d) {
        d.date = parseDate(d.date);
        d.close = +d.close;
    });

This block of code simply ensures that all the numeric values that are pulled out of the csv file are set and formatted correctly. The first line sets the data variable that is being dealt with (called slightly confusingly ‘data’) and tells the block of code that, for each group within the ‘data’ array it should carry out a function on it. That function is designated ‘d’.

    data.forEach(function(d) { 

The information in the array can be considered as being stored in rows. Each row consists of two values: one value for ‘date’ and another value for ‘close’.

The function is pulling out values of ‘date’ and ‘close’ one row at a time.

Each time it gets a value of ‘date’ and ‘close’ it carries out the following operations;

        d.date = parseDate(d.date);

For this specific value of date being looked at (d.date), d3.js changes it into a date format that is processed via a separate function ‘parseDate’. (The ‘parseDate’ function is defined in a separate part of the script, and we will examine that later.) For the moment, be satisfied that it takes the raw date information from the csv file in a specific row and converts it into a format that D3 can then process. That value is then re-saved in the same variable space.

The next line then sets the ‘close’ variable to a numeric value (if it isn’t already) using the ‘+’ operator.

        d.close = +d.close;

So, at the end of that section of code, we have gone out and picked up a file with data in it of a particular type (comma separated values) and ensured that it is formatted in a way that the rest of the script can use correctly.

Now, the astute amongst you will have noticed that in the first line of that block of code (d3.csv("data.csv", function(error, data) {) we opened a normal bracket ( ( ) and a curly bracket ( { ), but we never closed them. That’s because they stay open until the very end of the file. That means that all those blocks that occur after the d3.csv bit are referenced to the ‘data’ array. Or put another way, it uses ‘data’ to draw stuff!

But anyway, let’s get back to figuring what the code is doing by jumping back to the end of the margins block.

Formatting the Date / Time.

One of the glorious things about the World is that we all do things a bit differently. One of those things is how we refer to dates and time.

In my neck of the woods, it’s customary to write the date as day - month – year. E.g 23-12-2012. But in the United States the more common format would be 12-23-2012. Likewise, the data may be in formats that name the months or weekdays (E.g. January, Tuesday) or combine dates and time together (E.g. 2012-12-23 15:45:32). So, if we were to attempt to try to load in some data and to try and get D3 to recognise it as date / time information, we really need to tell it what format the date / time is in.

Time for a little demonstration (see what I did there).

We will change our data.csv file so that it only includes two points. The first one and the last one with a separation of a month and a bit.

date,close
1-May-12,58.13
26-Mar-12,606.98

The graph now looks like this;

Simple line graph
Simple line graph

Nothing too surprising here, a very simple graph (note the time scale on the x axis).

Now we will change the later date in the data.csv file so that it is a lot closer to the starting date;

date,close
29-Mar-12,58.13
26-Mar-12,606.98

So, just a three day difference. Let’s see what happens.

Simple line graph over three days
Simple line graph over three days

Ahh…. Not only did we not have to make any changes to our JavaScript code, but it was able to recognise the dates were closer and fill in the intervening gaps with appropriate time / day values. Now, one more time for giggles.

This time we’ll stretch the interval out by a few years.

date,close
29-Mar-21,58.13
26-Mar-12,606.98

and the result is…

Simple line graph over several years
Simple line graph over several years

Hopefully that’s enough encouragement to impress upon you that formatting the time is a REALLY good thing to get right. Trust me, it will never fail to impress :-).

Back to formatting.

The line in the JavaScript that parses the time is the following;

var parseDate = d3.time.format("%d-%b-%y").parse;

This line is used when the data.forEach(function(d) portion of the code (that we looked at a couple of pages back) used d.date = parseDate(d.date) as a way to take a date in a specific format and to get it recognised by D3. In effect it said “take this value that is supposedly a date and make it into a value I can work with”.

The function used is the d3.time.format(specifier) function where the specifier in this case is the mysterious combination of characters %d-%b-%y. The good news is that these are just a combination of directives specific for the type of date we are presenting.

The % signs are used as prefixes to each separate format type and the ‘-’ (minus) signs are literals for the actual ‘-’ (minus) signs that appear in the date to be parsed.

The d refers to a zero-padded day of the month as a decimal number [01,31].

The b refers to an abbreviated month name.

And the y refers to the year (without the centuries) as a decimal number.

If we look at a subset of the data from the data.csv file we see that indeed, the dates therein are formatted in this way.

1-May-12,58.13
30-Apr-12,53.98
27-Apr-12,67.00
26-Apr-12,89.70
25-Apr-12,99.00

That’s all well and good, but what if your data isn’t formatted exactly like that?

Good news. There are multiple different formatters for different ways of telling time and you get to pick and choose which one you want. Check out the Time Formatting page on the D3 Wiki for a the authoritative list and some great detail, but the following is the list of currently available formatters (from the d3 wiki);

  • %a - abbreviated weekday name.
  • %A - full weekday name.
  • %b - abbreviated month name.
  • %B - full month name.
  • %c - date and time, as “%a %b %e %H:%M:%S %Y”.
  • %d - zero-padded day of the month as a decimal number [01,31].
  • %e - space-padded day of the month as a decimal number [ 1,31].
  • %H - hour (24-hour clock) as a decimal number [00,23].
  • %I - hour (12-hour clock) as a decimal number [01,12].
  • %j - day of the year as a decimal number [001,366].
  • %m - month as a decimal number [01,12].
  • %M - minute as a decimal number [00,59].
  • %p - either AM or PM.
  • %S - second as a decimal number [00,61].
  • %U - week number of the year (Sunday as the first day of the week) as a decimal number [00,53].
  • %w - weekday as a decimal number [0(Sunday),6].
  • %W - week number of the year (Monday as the first day of the week) as a decimal number [00,53].
  • %x - date, as “%m/%d/%y”.
  • %X - time, as “%H:%M:%S”.
  • %y - year without century as a decimal number [00,99].
  • %Y - year with century as a decimal number.
  • %Z - time zone offset, such as “-0700”.
  • There is also a a literal “%” character that can be presented by using double % signs.

As an example, if you wanted to input date / time formatted as a generic MySQL ‘YYYY-MM-DD HH:MM:SS’ TIMESTAMP format the D3 parse script would look like;

parseDate = d3.time.format("%Y-%m-%d %H:%M:%S").parse;

Setting Scales Domains and Ranges

This is another example where, if you set it up right, D3 will look after you forever.

From our basic web page we have now moved to the section that includes the following lines;

var x = d3.time.scale().range([0, width]);
var y = d3.scale.linear().range([height, 0]);

The purpose of these portions of the script is to ensure that the data we ingest fits onto our graph correctly. Since we have two different types of data (date/time and numeric values) they need to be treated separately (but they do essentially the same job). To examine this whole concept of scales, domains and ranges properly, we will also move slightly out of sequence and (in conjunction with the earlier scale statements) take a look at the lines of script that occur later and set the domain. They are as follows;

    x.domain(d3.extent(data, function(d) { return d.date; }));
    y.domain([0, d3.max(data, function(d) { return d.close; })]);

The idea of scaling is to take the values of data that we have and to fit them into the space we have available.

If we have data that goes from 53.98 to 636.23 (as the data we have for ‘close’ in our csv file does), but we have a graph that is 210 pixels high (height = 270 - margin.top – margin.bottom;) we clearly need to make an adjustment.

Not only that. Even though our data goes from 53.98 to 636.23, that would look slightly misleading on the graph and it should really go from 0 to a bit over 636.23. It sound’s really complicated, but let’s simple it up a bit.

First we make sure that any quantity we specify on the x axis fits onto our graph.

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

Here we set our variable that will tell D3 where to draw something on the x axis. By using the d3.time.scale() function we make sure that D3 knows to treat the values as date / time entities (with all their ingrained peculiarities). Then we specify the range that those values will cover (.range) and we specify the range as being from 0 to the width of our graphing area (See! Setting those variables for margins and widths are starting to pay off now!).

Then we do the same for the Y axis.

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

There’s a different function call (d3.scale.linear()) but the .range setting is still there. In the interests of drawing a (semi) pretty picture to try and explain, hopefully this will assist;

Scaling the data to the graph size
Scaling the data to the graph size

I know, I know, it’s a little misleading because nowhere have we actually said to D3 this is our data from 53.98 to 636.23. All we’ve said is when we get the data, we’ll be scaling it into this space.

Now hang on, what’s going on with the [height, 0] part in y axis scale statement? The astute amongst you will note that for the time scale we set the range as [0, width] but for this one ([height, 0]) the values look backwards.

Well spotted.

This is all to do with how the screen is laid out and referenced. Take a look at the following diagram showing how the coordinates for drawing on your screen work;

Coordinates that the browser expects
Coordinates that the browser expects

The top left hand of the screen is the origin or 0,0 point and as we go left or down the corresponding x and y values increase to the full values defined by height and width.

That’s good enough for the time values on the x axis that will start at lower values and increase, but for the values on the y axis we’re trying to go against the flow. We want the low values to be at the bottom and the high values to be at the top.

No problem. We just tell D3 via the statement y = d3.scale.linear().range([height, 0]); that the larger values (height) are at the low end of the screen (at the top) and the low values are at the bottom (as you most probably will have guessed by this stage, the .range statement uses the format .range([closer_to_the_origin, further_from_the_origin]). So when we put the height variable first, that is now associated at the top of the screen.

Coordinates with adjusted ranges
Coordinates with adjusted ranges

We’ve scaled our data to the graph size and ensured that the range of values is set appropriately. What’s with the domain part that was in this section’s title?

Come on, you remember this little piece of script don’t you?

    x.domain(d3.extent(data, function(d) { return d.date; }));
    y.domain([0, d3.max(data, function(d) { return d.close; })]);

While it exists in a separate part of the file from the scale / range part, it is certainly linked.

That’s because there’s something missing from what we have been describing so far with the set up of the data ranges for the graphs. We haven’t actually told D3 what the range of the data is. That’s also the reason this part of the script occurs where it does. It is within the portion where the data.csv file has been loaded as ‘data’ and it’s therefore ready to use it.

So, the .domain function is designed to let D3 know what the scope of the data will be. This is what is then passed to the scale function.

Looking at the first part that is setting up the x axis values, it is saying that the domain for the x axis values will be determined by the d3.extent function which in turn is acting on a separate function which looks through all the ‘date’ values that occur in the ‘data’ array. In this case the .extent function returns the minimum and maximum value in the given array.

  • function(d) { return d.date; } returns all the ‘date’ values in ‘data’. This is then passed to…
  • The .extent function that finds the maximum and minimum values in the array and then…
  • The .domain function which returns those maximum and minimum values to D3 as the range for the x axis.

Pretty neat really. At first you might think it was overly complex, but breaking the function down into these components allows additional functionality with differing scales, values and quantities. In short, don’t sweat it. It’s a good thing.

The x axis values are dates; so the domain for them is basically from the 26th of March 2012 till 1st of May 2012. The y axis is done slightly differently

    y.domain([0, d3.max(data, function(d) { return d.close; })]);

Because the range of values desired on the y axis goes from 0 to the maximum in the data range, that’s exactly what we tell D3. The ‘0’ in the .domain function is the starting point and the finishing point is found by employing a separate function that sorts through all the ‘close’ values in the ‘data’ array and returns the largest one. Therefore the domain is from 0 to 636.23.

Let’s try a small experiment. Let’s change the y axis domain to use the .extent function (the same way the x axis does) to see what it produces.

The JavaScript for the y domain will be;

    y.domain(d3.extent(data, function(d) { return d.close; }));

You can see apart from a quick copy paste of the internals, all I had to change was the reference to ‘close’ rather than ‘date’.

And the result is…

Graph using .extent for data values
Graph using .extent for data values

Look at that! The starting point for the y axis looks like it’s pretty much on the 53.98 mark and the graph itself certainly touches the x axis where the data would indicate it should.

Now, I’m not really advocating making a graph like this since I think it looks a bit nasty (and a casual observer might be fooled into thinking that the x axis was at 0). However, this would be a useful thing to do if the data was concentrated in a narrow range of values that are quite distant from zero.

For instance, if I change the data.csv file so that the values are represented like the following;

Concentrated data range graph
Concentrated data range graph

Then it kind of loses the ability to distinguish between values around the median of the data.

But, if I put in our magic .extent function for the y axis and redraw the graph…

Expanded concentrated data range using .extent
Expanded concentrated data range using .extent

How about that?

The same data as the previous graph, but with one simple piece of the script changed and D3 takes care of the details.

Setting up the Axes

Now we come to our next piece of code;

var xAxis = d3.svg.axis().scale(x)
    .orient("bottom").ticks(5);

var yAxis = d3.svg.axis().scale(y)
    .orient("left").ticks(5);

I’ve included both the x and y axes because they carry out the formatting in very similar ways. It’s worth noting that this is not the point where the axes get drawn. That occurs later in the piece where the data.csv file has been loaded as ‘data’.

D3 has it’s own axis component that aims to take the fuss out of setting up and displaying the axes. So it includes a number of configurable options.

Looking first at the x axis;

var xAxis = d3.svg.axis().scale(x)
    .orient("bottom").ticks(5);

The axis function is called with d3.svg.axis(). Then the scale is set using the x values that we set up in the scales, ranges and domains section using .scale(x). Then a curious thing happens, we tell the graph to orientate itself to the bottom of the graph .orient("bottom"). If I tell you that “bottom” is the default setting, then you could be forgiven for thinking that technically, we don’t need to specify this since it will go there anyway, but it does give us an opportunity to change it to "top" to see what happens;

x axis orientated to top
x axis orientated to top

Well, I hope you didn’t see that coming, because I didn’t. It transpires that what we’re talking about is the orientation of the values and ticks about the axis line itself. Ahh… Ok. Useful if your x axis is at the top of your graph, but for this one? Not so useful.

The next part (.ticks(5)) sets the number of ticks on the axis. Hopefully you just did a quick count across the bottom of the previous graph and went “Yep, five ticks. Spot on”. Well done if you did, but there’s a little bit of a sneaky trick up D3’s sleeve with the number of ticks on a graph axis.

For instance, here’s what the graph looks like when the .ticks(5) value is changed to .ticks(4).

Five ticks on the x axis
Five ticks on the x axis

Eh? Hang on. Isn’t that some kind of mistake? There are still five ticks. Yep, sure is! But wait… we can keep dropping the ticks value till we get to two and it will still be the same. At .ticks(2) though, we finally see a change.

Two ticks on the x axis
Two ticks on the x axis

How about that? At first glance that just doesn’t seem right, then you have a bit of a think about it and you go “Hmm… When there were 5 ticks, they were separated by a week each, and that stayed that way till we got to a point where it could show a separation of a month.”.

D3 is making a command decision for you as to how your ticks should be best displayed. This is great for simple graphs and indeed for the vast majority of graphs. Like all things related to D3, if you really need to do something bespoke, it will let you if you understand enough code.

The following is the list of time intervals that D3 will consider when setting automatic ticks on a time based axis;

  • 1-, 5-, 15and 30-second.
  • 1-, 5-, 15and 30-minute.
  • 1-, 3-, 6and 12-hour.
  • 1 and 2-day.
  • 1-week.
  • 1 and 3-month.
  • 1-year.

Just for giggles have a think about what value of ticks you will need to increase to until you get D3 to show more than five ticks.

Hopefully you won’t sneak a glance at the following graph before you come up with the right answer.

Ten ticks on the x axis
Ten ticks on the x axis

Yikes! The answer is 10! And then when it does, the number of ticks is so great that they jumble all over each other. Not looking to good there. However, you could rotate the text (or perhaps slant it) and it could still fit in (that must be the topic of a future how-to). You could also make the graph longer if you wanted, but of course that is probably going to create other layout problems. Try to think about your data and presentation as a single entity.

The code that formats the y axis is pretty similar;

var yAxis = d3.svg.axis().scale(y)
    .orient("left").ticks(5);

We can change the orientation to "right" if we want, but it won’t be winning any beauty prizes.

y axis right orientated
y axis right orientated

Nope. Not a pretty sight.

What about the number of ticks? Well this scale is quite different to the x axis. Formatting the dates using logical separators (weeks, months) was tricky, but with standard numbers, it should be a little easier. In fact, there’s a fair chance that you’ve already had a look at the y axis and seen that there are 6 ticks there when the script is asking for 5 :-)

We can lower the tick number to 4 and we get a logical result.

Three ticks on the y axis
Three ticks on the y axis

We need to raise the count to 10 before we get more than 6.

Ten ticks on the y axis
Ten ticks on the y axis

Adding data to the line function

We’re getting towards the end of our journey through the script now. The next step is to get the information from the array ‘data’ and to place it in a new array that consists of a set of coordinates that we are going to plot.

var valueline = d3.svg.line()
    .x(function(d) { return x(d.date); })
    .y(function(d) { return y(d.close); });

I’m aware that the statement above may be somewhat ambiguous. You would be justified in thinking that we already had the data stored and ready to go. But that’s not strictly correct.

What we have is data in a raw format, we have added pieces of code that will allow the data to be adjusted for scale and range to fit in the area that we want to draw, but we haven’t actually taken our raw data and adjusted it for our desired coordinates. That’s what the code above does.

The main function that gets used here is the d3.svg.line() function. This function uses accessor functions to store the appropriate information in the right area and in the case above they use the x and y accessors (that would be the bits that are .x and .y). The d3.svg.line() function is called a ‘path generator’ and this is an indication that it can carry out some pretty clever things on its own accord. But in essence its job is to assign a set of coordinates in a form that can be used to draw a line.

Each time this line function is called on, it will go through the data and will assign coordinates to ‘date’ and ‘close’ pairs using the ‘x’ and ‘y’ functions that we set up earlier (which of course are responsible for scaling and setting the correct range / domain).

Of course, it doesn’t get the data all by itself, we still need to actually call the valueline function with ‘data’ as the source to act on. But never fear, that’s coming up soon.

Adding the SVG Canvas.

As the title states, the next piece of script forms and adds the canvas that D3 will then use to draw on.

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 + ")");

So what exactly does that all mean?

Well D3 needs to be able to have a space defined for it to draw things. When you define the space it’s going to use, you can also give the space you’re going to use an identifying name and attributes.

In the example we’re using here, we are ‘appending’ an SVG element (a canvas that we are going to draw things on) to the <body> element of the HTML page.

We also add an element ‘g’ that is referenced to the top left corner of the actual graph area on the canvas. ‘g’ is actually a grouping element in the sense that it is normally used for grouping together several related elements. So in this case those grouped elements will have a common reference.

Canvas and margins
Canvas and margins

(the image above is definitely not to scale, but I hope you get the general idea)

Interesting things to note about the code. The .attr(“stuff in here”) parts are attributes of the appended elements they are part of.

For instance;

    .append("svg")
        .attr("width", width + margin.left + margin.right)
        .attr("height", height + margin.top + margin.bottom)

tells us that the ‘svg’ element has a “width” of width + margin.left + margin.right and the “height” of height + margin.top + margin.bottom.

Likewise…

    .append("g")
        .attr("transform", 
              "translate(" + margin.left + "," + margin.top + ")");

tells us that the element “g” has been transformed by moving(translating) to the point margin.left, margin.top. Or to the top left of the graph space proper. This way when we tell something to be drawn on our canvas, we can use the reference point “g” to make sure everything is in the right place.

Actually Drawing Something!

Up until now we have spent a lot of time defining, loading and setting up. Good news! We’re about to finally draw something!

We jump lightly over some of the code that we have already explained and land on the part that draws the line.

    svg.append("path")		     // Add the valueline path.
        .attr("d", valueline(data));

This area occurs in the part of the code that has the data loaded and ready for action.

The svg.append("path") portion adds a new path element . A path element represents a shape that can be manipulated in lots of different ways (see more here: http://www.w3.org/TR/SVG/paths.html). In this case it inherits the ‘path’ styles from the CSS section and on the following line (.attr("d", valueline(data));) we add the attribute “d”.

This is an attributer that stands for ‘path data’ and sure enough the valueline(data) portion of the script passes the ‘valueline’ array (with its x and y coordinates) to the path element. This then creates a svg element which is a path going from one set of ‘valueline’ coordinates to another.

Then we get to draw in the axes;

    svg.append("g")                // Add the X Axis
        .attr("class", "x axis")
        .attr("transform", "translate(0," + height + ")")
        .call(xAxis);

    svg.append("g")               // Add the Y Axis
        .attr("class", "y axis")
        .call(yAxis);

We have covered the formatting of the axis components earlier. So this part is actually just about getting those components drawn onto our canvas.

So both axes start by being appended to the “g” group. Then each has its own classes applied for styling via CSS. If you recall from earlier, they look a little like this;

.axis path,
.axis line {
    fill: none;
    stroke: grey;
    stroke-width: 1;
    shape-rendering: crispEdges;
}

Feel free to mess about with these to change the appearance of your axes.

On the x axis, we have a transform statement (.attr("transform", "translate(0," + height + ")")). If you recall, our point of origin for drawing is in the top left hand corner. Therefore if we want our x axis to be on the bottom of the graph, we need to move (transform) it to the bottom by a set amount. The set amount in this case is the height of the graph proper (height). So, for the point of demonstration we will remove the transform line and see what happens;

x axis transformed to the top of the graph
x axis transformed to the top of the graph

Yep, pretty much as anticipated.

The last part of the two sections of script ( .call(xAxis); and .call(yAxis); ) call the x and y axis functions and initiate the drawing action.

Wrap Up

Well that’s it. In theory, you should now be a complete D3 ninja.

OK, perhaps a slight exaggeration. In fact there is a strong possibility that the information I have laid out here is at best borderline useful and at worst laden with evil practices and gross inaccuracies.

But look on the bright side. Irrespective of the nastiness of the way that any of it was accomplished or the inelegance of the code, if the picture drawn on the screen is pretty, you can walk away with a smile. :-)

This section concludes a very basic description of one type of a graphic that can be built with D3. We will look at adding value to it in subsequent chapters.

I’ve said it before and I’ll say it again. This is not a how-to for learning D3. This is how I have managed to muddle through in a bumbling way to try and achieve what I wanted to do. If some small part of it helps you. All good. Those with a smattering of knowledge of any of the topics I have butchered above (or below) are fully justified in feeling a large degree of righteous indignation. To those I say, please feel free to amend where practical and possible, but please bear in mind this was written from the point of view of someone with no experience in the topic and therefore try to keep any instructions at a level where a new entrant can step in.

Things you can do with the basic graph

The following headings in this section are intended to be a list of relatively simple ‘block’ type improvements that you can do to your graph to add functionality. The idea is to be able to use the simple graph that was used for the explanation of how D3 worked and just slot in code to add functionality (let’s hope it works for you :-)).

I have included the full code for a graph that includes rotated axis label, title, grid lines and filled area as an appendix (Graph with Many Features) for those who would prefer to see the code as a block.

The full code for this example can also be found on github or in the code samples bundled with this book (graph-with-many-features.html and data.csv). A live example can be found on bl.ocks.org

Adding Axis Labels

What’s the first thing you get told at school when drawing a graph?

“Always label your axes!”

So, time to add a couple of labels!

First things first (because they’re done slightly differently), the x axis. If we begin by describing what we want to achieve, it may make the process of implementing a solution a little more logical.

What we want to do is to add a simple piece of text under the x axis and in the centre of the total span. Wow, that does sound easy.

And it is, but there are different ways of accomplishing it, and I think I should take an opportunity to demonstrate them. Especially since one of those ways is a BAD idea. Lets start with the bad idea first :-).

This is the code we’re going to add to the simple line graph script;

    svg.append("text")             // text label for the x axis
        .attr("x", 265 )
        .attr("y",  240 )
        .style("text-anchor", "middle")
        .text("Date");

We will put it in between the blocks of script that add the x axis and the y axis.

    svg.append("g")              // Add the X Axis
        .attr("class", "x axis")
        .attr("transform", "translate(0," + height + ")")
        .call(xAxis);

    //	PUT THE NEW CODE HERE!

    svg.append("g")             // Add the Y Axis
        .attr("class", "y axis")
        .call(yAxis);

Before we describe what’s happening, let’s take a look at the result;

Date label on x axis
Date label on x axis

Well, it certainly did what it was asked to do. There’s a ‘Date’ label as advertised! (Yes, I know it’s not pretty.) Let’s describe the code and then work out why there’s a better way to do it.

    svg.append("text")                // text label for the x axis
        .attr("x", 265 )
        .attr("y", 240 )
        .style("text-anchor", "middle")
        .text("Date");

The first line appends a “text” element to our canvas. There is a lot more to learn about “text” elements at the home of the World Wide Web Consortium (W3C). The next two lines ( .attr("x", 265 ) and .attr("y", 240 ) ) set the attributes for the x and y coordinates to position the text on the canvas.

The second last line (.style("text-anchor", "middle")) ensures that the text ‘style’ is such that the text is centre aligned and therefore remains nicely centred on the x,y coordinates that we send it to.

The final line (.text("Date");) adds the actual text that we are going to place.

That seems really simple and effective and it is. However, the bad part about it is that we have hard coded the location for the date into the code. This means if we change any of the physical aspects of the graph, we will end up having to re-calculate and edit our code. And we don’t want to do that.

Here’s an example. If I decide that I would prefer to increase the height of the graph by editing the line here;

    height = 270 - margin.top - margin.bottom;

and making the height 350 pixels;

    height = 350 - margin.top - margin.bottom;

The result is as follows;

Hard coded Date label
Hard coded Date label

EVERYTHING about the graph has adjusted itself, except our nasty, hard coded ‘Date’ label. This is far from ideal and can be easily fixed by using the variables that we set up ever so carefully earlier.

So, instead of;

        .attr("x", 265 )
        .attr("y", 240 )

lets let our variables do the walking and use;

        .attr("x", width / 2 )
        .attr("y",  height + margin.bottom)

So with this code we tell the script that the ‘Date’ label will always be halfway across the width of the graph (no matter how wide it is) and at the bottom of the graph with respect to it’s height and the bottom margin (remember it uses a coordinates system that increases from the top down).

The end result of using variables is that if I go to an extreme of changing the height and width of my graph to;

    width = 400 - margin.left - margin.right,
    height = 200 - margin.top - margin.bottom;

We still finish up with an acceptable result;

Auto adjusting Date label
Auto adjusting Date label

Well, for the label position at least :-).

So the changes to using variables is just a useful lesson that variables rock and mean that you don’t have to worry about your graph staying in relative shape while you change the dimensions. The astute readers amongst you will have learned this lesson very early on in your programming careers, but it’s never a bad idea to make sure that users that are unfamiliar with the concept have an indicator of why it’s a good idea.

Now the third method that I mentioned at the start of our x axis odyssey. This is not mentioned because it’s any better or worse way to implement your script (The reason that I say this is because I’m not sure if it’s better or worse.) but because it’s sufficiently different to make it look confusing if you didn’t think of it in the first place.

So, we’ll take our marvellous coordinates code;

    .attr("x", width / 2 )
    .attr("y",  height + margin.bottom)

And replace it with a single (longer) line;

    .attr("transform",
          "translate(" + (width/2) + " ," + 
                         (height+margin.bottom) + ")")

This uses the "transform" attribute to move (translate) the point to place the ‘Date’ label to exactly the same spot that we’ve been using for the other two examples (using variables of course).

So, that’s the x axis label. Time to do the y axis. The code we’re going to use looks like this;

    svg.append("text")
        .attr("transform", "rotate(-90)")
        .attr("y", 0  margin.left)
        .attr("x",0 - (height / 2))
        .attr("dy", "1em")
        .style("text-anchor", "middle")
        .text("Value");

For the sake of neatness we will put the piece of code in a nice logical spot and this would be following the block of code that added the y axis (but before the closing curly bracket)

    svg.append("g")			// Add the Y Axis
        .attr("class", "y axis")
        .call(yAxis);

    //	PUT THE NEW CODE HERE!

});

And the result looks like this;

y axis label with rotation!
y axis label with rotation!

There we go, a label for the y axis that is nicely centred and (gasp!) rotated by 90 degrees! Woah, does the leetness never end! (No. No it does not.)

So, how do we get to this incredible result?

The first thing we do is the same as for the x axis and append a text element to our canvas (svg.append("text")).

Then things get interesting.

        .attr("transform", "rotate(-90)")

Because that line rotates everything by -90 degrees. While it’s obvious that the text label ‘Value’ has been rotated by -90 degrees (from the picture), the following lines of code show that we also rotated our reference point (which can be a little confusing).

        .attr("y", 0  margin.left)
        .attr("x",0 - (height / 2))

Let’s get graphical to illustrate how this works;

Reference point pre-rotation
Reference point pre-rotation

Here’s our starting position, with x,y in the 0,0 coordinate of the graph drawing area surrounded by the margins.

When we apply a -90 degrees transform we get the equivalent of this;

Reference point after rotation
Reference point after rotation

Here the 0,0 coordinate has been shifted by -90 degrees and the x,y designations are flipped so that we now need to tell the script that we’re moving a ‘y’ coordinate when we would have otherwise been moving ‘x’.

Hence, when the script runs…

        .attr("y", 0  margin.left)

… we can see that this is moving the x position to the left from the new 0 coordinate by the margin.left value.

Likewise when the script runs…

        .attr("x",0 - (height / 2))

… this is actually moving the y position from the new 0 coordinate halfway up the height of the graph area.

Right, we’re not quite done yet. The following line has the effect of shifting the text slightly to the right.

        .attr("dy", "1em")

Firstly the reason we do this is that our previous translation of coordinates means that when we place our text label it sits exactly on the line of 0 – margin.left. But in this case that takes the text to the other side of the line, so it actually sits just outside the boundary of the overall canvas.

The "dy" attribute is another coordinate adjustment move, but this time a relative adjustment and the “1em” is a unit of measure that equals exactly one unit of the currently specified text point size. So what ends up happening is that the ‘Value’ label gets shifted to the right by exactly the height of the text, which neatly places it exactly on the edge of the canvas.

The two final lines of this part of the script are the same as for the x axis. They make sure the reference point is aligned to the centre of the text (.style("text-anchor", "middle")) and then it prints the text (.text("Value");). There, that wasn’t too painful.

The astute amongst you (and I’m picking that if you’re reading this far into the book, you will definitely qualify as astute or at least suckers for punishment) will notice that the example that I have included in the appendices and online does not have the ‘Value’ label. Instead it has the text ‘Price ($)’and it appears on the area of the graph itself! Well spotted. The technique used above is identical to the one used in the description here, but in the example code we have taken an additional step of demonstrating how to place a white shadowy background under the text (more of that to come in a couple of sections!).

How to add a title to your graph

If you’ve read through the adding the axis labels section most of this will come as no surprise.

What we want to do to add a title to the graph is to add a text element (just a few words) that will appear above the graph and centred left to right.

The code block we will use will looks like this;

    svg.append("text")
        .attr("x", (width / 2))				
        .attr("y", 0 - (margin.top / 2))
        .attr("text-anchor", "middle")	
        .style("font-size", "16px") 
        .style("text-decoration", "underline") 	
        .text("Value vs Date Graph");

And the end result will look like this;

Basic graph with title
Basic graph with title

A nice logical place to put the block of code would be towards the end of the JavaScript. In fact I would put it as the last element we add. So here;

    svg.append("g")              // Add the Y Axis
        .attr("class", "y axis")
        .call(yAxis);

    // PUT THE NEW CODE HERE!

});

Now since the vast majority of the code for this block is a regurgitation of the axis labels code, I don’t want to revisit that and bloat up this document even more, so I will direct you back to that section if you need to refresh yourself on any particular line. But….. There are a couple of new ones in there which could benefit from a little explanation.

Both of them are style descriptors and as such their job is to apply a very specific style to this element.

        .style("font-size", "16px") 
        .style("text-decoration", "underline") 	

What they do is pretty self explanatory. Make the text a specific size and underline it. But what is perhaps slightly more interesting is that we have this declaration in the JavaScript code and not in the CSS portion of the file.

Smoothing out graph lines

When you draw a line graph, what you’re doing is taking two (or more) sets of coordinates and connecting them with a line (or lines). I know that sounds simplistic, but bear with me. When you connect these points, you’re telling the viewer of the graph that in between the individual points, you expect the value to vary in keeping with the points that the line passes through. So in a way, you’re trying to interpret the change in values that are not shown.

Now this is not strictly true for all graph types, but it does hold for a lot of line graphs.

So… when connecting these known coordinates together, you want to make the best estimate of how the values would be represented. In this respect, sometimes a straight line between points is not the best representation.

For instance. Earlier, when demonstrating the extent function for graphing we showed a graph of the varying values with the y axis showing a narrow range.

Expanded values for a narrow range
Expanded values for a narrow range

The resulting variation of the graph shows a fair amount of extremes and you could be forgiven for thinking that if this represented a smoothly flowing analog system of some kind then some of those sharp peaks and troughs would not be a true representation of how the system or figures varied.

So how should it look? Ahh… The $64,000 question. I don’t know :-). You will have a better idea since you are the person who will know your data best. However, what I do know is that D3 has some tricks up its sleeve to help.

We can easily change what we see above into;

Smoothing using "basis"
Smoothing using “basis”

How about that? And the massive amount of code required to carry out what must be a ridiculously difficult set of calculations?

    .interpolate("basis")	

So where does this neat piece of code go? Here;

var valueline = d3.svg.line()
    .interpolate("basis")	 		// <=== THERE IT IS!
    .x(function(d) { return x(d.date); })
    .y(function(d) { return y(d.close); });

So is that it? Nooooo…….. There’s more! This is one form of interpolation effect that can be applied to your data, but there is a range and depending on your data you can select the one that is appropriate.

Here’s the list of available options and for more about them head on over to the D3 wiki and look for ‘line.interpolate’.

  • linear – Normal line (jagged).
  • step-before – a stepping graph alternating between vertical and horizontal segments.
  • step-after - a stepping graph alternating between horizontal and vertical segments.
  • basis - a B-spline, with control point duplication on the ends (that’s the one above).
  • basis-open - an open B-spline; may not intersect the start or end.
  • basis-closed - a closed B-spline, with the start and the end closed in a loop.
  • bundle - equivalent to basis, except a separate tension parameter is used to straighten the spline. This could be really cool with varying tension.
  • cardinal - a Cardinal spline, with control point duplication on the ends. It looks slightly more ‘jagged’ than basis.
  • cardinal-open - an open Cardinal spline; may not intersect the start or end, but will intersect other control points. So kind of shorter than ‘cardinal’.
  • cardinal-closed - a closed Cardinal spline, looped back on itself.
  • monotone - cubic interpolation that makes the graph only slightly smoother.

Because in the course of writing this I took an opportunity to play with each of them, I was pleasantly surprised to see some of the effects and it seems like a shame to deprive the reader of the same joy :-). So at the risk of deforesting the planet (so I hope you are reading this in electronic format) here is each of the above interpolation types applied to the same data.

This is also an opportunity to add some reader feedback awesomeness. Many thanks to ‘enjalot’ for the great suggestion to plot the points of the data as separate circles on the graphs. Since the process of interpolation has the effect of ‘interpreting’ the trends of the data to the extent that in some cases, the lines don’t intersect the actual data much at all.

Each of the following shows the smoothing curve and the data that is used to plot the graph.

Smoothing using "linear"
Smoothing using “linear”
Smoothing using "step-before"
Smoothing using “step-before”
Smoothing using "step-after"
Smoothing using “step-after”
Smoothing using "basis"
Smoothing using “basis”
Smoothing using "basis-open"
Smoothing using “basis-open”
Smoothing using "basis-closed"
Smoothing using “basis-closed”
Smoothing using "bundle"
Smoothing using “bundle”
Smoothing using "cardinal"
Smoothing using “cardinal”
Smoothing using "cardinal-open"
Smoothing using “cardinal-open”
Smoothing using "cardinal-closed"
Smoothing using “cardinal-closed”
Smoothing using "monotone"
Smoothing using “monotone”

Just in case you’re in the mood for another example, here are voronoi tessellations drawn with various d3 line interpolators (the original interactive version by ‘shawnbot’ can be found here).

First a version using the linear interpolation when each of the points is joined faithfully with a straight line.

Polygon Smoothing using "linear"
Polygon Smoothing using “linear”

Now a version where the polygons are formed with the ‘basis-closed’ interpolator (note how the lines don’t go through the points that describe the bounds of the polygons/blobs).

Polygon Smoothing using "basis-closed"
Polygon Smoothing using “basis-closed”

And lastly, using the ‘cardinal-closed’ interpolator, while the line travels through each point in the polygon, they overshoot in an effort to maintain a nice curve and the resulting polygon/blobs overlap.

Polygon Smoothing using "cardinal-closed"
Polygon Smoothing using “cardinal-closed”

So, over to you to decide which format of interpolation is going to suit your data best:-).

Adding grid lines to a graph

Grid lines are an important feature for some graphs as they allow the eye to associate three analogue scales (the x and y axis and the displayed line).

There is currently a tendency to use graphs without grid lines online as it gives the appearance of a ‘cleaner’ interface, but they are still widely used and a necessary component for graphing.

This is what we’re going to draw;

Basic graph with gridlines
Basic graph with gridlines

How to build grid lines?

We’re going to use the axis function to generate two more axis elements (one for x and one for y) but for these ones instead of drawing the main lines and the labels, we’re just going to draw the tick lines. Really long ticklines (I’m considering calling them long cat lines).

To create them we have to add in 3 separate blocks of code.

  1. One in the CSS section to define what style the grid lines will have.
  2. One to define the functions that generate the grid lines. And…
  3. One to draw the lines.

The grid line CSS

This is the total styling that we need to add for the tick lines;

.grid .tick {
    stroke: lightgrey;
    stroke-opacity: 0.7;
    shape-rendering: crispEdges;
}
.grid path {
          stroke-width: 0;
}

Just add this block of code at the end of the current CSS that is in the simple graph template (just before the </style> tag).

The CSS here is done in two parts.

The first portion sets the line colour (stroke), the opacity (transparency) of the lines and make sure that the lines are narrow (crispEdges).

    stroke: lightgrey;
    stroke-opacity: 0.7;
    shape-rendering: crispEdges;

The colour is pretty standard, but in using the opacity style we give ourselves the opportunity to use a good shade of colour (if grey actually is a colour) and to juggle the degree to which it stands out a little better.

The second part is the stroke width.

    stroke-width: 0;

Now it might seem a little weird to be setting the stroke width to zero, but if you don’t (and we remove the style) this is what happens;

Axis lines made too thick
Axis lines made too thick

If you look closely (compare with the previous picture if necessary) the main lines for the axis have turned thicker. The stroke width style is obviously adding in new (thicker) axis lines and we’re not interested in them at the moment. Therefore, if we set the stroke width to zero, we get rid of the problem.

Define the grid line functions

We will need to define two functions to generate the grid lines and they look a little like this;

function make_x_axis() {		
    return d3.svg.axis()
        .scale(x)
        .orient("bottom")
        .ticks(5)
}

function make_y_axis() {		
    return d3.svg.axis()
        .scale(y)
        .orient("left")
        .ticks(5)
}

Each function will carry out it’s configuration when called from the later part of the script (the drawing part).

A good spot to place the code is just before we load the data with the d3.csv

        //   <== Put the functions here!
// Get the data
d3.csv("data.csv", function(error, data) {
    data.forEach(function(d) {
        d.date = parseDate(d.date);
        d.close = +d.close;
    });ticks(5)
}

Both functions are almost identical. They give the function a name (make_x_axis and make_y_axis) which will be used later when the piece of code that draws the lines calls out to them.

Both functions also show which parameters will be fed back to the drawing process when called. Both make sure they use the d3.svg.axis function and then they set individual attributes which make sense.

They make sure they’ve got the right axis (.scale(x) and .scale(y)). They set the orientation of the axes to match the incumbent axes (.orient("bottom") and .orient("left")). And they set the number of ticks to match the number of ticks in the main axis (.ticks(5) and .ticks(5)). You have the opportunity here to do something slightly different if you want. For instance, think back to when we were setting up the axis for the basic graph and we messed about, seeing how many ticks we could get to appear. If we increase the number of ticks that appear in the grid (lets say to .ticks(30) and .ticks(10))) we get the following;

Grid lines with greater divisions
Grid lines with greater divisions

So the grid lines can now show divisions of 50 on the y axis and per day on the x axis :-)

Draw the lines

The final block of code we need is the bit that draws the lines.

    svg.append("g")			
        .attr("class", "grid")
        .attr("transform", "translate(0," + height + ")")
        .call(make_x_axis()
            .tickSize(-height, 0, 0)
            .tickFormat("")
        )

    svg.append("g")			
        .attr("class", "grid")
        .call(make_y_axis()
            .tickSize(-width, 0, 0)
            .tickFormat("")
        )

The first two lines of both the x and y axis grid lines code above should be pretty familiar by now. The first one appends the element to be drawn to the group “g”. the second line (.attr("class", "grid")) makes sure that the style information set out in the CSS is applied.

The x axis grid lines portion makes a slight deviation from conformity here to adjust its positioning to take into account the coordinates system .attr("transform", "translate(0," + height + ")").

Then both portions call their respective make axis functions (.call(make_x_axis() and .call(make_y_axis()).

Now comes the really interesting bit.

What you will see if you go to the D3 API wiki is that for the .tickSize function, the following is the format.

axis.tickSize([major[[, minor], end]])

That tells us that you get to specify the size of the ticks on the axes, by the major ticks, the minor ticks and the end ticks (that is to say the lines on the very end of the graph which, in the case of the example we are looking at, aren’t there!).

So in our example we are setting our major ticks to a length that corresponds to the full height or width of the graph. Which of course means that they extend across the graph and have the appearance of grid lines! What a neat trick.

Something I haven’t done before is to see what would happen if I included the tick lines for the minor and end ticks. So here we go :-)

Disappointment! Where did I go wrong?
Disappointment! Where did I go wrong?

Darn! Disappointment. We can see a minor tick line for the y axis, but nothing for the x axis and nothing on the ends. Clearly I will have to run some experiments to see what’s going on there (later).

The last thing that is included in the code to draw the grid lines is the instruction to suppress printing any label for the ticks;

        .tickFormat("")

After all, that would become a bit confusing to have two sets of labels. Even if one was on top of the other. They do tend to become obvious if that occurs (they kind of bulk out a bit like bold text).

And that’s it. Grid lines!

Make a dashed line

Dashed lines totally rock!

One of the best parts about it is that they’re so simple to do!

Literally one line!!!!

So lets imagine that we want to make the line on our simple graph dashed. All we have to do is insert the following line in our JavaScript code here;

    svg.append("path")
        .attr("class", "line")
        .style("stroke-dasharray", ("3, 3"))  // <== This line here!!
        .attr("d", valueline(data));

And our graph ends up like this;

Dashed line for the basic graph
Dashed line for the basic graph

Hey! It’s dashtastic!

So how does it work?

Well, obviously "stroke-dasharray" is a style for the path element, but the magic is in the numbers.

Essentially they describe the on length and off length of the line. So "3, 3" translates to 3 pixels (or whatever they are) on and 3 pixels off. Then it repeats. Simple eh?

So, experiment time :-)

What would the following represent?

“5, 5, 5, 5, 5, 5, 10, 5, 10, 5, 10, 5”

Try not to cheat…

Dashed lines for fun
Dashed lines for fun

Ahh yes, Mr. Morse would be proud.

And you can put them anywhere. Here’s our axes perverted with dashes;

    svg.append("g")
        .attr("class", "x axis")
        .attr("transform", "translate(0," + height + ")")
        .style("stroke-dasharray", ("3, 3"))
        .call(xAxis);

    svg.append("g")
        .attr("class", "y axis")
        .style("stroke-dasharray", ("3, 3"))
        .call(yAxis);
When dashed lines go bad
When dashed lines go bad

Well… I suppose you can have too much of a good thing. With great power comes great responsibility. Use your dash skills wisely and only for good.

Filling an area under the graph

Lines are all very well and good, but that’s not the whole story for graphs. Sometimes you’ve just got to go with a fill.

Filling an area with a solid colour isn’t too hard. I mean we did it by mistake back a few pages when we were trying to draw a line.

But to do it in a nice coherent way is fairly straight forward.

It takes three sections of code in much the same way that we drew our grid lines earlier;

  1. One in the CSS section to define what style the area will have.
  2. One to define the functions that generate the area. And…
  3. One to draw the area.

The end result will looks a bit like this;

Basic graph with an area fill
Basic graph with an area fill

CSS for an area fill

This is pretty straight forward and only consists of two rules;

.area {
    fill: lightsteelblue;
    stroke-width: 0;
}

Put them at the bottom of your <style> section.

The first one (fill: lightsteelblue;) sets the colour of our fill (and in this case we have chosen a lighter shade of the same colour as our line to match it) and the second one (stroke-width: 0;) sets the width of the line that surrounds the area to zero. This last rule is kind of important in making a filled area work well. The whole idea is that the graph is made up of separate elements that will compliment each other. There’s the axes, the line and the fill. If we don’t tell the code that there is no line surrounding the filled area, it will assume that there is one and add it in like this.

Line surrounding filled area
Line surrounding filled area

So what has happened here is that the area element has inherited the line property from the path element and surrounding the area is a 2px wide steelblue line. Not too pretty. Let’s not go there.

Define the area function

We need a function that will tell the area what space to fill. This is accessed from the d3.svg.area function

The code that we will use is as follows;

var area = d3.svg.area()
    .x(function(d) { return x(d.date); })
    .y0(height)
    .y1(function(d) { return y(d.close); });

I have placed it in between the axis variable definitions and the line definitions here;

var yAxis = d3.svg.axis().scale(y)
    .orient("left").ticks(5);
                         <==== Put the new code here!
var valueline = d3.svg.line()
    .x(function(d) { return x(d.date); })
    .y(function(d) { return y(d.close); });

So the only changes to the code are the addition of the y0 line and the renaming of the y line y1.

Here’s a picture that might help explain;

How the area is defined
How the area is defined

As should be apparent, the top line (y1) follows the valueline line and the bottom line is at the constant ‘height’ value. Everything in between these lines is what gets filled. The function in this section describes the area.

Draw the area

Now to the money maker.

The final section of code in the area filling odyssey is as follows;

    svg.append("path")
        .datum(data)
        .attr("class", "area")
        .attr("d", area);

We should place this block directly after the domain functions but before the drawing of the valueline path;

    x.domain(d3.extent(data, function(d) { return d.date; }));
    y.domain([0, d3.max(data, function(d) { return d.close; })]);
                                //   <== Area drawing code here!
    svg.append("path")		
        .attr("class", "line")
        .attr("d", valueline(data));

This is actually a pretty good idea to put it there since the various bits and pieces that are drawn in the graph are done so one after the other. This means that the filled area comes first, then the valueline is layered on top and then the axes come last. This is a pretty good sequence since if there are areas where two or more elements overlap, it might cause the graph to look ‘wrong’.

For instance, here is the graph drawn with the area added last.

Area overlaps and obscures
Area overlaps and obscures

You should be able to notice that part of the valueline line has been obscured and the line for the y axis where it coincides with the area is obscured also.

Looking at the code we are adding here, the first line appends a path element (svg.append("path")) much like the script that draws the line.

The second line (.datum(data)) declares the data we will be utilising for describing the area and the third line (.attr("class", "area")) makes sure that the style we apply to it is as defined in the CSS section (under ‘area’).

The final line (.attr("d", area);) declares “d” as the attributer for path data and calls the ‘area’ function to do the drawing.

And that’s it!

Filling an area above the line

Pop Quiz:

How would you go about filling the area ABOVE the graph?

In this instance, you could fill the lower area as has been demonstrated here, and with a small change you can fill another area with a solid colour above another line.

How is this incredible feat achieved?

Well, remember the code that defined the area?

var area = d3.svg.area()
    .x(function(d) { return x(d.date); })
    .y0(height)
    .y1(function(d) { return y(d.close); });

All we have to do is tell it that instead of setting the y0 constant value to the height of the graph (remember, this is the bottom of the graph) we will set it to the constant value that is at the top of the graph. In other words zero (0).

    .y0(0)

That’s it.

Fill an area above a line
Fill an area above a line

Now, I’m not going to go over the process of drawing two lines and filling each in different directions to demonstrate the example I described, but this provides a germ of an idea that you might be able to flesh out :-)

Adding a drop shadow to allow text to stand out on graphics.

I’ve deliberately positioned this particular tip to follow the ‘filling an area’ description because it provides an opportunity to demonstrate the principle to slightly better effect.

There have been several opportunities where I have wanted to place text overlaid on graphs for convenience sake only to have it look overly messy as the text interferes with the graph.

Anyway, what we’ll do is leave the fill in place and place the title back on the graph, but position the title so that it lays on top of the fill like so;

Title lost in the area fill
Title lost in the area fill

The additional code for the title is the following and appears just after the drawing of the axes.

    svg.append("text")
        .attr("x", (width / 2))				
        .attr("y", 25 )
        .attr("text-anchor", "middle")	
        .style("font-size", "16px") 
        .style("text-decoration", "underline") 	
        .text("Value vs Date Graph");

(the only change from the previous title example is the ‘y’ attribute which has been hard coded to 25 to place it inconveniently on the filled area.)

So, what we want to end up with is something like the following…

A nice white drop shadow effect
A nice white drop shadow effect

In my humble opinion, it’s just enough to make the text acceptable :-).

The method that I’ll describe to carry this out is designed so that the drop shadow effect can be applied to any text elements in the graph, not the isolated example that we will use here. In order to implement this marvel of utility we will need to make changes in two areas. One in the CSS where we will define a style for white shadowy backgrounds and the second to draw it.

CSS for white shadowy background

The code to add to the CSS section is as follows;

text.shadow {
    stroke: white;
    stroke-width: 2.5px;
    opacity: 0.9;
}

The first line designates that the style applies to text with a ‘shadow’ label. The stroke is set to white. the width of the line is set to 2.5px and it is made to be slightly see-through. So by setting the line that surrounds the text to be thick, white and see-through gives it a slightly ‘cloudy’ effect. If we remove the black text from over the top we get a slightly better look;

A closer look at just the drop shadow
A closer look at just the drop shadow

Of course if you want to have a play with any of these settings, you should have a go and see what works best for your graph.

Drawing the white shadowy background.

Now that we’ve set the style for our background, we need to draw it in.

The code for this should be extremely familiar;

    svg.append("text")
        .attr("x", (width / 2))				
        .attr("y", 25 )
        .attr("text-anchor", "middle")	
        .style("font-size", "16px") 
        .style("text-decoration", "underline") 	
        .attr("class", "shadow")		// <=== Here's the different line
        .text("Value vs Date Graph");

That’s because it’s identical to the piece of code that was used to draw the title except for the one line that is indicated above. The reason that it’s identical is that what we are doing is placing a white shadow on the graph and then the text on top of it, if it deviated by a significant amount it will just look silly. Of course a slight amount could look effective, in which case adjust the ‘x’ or ‘y’ attributes.

One of the things I pointed out in the previous paragraph was extremely important. That’s the bit that tells you that we needed to place the shadow before we placed the black text. For the same reason that we placed the area fill on first in the area fill example, If black text goes on before the shadow, it will look pretty silly. So place this block of code just before the block that draws the title.

So the line that has been added in is the one that tells D3 that the text that is being drawn will have the white cloudy effect. And at the risk of repeating myself, if you have several text elements that could benefit from this effect, once you have the CSS code in place, all you need to do is duplicate the block that adds the text and add in that single line and voila!

Again the astute amongst you will note that in the example code in the appendices and online, the shadow effect is applied to the Y axis label. Never fear, it’s exactly the same principle :-).

Adding more than one line to a graph

All right, we’re starting to get serious now. Two lines on a graph is a bit of a step into a different world in one respect. I mean that in the sense that there’s more than one way to carry out the task, and I tend to do it one way and not the other mainly because I don’t fully understand the other way :-(.

So, how are we going to do this? I think that the best way will be to make the executive decision that we have suddenly come across more data and that it is also in our data.csv file (which we’ll rename data2.csv just to highlight the difference between the two data sets). In fact it looks a little like this (apologies in advance for the big ugly block of data);

date,close,open
1-May-12,68.13,34.12
30-Apr-12,63.98,45.56
27-Apr-12,67.00,67.89
26-Apr-12,89.70,78.54
25-Apr-12,99.00,89.23
24-Apr-12,130.28,99.23
23-Apr-12,166.70,101.34
20-Apr-12,234.98,122.34
19-Apr-12,345.44,134.56
18-Apr-12,443.34,160.45
17-Apr-12,543.70,180.34
16-Apr-12,580.13,210.23
13-Apr-12,605.23,223.45
12-Apr-12,622.77,201.56
11-Apr-12,626.20,212.67
10-Apr-12,628.44,310.45
9-Apr-12,636.23,350.45
5-Apr-12,633.68,410.23
4-Apr-12,624.31,430.56
3-Apr-12,629.32,460.34
2-Apr-12,618.63,510.34
30-Mar-12,599.55,534.23
29-Mar-12,609.86,578.23
28-Mar-12,617.62,590.12
27-Mar-12,614.48,560.34
26-Mar-12,606.98,580.12

Three columns, date open and close. The first two are exactly what we have been dealing with all along and the last (open) is our new made up data. Each column is separated by a comma (hence .csv (comma separated values)), which is the format we’re currently using to import data.

We should save this as a new file so we don’t mess up our previous data, so (as mentioned earlier) let’s call it data2.csv.

There is a copy of this file and the sample code at github and in the code samples bundled with this book (dual-line-with-labels.html and data2.csv). A live example can be found on bl.ocks.org. The dual-line-with-labels.html file includes the double lines and also a labelling scheme which we will be descriping in a later section.

We will build our new code using our simple graph template to start with, so the immediate consequence of this is that we need to edit the line that was looking for ‘data.csv’ to reflect the new name.

d3.csv("data2.csv", function(error, data) {

So when you browse to our new graph’s html file, we don’t see any changes. It still happily loads the new data, but because it hasn’t been told to do anything with it, nothing new happens.

What we need to do now it to essentially duplicate the code blocks that drew the first line for the second line.

The good news is that in the simplest way possible that’s just two code blocks. The first sets up the function that defines the new line;

var valueline2 = d3.svg.line()
    .x(function(d) { return x(d.date); })
    .y(function(d) { return y(d.open); });

You should notice that this block is identical to the block that sets up the function for the first line, except this one is called (imaginatively) valueline2. We should put it directly after the block that sets up the function for valueline.

The second block draws our new line;

    svg.append("path")          // Add the valueline2 path.
        .attr("d", valueline2(data));

Again, this is identical to the block that draws the first line, except this one is called valueline2. We should put it directly after the block that draws valueline.

After those three small changes, check out your new graph;

Two lines, but the same colour
Two lines, but the same colour

Hey! Two lines! Hmm…. Both being the same colour is a bit confusing. Good news. We can change the colour of the second line by inserting a line that adjusts it’s stroke (colour) very simply.

So here’s what our new drawing block looks like;

    svg.append("path")		// Add the valueline2 path.
        .style("stroke", "red")
        .attr("d", valueline2(data));

And as if by magic, here’s our new graph;

Two lines with two colours
Two lines with two colours

Wow. Right about now, we’re thinking ourselves pretty clever. But there’s two places where we’re not doing things right. We took a simple way, but we took some short cuts that might bite us in the posterior.

The first mistake we made was not ensuring that our variable "d.open" is being treated as a number or a string. We’re fortunate in this case that it is, but this can’t always be assumed. So, this is an easy fix and we just need to put the following (indicated line) in our code;

// Get the data
d3.csv("data.csv", function(error, data) {
    data.forEach(function(d) {
        d.date = parseDate(d.date);
        d.close = +d.close;
        d.open = +d.open;        //  <=== Add this line in!
    });

The second and potentially more fatal flaw is that nowhere in our code do we make allowance for our second set of data (the second line’s values) exceeding our first lines values.

That might not sound too normal straight away, but consider this. What if when we made up our data earlier, some of the new data exceeded our maximum value in our original data? As a means of demonstration, here’s what happens when our second line of data has values higher than the first lines;

Two lines but the domain's not right
Two lines but the domain’s not right

Ahh…. We’re not too clever now.

Good news though, we can fix it!

The problem comes about because when we set the domain for the y axis this is what we put in the code;

y.domain([0,d3.max(data, function(d) {return d.close;})]);

So that only considers d.close when establishing the domain. With d.open exceeding our domain, it just keeps drawing off the graph!

The good news is that ‘Bill’ has provided a solution for just this problem here;

All you need to replace the y.domain line with is this;

y.domain([0, d3.max(data, function(d) { 
    return Math.max(d.close, d.open); })]);

It does much the same thing, but this time it returns the maximum of d.close and d.open (whichever is largest). Good work Bill.

If we put that code into the graph with the higher values for our second line we are now presented with this;

Two lines with everything fitting onto the canvas
Two lines with everything fitting onto the canvas

And it doesn’t matter which of the two sets of data is largest, the graph will always adjust :-)

You will also have noticed that our y axis has auto adjusted again to cope. Clever eh?

Labelling multiple lines on a graph

Our previous example of a graph with multiple lines is a thing of rare beauty, but which line relates to which set of data? We have data that defines values for open and close, but we don’t know which line is which.

In this section we will add labels to our lines so that we know what it what.

This section was inspired by a question from a reader (Arun b.s) of the d3noob.org blog where the question was asked “How can we put text at the end of each line on the graph?”.

The question was so good I realised that it had to be part of the book, so here you go :-).

It’s actually not too difficult. What we are trying to achieve is to find the position of the end of each line and to add a text label at that position so that the association of proximity denotes the linkage. Of course we’re going to go a little further and colour the text so that it’s really clear which label belongs with which line, but you get the idea.

Each line requires a single block of script to add the text. The block that adds the open label is as follows;

svg.append("text")
  .attr("transform", "translate("+(width+3)+","+y(data[0].open)+")")
  .attr("dy", ".35em")
  .attr("text-anchor", "start")
  .style("fill", "red")
  .text("Open");

So firstly it appends a textual element to the svg object;

svg.append("text")

Then it finds the position of the end of the line;

  .attr("transform", "translate("+(width+3)+","+y(data[0].open)+")")

To do this we use the transform and translate attribute and find the x position that equates to the end of the graph plus 3 pixels ((width+3)) (we add in the three pixels to create a small separation between the end of the line and the label). The y position is far more interesting. We need to find the position of the last point in our line for the open data. Because the data is in the form of an indexed array and because the data has the latest date at the start of the array, we only need to find the point at the 0 position of the array. This is data[0].open. But of course, we also need to adjust our data for our scale and range, so we transform it using the y function (in the same way that we do it for the valueline and valueline2 points. So the script to find the point on the screen in the y direction is y(data[0].open).

If our data was arranged with the last date at the end of our data we would have to find the final index point and we would use y(data[data.length-1].open)).

Then it’s just a matter of aligning and justifying our text correctly;

  .attr("dy", ".35em")
  .attr("text-anchor", "start")

Then colouring it the correct colour;

  .style("fill", "red")

And adding out text;

  .text("Open");

We put this block of code after the blocks that add in the axes so that they make sure they’re on top of anything else we draw.

The only other small change we want to make is to change the right margin for the graph that we set at the start of our script from 20 to 40 so that there is enough room to add our label without cutting it off.

After that you have a marvellously labelled multi-line graph!

Multi-line graph with labels
Multi-line graph with labels

The full code for this example can be found on github or in the code samples bundled with this book (dual-line-graph-with-labels.html and data2.csv). A working example can be found on bl.ocks.org.

Now, I’d like to pretend that this is perfection, but it isn’t. If our lines end too close together, the labels will interfere with each other, so in the ideal world I would include a bit of fanciness to prevent that, but for the purposes of this exercise we can consider ourselves happy.

Multiple axes for a graph

Alrighty… Let’s imagine that we want to show our wonderful graph with two lines, much like we already have, but that the data that the lines are made from is significantly different in magnitude from the original data (in the example below, the data for the second line has been reduced by approximately a factor of 10 from our original data).

date,close,open
1-May-12,58.13,3.41
30-Apr-12,53.98,4.55
27-Apr-12,67.00,6.78
26-Apr-12,89.70,7.85
25-Apr-12,99.00,8.92
24-Apr-12,130.28,9.92
23-Apr-12,166.70,10.13
20-Apr-12,234.98,12.23
19-Apr-12,345.44,13.45
18-Apr-12,443.34,16.04
17-Apr-12,543.70,18.03
16-Apr-12,580.13,21.02
13-Apr-12,605.23,22.34
12-Apr-12,622.77,20.15
11-Apr-12,626.20,21.26
10-Apr-12,628.44,31.04
9-Apr-12,636.23,35.04
5-Apr-12,633.68,41.02
4-Apr-12,624.31,43.05
3-Apr-12,629.32,46.03
2-Apr-12,618.63,51.03
30-Mar-12,599.55,53.42
29-Mar-12,609.86,57.82
28-Mar-12,617.62,59.01
27-Mar-12,614.48,56.03
26-Mar-12,606.98,58.01

Now this isn’t a problem in itself. D3 will still make a reasonable graph of the data, but because of the difference in range, the detail of the second line will be lost.

One line is dominating the other
One line is dominating the other

What I’m proposing is that we have a second y axis on the right hand side of the graph that relates to the red line.

The mechanism used is based on the great examples put forward by Ben Christensen here.

The full code for this example can be found on github or in the code samples bundled with this book (dual-line-dual-axes.html and data2a.csv). A working example can be found on bl.ocks.org.

First things first, there won’t be space on the right hand side of our graph to show the extra axis, so we should make our right hand margin a little larger.

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

I went for 40 and it seems to fit pretty well.

Then (and here’s where the main point of difference for this graph comes in) you want to amend the code to separate out the two scales for the two lines in the graph. This is actually a lot easier than it sounds, since it consists mainly of finding anywhere that mentions y and replacing it with y0 and then adding in a reciprocal piece of code for y1.

Let’s get started.

Firstly, change the variable declaration for y to y0 and add in y1.

var x = d3.time.scale().range([0, width]);
var y0 = d3.scale.linear().range([height, 0]);
var y1 = d3.scale.linear().range([height, 0]);

Then change our yAxis declaration to be specific for y0 and specifically left. And add in a declaration for the right hand axis;

var yAxisLeft = d3.svg.axis().scale(y0)    //  <==  Add in 'Left' and 'y0'
    .orient("left").ticks(5);

var yAxisRight = d3.svg.axis().scale(y1)   // new declaration for 'Right', 'y\
1'
    .orient("right").ticks(5);             // and includes orientation .

Note the orientation change for the right hand axis.

Now change our valueline declarations so that they refer to the y0 and y1 scales.

var valueline = d3.svg.line()
    .x(function(d) { return x(d.date); })
    .y(function(d) { return y0(d.close); });    // <== y0
var valueline2 = d3.svg.line()
    .x(function(d) { return x(d.date); })
    .y(function(d) { return y1(d.open); });     // <== y1

There are a few different ways for the scaling to work, but we’ll stick with the fancy max method we used in the dual line example (although technically it’s not required).

   y0.domain([0, d3.max(data, function(d) { return Math.max(d.close); })]); 
   y1.domain([0, d3.max(data, function(d) { return Math.max(d.open); })]); 

Again, here’s the y0 and y1 changed and added and the maximums for d.close and d.open are separated out). The final piece of the puzzle is to draw the new axis, but we also want to make a slight change to the original y axis. Since we have two lines and two axes, we need to know which belongs to which, so we can colour code the text in the axes to match the lines;

    svg.append("g")
        .attr("class", "y axis")
        .style("fill", "steelblue")
        .call(yAxisLeft);	

    svg.append("g")				
        .attr("class", "y axis")	
        .attr("transform", "translate(" + width + " ,0)")	
        .style("fill", "red")		
        .call(yAxisRight);	

In the above code you can see where we have added in a ‘style’ change for the yAxisLeft to make it ‘steelblue’ and a complementary change in the new section for yAxisRight to make that text red.

The yAxisRight section obviously needs to be added in, but the only significant difference is the transform / translate attribute that moves the axis to the right hand side of the graph.

And after all that, here’s the result…

Two lines with full range of the domain and two axes
Two lines with full range of the domain and two axes

Now, let’s not kid ourselves that it’s a thing of beauty, but we should console our aesthetic concerns with the warm glow of understanding how the function works :-).

How to rotate the text labels for the x Axis.

The observant reader will recall the problem we had observed earlier when increasing the number of ticks on our x axis to 10. The effect had been to produce a large number of x axis ticks (actually 19) but they had run together and become unreadable.

x axis labels crammed together
x axis labels crammed together

We postulated at the time that an answer to the problem might be to rotate the text to provide more space. Well, it’s about time we solved that problem.

The answer I found most usable was provided by Aaron Ward on Google Groups.

The full code for this example can be found on github or in the code samples bundled with this book (simple-graph-rotated-axis-text.html and data.csv). A working example can be found on bl.ocks.org. The example code also includes the formatting of the x axis ticks in a specific format as described in the next section.

Starting out with our simple graph example, we should increase the number of ticks on the x axis to 10 to highlight the problem in the previous image.

The first substantive change would be a little housekeeping. Because we are going to be rotating the text at the bottom of the graph, we are going to need some extra space to fit in our labels. So we should change our bottom margin appropriately.

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

I found that 50 pixels was sufficient.

The remainder of our changes occur in the block that draws the x axis.

    svg.append("g")
        .attr("class", "x axis")
        .attr("transform", "translate(0," + height + ")")
        .call(xAxis)
        .selectAll("text")	
            .style("text-anchor", "end")
            .attr("dx", "-.8em")
            .attr("dy", ".15em")
            .attr("transform", function(d) {
                return "rotate(-65)" 
                });	

It’s pretty standard until the .call(xAxis) portion of the code. Here we remove the semicolon that was there so that the block continues with its function.

Then we select all the text elements that comprise the x axis with the .selectAll("text"). From this point onwards, we are operating on the text elements associated with the x axis. In effect; the following 4 ‘actions’ are applied to the text labels.

The .style("text-anchor", "end") line ensures that the text label has the end of the label ‘attached’ to the axis tick. This has the effect of making sure that the text rotates about the end of the date. This makes sure that the text all ends up at a uniform distance from the axis ticks.

The dx and dy attribute lines move the end of the text just far enough away from the axis tick so that they don’t crowd it and not too far away so that it appears disassociated. This took a little bit of fiddling to ‘look’ right and you will notice that I’ve used the ‘em’ units to get an adjustment if the size of the font differs.

The final action is kind of the money shot.

The transform attribute applies itself to each text label and rotates each line by -65 degrees. I selected -65 degrees just because it looked OK. There was no deeper reason.

The end result then looks like the following;

Rotated x axis labels
Rotated x axis labels

This was a surprisingly difficult problem to find a solution to that I could easily understand (well done Aaron). That makes me think that there are some far deeper mysteries to it that I don’t fully appreciate that could trip this solution up. But in lieu of that, enjoy!

Format a date / time axis with specified values

OK then. We’ve been very clever in rotating our text, but you will notice that D3 has used it’s own good judgement as to what format the days / date will be represented as.

Not that there’s anything wrong with it, but what if we want to put a specific format of date / time nomenclature as axis labels?

No problem. D3 has your back.

This is actually a pretty easy thing to do, but there are plenty of options for the formatting, so the only really tricky part is deciding what to put where.

But, before we start doing anything we are going to have to expand our bottom margin even more than we did with the rotate the axis labels feature.

var margin = {top: 30, right: 40, bottom: 70, left: 50},

That should see us right.

Right, now the simple part :-). Changing the format of the label is as simple as inserting the tickFormat command into the xAxis declaration a little like this;

var xAxis = d3.svg.axis().scale(x)
    .orient("bottom").ticks(10)
    .tickFormat(d3.time.format("%Y-%m-%d")); // insert the tickFormat function

An example using this code can be found on github or in the code samples bundled with this book (simple-graph-rotated-axis-text.html and data.csv). A working example can be found on bl.ocks.org. The example code also includes the rotating of the x axis text as described in the previous section.

What the tickFormat allows is the setting of formatting for the tick labels. The d3.time.format portion of the code is specifying the exact format of those ticks. This formatting is described using the same arguments that were explained in the earlier section on formatting date time values. That means that the examples we see here (%Y-%m-%d) should display the year as a four digit number then a hyphen then the month as a two digit number, then another hyphen, then a two digit number corresponding to the day.

Let’s take a look at the result;

Format change for the x axis labels
Format change for the x axis labels

There we go! You should be able to see this file in the downloads section on d3noob.org with the general examples as formatted-date-time-axis-labels.html.

So how about we try something a little out of the ordinary (extreme)?

How about the full weekday name (%A), the day (%d), the full month name (%B) and the year (%Y) as a four digit number?

    .tickFormat(d3.time.format("%A %d %B %Y"));

We will also need some extra space for the bottom margin, so how about 140?

var margin = {top: 30, right: 40, bottom: 140, left: 50},

and….

Extreme format change for the x axis labels
Extreme format change for the x axis labels

Oh yeah… When axis ticks go bad…

But seriously, that does work as a pretty good example of the flexibility available.

Update data dynamically - On Click

OK, you’re going to enjoy this section. Mainly because it takes the traditional graph that we know, love and have been familiar with since childhood and adds in an aspect that that has been missing for most of your life.

Animation!

Graphs are cool. Seeing information represented in a graphical way allows leaps of understanding that are difficult or impossible to achieve from raw data. But in this crazy ever-changing world, a static image is only as effective as the last update. The ability to being able to have the most recent data represented in your graph and to have it occur automatically provides a new dimension to traditional visualizations.

So what are we going to do?

First we’ll spend a bit of time setting the scene. We’ll add a button to our basic graph file so that we can control when our animation occurs, we’ll generate a new data set so that we can see how the data changes easily, then we’ll shuffle the code about a bit to make it do its magic. While we’re shuffling the code we’ll take a little bit of time to explain what’s going on with various parts of it that are different to what we might have seen thus far. Then we’ll change the graph to update automatically (on a schedule) when the data changes.

The code for this example can be found on github or in the code samples bundled with this book (data-load-button.html, data.csv and data-alt.csv). A working example can be found on bl.ocks.org.

Adding a Button

It’s all well and good animating your data, but if you don’t know when it’s supposed to happen or what should happen, it’s a little difficult to evaluate how successful you’ve been.

To make life easy, we’re going to take some of the mystery out of the equation (don’t worry, we’ll put it back later) and add a button to our graph that will give you control over when your graph should update it’s data. When complete it should look like this;

A graph with a button!
A graph with a button!

To add a button, we will take our simple-graph.html example and just after the <body> tag we add the following code;

<div id="option">
    <input name="updateButton" 
           type="button" 
           value="Update" 
           onclick="updateData()" 
    />
</div>

The HTML <div> element (or HTML Document Division Element) is used to assign a division or section in an HTML document. We use it here as it’s good practice to keep sections of your HTML document distinct so that it’s easier to perform operations them at a later date.

In this case we have given the div the identifier “option” so that we can refer to it later if we need to (embarrassingly, we won’t be referring to it at all, but it’s good practice none the less).

The following line adds our button using the HTML <input> tag. The <input> tag has a wide range of attributes (options) for allowing user input. Check out the links to w3schools and Mozilla for a whole lot of reading.

In our <input> line we have four different attributes;

  • name
  • type
  • value
  • onclick

Each of these attributes modifies the <input> function in some way so that our button does what we want it to do.

name:
This is the name of the control (in this case a button) so that we can reference it in other parts of our HTML script.

type:
Probably the most important attribute for a button, this declares that our type of input will be a button! There are heaps of other options for type which would form a significant section in itself.

value:
For a button input type, this is the starting value for our button and forms the label that our button will have.

onclick:
This is not an attribute that is specific to the <input> function, but it allows the browser to capture a mouse clicking event when it occurs and in our case we tell it to run the updateData() function (which we’ll be seeing more of soon).

Updating the data

To make our first start at demonstrating changing the data, we’ll add another data file to our collection. We’ll name it data-alt.csv (you can find it in the sample code collection that can be downloaded with the book on Leanpub). This file changes our normal data (only the values, not the structure) just enough to see a movement of the time period of the graph and the range of values on the y axis (this will become really obvious in the transition).

Changes to the d3.js code layout

While going through the process of working out how to do this, the iterations of my code were mostly horrifying to behold. However, I think my understanding has improved sufficiently to allow only a slight amendment to our simple-graph.html JavaScript code to get this going.

What we should do is add the following block of code to our script towards the end of the file just before the </script> tag;

function updateData() {

    // Get the data again
    d3.csv("data-alt.csv", function(error, data) {
       	data.forEach(function(d) {
	    	d.date = parseDate(d.date);
	    	d.close = +d.close;
	    });

    	// Scale the range of the data again 
    	x.domain(d3.extent(data, function(d) { return d.date; }));
            y.domain([0, d3.max(data, function(d) { return d.close; })]);

    // Select the section we want to apply our changes to
    var svg = d3.select("body").transition();

    // Make the changes
        svg.select(".line")   // change the line
            .duration(750)
            .attr("d", valueline(data));
        svg.select(".x.axis") // change the x axis
            .duration(750)
            .call(xAxis);
        svg.select(".y.axis") // change the y axis
            .duration(750)
            .call(yAxis);

    });
}

What’s happening in the code?

There are several new concepts and techniques in this block of code for us to go through but we’ll start with the overall wrapper for the block which is a function call.

The entirety of our JavaScript code that we’re adding is a function called updateData. This is the subject of the first line in the code above (and the last closing curly bracket). It is called from the only other piece of code we’ve added to the file which is the button in the HTML section. So when that button is clicked, the updateData function is carried out.

Then we get our new data with the block that starts with d3.csv("data-alt.csv". This is a replica of the block in the main part of the code with one glaring exception. It is getting the data from our new file called data-alt.csv. However, one thing it’s doing that bears explanation is that it’s loading data into an array that we’ve already used to generate our line. At a point not too far from here (probably the next page) we’re going to replace the data that made up our line on the page with the new data that’s just been loaded.

We then set the scale and the range again using the x.domain and y.domain lines. We do this because it’s more than possible that our data has exceeded or shrunk with respect to our original domains so we recalculate them using our new data. The consequence of not doing this would be a graph that could exceed it’s available space or be cramped up.

Then we assign the variable svg to be our selection of the "body" div (which means the following actions will only be carried out on objects within the "body" div.

The other part of that line is the transition command (.transition()). This command goes to the heart of animating dynamic data and visualizations and is a real treasure.

As the name suggests, a transition is a method for moving from one state to another. In its simplest form for a d3.js visualisation, it could mean moving an object from one place to another, or changing an object’s properties such as opacity or colour. In our case, we will take our data which is in the form of a line, and change some of that data. And when we change the data we will get d3 to manage the change via a transition. At the same time (because we’re immensely clever) we will also make sure we change the axes if they need it.

So in short, we’re going to change this…

The initial set of data
The initial set of data

… into this…

'Updated' data
‘Updated’ data

Obviously the line values have changed, and both axes have changed as well. And using a properly managed transition, it will all occur in a smooth ballet :-).

So, looking at the short block that manages the line transition;

        svg.select(".line")   // change the line
            .duration(750)
            .attr("d", valueline(data));

We select the ".line" object and since we’ve already told the script that svg is all about the transition (var svg = d3.select("body").transition();) the attributes that follow specify how the transition for the line will proceed. In this case, the code describes the length of time that the transition will take as 750 milliseconds (.duration(750)) and uses the new data as transcribed by the valueline variable from the original part of the script (.attr("d", valueline(data));).

The same is true for both of the subsequent portions of the code that change the x and y axes. We’ve set both to a transition time of 750 milliseconds, although feel free to change those values (make each one different for an interesting effect).

Other attributes for the transition that we could have introduced would be a delay (.delay(500), perhaps to stagger the movements) and more interestingly an easing attribute (.ease(type[, arguments…])) which will have the effect of changing how the movement of a transition appears (kind of like a fast-slow-fast vs linear, but with lots of variations).

But for us we’ll survive with the defaults.

In theory, you’ve added in your new data file (data-alt.csv) and made the two changes to the simple graph file (the HTML block for the button and the JavaScript one for the updateData function). The result has been a new beginning in your wonderful d3 journey!

Update data dynamically – Automatically

I have no doubt that the excitement of updating your data and graph with the magic of buttons is quite a thrill. But believe it or not, there’s more to come.

In the example we’re going to demonstrate now, there are no buttons to click, the graph will simply update itself when the data changes.

I know, I know. It’s like magic!

So the sort of usage scenario that you would be putting this to is when you had a dashboard type display or a separate window just for the purposes of showing a changing value like a stock ticker or number of widgets sold (where the number was changing frequently).

So, how to create the magic?

Starting with the data-load-button.html file, firstly we should remove the button, so go ahead and delete the button block that we had in the HTML section (the bit that looked like this…).

<div id="option">
    <input name="updateButton" 
                 type="button" 
                value="Update" 
                onclick="updateData()" />
</div>

Now, we have two references in our JavaScript where we load our data. One loads data.csv initially, then when the button was pushed, we loaded data-alt.csv. We’re going to retain that arrangement for the moment, because we want to make sure we can see something happening, but ultimately, we would just have them referencing a single file.

So, the magic piece of script that will do your updating is as follows;

var inter = setInterval(function() {
                updateData();
        }, 5000); 

And we should put that just above the function updateData() { line in our code.

The key to this piece of code is the setInterval function which will execute specified code (in this case it’s updateData(); which will go and read in our new information) over and over again at a set interval (in this case 5000 milliseconds (}, 5000);)).

I honestly wish it was harder, but sadly it’s that simple. You now have in your possession the ability to make your visualizations do stuff on a regular basis, all by themselves!

There is a copy of this sample code and the data files at github and in the code samples bundled with this book (data-load-automatic.html, data.csv and data-alt.csv). A live example can be found on bl.ocks.org.

How to test?

Well, just load up your new file in a browsers. After an interval of 5 seconds, you should see the graph change all by itself. How cool is that?

You know it gets better though…

If you open your data-alt.csv file and change a value (increase one of the close values by a factor of 10 or something equally noticeable). Then save the file. Keep an eye on your graph. Before 5 seconds is up it should have changed to reflect your new data.

Elements, Attributes and Styles

This chapter is intended to provide an overview of some of the simpler things that d3.js can do, but in a way that may help some understand a little more about how images can be added to a web page and how they can be manipulated.

Loosely speaking we will look at how objects (elements (like circles, rectangles, lines and even text) can be declared and added to a page, how their attributes in relation to the page (position, size, shape, actions) can be changed and how their style (colour, width, transparency) can be applied.

As we go through the explanation of different changes that can be applied to different elements there will be a small amount of repetition where there is cross-over with related drawing features. Please be patient :-). The aim is to have each section as complete in its own right as practical.

The Framework

To be able to demonstrate how these three related aspects of drawing objects work we will have to use a small, simple script to draw them in your web browser.

We will just take a moment to explain the script that draws a circle.

Here’s the contents of the file in it’s entirety. I have imaginatively called it circle.html.

<!DOCTYPE html>
<meta charset="utf-8">

<body>

<!-- load the d3.js library -->	
<script type="text/javascript" src="d3/d3.v3.js"></script>

<script>
 
var holder = d3.select("body") // select the 'body' element
      .append("svg")           // append an SVG element to the body
      .attr("width", 449)      // make the SVG element 449 pixels wide
      .attr("height", 249);    // make the SVG element 249 pixels high

// draw a circle
holder.append("circle")        // attach a circle
    .attr("cx", 200)           // position the x-center
    .attr("cy", 100)           // position the y-center
    .attr("r", 50);            // set the radius

</script>

</body>

Please feel free to jump ahead slightly if you understand how a HTML file with JavaScript goes together :-).

The HTML part of the file can be thought of as a wrapper for the JavaScript that will draw our circle. These are the HTML parts here…

<!DOCTYPE html>
<meta charset="utf-8">

<body>

<!-- load the d3.js library -->	
<script type="text/javascript" src="d3/d3.v3.js"></script>

<script>
 
</script>

</body>

This portion of the file is built using HTML ‘tags’. These will set up the environment for the Javascript.

The tags tell the web browser what sort of language is being used and the type of characters used to write the code…

<!DOCTYPE html>
<meta charset="utf-8">

Areas of the code are labelled.

Like the body…

<body>

In this area we can put the stuff that will be 
displayed on our web page.

</body>

And the place where we put the JavaScript…

<script>

Our d3.js code will go here.

</script>

We even load an external file that contains JavaScript that will help run our code.

<!-- load the d3.js library -->	
<script type="text/javascript" src="d3/d3.v3.js"></script>

Yes, that’s the line that loads d3.js. Once it’s loaded we can use the instructions that it makes available to make other JavaScript code (in this case ours) work.

Then we have the JavaScript code that allows us to use the functions made possible by d3.js.

var holder = d3.select("body") // select the 'body' element
      .append("svg")           // append an SVG element to the body
      .attr("width", 449)      // make the SVG element 449 pixels wide
      .attr("height", 249);    // make the SVG element 249 pixels high

// draw a circle
holder.append("circle")        // attach a circle
    .attr("cx", 200)           // position the x-center
    .attr("cy", 100)           // position the y-center
    .attr("r", 50);            // set the radius

I’ve broken the code into two separate portions to provide some clarity to their function. We could make it one block, but that wouldn’t necessarily make it easier to understand.

Firstly we add a ‘holder’ for our graphics on the web page. I’ve named it holder but we could just as easily named it anything we wanted.

var holder = d3.select("body") // select the 'body' element
      .append("svg")           // append an SVG element to the body
      .attr("width", 449)      // make the SVG element 449 pixels wide
      .attr("height", 249);    // make the SVG element 249 pixels high

The first thing we do when declaring our holder is to select the body element of our web page (Remember those <body> tags in the HTML part earlier?).

Then we append a Scalable Vector Graphic (SVG) object to the body and we make it 449 pixels wide and 249 pixels high.

The width and height are ‘attributes’ of the SVG object. That is to say they describe a property of the object.

The second block of our JavaScript finally draws our circle.

holder.append("circle")        // attach a circle
    .attr("cx", 200)           // position the x-center
    .attr("cy", 100)           // position the y-center
    .attr("r", 50);            // set the radius

The first line appends a new element (a circle) to our SVG ‘holder’.

The second and third lines declare the attribute of our circle that specify where the centre of the circle is. In this case it’s at the x/y position 200/100 (cx/cy).

The last line adds the radius attribute r. Here it is set to 50 pixels.

The three attributes cx, cy and r are all required when drawing a circle. There are other attributes we can put in there (and when we look at some of the upcoming elements, you should get a feel for them), but these are the minimum.

The purpose of describing this block of code that draws a circle isn’t to show you how to draw a circle. This has only been a way of showing you how the code in the following sections is laid out and how it works. The elements we are going to generate can be drawn with exactly the same file but with just the section that adds the circle altered.

For example if you were to change this block of code;

holder.append("circle")        // attach a circle
    .attr("cx", 200)           // position the x-center
    .attr("cy", 100)           // position the y-center
    .attr("r", 50);            // set the radius

For this block of code;

holder.append("rect")          // attach a circle
    .attr("x", 150)            // x position of the top-left corner
    .attr("y", 50)             // y position of the top-left corner
    .attr("width", 100)        // set the rectangle width
    .attr("height", 100);      // set the rectangle height

Instead of drawing a circle we would be drawing a rectangle.

So this is what our circle will look like;

Circle
Circle

Because it will help a great deal to have a common frame of reference, I’m going to display the elements on a grid that looks a little like this;

Circle with Grid
Circle with Grid

With the grid in place it’s far easier to see that the centre of our circle is indeed at the coordinates x = 200, y = 100 and that the radius is 50.

The circle is still somewhat plain, but bear with me because as we start to explore what we can do with styles and attributes we can add some variation to our elements.

Fancier Circle
Fancier Circle

With that explanation behind us we should begin our odyssey into the world of d3 elements.

Elements

We will begin by describing what we mean when we talk about an ‘element’.

There is considerable scope for confusion when talking about elements on a web page. Are we talking about HTML elements, SVG elements or something different?

In fact we are going to be describing a subset of SVG elements. Specifically those that are described in the d3.js API reference (since that’s why we’re here right?). These are a collection of common shapes and objects which include circles, ellipses, rectangles, lines, polylines, polygons, text and paths.

“Text?” I hear you say. “Doesn’t sound like a shape.” I suppose it depends on how you think of it. We can use text in different ways in d3, but for this particular exercise we can regard text as an SVG element.

Circle

A circle is a simple SVG shape that is described by three required attributes.

  • cx: The position of the centre of the circle in the x direction (left / right) measured from the left side of the screen.
  • cy: The position of the centre of the circle in the y direction (up / down) measured from the top of the screen.
  • r: The radius of the circle from the cx, cy position to the perimeter of the circle.

The following is an example of the code section required to draw a circle in conjunction with the HTML file outlined at the start of this chapter;

holder.append("circle")        // attach a circle
    .attr("cx", 200)           // position the x-center
    .attr("cy", 100)           // position the y-center
    .attr("r", 50);            // set the radius

This will produce a circle as follows;

Circle
Circle

The centre of the circle is at x = 200 and y = 100 and the radius is 50 pixels.

Ellipse

An ellipse is described by four required attributes;

  • cx: The position of the centre of the ellipse in the x direction (left / right) measured from the left side of the screen.
  • cy: The position of the centre of the ellipse in the y direction (up / down) measured from the top of the screen.
  • rx: The radius of the ellipse in the x dimension from the cx, cy position to the perimeter of the ellipse.
  • ry: The radius of the ellipse in the y dimension from the cx, cy position to the perimeter of the ellipse.

The following is an example of the code section required to draw an ellipse in conjunction with the HTML file outlined at the start of this chapter;

holder.append("ellipse")       // attach an ellipse
    .attr("cx", 200)           // position the x-centre
    .attr("cy", 100)           // position the y-centre
    .attr("rx", 100)           // set the x radius
    .attr("ry", 50);           // set the y radius

This will produce an ellipse as follows;

Ellipse
Ellipse

The centre of the ellipse is at x = 200 and y = 100 and the radius is 50 pixels vertically and 100 pixels horizontally.

Rectangle

A rectangle is described by four required attributes and two optional ones;

  • x: The position on the x axis of the left hand side of the rectangle (required).
  • y: The position on the y axis of the top of the rectangle (required).
  • width: the width (in pixels) of the rectangle (required).
  • height: the height (in pixels) of the rectangle (required).
  • rx: The radius curve of the corner of the rectangle in the x dimension (optional).
  • ry: The radius curve of the corner of the rectangle in the y dimension (optional).

The following is an example of the code section required to draw a rectangle (using only the required attributes) in conjunction with the HTML file outlined at the start of this chapter;

holder.append("rect")       // attach a rectangle
    .attr("x", 100)         // position the left of the rectangle
    .attr("y", 50)          // position the top of the rectangle
    .attr("height", 100)    // set the height
    .attr("width", 200);    // set the width

This will produce a rectangle as follows;

Rectangle
Rectangle

The top left corner of the rectangle is at 100, 50 and the rectangle is 200 pixels wide and 100 pixels high.

The following code section includes the optional attributes for the curved corners;

holder.append("rect")       // attach a rectangle
    .attr("x", 100)         // position the left of the rectangle
    .attr("y", 50)          // position the top of the rectangle
    .attr("height", 100)    // set the height
    .attr("width", 200)     // set the width
    .attr("rx", 10)         // set the x corner curve radius
    .attr("ry", 10);        // set the y corner curve radius

This will produce a rectangle (with curved corners) as follows;

Rectangle with curved corners
Rectangle with curved corners

The corners are curved with radii in the x and y direction of 10 pixels.

Line

A line is a simple line between two points and is described by four required attributes.

  • x1: The x position of the first end of the line as measured from the left of the screen.
  • y1: The y position of the first end of the line as measured from the top of the screen.
  • x2: The x position of the second end of the line as measured from the left of the screen.
  • y2: The y position of the second end of the line as measured from the top of the screen.

The following is an example of the code section required to draw a line in conjunction with the HTML file outlined at the start of this chapter. A notable addition to this code is the style declaration. In this case the line has no colour and this can be added with the stroke style which applies a colour to a line;

holder.append("line")          // attach a line
    .style("stroke", "black")  // colour the line
    .attr("x1", 100)     // x position of the first end of the line
    .attr("y1", 50)      // y position of the first end of the line
    .attr("x2", 300)     // x position of the second end of the line
    .attr("y2", 150);    // y position of the second end of the line

This will produce a line as follows;

Line
Line

The line extends from the point 100,50 to 300,150.

Polyline

A polyline is a sequence of connected lines described with a single attribute. The d3.js wiki rightly makes the point that “it is typically more convenient and flexible to use the d3.svg.line path generator in conjunction with a path element”. So while drawing a polyline using this method may be possible, bear in mind that depending on your application, there may be a better way.

  • points: The points attribute is a list of x,y coordinates that are the locations of the connecting points of the polyline.

The following is an example of the code section required to draw a polyline in conjunction with the HTML file outlined at the start of this chapter. A notable addition to this code are the style declarations. In this case the line of the polyline has no colour and this can be added with the stroke style which applies the colour black to a line. Likewise the area that is bounded by the polyline will be automatically filled with black unless we explicitly tell the object not to. This is achieved in this example by addition of the fill style to none.

holder.append("polyline")      // attach a polyline
    .style("stroke", "black")  // colour the line
    .style("fill", "none")     // remove any fill colour
    .attr("points", "100,50, 200,150, 300,50");  // x,y points

This will produce a polyline as follows;

Polyline
Polyline

The polyline extends from the point 100,50 to 200,150 to 300,50.

Polygon

A polygon is a sequence of connected lines which form a closed shape described with a single attribute. The d3.js wiki rightly makes the point that “it is typically more convenient and flexible to use the d3.svg.line path generator in conjunction with a path element”. So while drawing a polygon using this method may be possible, bear in mind that depending on your application, there may be a better way.

  • points: The points attribute is a list of x,y coordinates that are the locations of the connecting points of the polygon. The last point is in turn connected to the first point.

The following is an example of the code section required to draw a polygon in conjunction with the HTML file outlined at the start of this chapter. A notable addition to this code are the style declarations. In this case the line of the polygon has no colour and this can be added with the stroke style which applies the colour black to a line. Likewise the area that is bounded by the polygon will be automatically filled with black unless we explicitly tell the object not to. This is achieved in this example by addition of the fill style to none.

holder.append("polygon")       // attach a polygon
    .style("stroke", "black")  // colour the line
    .style("fill", "none")     // remove any fill colour
    .attr("points", "100,50, 200,150, 300,50");  // x,y points 

This will produce a polygon as follows;

Polyline
Polyline

The polygon extends from the point 100,50 to 200,150 to 300,50 and then back to 100,50.

Path

A path is an outline of an SVG shape which is described with a ‘mini-language’ inside a single attribute.

  • d: This attribute is a list of instructions that allow a shape to be drawn in a complex way using a ‘mini-language’ of commands. These commands are written in a shorthand of single letters such as M-moveto, Z-closepath, L-lineto, C-curveto. These commands can be absolute (normally designated by capital letters) or relative (lower case).

The following is an example of the code section required to draw a triangle in conjunction with the HTML file outlined at the start of this chapter. A notable addition to this code are the style declarations. In this case the line of the path has no colour and this can be added with the stroke style which applies the colour black to a line. Likewise the area that is bounded by the path will be automatically filled with black unless we explicitly tell the object not to. This is achieved in this example by addition of the fill style to none.

holder.append("path")          // attach a path
    .style("stroke", "black")  // colour the line
    .style("fill", "none")     // remove any fill colour
    .attr("d", "M 100,50, L 200,150, L 300,50 Z");  // path commands 

This will produce a path as follows;

Path
Path

The path mini-language first moves (M) to 100,50 then draws a line (L) to 200,150 then draws another line (L) to 300,50 then closes the path (Z).

Text

A text element is an SVG object which is shaped as text. It is described by two required attributes and three optional ones.

  • x: This attribute designates the anchor point location for the text in the x dimension (required).
  • y: This attribute designates the anchor point location for the text in the y dimension (required).
  • dx: This attribute designates the offset of the text from the anchor point in the x dimension (optional). There are several different sets of units that can be used to designated the offset of the text from an anchor point. These include em which is a scalable unit (used in these examples), px (pixels), pt (points (kind of like pixels)) and 5 (percent (scalable and kind of like em))
  • dy: This attribute designates the offset of the text from the anchor point in the y dimension (optional).
  • text-anchor: This attribute controls the horizontal text alignment (optional). It has three values; start (left aligned), middle (centre aligned) and end (right aligned).

The following is an example of the code section required to draw the text “Hello World” in conjunction with the HTML file outlined at the start of this chapter. A notable addition to this code is the style declaration which applies a black fill to the text. Additionally there is the declaration .text which defines the text that will be displayed.

holder.append("text")         // append text
    .style("fill", "black")   // fill the text with the colour black
    .attr("x", 200)           // set x position of left side of text
    .attr("y", 100)           // set y position of bottom of text 
    .text("Hello World");     // define the text to display 

This will produce text as follows;

Text
Text

It can be seen from the image that the anchor point for the text is at 200,100 and that the text is positioned with this anchor point at the bottom, left of the text.

The following examples will demonstrate the various options for positioning and aligning text so that you can arrange it correctly.

Anchor at the bottom, middle of the text:
holder.append("text")         // append text
    .style("fill", "black")   // fill the text with the colour black
    .attr("x", 200)           // set x position of left side of text
    .attr("y", 100)           // set y position of bottom of text
    .attr("text-anchor", "middle") // set anchor y justification 
    .text("Hello World");          // define the text to display

This will produce text as follows;

Text: Anchored Bottom-middle
Text: Anchored Bottom-middle
Anchor at the bottom, right of the text:
holder.append("text")         // append text
    .style("fill", "black")   // fill the text with the colour black
    .attr("x", 200)           // set x position of left side of text
    .attr("y", 100)           // set y position of bottom of text
    .attr("text-anchor", "end")  // set anchor y justification 
    .text("Hello World");        // define the text to display

This will produce text as follows;

Text: Anchored Bottom-Right
Text: Anchored Bottom-Right
Anchor at the middle, left of the text:
holder.append("text")         // append text
    .style("fill", "black")   // fill the text with the colour black
    .attr("x", 200)           // set x position of left side of text
    .attr("y", 100)           // set y position of bottom of text
    .attr("dy", ".35em")           // set offset y position
    .attr("text-anchor", "start")  // set anchor y justification 
    .text("Hello World");          // define the text to display

This will produce text as follows;

Text: Anchored Middle-Left
Text: Anchored Middle-Left
Anchor in the middle, centre of the text:
holder.append("text")         // append text
    .style("fill", "black")   // fill the text with the colour black
    .attr("x", 200)           // set x position of left side of text
    .attr("y", 100)           // set y position of bottom of text
    .attr("dy", ".35em")           // set offset y position
    .attr("text-anchor", "middle") // set anchor y justification 
    .text("Hello World");          // define the text to display

This will produce text as follows;

Text: Anchored Middle-Centre
Text: Anchored Middle-Centre
Anchor in the middle, right of the text:
holder.append("text")         // append text
    .style("fill", "black")   // fill the text with the colour black
    .attr("x", 200)           // set x position of left side of text
    .attr("y", 100)           // set y position of bottom of text
    .attr("dy", ".35em")         // set offset y position
    .attr("text-anchor", "end")  // set anchor y justification 
    .text("Hello World");        // define the text to display

This will produce text as follows;

Text: Anchored Middle-Right
Text: Anchored Middle-Right
Anchor at the top, left of the text:
holder.append("text")         // append text
    .style("fill", "black")   // fill the text with the colour black
    .attr("x", 200)           // set x position of left side of text
    .attr("y", 100)           // set y position of bottom of text
    .attr("dy", ".71em")           // set offset y position
    .attr("text-anchor", "start")  // set anchor y justification 
    .text("Hello World");          // define the text to display

This will produce text as follows;

Text: Anchored Top-Left
Text: Anchored Top-Left
Anchor at the top, middle of the text:
holder.append("text")         // append text
    .style("fill", "black")   // fill the text with the colour black
    .attr("x", 200)           // set x position of left side of text
    .attr("y", 100)           // set y position of bottom of text
    .attr("dy", ".71em")            // set offset y position
    .attr("text-anchor", "middle")  // set anchor y justification 
    .text("Hello World");           // define the text to display

This will produce text as follows;

Text: Anchored Top-Middle
Text: Anchored Top-Middle
Anchor at the top, right of the text:
holder.append("text")         // append text
    .style("fill", "black")   // fill the text with the colour black
    .attr("x", 200)           // set x position of left side of text
    .attr("y", 100)           // set y position of bottom of text
    .attr("dy", ".71em")          // set offset y position
    .attr("text-anchor", "end")   // set anchor y justification 
    .text("Hello World");         // define the text to display

This will produce text as follows;

Text: Anchored Top-Right
Text: Anchored Top-Right

Attributes

At the start of writing this section I was faced with the question “What’s an attribute?”. But a reasonable answer has eluded me, so I will make the assumption that the answer will be something of a compromise :-). I like to think that an attribute of an element is something that is a characteristic of the object without defining it, and/or it may affect the object’s position or orientation on the page. There could be a strong argument to say that the following section on styles could be seen to cross-over into attributes and I agree. However, for the purposes of providing a description of the syntax and effects, I’m happy with the following list :-).

Because not all attributes are applicable to all elements, there will be a bit of variation in the type of shapes we deal with in the description below, but there won’t be any that are different to those that we’ve already looked at. There will be some repetition with recurring information from the elements section. This is intentional to hopefully allow each section to exist in its own right.

x, y

The x and y attributes are used to designate a position on the web page that is set from the top, left hand corner of the web page. Using the x and y attributes places the anchor points for these elements at a specified location. Of the elements that we have examined thus far, the rectangle element and the text element have anchor points to allow them to be positioned.

For example the following is a code section required to draw a rectangle (using only the required attributes) in conjunction with the HTML file outlined at the start of this chapter;

holder.append("rect")       // attach a rectangle
    .attr("x", 100)         // position the left of the rectangle
    .attr("y", 50)          // position the top of the rectangle
    .attr("height", 100)    // set the height
    .attr("width", 200);    // set the width

This will produce a rectangle as follows;

Rectangle with `x`,`y` at 100,50
Rectangle with x,y at 100,50

The top left corner of the rectangle is specified using x and y at 100 and 50 respectively.

x1, x2, y1, y2

The x1, x2, y1 and y2 attributes are used to designate the position of two points on a web page that are set from the top, left hand corner of the web page. These two points are connected with a line as part of the line element.

The attributes are described as follows;

  • x1: The x position of the first end of the line as measured from the left of the screen.
  • y1: The y position of the first end of the line as measured from the top of the screen.
  • x2: The x position of the second end of the line as measured from the left of the screen.
  • y2: The y position of the second end of the line as measured from the top of the screen.

The following is an example of the code section required to draw a line in conjunction with the HTML file outlined at the start of this chapter. The attributes connect the point 100,50 (x1, y1) with 300,150 (x2, y2);

holder.append("line")          // attach a line
    .style("stroke", "black")  // colour the line
    .attr("x1", 100)     // x1 position of the first end of the line
    .attr("y1", 50)      // y1 position of the first end of the line
    .attr("x2", 300)     // x2 position of the second end of the line
    .attr("y2", 150);    // y2 position of the second end of the line

This will produce a line as follows;

Line
Line

The line extends from the point 100,50 to 300,150.

points

The points attribute is used to set a series of points which are subsequently connected with a line and / or which may form the bounds of a shape. These are specifically associated with the polyline and polygon elements. Like the x, y and x1, x2, y1, y2 attributes, the coordinates are set from the top, left hand corner of the web page.

The data for the points is entered as a sequence of x,y points in the following format;

    .attr("points", "100,50, 200,150, 300,50"); 

Where 100,50 is the first x,y point then 200,150 is the second. Now is probably the best time to mention that the d3.js wiki makes the point that “it is typically more convenient and flexible to use the d3.svg.line path generator in conjunction with a path element” when describing complex shapes. So while drawing a polyline or polygon using this method may be possible, bear in mind that depending on your application, there may be a better way.

The following is an example of the code section required to draw a polyline in conjunction with the HTML file outlined at the start of this chapter. The additional style declarations are included to illustrate the shape better. The points values can be compared with the subsequent image.

holder.append("polyline")      // attach a polyline
    .style("stroke", "black")  // colour the line
    .style("fill", "none")     // remove any fill colour
    .attr("points", "100,50, 200,150, 300,50");  // x,y points

This will produce a polyline as follows;

Polyline using `points` attribute
Polyline using points attribute

The polyline extends from the point 100,50 to 200,150 to 300,50.

cx, cy

The cx, cy attributes are associated with the circle and ellipse elements and designate the centre of each shape. The coordinates are set from the top, left hand corner of the web page.

  • cx: The position of the centre of the element in the x axis measured from the left side of the screen.
  • cy: The position of the centre of the element in the y axis measured from the top of the screen.

The following is an example of the code section required to draw an ellipse in conjunction with the HTML file outlined at the start of this chapter. In it the centre of the ellipse is set by cx, cy as 200, 100.

holder.append("ellipse")       // attach an ellipse
    .attr("cx", 200)           // position the x-centre
    .attr("cy", 100)           // position the y-centre
    .attr("rx", 100)           // set the x radius
    .attr("ry", 50);           // set the y radius

This will produce an ellipse as follows;

Ellipse with Centre at 200, 100
Ellipse with Centre at 200, 100

The centre of the ellipse is at x = 200 and y = 100 and the radius is 50 pixels vertically and 100 pixels horizontally.

r

The r attribute determines the radius of a circle element from the cx, cy position (the centre of the circle) to the perimeter of the circle.

The following is an example of the code section required to draw a circle in conjunction with the HTML file outlined at the start of this chapter;

holder.append("circle")        // attach a circle
    .attr("cx", 200)           // position the x-center
    .attr("cy", 100)           // position the y-center
    .attr("r", 50);            // set the radius

This will produce a circle with a radius of 50 pixels as follows;

Circle with Radius of 50 Pixels
Circle with Radius of 50 Pixels

The centre of the circle is at x = 200 and y = 100 and the radius is 50 pixels.

rx, ry

The rx, ry attributes are associated with the ellipse element and designates the radius in the x direction (rx) and the radius in the y direction (ry).

  • rx: The radius of the ellipse in the x dimension from the cx, cy position to the perimeter of the ellipse.
  • ry: The radius of the ellipse in the y dimension from the cx, cy position to the perimeter of the ellipse.

The following is an example of the code section required to draw an ellipse in conjunction with the HTML file outlined at the start of this chapter. In it, the centre of the ellipse is set by cx, cy as 200, 100 and the radius in the x direction (rx) is 100 pixels and the radius in the y direction (ry) is 50 pixels.

holder.append("ellipse")       // attach an ellipse
    .attr("cx", 200)           // position the x-centre
    .attr("cy", 100)           // position the y-centre
    .attr("rx", 100)           // set the x radius
    .attr("ry", 50);           // set the y radius

This will produce an ellipse as follows;

Ellipse with x Radius of 100 and y Radius of 50
Ellipse with x Radius of 100 and y Radius of 50

The centre of the ellipse is at x = 200 and y = 100 and the radius is 50 pixels vertically and 100 pixels horizontally.

transform (translate(x,y), scale(k), rotate(a))

The transform attribute is a powerful one which allows us to change the properties of an element in several different ways.

  • translate: Where the element is moved by a relative value in the x,y direction.
  • scale: Where the element’s attributes are increased or reduced by a specified factor.
  • rotate: Where the element is rotated about its reference point by an angular value.

Without a degree of prior understanding, these transforms can appear to behave in unusual ways, but hopefully we’ll explain it sufficiently here so that you can appreciate the logic in the way they work.

transform (translate(x,y))

The transform-translate attribute will take an elements position and adjust it based on a specified value(s) in the x,y directions.

The best way to illustrate this is with an example;

This is the code snippet from the HTML file outlined at the start of this chapter which draws a circle at the position 200,100 (cx,cy);

holder.append("circle")        // attach a circle
    .attr("cx", 200)           // position the x-center
    .attr("cy", 100)           // position the y-center
    .attr("r", 50);            // set the radius

This will produce a circle as follows;

Circle
Circle

If we add in a transform (translate(*x*,*y*)) attribute for values of x,y of 50,50 this will shift our circle by an additional 50 pixels in the x direction and 50 pixels in the y direction.

Here’s the code snippet that will draw our new circle;

holder.append("circle")        // attach a circle
    .attr("cx", 200)           // position the x-center
    .attr("cy", 100)           // position the y-center
    .attr("transform", "translate(50,50)") // translate the circle
    .attr("r", 50);            // set the radius

And here’s the resulting change;

Circle
Circle

The circle was positioned at the point 200,100 and then translated by 50 pixels in both axes to 250,150.

The original code snippet could in fact be written as follows;

holder.append("circle")        // attach a circle
    .attr("transform", "translate(200,100)") // translate the circle
    .attr("r", 50);            // set the radius

Since by default our starting position is 0,0 if we apply a translation of 200,100 we will end up at 200,100.

transform (scale(k))

The translate-scale attribute will take an element’s attributes and scale them by a factor k.

Originally I thought that this attribute would affect the size of the element, but it affects more than that! As with the transform-translate attribute, the best way to illustrate this is with an example;

The following code snippet (in conjunction with the HTML file outlined at the start of this chapter) which draws a circle at the position 150,50 with a radius of 25 pixels;

holder.append("circle")     // attach a circle
    .attr("cx", 150)        // position the x-centre
    .attr("cy", 50)         // position the y-centre
    .attr("r", 25);         // set the radius

This will produce a circle as follows;

Circle
Circle

If we now introduce a transform-scale attribute with a scale of 2 we will see all three of the other attributes (cx, cy and r) scaled by a factor of two to 300, 100 and 50 respectively.

Here is the code;

holder.append("circle")             // attach a circle
    .attr("cx", 150)                // position the x-centre
    .attr("cy", 50)                 // position the y-centre
    .attr("r", 25)                  // set the radius
    .attr("transform", "scale(2)"); // scale the circle attributes

Which will produce a circle as follows;

Circle
Circle

In this example we can see that the position (cx, cy) and the radius (r) have been scaled up by a factor of 2.

transform (rotate(a))

The translate-rotate attribute will rotate an element and its attributes by a declared angle in degrees.

The ability to rotate elements is obviously a valuable tool. The transform-rotate attribute does a great job of it, but the key to making sure that you know exactly what will happen to an object is to remember where the anchor point is for the object and to ensure that the associated attributes are set appropriately. As with the transform translate & scale attributes, the best way to illustrate this is with an example;

The following is the code snippet (in conjunction with the HTML file outlined at the start of this chapter) which draws the text “Hello World” at the position 200,100 with the anchor point being the the middle of the text;

holder.append("text")         // append text
    .style("fill", "black")   // fill the text with the colour black
    .attr("x", 200)           // set x position of left side of text
    .attr("y", 100)           // set y position of bottom of text
    .attr("dy", ".35em")           // set offset y position
    .attr("text-anchor", "middle") // set anchor y justification
    .text("Hello World");          // define the text to display

This will produce text as follows;

Text: Anchored Middle-Centre
Text: Anchored Middle-Centre

If we then apply a transform-rotate of 10 degrees as follows;

holder.append("text")         // append text
    .style("fill", "black")   // fill the text with the colour black
    .attr("x", 200)           // set x position of left side of text
    .attr("y", 100)           // set y position of bottom of text
    .attr("dy", ".35em")           // set offset y position
    .attr("text-anchor", "middle") // set anchor y justification
    .attr("transform", "rotate(10)")
    .text("Hello World");          // define the text to display

We will see the following on the screen;

Text: Anchored Middle-Centre
Text: Anchored Middle-Centre

Obviously the text has been rotated, but hopefully you’ll have noticed that it’s also been displaced. This is because the transform-rotate attribute has been applied to both the text element (which has been rotated by 10 degrees) and the x,y attributes. If you imagine the origin point for the element being at 0,0, the centre, middle of the text element has been rotated about the point 0,0 by 10 degrees (hopefully slightly better explained in the following picture).

Text: All positioning Attributes Rotated
Text: All positioning Attributes Rotated

This could be seen as an impediment to getting things to move / change as you want to, but instead it’s an indication of a different way of doing things. The solution to this particular feature is to combine the transform-rotate with the transform-translate that we used earlier so that the code looks like this;

holder.append("text")         // append text
    .style("fill", "black")   // fill the text with the colour black
    .attr("dy", ".35em")           // set offset y position
    .attr("text-anchor", "middle") // set anchor y justification
    .attr("transform", "translate(200,100) rotate(10)")
    .text("Hello World");          // define the text to display

And the image on the page looks like this;

Text: Rotated by 10 Degrees Anchored Middle-Centre
Text: Rotated by 10 Degrees Anchored Middle-Centre

Which leads us to the final example for which is a combination of all three aspects of the transform attribute.

holder.append("text")         // append text
    .style("fill", "black")   // fill the text with the colour black
    .attr("dy", ".35em")           // set offset y position
    .attr("text-anchor", "middle") // set anchor y justification
    .attr("transform", "translate(200,100) scale(2) rotate(10)")
    .text("Hello World");          // define the text to display
Text: Translated, Scaled and Rotated
Text: Translated, Scaled and Rotated

Here we have a text element translated to its position on the page, rotated by 10 degrees about the centre of the text and scaled by a factor of two.

width, height

width and height are required attributes of the rectangle element. width designates the width of the rectangle and height designates the height (If you’re wondering, I often struggle defining the obvious).

The following is an example of the code section required to draw a rectangle (using only the required attributes) in conjunction with the HTML file outlined at the start of this chapter;

holder.append("rect")       // attach a rectangle
    .attr("x", 100)         // position the left of the rectangle
    .attr("y", 50)          // position the top of the rectangle
    .attr("height", 100)    // set the height
    .attr("width", 200);    // set the width

This will produce a rectangle as follows;

Rectangle
Rectangle

The width of the triangle is 200 pixels and the height is 100 pixels.

text-anchor

The text-anchor attribute determines the justification of a text element

Text can have one of three text-anchor types;

  • start where the text is left justified.
  • middle where the text is centre justified.
  • end where the text is right justified.

The following is an example of code that will draw three separate lines of text with the three different text-anchor types in conjunction with the HTML file outlined at the start of this chapter;

holder.append("text")         // append text
    .style("fill", "black")   // fill the text with the colour black
    .attr("x", 200)           // set x position of left side of text
    .attr("y", 50)            // set y position of bottom of text
    .attr("text-anchor", "start") // set anchor y justification
    .text("Hello World - start"); // define the text to display

holder.append("text")         // append text
    .style("fill", "black")   // fill the text with the colour black
    .attr("x", 200)           // set x position of left side of text
    .attr("y", 100)           // set y position of bottom of text
    .attr("text-anchor", "middle") // set anchor y justification
    .text("Hello World - middle"); // define the text to display
    
holder.append("text")         // append text
    .style("fill", "black")   // fill the text with the colour black
    .attr("x", 200)           // set x position of left side of text
    .attr("y", 150)           // set y position of bottom of text
    .attr("text-anchor", "end") // set anchor y justification
    .text("Hello World - end"); // define the text to display

This will produce an output as follows;

Text with Different `text-anchor` Attributes
Text with Different text-anchor Attributes

dx, dy

dx and dy are optional attributes that designate an offset of text elements from the anchor point in the x and y dimension . There are several different sets of units that can be used to designate the offset of the text from an anchor point. These include em which is a scalable unit, px (pixels), pt (points (kind of like pixels)) and % (percent (scalable and kind of like em))

We can demonstrate the offset effect by noting the difference in two examples.

The first is a simple projection of SVG text that aligns the text “Hello World” above and to the right of the anchor point at 200,100 (It does this in conjunction with the HTML file outlined at the start of this chapter.).

holder.append("text")         // append text
    .style("fill", "black")   // fill the text with the colour black
    .attr("x", 200)           // set x position of left side of text
    .attr("y", 100)           // set y position of bottom of text 
    .text("Hello World");     // define the text to display 

Which produces the following on the page;

Text with the Anchor at the Bottom Left Corner
Text with the Anchor at the Bottom Left Corner

The second example introduces the dx attribute setting the offset to 50 pixels. This adds another 50 pixels to the x dimension. We also introduce the dy attribute with an offset of .35em. This scalable unit allows the text to be set as a factor of the size of the text. In this case .35em will add half the height of the text to the y dimension placing the text so that it is exactly in the middle (vertically) of the 100 pixel line on the y dimension.

holder.append("text")         // append text
    .style("fill", "black")   // fill the text with the colour black
    .attr("x", 200)           // set x position of left side of text
    .attr("y", 100)           // set y position of bottom of text 
    .attr("dx", "50px")       // set offset x position
    .attr("dy", ".35em")      // set offset y position
    .text("Hello World");     // define the text to display 

Which produces the following on the page;

Text with 50 Pixel x Offset and Half Height y Offset
Text with 50 Pixel x Offset and Half Height y Offset

The text has been moved 50 pixels to the right and half the height of the text down the page.

textLength

The textLength attribute adjusts the length of the text to fit a specified value.

The following is a code snippet that prints the text “Hello World” above and to the right of the anchor point at 200,100 (It does this in conjunction with the HTML file outlined at the start of this chapter.). The addition of the textLength attribute declaration in the code stretches the “Hello World” out so that it fills 150 pixels.

holder.append("text")          // append text
    .style("fill", "black")    // fill the text with the colour black
    .attr("x", 200)            // set x position of left side of text
    .attr("y", 100)            // set y position of bottom of text 
    .attr("textLength", "150") // set text length
    .text("Hello World");      // define the text to display 

Which produces the following on the page;

Text Stretched to 150 Pixels Wide
Text Stretched to 150 Pixels Wide

It is worth noting that while the text has been spread out, the individual letters remain un-stretched. Only the letter and word spacing has been adjusted. However, using the lengthAdjust attribute can change this.

lengthAdjust

The lengthAdjust attribute allows the textLength attribute to have the spacing of a text element controlled to be either spacing or spacingAndGlyphs;

  • spacing: In this option the letters remain the same size, but the spacing between the letters and words are adjusted.
  • spacingAndGlyphs: In this option the text is stretched or squeezed to fit.

The attribute can be best illustrated via an example. The following code snippet (which works in conjunction with the HTML file outlined at the start of this chapter) shows three versions of the text element. The top line is the standard text. The middle line is the textLength set to 150 and the lengthAdjust set to spacing (which is the default). The bottom line is the textLength set to 150 and the lengthAdjust set to spacingAndGlyphs.

holder.append("text")          // append text
    .style("fill", "black")    // fill the text with the colour black
    .attr("x", 200)            // set x position of left side of text
    .attr("y", 50)             // set y position of bottom of text 
    .text("Hello World");      // define the text to display 
    
holder.append("text")          // append text
    .style("fill", "black")    // fill the text with the colour black
    .attr("x", 200)            // set x position of left side of text
    .attr("y", 100)            // set y position of bottom of text 
    .attr("textLength", "150") // set text length
    .attr("lengthAdjust", "spacing")
    .text("Hello World");      // define the text to display 

holder.append("text")          // append text
    .style("fill", "black")    // fill the text with the colour black
    .attr("x", 200)            // set x position of left side of text
    .attr("y", 150)            // set y position of bottom of text 
    .attr("textLength", "150") // set text length
    .attr("lengthAdjust", "spacingAndGlyphs")
    .text("Hello World");      // define the text to display 

The image on the screen will look like the following;

Text Stretched in Three Ways
Text Stretched in Three Ways

The image shows that the top line looks normal, the middle line has had the spaces increased to increase the length of the text and the bottom line has been stretched.

Styles

What’s a style?

Believe it or not, that’s as difficult a question to answer as “What’s an attribute?”. I like to think that an element can be selected and arranged on a web page with select and attr, but once it’s there, changes to how it looks are a matter for style. We will cover a range of qualities that neatly fit into this definition in the following section (such as fill, opacity and stroke-width) but there are also a range of unusual style declarations that many may not have come across (I certainly hadn’t before writing this).

The other important thing to mention about setting styles for elements is that there are different ways to accomplish the task. We’ll go through the process of describing different styles as they can be applied to individual elements in isolation, but there is a more powerful way to manage styles across a range of elements via Cascading Style Sheets (CSS) in the <style> section of a web page or even via an external style sheet. We will examine these possibilities at the end of the section.

Full disclosure: I have not figured out how to work some of the styles for d3.js I’m afraid that clip-path and mask have exceeded my skill-set and I will have to leave them for another day :-(. I found that there are several good examples that make use of these styles, but I have struggled (unsuccessfully) to present them in a simple example.

fill

The fill style will fill the element being presented with a specified colour.

By default, most elements will be filled with black (the majority of the examples used in this chapter make no fill declaration).

The following example (which works in conjunction with the HTML file outlined at the start of this chapter) shows the syntax for filling a simple circle with the colour red;

holder.append("circle")        // attach a circle
    .attr("cx", 200)           // position the x-centre
    .attr("cy", 100)           // position the y-centre
    .attr("r", 50)             // set the radius
    .style("fill", "red");     // set the fill colour 

Which results in the following image;

Circle with Red Fill
Circle with Red Fill

As we saw with the polyline and polygon examples earlier in the chapter some shapes may need to have their fill colour turned off in some circumstances and this can be accomplished by declaring the colour to be none (.style("fill", "none");).

There are several different ways to define exactly what colour we want as a fill. The example above uses a ‘named colour code’ to declare the colour as “red” but we could also have defined it as rgb (.style("fill", "rgb(255,0,0)");) or in hexadecimal (.style("fill", "#f00");)

stroke

The stroke style applies a colour to lines.

By default many elements do not have a stroke colour set, so it’s a matter of declaring the colour with either a named colour code (“red”), an rgb value (“rgb(255,0,0)”) or the appropriate hex (“#f00”).

The following example (which works in conjunction with the HTML file outlined at the start of this chapter) shows the syntax for applying the colour red to a simple circle. The fill has been set to none to help the colour stand out.

holder.append("circle")        // attach a circle
    .attr("cx", 200)           // position the x-centre
    .attr("cy", 100)           // position the y-centre
    .attr("r", 50)             // set the radius
    .style("stroke", "red")    // set the line colour
    .style("fill", "none");    // set the fill colour 

Which results in the following image;

Circle with Red Border
Circle with Red Border

opacity

The opacity style has the effect of varying an element’s transparency.

The valid range for opacity is from 0 (completely transparent) to 1 (solid colour). We should make the distinction at this point that opacity affects the entire element, whereas the following fill-opacity and stroke-opacity affects only the fill and stroke respectively.

The following code snippet (which works in conjunction with the HTML file outlined at the start of this chapter) creates a green circle with a red border. The opacity value of .2 creates a degree of transparency which will show the grid lines underneath the element.

holder.append("circle")        // attach a circle
    .attr("cx", 200)           // position the x-centre
    .attr("cy", 100)           // position the y-centre
    .attr("r", 50)             // set the radius
    .style("opacity", .2)      // set the element opacity
    .style("stroke", "red")    // set the line colour
    .style("fill", "green");   // set the fill colour

Which results in the following image;

Circle with opacity
Circle with opacity

fill-opacity

The fill-opacity style changes the transparency of the fill of an element.

The valid range for fill-opacity is from 0 (completely transparent) to 1 (solid colour). We should make the distinction at this point that fill-opacity affects only the fill of an element, whereas opacity will affect the entire element.

The following code snippet (which works in conjunction with the HTML file outlined at the start of this chapter) creates a green circle with a red border. The opacity value of .2 creates a degree of transparency for the fill which will show the grid lines underneath.

holder.append("circle")        // attach a circle
    .attr("cx", 200)           // position the x-centre
    .attr("cy", 100)           // position the y-centre
    .attr("r", 50)             // set the radius
    .style("fill-opacity", .2) // set the fill opacity
    .style("stroke", "red")    // set the line colour
    .style("fill", "green");   // set the fill colour

Which results in the following image;

Circle with Semi-Transparent Fill
Circle with Semi-Transparent Fill

The distinction between this image and the one for the opacity style clearly shows the line around the outside of the object as still a solid (opaque) colour.

stroke-opacity

The stroke-opacity style changes the transparency of the stroke (line) of an element.

The valid range for stroke-opacity is from 0 (completely transparent) to 1 (solid colour). We should make the distinction at this point that stroke-opacity affects only the line or border of an element, whereas opacity will affect the entire element.

The following code snippet (which works in conjunction with the HTML file outlined at the start of this chapter) creates an empty circle with a red border. The opacity value of .2 creates a degree of transparency for the stroke which will show the grid lines underneath (or at least make it appear more ‘muted’).

holder.append("circle")          // attach a circle
    .attr("cx", 200)             // position the x-centre
    .attr("cy", 100)             // position the y-centre
    .attr("r", 50)               // set the radius
    .style("stroke-opacity", .2) // set the stroke opacity
    .style("stroke", "red")      // set the line colour
    .style("fill", "none");      // set the fill colour

Which results in the following image;

Circle with Red Border and opacity
Circle with Red Border and opacity

Although it is not necessarily easy to see in this example because the line is quite thin, the lines of the grid behind the circle will be showing through the line of the circle.

stroke-width

The stroke-width style adjusts the width of the line of an element.

The value specified when setting stroke-width is in pixels.

The following code snippet (which works in conjunction with the HTML file outlined at the start of this chapter) creates an empty circle with a red border. The stroke-width is set to 5 which equates to 5 pixels (it can also be specified as “5px”).

holder.append("circle")          // attach a circle
    .attr("cx", 200)             // position the x-centre
    .attr("cy", 100)             // position the y-centre
    .attr("r", 50)               // set the radius
    .style("stroke-width", 5)    // set the stroke width
    .style("stroke", "red")      // set the line colour
    .style("fill", "none");      // set the fill colour

Which results in the following image;

Circle with Thicker Red Border
Circle with Thicker Red Border

The width of the line that forms the border of the circle is now 5 pixels wide :-).

stroke-dasharray

The stroke-dasharray style allows us to form element lines with dashes instead of solid lines.

We have covered dashed lines in practical way in a previous section of the book (‘Make a Dashed Line’) but for the sake of completeness I will include dashed lines here as well.

We create a dashed line by specifying the length of a dash and then the length of a space. We can include a long list of dashes and spaces and once complete our line will simply repeat the pattern we have specified.

For example the following code snippet (which works in conjunction with the HTML file outlined at the start of this chapter) creates a line with a dash of 10 pixels followed by a space of 2 pixels;

holder.append("circle")       // attach a circle
    .attr("cx", 200)          // position the x-centre
    .attr("cy", 100)          // position the y-centre
    .attr("r", 50)            // set the radius
    .style("stroke-dasharray", ("10,3")) // make the stroke dashed
    .style("stroke", "red")   // set the line colour
    .style("fill", "none");   // set the fill colour

Which results in the following image;

Circle with Dashed Red Border
Circle with Dashed Red Border

More complex combinations of dashes and spaces are possible as are complex animation sequences that leverage the ability to move objects along a path (these are certainly more advanced examples).

stroke-linecap

The stroke-linecap style allows control of the shape of the ends of lines in d3.js.

There are three shape options;

  • butt where the line simply butts up to the starting or ending position and is cut off squarely.
  • round where the line is rounded in proportion to its width.
  • square where the line is squared off but extended in proportion to its width.

The following code snippet (which works in conjunction with the HTML file outlined at the start of this chapter) generates three lines showing each stroke-linecap style option. The top line uses butt. The middle line uses round and the bottom line uses square.

holder.append("line")                 // attach a line
    .style("stroke", "black")         // colour the line
    .style("stroke-width", 20)        // adjust line width
    .style("stroke-linecap", "butt")  // stroke-linecap type
    .attr("x1", 100)     // x position of the first end of the line
    .attr("y1", 50)      // y position of the first end of the line
    .attr("x2", 300)     // x position of the second end of the line
    .attr("y2", 50);     // y position of the second end of the line

holder.append("line")                  // attach a line
    .style("stroke", "black")          // colour the line
    .style("stroke-width", 20)         // adjust line width
    .style("stroke-linecap", "round")  // stroke-linecap type
    .attr("x1", 100)     // x position of the first end of the line
    .attr("y1", 100)     // y position of the first end of the line
    .attr("x2", 300)     // x position of the second end of the line
    .attr("y2", 100);    // y position of the second end of the line

holder.append("line")                   // attach a line
    .style("stroke", "black")           // colour the line
    .style("stroke-width", 20)          // adjust line width
    .style("stroke-linecap", "square")  // stroke-linecap type
    .attr("x1", 100)     // x position of the first end of the line
    .attr("y1", 150)     // y position of the first end of the line
    .attr("x2", 300)     // x position of the second end of the line
    .attr("y2", 150);    // y position of the second end of the line

Which results in the following image;

Three Lines with Different End Shapes
Three Lines with Different End Shapes

The shapes are quite distinct for each type and it is useful to note the degree to which the lines extend beyond their start and end points.

stroke-linejoin

The stroke-linejoin style specifies the shape of the join of two lines. This would be used on path, polyline and polygon elements (and possibly more).

There are three line join options;

  • miter where the join is squared off as would be expected at the join of two lines.
  • round where the outside portion of the join is rounded in proportion to its width.
  • bevel where the join has a straight edged outer portion clipped off to provide a slightly more contoured effect while still being angular.

The following code snippet (which works in conjunction with the HTML file outlined at the start of this chapter) generates a poly line where the join has the connection shaped using the stroke-linejoin round style.

holder.append("polyline")       // attach a polyline
    .style("stroke", "black")   // colour the line
    .style("fill", "none")      // remove any fill colour
	.style("stroke-width", 20)  // colour the line
	.style("stroke-linejoin", "round")  // shape the line join
    .attr("points", "100,50, 200,150, 300,50");  // x,y points 

Which results in the following image;

Polyline with Round Join
Polyline with Round Join

Note the curve on the outer of the join.

Changing the shape of the line join to bevel produces the following;

Polyline with Bevel Join
Polyline with Bevel Join

Here we can see the clipping of the outer portion of the join.

And using miter produces a standard connection;

Polyline with Miter Join
Polyline with Miter Join

This is the default setting for line joins and does not need to be added unless the line join type has already been set to a different default.

writing-mode

The writing-mode style changes the orientation of the text so that it prints out top to bottom. It has a single option “tb” that accomplishes this. It is relatively limited in scope compared to the equivalent for CSS, but for the purposes of generating some text it has a definite use.

The following code snippet (hich works in conjunction with the HTML file outlined at the start of this chapter) creates a line of text that is now printed from top to bottom instead of left to right.

holder.append("text")            // append text
    .style("fill", "black")      // make the text black
    .style("writing-mode", "tb") // set the writing mode
    .attr("x", 200)         // set x position of left side of text
    .attr("y", 100)         // set y position of bottom of text
    .text("Hello World");   // define the text to display

Which results in the following image;

Text rotated using writing-mode
Text rotated using writing-mode

It is significant to note that while it looks like the text has been rotated about it’s anchor point, this actually isn’t the case since the anchor point should be at 200,100. Also, the glyph-orientation-vertical style (which follows) will allow the text to be orientated vertically which will be useful.

glyph-orientation-vertical

The glyph-orientation-vertical style changes the rotation of the individual glyphs (characters) in text and if used in conjunction with the writing-mode style (and set to 0) will allow the text to be displayed vertically with the letters orientated vertically as well.

The following code snippet (which works in conjunction with the HTML file outlined at the start of this chapter) creates a line of text that is now printed from top to bottom with letters orientated vertically.

holder.append("text")            // append text
    .style("fill", "black")      // make the text black
    .style("writing-mode", "tb") // set the writing mode
    .style("glyph-orientation-vertical", 0)
    .attr("x", 200)         // set x position of left side of text
    .attr("y", 25)          // set y position of bottom of text
    .text("Hello World");   // define the text to display

Which results in the following image;

Text rotated and orientated
Text rotated and orientated

It is worth noting that the text spacing increases dramatically as the spacing for each letter relies on the normal distance between the bottom and top of a line of text.

Using styles in Cascading Style Sheets

Declaring styles on an element by element basis is an OK way to apply styles, but when our visualizations become more complex, this can be an inefficient use of code.

A smarter way to provide a common set of styles to elements is to declare them in the <style> section of our HTML document using Cascading Style Sheets (CSS). These will then be automatically applied to our elements.

We start with an example script that draws our three lines that have different styles of linecaps. Our previous example looked like the following (in conjunction with the HTML file outlined at the start of this chapter)

holder.append("line")                 // attach a line
    .style("stroke", "black")         // colour the line
    .style("stroke-width", 20)        // adjust line width
    .style("stroke-linecap", "butt")  // stroke-linecap type
    .attr("x1", 100)     // x position of the first end of the line
    .attr("y1", 50)      // y position of the first end of the line
    .attr("x2", 300)     // x position of the second end of the line
    .attr("y2", 50);     // y position of the second end of the line

holder.append("line")                  // attach a line
    .style("stroke", "black")          // colour the line
    .style("stroke-width", 20)         // adjust line width
    .style("stroke-linecap", "round")  // stroke-linecap type
    .attr("x1", 100)     // x position of the first end of the line
    .attr("y1", 100)     // y position of the first end of the line
    .attr("x2", 300)     // x position of the second end of the line
    .attr("y2", 100);    // y position of the second end of the line

holder.append("line")                   // attach a line
    .style("stroke", "black")           // colour the line
    .style("stroke-width", 20)          // adjust line width
    .style("stroke-linecap", "square")  // stroke-linecap type
    .attr("x1", 100)     // x position of the first end of the line
    .attr("y1", 150)     // y position of the first end of the line
    .attr("x2", 300)     // x position of the second end of the line
    .attr("y2", 150);    // y position of the second end of the line

Which resulted in the following image;

Three Lines with Different End Shapes
Three Lines with Different End Shapes

The block of code for each of the three lines contains three separate style declarations. Two of which are identical for all three blocks of code;

    .style("stroke", "black")         // colour the line
    .style("stroke-width", 20)        // adjust line width

To make these styles available from a common point, we declare them in the <style> section of our HTML file as follows;

<style>
line.linecap {
  stroke: black;
  stroke-width: 20;  
}
</style>

The <style> tags simply tell our browser which part of the html file we are using to define our styles.

The line.linecap portion identifies the following styles as belonging to the line elements that are also identified as belonging to the ‘class’ linecap (We have used the linecap name as a convenience only and it could just as easily been foobar.).

The two styles are enclosed within curly braces and are declared in the form <style-name>: <style-value>;. So for our example here, the stroke is black and its width is 20 pixels.

Then our example script can have the two styles removed from each of the blocks that draws the lines and in their place we add a new attribute class that assigns a class to the element (in this case the class linecap). Our new code will look like this;

holder.append("line")           // attach a line
    .style("stroke-linecap", "butt")  // stroke-linecap type
    .attr("class", "linecap")   // inherits styles from CSS
    .attr("x1", 100)     // x position of the first end of the line
    .attr("y1", 50)      // y position of the first end of the line
    .attr("x2", 300)     // x position of the second end of the line
    .attr("y2", 50);     // y position of the second end of the line

holder.append("line")           // attach a line
    .style("stroke-linecap", "round")  // stroke-linecap type
    .attr("class", "linecap")   // inherits styles from CSS
    .attr("x1", 100)     // x position of the first end of the line
    .attr("y1", 100)     // y position of the first end of the line
    .attr("x2", 300)     // x position of the second end of the line
    .attr("y2", 100);    // y position of the second end of the line

holder.append("line")           // attach a line
    .style("stroke-linecap", "square")  // stroke-linecap type
    .attr("class", "linecap")   // inherits styles from CSS
    .attr("x1", 100)     // x position of the first end of the line
    .attr("y1", 150)     // y position of the first end of the line
    .attr("x2", 300)     // x position of the second end of the line
    .attr("y2", 150);    // y position of the second end of the line

While this has only replaced two lines with one in our code, the potential for use in far more complex examples should be obvious. There is significantly more detail that can be gone into with regard to CSS, but that would be beyond my meagre abilities.

Assorted Tips and Tricks

Change a line chart into a scatter plot

Confession time.

I didn’t actually intend to add in a section with a scatter plot in it for its own sake because I thought it would be;

  1. tricky
  2. not useful
  3. all of the above

I was wrong on all counts.

All you need to do is take the simple graph example file and slot the following block in between the ‘Add the valueline path’ and the ‘add the x axis’ blocks.

    svg.selectAll("dot")
        .data(data)
    .enter().append("circle")
        .attr("r", 3.5)
        .attr("cx", function(d) { return x(d.date); })
        .attr("cy", function(d) { return y(d.close); });

And you will get…

A scatter plot! (with a line)
A scatter plot! (with a line)

The full code for this graph can also be found on github or in the code samples bundled with this book (simple-scatterplot.html and data.csv). A live example can be found on bl.ocks.org.

I deliberately put the dots after the line in the drawing section, because I thought they would look better, but you could put the block of code before the line drawing block to get the following effect;

A scatter plot with the line in front of the dots
A scatter plot with the line in front of the dots

(just trying to reinforce the concept that ‘order’ matters when drawing objects :-)).

You could of course just remove the line block all together…

A scatter plot without the line this time
A scatter plot without the line this time

But in my humble opinion it loses something.

So what do the individual lines in the scatter plot block of JavaScript do?

The first line (svg.selectAll("dot")) essentially provides a suitable grouping label for the svg circle elements that will be added. The next line associates the range of data that we have to the group of elements we are about to add in.

Then we add a circle for each data point (.enter().append("circle")) with a radius of 3.5 pixels (.attr("r", 3.5)) and appropriate x (.attr("cx", function(d) { return x(d.date); })) and y (.attr("cy", function(d) { return y(d.close); });) coordinates.

There is lots more that we could be doing with this piece of code (check out the scatter plot example) including varying the colour or size or opacity of the circles depending on the data and all sorts of really neat things, but for the mean time, there we go. Scatter plot!

Adding tooltips.

Tooltips have a marvellous duality. They are on one hand a pretty darned useful thing that aids in giving context and information where required and on the other hand, if done with a bit of care, they can look very stylish :-).

Technically, they represent a slight move from what we have been playing with so far into a mildly more complex arena of ‘transitions’ and ‘events’. You can take this one of two ways. Either accept that it just works and implement it as shown, or you will know what’s going on and feel free to deride my efforts as those of a rank amateur :-).

Just in case there is some confusion, a tooltip (one word or two?) is a discrete piece of information that will pop into view when the mouse hovers over somewhere specific. Most of us have seen and used them, but I suppose we all tend to call them different things such as ‘infotip’, ‘hint’ or ‘hover box’ I don’t know if there’s a right name for them, but here’s an example of what we’re trying to achieve;

A tooltip magically appears over a dot
A tooltip magically appears over a dot

You can see the mouse has hovered over one of the scatter plot circles and a tip has appeared that provides the user with the exact date and value for that point.

Now, you may also notice that there’s a certain degree of ‘fancy’ here as the information is bound by a rectangular shape with rounded corners and a slight opacity. The other piece of ‘fancy’ which you don’t see in a PDF (or whatever format this distinguished tome will be published in on its 33rd reprint in the year 2034), is that when these tool tips appear and disappear, they do so in an elegant fade-in, fade-out way. Purty.

Now, before we get started describing how the code goes together, let’s take a quick look at the two technique specifics that I mentioned earlier, ‘transitions’ and ‘events’.

Transitions

From the main d3.js web page (d3js.org) transitions are described as gradually interpolating styles and attributes over time. So what I take that to mean is that if you want to change an object, you can do so be simply specifying the attribute / style end point that you want it to end up with and the time you want it to take and go!

Of course, it’s not quite that simple, but luckily, smarter people than I have done some fantastic work describing different aspects of transitions so please see the following for a more complete description of the topic;

Hopefully observing the mouseover and mouseout transitions in the tooltips example will whet your appetite for more!

Events

The other technique is related to mouse ‘events’. This describes the browser watching for when ‘something’ happens with the mouse on the screen and when it does, it takes a specified action. A (probably non-comprehensive) list of the types of events are the following;

  • mousedown: Triggered by an element when a mouse button is pressed down over it
  • mouseup: Triggered by an element when a mouse button is released over it
  • mouseover: Triggered by an element when the mouse comes over it
  • mouseout: Triggered by an element when the mouse goes out of it
  • mousemove: Triggered by an element on every mouse move over it.
  • click: Triggered by a mouse click: mousedown and then mouseup over an element
  • contextmenu: Triggered by a right-button mouse click over an element.
  • dblclick: Triggered by two clicks within a short time over an element

How many of these are valid to use within d3 I’m not sure, but I’m willing to bet that there are probably more than those here as well. Please go to http://javascript.info/tutorial/mouse-events for a far better description of the topic if required.

Get tipping

So, bolstered with a couple of new concepts to consider, let’s see how they are enacted in practice.

The full code for this graph can also be found on github or in the code samples bundled with this book (simple-tooltips.html and data.csv). A live example can be found on bl.ocks.org.

If we start with our simple-scatter plot graph there are 4 areas in it that we will want to modify (it may be easier to check the tooltips.html file in the example files in the downloads section on d3noob.org).

The first area is the CSS. The following code should be added just before the </style> tag;

div.tooltip {	
    position: absolute;			
    text-align: center;			
    width: 60px;					
    height: 28px;					
    padding: 2px;				
    font: 12px sans-serif;		
    background: lightsteelblue;	
    border: 0px;		
    border-radius: 8px;			
    pointer-events: none;			
}

These styles are defining how our tooltip will appear . Most of them are fairly straight forward. The position of the tooltip is done in absolute measurements, not relative. The text is centre aligned, the height, width and colour of the rectangle is 28px, 60px and lightsteelblue respectively. The ‘padding’ is an interesting feature that provides a neat way to grow a shape by a fixed amount from a specified size.

We set the border to 0px so that it doesn’t show up and a neat style (attribute?) called border-radius provides the nice rounded corners on the rectangle.

Lastly, but by no means least, the ‘pointer-events: none’ line is in place to instruct the mouse event to go “through” the element and target whatever is “underneath” that element instead (Read more here). That means that even if the tooltip partly obscures the circle, the code will still act as if the mouse is over only the circle.

The second addition is a simple one-liner that should (for forms sake) be placed under the ‘parseData’ variable declaration;

var formatTime = d3.time.format("%e %B");

This line formats the date when it appears in our tooltip. Without it, the time would default to a disturbingly long combination of temporal details. In the case here we have declared that we want to see the day of the month (%e) and the full month name(%B).

The third block of code is the function declaration for ‘div’.

var div = d3.select("body").append("div")	
    .attr("class", "tooltip")				
    .style("opacity", 0);

We can place that just after the ‘valueline’ definition in the JavaScript. Again there’s not too much here that’s surprising. We tell it to attach ‘div’ to the body element, we set the class to the tooltip class (from the CSS) and we set the opacity to zero. It might sound strange to have the opacity set to zero, but remember, that’s the natural state of a tooltip. It will live unseen until it’s moment of revelation arrives and it pops up!

The final block of code is slightly more complex and could be described as a mutant version of the neat little bit of code that we used to do the drawing of the dots for the scatter plot. That’s because the tooltips are all about the scatter plot circles. Without a circle to ‘mouseover’, the tooltip never appears :-).

So here’s the code that includes the scatter plot drawing (it’s included since it’s pretty much integral);

    svg.selectAll("dot")	
        .data(data)			
    .enter().append("circle")								
        .attr("r", 5)		
        .attr("cx", function(d) { return x(d.date); })		 
        .attr("cy", function(d) { return y(d.close); })		
        .on("mouseover", function(d) {		
            div.transition()		
                .duration(200)		
                .style("opacity", .9);		
            div	.html(formatTime(d.date) + "<br/>"  + d.close)	
                .style("left", (d3.event.pageX) + "px")		
                .style("top", (d3.event.pageY - 28) + "px");	
            })					
        .on("mouseout", function(d) {		
            div.transition()		
                .duration(500)		
                .style("opacity", 0);	
        });

The first six lines of the code are a repeat of the scatter plot drawing script. The only changes are that we’ve increased the radius of the circle from 3.5 to 5 (just to make it easier to mouse over the object) and we’ve removed the semicolon from the cy attribute line since the code now has to carry on.

So the additions are broken into two areas that correspond to the two events. mouseover and mouseout. When the mouse moves over any of the circles in the scatter plot, the mouseover code is executed on the div element. When the mouse is moved off the circle a different set of instructions are executed.

on.mouseover

The .on("mouseover" line initiates the introduction of the tooltip. Then we declare the element we will be introducing (‘div’) and that we will be applying a transition to its introduction (.transition()). The next two lines describe the transition. It will take 200 milliseconds (.duration(200)) and will result in changing the element’s opacity to .9 (.style("opacity", .9);). Given that the natural state of our tooltip is an opacity of 0, this make sense for something appearing, but it doesn’t go all the way to a solid object and it retains a slight transparency just to make it look less permanent.

The following three lines format our tooltip. The first one adds an html element that contains our x and y information (the date and the d.close value). Now this is done in a slightly strange way. Other tooltips that I have seen have used a ‘.text’ element instead of a ‘.html’ one, but I have used ‘.html’ in this case because I wanted to include the line break tag <br/> to separate the date and value. I’m sure there are other ways to do it, but this worked for me. The other interesting part of this line is that this is where we call our time formatting function that we described earlier. The next two lines position the tooltip on the screen and to do this they grab the x and y coordinates of the mouse when the event takes place (with the d3.event.pageX and d3.event.pageY snippets) and apply a correction in the case of the y coordinate to raise the tooltip up by the same amount as its height (28 pixels).

on.mouseout

The .on("mouseout" section is slightly simpler in that it doesn’t have to do any fancy text / html / coordinate stuff. All it has to do is to fade out the ‘div’ element. And that is done by simply reversing the opacity back to 0 and setting the duration for the transition to 500 milliseconds (being slightly longer than the fade-in makes it look slightly cooler IMHO).

Right, there you go. As a description it’s ended up being a bit of a wall of text I’m afraid. But hopefully between the explanation and the example code you will get the idea. Please take the time to fiddle with the settings described here to find the ones that work for you and in the process you will reinforce some of the principles that help D3 do its thing.

There was an interesting question on d3noob.org about adding an HTML link to a tooltip. While the person asking the question had the problem pretty much solved already, I thought it might be useful for others.

The premise is that you want to add a tool tip to your visualization using the method described here, but you also want to include an HTML link in the tooltip that will link somewhere else. This might look a little like the following;

Tool tip with an HTML Link
Tool tip with an HTML Link

In the image above the date has been turned into a link. In this case the link goes to google.com, but that can obviously be configurable.

The full code for this example can be found on github or in the code samples bundled with this book (tooltips-link.html and data.csv). A working example can be found on bl.ocks.org.

There are a few changes that we would want to make to our original tooltip code to implement this feature.

First of all, we’ll add the link to the date element. Adding an HTML link can be as simple as wrapping the ‘thing’ to be used as a link in <a> tags with an appropriate URL to go to.

The following adaptation of the code that prints the information into our tooltip code does just that;

    div .html(
        '<a href= "http://google.com">' + // The first <a> tag
        formatTime(d.date) +
        "</a>" +                          // closing </a> tag
        "<br/>"  + d.close)     
        .style("left", (d3.event.pageX) + "px")             
        .style("top", (d3.event.pageY - 28) + "px");

<a href= "http://google.com"> places our first <a> tag and declares the URL and the second tag follows after the date.

The second change we will want to make is to ensure that the tooltip stays in place long enough for us to actually click on the link. The problem being solved here is that our original code relies on the mouse being over the dot on the graph to display the tooltip. if the tooltip is displayed and the cursor moves to press the link, it will move off the dot on the graph and the tooltip vanishes (Nice!).

To solve the problem we can leave the tooltip in place adjacent to a dot while the mouse roams freely over the graph until the next time it reaches a dot and then the previous tooltip vanishes and a new one appears. The best way to appreciate this difference is to check out the live example on bl.ocks.org.

The code is as follows (you may notice that this also includes the link as described above);

    .on("mouseover", function(d) {        
        div.transition()
            .duration(500)    
            .style("opacity", 0);
        div.transition()
            .duration(200)    
            .style("opacity", .9);    
        div .html(
            '<a href= "http://google.com">' + // The first <a> tag
            formatTime(d.date) +
            "</a>" +                          // closing </a> tag
            "<br/>"  + d.close)     
            .style("left", (d3.event.pageX) + "px")             
            .style("top", (d3.event.pageY - 28) + "px");
        });

We have removed the .on("mouseout" portion and moved the function that it used to carry out to the start of the .on("mouseover" portion. That way the first thing that occurs when the mouse cursor moves over a dot is that it removes the previous tooltip and then it places the new one.

The last change we need to make is to remove from the <style> section the line that told the mouse to ignore the tooltip;

  /*  pointer-events: none;    This line needs to be removed */

In this case I have just commented it out so that it’s a bit more obvious that it gets removed.

One link is interesting, but let’s face it, we didn’t go to all the trouble of putting a link into a tool tip to just go to one location. Now we shift it up a gear and start linking to different places depending on our data. At the same time (and because someone asked) we will make the link open in a new tab!

The changes to the script are fairly minor, but one fairly large change is the need to have links to go to. For this example I have added a range of links to visit to our csv file so it now looks like this;

date,close,link
1-May-12,58.13,http://bl.ocks.org/d3noob/c37cb8e630aaef7df30d
30-Apr-12,53.98,http://bl.ocks.org/d3noob/11313583
27-Apr-12,67.00,http://bl.ocks.org/d3noob/11306153
26-Apr-12,89.70,http://bl.ocks.org/d3noob/11137963
25-Apr-12,99.00,http://bl.ocks.org/d3noob/10633856
24-Apr-12,130.28,http://bl.ocks.org/d3noob/10633704
23-Apr-12,166.70,http://bl.ocks.org/d3noob/10633421
20-Apr-12,234.98,http://bl.ocks.org/d3noob/10633192
19-Apr-12,345.44,http://bl.ocks.org/d3noob/10632804
18-Apr-12,443.34,http://bl.ocks.org/d3noob/9692795
17-Apr-12,543.70,http://bl.ocks.org/d3noob/9267535
16-Apr-12,580.13,http://bl.ocks.org/d3noob/9211665
13-Apr-12,605.23,http://bl.ocks.org/d3noob/9167301
12-Apr-12,622.77,http://bl.ocks.org/d3noob/8603837
11-Apr-12,626.20,http://bl.ocks.org/d3noob/8375092
10-Apr-12,628.44,http://bl.ocks.org/d3noob/8329447
9-Apr-12,636.23,http://bl.ocks.org/d3noob/8329404
5-Apr-12,633.68,http://bl.ocks.org/d3noob/8150631
4-Apr-12,624.31,http://bl.ocks.org/d3noob/8273682
3-Apr-12,629.32,http://bl.ocks.org/d3noob/7845954
2-Apr-12,618.63,http://bl.ocks.org/d3noob/6584483
30-Mar-12,599.55,http://bl.ocks.org/d3noob/5893649
29-Mar-12,609.86,http://bl.ocks.org/d3noob/6077996
28-Mar-12,617.62,http://bl.ocks.org/d3noob/5193723
27-Mar-12,614.48,http://bl.ocks.org/d3noob/5141528
26-Mar-12,606.98,http://bl.ocks.org/d3noob/5028304

The code change is to the piece of JavaScript where we add the HTML. This is what we end up with;

    div .html(
        '<a href= "'+d.link+'" target="_blank">' + //with a link
        formatTime(d.date) +
        "</a>" +
        "<br/>"  + d.close)     
        .style("left", (d3.event.pageX) + "px")             
        .style("top", (d3.event.pageY - 28) + "px");

We’ve replaced the URL http://google.com with the variable for our link column d.link and we’ve also added in the target="_blank" statement so that our link opens in a new tab.

The full code for this multi link example can be found on github or in the code samples bundled with this book (tooltips-link-multi.html and datatips.csv). A working example can be found on bl.ocks.org.

Hopefully that helps people with a similar desire to include links in their tooltips. Many thanks to the reader who suggested it :-).

What are the predefined, named colours?

Throughout this document I generally use colours defined by name. This is mainly because I can, and not for any other reason. In fact there several different ways to define colours used in D3 / JavaScript / CSS and HTML. I have no idea what the limitations for use are and / or how their use in different browsers impacts on correct representation. But I do know that they’re used widely.

There seems to be several different standards for what constitute an authoritative list of named colours. After a cursory search I was able to find a great list on about.com and there are some nice representations on Wikipedia.

The overriding point of all this is that there’s more than one way to define colours in your graphs.

It means that considering… .style("fill", "steelblue")
and…
.style("fill", "#4682b4")
and…
.style("fill", "rgb(70,130,180)")

All three alternatives result in the same colour being applied.

For a long time I didn’t actually have the images of the colours represented here in D3 Tips and Tricks, but like all things, one day I thought ‘Hey, I could just write a simple script that placed them on the screen’. So here they are :-).

I have tried to group them as ‘like’ colours per the entry in Wikipedia.

You can also see a live page with the script that produces the rectangles at bl.ocks.org.

Selecting / filtering a subset of objects

OK, Imagine a scenario where you want to select (or should we say filter) a particular range of objects from a larger set.

For example, what if we wanted to use our scatter plot example to show the line as normal, but we are particularly interested in the points where the values of the points fall below 400. And when it does we want them highlighted with a circle as we have done with all the points previously.

So that we end up with something that looks a little like this…

Only the points below 400 are selected
Only the points below 400 are selected

Err… Yes, for those among you who are of the observant persuasion, I have deliberately coloured them red as well (red for DANGER!).

This is a fairly simple example, but serves to illustrate the principle adequately. From our simple scatter plot example we only need to add in two lines to the block of code that draws the circles as follows;

    svg.selectAll("dot")		
        .data(data)										
    .enter().append("circle")								
    .filter(function(d) { return d.close < 400 })    // <== This line
        .style("fill", "red")                        // <== and this one
        .attr("r", 3.5)										
        .attr("cx", function(d) { return x(d.date); })		 
        .attr("cy", function(d) { return y(d.close); });

The full code for this example can be found on github or in the code samples bundled with this book (filter-selection.html and data.csv). A working example can be found on bl.ocks.org.

The first added line uses the .filter function to act on the data points and according to the arguments passed to it in this case, only return those where the value of d.close is less than 400 (return d.close < 400).

The second added line is our line that simply colours the circles red (.style("fill", "red")).

That’s all there is to it. Pretty simple, but the filter function can be very powerful when used wisely.

I’ve placed a copy of the file for selecting / filtering into the downloads section on d3noob.org with the general examples as filter-selection.html.

Select items with an IF statement.

The filtering – selection section above is a good way to adapt what you see on a graph, but so is a more familiar friend… The ‘if’ statement.

An if statement will act to carry out a task in a particular way dependant on a condition that you specify.

Starting with the simple scatter plot example all we have to do is include the if statement in the block of code that draws the circles. Here’s the entire block with the additions highlighted;

    svg.selectAll("dot")	
        .data(data)										
    .enter().append("circle")								
        .attr("r", 3.5)		
        .style("fill", function(d) {            // <== Add these
            if (d.close <= 400) {return "red"}  // <== Add these
            else 	{ return "black" }          // <== Add these
        ;})                                     // <== Add these
        .attr("cx", function(d) { return x(d.date); })		 
        .attr("cy", function(d) { return y(d.close); });	

Our first added line introduces the style modifier and the rest of the code acts to provide a return for the ‘fill’ attribute.

The second line introduces our if statement. There’s very little difference using if statements between languages. Just look out for maintaining the correct syntax and you should be fine. In this case we’re asking if the value of d.close is less than or equal to 400 and if it is it will return the "red" statement for our fill.

The third line covers our rear and make sure that if the colour isn’t going to be red, it’s going to be black. The last line just closes the style and function statements.

The result?

Points above 400 black and points below 400 red
Points above 400 black and points below 400 red

Aww….. nice.

The full code for this example can be found on github or in the code samples bundled with this book (if-selection.html and data.csv). A working example can be found on bl.ocks.org.

Could it be any cooler? I’m glad you asked.

What if we wanted to have all the points where close was less than 400 red and all those where close was greater than 620 green? Oh yeah! Now we’re talking.

So with one small change to the if statement;

        .style("fill", function(d) { 		
            if (d.close <= 400) {return "red"}	
            else if (d.close >= 620) {return "lawngreen"} // <== Right here 
            else { return "black" } 			
        ;})		

Check it out…

Points coloured differently depending on their value
Points coloured differently depending on their value

Nice.

Applying a colour gradient to a line based on value.

I just know that you were impressed with the changing dots in a scatter plot based on the value. But could we go one better?

How about we try to reproduce the same effect but by varying the colour of the plotted line. This is a neat feature and a useful example of the flexibility of d3.js and SVG in general. I used the appropriate bits of code from Mike Bostock’s Threshold Encoding example. And I should take the opportunity to heartily recommend browsing through his collection of examples on bl.ocks.org. For those who prefer to see the code in it’s fullest, there is an example as an appendix (Graph with Area Gradient) that can assist (although it is for a later example that uses a gradient in a similar way (don’t worry we’ll get to it in a few pages)).

Here then is a plotted line that is red below 400, green above 620 and black in between.

Line colour varied with gradient
Line colour varied with gradient

How cool is that?

Enough beating around the bush, how is the magic line produced?

Starting with our simple line graph, there are only two blocks of code to go in. One is CSS in the <style> area and the second is a tricky little piece of code that deals with gradients.

The full code for this example can be found on github or in the code samples bundled with this book (line-colour-gradient-graph.html and data.csv). A working example can be found on bl.ocks.org.

So, first the CSS.

.line {							
    fill: none;					
    stroke: url(#line-gradient);	
    stroke-width: 2px;			
}	

This block can go in the <style> area towards the end.

There’s the fairly standard fill of none and a stroke width of 2 pixels, but the stroke: url(#line-gradient); is something different.

In this case the stroke (the colour of the line) is being determined at a link within the page which is set by the anchor #line-gradient. We will see shortly that this is in our second block of code, so the colour is being defined in a separate portion of the script.

And now the JavaScript gradient code;

    svg.append("linearGradient")				
        .attr("id", "line-gradient")			
        .attr("gradientUnits", "userSpaceOnUse")	
        .attr("x1", 0).attr("y1", y(0))			
        .attr("x2", 0).attr("y2", y(1000))		
    .selectAll("stop")						
        .data([								
            {offset: "0%", color: "red"},		
            {offset: "40%", color: "red"},	
            {offset: "40%", color: "black"},		
            {offset: "62%", color: "black"},		
            {offset: "62%", color: "lawngreen"},	
            {offset: "100%", color: "lawngreen"}	
        ])					
    .enter().append("stop")			
        .attr("offset", function(d) { return d.offset; })	
        .attr("stop-color", function(d) { return d.color; });		

There’s our anchor on the second line!

But let’s not get ahead of ourselves. This block should be placed after the x and y domains are set, but before the line is drawn.

So, our first line adds our linear gradient. Gradients consist of continuously smooth colour transitions along a vector from one colour to another We can have a linear one or a radial one and depending on which you select, there are a few options to define. There is some great information on gradients at http://www.w3.org/TR/SVG/pservers.html (more than I ever thought existed).

The second line (.attr("id", "line-gradient")) sets our anchor for the CSS that we saw earlier.

The third fourth and fifth lines define the bounds of the area over which the gradient will act. Since the coordinates x1, y1, x2, y2 will describe an area. The values for y1 (0) and y2 (1000) are used more for convenience to align with our data (which has a maximum value around 630 or so). For more information on the gradientUnits attribute I found this page useful https://developer.mozilla.org/en-US/docs/SVG/Attribute/gradientUnits. We’ll come back to the coordinates in a moment.

The next block selects all the ‘stop’ elements for the gradients. These stop elements define where on the range covered by our coordinates the colours start and stop. These have to be defined as either percentages or numbers (where the numbers are really just percentages in disguise (i.e. 45% =0.45)).

The best way to consider the stop elements is in conjunction with the gradientUnits. The image following may help.

Varying colours for varying values make a gradient
Varying colours for varying values make a gradient

In this case our coordinates describe a vertical line from 0 to 1000. Our colours transition from red (0) to red (400) at which point they change to black (400) and this will continue until it gets to black (620). Then this changes to green (620) and from there, any value above that will be green.

So after defining the stop elements, we enter and append the elements to the gradient (.enter().append("stop")) with attributes for offset and colour that we defined in the stop elements area.

Now, that IS cool, but by now, I hope that you have picked that a gradient function really does mean a gradient, and not just a straight change from one colour to another.

So, let’s try changing the stop element offsets to the following (and making the stroke-width slightly larger to see more clearly what’s going on);

        .data([								
            {offset: "0%", color: "red"},		
            {offset: "30%", color: "red"},	
            {offset: "45%", color: "black"},		
            {offset: "55%", color: "black"},		
            {offset: "60%", color: "lawngreen"},	
            {offset: "100%", color: "lawngreen"}	
        ])		

And here we go…

Line with a gradually changing gradient
Line with a gradually changing gradient

Ahh… A real gradient.

I have tended to find that I need to have a good think about how I set the offsets and bounds when doing this sort of thing since it can get quite complicated quite quickly :-)

Applying a colour gradient to an area fill.

The previous example of a varying gradient on a line is neat, but hopefully you’re already thinking “Hang on, can’t that same thing be applied to an area fill?”.

Damn! You’re catching on.

To do this there’s only a few things we need to change;

First of all the CSS for the line needs to be amended to refer to the area. So this…

.line {							
    fill: none;					
    stroke: url(#line-gradient);	
    stroke-width: 2px;			
}		

…gets changed to this…

.area {							
    fill: url(#area-gradient);					
    stroke-width: 0px;			
}		

We’ve defined the styles for the area this time, but instead of the stroke being defined by the separate script, now it’s the area. While we’ve changed the url name, it’s actually the same piece of code, with a different id (because it seemed wrong to be talking about an area when the label said line). We’ve also set the stroke width to zero, because we don’t want any lines around our filled area.

Now we want to take the block of code that defined our line…

var	valueline = d3.svg.line()
    .x(function(d) { return x(d.date); })
    .y(function(d) { return y(d.close); });		

… and we need to replace it with the standard block that defined an area fill.

var	area = d3.svg.area()	
    .x(function(d) { return x(d.date); })	
    .y0(height)					
    .y1(function(d) { return y(d.close); });		

So we’re not going to be drawing a line at all. Just the area fill.

Next, as I mentioned earlier, we change the id for the linearGradient block from "line-gradient" to "area-gradient"

        .attr("id", "area-gradient")			

And lastly, we remove the block of code that drew the line and replace it with a block that draws an area. So change this….

    svg.append("path")				
        .attr("class", "line")		
        .attr("d", valueline(data));

… to this;

    svg.append("path") 	
        .datum(data)	
        .attr("class", "area")	
        .attr("d", area);

And then sit back and marvel at your creation;

Area fill with a gradually changing gradient
Area fill with a gradually changing gradient

The full code for this example can be found on github or in the code samples bundled with this book (area-colour-gradient-graph.html and data.csv). A working example can be found on bl.ocks.org.

For a slightly ‘nicer’ looking example, you could check out a variation of one of Mike Bostocks originals here; http://bl.ocks.org/4433087.

Show / hide an element by clicking on another element

This is a trick that I found I wanted to impliment in order to present a graph with a range of lines and to then provide the reader with the facility to click on the associated legend to toggle the visibility of the lines off and on as required.

The example we’ll follow is our friend from earlier, a slightly modified example of the graph with two lines.

Show / hide lines on a graph
Show / hide lines on a graph

In this example we will be able to click on either of the two titles at the bottom of the graph (‘Blue Line’ or ‘Red Line’) and have it toggle the respective line and Y axis.

The code

The code for the example is available online at bl.ocks.org or GitHub. It is also available as the file ‘show-hide.html’ as a separate download with D3 Tips and Tricks. A copy of most the files that appear in the book can be downloaded (in a zip file) when you download the book from Leanpub.

There are two main parts to implementing this technique. Firstly we have to label the element (or elements) that we wish to show / hide and then we have to give the object that will get clicked on the attribute that allows it to recognise a mouse click and the code that it subsequently uses to show / hide our labelled element.

Labelling the element that is to be switched on and off is dreadfully easy. It simply involves including an id attribute to an element that identifies it uniquely.

svg.append("path")
    .attr("class", "line")
    .attr("id", "blueLine")
    .attr("d", valueline(data));

In the example above we have applied the id blueLine to the path that draws the blue line on our graph.

The second part is a little trickier. The following is the portion of JavaScript that places our text label under the graph. The only part of it that is unusual is the .on("click", function() section of the code.

svg.append("text")
    .attr("x", 0)             
    .attr("y", height + margin.top + 10)    
    .attr("class", "legend")
    .style("fill", "steelblue")         
    .on("click", function(){
        // Determine if current line is visible
        var active   = blueLine.active ? false : true,
          newOpacity = active ? 0 : 1;
        // Hide or show the elements
        d3.select("#blueLine").style("opacity", newOpacity);
        d3.select("#blueAxis").style("opacity", newOpacity);
        // Update whether or not the elements are active
        blueLine.active = active;

When we click on our ‘Blue Line’ text element the .on("click", function() section executes.

We’re using a short-hand version of the if statement a couple of times here. Firstly we check to see if the variable blueLine.active is true or false and if it’s true it gets set to false and if it’s false it gets set to true (not at all confusing).

        var active   = blueLine.active ? false : true,
          newOpacity = active ? 0 : 1;

Then after toggling this variable we set the value of newOpacity to either 0 or 1 depending on whether active is false or true (the second short-hand JavaScript if statement).

We can then select our identifiers that we have declared using the id attributes in the earlier pieces of code and modify their opacity to either 0 (off) or 1 (on)

        d3.select("#blueLine").style("opacity", newOpacity);
        d3.select("#blueAxis").style("opacity", newOpacity);

Lastly we update our blueLine.active variable to whatever the active state is so that it can toggle correctly the next time it is clicked on.

        blueLine.active = active;

Quite a neat piece of code. Kudos to Max Leiserson for providing the example on which it is largely based in an answer to a question on Stack Overflow.

Export an image from a d3.js page as a SVG or bitmap

At some point you will want to take your lovingly crafted D3 graphical masterpiece and put it in a (close your eyes if you’re squeamish) Power Point presentation or Word document or export it for sharing in some other way.

There could be many reasons for wanting to do this and some may be more complicated than I will be willing to explore, but for the occasional conversion of images I have found what I regard as a fairly easy process.

Before we begin our exporting odyssey, let’s cover a little bit of housekeeping and describe the difference between a vector graphic (in this case specifically Scalable Vector Graphics) and a bitmap. Please skip ahead if you’re comfortable with the terms.

Bitmaps

A bitmap (or raster) image is one that is composed of lots of discrete individual dots (let’s call them pixels) which, when joined together (and zoomed out a bit) give the impression of an image. If we use the example of the force layout example we developed, and look at a screen shot (and it’s important to remember that this is a screen shot) of the image we see a picture that looks fairly benign.

A bitmap at a normal zoom level
A bitmap at a normal zoom level

However, as we enlarge the image by doubling it’s size (x 2) we begin to see some rough edges appear.

A bitmap at 200%
A bitmap at 200%

And if we enlarge it by doubling again (x 4) , it starts to look decidedly rough.

A bitmap at 400%
A bitmap at 400%

Doubling again (x 8), starts to show the pixels pretty clearly.

A bitmap at 800%
A bitmap at 800%

Doubling again for the last time (x 16) and the pixels are plainly evident.

A bitmap at 1600%
A bitmap at 1600%

Bitmaps can be saved in a wide range of formats depending on users requirements including compression, colour depth, transparency and a host of other attributes. Typically they can be identified by the file suffix .jpg, .png or .bmp (and there are an equally large number of other suffixes).

This will be the type of format that most people will be familiar with for images and their ubiquity with the advent of digital cameras almost makes it redundant to describe them.

However, there is another type of image and it is even more important to d3.js users.

Vector Graphics (Specifically SVG)

Scalable Vector Graphics (SVG) use a technique of drawing an image that relies more on a description of an image than the final representation that a user sees. Instead of arranging individual pixels, an image is created by describing the way the image is created.

For instance, drawing a line would be accomplished by defining two sets of coordinates and specifying a line of a particular width and colour be drawn between the points.

This might sound a little long winded, and it does create a sense of abstraction, but it is a far more powerful mechanism for drawing as there is no loss of detail with increasing scale. Changes to the image can be simply carried out by adjusting the coordinates, colour description, line width or curve diameter. If this all sounds a little familiar, you have definitely been paying attention, because this is the heart of the way that d3.js draws images in a browser. It uses a combination of coordinates, shapes and attributes to create vector images in a web page.

As a demonstration of the difference, here is the same original picture which I have saved as a SVG image.

A SVG image at a normal zoom level
A SVG image at a normal zoom level

Enlarged by doubling it’s size (x 2) everything looks smooth.

A SVG at 200%
A SVG at 200%

If we enlarge it by doubling again (x 4) , it still looks good.

A SVG at 400%
A SVG at 400%

Doubling again (x 8) and we can see that the text ‘James’ is actually composed of a fill colour and a border.

A SVG at 800%
A SVG at 800%

Doubling again for the last time (x 16) everything still retains it’s clear sharp edges.

A SVG at 1600%
A SVG at 1600%

Let’s get exporting!

We’ll use a three stage process for exporting our image (assuming the desired end result is a bitmap) and usefully, the first stage will result in us having a vector image as well!

The sequence will go as follows:

  1. Copy the image from the web page and save it as a SVG file
  2. Open the SVG image in a program designed to use vector images and edit it if required.
  3. Export that image as a bitmap
Copying the image off the web page

Getting the image out of a web page is made easy by using ‘SVG Crowbar’. This is a “A Chrome-specific bookmarklet that extracts SVG nodes and accompanying styles from an HTML document and downloads them as an SVG file”. What that means is that once you drag the bookmarklet from the web page to your bookmarks (You need to be using Google Chrome, and I’m told that about 60% of the people who visit d3noob.org do) you’re ready to go.

Drag the 'SVG Crowbar' Object from the web page to your bookmarks bar
Drag the ‘SVG Crowbar’ Object from the web page to your bookmarks bar

Now when you have a web page open that’s displaying a D3 creation, all you need to do is click on the SVG Crowbar bookmark and you will be prompted for a location to save a svg image.

Really. It’s that simple.

Open the SVG Image and Edit

Obviously now that you have a SVG image, you need to be able to do something with it. My preferred software for this is Inkscape.

Inkscape is “An Open Source vector graphics editor, with capabilities similar to Illustrator, CorelDraw, or Xara X, using the W3C standard Scalable Vector Graphics (SVG) file format”.

It really is an extremely capable drawing program and it is capable of a lot more than the job we’re going to use it for, so you may find it has other uses that may be valuable.

Once installed, you can open the saved file directly into Inkscape.

Inkscape with our force diagram
Inkscape with our force diagram

While here you can edit the drawing to your hearts delight. I particularly recommend ungrouping the diagram and removing or adjusting individual elements if required.

Once you have finished editing, you are ready for the final step.

Saving as a bitmap

While still in Inkscape, go to the ‘File’, ‘Export Bitmap…’ menu.

Inkscape Export Bitmap menu
Inkscape Export Bitmap menu

This will open a dialog box where you can select an appropriate resolution and location for your bitmap and then press the export button.

Inkscape Export Bitmap dialog
Inkscape Export Bitmap dialog

There you go.

It is worth knowing that the default settings here will export the diagram with a transparent background (using *.png) which will fit in nicely with a wide range of graphical end uses.

Using HTML inputs with d3.js

Part of the attraction of using technologies like d3.js is that it expands the scope of what is possible in a web page. At the same time, there are many different options for displaying content on a page and plenty of ways of interacting with it.

Some of the most basic of capabilities has been the use of HTML entities that allow the entry of data on a page. This can take a range of different forms (pun intended) and the <input> tag is one of the most basic.

What is an HTML input?

An HTML input is an element in HTML that allows a web page to input data. There are a range of different input types (with varying degrees of compatibility with browsers) and they are typically utilised inside a <form> element.

For example the following code allows a web page to place two fields on a web page so that a user can enter their first and last names in separate boxes;

<form>
  First name: <input type="text" name="firstname"><br>
  Last name: <input type="text" name="lastname">
</form>

The page would then display the following;

A form input
A form input

The range of input types is large and includes;

  • text: A simple text field that a user can enter information into.
  • radio: Buttons that let a user select only one of a limited number of choices.
  • button: A clickable button that can activate JavaScript.
  • range: A slider control for setting a number whose exact value is not important.
  • number: A field for entering a number or toggling a number up and down.

… and many more. To check out others and get further background, it would be worth while visiting the Mozilla developer pages or w3schools.com.

While d3.js has the power to control and manipulate a web page to an extreme extent, sometimes it’s desirable to use a simple process to get a result. The following explanations will demonstrate a simple use case linking an HTML input with a d3.js element and will go on to provide examples of using multiple inputs, affecting multiple elements and using different input types. The examples are deliberately kept simple. They are intended to demonstrate functionality and to provide a starting position for you to go forward :-).

Using a range input with d3.js

The first example we will follow will use a range input to adjust the radius of a circle.

Adjust the radius of a circle
Adjust the radius of a circle
The code

The following is the full code for the example. A live version is available online at bl.ocks.org or GitHub. It is also available as the file ‘input-radius.html’ as a separate download with D3 Tips and Tricks. A copy of most the files that appear in the book can be downloaded (in a zip file) when you download the book from Leanpub.

<!DOCTYPE html>
<meta charset="utf-8">
<title>Input test (circle)</title>
  
<p>
  <label for="nRadius" 
         style="display: inline-block; width: 240px; text-align: right">
         radius = <span id="nRadius-value"></span>
  </label>
  <input type="range" min="1" max="150" id="nRadius">
</p>

<script src="http://d3js.org/d3.v3.min.js"></script>
<script>

var width = 600;
var height = 300;
 
var holder = d3.select("body")
      .append("svg")
      .attr("width", width)    
      .attr("height", height); 

// draw the circle
holder.append("circle")
  .attr("cx", 300)
  .attr("cy", 150) 
  .style("fill", "none")   
  .style("stroke", "blue") 
  .attr("r", 120);

// when the input range changes update the circle 
d3.select("#nRadius").on("input", function() {
  update(+this.value);
});

// Initial starting radius of the circle 
update(120);

// update the elements
function update(nRadius) {

  // adjust the text on the range slider
  d3.select("#nRadius-value").text(nRadius);
  d3.select("#nRadius").property("value", nRadius);

  // update the circle radius
  holder.selectAll("circle") 
    .attr("r", nRadius);
}

</script>
The explanation

As with the other examples in the book I will not go over some of the simpler lines of code that are covered in greater detail in earlier sections of the book and will concentrate on those sections that contain new concepts, code or look like they might need expanding :-).

The first section is the portion that sets out the html range input;

<p>
  <label for="nRadius" 
         style="display: inline-block; width: 240px; text-align: right">
         radius = <span id="nRadius-value"></span>
  </label>
  <input type="range" min="1" max="150" id="nRadius">
</p>

The entire block is enclosed in a paragraph (<p>) tag so that is appears on a single line. It can be broken down into the label that occurs before the input slider which is given the id nRadius-value and the input proper.

The for attribute of the label tag equals to the id attribute of the input element to bind them together. This allows us to update the text later as the slider is moved.

The input tag can include four attributes that specify restrictions on the operation of the slider;

  • max: specifies the maximum value allowed
  • min: specifies the minimum value allowed
  • step: specifies the number intervals as you move the slider
  • value: Specifies the default value

The ids supplied for both the label and the input are important since they provide the reference for our d3.js script.

The first portion of our JavaScript is fairly routine if you’ve been following along with the rest of the book.

var width = 600;
var height = 300;
 
var holder = d3.select("body")
      .append("svg")
      .attr("width", width)    
      .attr("height", height); 

// draw the circle
holder.append("circle")
  .attr("cx", 300)
  .attr("cy", 150) 
  .style("fill", "none")   
  .style("stroke", "blue") 
  .attr("r", 120);

We append an SVG element to the body of our page and then we append a circle with some particular styling to the SVG element.

Then things start to get more interesting…

d3.select("#nRadius").on("input", function() {
  update(+this.value);
});

We select our input using the id that we had declared earlier in the html (nRadius). Then we use the .on operator which adds what is called an ‘event listener’ to the element so that when there is a change in the element (in this case an adjustment of the slider of the input) a function is called (function()) that in turn calls the update function with the value from the input (+this.value). We haven’t seen the update function yet, but never fear, it’s coming.

We also call the update function with a specific value in the next line;

update(120);

This might seem slightly redundant, but unless the function gets a value, the text associated with the range input doesn’t get a reading and remains on ‘…’ until the slider is moved.

Lastly we have our update function;

function update(nRadius) {

  // adjust the text on the range slider
  d3.select("#nRadius-value").text(nRadius);
  d3.select("#nRadius").property("value", nRadius);

  // update the circle radius
  holder.selectAll("circle") 
    .attr("r", nRadius);
}

The first part of the function selects the label associated with our input (with the id, nRadius-value) and applies the vaule that has been passed into the function (nRadius). The next line selects the input itself and applies the value to it (this would be the equivalent of having value="<number here>" as a property in the html).

Lastly, we select the circle element and apply the new radius value based on our input value nRadius (.attr("r", nRadius)).

And there we have it, a fully adjustable radius for our circle controlled with an HTML input.

Maximum radius for our circle
Maximum radius for our circle

Using more than one input

In this example we will use two separate inputs (range type) to adjust the height and width of a rectangle.

Dual inputs
Dual inputs

This is not too much of a stretch from the previous single input example with the radius of a circle, but it may be useful to reinforce the concept and illustrate something slightly different.

The code

The following is the full code for the example. A live version is available online at bl.ocks.org or GitHub. It is also available as the file ‘input-double.html’ as a separate download with D3 Tips and Tricks. A copy of most the files that appear in the book can be downloaded (in a zip file) when you download the book from Leanpub.

<!DOCTYPE html>
<meta charset="utf-8">
<title>Double Input Test</title>

<p>
  <label for="nHeight" 
         style="display: inline-block; width: 240px; text-align: right">
         height = <span id="nHeight-value"></span>
  </label>
  <input type="range" min="1" max="280" id="nHeight">
</p>

<p>
  <label for="nWidth" 
         style="display: inline-block; width: 240px; text-align: right">
         width = <span id="nWidth-value"></span>
  </label>
  <input type="range" min="1" max="400" id="nWidth">
</p>

<script src="http://d3js.org/d3.v3.min.js"></script>
<script>

var width = 600;
var height = 300;
 
var holder = d3.select("body")
      .append("svg")
      .attr("width", width)    
      .attr("height", height); 

// draw a rectangle
holder.append("rect")
    .attr("x", 300)
    .attr("y", 150)
    .style("fill", "none")
    .style("stroke", "blue")
    .attr("height", 150) 
    .attr("width", 200);

// read a change in the height input
d3.select("#nHeight").on("input", function() {
  updateHeight(+this.value);
});

// read a change in the width input
d3.select("#nWidth").on("input", function() {
  updateWidth(+this.value);
});

// update the values
updateHeight(150);
updateWidth(100);

// Update the height attributes
function updateHeight(nHeight) {

  // adjust the text on the range slider
  d3.select("#nHeight-value").text(nHeight);
  d3.select("#nHeight").property("value", nHeight);

  // update the rectangle height
  holder.selectAll("rect") 
    .attr("y", 150-(nHeight/2)) 
    .attr("height", nHeight); 
}

// Update the width attributes
function updateWidth(nWidth) {

  // adjust the text on the range slider
  d3.select("#nWidth-value").text(nWidth);
  d3.select("#nWidth").property("value", nWidth);

  // update the rectangle width
  holder.selectAll("rect")
    .attr("x", 300-(nWidth/2)) 
    .attr("width", nWidth);
}

</script>
The explanation

For the sake of brevity, this explanation will simply concentrate on the differences between the previous single input example and this one.

The declarations for the inputs in the HTML at the start of the code are simply duplicates of each other in terms of function;

<p>
  <label for="nHeight" 
         style="display: inline-block; width: 240px; text-align: right">
         height = <span id="nHeight-value"></span>
  </label>
  <input type="range" min="1" max="280" id="nHeight">
</p>

<p>
  <label for="nWidth" 
         style="display: inline-block; width: 240px; text-align: right">
         width = <span id="nWidth-value"></span>
  </label>
  <input type="range" min="1" max="400" id="nWidth">
</p>

The only significant difference is the declaration of the id’s for each input and it’s respective label.

The JavaScript selection of the inputs is more duplication;

d3.select("#nHeight").on("input", function() {
  updateHeight(+this.value);
});

d3.select("#nWidth").on("input", function() {
  updateWidth(+this.value);
});

Again the only substantive difference is the use of the appropriate id values.

The updating of the width and height is done via two different functions;

function updateHeight(nHeight) {

  // adjust the text on the range slider
  d3.select("#nHeight-value").text(nHeight);
  d3.select("#nHeight").property("value", nHeight);

  // update the rectangle height
  holder.selectAll("rect") 
    .attr("y", 150-(nHeight/2)) 
    .attr("height", nHeight); 
}

// Update the width attributes
function updateWidth(nWidth) {

  // adjust the text on the range slider
  d3.select("#nWidth-value").text(nWidth);
  d3.select("#nWidth").property("value", nWidth);

  // update the rectangle width
  holder.selectAll("rect")
    .attr("x", 300-(nWidth/2)) 
    .attr("width", nWidth);
}

The rectangle is selected using a common rect designator, so multiple rectangles could be controlled. But each function controls only a specific attribute (height or width).

Rotate text with an input

This example is really just a derivative of the adjustment of a single attribute of an element.

I happen to think it’s just a little bit ‘neater’ because it includes text, but in reality, it’s just another attribute that can be adjusted.

Here we let our range input adjust the rotation of a piece of text.

Text rotation with an input
Text rotation with an input
The explanation

We’ll dispense with the full code listing since it’s just a regurgitation of the adjusting of the radius of the circle example, but the code for the example is available online at bl.ocks.org or GitHub. It is also available as the file ‘input-text-rotate.html’ as a separate download with D3 Tips and Tricks. A copy of most the files that appear in the book can be downloaded (in a zip file) when you download the book from Leanpub.

The only, thing of even a slight difference (other than some naming conventions) is the initial drawing of the text…

holder.append("text")
  .style("fill", "black")
  .style("font-size", "56px")
  .attr("dy", ".35em")
  .attr("text-anchor", "middle")
  .attr("transform", "translate(300,150) rotate(0)")
  .text("d3noob.org");

… and the update function;

function update(nAngle) {

  // adjust the text on the range slider
  d3.select("#nAngle-value").text(nAngle);
  d3.select("#nAngle").property("value", nAngle);

  // rotate the text
  holder.select("text") 
    .attr("transform", "translate(300,150) rotate("+nAngle+")");
}

Use a number input with d3.js

There are obviously different inputs that can be selected. The following example still rotates our text, but uses a number type of input to do it;

<p>
  <label for="nValue" 
         style="display: inline-block; width: 240px; text-align: right">
         angle = <span id="nValue-value"></span>
  </label>
  <input type="number" min="0" max="360" step="5" value="0" id="nValue">
</p>

we have set the step value to speed things up a bit when rotating, but it’s completely optional.

The input itself can be adjusted up or down using a mouse click or have a number typed into the input box.

Text rotation with a number input
Text rotation with a number input

This type of input is slightly different from the range type since it isn’t fully supported under Firefox and as a result when I was testing it the arrow keys for going up and down weren’t present.

The full code for the example is available online at bl.ocks.org or GitHub. It is also available as the file ‘input-number-text.html’ as a separate download with D3 Tips and Tricks. A copy of most the files that appear in the book can be downloaded (in a zip file) when you download the book from Leanpub.

Change more than one element with an input

The final example looking at using HTML inputs with d3.js incorporates a single input acting or two different elements. This might seem self evident, but if you’re as unfamiliar with HTML as I am (it’s embarrassing I know, but what can you do?) it may be of assistance.

The end result is to produce a single slider as a range input that rotates two separate text objects in different directions simultaneously.

Dual text rotation
Dual text rotation
The code

The following is the full code for the example. A live version is available online at bl.ocks.org or GitHub. It is also available as the file ‘input-text-rotate-2.html’ as a separate download with D3 Tips and Tricks. A copy of most the files that appear in the book can be downloaded (in a zip file) when you download the book from Leanpub.

<!DOCTYPE html>
<meta charset="utf-8">
<title>Input test</title>

<p>
  <label for="nAngle" 
         style="display: inline-block; width: 240px; text-align: right">
         angle = <span id="nAngle-value"></span>
  </label>
  <input type="range" min="0" max="360" id="nAngle">
</p>

<script src="http://d3js.org/d3.v3.min.js"></script>
<script>

var width = 600;
var height = 300;
 
var holder = d3.select("body")
      .append("svg")
      .attr("width", width)    
      .attr("height", height); 

// draw d3.js text
holder.append("text")
  .attr("class", "d3js")
  .style("fill", "black")
  .style("font-size", "56px")
  .attr("dy", ".35em")
  .attr("text-anchor", "middle")
  .attr("transform", "translate(300,55) rotate(0)")
  .text("d3.js");

// draw d3noob.org text
holder.append("text")
  .attr("class", "d3noob")
  .style("fill", "black")
  .style("font-size", "56px")
  .attr("dy", ".35em")
  .attr("text-anchor", "middle")
  .attr("transform", "translate(300,130) rotate(0)")
  .text("d3noob.org");

// when the input range changes update the rectangle 
d3.select("#nAngle").on("input", function() {
  update(+this.value);
});

// Initial starting height of the rectangle 
update(0);

// update the elements
function update(nAngle) {

// adjust the range text
  d3.select("#nAngle-value").text(nAngle);
  d3.select("#nAngle").property("value", nAngle);

  // adjust d3.js text
  holder.select("text.d3js") 
    .attr("transform", "translate(300,55) rotate("+nAngle+")");

  // adjust d3noob.org text
  holder.select("text.d3noob") 
    .attr("transform", "translate(300,130) rotate("+(360 - nAngle)+")");
}

</script>
The explanation

The explanation for this example differes from the others in the way that the d3.js elements (the two pieces of text) are initially appended and then updated.

When they are initially drawn…

holder.append("text")
  .attr("class", "d3js")
  .style("fill", "black")
  .style("font-size", "56px")
  .attr("dy", ".35em")
  .attr("text-anchor", "middle")
  .attr("transform", "translate(300,55) rotate(0)")
  .text("d3.js");

holder.append("text")
  .attr("class", "d3noob")
  .style("fill", "black")
  .style("font-size", "56px")
  .attr("dy", ".35em")
  .attr("text-anchor", "middle")
  .attr("transform", "translate(300,130) rotate(0)")
  .text("d3noob.org");

… both elements are declared with a class attribute that serves as a reference for the future updating. Here, the text ‘d3.js’ is given a class name of d3js and the text ‘d3noob.org’ is given a class name of d3noob.

Then when we call the update function each of the two text elements is adjusted seperately by selecting each based on the class name that was applied in the initial setup;

function update(nAngle) {

// adjust the range text
  d3.select("#nAngle-value").text(nAngle);
  d3.select("#nAngle").property("value", nAngle);

  // adjust d3.js text
  holder.select("text.d3js") 
    .attr("transform", "translate(300,55) rotate("+nAngle+")");

  // adjust d3noob.org text
  holder.select("text.d3noob") 
    .attr("transform", "translate(300,130) rotate("+(360 - nAngle)+")");
}

So the ‘d3.js’ text is selected using text.d3js and ‘d3noob.org’ is selected using text.d3noob. That’s a pretty neat trick and a good lesson for applying specific transformations to specific objects.

Add an HTML table to your graph

So graphs and graphics are D3’s bread and butter you’d think. Hmm…

Well yes and no.

Yes D3 has extraordinary powers for presenting and manipulating images in a web page. But if you’ve read through the entirety of the d3.js main site (haven’t we all) you will recall that D3 actually stands for Data Driven Documents. It’s not necessarily about the pretty pictures and the swirling cascade of colour. It’s about generating something in a web browser based on data.

This transitions nicely into consideration of adding a table of information that can accompany your graph (it could just as easily (or easier) stand alone, but for the sake of continuity, we’ll use the graph).

What we’ll do is add the data that we’ve used to make our graph under the graph itself. To make sure that it’s all nicely aligned, we’ll place it in a table.

It should end up looking a little like this (and this has been cropped slightly at the bottom to avoid expanding the page with rows of numbers / dates).

Basic graph with a table of data
Basic graph with a table of data

The code was drawn from an example provided by Shawn Allen on Google Groups. In fact, the post itself is an excellent one if you are considering creating a table straight from a csv file.

HTML Tables

Tables are made up of rows, columns and data (that goes in each cell). All you need to do to successfully place a table on a web page is to lay out the rows and columns in a logical sequence using the appropriate HTML tags and you’re away.

For example here’s the total HTML code for a web page to display a simple table;

<!DOCTYPE html>
<body>
    <table border="1">
        <tr>
            <th>Header 1</th>
            <th>Header 2</th>
        </tr>
        <tr>
            <td>row 1, cell 1</td>
            <td>row 1, cell 2</td>
        </tr>
        <tr>
            <td>row 2, cell 1</td>
            <td>row 2, cell 2</td>
        </tr>
    </table>
</body>

This will result in a table that looks a little like this in a web browser;

Header 1 Header 2
row 1, cell 1 row 1, cell 2
row 2, cell 1 row 2, cell 2

The entire table itself is enclosed in <table> tags. Each row is enclosed in <tr> tags. Each row has two items which equate to the two columns. Each piece of data for each cell is enclosed in a <td> tag except for the first row, which is a header and therefore has a special tag <th> that denotes it as a header making it bold and centred. For the sake of ease of viewing we have told the table to place a border around each cell and we do this in the first <table> tag with the border="1" statement (although in this book view it may be absent).

There are three main things you need to do to the basic line graph to get your table to display.

  1. Add some CSS
  2. Add some table building d3.js code
  3. Make a small but cunning change…

There is a copy of the code and the data file for this example at github and in the code samples bundled with this book (simple-graph-plus-table.html and data.csv). A live example can be found on bl.ocks.org.

First the CSS

This just helps the table with formatting and making sure the individual cells are spaced appropriately;

td, th {
    padding: 1px 4px;
}

This sets a padding of 1 px around each cell and 4 px between each column.

I’ve placed this portion of CSS at the end of our <style> section.

Now the d3.js code

Oki doki… Hopefully you have a loose understanding of the html layout of a table as explained above, but if not you can always go with the ‘it just works’ approach.

Here’s what we should add into our simple graph example;

function tabulate(data, columns) {
    var table = d3.select("body").append("table")
            .attr("style", "margin-left: 250px"),
        thead = table.append("thead"),
        tbody = table.append("tbody");

    // append the header row
    thead.append("tr")
        .selectAll("th")
        .data(columns)
        .enter()
        .append("th")
            .text(function(column) { return column; });

    // create a row for each object in the data
    var rows = tbody.selectAll("tr")
        .data(data)
        .enter()
        .append("tr");

    // create a cell in each row for each column
    var cells = rows.selectAll("td")
        .data(function(row) {
            return columns.map(function(column) {
                return {column: column, value: row[column]};
            });
        })
        .enter()
        .append("td")
        .attr("style", "font-family: Courier")
            .html(function(d) { return d.value; });
    
    return table;
}

// render the table
 var peopleTable = tabulate(data, ["date", "close"]);

And we should take care to add it into the code at the end of the portion where we’ve finished drawing the graph, but before the enclosing curly and regular brackets that complete the portion of the graph that has loaded our data.csv file. This is because we want our new piece of code to have access to that data and if we place it after those brackets it won’t know what data to display.

So, right about here;

        // Add the Y Axis
        svg.append("g")
            .attr("class", "y axis")
            .call(yAxis);
                                 // <= Add the code right here!
});

Now, we’re going to break with tradition a bit here and examine what our current state of code produces. Then we’re going to explain something different. THEN we’re going to come back and explain the code…

Check it out…

Woah! What happened to the date?
Woah! What happened to the date?

Not quite as we has originally envisaged?

Indeed, the date has taken it upon itself to expand from a relatively modest format of day-abbreviated month-two digit year (30-Apr-12) to a behemoth of a thing (Mon Apr 30 2012 00:00:00 GMT+1200 (New Zealand Standard Time)) that we certainly didn’t intend, let alone have in our data.csv file.

What’s going on here?

Well, To be perfectly frank, I’m not entirely sure. But this is what I’m going to propose. The JavaScript code recognises and deals with the ‘date’ variable as being a date/time. So that when we proceed to display the variable on the screen, the browser says, “this is a date / time formatted piece of data, therefore it must be formatted in the following way”. I had a play with a few ideas to correct it via an HTML formatting instruction, but drew a blank and then I stumbled on another way to solve the problem. Hence the third small but cunning change to our original code.

A small but cunning change…

So… Our table has decided to develop a mind of it’s own and format the date time as it sees fit. Well fair enough (I for one welcome our web time formatting overlords). So how do we convince it to display the values in their natural form?

Well, one solution that we could employ is to not tell the JavaScript that our date value in the data is actually time. In that condition, the code should treat the values as an ordinary string and print it directly as it appears.

The good news is that this is pretty easy to do. Where originally we had a block of data that consisted of date and close, all at different times, we will now add a new variable called date1 which will be the variable that we convert to a time and draw the graph with. Leaving date to be the text string that will be printed in our table.

How to do it?

It’s actually remarkably easy. Just change the following lines in the basic line graph code to amend date to date1 and you’re good to go.

    .x(function(d) { return x(d.date1); })
    d.date1 = parseDate(d.date);
    x.domain(d3.extent(data, function(d) { return d.date1; }));

The middle line is probably the most significant, since this is the point where we declare date1, assign a time format and bring a new column of data into being. The others simply refer to the data.

So we’ll make those small changes and now we can return to explain the d3.js code…

Explaining the d3.js code (reloaded).

So back we come to explain what is going on in the d3.js code that we presented a page or two back. Obviously it’s a fairly large chunk, and we can first break it down into two chunks. The first chunk we’ll look at is in fact the last part of the code that look like this;

// render the table
 var peopleTable = tabulate(data, ["date", "close"]);

This portion simply calls the tabulate function using the date and close columns of our data array. Simply add or remove whichever columns you want to appear in your table (so long as they are in your data.csv file) and they will be in your table. The tabulate function makes up all of the other part of the added code. So we come to the first block of the tabulate function;

function tabulate(data, columns) {
    var table = d3.select("body").append("table")
            .attr("style", "margin-left: 250px"),
        thead = table.append("thead"),
        tbody = table.append("tbody");

Here the tabulate function is declared (function tabulate) and the variables that the function will be using are specified ((data, columns)). In our case data is of course our data array and columns refers to ["date", "close"].

The next line appends the table to the body of the web page (so it will occur just under the graph in this case). The I do something just slightly sneaky. The line .attr("style", "margin-left: 250px"), is actually not the code that was used to produce the table with the huge date/ time formatted info on. I deliberately used .attr("style", "margin-left: 0px"), for the huge date / time table since it’s job is to indent the table by a specified amount from the left hand side of the page. And since the huge date time values would have pushed the table severely to the right, I cheated and used 0 instead of 250. For the purposes of the final example where the date / time values are formatted as expected, 250 is a good value.

The next two lines declare the functions we will use to add in the header cells (since they use the <th> tags for content) and the cells for the main body of the table (they use <td>).

The next block of code adds in the header row;

    thead.append("tr")
        .selectAll("th")
        .data(columns)
        .enter()
        .append("th")
            .text(function(column) { return column; });

Here we first append a row tag (<tr>), then we gather all the columns that we have in our function (remember they were ["date", "close"] and add them to our row using header tags (<th>).

The next block of code assigns the row variable to return (append) a row tag (<tr>) whenever it’s called …

    var rows = tbody.selectAll("tr")
        .data(data)
        .enter()
        .append("tr");

… and it is in the following block of code…

    var cells = rows.selectAll("td")
        .data(function(row) {
            return columns.map(function(column) {
                return {column: column, value: row[column]};
            });
        })
        .enter()
        .append("td")
        .attr("style", "font-family: Courier")
            .html(function(d) { return d.value; });

… where we select each row that we’ve added (var cells = rows.selectAll("td")). Then the following five lines works out from the intersection of the row and column which piece of data we’re looking at for each cell.

Then the last four lines take that piece of data (d.value) and wrap it in table data tags (<td>) and place it in the correct cell as HTML.

It’s a very neat piece of code and I struggle to get my head around it, but that doesn’t mean that I can’t appreciate the cleverness of it :-).

Wrap up

So there we have it. Hopefully enough to explain what is going on and perhaps also enough to convince ourselves that D3 is indeed more than just pretty pictures. It’s all about the Data Driven Documents.

This file has been saved as table-plus-graph.html and has been added into the downloads section on d3noob.org with the general examples files.

More table madness: sorting, prettifying and adding columns

When we last left our tables they were happily producing a faithful list of the data points that we had in our graph.

But what if we wanted more?

From the original contributors that bought you tables (Shawn Allen on Google Groups) and some neat additions from Christophe Viau comes extra coolness that I didn’t include in the previous example :-).

There is a copy of the code and the data file for this example at github and in the code samples bundled with this book (simple-graph-plus-table-plus-addins.html and data2.csv). A live example can be found on bl.ocks.org.

Add another column of information:

Firstly, lets add another column of data to our table. To do this we want to have something extra in our csv file to use, so let’s resurrect our old friend data2.csv that we used for the graph with two lines previously. All we have to do to make this a reality is change the reference that loads data.csv to data2.csv here;

d3.csv("data2.csv", function(error, data) {

From here (and as promised in the previous chapter), it’s just a matter of adding in the extra column you want (in this case it’s the open column) like so;

var peopleTable = tabulate(data, ["date", "close", "open"]);
Table with extra column
Table with extra column

So can we go further?

You know we can…

In the section where we get our data and format it, lets add another column to our array in the form of a difference between the close value and the open value (and we’ll call it diff).

d3.csv("data2.csv", function(error, data) { 
        data.forEach(function(d) {
                d.date1 = parseDate(d.date);
                d.close = +d.close;
                d.open = +d.open;  //  <= added this for tidy house keeping
                d.diff = Math.round(( d.close - d.open ) * 100 ) / 100;
        });

(the Math.round function is to make sure we get a reasonable figure to display, otherwise it tends to get carried away with decimal places)

So now we add in our new column (diff) to be tabulated;

var peopleTable = tabulate(data, ["date", "close", "open", "diff"]);
Table with extra extra column
Table with extra extra column

Sorting on a column

So now with our four columns of awesome data, it turns out that we’re really interested in the ones that have the highest close values. So we can sort on the close column by adding the following lines directly after the line where we declare the peopleTable function (which I will include in the code snipped below for reference).

var peopleTable = tabulate(data, ["date", "close", "open", "diff"]); 

peopleTable.selectAll("tbody tr") 
        .sort(function(a, b) {
                return d3.descending(a.close, b.close);
        });

Which works magnificently;

Table sorted descending on 'close'
Table sorted descending on ‘close’

Prettifying (actually just capitalising the header for each column)

Just a little snippet that capitalises the headers for each row to make them look slightly more authoritative.

Add the following lines of code directly below the block that you just added for sorting the table;

 peopleTable.selectAll("thead th")
        .text(function(column) {
                return column.charAt(0).toUpperCase() + column.substr(1);
        });

This is quite a tidy little piece of script. You can see it selecting the headers (selectAll("thead th")), then the first character in each header (column.charAt(0)), changing it to upper-case (.toUpperCase()) and adding it back to the rest of the string (+ column.substr(1)).

With the ultimate result…

Table with capitalised first characters in headers
Table with capitalised first characters in headers

Add borders

Sure our table looks nice and neatly arranged, but would a border look better?

Well, here’s one way to do it;

All we need to do is add a border style to our table by adding in this line here;

function tabulate(data, columns) {
    var table = d3.select("body").append("table")
            .attr("style", "margin-left: 200px") // <= Remove the comma
            .style("border", "2px black solid"), // <= Add this line in
        thead = table.append("thead"),
        tbody = table.append("tbody");

(don’t forget to move the comma from the end of the margin-left line)

And the result is a tidy black border.

Table with a border
Table with a border

OK, so what about the individual cells?

No problem.

If we remember back to our CSS that we added in, we’ll just tell each cell that we want a 1 pixel border buy amending the CSS for our table to this;

td, th {
    padding: 1px 4px;
    border: 1px black solid;
}

So now each cell has a slightly more subtle border like this;

Table with cells with individual borders
Table with cells with individual borders

Yikes! Not quite as subtle as I would have expected. I suppose it’s another example of the code actually doing what you asked it to do. No problem, border-collapse to the rescue. Add the following line into here;

function tabulate(data, columns) {
    var table = d3.select("body").append("table")
            .attr("style", "margin-left: 200px")
            .style("border-collapse", "collapse")   // <= Add this line in.
            .style("border", "2px black solid"),
        thead = table.append("thead"),
        tbody = table.append("tbody");

How does that look?

Table with cells with collapsed borders
Table with cells with collapsed borders

Ahh…. Much more refined.

The idea with this tip / trick is to be able to add a ‘link’ to an object so that when you click on it, it takes you to a web page that we will pre-define.

We are going to generate a simple rectangle with some text and look at linking from the rectangle and the text separately and with some fanciness at the end :-).

The end result will be something that looks a little like this;

Objects with links
Objects with links

(Notice the little pointing finger at the bottom that would indicate that there actually is a link there.)

The code that we will use as a starting point is this simple example that draws a green rectangle and overlays some text on it;

<!DOCTYPE html>
<meta charset="utf-8">

<body>

<!-- load the d3.js library -->	
<script src="http://d3js.org/d3.v3.min.js"></script>

<script>
 
var width = 449;
var height = 249;
var word = "gongoozler";
 
var holder = d3.select("body")
      .append("svg")
      .attr("width", width)    
      .attr("height", height); 

// draw a rectangle
holder.append("rect")  
    .attr("x", 100)
    .attr("y", 50)
    .attr("height", 100)
    .attr("width", 200)
    .style("fill", "lightgreen")
    .attr("rx", 10)
    .attr("ry", 10);

// draw text on the screen
holder.append("text")
    .attr("x", 200)
    .attr("y", 100)
    .style("fill", "black")
    .style("font-size", "20px")
    .attr("dy", ".35em")
    .attr("text-anchor", "middle")
    .text(word);

</script>

</body>

There’s nothing too spectacular about the file. There’s a little bit of styling and tweaking of attributes, but nothing too extreme. The only slightly ‘odd’ part would be defining the word that is printed out as a variable (var word = "gongoozler";) and then adding it as a variable (.text(word);) instead of just putting the word directly in there (which we could do like this .text("gongoozler");). We’re going to do this deliberately to explore additional options for making our links a little more dynamic.

The <a> tag in an HTML file defines a hyperlink. Items bounded by an <a> tag will become a link to another web address. So what we will do is create an <a> tag and then append our d3.js, svg object to it.

Of course as well as including a link, we need to tell it where to go. We do this by setting the xlink:href attribute for our tag to point to a specific page. Xlink is short for XML Linking Language and it is used to create hyperlinks in XML documents. In our case we will be defining the link that we will want our user to go to.

The following is the adjusted code for our rectangle that adds in the <a> tag with the xlink:href attribute.

holder.append("a")
    .attr("xlink:href", "http://en.wikipedia.org")
    .append("rect")  
    .attr("x", 100)
    .attr("y", 50)
    .attr("height", 100)
    .attr("width", 200)
    .style("fill", "lightgreen")
    .attr("rx", 10)
    .attr("ry", 10);

It’s important to append the link before the object (otherwise it won’t work) but other than that, it’s a pretty simple job.

The only fly in the ointment is that while we now have a rectangle that links to Wikipedia, if we hover our mouse over the text, we lose our link (since we haven’t told the text to link anywhere).

We can remedy that by doing exactly the same thing with the text element;

holder.append("a")
    .attr("xlink:href", "http://en.wikipedia.org/wiki/"+word)
    .append("text")
    .attr("x", 200)
    .attr("y", 100)
    .style("fill", "black")
    .style("font-size", "20px")
    .attr("dy", ".35em")
    .attr("text-anchor", "middle")
    .text(word);

The only slight difference here is that we have used the address for Wikipedia as our base and added the variable for our word to the end of it so that the resulting web address takes us to Wikipedia and the specific page for the word ‘gongoozler’. Hopefully this will indicate that if we had a set of variables in an array we would make our links a little more dynamic.

Making the mouse pointer ignore an object

So in theory we’re done, but in practice this has been a slightly crude method for adding what should be a single link to two objects when we should be able to accomplish it by defining the link once.

What we could do as an alternative to linking both the rectangle and the text using two separate links is to make the mouse ignore the text and have it rely solely on the rectangle. We can do this using the pointer-events style when drawing our text. By setting it to none we are instructing our mouse to ignore any potential interaction with the text when it hovers over it and instead the pointer will register the link on the rectangle below it.

The code for the text therefore becomes…

holder.append("text")
    .attr("x", 200)
    .attr("y", 100)
    .style("fill", "black")
    .style("font-size", "20px")
    .attr("dy", ".35em")
    .attr("text-anchor", "middle")
    .style("pointer-events", "none")
    .text(word);

And as you can see from the image below, the pointer will happily ignore the text while reading the link from the rectangle.

Objects with links
Objects with links

The complete code for this example is available in the appendices and a live version can be found on bl.ocks.org and GitHub. The code is also in the downloadable files available from Leanpub with the book in a file called ‘xlink-rect&pointerevents.html’.

Understanding JavaScript Object Notation (JSON)

One of the most useful things you might want to learn when understanding how to present your data with D3 is how to structure your data so that it is easy to use.

As explained earlier in the book, there are several different types of data that can be requested by D3 including text, Extensible Markup Language (xml), HyperText Markup Language (html), Comma Separated Values (csv), Tab Separated Values (tsv) and JavaScript Object Notation (json).

Comma separated values and tab separated values are fairly well understood forms of data. They are expressed as rows and columns of information that are separated using a known character. While these forms of data are simple to understand, it is not easy to incorporate a hierarchy structure to the data, and when you try, it isn’t natural and makes managing the data difficult.

JavaScript Object Notation (JSON) presents a different mechanism for storing data. A light weight description could read “JSON is a text-based open standard designed to present human-readable data. It is derived from the JavaScript scripting language, but it is language and platform independent.”

Unfortunately, when I first started using JSON, I struggled with the concept of how it was structured, in spite of some fine descriptions on the web (start with http://www.json.org/ in my humble opinion). So the following is how I came to think of and understand JSON.

In the following steps we’ll go through a process that (hopefully) demonstrates that we can transform identifiers that would represent the closing price for a stock of 58.3 on 2013-03-14 into more traditional x,y coordinates.

I think of data as having an identifier and a value.

identifier: value

If a point on a graph is located at the x,y coordinates 150,25 then the identifier ‘x’ has a value 150.

"x": 150

If the x axis was a time-line, the true value for ‘x’ could be “2013-03-14”.

"x": "2013-03-14"

This example might look similar to those seen by users of d3.js, since if we’re using date / time format we can let D3 sort out the messy parts like what coordinates to provide for the screen.

And there’s no reason why we couldn’t give the ‘x’ identifier a more human readable label such as “date”. So our data would look like;

"date": "2013-03-14"

This is only one part of our original x,y = 150,25 data set. The same way that the x value represented a position on the x axis that was really a date, the y value represents a position on the y axis that is really another number. It only gets converted to 25 when we need to plot a position on a graph at 150,25. If the ‘y’ component represents the closing price of a stock we could take the same principles used to transform…

"x": 150

… into …

"date": "2013-03-14"

… to change ….

"y": 25

… into …

"close": 58.3

This might sound slightly confusing, so try to think of it this way. We want to plot a point on a graph at 150,25, but the data that this position is derived from is really “2013-03-14”, 58.3. D3 can look after all the scaling and determination of the range so that the point gets plotted at 150,25 and our originating data can now be represented as;

"date": "2013-03-14", "close": 58.3

This represents two separate pieces of data. Each of which has an identifier (“date” or “close”) and a value (“2013-03-14” and 58.3)

If we wanted to have a series of these data points that represented several days of closing prices, we would store them as an array of identifiers and values similar to this;

{ "date": "2013-03-14", close: 58.13 },
{ "date": "2013-03-15", close: 53.98 },
{ "date": "2013-03-16", close: 67.00 },
{ "date": "2013-03-17", close: 89.70 },
{ "date": "2013-03-18", close: 99.00 }

Each of the individual elements of the array is enclosed in curly brackets and separated by commas.

Now that we have an array, we can apply the same rules to it as we did the the item that had a single value. We can give it an identifier all of its own. In this case we will call it “data”. Now we can use our identifier: value analogy to use “data” as the identifier and the array as the value.

{ "data": [
  { "date": "2013-03-14", close: 58.13 },
  { "date": "2013-03-15", close: 53.98 },
  { "date": "2013-03-16", close: 67.00 },
  { "date": "2013-03-17", close: 89.70 },
  { "date": "2013-03-18", close: 99.00 }
] }

The array has been enclosed in square brackets to designate it as an array and the entire identifier: value sequence has been encapsulated with curly braces (much the same way that the subset “date”, “close” values were enclosed with curly braces.

If we try to convey the same principle in a more graphical format, we could show our initial identifier and value for the x component like so;

Single identifier and value
Single identifier and value

The we can add our additional component for the y value;

Single identifier and value
Single identifier and value

We can then add several of these combinations together in an array;

Single identifier and value
Single identifier and value

Then the array becomes a value for another identifier “data”;

Single identifier and value
Single identifier and value

More complex JSON files will have multiple levels of identifiers and values arranged in complex hierarchies which can be difficult to interpret. However, laying out the data in a logical way in a text file is an excellent way to start to make sense of the data.

Using the Yahoo Query Language (YQL) to get data.

One of the things that I find frustrating is difficulty getting data to play with. Often I will find myself thinking “It would be neat to see if this technique works” but then I spend a couple of hours looking for data that will be appropriate.

Another difficulty is that access to dynamic data is also problematic. D3.js really shines when displaying information that is changing and interactive. Finding data that is changing and which you can interact with is not easy.

Then, when you do come across a web site that has an interesting data set, accessing that data becomes programmatically difficult because of restrictions in accessing information that crosses domains. A solution to this problem (and there are a few) is to have a data set that is in a domain that permits Cross-Origin Resource Sharing (CORS). These are not as common as you might hope as it presents security concerns that need to be addressed.

One resource that I have come across is particularly useful for solving the problems outlined above is the Yahoo! Developer Network which operates a service where you can write a query in an SQL type fashion that will return data in json (or others) format. This query can be formed into an http request so that you can simply paste a generated URL directly into your browsers URL bar and it will return the requested data. Better than that, because it supports CORS (and because d3.js can manage a CORS request without breaking a sweat) all you need to do is put the URL into you code (as a d3.json call for example) and you will be up and running.

In the ‘Examples’ chapter there will be several examples that use YQL queries to retrieve data as is is a great resource.

YQL by example

I will use an example of looking for current weather information for Wellington New Zealand from the Weather Underground (wunderground).

In this example, we’re interested in retrieving a JSON ‘blob’ that contains data on the weather conditions (temperature, wind speed / direction, humidity etc) for a specific location.

Start at the page for the console (http://developer.yahoo.com/yql/console/). On the left there is a range of data ‘topics that you can choose from. We will want to access one of the community tables, so select ‘Show Community Tables’ and scroll down the list until you find ‘wunderground’ and ‘wunderground.currentobservation’

Select Community Tables and wunderground.currentobservation
Select Community Tables and wunderground.currentobservation

This will bring up a query in the console that is looking for the current weather from San Francisco International airport.

Query for weather from San Francisco
Query for weather from San Francisco

For our example we want to be a bit more specific, so we replace the ‘SFO’ ‘location’ with ‘Wellington, New Zealand’.

If you then click on the ‘Test’ button the query will run and results will be presented in the box below it;

JSON weather from San Francisco
JSON weather from San Francisco

If that looks like the results you were expecting, fantastic!

The next gem from YQL is the REST query that is automatically generated at the bottom of the page;

REST Query
REST Query

If you select this query and past it in your URL bar, you will have the JSON data returned into your browser.

At this point it might seem a bit confusing, and if you’re unfamiliar with how JSON based data sets are structured it will DEFINITELY be confusing (You will need to do some research to get up to speed there). My advice is to first understand a bit about JSON and then examine the output from the REST query in the developer console on your browser (this is a fairly long topic in itself, so I will not cover it here sorry). Additionally check out the ‘Examples’ chapter for real world usage.

If you are comfortable with understanding how the data can be encoded, it is then just a matter of including the REST query in your call when selecting data using d3.json and you will be away.

Please check out the examples I will include in the ‘Examples’ chapter for a deeper look at the use of the data with d3.js (This will also help with understanding the JSON structure).

Using Plunker for development and hosting your D3 creations.

Recently Mike Bostock recommended ‘Plunker’ (http://plnkr.co/) as a tool for saving work online for collaboration and sharing. Although I had a quick look, I didn’t quite ‘get it’ and although it looked like something that I should be interested in, I (foolishly) moved on to other things.

Quite frankly I should have persevered.

Plunker is awesome.

So what can it do for you?

Well, in short, this gives you a place to put your graphs on the web without the hassle of needing a web server as well as allowing others to view and collaborate! There are some limitations to hosting graphs in this environment, but there’s no denying that for ease of use and visibility to the outside world, it’s outstanding!

Time for an example. I’ll try to go through the process of implementing the simple graph example on Plunker.

So it’s as simple as going to http://plnkr.co/edit/

Plunker editing page
Plunker editing page

What you’re seeing here is an area where you can place your entire HTML code. So let’s replace the 11 lines of the place holder code with the simple graph example (just copy and paste it in there over the top of the current 11 lines);

Now, there are two important things we have to do before it will work.

  1. We need to tell he script where to find d3.js
  2. We need to make our data accessible

Helping the script find d3.js is nice and easy. Just replace this line in your plunk;

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

…with this line…

<script src="http://d3js.org/d3.v3.min.js"></script>

That will allow your plunk to use the version of d3.js that is hosted on d3js.org (it uses the minimised version (which is why it has the ‘min’ in it), but never fear, it’s still d3, just distilled to enhance the flavour :-)).

Making our data available is only slightly more difficult.

In experimenting with Plunker, I found that there appears to be something ‘odd’ about accessing tab separated values (in the data.tsv file), however, D3 to the rescue! We can simply use Comma Separated Values (csv) instead.

We will host our data.csv file on plunker as well and there is built in functionality to let us do it.

Create a new file
Create a new file

In the top left hand corner, beside the ‘FILES’ note, there is a ‘+NEW…’ section. Clicking on this will allow you to create another file that will exist with your plunk for its use, so let’s do that.

This will open a dialogue box that will ask you to name your new file.

Name your file
Name your file

Enter the name data.csv.

Now another file has appeared under the ‘Files’ heading called data.csv. Click on it.

The empty data.csv file
The empty data.csv file

This now shows us a blank file called data.csv, so now open up your data.csv file in whatever editor you’re using (I don’t think a spreadsheet program is going to be a good idea since I doubt that it will maintain the information in a textual form as we’re wanting it to do. So it’s Geany for me). Copy the contents of your local data.csv file and paste it into the new plunker data.csv file.

So now we have our data in there we need to tell our JavaScript where it is. So go back to the ‘index.html’ file (which is our simple graph code) and edit the line which finds the data.tsv file from this

d3.tsv("data/data.tsv", function(error, data) {

… to this …

d3.csv("data.csv", function(error, data) {

Because we’re using relative addressing, and plunker stores the files for the graphing script and the data side by side, we just removed the portion of the address that told our original code to look in the ‘data’ directory and told it to look in the current directory. And that should be that!

Now if you look on the right hand side of the screen, there is a little eye icon. If you click on it, it opens up a preview window of your file in action and viola!

Preview your graph
Preview your graph

If the graph doesn’t appear, go through the steps outlined above and just check that the edits are made correctly. Unfortunately I haven’t found a nice mechanism for troubleshooting inside Plunker yet (not like using F12 on Chrome).

But wait! There’s more!

If you now click on the ‘Save’ button at the top of the screen, you will get some new button options.

One of them is the orange one for showing off your work.

Show off your work
Show off your work

If you click on this, it will present you with several different options.

Preview your graph
Preview your graph

The first one is a link that will give others the option to collaborate on the script.

The second is a link that will allow others to preview the work; http://embed.plnkr.co/QSCkG8Rf2qFgrCqq7Vfn

The last will allow you to embed your graph in a separate web page somewhere. Which I’ve tested with blogger and seems to work really well! (see image below).

Plunker iframe inserted in a blog post
Plunker iframe inserted in a blog post

So, I’m impressed, Nice work by Plunker and it’s creator Geoff Goodman.

Manipulating data

How to use data imported from a csv file with spaces in the header.

When importing data from a csv file that has headers with spaces in the middle of some of the fields there is a need to address the data slightly differently in order for it to be used easily in your JavaScript.

For example the following csv data has a column named ‘Date Purchased’;

Value,Date Purchased,Score
12345,2011-03-23,99
22345,2011-03-24,100
32345,2011-03-25,99
42345,2011-03-26,100

This is not an uncommon occurrence since RFC 4180 which specifies csv content allows for it and d3.js supports the RFC;

Within the header and each record, there may be one or more fields, separated by commas. Each line should contain the same number of fields throughout the file. Spaces are considered part of a field and should not be ignored.

When we go to import the data using the d3.csv function, we need to reference the ‘Data Purchased’ column in a way that makes allowances for the space. The following piece of script (with grateful thanks to Stephen Thomas for answering my Stack Overflow question) appears to be the most basic solution.

d3.csv("sample-data.csv", function(error, data) {
    data.forEach(function(d) {
        d.date = parseDate(d['Date Purchased']);
    });
...
});

In the example above the ‘Date Purchased’ column is re-declared as ‘date’ making working in the following script much easier.

Extracting data from a portion of a string.

The example problem here would be as if we have a set of values in a string that we want to extract but which in their original form it would not be possible.

For example, the following csv file contains the column ‘value’ and the values of the data in that column are prefixed with a dollar sign ($).

value,date,score
$1234,2011-03-23,99
$2234,2011-03-24,100
$3234,2011-03-25,99
$4235,2011-03-26,100

We can use the JavaScript substring() method to easily remove the leading character from the data.

The following example processes our csv file after loading it and for each ‘value’ entry on each row takes a substring of the entry that removes the first character and retains the rest.

d3.csv("sample-data.csv", function(error, data) {
    data.forEach(function(d) {
        d.value = +d.value.substring(1);
    });
...
});

The substring() function includes a ‘start’ index (as used above) and optionally a ‘stop’ index. More on how these can be configured can be found on the w3schools site.

Grouping and summing data (d3.nest).

Often we will wish to group elements in an array into a hierarchical structure similar to the GROUP BY operator in SQL (but with the scope for multiple levels). This can be achieved using the d3.nest operator. Additionally we will sometimes wish to collapse the elements that we are grouping in a specific way (for instance to sum values). This can be achieved using the rollup function.

The example we will use is having the following csv file consisting of a column of dates and corresponding values;

date,value
2011-03-23,3
2011-03-23,2
2011-03-24,3
2011-03-24,3
2011-03-24,6
2011-03-24,2
2011-03-24,7
2011-03-25,4
2011-03-25,5
2011-03-25,1
2011-03-25,4

We will nest the data according to the date and sum the data for each date so that our data is in the equivalent form of;

key,values
2011-03-23,5
2011-03-24,21
2011-03-25,14

We will do this with the following script;

d3.csv("source-data.csv", function(error, csv_data) {
	var data = d3.nest()
		.key(function(d) { return d.date;})
		.rollup(function(d) { 
			return d3.sum(d, function(g) {return g.value; });
		}).entries(csv_data);
...
});

We are assuming the data is in the form of our initial csv file and is named source-data.csv.

The first thing we do is load that file and assign the loaded array the variable name csv_data.

d3.csv("source-data.csv", function(error, csv_data) {

Then we declare our new array’s name will be data and we initiate the nest function;

	var data = d3.nest()

We assign the key for our new array as date. A ‘key’ is like a way of saying “This is the thing we will be grouping on”. In other words our resultant array will have a single entry for each unique date value.

		.key(function(d) { return d.date;})

Then we include the rollup function that takes all the individual value variables that are in each unique date field and sums them;

		.rollup(function(d) { 
			return d3.sum(d, function(g) {return g.value; });

Lastly we tell the entire nest function which data array we will be using for our source of data.

		}).entries(csv_data);

What if your data turns out to be unsorted? Never fear, we can easily sort on the key value by tacking on the sortKeys function like so;

        .key(function(d) { return d.date;}).sortKeys(d3.ascending)

You should note that our data will have changed name from date and value. This is as a function of the nest and rollup process. But never fear, it’s a simple task to re-name them if necessary using the following function (which could include a call to parse the date, but I have omitted it for clarity);

data.forEach(function(d) {
	d.date = d.key;
	d.value = d.values;
});

Padding for zero values

The ‘Padding for zero values’ question is one that I posted on Stack Overflow in April 2014.

The premiss is that I want to be able to graph points in a time series that has regular intervals but where some of those intervals are missing. For example a line graph of the following data (notice that August 2013 is missing)…

date,value
2013-01,53
2013-02,165
2013-03,269
2013-04,344
2013-05,376
2013-06,410
2013-07,421
2013-09,376
2013-10,359
2013-11,392
2013-12,433
2014-01,455
2014-02,478

… would produce a graph that looked like the following

interpolated time series
interpolated time series

But if the reason for the missing August month was that the value would have been zero, then the graph should actually look like this;

interpolated time series
interpolated time series

Now, this may sound like a contrived example, but I have come across it in cases where querying for data from a MySQL database by counting (COUNT(*)) the number of instances of an event in a time series (number of sales of an individual item on a monthly basis for instance). The end result is that you are left with a list of months with values for those months where sales occurred, but where no sales occurred, there is no result at all. This can be overcome by some serious MySQL-fu or PHP trickery, but every solution I implemented had a ‘cracking a walnut with a sledgehammer’ feel about it.

Now I know that shifting the resolution of this problem from the server to the client might not sit well for everyone, but that doesn’t mean it’s not a suitable solution.

The solution that was provided by the user ‘explunit’ has a nice natural feel to it and in spite of introducing an additional JavaScript library to load (Lo-Dash) it’s a solution that I will be using in the future for this type of problem. You will need to bear in mind that the lodash.js library will need to be loaded to enable this solution.

<script src=
  "http://cdnjs.cloudflare.com/ajax/libs/lodash.js/2.4.1/lodash.min.js">
</script>

Lo-Dash

Lo-Dash is a utility JavaScript library released under the MIT license that provides a range of useful functional programming helpers such as low level functions that can be used for iteration support for arrays, strings, objects, and arguments objects. It is often compared to underscore.js in terms of functionality.

_.find
_.find(collection, [callback=identity], [thisArg])

The ` _.find` function iterates over elements of a collection, returning the first element that the callback returns ‘truey’ for. The callback is bound to thisArg and invoked with three arguments; (value, index|key, collection).

If a property name is provided for callback the created “_.pluck” style callback will return the property value of the given element.

If an object is provided for callback the created “_.where” style callback will return true for elements that have the properties of the given object, else false.

Arguments

  1. collection (Array|Object|string): The collection to iterate over.
  2. [callback=identity] (Function|Object|string): The function called per iteration. If a property name or object is provided it will be used to create a “.pluck” or “.where” style callback, respectively.
  3. [thisArg] (*): The this binding of callback.

The explunit method

The following is the layout of the critical part of the code that the explunit method uses.

var period = d3.time.month; // set the time interval

// Scale the range of the data
x.domain(d3.extent(data, function(d) { return d.date; }))
     .ticks(period);
y.domain([0, d3.max(data, function(d) { return d.value; })]);

// Populate the new array
var newData = x.ticks(period)
               .map(function(monthBucket) { 
                   return _.find(data, 
                       { date: monthBucket }) || 
                       { date: monthBucket, value: 0 };
                });

It can be thought of as breaking the problem down into two steps. Firstly a new array is built that covers the range of values declared in the data and contains dates at a specified interval. Secondly, the script iterates over the old data and where the date matches a date in the new array it maps the value from the old array to the new array. Where there is no match, it maps the value of 0.

Build the array
scale.ticks([count])

The domain in the x axis is set using the following line;

x.domain(d3.extent(data, function(d) { return d.date; }))
     .ticks(period);

Here the extent of the domain is declared in a standard way (d3.extent(data, function(d) { return d.date; })) but by including the .ticks function the domain is broken into uniformly spaced time intervals defined by the specified time interval (in this case months per the declared variable period (d3.time.month)). This has just created an array across the x axis with all the month values!

Populate the array

In the next section we generate our new array (newData) with the values from the original data array and where necessary (there’s no value) 0 in the value field.

It’s all contained within the following code;

var newData = x.ticks(period)
               .map(function(monthBucket) { 
                   return _.find(data, 
                       { date: monthBucket }) || 
                       { date: monthBucket, value: 0 };
                });
x.ticks()

The x.ticks(period) portion recalls our new padded array of months.

.map([object])

In a wider (JavaScript) sense the map() method creates a new array by copying all enumerable properties from the object into the new array. This is implemented with .map which is used here;

               .map(function(monthBucket) { 
                   return _.find(data, 
                       { date: monthBucket }) || 
                       { date: monthBucket, value: 0 };
                });

In the example we are examining, the [object] is provided by the passing the date from the x.ticks(period) function as monthBucket. Whatever is returned by the function will get placed into newData. It’s within this function that we use the Lo-Dash _.find utility.

In common speak _.find will look over over the objects in an array (data) and will return the the first instance that it finds which matches its specified patten (in our case it will match whenever the date element in data equals an array element specified by our x.ticks(period) (monthBucket) values or (if no match is made) it will return the date element from monthBucket and the value of 0.

The end result is an array called newData of objects containing equally spaced date elements across the range that we provided at an interval that we specify with values corresponding to our original array data where it matches the date in our new array and where it doesn’t, we introduce the value 0.

Bar Charts

A bar chart is a visual representation using either horizontal or vertical bars to show comparisons between discrete categories. There are a number of variations of bar charts including stacked, grouped, horizontal and vertical.

There is a wealth of examples of bar charts on the web, but I would recommend a visit to the D3.js gallery maintained by Christophe Viau as a starting point to get some ideas.

We will work through a simple vertical bar chart that uses a value on the y axis and date values on the x axis.

The end result will look like this;

Bar chart
Bar chart

The data

The data for this example will be sourced from an external csv file named bar-data.csv. It consists of a column of dates in year-month format and it’s contents are as follows;

date,value
2013-01,53
2013-02,165
2013-03,269
2013-04,344
2013-05,376
2013-06,410
2013-07,421
2013-08,405
2013-09,376
2013-10,359
2013-11,392
2013-12,433
2014-01,455
2014-02,478

The code

The full code listing for the example we are going to work through is as follows;

<!DOCTYPE html>
<meta charset="utf-8">

<head>
	<style>

	.axis {
	  font: 10px sans-serif;
	}

	.axis path,
	.axis line {
	  fill: none;
	  stroke: #000;
	  shape-rendering: crispEdges;
	}

	</style>
</head>

<body>
	
<script src="http://d3js.org/d3.v3.min.js"></script>

<script>

var margin = {top: 20, right: 20, bottom: 70, left: 40},
    width = 600 - margin.left - margin.right,
    height = 300 - margin.top - margin.bottom;

// Parse the date / time
var	parseDate = d3.time.format("%Y-%m").parse;

var x = d3.scale.ordinal().rangeRoundBands([0, width], .05);

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

var xAxis = d3.svg.axis()
    .scale(x)
    .orient("bottom")
    .tickFormat(d3.time.format("%Y-%m"));

var yAxis = d3.svg.axis()
    .scale(y)
    .orient("left")
    .ticks(10);

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 + ")");

d3.csv("bar-data.csv", function(error, data) {

    data.forEach(function(d) {
        d.date = parseDate(d.date);
        d.value = +d.value;
    });
	
  x.domain(data.map(function(d) { return d.date; }));
  y.domain([0, d3.max(data, function(d) { return d.value; })]);

  svg.append("g")
      .attr("class", "x axis")
      .attr("transform", "translate(0," + height + ")")
      .call(xAxis)
    .selectAll("text")
      .style("text-anchor", "end")
      .attr("dx", "-.8em")
      .attr("dy", "-.55em")
      .attr("transform", "rotate(-90)" );

  svg.append("g")
      .attr("class", "y axis")
      .call(yAxis)
    .append("text")
      .attr("transform", "rotate(-90)")
      .attr("y", 6)
      .attr("dy", ".71em")
      .style("text-anchor", "end")
      .text("Value ($)");

  svg.selectAll("bar")
      .data(data)
    .enter().append("rect")
      .style("fill", "steelblue")
      .attr("x", function(d) { return x(d.date); })
      .attr("width", x.rangeBand())
      .attr("y", function(d) { return y(d.value); })
      .attr("height", function(d) { return height - y(d.value); });

});

</script>

</body>

The bar chart explained

In the course of describing the operation of the file I will gloss over the aspects of the structure of an HTML file which have already been described at the start of the book. Likewise, aspects of the JavaScript functions that have already been covered will only be briefly explained.

The start of the file deals with setting up the document’s head and body, loading the d3.js script and setting up the css in the <style> section.

The css section sets styling for the axes. It sizes the font to be used and make sure the lines are formatted appropriately.

	.axis {
	  font: 10px sans-serif;
	}

	.axis path,
	.axis line {
	  fill: none;
	  stroke: #000;
	  shape-rendering: crispEdges;
	}

Then our JavaScript section starts and the first thing that happens is that we set the size of the area that we’re going to use for the chart and the margins;

var margin = {top: 20, right: 20, bottom: 70, left: 40},
    width = 600 - margin.left - margin.right,
    height = 300 - margin.top - margin.bottom;

The next section of our code includes some of the functions that will be called from the main body of the code.

We have a familiar parseDate function with a slight twist. Since our source data for the date is made up of only the year and month, these are the only two portions of the date that need to be recognised;

var	parseDate = d3.time.format("%Y-%m").parse;

The next section declares the function to determine positioning in the x domain.

var x = d3.scale.ordinal().rangeRoundBands([0, width], .05);

The ordinal scale is used to describe a range of discrete values. In our case they are a set of monthly values. The rangeRoundBands operator provides the magic that arranges our bars in a graceful way across the x axis. In our example we use it to set the range that our bars will cover (in this case from 0 to the width of the graph) and the amount of padding between the bars (in this case we have selected .05 which equates to approximately (depending on the number of pixels available) 5% of the bar width.

The function to set the scaling in the y domain is the same as most of our other graph examples;

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

The declarations for our two axes are relatively simple, with the only exception being to force the format of the labels for the x axis into a ‘year-month’ format.

var xAxis = d3.svg.axis()
    .scale(x)
    .orient("bottom")
    .tickFormat(d3.time.format("%Y-%m"));

var yAxis = d3.svg.axis()
    .scale(y)
    .orient("left")
    .ticks(10);

The next block of code selects the body on the web page and appends an svg object to it of the size that we have set up with our width, height and margin’s.

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 + ")");

It also adds a g element that provides a reference point for adding our axes.

Then we begin the main body of our JavaScript. We load our csv file and then loop through it making sure that the dates and numerical values are recognised correctly;

d3.csv("bar-data.csv", function(error, data) {

    data.forEach(function(d) {
        d.date = parseDate(d.date);
        d.value = +d.value;
    });

We then then work through our x and y data and ensure that it is scaled to the domains we are working in;

  x.domain(data.map(function(d) { return d.date; }));
  y.domain([0, d3.max(data, function(d) { return d.value; })]);

Following that we append our x axis;

  svg.append("g")
      .attr("class", "x axis")
      .attr("transform", "translate(0," + height + ")")
      .call(xAxis)
    .selectAll("text")
      .style("text-anchor", "end")
      .attr("dx", "-.8em")
      .attr("dy", "-.55em")
      .attr("transform", "rotate(-90)" );

This is placed in the correct position .attr("transform", "translate(0," + height + ")") and the text is positioned (using dx and dy) and rotated (.attr("transform", "rotate(-90)" );) so that it is aligned vertically.

Then we append our y axis in a similar way and append a label (.text("Value ($)"););

  svg.append("g")
      .attr("class", "y axis")
      .call(yAxis)
    .append("text")
      .attr("transform", "rotate(-90)")
      .attr("y", 6)
      .attr("dy", ".71em")
      .style("text-anchor", "end")
      .text("Value ($)");

Lastly we add the bars to our chart;

  svg.selectAll("bar")
      .data(data)
    .enter().append("rect")
      .style("fill", "steelblue")
      .attr("x", function(d) { return x(d.date); })
      .attr("width", x.rangeBand())
      .attr("y", function(d) { return y(d.value); })
      .attr("height", function(d) { return height - y(d.value); });

This block of code creates the bars (selectAll("bar")) and associates each of them with a data set (.data(data)).

We then append a rectangle (.append("rect")) with values for x/y position and height/width as configured in our earlier code.

The end result is our pretty looking bar chart;

Bar chart
Bar chart

Sankey Diagrams

What is a Sankey Diagram?

A Sankey diagram is a type of flow diagram where the ‘flow’ is represented by arrows of varying thickness depending on the quantity of flow.

They are often used to visualize energy, material or cost transfers and are especially useful in demonstrating proportionality to a flow where different parts of the diagram represent different quantities in a system.

Probably the most famous example of a Sankey diagram is Charles Minard’s Map of Napoleon’s Russian Campaign of 1812.

Napoleon's Russian March
Napoleon’s Russian March

From Wikipedia;

Étienne-Jules Marey first called notice to this dramatic depiction of the fate of Napoleon’s army in the Russian campaign, saying it defies the pen of the historian in its brutal eloquence. Edward Tufte says it “may well be the best statistical graphic ever drawn” and uses it as a prime example in The Visual Display of Quantitative Information.”

Wikipedia has a great explanation of the diagram type and there is a wealth of information dedicated to it on the inter-web. I heartily recommend http://www.sankey-diagrams.com/ for all things Sankey!

So it would come as little surprise that Mike Bostock has developed a plugin for Sankey diagrams (http://bost.ocks.org/mike/sankey/) so that we can all enjoy Sankey goodness with lashings of D3.

How d3.js Sankey Diagrams want their data formatted

If we think of Sankey diagrams consisting of ‘nodes’ and ‘links’…

A simple Sankey diagram
A simple Sankey diagram

… the data that generates them must be formatted as nodes and links as well.

For instance a JSON file with appropriate data to build the diagram above could look like the following;

{
"nodes":[
{"node":0,"name":"node0"},
{"node":1,"name":"node1"},
{"node":2,"name":"node2"},
{"node":3,"name":"node3"},
{"node":4,"name":"node4"}
],
"links":[
{"source":0,"target":2,"value":2},
{"source":1,"target":2,"value":2},
{"source":1,"target":3,"value":2},
{"source":0,"target":4,"value":2},
{"source":2,"target":3,"value":2},
{"source":2,"target":4,"value":2},
{"source":3,"target":4,"value":4}
]}

In the file above we have 6 nodes (0-5) sequentially numbered and with names appropriate to their position in the list.

The sequential numbering is only for the purpose of highlighting the structure of the data, since when we get D3 running, it will automatically index each of the nodes according to its position. In other words, we could have omitted the “node”:n parts since D3 will know where each node is anyway. The big deal is that WE need to know what each node is as well especially if we’re going to be building the data by hand (doing it dynamically would be cool, but let’s not get ahead of ourselves just yet).

The links part of the data can be broken down into individual source to target ‘links’ that have an associated value (could be a quantity or strength, but at least a numeric value).

The ‘source’ and target numbers are references to the list of nodes. So, “source”:1, “target”:2 means that this link is whatever node appears at position 1 going to whatever node appears at position 2. The important point to make here is that D3 will not be interested in the numerical value of the node, just it’s position in the list (starting at zero).

Description of the code

The code for the Sankey diagram is significantly different to that for a line graph although it shares the same core language and programming methodology.

The code we’ll go through is an adaptation of the version first demonstrated by Mike Bostock so it’s got a pretty good pedigree. I will begin with a version that uses data that is formatted so that it can be used directly with no manipulation, then in subsequent sections I will describe different techniques for getting data from different formats to work.

I found that getting data in the correct format was the biggest hurdle for getting a Sankey diagram to work. I make the assumption that this may be a similar story for others as well. We will start off assuming that the data is perfectly formatted, then where only the link data is available, then where there is just names to work with (no numeric node values) and lastly, one that can be used for people with changeable data from a MySQL database.

I won’t try to go over every inch of the code as I did with the previous simple graph example (I’ll skip things like the HTML header) and will focus on the style sheet (CSS) portion and the JavaScript.

The full code for this example can be found on github or in the code samples bundled with this book (sankey-formatted-json.html, sankey.js and sankey-formatted.json). A live example can be found on bl.ocks.org.

On to the code…

<style>
.node rect {
  cursor: move;
  fill-opacity: .9;
  shape-rendering: crispEdges;
}
.node text {
  pointer-events: none;
  text-shadow: 0 1px 0 #fff;
}
.link {
  fill: none;
  stroke: #000;
  stroke-opacity: .2;
}
.link:hover {
  stroke-opacity: .5;
}
</style>
<body>
<p id="chart">
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="sankey.js"></script>
<script>
var units = "Widgets";

var margin = {top: 10, right: 10, bottom: 10, left: 10},
    width = 700 - margin.left  margin.right,
    height = 300 - margin.top  margin.bottom;

var formatNumber = d3.format(",.0f"),    // zero decimal places
    format = function(d) { return formatNumber(d) + " " + units; },
    color = d3.scale.category20();

// append the svg canvas to the page
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 + ")");

// Set the sankey diagram properties
var sankey = d3.sankey()
    .nodeWidth(36)
    .nodePadding(40)
    .size([width, height]);

var path = sankey.link();

// load the data
d3.json("sankey-formatted.json", function(error, graph) {

  sankey
      .nodes(graph.nodes)
      .links(graph.links)
      .layout(32);

// add in the links
  var link = svg.append("g").selectAll(".link")
      .data(graph.links)
    .enter().append("path")
      .attr("class", "link")
      .attr("d", path)
      .style("stroke-width", function(d) { return Math.max(1, d.dy); })
      .sort(function(a, b) { return b.dy - a.dy; });

// add the link titles
  link.append("title")
        .text(function(d) {
    		return d.source.name + " → " + 
                d.target.name + "\n" + format(d.value); });

// add in the nodes
  var node = svg.append("g").selectAll(".node")
      .data(graph.nodes)
    .enter().append("g")
      .attr("class", "node")
      .attr("transform", function(d) { 
		  return "translate(" + d.x + "," + d.y + ")"; })
    .call(d3.behavior.drag()
      .origin(function(d) { return d; })
      .on("dragstart", function() { 
		  this.parentNode.appendChild(this); })
      .on("drag", dragmove));

// add the rectangles for the nodes
  node.append("rect")
      .attr("height", function(d) { return d.dy; })
      .attr("width", sankey.nodeWidth())
      .style("fill", function(d) { 
		  return d.color = color(d.name.replace(/ .*/, "")); })
      .style("stroke", function(d) { 
		  return d3.rgb(d.color).darker(2); })
    .append("title")
      .text(function(d) { 
		  return d.name + "\n" + format(d.value); });

// add in the title for the nodes
  node.append("text")
      .attr("x", -6)
      .attr("y", function(d) { return d.dy / 2; })
      .attr("dy", ".35em")
      .attr("text-anchor", "end")
      .attr("transform", null)
      .text(function(d) { return d.name; })
    .filter(function(d) { return d.x < width / 2; })
      .attr("x", 6 + sankey.nodeWidth())
      .attr("text-anchor", "start");

// the function for moving the nodes
  function dragmove(d) {
    d3.select(this).attr("transform", 
        "translate(" + (
            d.x = Math.max(0, Math.min(width - d.dx, d3.event.x))
        )
        + "," + (
            d.y = Math.max(0, Math.min(height - d.dy, d3.event.y))
        ) + ")");
    sankey.relayout();
    link.attr("d", path);
  }
});

So, going straight to the style sheet bounded by the <style> tags;

.node rect {
  cursor: move;
  fill-opacity: .9;
  shape-rendering: crispEdges;
}

.node text {
  pointer-events: none;
  text-shadow: 0 1px 0 #fff;
}

.link {
  fill: none;
  stroke: #000;
  stroke-opacity: .2;
}

.link:hover {
  stroke-opacity: .5;
}

The CSS in this example is mainly concerned with formatting of the mouse cursor as it moves around the diagram.

The first part…

.node rect {
  cursor: move;
  fill-opacity: .9;
  shape-rendering: crispEdges;
}

… provides the properties for the node rectangles. It changes the icon for the cursor when it moves over the rectangle to one that looks like it will move the rectangle (there is a range of different icons that can be defined here http://www.echoecho.com/csscursors.htm), sets the fill colour to mostly opaque and keeps the edges sharp.

The next block…

.node text {
  pointer-events: none;
  text-shadow: 0 1px 0 #fff;
}

… sets the properties for the text at each node. The mouse is told to essentially ignore the text in favour of anything that’s under it (in the case of moving or highlighting something else) and a slight shadow is applied for readability).

The following block…

.link {
  fill: none;
  stroke: #000;
  stroke-opacity: .2;
}

… makes sure that the link has no fill (it actually appears to be a bendy rectangle with very thick edges that make the element appear to be a solid block), colours the edges black (#000) and makes the edges almost transparent.

The last block….

.link:hover {
  stroke-opacity: .5;
}

… simply changes the opacity of the link when the mouse goes over it so that it’s more visible. If so desired, we could change the colour of the highlighted link by adding in a line to this block changing the colour like this stroke: red;.

Just before we get into the JavaScript, we do something a little different for d3.js. We tells it to use a plug-in with the following line;

<script src="sankey.js"></script>

The concept of a plug-in is that it is a separate piece of code that will allow additional functionality to a core block (which in this case is d3.js). There are a range of plug-ins available and we will need to source the sankey.js file from the repository and place that somewhere where our HTML code can access it. In this case I have put it in the same directory as the main sankey web page.

The start of our JavaScript begins by defining a range of variables that we’ll be using.

Our units are set as ‘Widgets’ (var units = "Widgets";), which is just a convenient generic (nonsense) term to provide the impression that the flow of items in this case is widgets being passed from one person to another.

We then set our canvas size and margins…

var margin = {top: 10, right: 10, bottom: 10, left: 10},
    width = 700 - margin.left  margin.right,
    height = 300 - margin.top  margin.bottom;

… before setting some formatting.

var formatNumber = d3.format(",.0f"),    // decimal places
    format = function(d) { return formatNumber(d) + " " + units; },
    color = d3.scale.category20();

The formatNumber function acts on a number to set it to zero decimal places in this case. In the original Mike Bostock example it was to three places, but for ‘widgets’ I’m presuming we don’t divide :-).

format is a function that returns a given number formatted with formatNumber as well as a space and our units of choice (‘Widgets’). This is used to display the values for the links and nodes later in the script.

The color = d3.scale.category20(); line is really interesting and provides access to a colour scale that is pre-defined for your convenience! Later in the code we will see it in action.

Our next block of code positions our canvas onto our page in relation to the size and margins we have already defined;

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 + ")");

Then we set the variables for our sankey diagram;

var sankey = d3.sankey()
    .nodeWidth(36)
    .nodePadding(40)
    .size([width, height]);

Without trying to state the obvious, this sets the width of the nodes (.nodeWidth(36)), the padding between the nodes (.nodePadding(40)) and the size of the diagram(.size([width, height]);).

The following line defines the path variable as a pointer to the sankey function that makes the links between the nodes do their clever thing of bending into the right places;

var path = sankey.link();

I make the presumption that this is a defined function within sankey.js. Then we load the data for our sankey diagram with the following line;

d3.json("sankey-formatted.json", function(error, graph) {

As we have seen in previous usage of the d3.json, d3.csv and d3.tsv functions, this is a wrapper that acts on all the code within it bringing the data in the form of graph to the remaining code.

I think it’s a good time to take a slightly closer look at the data that we’ll be using;

{
"nodes":[
{"node":0,"name":"node0"},
{"node":1,"name":"node1"},
{"node":2,"name":"node2"},
{"node":3,"name":"node3"},
{"node":4,"name":"node4"}
],
"links":[
{"source":0,"target":2,"value":2},
{"source":1,"target":2,"value":2},
{"source":1,"target":3,"value":2},
{"source":0,"target":4,"value":2},
{"source":2,"target":3,"value":2},
{"source":2,"target":4,"value":2},
{"source":3,"target":4,"value":4}
]}

I want to look at the data now, because it highlights how it is accessed throughout this portion of the code. It is split into two different blocks, ‘nodes’ and ‘links’. The subset of variables available under ‘nodes’ is ‘node’ and ‘name’. Likewise under ‘links’ we have ‘source’, ‘target’ and ‘value’. This means that when we want to act on a subset of our data we define which piece by defining the hierarchy that leads to it. For instance, if we want to define an action for all the links, we would use graph.links (they’re kind of chained together).

Now that we have our data loaded, we can assign the data to the sankey function so that it knows how to deal with it behind the scenes;

  sankey
      .nodes(graph.nodes)
      .links(graph.links)
      .layout(32);

In keeping with our previous description of what’s going on with the data, we have told the sankey function that the nodes it will be dealing with are in graph.nodes of our data structure.

I’m not sure what the .layout(32); portion of the code does, but I’d be interested to hear from any more knowledgeable readers. I’ve tried changing the values to no apparent effect and googling has drawn a blank. Internally to the sankey.js file it seems to indicate ‘iterations’ while it establishes computeNodeLinks, computeNodeValues, computeNodeBreadths, computeNodeDepths (iterations) and computeLinkDepths.

Then we add our links to the diagram with the following block of code;

  var link = svg.append("g").selectAll(".link")
      .data(graph.links)
    .enter().append("path")
      .attr("class", "link")
      .attr("d", path)
      .style("stroke-width", function(d) { return Math.max(1, d.dy); })
      .sort(function(a, b) { return b.dy - a.dy; });

This is an analogue of the block of code we examined way back in the section that we covered in explaining the code of our first simple graph.

We append svg elements for our links based on the data in graph.links, then add in the paths (using the appropriate CSS). We set the stroke width to the width of the value associated with each link or ‘1’. Whichever is the larger (by virtue of the Math.max function). As an interesting sideline, if we force this value to ‘10’ thusly…

      .style("stroke-width", 10)

… the graph looks quite interesting.

stroke-width 10 for Sankey links
stroke-width 10 for Sankey links

The sort function (.sort(function(a, b) { return b.dy - a.dy; });) makes sure the link for which the target has the highest y coordinate departs first out of the rectangle. Meaning if you have flows of 30,40,50 out of node 1, heading towards nodes 2, 3 and 4, with node 3 located above node 2 and that above node 4, the outflow order from node 1 will be 40,50,30. This makes sure there are as least crosses of flows as possible. It’s slightly confusing and for a long time it was a mystery (big thanks and kudos to ‘napicool’ who was able to explain it on d3noob.org.

The next block adds the titles to the links;

  link.append("title")
        .text(function(d) {
                return d.source.name + " → " + 
                        d.target.name + "\n" + format(d.value); });

This code appends a text element to each link when moused over that contains the source and target name (with a neat little arrow in between and the value) which, when applied with the format function, adds the units.

The next block appends the node objects (but not the rectangles or text) and contains the instructions to allow them to be arranged with the mouse.

  var node = svg.append("g").selectAll(".node")
      .data(graph.nodes)
    .enter().append("g")
      .attr("class", "node")
      .attr("transform", function(d) { 
		  return "translate(" + d.x + "," + d.y + ")"; })
    .call(d3.behavior.drag()
      .origin(function(d) { return d; })
      .on("dragstart", function() { 
		  this.parentNode.appendChild(this); })
      .on("drag", dragmove));

While it starts off in familiar territory with appending the node objects using the graph.nodes data and putting them in the appropriate place with the transform attribute, I can only assume that there is some trickery going on behind the scenes to make sure the mouse can do what it needs to do with the d3.behaviour,drag function. There is some excellent documentation on the wiki (https://github.com/mbostock/d3/wiki/Drag-behavior), but I can only presume that it knows what it’s doing :-). The dragmove function is laid out at the end of the code, and we will explain how that operates later.

I really enjoyed the next block;

  node.append("rect")
      .attr("height", function(d) { return d.dy; })
      .attr("width", sankey.nodeWidth())
      .style("fill", function(d) { 
		  return d.color = color(d.name.replace(/ .*/, "")); })
      .style("stroke", function(d) { 
		  return d3.rgb(d.color).darker(2); })
    .append("title")
      .text(function(d) { 
		  return d.name + "\n" + format(d.value); });

It starts off with a fairly standard appending of a rectangle with a height generated by its value { return d.dy; } and a width dictated by the sankey.js file to fit the canvas (.attr("width", sankey.nodeWidth())).

Then it gets interesting.

The colours are assigned in accordance with our earlier colour declaration and the individual colours are added to the nodes by finding the first part of the name for each node and assigning it a colour from the palate (the script looks for the first space in the name using a regular expression). For instance: ‘Widget X’, ‘Widget Y’ and ‘Widget’ will all be coloured the same even if the ‘Widget X’ and ‘Widget Y’ are inputs on the left and ‘Widget’ is a node in the middle.

The stroke around the outside of the rectangle is then drawn in the same shade, but darker. Then we return to the basics where we add the title of the node in a tool tip type effect along with the value for the node.

From here we add the titles for the nodes;

   node.append("text")
      .attr("x", -6)
      .attr("y", function(d) { return d.dy / 2; })
      .attr("dy", ".35em")
      .attr("text-anchor", "end")
      .attr("transform", null)
      .text(function(d) { return d.name; })
    .filter(function(d) { return d.x < width / 2; })
      .attr("x", 6 + sankey.nodeWidth())
      .attr("text-anchor", "start");

Again, this looks pretty familiar. We position the text titles carefully to the left of the nodes. All except for those affected by the filter function (return d.x < width / 2;). Where if the position of the node on the x axis is less than half the width, the title is placed on the right of the node and anchored at the start of the text. Very neat.

The last block is also pretty neat, and contains a little surprise for those who are so inclined.

  function dragmove(d) {
    d3.select(this).attr("transform", 
       "translate(" + d.x + "," + (
                d.y = Math.max(0, Math.min(height - d.dy, d3.event.y))
            ) + ")");
    sankey.relayout();
    link.attr("d", path);

This declares the function that controls the movement of the nodes with the mouse. It selects the item that it’s operating over (d3.select(this)) and then allows translation in the y axis while maintaining the link connection (sankey.relayout(); link.attr("d", path);).

But that’s not the cool part. A quick look at the code should reveal that if you can move a node in the y axis, there should be no reason why you can’t move it in the x axis as well!

Sure enough, if you replace the code above with this…

  function dragmove(d) {
    d3.select(this).attr("transform", 
        "translate(" + (
            d.x = Math.max(0, Math.min(width - d.dx, d3.event.x))
        )
        + "," + (
            d.y = Math.max(0, Math.min(height - d.dy, d3.event.y))
        ) + ")");
    sankey.relayout();
    link.attr("d", path);

… you can move your nodes anywhere on the canvas.

Move your nodes in x *and* y!
Move your nodes in x and y!

I know it doesn’t seem to add anything to the diagram (in fact, it could be argued that there is a certain aspect of detraction) however, it doesn’t mean that one day the idea doesn’t come in handy :-). You can see a live version on github.

Formatting data for Sankey diagrams

As explained in the previous section, data to form a Sankey diagram needs to be a combination of nodes and links.

{
"nodes":[
{"node":0,"name":"node0"},
{"node":1,"name":"node1"},
{"node":2,"name":"node2"},
{"node":3,"name":"node3"},
{"node":4,"name":"node4"}
],
"links":[
{"source":0,"target":2,"value":2},
{"source":1,"target":2,"value":2},
{"source":1,"target":3,"value":2},
{"source":0,"target":4,"value":2},
{"source":2,"target":3,"value":2},
{"source":2,"target":4,"value":2},
{"source":3,"target":4,"value":4}
]}

As we also noted earlier, the “node” entries in the ”nodes” section of the json file are superfluous and are really only there for our benefit since D3 will automatically index the nodes starting at zero. As a test to check this out we can change our data to the following;

{
"nodes":[
{"name":"Barry"},
{"name":"Frodo"},
{"name":"Elvis"},
{"name":"Sarah"},
{"name":"Alice"}
],
"links":[
{"source":0,"target":2,"value":2},
{"source":1,"target":2,"value":2},
{"source":1,"target":3,"value":2},
{"source":0,"target":4,"value":2},
{"source":2,"target":3,"value":2},
{"source":2,"target":4,"value":2},
{"source":3,"target":4,"value":4}
]}

This will produce the following graph;

Sankey graph with names
Sankey graph with names

As you can see, essentially the same, but with easier to understand names.

As you can imagine, while the end result is great, the creation of the JSON file manually would be painful at best. Doing something similar but with a greater number of nodes / links would be a nightmare.

Let’s see if we can make the process a bit easier and more flexible.

It would make thing much easier, if you are building the data from hand, to have nodes with names, and the ‘source’ and ‘target’ links to have those same name values as identifiers.

In other words a list of unique names for the nodes (and perhaps some details) and a list of the links between those nodes using the names for the nodes.

So, something like this;

{
"nodes":[
{"name":"Barry"},
{"name":"Frodo"},
{"name":"Elvis"},
{"name":"Sarah"},
{"name":"Alice"}
],
"links":[
{"source":"Barry","target":"Elvis","value":2},
{"source":"Frodo","target":"Elvis","value":2},
{"source":"Frodo","target":"Sarah","value":2},
{"source":"Barry","target":"Alice","value":2},
{"source":"Elvis","target":"Sarah","value":2},
{"source":"Elvis","target":"Alice","value":2},
{"source":"Sarah","target":"Alice","value":4}
]}

Once again, D3 to the rescue!

The little piece of code that can do this for us is here;

    var nodeMap = {};
    graph.nodes.forEach(function(x) { nodeMap[x.name] = x; });
    graph.links = graph.links.map(function(x) {
      return {
        source: nodeMap[x.source],
        target: nodeMap[x.target],
        value: x.value
      };
    });

This elegant solution comes from Stack Overflow and was provided by Chris Pettitt (nice job).

So if we sneak this piece of code into here…

d3.json("data/sankey-formatted.json", function(error, graph) {

            //  <= Put the code here.

  sankey
      .nodes(graph.nodes)
      .links(graph.links)
      .layout(32);

… and this time we use our JSON file with just names (sankey-formatted-names.json) and our new html file (sankey-formatted-names.html) we find our Sankey diagram working perfectly!

The full code for this example can be found on github or in the code samples bundled with this book (sankey-formatted-names.html, sankey.js and sankey-formatted-names.json). A live example can be found on bl.ocks.org.

Sankey graph with names again
Sankey graph with names again

Looking at our new piece of code…

    var nodeMap = {};
    graph.nodes.forEach(function(x) { nodeMap[x.name] = x; });

… the first thing it does is create an object called nodeMap (The difference between an array and an object in JavaScript is one that is still a little blurry to me and judging from online comments, I am not alone).

Then for each of the graph.node instances (where x is a range of numbers from 0 to the last node), we assign each node name to a number.

Then in the next piece of code…

    graph.links = graph.links.map(function(x) {
      return {
        source: nodeMap[x.source],
        target: nodeMap[x.target],
        value: x.value
      };

… we go through all the links we have and for each link, we map the appropriate number to the correct name.

Very clever.

From a CSV with ‘source’, ‘target’ and ‘value’ info only.

In the first iteration of this section I had no solution to creating a Sankey diagram using a csv file as the source of the data.

But cometh the hour, cometh the man. Enter @timelyportfolio who, while claiming no expertise in D3 or JavaScript was able to demonstrate a solution to exactly the problem I was facing! Well done Sir! I salute you and name the technique the timelyportfolio csv method!

The full code for this example can be found on github or in the code samples bundled with this book (sankey-formatted-csv.html, sankey.js and sankey.csv). A live example can be found on bl.ocks.org.

So here’s the cleverness that @timelyportfolio demonstrated;

Using a csv file (in this case called sankey.csv) that looks like this;

source,target,value
Barry,Elvis,2
Frodo,Elvis,2
Frodo,Sarah,2
Barry,Alice,2
Elvis,Sarah,2
Elvis,Alice,2
Sarah,Alice,4

We take this single line from our original Sankey diagram code;

d3.json("sankey-formatted.json", function(error, graph) {

And replace it with the following block;

d3.csv("sankey.csv", function(error, data) {

  //set up graph in same style as original example but empty
  graph = {"nodes" : [], "links" : []};

    data.forEach(function (d) {
      graph.nodes.push({ "name": d.source });
      graph.nodes.push({ "name": d.target });
      graph.links.push({ "source": d.source,
                         "target": d.target,
                         "value": +d.value });
     });

     // return only the distinct / unique nodes
     graph.nodes = d3.keys(d3.nest()
       .key(function (d) { return d.name; })
       .map(graph.nodes));

     // loop through each link replacing the text with its index from node
     graph.links.forEach(function (d, i) {
       graph.links[i].source = graph.nodes.indexOf(graph.links[i].source);
       graph.links[i].target = graph.nodes.indexOf(graph.links[i].target);
     });

     //now loop through each nodes to make nodes an array of objects
     // rather than an array of strings
     graph.nodes.forEach(function (d, i) {
       graph.nodes[i] = { "name": d };
     });

The comments in the code (and they are fuller in @timelyportfolio’s original gist solution) explain the operation;

d3.csv("sankey.csv", function(error, data) {

… Loads the csv file from the data directory.

  graph = {"nodes" : [], "links" : []};

… Declares graph to consist of two empty arrays called nodes and links.

      data.forEach(function (d) {
      graph.nodes.push({ "name": d.source });
      graph.nodes.push({ "name": d.target });
      graph.links.push({ "source": d.source,
                         "target": d.target,
                         "value": +d.value });
     });

… Takes the data loaded with the csv file and for each row loads variables for the source and target into the nodes array. Then for each row it loads variables for the source target and value into the links array.

     graph.nodes = d3.keys(d3.nest()
       .key(function (d) { return d.name; })
       .map(graph.nodes));

… Is a routine that Mike Bostock described on Google Groups that (as I understand it) nests each node name as a key so that it returns with only unique nodes.

     graph.links.forEach(function (d, i) {
       graph.links[i].source = graph.nodes.indexOf(graph.links[i].source);
       graph.links[i].target = graph.nodes.indexOf(graph.links[i].target);
     });

… Goes through each link entry and, for each source and target, it finds the unique index number of that name in the nodes array and assigns the link source and target an appropriate number.

And finally…

     graph.nodes.forEach(function (d, i) {
       graph.nodes[i] = { "name": d };
     });

… Goes through each node and (in the words of @timelyportfolio) “make nodes an array of objects rather than an array of strings” (I don’t really know what that means :-(. I just know it works :-).)

There you have it. A Sankey diagram from a csv file. Well played @timelyportfolio!

So, here we are. Faced with a dilemma of trying to get my csv formatted links into a Sankey diagram. In theory we need to go through our file, identify all the unique nodes and format the entire blob into JSON for use.

There must be a better way!

Well, I’m not going to claim that this is any better since it’s a little like cracking a walnut with a sledgehammer. But to a man with just a sledgehammer, everything’s a walnut.

So, let’s use our MySQL and PHP skills to solve our problem. In fact, let’s make it slightly harder for ourselves. Let’s imagine that we don’t even have a value associated with our data, just a big line of source and target links. Something like this;

source,target
Barry,Elvis
Barry,Elvis
Frodo,Elvis
Frodo,Elvis
Frodo,Sarah
Frodo,Sarah
Barry,Alice
Barry,Alice
Elvis,Sarah
Elvis,Sarah
Elvis,Alice
Elvis,Alice
Sarah,Alice
Sarah,Alice
Sarah,Alice
Sarah,Alice

First thing first, just as we did in the example on using MySQL, import your csv file into a MySQL table which we’ll call sankey1 in database homedb.

Now we want to write a query that pulls out all the DISTINCT names that appear it the ‘source’ and ‘target’ columns. This will form our ‘nodes’ portion of the JSON data.

SELECT DISTINCT(`source`) AS name FROM `sankey1`
UNION
SELECT DISTINCT(`target`) AS name FROM `sankey1`
GROUP BY name

This query actually mashes two separate queries together where each returns DISTINCT instances of each source and target from the source and target columns. By default, the UNION operator eliminates duplicate rows from the result which means we have a list of each node in the table.

Sankey nodes from MySQL
Sankey nodes from MySQL

Exxxeellennt……. (channelling Mr Burns)

Now we run a separate query that pulls out each distinct ‘source’ and ‘target’ combination and the number of times (COUNT(*)) that it occurs.

SELECT `source` AS source, `target`  as target, COUNT(*) as value 
FROM `sankey1`
GROUP BY source, target 

This query gets all the sources plus all the targets and groups them by first the source and then the target. Each line is therefore unique and the COUNT(*) sums up the number of times that each unique combination occurs.

Sankey links from MySQL
Sankey links from MySQL

That was surprisingly easy wasn’t it?

MySQL is good for simple jobs, but we are of course a long way from finished since at this stage all we have is what looks like two tables in a spreadsheet.

So now we turn to PHP.

Remembering from the start of the book, we described PHP as the glue that could connect parts of web pages together. In this case we will use it to glue our MySQL database to our JavaScript.

We need to carry out our queries and return the information in a format that d3.js can understand. In this instance we will select JSON as it’s probably the most ubiquitous, and it suits the format of our original manual data.

Let’s cut to the chase and look at the code:

<?php
    $username = "homedbuser"; 
    $password = "homedbuser";   
    $host = "localhost";
    $database="homedb";
    
    $server = mysql_connect($host, $username, $password);
    $connection = mysql_select_db($database, $server);

    $myquery = "
SELECT DISTINCT(`source`) AS name FROM `sankey1`
UNION
SELECT DISTINCT(`target`) AS name FROM `sankey1`
GROUP BY name
";
    $query = mysql_query($myquery);
    
    if ( ! $myquery ) {
        echo mysql_error();
        die;
    }
    
    $nodes = array();
    
    for ($x = 0; $x < mysql_num_rows($query); $x++) {
        $nodes[] = mysql_fetch_assoc($query);
    }

    $myquery = "
SELECT `source` AS source, `target`  as target, COUNT(*) as value 
FROM `sankey1`
GROUP BY source, target 
";
    $query = mysql_query($myquery);
    
    if ( ! $myquery ) {
        echo mysql_error();
       die;
    }
    
    $links = array();
    
    for ($x = 0; $x < mysql_num_rows($query); $x++) {
        $links[] = mysql_fetch_assoc($query);
    }

echo "{";
echo '"links": ', json_encode($links), "\n";
echo ',"nodes": ', json_encode($nodes), "\n";
echo "}";

    mysql_close($server);
?>

Astute readers will recognise that this is very similar to the script that we used to extract data from the MySQL database for generating a simple line graph. If you haven’t checked it out, and you’re unfamiliar with PHP, you will want to read that section first.

We declare all the appropriate variables which we will use to connect to the database. We then connect to the database and run our query.

After that we store the nodes data in an array called $nodes.

Then we run our second query (we don’t close the connection to the database since we’re not finished with it yet).

The second query returns the link results into a second array called $links (pretty imaginative).

Now we come to a part that’s a bit different. We still need to echo out the data in the same way we did in our line graph, but in this case we need to add the data together with the associated links and nodes identifiers.

echo "{";
echo '"links": ', json_encode($links), "\n";
echo ',"nodes": ', json_encode($nodes), "\n";
echo "}";

(if you look closely, the syntax will produce our JSON formatted output).

At last, we need to call this PHP script from our html file in the same way that we did for the line graph. So amend the html file to change the loading of the JSON data to be from our PHP file thusly;

d3.json("php/sankey.php", function(error, graph) {

And there you have it! So many ways to get the data.

Both the PHP file (sankey.php) and the html file (sankey-mysql-import.html) are available as code samples bundled with this book.

Sankey diagram case study

Armed with all this new found knowledge on building Sankey diagrams, what can you do?

Well, I suppose it all depends on your data set, but remember, Sankey diagrams are good at flows, but they won’t do loops / cycles easily (although there has been some good work done in this direction here http://bl.ocks.org/cfergus/3956043 and here http://bl.ocks.org/kunalb/4658510).

So let’s choose a flow.

In this case we’ll selected the flow of data that represents a view of global, anthropogenic greenhouse gas (GHG) emissions. The image is an alternative to the excellent diagram on the World Resources Institute (http://www.wri.org/chart/world-greenhouse-gas-emissions-2005) and as such my version pales in comparison to theirs.

However, the aim is to play with the technique, not to emulate :-).

So starting with the data presented in the original diagram, we have to capture the links into a csv file. I did this the hard way (since there didn’t appear to be an electronic version of the data) by reading the graph and entering the figures into a csv file. From here we import it into our MySQL database and then convert it into sankey formatted JSON by using our PHP script that we played with in the example of extracting information from a MySQL database. In this case, instead of needing to perform a COUNT(*) on the data, it’s slightly easier since the value is already present.

Because we want this diagram to be hosted on Gist and accessible on bl.ocks.org, we run the PHP file directly into the browser so that it just shows the JSON data on the screen. We save this file with the suffix .json and we have our data (in this case the file is named sankeygreenhouse.json).

We amend our html file to look at our new .json file and voila!

Sankey diagram of greenhouse gas emissions in 2005
Sankey diagram of greenhouse gas emissions in 2005

Sankeytastic!

You can find this as a live example and with all the code and data on bl.ocks.org.

Tree Diagrams

What is a Tree Diagram?

The ‘Tree layout’ is not a distinct type of diagram per se. Instead, it’s representative of D3’s family of hierarchical layouts.

It’s designed to produce a ‘node-link’ diagram that lays out the connection between nodes in a method that displays the relationship of one node to another in a parent-child fashion.

For example, the following diagram shows a root node (the starting position) labelled ‘Top Node’ which has two children (Bob: Child of Top Node and Sally: Child of Top Node). Subsequently, Bob:Child of Top Node has two dependant nodes (children) ‘Son of Bob’ and ‘Daughter of Bob’.

Tree layout diagram
Tree layout diagram

The clear advantage to this style of diagram is that describing it in text is difficult, but representing it graphically makes the relationships easy to determine.

The data required to produce this type of layout needs to describe the relationships, but this is not necessarily an onerous task. For example, the following is the data (in JSON form) for the diagram above and it shows the minimum information required to form the correct layout hierarchy.

  {
    "name": "Top Node",
    "children": [
      {
        "name": "Bob: Child of Top Node",
        "parent": "Top Node",
        "children": [
          {
            "name": "Son of Bob",
            "parent": "Bob: Child of Top Node"
          },
          {
            "name": "Daughter of Bob",
            "parent": "Bob: Child of Top Node"
          }
        ]
      },
      {
        "name": "Sally: Child of Top Node",
        "parent": "Top Node"
      }
    ]
  }

It shows each node as having a name that identifies it on the tree and, where appropriate, the children it has (as an array) and its parent.

There is a wealth of examples of tree diagrams on the web, but I would recommend a visit to the D3.js gallery maintained by Christophe Viau as a starting point to get some ideas.

In this chapter we’re going to look at a very simple piece of code to generate a tree diagram before looking at different ways to adapt it. Including rotating it to be vertical, adding some dynamic styling to the nodes, importing from a flat file and from an external source. Finally we’ll look at a more complex example that is more commonly used on the web that allows a user to expand and collapse nodes interactively.

A simple Tree Diagram explained

We are going to work through a simple example of the code that draws a tree diagram, This is more for the understanding of the process rather than because it is a good example of code for drawing a tree diagram. It is a very limited example that lacks any real interactivity which is one of the strengths of d3.js graphics. However, we will outline the operation of an interactive version towards the end of the chapter once we have explored some possible configuration options that we might want to make.

The graphic that we are going to generate will look like this…

Simple tree layout diagram
Simple tree layout diagram

And the full code for it looks like this;

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">

    <title>Collapsible Tree Example</title>

    <style>

	.node circle {
	  fill: #fff;
	  stroke: steelblue;
	  stroke-width: 3px;
	}

	.node text { font: 12px sans-serif; }

	.link {
	  fill: none;
	  stroke: #ccc;
	  stroke-width: 2px;
	}
	
    </style>

  </head>

  <body>

<!-- load the d3.js library -->	
<script src="http://d3js.org/d3.v3.min.js"></script>
	
<script>

var treeData = [
  {
    "name": "Top Level",
    "parent": "null",
    "children": [
      {
        "name": "Level 2: A",
        "parent": "Top Level",
        "children": [
          {
            "name": "Son of A",
            "parent": "Level 2: A"
          },
          {
            "name": "Daughter of A",
            "parent": "Level 2: A"
          }
        ]
      },
      {
        "name": "Level 2: B",
        "parent": "Top Level"
      }
    ]
  }
];

// ************** Generate the tree diagram	 *****************
var margin = {top: 20, right: 120, bottom: 20, left: 120},
	width = 960 - margin.right - margin.left,
	height = 500 - margin.top - margin.bottom;
	
var i = 0;

var tree = d3.layout.tree()
	.size([height, width]);

var diagonal = d3.svg.diagonal()
	.projection(function(d) { return [d.y, d.x]; });

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

root = treeData[0];
  
update(root);

function update(source) {

  // Compute the new tree layout.
  var nodes = tree.nodes(root).reverse(),
	  links = tree.links(nodes);

  // Normalize for fixed-depth.
  nodes.forEach(function(d) { d.y = d.depth * 180; });

  // Declare the nodes…
  var node = svg.selectAll("g.node")
	  .data(nodes, function(d) { return d.id || (d.id = ++i); });

  // Enter the nodes.
  var nodeEnter = node.enter().append("g")
	  .attr("class", "node")
	  .attr("transform", function(d) { 
		  return "translate(" + d.y + "," + d.x + ")"; });

  nodeEnter.append("circle")
	  .attr("r", 10)
	  .style("fill", "#fff");

  nodeEnter.append("text")
	  .attr("x", function(d) { 
		  return d.children || d._children ? -13 : 13; })
	  .attr("dy", ".35em")
	  .attr("text-anchor", function(d) { 
		  return d.children || d._children ? "end" : "start"; })
	  .text(function(d) { return d.name; })
	  .style("fill-opacity", 1);

  // Declare the links…
  var link = svg.selectAll("path.link")
	  .data(links, function(d) { return d.target.id; });

  // Enter the links.
  link.enter().insert("path", "g")
	  .attr("class", "link")
	  .attr("d", diagonal);

}

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

In the course of describing the operation of the file I will gloss over the aspects of the structure of an HTML file which have already been described at the start of the book. Likewise, aspects of the JavaScript functions that have already been covered will only be briefly explained.

The start of the file deals with setting up the document’s head and body loading the d3.js script and setting up the css in the <style> section.

The css section sets styling for the circle that represents the nodes, the text alongside them and the links between them.

	.node circle {
	  fill: #fff;
	  stroke: steelblue;
	  stroke-width: 3px;
	}

	.node text { font: 12px sans-serif; }

	.link {
	  fill: none;
	  stroke: #ccc;
	  stroke-width: 2px;
	}

Then our JavaScript section starts and the first thing that happens is that we declare our array of data in the following code;

var treeData = [
  {
    "name": "Top Level",
    "parent": "null",
    "children": [
      {
        "name": "Level 2: A",
        "parent": "Top Level",
        "children": [
          {
            "name": "Son of A",
            "parent": "Level 2: A"
          },
          {
            "name": "Daughter of A",
            "parent": "Level 2: A"
          }
        ]
      },
      {
        "name": "Level 2: B",
        "parent": "Top Level"
      }
    ]
  }
];

As outlined at the start of the chapter, this data is encoded hierarchically in JavaScript Object Notation (JSON). Each node must have a name and either a parent or child node(s) or both. There are many examples of hierarchical data that can be encoded in this way. From the traditional parent - offspring example to directories on a hard drive or a breakdown of materials for a complex object. Any system of encoding where there is a single outcome from multiple sources like an election or an alert encoding system dependent on multiple trigger points.

The next section of our code declares some of the standard features for our diagram such as the size and shape of the svg container with margins included.

var margin = {top: 20, right: 120, bottom: 20, left: 120},
	width = 960 - margin.right - margin.left,
	height = 500 - margin.top - margin.bottom;
	
var i = 0;

var tree = d3.layout.tree()
	.size([height, width]); 

It also assigns the variable / function tree to the d3.js function that is used to assign and calculate the data required for the nodes and links for our diagram. We will be calling that later.

The next block of code declares the function that will be used to draw the links between the nodes. This isn’t the part of the code where the links are drawn, this is just declaring the variable/function that will be used when it does happen.

var diagonal = d3.svg.diagonal()
	.projection(function(d) { return [d.y, d.x]; });

This uses the d3.js diagonal function to help draw a path between two points such that the line exhibits some nice flowing curves (cubic Bézier ) to make the connection.

The next block of code appends our SVG working area to the body of our web page and creates a group elements (<g>) that will contain our svg objects (our nodes, text and links).

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

The next line is one that vexed me for a while and one that I think means there are other areas of my code that could be improved (for a short interlude on why this tried me, feel free to catch this question on Stack Overflow).

root = treeData[0];

It might not look like much and to those familiar with JavaScript, it will be a no-brainer, but what the line is doing is defining what ‘tree’ from our data is going to be used. Because our data is an array, the first level of the array is treeData. The name of the first object on the first level of treeData is ‘Top Level’. This (being the first object) is object 0. Therefore our starting point is treeData[0]. We could confirm this by changing the declaration to …

root = treeData[0].children[0];

This will take the root point for our diagram as being the first child (child[0]) of the the first level of treeData. As a result, our tree diagram will look like this…

Tree layout diagram using a different root point
Tree layout diagram using a different root point

… since ‘Level 2: A’ is the first child of ‘Top Level’.

Then we call the function that draws our tree diagram.

update(root);

This calls the function update and uses the data root to create our tree.

The last significant part of the code is the function update. This is the part of the code that pulls together the functions and data that we have declared and draws our tree.

The first step in that process is to assign our nodes and links.

  var nodes = tree.nodes(root),
	  links = tree.links(nodes);

This uses our previously declared tree function to work its d3.js magic on our data (root) and to determine the node details and from the node details we can determine the link details.

If you’re wondering how this all works, I’m afraid that I won’t be able to help much, but a starting point would be the results that the process produces which is a set of nodes, each of which has a set of characteristics. Those characteristics are; - .children: Which is an array of any children that exist for that node. - .depth: Which is the depth (described in a few paragraphs time). - .id: Which is a unique number identifier for each node. - .name: The name we have assigned from our data. - .parent: The name of the parent of the node. - .x and .y: Which are the x and y positions on the screen of the node.

From this node data a set of links joining the nodes is created. Each link consists of a .source and .target. Each of which is a node.

We then determine the horizontal spacing of the nodes.

  nodes.forEach(function(d) { d.y = d.depth * 180; });

This uses the depth of the node (as determined for each node in the nodes = tree.nodes(root) line) to calculate the position on the y axis of the screen.

The depth refers to the position in the tree relative to the root node on the left. The following picture shows how the depth relates to the position of the node in the tree.

Depth of nodes on the tree
Depth of nodes on the tree

So by adjusting our ‘expansion factor’ (currently set to 180) we can adjust the spacing of the nodes. For instance, here is the spacing changed to 80.

Adjusting the depth of the tree
Adjusting the depth of the tree

We then declare the variable / function node so that when we call it later it will know to select the appropriate object (a node) with the appropriate .id.

  var node = svg.selectAll("g.node")
	  .data(nodes, function(d) { return d.id || (d.id = ++i); });

The next block of code assigns the variable / function nodeEnter to the action of appending a node to a particular position.

  var nodeEnter = node.enter().append("g")
	  .attr("class", "node")
	  .attr("transform", function(d) { 
		  return "translate(" + d.y + "," + d.x + ")"; });

Then we get to the piece of code that appends the circle that comprises the node (using nodeEnter).

  nodeEnter.append("circle")
	  .attr("r", 10)
	  .style("fill", "#fff");

(using a radius of 10 pixels and a white fill).

And we add in the text for each node…

  nodeEnter.append("text")
	  .attr("x", function(d) { 
		  return d.children || d._children ? -13 : 13; })
	  .attr("dy", ".35em")
	  .attr("text-anchor", function(d) { 
		  return d.children || d._children ? "end" : "start"; })
	  .text(function(d) { return d.name; })
	  .style("fill-opacity", 1);

This is a neat piece of code that allows the text to be placed on the left side of the node if it has children (d.children) or on the right if it has has no children or d._children. This is a slightly redundant piece of code (the d._children piece) for this diagram, but it becomes more useful in the interactive version towards the end of the chapter. It also aligns the text correctly and makes sure it is visible.

Then we declare the link variable / function and tell it to make a link based on all the links that have unique target id’s.

  var link = svg.selectAll("path.link")
	  .data(links, function(d) { return d.target.id; });

This might not be obvious at first glance, but we only want to draw links between a node and it’s parent. There should be one less link than the total number of nodes since the root node (‘Top Level’) has no parent. Therefore only those links with unique target id’s in the data need to have links produced. If we were to replace the .target in the above code with .source we would have only two unique .source id’s. It would therefore look like this;

Only Links from unique sources
Only Links from unique sources

Our final block of JavaScript adds in our link as a diagonal path (as declared early in the JavaScript portion of the code).

  link.enter().insert("path", "g")
	  .attr("class", "link")
	  .attr("d", diagonal);

There are only a couple of lines of HTML to close off the file and we are left with our tree diagram!

Simple tree layout diagram
Simple tree layout diagram

Don’t forget, the full code for this example can be found on github, in the appendices of this book or in the code samples bundled with this book (simple-tree-diagram.html). A working example can be found on bl.ocks.org.

Styling nodes in a tree diagram

The nodes in a tree diagram are objects that exist to provide a representation of the structure of data, but on a tree diagram they should also be viewed as an opportunity to encode additional information about the underlying data.

From the initial simple example that we covered at the start of the chapter we have encoded a certain amount of information already. The position of the text relative to each node is determined by whether or not the node is the parent of another node (if it’s a parent it’s on the left) or a child that is on the edge of the tree (in which case it is on the right of the node).

Node position on the tree diagram
Node position on the tree diagram

Now, that’s nice, but are we going to be satisfied with that??? (The answer is “No” by the way.)

This example is fairly simple, but it is an example of applying different styles to the nodes to convey additional information. I should be clear at this stage that I am not advocating turning your tree diagram into something that looks like it came out of a circus, because that would be a crime against style, so don’t repeat my upcoming example, but let some of the features be a trigger for developing your own subtle, yet compelling visualizations.

Brace yourself. Here’s a picture of the tree diagram that we’re going to generate. Those with weaker constitutions should look away and flip forward a few pages;

Tree diagram with excessive styling
Tree diagram with excessive styling

The changes that have been made are as a result of additional data fields that have been added to the JSON array and these fields have been applied to various style options throughout the code.

The types of style changes we have made are - Variation of the diameter of nodes - Changing the fill and stroke colour of nodes - Changing the colour of links depending on the associated target node they connect to.

We’ll start by looking at the new JSON data set;

  {
    "name": "Top Level",
    "parent": "null",
    "value": 10,
    "type": "black",
    "level": "red",
    "children": [
      {
        "name": "Level 2: A",
        "parent": "Top Level",
        "value": 15,
        "type": "grey",
        "level": "red",
        "children": [
          {
            "name": "Son of A",
            "parent": "Level 2: A",
            "value": 5,
            "type": "steelblue",
            "level": "orange"
          },
          {
            "name": "Daughter of A",
            "parent": "Level 2: A",
            "value": 8,
            "type": "steelblue",
            "level": "red"
          }
        ]
      },
      {
        "name": "Level 2: B",
        "parent": "Top Level",
        "value": 10,
        "type": "grey",
        "level": "green"
      }
    ]
  }

Each node now has a value which might represent a degree of importance (we will use this to affect the radius of the nodes), a type which might indicate a difference in the type of node (they might be in active, inactive or undetermined states) and a level which might indicate an alert level for determining problems (red = bad, orange = caution and green = normal).

Irrespective of the contrived nature of our styling options, they are applied to our tree in fairly similar ways with some subtle differences.

Remember, the full code for this example can be found on github or in the code samples bundled with this book (simple-tree-features.html). A working example can be found on bl.ocks.org.

The first change is to the node radius, stroke colour and fill colour.

We simply change the portion of the code that appends the circle from this…

  nodeEnter.append("circle")
	  .attr("r", 10)
	  .style("fill", "#fff");

… to this …

  nodeEnter.append("circle")
	  .attr("r", function(d) { return d.value; })
	  .style("stroke", function(d) { return d.type; })
	  .style("fill", function(d) { return d.level; });

The changes return the radius attribute as a function using value, the stroke colour is returned using type and the fill colour is returned with level. This is nice and simple, but we do need to make a slight adjustment to the code that sets the distance that the text is from the nodes so that when the radius expands or contracts, the text distance from the edge of the node adjusts as well.

To do this we take the clever piece of code that adjusts the distance that the text is in the x dimension from the node that looks like this …

	  .attr("x", function(d) { 
		  return d.children || d._children ? -13 : 13; })

… and we add in a dynamic aspect using the value field.

	  .attr("x", function(d) { 
		  return d.children || d._children ? 
		  (d.value + 4) * -1 : d.value + 4 })

The last thing we wanted to do is to change the colour of the link based on the colour of the target node. We accomplish this by taking the code that inserts the links…

  link.enter().insert("path", "g")
	  .attr("class", "link")
	  .attr("d", diagonal);

… and adding in a line that styles the link colour (the stroke) based on the level colour of the target end of the link d.target.level).

  link.enter().insert("path", "g")
	  .attr("class", "link")
	  .style("stroke", function(d) { return d.target.level; })
	  .attr("d", diagonal);

Use the concepts here wisely. I don’t want to see any heinously styled tree diagrams floating around the internet with “Thanks to the help from D3 Tips and Tricks” next to them. Be subtle, be thoughtful :-).

Changing the nodes to different shapes

Many thanks to Josiah who asked a question on the d3noob.org blog on how the shapes of the nodes could be varied based on an associated value in the data.

There is more than one way to do this, but perhaps the simplest is to replace the section of the JavaScript that appends the circle with one that appends a symbol from d3’s symbol generator.

There are six pre-defined symbol types as follows;

  • circle - a circle.
  • cross - a Greek cross or plus sign.
  • diamond - a rhombus.
  • square - an axis-aligned square.
  • triangle-down - a downward-pointing equilateral triangle.
  • triangle-up - an upward-pointing equilateral triangle.

The following script will look at the value in the data and assign either a cross or a diamond depending on the value

  nodeEnter.append("path")
    .style("stroke", "black")
    .style("fill", "white")
    .attr("d", d3.svg.symbol()
                 .size(200)
                 .type(function(d) { if
                    (d.value >= 9) { return "cross"; } else if
                    (d.value <= 9) { return "diamond";}
                  })); 
Tree diagram different node shapes
Tree diagram different node shapes

The full code for this example can be found on github or in the code samples bundled with this book (simple-tree-shapes.html). A working online example can be found on bl.ocks.org.

Using images as nodes

Many thanks to nbhatta who asked a question on the d3noob.org blog on how to use images as nodes.

Tree diagram with images for nodes
Tree diagram with images for nodes

This was a slightly simpler change and just involved replacing the code snippet that added the circles with one that added an image;

  nodeEnter.append("image")
      .attr("xlink:href", function(d) { return d.icon; })
      .attr("x", "-12px")
      .attr("y", "-12px")
      .attr("width", "24px")
      .attr("height", "24px");

The images I chose were all 48 x 48 pixel for the sake of consistency and in the code above I formatted them to be half that size and moved them in the x and y direction so that they were centred correctly.

The cool thing that you will notice is that the specific icon that is placed at each node position is set by the name of the icon which is gathered from the JSON file with the tree details;

var treeData = [
  {
    "name": "Top Level",
    "parent": "null",
    "value": 10,
    "type": "black",
    "level": "red",
    "icon": "earth.png",
    "children": [
      {
        "name": "Level 2: A",
        "parent": "Top Level",
        "value": 5,
        "type": "grey",
        "level": "red",
       "icon": "cart.png",
        "children": [
          {
            "name": "Son of A",
            "parent": "Level 2: A",
            "value": 5,
            "type": "steelblue",
            "icon": "lettern.png",
            "level": "orange"
          },
          {
            "name": "Daughter of A",
            "parent": "Level 2: A",
            "value": 18,
            "type": "steelblue",
            "icon": "vlc.png",
            "level": "red"
          }
        ]
      },
      {
        "name": "Level 2: B",
        "parent": "Top Level",
        "value": 10,
        "type": "grey",
        "icon": "random.png",
        "level": "green"
      }
    ]
  }
];

It’s possible to just have a single image and to hard-code it into the script, but where’s the fun in that?

The full code for this example can be found on github or in the code samples bundled with this book (simple-tree-images.html, cart.png, earth.png, lettern.png, random.png and vlc.png). A working online example can be found on bl.ocks.org.

Making a vertical tree diagram

Changing a tree diagram from a horizontal view to a vertical one is fairly easy. There are only three things to change from the code that we used for our original simple tree diagram.

The first is to change the orientation of the nodes by transposing the x and y coordinates.

That means taking the section of code that appends the nodes…

  var nodeEnter = node.enter().append("g")
	  .attr("class", "node")
	  .attr("transform", function(d) { 
		  return "translate(" + d.y + "," + d.x + ")"; });

… and swapping the d.x and d.y designators so that it looks like this…

  var nodeEnter = node.enter().append("g")
	  .attr("class", "node")
	  .attr("transform", function(d) { 
		  return "translate(" + d.x + "," + d.y + ")"; });

Because the vertical version of the tree diagram can be a lot more compact, we can adjust our difference between the depths to a more rational value. In our example we can change the separation from 180 to 100 pixels in the following line of code…

  nodes.forEach(function(d) { d.y = d.depth * 100; });

The second is to do the same adjustment for the links. We take the block of code that generates the curvy diagonal paths…

var diagonal = d3.svg.diagonal()
	.projection(function(d) { return [d.y, d.x]; });

… and swap the d.x and d.y designators so that it looks like this…

var diagonal = d3.svg.diagonal()
	.projection(function(d) { return [d.x, d.y]; });

At this point we have our tree diagram ready to go except for one small detail…

Vertical tree diagram with sideways text
Vertical tree diagram with sideways text

The text is still aligned to the left and right of the nodes. On this example, it looks pretty good, but if we were to introduce a few more nodes, it would start to get pretty cramped, so we can place the text above and below the nodes dependent on whether the node is a parent (above) or a child on the bottom level (below).

To do this we take the original text appending code…

  nodeEnter.append("text")
	  .attr("x", function(d) { 
		  return d.children || d._children ? -13 : 13; })
	  .attr("dy", ".35em")
	  .attr("text-anchor", function(d) { 
		  return d.children || d._children ? "end" : "start"; })
	  .text(function(d) { return d.name; })
	  .style("fill-opacity", 1);

… and change the x attribute to a y attribute, anchor the text in the middle (which is actually a simplification of the code) and extend the distance between the node and the anchor point slightly to 18 (and -18) pixels.

  nodeEnter.append("text")
	  .attr("y", function(d) { 
		  return d.children || d._children ? -18 : 18; })
	  .attr("dy", ".35em")
	  .attr("text-anchor", "middle")
	  .text(function(d) { return d.name; })
	  .style("fill-opacity", 1);

And there we have it! A vertical tree diagram.

Vertical tree diagram
Vertical tree diagram

The full code for this example can be found on github or in the code samples bundled with this book (simple-tree-vertical.html). A working online example can be found on bl.ocks.org.

Generating a tree diagram from ‘flat’ data

Tree diagrams are a fantastic way of displaying information, but one of the drawbacks (to the examples we’ve been using so far) is the need to have your data encoded hierarchically. Most data in a raw form will be flat. That is to say, it won’t be formatted as an array with the parent - child relationships. Instead it will be a list of objects (which we will want to turn into nodes) that might describe the relationship to each other, but they won’t be encoded that way. For example, the following is the flat representation of the example data we have been using thus far.

    { "name" : "Level 2: A", "parent":"Top Level" },
    { "name" : "Top Level", "parent":"null" },
    { "name" : "Son of A", "parent":"Level 2: A" },
    { "name" : "Daughter of A", "parent":"Level 2: A" },
    { "name" : "Level 2: B", "parent":"Top Level" }

It is actually fairly simple and consists of only the name of the node and the name of it’s parent node. It’s easy to see how this data could be developed into a hierarchical form, but it would take a little time and for a larger data set, that would be tiresome.

Luckily computers are built for shuffling data about and with kudos to ‘nrabinowitz’ for answering a question (and Prateek Tandon for asking) on Stack Overflow (and Jesus Ruiz with AmeliaBR for setting me on the right path), here is how we can take our flat data and convert it for use in our tree diagram.

We will be using the simple example that we started with at the start of the chapter and the first change we need to make is to replace our original data…

var treeData = [
  {
    "name": "Top Level",
    "parent": "null",
    "children": [
      {
        "name": "Level 2: A",
        "parent": "Top Level",
        "children": [
          {
            "name": "Son of A",
            "parent": "Level 2: A"
          },
          {
            "name": "Daughter of A",
            "parent": "Level 2: A"
          }
        ]
      },
      {
        "name": "Level 2: B",
        "parent": "Top Level"
      }
    ]
  }
];

… with our flat data array…

var data = [
    { "name" : "Level 2: A", "parent":"Top Level" },
    { "name" : "Top Level", "parent":"null" },
    { "name" : "Son of A", "parent":"Level 2: A" },
    { "name" : "Daughter of A", "parent":"Level 2: A" },
    { "name" : "Level 2: B", "parent":"Top Level" }
    ];

It’s worth noting here that we have also changed the name of the array (to data) since we are going to convert, then declare our newly massaged data with our original variable name treeData so that the remainder of our code thinks there have been no changes.

Then we create a name-based map for the nodes. In his answer on Stack Overflow, ‘nrabinowitz’ uses the .reduce method, which starts with an empty object and iterates over the data array, adding an entry for each node.

var dataMap = data.reduce(function(map, node) {
	map[node.name] = node;
	return map;
}, {});

Don’t feel upset if you don’t understand exactly how it works. I struggle to understand internal combustion engines, but I’m ok at driving a car :-). Think of this in the same way.

Then we iteratively add each child to its parents, or to the root array if no parent is found;

var treeData = [];
data.forEach(function(node) {
	// add to parent
	var parent = dataMap[node.parent];
	if (parent) {
		// create child array if it doesn't exist
		(parent.children || (parent.children = []))
			// add node to child array
			.push(node);
	} else {
		// parent is null or missing
		treeData.push(node);
	}
});

The code is essentially working through each node in the array and if it has a child it adds it to the children sub-array and if necessary creates the array. Likewise, if the node has no parent, it simply add it as a root node.

That’s it!

The brevity of the code to do this should not detract from its elegance. It really is very clever. The end result doesn’t look any different from our original diagram…

Tree diagram (but from flat data)
Tree diagram (but from flat data)

… but it adds a significant capability for use of additional data.

The full code for this example can be found on github or in the code samples bundled with this book (simple-tree-from-flat.html). A working example can be found on bl.ocks.org.

Generating a tree diagram from external data

In all the examples we have looked at so far we have used data that we have declared from within the file itself. Being able to import data from an external file is an important feature that we need to know how to implement.

Starting from the simple tree diagram example that we began with at the start of the chapter, the first change that we need to make is to remove the section of code that declares our data. But don’t throw it away since we will use it to create a separate file called treeData.json. It’s contents will be;

[
  {
    "name": "Top Level",
    "parent": "null",
    "children": [
      {
        "name": "Level 2: A",
        "parent": "Top Level",
        "children": [
          {
            "name": "Son of A",
            "parent": "Level 2: A"
          },
          {
            "name": "Daughter of A",
            "parent": "Level 2: A"
          }
        ]
      },
      {
        "name": "Level 2: B",
        "parent": "Top Level"
      }
    ]
  }
]

(don’t include the treeData = part, or the semicolon at the end (you can delete those))

Then all we need to do is change the portion of the code that declared the root variable and updates the diagram;

root = treeData[0];
  
update(root);

… into a small section that uses the d3.json accessor to load the file treeData.json (Remember to correctly address the file. This one assumes that the treeData.json file is in the same directory as the html file we are opening).

d3.json("treeData.json", function(error, treeData) {
  root = treeData[0];
  update(root);
});

It then declares the variable root in the same way and calls the update function to draw the tree diagram. Viola!

The full code for this example can be found on github or in the code samples bundled with this book (simple-tree-from-external.html and treeData.json). A working example can be found on bl.ocks.org.

Generating a tree diagram from a CSV file.

Creating a tree diagram from a csv file is an extension of the sections where we create a diagram from flat data and where we create a diagram from an external file.

By mashing these together and using a csv file something like the following…

name,parent
Level 2: A,Top Level
Top Level,null
Son of A,Level 2: A
Daughter of A,Level 2: A
Level 2: B,Top Level

… we can ingest the name of the nodes and their relationships and then format the data correctly.

Tree diagram (but from csv)
Tree diagram (but from csv)

The full code for this example can be found on github or in the code samples bundled with this book (simple-tree-from-csv.html and treedata.csv). A working example can be found on bl.ocks.org.

An interactive tree diagram

The examples presented thus far have all been static in the sense that they present information on a web page, but that’s where they stop. One of the strengths of web content is the ability to involve the reader to a greater extent. Therefore the following tree diagram example includes an interactive element where the user can click on any parent node and it will collapse on itself to make more room for others or to simplify a view. Additionally, any collapsed parent node can be clicked on and it will re-grow to its previous condition.

The example included here is a close derivative of Mike Bostock’s example. I won’t fully explain the operation of this file, but we will consider parts of it for interest’s sake.

The full code for this example can be found on github, in the appendices of this book or in the code samples bundled with this book (interactive-tree.html). A working online example can be found on bl.ocks.org.

For a brief visual description of the action. The diagram will initially display the complete tree…

Tree diagram)
Tree diagram)

Then when clicking on the ‘Level 2: A’ node, the tree partially collapses to…

Partially collapsed tree diagram)
Partially collapsed tree diagram)

We could also click on the root node (`Top Level’) to fully collapse the tree…

Fully collapsed tree diagram)
Fully collapsed tree diagram)

Then clicking on the nodes opens the diagram back up again.

One of the important changes to start with is to make each node responsive to the mouse pointer. This is done by including the following in the <style> section.

	.node {
		cursor: pointer;
	}

The code then adds sections to allow the diagram to follow the d3.js model of enter - update - exit for the nodes with a suitable transition in between.

Nodes are coloured (“steelblue”) if they have been collapsed and at the end of the script we have a function that makes use of the d._children reference we have been using in most of our examples.

function click(d) {
  if (d.children) {
	d._children = d.children;
	d.children = null;
  } else {
	d.children = d._children;
	d._children = null;
  }
  update(d);
}

This allows the action of clicking on the nodes to update the data associated with the node and as a consequence change it’s properties in the script based on if statements (Such as "fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; } which will fill the node with “lightsteelblue” if d._children exists, otherwise make it white.)

The examples we have looked at in the previous sections in this chapter are all applicable to this interactive version, so this should provide you with the capability to generate some interesting visualizations.

Enjoy.

Force Layout Diagrams

What is a Force Layout Diagram?

This is not a distinct type of diagram per se. Instead, it’s a way of representing data so that individual data points share relationships to other data points via forces. Those forces can then act in different ways to provide a natural structure to the data. The end result can be a wide variety of representations of connectedness and groupings.

Mike Bostock gave a great talk which focussed on force layout techniques in 2011 at Trulia for the Data Visualization meetup group. Check video of the presentation here: http://vimeo.com/29458354 and the slides here: http://mbostock.github.com/d3/talk/20110921/#0. The most memorable quote I recall from the talk describes force layout diagrams as an “Implicit way to do position encoding”.

Here’s some examples for those who need a reason to view the talk.

Multi-Foci Force Layout

Multi-Foci Force Layout
Multi-Foci Force Layout

Simultaneous forces of repulsion and multiple gravitational focus points create a natural clustering of data points (Source: Mike Bostock http://bl.ocks.org/mbostock/1249681). The graph is animated, so the artefacts such as overlapping circles and the purple circle that is located beside the red area are transitory.

Force Directed Graph with Pan / Zoom

Force Directed Graph with Pan / Zoom
Force Directed Graph with Pan / Zoom

Multiple linked nodes show connections between related entities where those entities are labelled and encoded with relevant information. Created by David Graus and presented here: http://graus.nu/blog/force-directed-graphs-playing-around-with-d3-js/.

Collapsible Force Layout

Collapsible Force Layout
Collapsible Force Layout

This force directed graph can have individual nodes expanded or collapsed by clicking on them to reveal or hide greater detail (Source: Mike Bostock http://bl.ocks.org/mbostock/1062288).

Force Directed Graph showing Directionality

Force Directed Graph showing Directionality
Force Directed Graph showing Directionality

This example showing mobile patent lawsuits between companies presents the direction associated with the links and encodes the links to show different types (Source: Mike Bostock http://bl.ocks.org/mbostock/1153292).

Collision Detection

Collision Detection
Collision Detection

In this example the mouse exerts a repulsive force on the objects as it moves on the screen (Source: Mike Bostock http://bl.ocks.org/mbostock/3231298).

Molecule Diagram

Molecule Diagram
Molecule Diagram

Just for fun, here is a diagram that Mike Bostock made to demonstrate drawing two parallel lines between nodes. He’s the first to admit that increasing the number of lines becomes awkward, but it serves as another example of the flexibility of force diagrams in D3 (Source: Mike Bostock http://bl.ocks.org/mbostock/3037015).

The main forces in play in these diagrams are charge, gravity and friction. More detailed information on these forces and the other parameters associated with the force layout code can be found in the D3 Wiki.

Charge

Charge is a force that a node can exhibit where it can either attract (positive values) or repel (negative values). Varying this value in conjunction with other forces (such as gravity) or a link (on a node by node basis) is generally necessary to maintain stability.

Gravity

The gravity force isn’t actually a true representation of gravitational attraction (this can be more closely approximated using positive values of charge). Instead it approximates the action of a spring connected to a node. This has a more pleasant visual effect when the affected node is closer to its ‘great attractor’ and avoids what would otherwise be a small black hole type effect.

Friction

The frictional force is one designed to act on the movement of a node to reduce its speed over time. It isn’t implemented as true friction (in the physical sense) and should be thought of as a ‘velocity decay’ in the truer sense.

Mike makes the point in the 2011 talk at Trulia that when using gravity in a force layout diagram, it is useful to include a degree of charge repulsion to provide stability. This can be demonstrated by experimenting with varying values of the charges in a diagram and observing the effects.

Force directed graph examples.

There are a large number of possible examples to use to demonstrate force directed graphs. I chose to combine two examples that Mike Bostock has demonstrated in the past. Both use the data for the ‘who’s suing who’ graph because I wanted especially to include the directionality aspect of the links. The two graphs I based the final graph on were the Mobile Patent Suits graph….

Mobile Patent Suits
Mobile Patent Suits

… for the directionality and link encoding and the Force-Directed Graph with Mouseover graph…

Force-Directed Graph with Mouseover
Force-Directed Graph with Mouseover

… for the mouseover effects (note the enlarged ‘Microsoft’ circle).

In spite of the similarities to each other in terms of data and network linkages, the final example code was quite different, so the end result is a distinct hybrid of the two and will look something like this;

Force-Directed Graph with Node Highlighting and Link Value Gradients
Force-Directed Graph with Node Highlighting and Link Value Gradients

In this example the nodes can be clicked on once to enlarge the associated circle and text and then double clicked on to return them to normal. The links vary in opacity depending on an associated value loaded with the data. The example code for the graph above will be explained later in the chapter and can be found on bl.ocks.org or in the code samples bundled with this book (force-highlight-opacity.html and force.csv).

Basic force directed graph showing directionality

The data for this graph has been altered from the data that was comprised of litigants in the mobile patent war to fictitious people’s names and associated values (to represent the strength of the links between the two).

The full code for this diagram can also be found on github or in the code samples bundled with this book (force.html and force.csv). A live example can be found on bl.ocks.org.

In the original examples the data was contained in the graph code. In the following example it is loaded from a csv file. The values loaded are as follows;

source,target,value
Harry,Sally,1.2
Harry,Mario,1.3
Sarah,Alice,0.2
Eveie,Alice,0.5
Peter,Alice,1.6
Mario,Alice,0.4
James,Alice,0.6
Harry,Carol,0.7
Harry,Nicky,0.8
Bobby,Frank,0.8
Alice,Mario,0.7
Harry,Lynne,0.5
Sarah,James,1.9
Roger,James,1.1
Maddy,James,0.3
Sonny,Roger,0.5
James,Roger,1.5
Alice,Peter,1.1
Johan,Peter,1.6
Alice,Eveie,0.5
Harry,Eveie,0.1
Eveie,Harry,2.0
Henry,Mikey,0.4
Elric,Mikey,0.6
James,Sarah,1.5
Alice,Sarah,0.6
James,Maddy,0.5
Peter,Johan,0.7

The code is as follows;

<!DOCTYPE html>
<meta charset="utf-8">
<script src="http://d3js.org/d3.v3.js"></script>

<style>
path.link {
  fill: none;
  stroke: #666;
  stroke-width: 1.5px;
}

circle {
  fill: #ccc;
  stroke: #fff;
  stroke-width: 1.5px;
}

text {
  fill: #000;
  font: 10px sans-serif;
  pointer-events: none;
}
</style>
<body>
<script>
// get the data
d3.csv("force.csv", function(error, links) {

var nodes = {};

// Compute the distinct nodes from the links.
links.forEach(function(link) {
    link.source = nodes[link.source] || 
        (nodes[link.source] = {name: link.source});
    link.target = nodes[link.target] || 
        (nodes[link.target] = {name: link.target});
    link.value = +link.value;
});

var width = 960,
    height = 500;

var force = d3.layout.force()
    .nodes(d3.values(nodes))
    .links(links)
    .size([width, height])
    .linkDistance(60)
    .charge(-300)
    .on("tick", tick)
    .start();

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

// build the arrow.
svg.append("svg:defs").selectAll("marker")
    .data(["end"])      // Different link/path types can be defined here
  .enter().append("svg:marker")    // This section adds in the arrows
    .attr("id", String)
    .attr("viewBox", "0 -5 10 10")
    .attr("refX", 15)
    .attr("refY", -1.5)
    .attr("markerWidth", 6)
    .attr("markerHeight", 6)
    .attr("orient", "auto")
  .append("svg:path")
    .attr("d", "M0,-5L10,0L0,5");

// add the links and the arrows
var path = svg.append("svg:g").selectAll("path")
    .data(force.links())
  .enter().append("svg:path")
    .attr("class", "link")
    .attr("marker-end", "url(#end)");

// define the nodes
var node = svg.selectAll(".node")
    .data(force.nodes())
  .enter().append("g")
    .attr("class", "node")
    .call(force.drag);

// add the nodes
node.append("circle")
    .attr("r", 5);

// add the text 
node.append("text")
    .attr("x", 12)
    .attr("dy", ".35em")
    .text(function(d) { return d.name; });

// add the curvy lines
function tick() {