Line Graph Tutorial

1) Import d3 library -

Make sure to include this text so that you can access the d3 library. This is typically placed in the main Head of the HTML file.

<script type="text/javascript" src="http://mbostock.github.com/d3/d3.js">  </script>

2) Insert the div container -

This code will specify where your d3 visualization will be placed in the HTML page

<div id="viz"></div>

3) Declare Variables -

Here we first specify the data we will be using in our line graph as the arrays data1 and data2. The height and width of our graph will be determined by w and h. The margin will be the blank space between our x and y axis and the edge of our graph which we will use to display the number scale. Finally we have our x and y linear scale functions which we will need to convert our data values to x and y positions on the screen

<script type="text/javascript">
          
    var data2 = [1, 3, 4, 3, 6, 1, 8, 2, 4, 1, 3, 4, 1]
    var data1 = [3, 6, 2, 7, 5, 2, 1, 3, 8, 9, 2, 5, 7],
     
     w = 500,
     h = 200,
     margin = 20,
     y = d3.scale.linear().domain([0, d3.max(data1)]).range([0 + margin, h - margin]),
     x = d3.scale.linear().domain([0, data1.length]).range([0 + margin, w – margin])

4) Create our SVG -

Here we first select our viz container and add an SVG element to it, and then specify the height and width. We then append a g element to our SVG element so that everything added to the g element will be grouped together. The transformation is used to move our coordinate grid down by 200 pixels.

var vis = d3.select("#viz")
     .append("svg:svg")
     .attr("width", w)
     .attr("height", h)

var g = vis.append("svg:g")
     .attr("transform", "translate(0, 200)")

5) Draw the axis -

We start by appending a SVG line element to our g element from earlier, and to give it some visual appeal we add a transition operator which will make it look as if the line is drawn when the visualization is first loaded. We then specify the color of the line by declaring it's stroke as black. To actually draw the line we need to specify a starting point (x1, y1) and an end point (x2, y2). Remember that the 0,0 coordinate is in the top left corner, so that is why our y values are negative. Drawing the Y axis is very similar, the only difference is that we call d3.max(data1) so that we know what the maximum value is and draw the scale accordingly.

//Lets draw the X axis
g.append("svg:line")
     .transition()
     .duration(1000)
          .style("stroke", "black")

          .attr("x1", x(0))
          .attr("y1", -1 * y(0))
          .attr("x2", x(w))
          .attr("y2", -1 * y(0))

//Lets draw the Y axis
g.append("svg:line")
     .transition()
     .duration(1000)
     .style("stroke", "black")
      .attr("x1", x(0))
      .attr("y1", -1 * y(0))
      .attr("x2", x(0))
      .attr("y2", -1 * y(d3.max(data1)))

As you can see from the example below we are making progress and now have our axis displayed.

6) Add axis labels -

Here we will add in our numerical labels for both the x and y axis. First we start by selecting the x labels, then we use the scalar function x.ticks(5) which will return the proper tickmarks for where the numbers should go. Naturally we will add another transition here so that when the page is loaded our labels will smoothly slide into place. We then add the text element to our g element, and we define our x values by simply calling a function with the parameter I for index and just return it. Since this is for the x axis we leave the y value set to 0, and vice verse for the y labels. Finally we set the text-anchor so that the numbers will appear directly below the tick marks we will draw in the next step.

//X Axis labels
g.selectAll(".xLabel")
     .data(x.ticks(5))
     .style("font-size","9pt")
     .enter()
     .append("svg:text")
     .transition()
     .duration(1000)
      .attr("class", "xLabel")
      .text(String)
      .attr("x", function(i) { return x(i) })
      .attr("y", 0)
      .attr("text-anchor", "middle")

//Y axis labels
g.selectAll(".yLabel")
     .data(y.ticks(4))
     .style("font-size","9pt")
     .enter().append("svg:text")
     .transition()
     .duration(1000)
      .attr("class", "yLabel")
      .text(String)
      .attr("x", 0)
      .attr("y", function(i) { return -1 * y(i) })
      .attr("text-anchor", "right")
      .attr("dy", 4)

7) Add tick marks -

This step is very similar to the last. We now will select the xTicks and add the data points using the scalar function x.ticks(5). However, this time instead of adding a text element we will just add a line element. Again we use another transition to help animate the visualization. Similar to when we where drawing the lines for the axis here we must also specify the start and end points for each tick mark. Remember that we are using SelectAll here, so this will evaluate for each individual tick mark.

//X axis tick marks
g.selectAll(".xTicks")
    .data(x.ticks(5))
    .enter().append("svg:line")
     .transition()
     .duration(1000)
      .style("stroke", "black")
      .attr("class", "xTicks")
      .attr("x1", function(i) { return x(i); })
      .attr("y1", -1 * y(0))
      .attr("x2", function(i) { return x(i); })
      .attr("y2", -1 * y(-0.3))
 
//Y axis tick marks
g.selectAll(".yTicks")
    .data(y.ticks(4))
    .enter().append("svg:line")
     .transition()
     .duration(1000)
      .style("stroke", "black")
      .attr("class", "yTicks")
      .attr("y1", function(d) { return -1 * y(d); })
      .attr("x1", x(-0.3))
      .attr("y2", function(d) { return -1 * y(d); })
      .attr("x2", x(0));

Now we can see that both our labels and tick marks have been added to the axis we drew earlier.

8) Draw the line -

Now that we have our axis down lets add a line to represent our values in data1. We begin by defining a variable/function line that will allow us to draw this line, we use the helper function d3.svg.line() to define our d attribute which we will need to actually store our datapoints. Note how we use the x and y functions from earlier to find exactly where the place these points. We then load the data to this line by appending the path to the g elecment and passing the d attribute our data1 values. We also add a transition operation to the line as well, mainly to add the delay of 1.1s so that the line graph will only appear once the axis and labels have moved into place.

var line = d3.svg.line()
     .x(function(d,i) { return x(i); })
     .y(function(d) { return -1 * y(d); })

g.append("svg:path")
    .transition()
    .delay(1100)
        .attr("d", line(data1))
        .style("stroke", "indianred")
        .style("stroke-width", 3)
        .style("fill", "none")

You can now see our static graph loaded here:

9) Adding interactivity -

Now we will demonstrate how you can add a simple mouse event to this graph to allow us to load in the values in data2 by clicking anywhere on the graph. We start by declaring a boolean variable change which will be either true or false depending on which data set we are trying to load. We start with our vis element, which if you remember is the main parent element for our whole graph, and call the on operator and specify that on a mouse click (mousedown) we will launch a function. In this function we will use the change variable to determine which dataset we are currently displaying, and then through an if-else statement we will select the path element of our g element, use a transition of course, and then change the d attribute of this path element to the opposing dataset, and change the color of our line for added effect. When loading back in data1 we also specify the .ease function of back which will make our transition effect "simulate backing into a parking space."

var change=new Boolean()
change = true
     vis.on("mousedown" , function(){
          if(change){
               g.select("path")
               .transition()
               
               .duration(2000)
                    .attr("d", line(data2))
                    .style("stroke", "steelblue")
                                        
               change = false
          }
          else {
               g.select("path")
                    .transition()
                    .ease("back")
                    .duration(2000)
                         .attr("d", line(data1))
                         .style("stroke", "indianred")
                                                       
               change = true
          }
     })

Here is the final product, a line graph with several transition effects.