Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I keep tick marks from repeating when I have a small number of dates on an nvd3 chart

Tags:

d3.js

nvd3.js

The x axis likes to repeat dates when there are a limited number of dates. Please see this fiddle:

http://jsfiddle.net/skilesare/bFfJ2/1/

nv.addGraph(function() {  

    var data = fakeActivityByDate();

    var chart = nv.models.lineChart();




chart.xAxis
       .axisLabel('Date')
       .rotateLabels(-45)
       .tickFormat(function(d) { return d3.time.format('%b %d')(new Date(d)); });

   chart.yAxis
       .axisLabel('Activity')
       .tickFormat(d3.format('d'));

   d3.select('#chart svg')
       .datum(data)
     .transition().duration(500)
       .call(chart);

   nv.utils.windowResize(function() { d3.select('#chart svg').call(chart) });

   return chart;
 });

function days(num) {
  return num*60*60*1000*24
}
 /**************************************
  * Simple test data generator
  */

function fakeActivityByDate() {
   var lineData = [];
   var y=0;
   var start_date = new Date() - days(365); // one year ago

   for (var i = 0; i < 4; i++) {
     lineData.push({x: new Date(start_date + days(i)), y: y});
     y=y+Math.floor((Math.random()*10)-3);
   }

   return [
     {
       values: lineData,
       key: 'Activity',
       color: '#ff7f0e'
     }
   ];
 }`

If your screen is wide enough you will see dates repeated. If you sqeeze the window, the problem goes away.

like image 402
Austin Fatheree Avatar asked Feb 10 '13 15:02

Austin Fatheree


2 Answers

The problem is not related to screen size as it might appear also for cases like this: you have just 3 days to display and instead of 3 ticks you will get 6 or 10 or whatever (actually that's precisely the case you're in if I look at your jsfiddle). It is true that nvd3 uses something like this to set the dimensions of the ticks (take a look at axis.js):

if (ticks !== null)
    axis.ticks(ticks);
 else if (axis.orient() == 'top' || axis.orient() == 'bottom')
    axis.ticks(Math.abs(scale.range()[1] - scale.range()[0]) / 100);

There are several solutions to this problem:

  • you either implement your own axis component and replace the nvd3's axis

  • you either modify the nvd3's axis component directly and add some settings that would then be used specifically for this scenario you are describing (you will notice that as it is nvd3 is highly customizable).

  • Hack: you use d3's own tickValues() and pass it an array with the dates you want to have on the screen (example: first date, last date and several dates in between calculated by your algorithm). See d3's documentation: https://github.com/mbostock/d3/wiki/SVG-Axes#wiki-tickValues. Depending on the case (which axis you want to modify and which chart) you not only need to comment the code I pasted you and add your algorithm for setting the tickValues to the chart component (lineChart for example) but also you will need to add a setter for tickValues and overwrite the values with your values.

like image 65
paxRoman Avatar answered Nov 14 '22 02:11

paxRoman


A late reply , but might be useful to others. I use a workaround to eliminate duplicate ticks, by maintaining an array of already applied ticks & d3's multi format specifier.

    uniqueTicks = [];
    xTickFormat = d3.time.format.multi([
                                        ["%Y", function (d) {
                                        // If tick not applied yet                                            
                                        if (uniqueTicks.indexOf(d.getFullYear()) === -1) {
                                                uniqueTicks.push(d.getFullYear());
                                                return true;
                                            } else {
                                                return false;
                                            }                                        
                                        }],
                                        ["", function () {
                                            return true;
                                        }]
                                    ]);

    d3.svg.axis().tickFormat(xTickFormat );

This trick can be tweaked for any other type of tick format.

like image 44
user3415370 Avatar answered Nov 14 '22 00:11

user3415370