Question

I'm using D3.js to generate a graph with response times over time. Previously I was using Google Charts but it was too heavy. D3 is nice and lightweight, but there's something I can't manage to do that I could do with Google Charts.

The problem is that those graphs sometimes span over one week, sometimes over one day, sometimes over one hour. For each case I have to manually modify how the ticks in the X axis appear. I'm doing it by comparing the first and the last values for the X axis, and checking how much time there's between them, like this:

if (dateDif < 25) { // daily
    xAxis = d3.svg.axis()
        .scale(x)
        .tickFormat(d3.time.format('%H:%M'))
        .ticks(d3.time.hours, 3)
        .orient("bottom");
}

else if (dateDif <= 170) {  // ~ weekly
    xAxis = d3.svg.axis()
        .scale(x)
        .tickFormat(d3.time.format('%d/%m %H:%M'))
        .ticks(d3.time.hours, 24)
        .orient("bottom");
} else { // more than weekly
    xAxis = d3.svg.axis()
        .scale(x)
        .tickFormat(d3.time.format('%d/%m %H:%M'))
        .ticks(d3.time.hours, 96)
        .orient("bottom");
}

But that it's not good, at all, specially when there are just a few values in the chart (they're generated every minute), then no ticks appear (because it falls in the first case, and there are no enough values to span 3 hours).

Do you know of any plugin or method that automagically adapts the X axis for this kind of situations?

Was it helpful?

Solution

d3.svg.axis() is fairly good at handling this type of behavior by default - try removing .tickFormat(...) from your axis, and setting .ticks(n) where n is the desired number of ticks you want on the axis at any scale zoom level - this might be sufficient for what you desire.

Here are a couple of related examples:

http://bl.ocks.org/mbostock/2983699

http://bl.ocks.org/mbostock/1166403

OTHER TIPS

You could define a function to get the tickFormat based on the dateDif, so you wouldn't need to have such a large if-else block.

function getFormat() {
  if (dateDif < 25) {
    return d3.time.format('%H:%M');
  } else {
    return d3.time.format('%d/%m %H:%M');
  }
}

Then you can set .ticks() to a number. In the output, roughly that many ticks will be shown. d3 chooses a number of ticks that is close to your requested value, but also makes a few decisions to try to give optimal output.

// approximately 10 ticks will be displayed
xAxis = d3.svg.axis()
    .scale(x)
    .tickFormat(getFormat())
    .ticks(10)
    .orient("bottom");

If you use this method, you lose a little control over the exact number of ticks that will be shown, but you are guaranteed to have ticks displayed, and if you choose a sensible number for your ticks value, the output will probably be satisfactory.

Here's a fiddle using this technique with some contrived data.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top