Ben Kimball
Ben Kimball

Reputation: 53

D3 time scale axis ticks are irregular on year boundaries

UPDATE: The bl.ocks.org example has been updated with the solution.

Is it possible to make the default tick generator for a time scale axis ignore month and year boundaries when calculating tick positions?

As seen in this bl.ocks.org example, when a D3 time scale axis with day ticks spans the end of a year, the default tick generator sometimes draws ticks with irregular spacing. This code (also in the gist above) result in an axis with ticks spaced every two days except for December 31 and January 1, which are both drawn, resulting in an irregular appearance. I've highlighted the undesirable tick labels in red.

<!DOCTYPE html>
<meta charset="utf-8">
<style>
  p { width: 600px; margin-bottom: 30px; }
  .chart { shape-rendering: crispEdges; }
  .axis text { font: 10px sans-serif; }
  .axis line, .axis path { fill: none; stroke: black; }
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>

var start_date = "2013-12-21T00:00:00Z",
    end_date   = "2014-01-07T00:00:00Z",
    iso        = d3.time.format.iso,
    t1         = iso.parse(start_date),
    t2         = iso.parse(end_date),

var margin = {top: 0, right: 10, bottom: 20, left: 10},
    width  = 600 - margin.left - margin.right,
    height = 100 - margin.top - margin.bottom;

var x = d3.time.scale()
    .domain([t1, t2])
    .range([0, width])

var xAxis = d3.svg.axis()
    .scale(x)

var svg = d3.select("body").append("svg")
    .attr("class", "chart")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
  .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

svg.append('g')
    .attr('class', 'x axis')
    .call(xAxis)
  .selectAll('text')
    .attr('fill', function(d) { if(is_problematic(d)) { return "red"; } })

function is_problematic(d) {
  return ((d.getDate() == 31) || (d.getDate() == 1));
}

Upvotes: 5

Views: 1760

Answers (1)

Adam Pearce
Adam Pearce

Reputation: 9293

If you explicitly set the tick values, you'll avoid the weird end of the year rounding issue:

var xAxis = d3.svg.axis()
    .scale(x)
    .tickValues(d3.time.days(t1, t2).filter(function(d, i){ return !(i % 2); }))

fixed

Upvotes: 2

Related Questions