D3 Tips and Tricks v4

Wednesday, 31 August 2016

Rotating text labels for a graph axis in v4

The following post is a section of the book 'D3 Tips and Tricks v4.x'.  The entire book can be downloaded in pdf format for free from Leanpub or you can read it online here.
Since this post is a snapshot in time. I recommend that you download a copy of the book which is updated frequently to improve and expand the content.
---------------------------------------

As referenced in the post where we initially developed our simple graph, the axes of that graph had no styling or configuration changes made to them at all. One of the results of this is that the font size, type, number of ticks and the way that the values are represented is very much at the default settings.

Rotating text labels for a graph axis

An answer to the problem of overlapping axis values might be to rotate the text to provide more space.
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-axis-rotated.html and data.csv). A working example can be found on bl.ocks.org.
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: 20, right: 20, bottom: 70, left: 50},
I found that 70 pixels was sufficient.
The remainder of our changes occur in the block that draws the x axis.
  // Add the X Axis
  svg.append("g")
      .attr("class", "axis")
      .attr("transform", "translate(0," + height + ")")
      .call(d3.axisBottom(x).ticks(10))
      .selectAll("text") 
        .style("text-anchor", "end")
        .attr("dx", "-.8em")
        .attr("dy", ".15em")
        .attr("transform", "rotate(-65)"); 
It’s pretty standard until the .call(d3.axisBottom(x).ticks(10)) 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 onward, we are operating on the text elements associated with the x axis. In effect; the following four ‘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

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!

The post above (and heaps of other stuff) is in the book 'D3 Tips and Tricks v4.x' that can be downloaded for free (or donate to encourage further development :-)).

Sunday, 28 August 2016

Changing the number of ticks on an axis in d3.js v4

The following post is a section of the book 'D3 Tips and Tricks v4.x'.  The entire book can be downloaded in pdf format for free from Leanpub or you can read it online here.
Since this post is a snapshot in time. I recommend that you download a copy of the book which is updated frequently to improve and expand the content.
---------------------------------------
As referenced in the post where we initially developed our simple graph, the axes of that graph had no styling or configuration changes made to them at all. One of the results of this is that the font size, type, number of ticks and the way that the values are represented is very much at the default settings.

Changing the number of ticks on an axis

Now we shall address the other problem that cropped up when we changed the size of the text. We have overlapping values on the x axis.
Overlapping axis values
If I was to be brutally honest, I think that the number of values (ticks) on the graph is a bit too many. The format of the values (especially on the x axis) is too wide and this type of overlap was bound to happen eventually.
Good news. D3 has got us covered.
The axis component includes a function to specify the number of ticks on an axis. All we need to do is add in the function and the number of ticks like so;
  // Add the X Axis
  svg.append("g")
      .attr("class", "axis")
      .attr("transform", "translate(0," + height + ")")
      .call(d3.axisBottom(x)
              .ticks(5));
With the end result looking like this;
Nicely spaced axis values
We can see that D3 has picked tick values that seem nice and logical. There’s one that starts on the 1st of April that’s just labelled ‘April’ and they go at a nice interval of one week for the subsequent ticks. Nice.
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
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
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, 15 and 30-second.
  • 1, 5, 15 and 30-minute.
  • 1, 3, 6 and 12-hour.
  • 1 and 2-day.
  • 1-week.
  • 1 and 3-month.
  • 1-year.
And yes. If you increase the number of ticks, you need to wait till you get to 10 before they change to an axis with interval of two days. And yes, the overlap is still there;
Still overlapping axis values
If we do a quick count we should also notice that we have 19 ticks!
The question should be asked. Can we specify our own intervals? Great question! Yes we can.
What we need to do is to use another D3 trick and specify an exact interval using the d3 time component. In our particular situation all we need to do is specify an interval inside the .ticks function. Specifically for an interval of 4 days for example we would use something like;
  // Add the X Axis
  svg.append("g")
      .attr("class", "axis")
      .attr("transform", "translate(0," + height + ")")
      .call(d3.axisBottom(x)
              .ticks(d3.timeDay.every(4)));
