How to add an independent text in MATLAB plot legend - matlab

I need an additional text in the legend that is not related with graphical data together with the legend captions.
Something like this (it was made in OriginLab):
Following to this link Add custom legend without any relation to the graph
I can add some text using plot(NaN,NaN,'display','add info here2', 'linestyle', 'none'). But there is an indent in this text:
How to avoid this indent? And is there a more elegant method to add the text that is not associated with the legend together with the legend captions?

The legend function will return as its second output argument handles for all of the components that make up the symbols and text in the legend. You can therefore plot "dummy" lines as placeholders in the legend, reorder the handles when creating the legend to put the text where you want it, and modify the legend objects accordingly. Here's an example:
x = linspace(0, 2*pi, 100);
hl = plot(x, [sin(x); cos(x); tan(x); nan(size(x))].'); % Add a line of NaNs
axis([0 2*pi -4 4]);
[~, objH] = legend(hl([1 2 4 3]), 'sin', 'cos', 'junk', 'tan'); % Reorder handles
set(findobj(objH, 'Tag', 'junk'), 'Vis', 'off'); % Make "junk" lines invisible
pos = get(objH(3), 'Pos'); % Get text box position
set(objH(3), 'Pos', [0.1 pos(2:3)], 'String', 'also...'); % Stretch box and change text

You can use annotations. It's not perfect, but with few adjustments you'll get what you want. Here's an example:
% somthing to plot:
x = [0:0.1:5; 5:0.1:10].';
y = sin(x);
% plot the real data:
plot(x,y);
hold on
% make some space in the legend:
Spacing_lines = 3;
h = plot(nan(size(x,1),Spacing_lines));
hold off
set(h,{'Color'},{'w'}); % clear the dummy lines
% place the legend:
hl = legend([{'lable1','lable2'} repmat({''},1,Spacing_lines)]);
% add your text:
annotation('textbox',hl.Position,'String',{'Some info';'in 2 lines'},...
'VerticalAlignment','Bottom','Edgecolor','none');
And from this you get:

You can just add any text to any point of plot in this way:
txt1 = 'some information';
text(x1,y1,txt1)
where x1, y1 - coordinates.
By the way function text function has a lot of different properties (colors, font size, alignment etc.).

I think the easiest way is to just create some dummy function, plot it but set the color="none" - that way it will only show up in the legend (if that is where you wanted it).

Related

Labeling plots such that label is aligned with the ylabel outside the axes

Please see the following code which creates a 2 by 2 subplot with some plots:
x = linspace(0,2*pi);
y = sin(x);
hfig = figure('Position',[1317 474 760 729]);
subplot(2,2,1)
plot(x,y)
ylabel('plot1');
subplot(2,2,2)
plot(x,y.^2)
ylabel('plot2');
subplot(2,2,3)
plot(x,y.^3)
ylabel('plot3');
subplot(2,2,4)
plot(x,abs(y))
ylabel('plot4');
in each one, I have added labels by hand in Tools: Edit plot (a) (b) (c) (d) producing this figure:
The problem is, if I resize the plot they are no longer aligned with the ylabel text:
Is there a way to add these labels programmatically and have them automatically align to the ylabel text? I am surprised MATLAB does not have something like this built in already.
Thanks
This is not something that is easy to do without attaching a listener to the figure resize event (see example), and doing some computations related to aspect ratios.
It's not entirely clear what sort of objects your labels are (text or annotation), so I'll just show how to do this programmatically using the text command, which creates labels in axes coordinates (as opposed to figure coordinates). This doesn't solve the problem entirely, but it looks better, possibly to an acceptable degree:
function q56624258
x = linspace(0,2*pi);
y = sin(x);
hF = figure('Position',[-1500 174 760 729]);
%% Create plots
[hAx,hYL] = deal(gobjects(4,1));
for ind1 = 1:3
hAx(ind1) = subplot(2,2,ind1, 'Parent' , hF);
plot(hAx(ind1), x,y.^ind1);
hYL(ind1) = ylabel("plot" + ind1);
end
hAx(4) = subplot(2,2,4);
plot(hAx(4), x,abs(y));
hYL(4) = ylabel('plot4');
%% Add texts (in data coordinates; x-position is copied from the y-label)
for ind1 = 1:4
text(hAx(ind1), hYL(ind1).Position(1), 1.1, ['(' char('a'+ind1-1) ')'], ...
'HorizontalAlignment', 'center');
end
Note several modifications to your code:
The handles returned by some functions that create graphical elements are now stored (mainly: hAx, hYL).
All functions that create graphical elements (subplot, plot, ylabel) now have the target (i.e. parent or container) specified.
I changed the 'Position' of the figure so that it works in my setup (you might want to change it back).

Can we add legend for function "text" to matlab?

I want to add legend for my plot. Because I want to use the marker plot 'heartsuit', I use the 'text' function. If I add legend function in my code, it can't work. The command window say that 'Warning: Plot empty.' So, can we add legend to 'text' function? I have searched in many source, and I cannot find it.
clear all;
clc;
m = '\heartsuit';
x = 0:pi/5:2*pi;
y = sin(x);
text(x,y,m,'fontname','Arial','color','red','FontSize',18,'HorizontalAlignment','center','VerticalAlignment','middle');
grid on;
xlim([min(x) max(x)])
ylim([min(y) max(y)])
legend('Solusi Numerik');
Here's a hack. Plot a fake NaN point, create a legend for it, hide its legend line, and add the heart-suit in the string with appropriate space at an appropriate position. Adjust the color of heart-suit and/or string if needed.
hold on;
LgdStr = 'Solusi Numerik'; %Your legend string
hNaN = plot(NaN,NaN); %Plotting nothing
[~, icons] = legend(hNaN, LgdStr);%Creating a legend to get required space for string
icons(2).Visible = 'off'; %Hiding the fake legend line
icons(1).Position(1) = 0.125; %Adjusting the starting position of text
icons(1).String = ['\color{red}', m, ' \color{black}',LgdStr];
%Last line includes red-colored heart-suit at reasonable space from black-colored text
Result:

How can I show only the legend in MATLAB

I want to show only the legend for a group of data in MATLAB.
The reason I want to do this is that I want to export the legend to .eps, but I only want the legend, not the plots.
Is there a way to turn off the plots and remove them from the figure, but still show just the legend centered?
This seems to do the trick:
plot(0,0,'k',0,0,'.r') %make dummy plot with the right linestyle
axis([10,11,10,11]) %move dummy points out of view
legend('black line','red dot')
axis off %hide axis
There is probably a lot of whitespace around the legend. You could try to resize the legend by hand, or save the plot and use some other program to set the bounding box of the eps.
The chosen solution by Marcin doesn't work anymore for R2016b because MATLAB's legend will automatically gray out invisible plots like this:
Neither turning off the automatic update of the legend nor changing the TextColor property afterwards fixes this. To see that, try Marcin's modified example:
clear all; close all;
figHandle = figure;
p1 = plot([1:10], [1:10], '+-');
hold on;
p2 = plot([1:10], [1:10]+2, 'o--');
legHandle = legend('text1', 'text2');
%turn off auto update
set(figHandle,'defaultLegendAutoUpdate','off');
set(p1, 'visible', 'off');
set(p2, 'visible', 'off');
set(gca, 'visible', 'off');
%set legend text color to black
legHandle.TextColor = [0 0 0];
The result remains the same. (To avoid throwing my laptop through the window) and fix this without zooming, which might leave fragments of the plot in the way, I wrote a function that fixes the legend and saves it to a file (with frame):
function saveLegendToImage(figHandle, legHandle, ...
fileName, fileType)
%make all contents in figure invisible
allLineHandles = findall(figHandle, 'type', 'line');
for i = 1:length(allLineHandles)
allLineHandles(i).XData = NaN; %ignore warnings
end
%make axes invisible
axis off
%move legend to lower left corner of figure window
legHandle.Units = 'pixels';
boxLineWidth = legHandle.LineWidth;
%save isn't accurate and would swallow part of the box without factors
legHandle.Position = [6 * boxLineWidth, 6 * boxLineWidth, ...
legHandle.Position(3), legHandle.Position(4)];
legLocPixels = legHandle.Position;
%make figure window fit legend
figHandle.Units = 'pixels';
figHandle.InnerPosition = [1, 1, legLocPixels(3) + 12 * boxLineWidth, ...
legLocPixels(4) + 12 * boxLineWidth];
%save legend
saveas(figHandle, [fileName, '.', fileType], fileType);
end
Tips for use:
fileType is a string that specifies a valid argument for saveas(), such as 'tif'.
Use it after you have plotted everything you want to appear in your legend, but without extra stuff. I'm not sure if all potential elements of a plot contain an XData member that isn't empty.
Add other types of displayed things that you want deleted, but are not of type line if there are any.
The resulting image will contain the legend, its box, and a little bit of space around it the legend is smaller than the minimum width (see Minimum Width of Figures in MATLAB under Windows). However, this is usually not the case.
Here is a complete example using the function from above:
clear all; close all;
fig = figure;
p1 = plot([1:10], [1:10], '+-');
hold on;
p2 = plot([1:10], [1:10]+2, 'o--');
legendHandle = legend('myPrettyGraph', 'evenMoreGraphs');
saveLegendToImage(fig, legendHandle, 'testImage', 'tif');
I think that you need to "hide" the elements you don't want in your plot, leaving out only the legend. For example,
clear all; close all;
figure;
p1 = plot([1:10], [1:10], '+-');
hold on;
p2 = plot([1:10], [1:10]+2, 'o--');
legend('text1', 'text2');
set(p1, 'visible', 'off');
set(p2, 'visible', 'off');
set(gca, 'visible', 'off');

Matlab: Overlapping subplot titles

I'm using subplot which contains three different plots. Each plot has its own labels and title.
The problem is that I have to maximize the plot when I save it. Otherwise, the texts will overlap each other.
When I maximize it, the subplot's label text will appear little blurry in the image, even if I use ESP format or any vector format.
How can I resolve this issue?
For the title overlap issues, you can produce multiple lines of title text use a cell array of strings as the input parameter of title():
title_text = {'first line', 'second line', 'third line'};
title(title_text);
And it works for label text too.
In addition to Da Kuang's answer, if you would like to keep your titles and labels on the same line, you could change the font size
a = axes;
t = title('My Really Long Title');
l = xlabel('My Really Long x label')
set(t, 'FontSize', 8)
set(l, 'FontSize', 8)
I'm not sure why your labels are blurry, but I can help with the overlap.
I never use subplot when I want to save images (eg. for a paper). What I do instead is create each axes individually, which allows a lot more control over each of them.
Below is a rather general example, which illustrates how to generate an arbitrary grid of axes with much finer control over their placement than subplot allows. Of course, with only 3 axes, you don't really need the loop, but I'm sure you can adapt this to fit your needs.
% first create the figure
figPos = [200 200 800 500];
figure('Color', 'w', 'Position', figPos)
% next, determine how much padding you want on each side of the axes, and in
% between axes. I usually play around with these, and the figure size until
% the layout looks correct.
leftPadding = 50/figPos(3); % the space at the left of the figure
rightPadding = 25/figPos(3); % the space at the right of the figure
horizPadding = 80/figPos(3); % the space between axes (horizontally)
topPadding = 30/figPos(4); % the space at the top of the figure
bottomPadding = 50/figPos(4); % the space at the bottom of the figure
vertPadding = 120/figPos(4); % the space between axes (vertically)
% set up the grid size
nHorizAxes = 2;
nVertAxes = 3;
% figure out how big each axes should be
horizPlotSpace = 1-leftPadding-rightPadding-(nHorizAxes-1)*horizPadding;
vertPlotSpace = 1-topPadding-bottomPadding-(nVertAxes-1)*vertPadding;
width = horizPlotSpace/nHorizAxes;
height = vertPlotSpace/nVertAxes;
myAxes = zeros(nVertAxes, nHorizAxes);
% create some sample data to plot for illustrative purposes
x = linspace(0, 2*pi);
y = sin(x);
for iRow = 1:nVertAxes
for iCol = 1:nHorizAxes
% calculate the position
left = leftPadding+(iCol-1)*(width+horizPadding);
bottom = bottomPadding+(iRow-1)*(height+vertPadding);
position = [left bottom width height];
myAxes(iRow, iCol) = axes('Position', position);
plot(x, y)
xlabel('Test Label')
ylabel('Test Label')
title(sprintf('axes(%d, %d)', iRow, iCol))
end
end
Those answers should help but here are some other things to try, depending on the cause of the overlapping text:
Change the figure's size so there's room for the text. For example:
set(gcf, 'PaperSize', [5 7])
Change the size of the subplots.
s = get(gca, 'Position');
set(gca, 'Position', [s(1), s(2), s(3), s(4) * 0.5])
MATLAB (R2021b) appears to stop updating the size of subplots after the axes function is used to set the current axes. The following code causes the title to be cut off.
sp1 = subplot(2, 1, 1);
sp2 = subplot(2, 1, 2);
axes(sp1) % Set the current axes to the first subplot.
title(sprintf('Hello\nCruel\nWorld'))
On the other hand, if title is called immediately after the first subplot is opened, without using axes, then the title has sufficient space to be completely visible.
sp1 = subplot(2, 1, 1);
title(sprintf('Hello\nCruel\nWorld'))
sp2 = subplot(2, 1, 2);
As a workaround, if you need to set the values for a prior subplot, you can simply pass sp1 as the first argument of the desired function.
sp1 = subplot(2, 1, 1);
sp2 = subplot(2, 1, 2);
title(sp1, sprintf('Hello\nCruel\nWorld'))
Simple you can use below function :
plt.tight_layout()
description: The tight_layout() function in pyplot module of matplotlib library is used to automatically adjust subplot parameters to give specified padding.
note : always use this function before plt.show() function.

Matlab - how to make a custom legend

I have the following picture :
And I would like to make a legend for it. Basically, I want to make a legend for each type of rectangle. In the legend box, I want to mark each color line according to the type of body which it marks:
green line : head
yellow line : torso
purple line : right arm
cyan line : left arm
red line : left leg
blue line : right leg
This is basically custom, because I have more rectangles of each type. How can I do a custom legend and attach it to the figure which draws this picture?
There are 2 ways you could go about this. You could create your squares and then assign them to an hggroup. This way you dont have multiple items for each color. Something like this:
hold on
for ii = 1:4
hb(ii) = plot(rand(1,2), rand(1,2),'color','r');
end
hg = hggroup;
set(hb,'Parent',hg)
set(hg,'Displayname','Legs')
legend(hg)
Or you could create dummy objects, like this:
hold on
for ii = 1:4
hb(ii) = plot(rand(1,2), rand(1,2),'color','r');
end
p = plot([],[],'r');
legend(p,'Legs')
The former is a little more elegant.
I would like to add to dvreed77's answer on using hggroup that for clean legend use, I also needed to set the 'IconDisplayStyle' (Matlab R2014a), such that:
%4 kinds of lines:
n_areas = 4;
n_lines = 10;
%use built-in color map
cmap = hsv(n_areas);
%plot lines and generate handle vectors
h_fig = figure;
hold on
h_lines = zeros(1,n_lines);
for l = 1:n_areas
for k = 1:n_lines
h_lines(k) = plot(rand(1,2), rand(1,2),'Color',cmap(l,:));
end
%Create hggroup and set 'icondistplaystyle' to on for legend
curPlotSet = hggroup;
set(h_lines,'Parent',curPlotSet);
set(get(get(curPlotSet,'Annotation'),'LegendInformation'),...
'IconDisplayStyle','on');
end
%Now manually define legend label
legend('heads','legs','hands','feet')
The simplest way I can think of is to first plot one rectangle of each type and construct a legend for only unique rectangles. Like so:
figure;
hold on;
% unique rectangles
plot(rand(1, 10), 'b');
plot(rand(1, 10), 'g');
% the rest
plot(rand(1, 10), 'b');
plot(rand(1, 10), 'g');
% use normal legend with only as many entries as there are unique rectangles
legend('Blue', 'Green');
You will have many lines of the same color, but a legend only for unique colors.
Just draw legend dots outside the plot:
figure;
plot(-1,-1,'gs',-1,-1,'b^',-1,-1,'ro');
legend('x1','x2','x3','Location','NorthWest');
xlim([0,1]); ylim([0,1]);
To control the appearance of legend entries, plot points that have values which are NaN then pass the objects returned by plot and an array of labels to the legend function (NaN points are not visible in the plot, but appear in the legend).
colors = ["red", "blue"];
labels = ["this is red", "this is blue"];
% We 'plot' a invisible dummy point (NaN values are not visible in plots),
% which provides the line and marker appearance for the corresponding legend entry.
p1 = plot(nan, nan, colors(1));
hold on
p2 = plot(nan, nan, colors(2));
% Plot the actual plots. You can change the order of the next two function calls
% without affecting the legend.
plot([0, 1], [0, 1], colors(1));
plot([0, 1], [1, 0], colors(2));
legend([p1, p2], labels)
If the plots in [p1, p2] are not in the current figure when legend([p1, p2], labels) is called, then it will raise the following error:
Invalid argument. Type 'help legend' for more information.
You can filter plots that are not in the current figure using something like this:
plots_in_figure = findall(gcf,'Type','Line');
plots_for_legend_indices = ismember([p1, p2], plots_in_figure);
plots_for_this_legend = this.plots_for_legend(plots_for_legend_indices);
legend(plots_for_this_legend, labels)