How to align legend dynamically on top-center of the chart in c3.js? - d3.js

I want to put the same legend top center of the chart. Legend items will be dynamic, likes sometimes 2,5 or 9, etc.
So, it should take space dynamically like how it is acting on the bottom.
I tried with inset functionality but it seems this is not looking better like the bottom one.
and there are few more complexity like I want it like a flat, so now if I define step size 3 then maybe, for now, it looks good for 9 items. but when there will be 2 items, it will show as a list!!

Although I solved that problem through the following solution:
// get parent elements
let parentEle = d3.selectAll("#chartID svg");
let childrenEle = parentEle
//Convert selection to selection representing the children
.selectAll(function () { return this.childNodes; })
.filter('g');
// putting legends position on TOP
d3.select(childrenEle._groups[0][2]).attr('transform', 'translate(0, 5)');
you have to keep eye on chart width - height and according to this, you may have to control padding in TOP for the chart.
might be this is not a good solution but it works fine for me :D

Related

AmCharts. Aligning balloons

I've spent a lot of time finding the solution, but i can't see any property in AmChatrts documentation that can align balloons not vertically. Actually, I just want to see all balloons, but not in one column. Can anybody help me?
There is currently no way to make the balloons stack in any different way than in once column. However, there are a few alternatives you can consider.
1) Displaying just one balloon.
To do that, set oneBalloonOnly to true:
var chart = AmCharts.makeChart("chartdiv",{
...
"chartCursor": {
"oneBalloonOnly": true
}
});
This will make the cursor display just one balloon of the closest graph.
2) Disable balloons and use a legend instead.
To disable balloons, simply set [valueBalloonsEnabled][3] in chart cursor's settings to false.
var chart = AmCharts.makeChart("chartdiv",{
...
"chartCursor": {
"valueBalloonsEnabled": false
},
"legend": {}
});
The legend will show relative value next to each graph title when you hover over the chart.
3) Consolidate values from multiple graphs into a single balloon.
To do that, use graph's balloonText property. It lets you reference to any field in data, so you can make it display values from any graph.
Here's a good example of the above.
Here's a good demo on how to do that.

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

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:
With red lines I marked where the labels should be.
I made jsFiddle: http://jsfiddle.net/n2hfN/
Thanks!
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.

How to disable legend in nvd3 or limit it's size

I'm using nvd3 and have a few charts where the legend is much to large. E.g. a scatter/bubble with 15 groups and the group names are long. The legend is so large that it leaves almost no room for the chart itself.
Is there a way to remove the legend or toggle the legend or limit the height/width it is taking up? Any example would be great.
Also, is there a way to have the bubble show a descriptive string? Right now when you stand on top of a bubble it highlights the x/y coordinates. I also want it to show the bubble name.
For example, each of my bubbles represents a country (which has a name), the x is GDP and the y is debt. The group is a classification/not name.
.showLegend(false) will help you. Here is an example -
chart = nv.models.multiBarHorizontalChart().x(function(d) {
return d.x
}).y(function(d) {
return d.y
}).showLegend(false);

d3 autospace overlapping tick labels

Is there a way in d3 to not draw overlapping tick labels? For example, if I have a bar chart, but the bars are only 5 pixels wide and the labels are 10 pixels wide, I end up with a cluttered mess. I'm currently working on an implementation to only draw the labels when they do not overlap. I can't find any existing way to do that, but wasn't sure if anyone else had dealt with this problem.
There is no way of doing this automatically in D3. You can set the number of ticks or the tick values explicitly (see the documentation), but you'll have to figure out the respective numbers/values yourself. Another option would be to rotate the labels such that there is less chance of them overlapping.
Alternatively, like suggested in the other answer, you could try using a force layout to place the labels. To clarify, you would use the force layout on the labels only -- this is completely independent of the type of chart. I have done this in this example, which is slightly more relevant than the one linked in the other answer.
Note that if you go with the force layout solution, you don't have to animate the position of the labels. You could simply compute the force layout until it converges and then plot the labels.
I've had a similar problem with multiple (sub-)axis, where the last tick overlaps my vertical axis in some situations (depending on the screen width), so I've just wrote a little function that compares the position of the end of the text label with the position of the next axis. This code is very specific to my use case, but could adapted easily to your needs:
var $svg = $('#svg');
// get the last tick of each of my sub-axis
$('.tick-axis').find('.tick:last-of-type').each(function() {
// get position of the end of this text field
var endOfTextField = $(this).offset().left + $(this).find('text').width();
// get the next vertical axis
var $nextAxis = $('line[data-axis="' + $(this).closest('.tick-axis').attr('data-axis') + '"]');
// there is no axis on the very right, so just use the svg width
var positionOfAxis = ($nextAxis.length > 0) ? $nextAxis.offset().left : $svg.offset().left + $svg.width();
// hide the ugly ones!
if (endOfTextField > positionOfAxis) {
$(this).attr('class', 'tick hide');
}
});
The ticks with color: aqua are the hidden ones:

Line Plus Bar with Multi Bars?

I'm trying to make an chart using the default line plus bar chart, but I want to use two or more streams in the bars, is it possible?
Currently, when I try to do this, I got some trouble with the effects of the chart, and so I can't show properly the hover balloon of the bars, he always display the data of just one of the streams. But the main problem is the dates of x axis, displaying 1970's dates, when I remove the second stream of bars, the dates display well:
Anyone already tried to do this kind of chart successfully?
EDIT
Adding Fiddles:
Fiddle with two columns stream and messy dates
Fiddle with just one column stream and ok dates
I'm calling this kind of graph:
linePlusBarChart()
The problem with the dates is that your data contains timestamps (i.e. in seconds), but Javascript expects milliseconds. This is easily fixed by multiplying the values by 1000:
series.values = series.values.map(function (d) {
return {
x: d[0]*1000,
y: d[1]
}
});
The tooltip problem is actually a bug in NVD3 -- it's not meant to be used this way. The problem boils down to the mouseover handler assuming that the first item of the data is representative of what you want. You can fix this for your case by selecting the item by data point number modulo 2 (because there're two bars):
.on('mouseover', function(d,i) {
d3.select(this).classed('hover', true);
dispatch.elementMouseover({
point: d,
series: data[i%2],
pos: [x(getX(d,i)), y(getY(d,i))],
pointIndex: i,
seriesIndex: i%2,
e: d3.event
});
})
This will only work for exactly two bar series though. Updated jsfiddle with the modified NVD3 code here.

Resources