Visualizing Data with D3

by Larry Spencer Saturday, September 1, 2012 5:24 PM

In the previous post of this introductory series on D3, we put a rounded rectangle on a Web page. It wasn't much, but it was the beginning of our Reversi (OthelloTM) game. This time, we will lay down the green squares on which we play the game.

 

function createBoard(state) {
    var squareSize = 60;            // Size of a square in pixels.
    var gapSize = squareSize / 10;  // Gap between squares.
    var boardBorder = gapSize * 2;  // Border around the whole board.
    var boardSize = squareSize * squaresInRow + gapSize * (squaresInRow -1) + boardBorder*2;

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

    var newSquares = svg.selectAll("rect")
        .data(state.pieces)
        .enter();

    svg.append("rect")
        .attr("x", 0).attr("y", 0)
        .attr("rx", boardBorder)
        .attr("width", boardSize).attr("height", boardSize);

    newSquares.append("rect")
        .attr("id", function (d, i) { return "square_" + i; })
        .attr("x", function (d, i) { return boardBorder + d.ixCol * (squareSize + gapSize); })
        .attr("y", function (d, i) { return boardBorder + d.ixRow * (squareSize + gapSize); })
        .attr("width", squareSize)
        .attr("height", squareSize)
        .attr("fill", squareColor);
}

 

You can see the result here (SVG-compatible browser required) and this is how it works.

Line 7 is what we saw last time. We add an SVG drawing area to the body of the HTML.

On line 9, we select all the <rect> elements under the newly created <svg> element. But wait! There aren't any yet! Just suspend your doubts until I explain the next couple of lines.

Line 10 is where the data of Data-Driven Documents (D3) comes in. The game state (not shown) contains an array of 64 pieces. Each piece can be white, black or invisible (the default). The .data function says to marry up those 64 objects with the results of the .selectAll on the previous line.

The .selectAll didn't return anything (there were no <rect> elements) so all 64 objects are extras. This is a good time to mention that D3's .data function actually provides three sets. The primary set consists of the first n data points, which matched the n elements returned from the .selectAll. In our case n was 0 so this set is empty.

The second set is called the exit set. It consists of the extra elements in the selection. They are exiting the scene, as it were, because they have no matching data points.

The third set is the enter set. Those are the data elements that are available to enter the display because they are not matched up with existing HTML elements. That's what we have: 64 data points that are not linked to <rect> elements yet. You obtain them with the .enter() function (line 11).

So, what we get from lines 9 through 11 is a selection of 64 invisible game pieces that are not yet linked to graphical elements.

Lines 13 through 16 are also from the previous post. They create the large, black square in the background. I put this after the bit on lines 9 through 11 because I did not want the <rect> that lines 13 through 16 creates to be selected by the .selectAll. That was lazy of me. A more robust way would have been to make the large, background <rect> be of one class and the 64 squares be of another, and then select based on the class-qualified rect: .selectAll("rect.backgroundSquare"). All of D3's selects are done in the standard HTML way.

The final lines create the 64 squares based on the 64 "enter" data points. We are appending them to (making them child elements of) the <svg> element, because selectAll was called on the svg variable.

What's interesting is the way the id, x and y attributes are assigned. D3 lets you assign them from functions that take two parameters, conventionally named d and i, representing the data item and its index in the .enter set, respectively. Our data items happen to have properties for ixRow and ixCol that represent each square's logical location on the grid. We can use these to compute the physical locations on the SVG canvas.

In other situations, your data items might contain histogram count-values, x/y coordinates on a line graph, or whatever. You would use these properties to create the appropriate SVG elements, possibly with the assistance of D3.

Next time, we'll see how to handle events with D3.

Tags: , ,

All | Talks

About the Author

Larry Spencer

Larry Spencer develops software with the Microsoft .NET Framework for ScerIS, a document-management company in Sudbury, MA.