NVD3.js multiChart x-axis labels is aligned to lines, but not bars
Asked Answered
J

1

6

I am using NVD3.js multiChart to show multiple lines and bars in the chart. All is working fine, but the x-axis labels is aligned only to the line points, not bars. I want to correctly align labels directly below the bars as it should. But I get this:

enter image description here

With red lines I marked where the labels should be.

I made jsFiddle: http://jsfiddle.net/n2hfN/

Thanks!

Jemie answered 8/7, 2014 at 8:0 Comment(5)
You could use the line plus bar chart instead.Fitzhugh
Thank you for answer. But in nv.models.linePlusBarChart I can use attribute bar true|false, I have no more options, for different charts.Jemie
Additionally I need to position - on which axis is lines and on which is bars. multiChart is allowing that.Jemie
I think you might be up against a bug in nvd3: if you check the source, you will find a a comment saying "// TODO: Figure out why the value appears to be shifted" in the x-coordinate calculation of multiBar (which in turn is used by multiChart).Hill
This somehow works perfectly lines1.padData(true)Doiron
R
10

As @Miichi mentioned, this is a bug in nvd3...

I'm surprised that they have a TODO to "figure out why the value appears to be shifted" because it's pretty obvious... The bars use an ordinal scale with .rangeBands() and the line uses a linear scale, and the two scales are never made to relate to one another, except in that they share the same endpoints.

One solution would be to take the ordinal scale from the bars, and simply adjust it by half of the bar width to make the line's x-scale. That would put the line points in the center of the bars. I imagine that something similar is done in the nv.models.linePlusBarChart that @LarsKotthoff mentioned.

Basically, your line's x-scale would look something like this:

var xScaleLine = function(d) {
  var offset = xScaleBars.rangeBand() / 2;
  return xScaleBars(d) + offset;  
};

...where xScaleBars is the x-scale used for the bar portion of the chart.

By combing through the source code for nvd3, it seems that this scale is accessible as chart.bars1.scale().

Maybe someday the authors of nvd3 will decide that their kludge of a library deserves some documentation. For now, I can show you the kind of thing that would solve the problem, by making a custom chart, and showing how the two scales would relate.

First, I'll use your data, but separate the line and bar data into two arrays:

var barData = [
  {"x":0,"y":6500},
  {"x":1,"y":8600},
  {"x":2,"y":17200},
  {"x":3,"y":15597},
  {"x":4,"y":8600},
  {"x":5,"y":814}
];

var lineData = [
  {"x":0,"y":2},
  {"x":1,"y":2},
  {"x":2,"y":4},
  {"x":3,"y":6},
  {"x":4,"y":2},
  {"x":5,"y":5}
];

Then set up the scales for the bars. For the x-scale, I'll use an ordinal scale and rangeRoundBands with the default group spacing for nvd3's multiBar which is 0.1. For the y-scale I'll use a regular linear scale, using .nice() so that the scale doesn't end on an awkward value as it does by default in nvd3. Having some space above the largest value gives you some context, which is "nice" to have when trying to interpret a chart.

var xScaleBars = d3.scale.ordinal()
  .domain(d3.range(barData.length))
  .rangeRoundBands([0, w], 0.1);

var yScaleBars = d3.scale.linear()
  .domain([0, d3.max(barData, function(d) {return d.y;})])
  .range([h, 0])
  .nice(10);

Now here's the important part. For the line's x-scale, don't make a separate scale, but just make it a function of the bars' x-scale:

var xScaleLine = function(d) {
  var offset = xScaleBars.rangeBand() / 2;
  return xScaleBars(d) + offset;  
};

Here's the complete example as a JSBin. I've tried to document the major sections with comments so it's easy to follow the overall logic of it. If you can figure out from the nvd3 source code exactly what each of the elements of the multiChart are called and how to set the individual scales of the constituent parts, then you might be able to just plug in the new scale.

My feeling on it is that you need to have a pretty good handle on how d3 works to do anything useful with nvd3, and if you want to customize it, you're probably better off just rolling your own chart. That way you have complete knowledge and control of what the element classes and variable names of the parts of your chart are, and can do whatever you want with them. If nvd3 ever gets proper documentation, maybe this will become a simple fix. Good luck, and I hope this at least helps you get started.

Roil answered 14/7, 2014 at 18:47 Comment(5)
Thank you! I see that your example is working and am accepting your answer as the best (thanks a lot for such a deep research). I will try it little later and report results.Jemie
You offer to rewrite NVD3 chart, but in your example is working with simple data. I update fiddle jsfiddle.net/n2hfN/28 with more complex data and don't understand how to manage grouped bars and multiline possibility.Jemie
I'd be happy to help you with a different scenario, but the comments is not the place for that discussion. This was a specific question to which I gave a specific answer. If you ask a new question specific to your new issue we can discuss it there. One thing I'll say is that it looks like the multiChart model breaks up the data much like I did, but they do it with filters. Here are the relevant lines in the source codeRoil
Ok, maybe I didn't correctly formatted my question. So I left accepted it, but I can not use it, so I created new one - #24798105Jemie
is there any way to show multiple bar with same label?Beatrice

© 2022 - 2024 — McMap. All rights reserved.