Working with basic axes

In this recipe, we will focus on introducing the basic concepts and supports of the Axis component in D3 while covering different types and features of Axis as well as their SVG structures.

Getting Ready

Open your local copy of the following file in your web browser:

https://github.com/NickQiZhu/d3-cookbook/blob/master/src/chapter5/basic-axes.html

How to do it...

Let's first take a look at the following code sample:

<div class="control-group">
    <button onclick="renderAll('bottom')">
        horizontal bottom
    </button>
    <button onclick="renderAll('top')">
        horizontal top
    </button>
    <button onclick="renderAll('left')">
        vertical left
    </button>
    <button onclick="renderAll('right')">
        vertical right
    </button>
</div>

<script type="text/javascript">
    var height = 500, 
        width = 500, 
        margin = 25,
        offset = 50,
        axisWidth = width - 2 * margin,
        svg;
    
    function createSvg(){ // <-A
         svg = d3.select("body").append("svg") // <-B
            .attr("class", "axis") // <-C
            .attr("width", width)
            .attr("height", height);
    }
    
    function renderAxis(scale, i, orient){
        var axis = d3.svg.axis() // <-D
            .scale(scale) // <-E
            .orient(orient) // <-F
            .ticks(5); // <-G
        
        svg.append("g")        
            .attr("transform", function(){ // <-H
                if(["top", "bottom"].indexOf(orient) >= 0)
                    return "translate("+margin+","+i*offset+")";
                else
                    return "translate("+i*offset+", "+margin+")";
            })
            .call(axis); // <-I
    }
    
    function renderAll(orient){
        if(svg) svg.remove();
        
        createSvg();
        
        renderAxis(d3.scale.linear()
                    .domain([0, 1000])
                    .range([0, axisWidth]), 1, orient);
        renderAxis(d3.scale.pow()
                    .exponent(2)
                    .domain([0, 1000])
                    .range([0, axisWidth]), 2, orient);
        renderAxis(d3.time.scale()
                    .domain([new Date(2012, 0, 1), new Date()])
                    .range([0, axisWidth]), 3, orient);
    }
</script>

The preceding code produces a visual output with only the four buttons shown in the following screenshot. Once you click on horizontal bottom, it shows the following:

How to do it...

Horizontal Axis

How to do it...

Vertical Axis

How it works...

The first step in this recipe is to create the svg element which will be used to render our axes. This is done by the createSvg function, which is defined on line A, and using D3 append and attr modifier functions shown on line B and C.

Note

This is the first recipe in this book that uses SVG instead of HTML element since D3 Axis component only supports SVG. If you are not familiar with SVG standard, don't worry; we will cover it in detail in Chapter 7, Getting into Shape. While for the purpose of this chapter, some of the basic and limited SVG concepts will be introduced when they are used by D3 Axis component.

Let's look at how we created the SVG canvas in the following code:

var height = 500, 
  width = 500, 
  margin = 25,
  offset = 50,
  axisWidth = width - 2 * margin,
  svg;
    
function createSvg(){ // <-A
     svg = d3.select("body").append("svg") // <-B
        .attr("class", "axis") // <-C
        .attr("width", width)
        .attr("height", height);
} 

Now we are ready to render the axes on this svg canvas. The renderAxis function is designed to do exactly just that. On line D, we first create an Axis component using the d3.svg.axis function:

var axis = d3.svg.axis() // <-D
            .scale(scale) // <-E
            .orient(orient) // <-F
            .ticks(5); // <-G

D3 Axis is designed to work out of the box with D3 quantitative, time, and ordinal scales. Axis scale is provided using the scale() function (see line E). In this example, we render three different axes with the following scales:

d3.scale.linear().domain([0, 1000]).range([0, axisWidth])
d3.scale.pow().exponent(2).domain([0, 1000]).range([0, axisWidth])
d3.time.scale()
  .domain([new Date(2012, 0, 1), new Date()])
  .range([0, axisWidth])

The second customization we have done with the axis object is its orient. Orient tells D3 how this axis will be placed (orientation), therefore, how it should be rendered, whether horizontally or vertically. D3 supports four different orient configurations for an axis:

  • top: A horizontal axis with labels placed on top of the axis
  • bottom: A horizontal axis with labels placed at the bottom of the axis
  • left: A vertical axis with labels placed on the left hand side of the axis
  • right: A vertical axis with labels placed on the right hand side of the axis

On line G, we set the number of ticks to 5. This tells D3, ideally how many ticks we want to render for this axis, however, D3 might choose to render slightly more or less ticks based on the available space and its own calculation. We will explore Axis ticks configuration in detail in the next recipe.

Once the axis is defined, the final step in this creation process is to create a svg:g container element, which will then be used to host all SVG structures that are required to render an axis:

svg.append("g")        
  .attr("transform", function(){ // <-H
    if(["top", "bottom"].indexOf(orient) >= 0)
      return "translate(" + margin + ","+ i * offset + ")";
    else
      return "translate(" + i * offset + ", " + margin + ")";
    })
    .call(axis); // <-I

Note

Having a g element to contain all SVG elements related to an axis is not only a good practice but also a requirement of D3 axis component.

Most of the logic in this code snippet is related to the calculation of where to draw the axis on svg canvas using the transform attribute (see line H). In the preceding code example, to shift the axis using offsets we used the translate SVG transformation, which allows us to shift an element using a distance parameter that is defined with the coordinates in x and y.

Note

SVG transformation will be discussed in detail in Chapter 7, Getting into Shape, or you can refer to the following URL for more information on this topic:

http://www.w3.org/TR/SVG/coords.html#TranslationDefined

The more relevant part of this code is on line I, where the d3.selection.call function is used with the axis object being passed in as the parameter. The d3.selection.call function invokes the given function (in our case the axis object) with the current selection passed in as an argument. In other words, the function being passed into the d3.selection.call function should have the following form:

function foo(selection) {
  ...
}

Tip

The d3.selection.call function also allows you to pass in additional arguments to the invoking function. For more information visit the following link:

https://github.com/mbostock/d3/wiki/Selections#wiki-call

Once the D3 Axis component is called, it will take care of the rest and automatically create all necessary SVG elements for an axis (see line I). For example, the horizontal-bottom time axis in the example shown in this recipe shown in this recipe has the following complicated SVG structure automatically generated, which we don't really need to know much about:

How it works...

Horizontal bottom time Axis SVG structure

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset