<b>NB: As of R2014b this functionality is built into MATLAB axes using the 'XTickLabelRotation' property.</b>
This function rotates the x-tick labels on a plot. An arbitrary angle can be specified for the text and the label justification adjusts appropriately to ensure the labels lie below the ticks. The axes font properties are preserved in the labels, which will update in response to font changes. Changes to the axes limits are also handled.
* Arbitrary angle of rotation
* Justification adjusts appropriately for the specified angle
* Axes font property changes are reflected
* Labels move correctly when zooming and panning
As with other functions that achieve the same result, this works by replacing the xtick labels with text objects. Handles to the created text objects are returned for you to tinker with.
Thanks a lot!
Excellent codes, works very well.
Works good ! thank's
Thanks a lot!!!
I had to change the addListeners() function to the following to get it to work in R2015a
function addListeners( ax )
% Create listeners. We store the array of listeners in the axes to make
% sure that they have the same life-span as the axes they are listening to.
axh = handle( ax );
listeners = [
addlistener( axh, findprop( axh, 'FontName' ), 'PostSet', @onAxesFontChanged )
addlistener( axh, findprop( axh, 'FontSize' ), 'PostSet', @onAxesFontChanged )
addlistener( axh, findprop( axh, 'FontWeight' ), 'PostSet', @onAxesFontChanged )
addlistener( axh, findprop( axh, 'FontAngle' ), 'PostSet', @onAxesFontChanged )
addlistener( axh, findprop( axh, 'FontUnits' ), 'PostSet', @onAxesFontChanged )
addlistener( axh, findprop( axh, 'OuterPosition' ), 'PostSet', @onAxesPositionChanged )
addlistener( axh, findprop( axh, 'XLim' ), 'PostSet', @onAxesLimitsChanged )
addlistener( axh, findprop( axh, 'YLim' ), 'PostSet', @onAxesLimitsChanged )
addlistener( axh, findprop( axh, 'XAxisLocation' ), 'PostSet', @onXAxisLocationChanged )
setappdata( ax, 'RotateXLabelsListeners', listeners );
end % addListeners
Excellent ! Works as promised. No fuss.
Awesome. This code perfectly works. Here you get your five stars. =)
Cool, works as it should (tested in <=R2013b).
Big Pro: you can specify the handle to the plot/subplot whose labels shall be rotated, and don't need to set this axes active for automatic determinations.
I met the same problem with 2014b and solved it like this:
Add at beginning to rotatexlabels.m
% execute code for R2014b or higher
% execute code for R2014a or earlier
(put this end just before the first function [maxStringLength] = parseInputs( varargin ).
I am working with Matlab14b and your function shows the following error:
No constructor 'handle.listener' with matching signature found.
Error in rotateXLabels/addListeners (line 390)
listeners = [
Error in rotateXLabels/repositionAxes (line 214)
addListeners( ax );
Error in rotateXLabels (line 37)
repositionAxes( ax );
When I was working with Matlab 14a it was operating correctly, could you please recommend me ways to solve this problem?
very simple to deploy and producing the desired result !
Hey Ben, awesome job with this one! I was just in need of something like this. Just one detail... When I run your code everything goes well except for the plot title which lowers slightly. Any way of fixing this?
Ok I've found out the reason: removing the function repositionXLabel makes the labels to go outside the plot when zomming in.
many thanks for your amazing script. I have one question for you: if I profile your function the most of the execution time (about 66%) comes from "repositionXLabel", ( in particular from "ext = get( textLabels(ll), 'Extent' )" ). I do not understand what this function is used for (line 49) also because it works even if i comment that out. I'm sure the funcion is there for a reason, could you please give me some further info?
This was very helpful. I tried various user-created functions to rotate text tick labels (cell array of strings) for the x-axis tick label. This is the only one I found that can rotate text tick labels on subplots without changing the subplots' sizes. Thanks.
Got an issue when YDir is reversed.
Here is the fix, starting at line 137:
reverse = xor(strcmpi( get( ax, 'XAxisLocation' ), 'Top' ),strcmpi( get( ax, 'YDir' ), 'Reverse' ))
y = ylim(2);
y = ylim(1);
Fulfill my need. Works for subplot, too. Thanks,
Great! But not working with the pre-release of 2014b.
very useful, thanks
Having problems with this. consider
set(gca, 'xlim', [0,15]);
The labels do not update correctly. 13,14,&15 don't show. Adding 'xtick',[0:15] to the set command didn't help.
Using matlab 7.12.0 on an xp box.
Follow-on to the discussion about imagesc
Had some trouble (position of labels) using this with imagesc. Solved it modifying the following
line 139: y = ylim(1);
line 141: y = ylim(2);
line 357: pos = [0 ylim(2)];
line 359: pos = [0 ylim(1)];
Still a remaining problem. If colorbar is visible the figure sizing is not correct.
Not sure how to fix this part.
Very useful! Some errors when saving a figure and reopening them (it looks like the old axes is trying to be modified).
things are not right if one set the axis reverse as:
Pretty good and handy tool to display long strings as X tick label!
Idea is great and it works for some plots. I had issues with plots being incorrectly resized that required code changes. I also had some blots for which this function placed tick labels inside the plot area. So it needs more debugging and possibly a tutorial page with examples.
I've pinpointed the issue with the "Index exceeds matrix dimensions." error and it's a bug somewhere else.
It has to do with "hold on" somehow adding 2 new tick marks to the figure.
Here is how to reproduce it on 2012a:
dat = 1:5;
fh = figure;
If you would remove "hold on", no error.
Thanks for sharing! Do you have any ideas on applying this to the Y-axis ?
Very nice function!!
the imagesc() workaround would be to create a copy of the function e.g. 'rotateXLabelsIMGSC()' and change
'y = ylim(2);' to 'y = ylim(1);' in line 124
'y = ylim(1);' to 'y = ylim(2);' in line 126
..would be nicer to catch the imagesc()-case by an 'if' but I am not sure if there is a handle/handle-combination to do it...
Hi Fernando & Martin, could one of you post (or email me) an example? When I try the obvious:
it all looks fine. Also, which MATLAB version, just in case it matters?
Thanks Matt, that's an interesting bug you've found.
I think we can do something slightly better and remove everything to do with rotated labels when the axes are cleared. I'll post an update that does this for you to try. Hopefully it won't break any existing functionality.
Hey Ben. Very useful function indeed! I may have found an odd issue that occurs if you run cla on the axis that contains rotated x-axis labels. In that case, the next time you trigger one of the listeners that your function sets up, an error is thrown because all of the properties it expects to find have been cleared. I hacked a quick fix by checking inside of each of the listener callbacks to check if the particular axis property is empty. If so, I remove the listeners. Perhaps you have a better suggestion? Thanks again for the function.
Thanks!! Just what I needed.
great! eg rotateXLabels(gca, 40);
I have the same problem of Martin
Love the function, but have one problem: when I use it with imagesc() the labels end up on the top of the graph and inside the face of the graph. How can I move them to be outside?
Thanks for the sweet function. However, I'm also getting an "Index exceeds matrix dimensions" error with R2012a, and I isolated what's causing the problem for me at least (although I have no idea why). The code below simulates the scenario I'm working on. When I set 'YGrid' to 'on', I get the error that follows the code. No error when I leave out a 'YGrid' specification. (Note that the code seems to execute just fine despite the error, so it's not a huge deal.)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% Create fake data
%%% 14 groups of 4 percentages, each group adding to 100
data = zeros(14,4) ;
data(:,1) = randi(100,14,1) ;
for y = 2:3
for x = 1:14
if sum(data(x,:)) == 100
data(x,y) = randi(100-sum(data(x,:)),1,1) ;
for x = 1:14
data(x,4) = 100 - sum(data(x,:)) ;
% Make grouped bar graph
xlabels = char('Grp01','Grp02','Grp03','Grp04','Grp05','Grp06','Grp07','Grp08','Grp09','Grp10','Grp11','Grp12','Grp13','Grp14') ;
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Here's the error message:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Index exceeds matrix dimensions.
Error in rotateXLabels/getLabelHeight (line 226)
oldUnits = get( textLabels(1), 'Units' );
Error in rotateXLabels/repositionXLabel (line 204)
labelHeight = getLabelHeight(ax);
Error in rotateXLabels/onAxesLimitsChanged (line 353)
repositionXLabel( ax );
Warning: Error occurred while evaluating listener callback.
> In graphics/private/clo at 83
In cla at 29
In newplot>ObserveAxesNextPlot at 125
In newplot at 74
In bar at 76
Hi Vaibhav, any chance you could post a version of the code that doesn't require a data-file? You can use RAND etc to generate some fake data. The important thing is that the range of the data (both in x and y) is similar to the data that you are having trouble with.
Thank you for your function. It works absolutely perfect but I am facing a problem when converting the x ticks to mm/dd hh:mm format using datetick command. The eroor message arises specifically when I increase the number of x ticks beyond a certain limit (more than 8 in this case) using linspace command.
Please look at the following code. I have tried to explain its steps in the commented lines. In the end I am pasting the error message I get on running. I would like to know your views on it. Thanks again!
% textdata is a <168x1 double> array with values in 'dd/mm hh:mm:ss' format
% Converts mm/dd hh:mm to numeric values
% data is a <168x1 double> array with numeric format
% linspace('first value of DateTime array', 'last value of DateTime array',
% 'number of XTicks in between')
??? Index exceeds matrix dimensions.
Error in ==> rotateXLabels>createNewLabels at 132
textLabels(ll) = text( ...
Error in ==> rotateXLabels at 35
h = createNewLabels( ax, vals, labels, angle );
Error in ==> HourlyTempPlot at 29
- Vaibhav Jain
Worked perfectly in 2 minutes.
A great and user-friendly file.
Some warnings about latex markups while using no interpreter but it's harmless so far
Thanks for sharing!
OK, I've submitted an update that will hopefully fix the printing issues. Please keep a backup of the old version in case this change breaks something else (it passes all my tests, but they probably don't cover all uses). The update should appear here in the next few days. When it does, let me know if it helped.
Hi Johan, I can see the problem. I am using "normalized" coordinates for the text used as the labels. It seems that with the units set to normalized they don't print, but if I set them to something else they do. I have no idea why this would be.
I will see if I can re-arrange the code to use something other than normalized units without breaking something else (zooming, panning, titles, etc).
On screen everything looks fine. However, when I try to print, the tick labels disappear, same as for Wolfgang. I'm running MATLAB 2011b (220.127.116.114) on Windows 7 (x64 version). All plot settings are at the default.
Other than that, great function!
Here's the test script that used:
x = 1:0.2:2.8;
y = x.^2;
xlab = get(gca,'XTickLabel');
xlab = num2str(xlab);
xlabel('X axis label');
ylabel('Y axis label');
set(gcf, 'PaperPositionMode', 'auto');
yes I am using an old version of Matlab - thanks for the suggestion. Great script btw!
Hi Chris, are you by any chance using a version of MATLAB older than R2010a?
If so, you may still be able to get it to work by replacing the '~' with a real variable name, but bear in mind this hasn't been developed or tested on older releases.
I get the following error:
>> rotateXLabels( gca(), 22 )
??? Error: File: rotateXLabels.m Line: 310 Column: 33
Unexpected MATLAB operator.
line 310 is function onAxesFontChanged( ~, ~ )
it will work if I commnet out the functions having ( ~, ~ )
any thoughts why I have this error?
Hi Wolfgang, I've just tried this using the example in the help and printing using the lines you provided and the labels appear OK. Could you provide a more detailed example that shows the problem? There may be some setting of your figure that makes a difference.
This works fine with me, but the x-Axis labels are not printed into a png-file via
set(gcf, 'PaperPositionMode', 'auto');
Can you reproduce this, and do you have a solution.
First rotate script I came across that works without issues (yet ;) ) on subplots.
Hi Ben and Miranda:
The program is cool, especially comparing to other rotation programs which may not adjust the text object position well. Also the code style is clear.
For my previous request of setting the axis to the top, I just manually set the y to a proper value in creatNewLabels(...) and it works.
Thank you for this nice program. One disadvantage is that the labels are very close to the x-axes, especially if I choose an angle of -90 or 90. I thought of including an extra space in the labels:
if 0 <= angle && angle < 180
labels = cellfun(@(x) horzcat(x,' '),labels,'UniformOutput', false);
labels = cellfun(@(x) horzcat(' ',x),labels,'UniformOutput', false);
Maybe an extra if-expression is needed for the labels on XAxisLocation top...
Hi Denzel, my first attempt at getting this to work is on its way through the approval process now. You should see it arrive on here in the next day or two.
Thanks for your quick reply. Having xticklabels on top and yticklabels on right are especially useful when displaying heatmap with labels. Please keep me updated. Thanks.
this hack works by replacing the tick labels with text objects and removing the existing tick labels. This means that setting properties of the tick labels (fonts, position etc) will no longer have any effect. I'll have a look and see if I can make them honour the "XAxisLocation" property, but I suspect that even then you will need to set it before rotating the labels. Watch this space...
Thanks for the great tool. However it only produces xticklabels which is at the bottom and not on the top. That is, even with the function call:
set(gca, 'XAxisLocation', 'top');
the xticklabels are still at the bottom. Please note that get(gca,XAxisLocation') returns 'top'.
Please let me know if anyone has any suggestion. Thanks.
Thanks, the positioning now works great.
Hi Dan. I'm hoping this latest update will have fixed your problem. Please let me know if it doesn't.
I appreciate you looking into this!
Hi Dan. Having done some testing, it looks like text object positions are only stored to the nearest pixel. This means that when you call rotateXLabels, any labels that are drawn at the same pixel location will store their position as the same value. This seems to be a limitation of MATLAB's normalised position units or at least a restriction imposed when you switch to them. I will see if it's possible to achieve the same label behavior without using normalised coordinates as this may fix your problem. Watch this space...
This works beautifully, but when it is used with a plot that has a very large number of x tick labels (1000 for example) it does not position them correctly.
Initially of course in this case, they labels are all overlapping, but after zooming it can be seen that they do not position equally and some are directly on top of each other.
Does anyone have any suggestions?
Some function arguments missing? (line 256 col 33)
* On MATLAB R2014b, just use XTickLabelRotation instead
Improve behavior when "cla" is called on the axes.
Switch to using data coordinates internally to allow printing to work. Please keep the old version in case this breaks something else!
Should now honour the "XAxisLocation" property of the axes.
Switch to using normalized units for labels to prevent loss of accuracy with hundreds/thousands of labels.
Inspired by: Rotate XTick Labels for multi-axes