Say I have a graph where the x-axis tick labels are very long strings, and so I want to alternate the tick padding (the vertical distance between the text and the x-axis) so that the tick labels don't overlap.
I know this can be achieved post-rendering by selecting the tick elements and applying a transform attribute. But I'd like to do:
var xAxis = d3.svg.axis()
.scale(x0)
.orient("bottom")
.tickSize(0)
.tickPadding(function(i) {
// some logic here to determine the alternating-height strategy by index, e.g.
return i % 2 ? 20 : 30;
});
This doesn't work in d3 as-is -- the documentation (https://github.com/mbostock/d3/wiki/SVG-Axes#wiki-tickPadding) says that tickPadding just takes the number of pixels. Is there a better way to do this? Monkey patch d3's axis.tickPadding function to take a function or a number, then apply the function when drawing the ticks?
For the source-code patch, see:
https://github.com/adonomay/d3/commit/bfdb36fa17806666775c6804b86eb10bea3b3393
An alternate fix is to wrap the text manually after rendering:
https://github.com/mbostock/d3/issues/1641
http://bl.ocks.org/mbostock/7555321
I also was hoping to do this. I was using week numbers and it got a little cramped. Basically the x axis looked like
111213141516171819
and I was hoping to stack them. I ended up doing this
$('.tick text').each(function(i){
var yval = this.getAttribute("y");
if( i % 2 == 0 )this.setAttribute("y",parseInt(yval)+10);
});
Once the graph was drawn. I made sure to include some extra margin (10) on the bottom as well. The result was
12 14 16 18
11 13 15 17 19
The only choice you have is to modify the source. D3 assumes throughout the axis implementation that tickPadding is a number, not a function -- see the source.
Related
I am using NVD3 (a wrapper of d3) to draw a line graph. I want the data in the graph to be within the range of the axis. However, it looks inconsistent with the other labels, as the chart displays the max value of my data set on its own. See screenshot:
In this exanple, 18,554.41 is my highest data point. What I would like to see is the ticks/axis-lables to be in the same order of rounding throughout, with no overflow. i.e. 20,000,18000,16000 etc.
The caveat is that my dataset can vary quite differently - so I can't just set a max. Is there a way of just increasing the tick count by one or something?
Current relevant code:
var chart = nv.models.lineChart()
.showYAxis(true)
.forceY([0]);
chart.yAxis
.axisLabel('£')
.tickFormat(d3.format(','))
.ticks(8);
EDIT: added https://jsfiddle.net/60equ79h/2/
on the fiddle, I would like the first data set's topmost label to be 10,000. the second would be 80. i.e I would like to the y-axis to be increased by one tick
If I understand your question correctly, you can force the yAxis to have the values you define. You can hard code the values or write something clever to identify the min & max for your yAxis.
Update your chart to have the following:
chart.forceY([0, 20000]); // [min, max]
Hope it helps
I'm trying to recreate the following in D3
And I've got the following so far: http://codepen.io/jpezninjo/pen/dNwmVK
I looked it up and the best I could find was this answer: Show every other tick label on d3 time axis?, but I'm not using a class to create each column label. I think the following two lines are the ones that control my labels, but I'm not sure how to go about this.
var x = d3.scaleBand()
.range([0, width])
.padding(0.1);
x.domain(data.map(function(d) { return d.key; }));
I'm also trying to figure out how I can put some padding on the left and right of the bars
At least two possible ways:
Make your X axis a time axis, and use d3.timeDay.every(2) to specify every 2nd day. That approach is shown here: http://codepen.io/anon/pen/YNdaRB.
Key part: var axisBottom = d3.axisBottom(x).ticks(d3.timeDay).tickArguments([d3.timeDay.every(2)]);.
To make this work, I also had to (a) make d.key equal to the result from parseDate instead of the formatted date string, (b) hard-code a width for the bars instead of using x.bandwidth(), and (c) translate the x axis by width/2 px to center it under the bars (line 94). Might be nicer ways to do (b) and (c) but I mainly wanted to show d3.timeDay.every(2)'s ability (docs).
Use your current approach but apply a style to every 2nd tick. d3 adds some classes automatically so selecting the ticks is easy. This is what they described in the post you linked to. http://codepen.io/anon/pen/qRLogy?editors=1010
Key part: d3.selectAll(".tick text").style("display", function (d, i) { return i % 2 ? "none" : "initial" })
I'm using Highstock (v4.2.3) to present data in a StockChart with a number of different Y axes, all plotted against time on the X axis. The data has gaps in it, and I'd like to depict those gaps, but when I turn on gapSize (with any value other than zero), there's a weird quirk that causes line rendering issues--when using the navigator to zoom in on certain date ranges (not all), in some cases (whose pattern I've yet to discern) the chart fails to fully render the line across the entire x axis.
This annotated screenshot depicts the issue.
When I turn gapSize off (or explicitly set it to zero), this problem goes away. Note that the gaps themselves appear correctly on the chart (when navigating to a date range that doesn't present the line rendering issue).
plotOptions: {
series: {gapSize:2}
}
Any ideas?
jsFiddle with your issue:
http://jsfiddle.net/2N52H/109/
As you can read in our API:
http://api.highcharts.com/highstock#plotOptions.line.gapSize
A gap size of 5 means that if the distance between two points is
greater than five times that of the two closest points, the graph will
be broken
As far as I know data you have has random gaps so you will never know what is the distance between two closest points. For example if you will have data in every one hour, distance between two closest points will be 15 minutes and your gapSize will be set to 2, you will see only your closest points.
When you are using zoom sometimes your visible data closest distance is changing so the gaps are changing as well.
See this example:
http://jsfiddle.net/2N52H/111/
Maybe you can use xAxis.ordinal parameter to visualise your gaps:
http://api.highcharts.com/highstock#xAxis.ordinal
You can also change standard functionallity by using wrapper. Here you can read about it:
http://www.highcharts.com/docs/extending-highcharts/extending-highcharts
For example you can change gappedPath function:
(function(H) {
H.wrap(H.Series.prototype, 'gappedPath', function(proceed) {
var gapSize = this.options.gapSize,
xAxis = this.xAxis,
points = this.points.slice(),
i = points.length - 1;
if (gapSize && i > 0) { // #5008
// extension for ordinal breaks
while (i--) {
if (points[i + 1].x - points[i].x > gapSize) {
points.splice( // insert after this one
i + 1,
0, {
isNull: true
}
);
}
}
}
return this.getGraphPath(points);
})
}(Highcharts))
example:
http://jsfiddle.net/2N52H/113/
Kind regards.
I am currently working with NVD3 using Angular Directive (angular-nvd3). I have a very simple line chart with very simple data.
The problem I have encountered now is that my data is wrongly aligned with the Axis. Example plunker available here: http://plnkr.co/edit/jWEYt6?p=preview ,
I am using dates on my xAxis, which are parsed using d3 library:
tickFormat: function(d) {return d3.time.format('%d/%m')(new Date(d))}
Description:
I would expect the xAxis labels to be correspondent to the grid.
In the example you can clearly notice that the xAxis is not evenly devided (values: 06/11, 08/11, 11/11, 13/11). So usually 2 days and sometimes 3 days :)
What is worse - the peaks are not matching the grid. Example: 06/11 tick is really not even close to the grid's line where I guess it is supposed to be.
I have also tried this on master's code from repo and it happens there too. There is a link in the HTML head section.
Is there a problem with my data, proper date formatting or something else? Thanks!
This bugged me for a while and I could not find an answer here. I even have opened a bug on GitHub: https://github.com/novus/nvd3/issues/1382#issuecomment-160694559 and I was clued in on the answer.
The problem:
The actual issue is hidden because of d3.time.format('%d/%m'). My example data is given in one tick per day manner, and the format was set accordingly. But d3 does not understand that. When drawing the grid it divides the max-min/someValue and the grid ticks does not have to occur on full day (midnight), but on any hour. And because of the formatting I could not see that.
The version showing this misconception is here: http://plnkr.co/edit/2iMHOp?p=preview
Solution:
So now, when I know what I could do, I managed to substitute the ticks by using tickValues parameter in nvd3 / angular wrapper.
The version with the solution is here:
http://plnkr.co/edit/23n3ll?p=preview
Yet another bug :)
Funny thing is that since the labels are too long to be displayed, I had to rotate them so they could fit. Another bug occurs here (I think). As you can see 2nd and last but one tick label is missing. First I tried using the solution mentioned here: NVD3 Line Chart X Axis Ticks Are Missing using the showMaxMin parameter but it does not work correctly. But if you rotate the labels to ~ -70 degrees the labels are displayed OK.
I guess this is not the end with my NVD3 journey ;)
Since the problem is, according to Atais:
The actual issue is hidden because of d3.time.format('%d/%m'). My example data is given in one tick per day manner, and the format was set accordingly. But d3 does not understand that. When drawing the grid it divides the max-min/someValue and the grid ticks does not have to occur on full day (midnight), but on any hour. And because of the formatting I could not see that.
I managed to pass the x's values as integer values (ex: 20160211) instead of formatted dates (ex: 2016-02-11 or similars) to nvd3, and then on tickFormatformat them to display properly.
I wrote another plunker with the problem and the commented solution (used momentjs):
Plunker with the simulated error: http://plnkr.co/edit/fXDQ0f?p=preview
Data is provided in format x: milliseconds, y: int, like {x: 1446418800000, y: 20}, and it is being formated with tickFormat:
xAxis: {
tickFormat: function(d) {
return moment(d).format('YYYY-MM-DD');
}
}
Plunker with the solution: http://plnkr.co/edit/KpALzo?p=preview
Data is provided in format x: int, y: int, like {x: 20160211, y: 20}, and it is being formated with tickFormat:
xAxis: {
tickFormat: function(d) {
moment(d, 'YYYYMMDD').format('YYYY-MM-DD');
}
}
Note that you can do it with time too, just by appending to the 'numeric date'.
As stated from #ajaybc, will not work well with dates from different months, since d3 will interpolate X axis with invalid filling dates (days 32, 33, 34 and so on)
I have created a Plunkr for my issue, found at the link
I am having a problem with scaling when using month values. I am sure this is a simple fix.
A visualization of my issue is below (look at December):
In short, when using d3.extent in an update pattern for the axis and the bars, d3 is not computing the domain correctly. It seems to think it is less than it is and so I have one entry too many, leading one bar to hang off and the x axis to be one value too long.
My xScale is computed normally:
xScale = d3.time.scale()
.domain(d3.extent(data,function(d){return d.funded_month}))
.rangeRound([0, (w + offsetBar) - (marginleft + marginright)]);
Both the x Axis, bars and text are all working off the same domain.
When visualizing all years, the viz appears correctly:
Is the issue that the number of values in each respective visualization are different (13 years vs 12 months? What is the mistake?
I was not able to implement an easy solution with existing libraries.
Because D3 interprets the "Month, Year" format assuming the day is the first of the month at midnight, January to December is considered only 11 months.
So I stepped the days of every month to create a smooth axis:
2001-01-01,68991364
2001-02-04,554541108
2001-03-07,151123291
2001-04-10,283000000
2001-05-12,8093737
2001-06-15,55718802
2001-07-18,95060100
2001-08-21,78000000
2001-09-23,150000000
2001-10-25,193330000
2001-11-28,59948530
2001-12-31,142424927
I was able to use an x translate on my axis text to move in sync with the bars.
Mike Bostock goes further into this issue here.