score:8

Accepted answer

If you want to control the ticks precisely, you should use .tickValues() rather than ticks().

API Doc

score:2

I understand that the question was asked a long time ago, but I recently faced the same issue and found a good solution for it.

The reason of the issue with inaccurate ticks on the x-axis is the inaccurate internal numeric representation of dates that we want to display on the x-axis. As we can see here https://nvd3.org/examples/line.html when values on the x-axis are numbers, all ticks are displayed accurately. When we want to display dates on the x-axis, we also need to convert dates to some numeric representation. Typically dates are converted to the numeric representation via the Date.prototype.getTime() function and then labels are formatted using a code like this chart.xAxis.tickFormat(d3.time.format('%b %Y')(date)) But the accuracy which the getTime() function provides to us is redundant in most cases. Assume we want to display only months and years on the x-axis and have a sorted list of dates where different items always have different moths but the item with a particular month may have any day and time. So, there may be a situation, where two adjacent list items have though have different months are very close to each other (for example, "Feb 28" and "Mar 1"). When we convert these list items to numeric representation using the getTime() function, the resulting list of big numbers remembers how far adjacent list items stand apart from each other. As the distance between adjacent chart points is always equal in NVD3 charts, NVD3 tries to fine-tune labels display to provide the info that some numbers are close to each other, but others are not. It leads to inaccurately displayed labels or even to duplicated labels if a chart has few points.

The solution is to omit redundant information about date and time when we convert dates to numbers. I needed to display only months and years on the x-axis and this code works great for me.

function getChartData(data) {
  var values = data.map(function(val) {
    return {
        x: val.x.getUTCFullYear() * 12 + val.x.getUTCMonth(),
        y: val.y
      }
  });

  return [{
    values: values,
    key: 'date'
  }];
}

function formatDate(numericValue) {
  var year = Math.floor(numericValue / 12);
  var month = numericValue % 12;
  return shortMonthNames[month] + ' ' + year;
}
    
nv.addGraph(function() {
  var data = [
   ...
    {
      x: new Date(2020, 2, 15, 15, 12),
      y: 90
    },
    {
      x: new Date(2020, 3, 3, 3, 54),
      y: 50
    },
    ...
  ];
  
  var chart2 = nv.models.lineChart()
  .showXAxis(true)
  .showYAxis(true);
  
  chart2.xAxis.tickFormat(formatDate);
  
  d3.select('svg#svg')
  .datum(getChartData(data))
  .call(chart2);
  
  return chart2;
});

Here we don't use the .tickValues() NVD3 function and so don't interfere to the default rendering engine, so the NVD3 lib can automatically add or remove axis labels when the viewport width is changed, and ticks and corresponding vertical lines are displayed exactly on the place where they should be.

Here is the full working example http://jsfiddle.net/AlexLukin/0ra43td5/48/


Related Query

More Query from same tag