Here we use the timeDay unit of ‘days’ and specify an interval of 4 days.
The graph will subsequently appear as follows;
Axis ticks at 4 day intervals
Intervals have a number of standard units (including UTC time) such as;
  • d3.timeMillisecond : Milliseconds
  • d3.timeSecond : Seconds
  • d3.timeMinute : Minutes
  • d3.timeHour : Hours
  • d3.timeDay : Days
  • d3.timeWeek : This is an alias for d3.timeSunday for a week
  • d3.timeSunday : A week starting on Sunday
  • d3.timeMonday : A week starting on Monday
  • d3.timeTuesday : A week starting on Tuesday
  • d3.timeWednesday : A week starting on Wednesday
  • d3.timeThursday : A week starting on Thursday
  • d3.timeFriday : A week starting on Friday
  • d3.timeSaturday : A week starting on Saturday
  • d3.timeMonth : Months starting on the 1st of the month
  • d3.timeYear : Years Starting on the 1st day of the year
But what if we really wanted that two day separation of ticks without the overlap? Check it out in the next post...

The post above (and heaps of other stuff) is in the book 'D3 Tips and Tricks v4.x' that can be downloaded for free (or donate to encourage further development :-)).

Friday, 26 August 2016

Changing the text size for axes in d3.js v4

The following post is a section of the book 'D3 Tips and Tricks v4.x'.  The entire book can be downloaded in pdf format for free from Leanpub or you can read it online here.
Since this post is a snapshot in time. I recommend that you download a copy of the book which is updated frequently to improve and expand the content.
---------------------------------------

As referenced in the post where we initially developed our simple graph, the axes of that graph had no styling or configuration changes made to them at all. One of the results of this is that the font size, type, number of ticks and the way that the values are represented is very much at the default settings.

Change the text size

The first thing that we will change is the text size for the axes. The default size (built into D3) is 10px with the font type of sans-serif.
There are a couple of different ways that we could change the font size and either one is valid. The first way is to specify the font as a style when drawing an individual axis. To do this we simply add in a font style as follows;
  svg.append("g")
      .style("font", "14px times")
      .attr("transform", "translate(0," + height + ")")
      .call(d3.axisBottom(x));
This will increase the x axis font size to 14px and change the font type to ‘times’. Just like this;
X axis 14px, times
So there are a few things to notice here.
Firstly, we do indeed have a larger font and it appears to be of the type ‘times’. Yay!
Secondly, the y axis has remained as 10px sans-serif (which is to be expected since we only added the style to the x axis code block)
Lastly, the number of values represented on the x axis has meant that with the increase in font size there is some overlapping going on. We will deal with that shortly…
The addition of the styling for the x axis has been successful and in a situation where only one element on a page is being adjusted, this is a perfectly valid way to accomplish the task. However, in this case we should be interested in changing the font on both the x and y axes. We could do this by adding a duplicate style line to the y axis block, but we have a slightly better way of accomplishing the task by declaring the style in the HTML style block at the start of the code and then applying the same style to both blocks.
In the <style> ... </style> section at the start of the file add in the following line;
.axis { font: 14px sans-serif; }
This will set the font to 14px sans-serif (I prefer this to ‘times’) for anything that has the axis class applied to it. All we have to do then is to tell our x and y axes blocks to use the axis class as an attribute. We can do this as follows;
  // Add the X Axis
  svg.append("g")
      .attr("class", "axis")
      .attr("transform", "translate(0," + height + ")")
      .call(d3.axisBottom(x));

  // Add the Y Axis
  svg.append("g")
      .attr("class", "axis")
      .call(d3.axisLeft(y));
It could be argued that this doesn’t really conserve more code, but in my humble opinion it adds a more elegant way to alter styling in this case.
The end result now looks like the following;
Both axes 14px, sans-serif
The post above (and heaps of other stuff) is in the book 'D3 Tips and Tricks v4.x' that can be downloaded for free (or donate to encourage further development :-)).