How to save a figure/plot after annotating?

20 views (last 30 days)
Hi, i have been trying to google whole day a very simple answer to a basic question but unfortunately i cannot find the answer. My query is very simple. I have a plot and using the tool menu in the figure display window, i drew a rectangle on my plot marking an area of interest. The problem is if i use save as command from file menu, i cannot control the resolution of the saved plot. I tried to use the code generator for the box created and added the line of the code in the plot code and tried saving the figure using print and export_ fig commands but both times, the location was different as compared to the orignal postion in the maxized window.
How can i save the annotated plot in eps format with having the desired resolution.1
for annotation i used this code . In the attachment one can see how the blue box moves after saving it using a command.
annotation(f_stacked,'rectangle',[0.317276041666667 0.508639308855292 0.0172291666666667 0.390928725701941],...
'Color',[0 0 1],...
'LineWidth',3);
set (f_stacked, 'Clipping', 'on');% experimented with it, did not worked
  4 Comments
William Rose
William Rose on 10 Jul 2023
@arjun luther, when the two previous commenters say stuff, I pay attention because they always give excellent advice.
dpb
dpb on 11 Jul 2023
@William Rose Agree, the stackedplot is not a regular axes into which one plots; I wouldn't be surprised if there are still warts with it that may not show on a regular axes as @arjun luther used for illustration.
@Adam Danz is good, although the link he posted uses only axes figures, not any that are a <standalone visualization object>. With only the laptop at the moment and a pressing deadline, I don't have time to try to explore much at this instant, sorry...

Sign in to comment.

Accepted Answer

Adam Danz
Adam Danz on 11 Jul 2023
Edited: Adam Danz on 12 Jul 2023
Thanks @William Rose for the kind words!
I had time to dig deeper.
Replicating the problem
Start with a default-sized figure. Create a stacked plot and an annotation rectangle placed at x=[1,3]. Note that I'm using the stackedplot position to compute the annotation rectangle position. Don't use undocumented methods to access the axes here.
fig = figure('Position', get(groot,'factoryFigurePosition'));
data = rand(10,5).*[ones(3,1);8*ones(7,1)];
dh = stackedplot(data);
hEdgeData = [1,3]; % horizontal edges of frame in data units
hEdgeNorm = dh.Position(1) + (hEdgeData-min(xlim(dh)))/range(xlim(dh))*dh.Position(3);
hWidth = diff(hEdgeNorm);
ah = annotation('rectangle',[hEdgeNorm(1),dh.Position(2),hWidth,dh.Position(4)],...
'Color', 'r', 'LineWidth', 2, 'LineStyle', '--');
After the stackedplot is rendered, change the figure size to half the width and height
fig = gcf;
fig.Position(3:4) = fig.Position(3:4)./2;
Explanation of the problem
The annotation rectangle is in normalized figure units. When the stackedplot size decreases, its position changes so the annotation rectangle is no longer in the intended position over the stackedplot.
Solution 1: Position constraint
An easy solution is to set the stackedplot's PositionConstraint to innerposition but this comes with a caveat. When the PositionConstrain is set to innerposition, the y axis labels may extend beyond the figure frame when the figure size is reduced.
dh = stackedplot(data);
dh.PositionConstraint = 'innerposition';
This is the result of the problem set above.
Solution 2: Add a listener that updates annotation rectangle upon position change
Whenever the stackedplot OuterPosition is changed, the listener will update the annotation rectangle. Set the figure to the desired size before exporting.
See inline comments for further explanation.
% Create stacked plot
fig = figure;
data = rand(10,5).*[ones(3,1);8*ones(7,1)];
stackHandle = stackedplot(data);
% Add the annotation rectangle but it will be all 0s for now.
annotHandle = annotation('rectangle',zeros(1,4),...
'Color', 'r', 'LineWidth', 2, 'LineStyle', '--');
% Add the listener
fig.UserData.PositionListener = addlistener(stackHandle,'OuterPositionChanged',@(h,evt)updateAnnotationPosition(h,evt,annotHandle));
% Call the listener function to initialize the annotation rectangle
updateAnnotationPosition(stackHandle,[],annotHandle)
function updateAnnotationPosition(stackHandle,~,annotationHandle)
% Update annotation rectangle position when stackedplot position changes.
hEdgeData = [1,3]; % horizontal edges of frame in data units
hEdgeNorm = stackHandle.Position(1) + (hEdgeData-min(xlim(stackHandle)))/range(xlim(stackHandle))*stackHandle.Position(3);
hWidth = diff(hEdgeNorm);
annotationHandle.Position = [hEdgeNorm(1),stackHandle.Position(2),hWidth,stackHandle.Position(4)];
end
Final result:
  4 Comments
Adam Danz
Adam Danz on 11 Jul 2023
Edited: Adam Danz on 11 Jul 2023
Thanks @dpb, your feedback is much appreciated!
Glad to hear you're unstuck, @arjun luther!
As for the final export step, I recommend following @William Rose's advice below. First set the figure to the desired size.
dpb
dpb on 12 Jul 2023
Edited: dpb on 12 Jul 2023
I dunno, but somehow it seems to me that having to write a user callback for something like this just doesn't seem as though the pieces work together natively as they should -- it just seems naturally expected that when one places an annotation on a figure that it should be part of the figure and behave natively the way the callback does.
That the user has to do this level of customization to get such a feature in MATLAB continues to be a real downside in the use of MATLAB for graphics as soon as one gets away from just the canned plots; even adding such seemingly trivial tidbits as this adds up to a lot of wasted time in a hurry; exactly what the system is supposed to be there to prevent.
That it can be done is a plus, but that it takes such a level of familiarity with coding to do so is ... well, "just plain rude!" to quote one of my granddaughter's favorite expressions...

Sign in to comment.

More Answers (1)

William Rose
William Rose on 10 Jul 2023
plot(0:10,0:10,'-rx')
ax=gca;
exportgraphics(ax,'MyFig300.eps','Resolution',300);
After issuing the plot command above, I added a rectangle using Insert -> Rectangle in the figure window. The saved eps file does include the rectangle.
Good luck.
  4 Comments
William Rose
William Rose on 11 Jul 2023
Edited: William Rose on 11 Jul 2023
[edit: correct spelling]
@dpb says rectangle() uses the units of the quantities being plotted. That is good to know, since it is a lot more convenient to use plot units.
Your sample plot has a rectangle that crosses subplots. This is probably not possible with rectangle(), but you can make such a rectange with annotate().
I have not used stackedplot() much. It works somewhat differently than regular plots, as @dpb noted. If the problem persists, and you really are bothered by it, you could investigate using a loop with subplot(9,1,i) to make 9 subplots. But you will get an x-axis under every plot, which is ugly, unnecessary, and a waste of space. I don't know if the rectangle placement in the eps file would work any better with subplots than it does with stackedplot().
% datetime vector: 8:00, 8:01,...,10:59,11:00, similar to your plot
D=datetime(2023,7,10,8,0:180,0);
t=convertTo(D,'excel');
x=cos(48*pi*t')*ones(1,9);
stackedplot(D,x)
annotation('rectangle',[.35 .47 .04 .46],'Color',[0 1 0],'Linewidth',3);
exportgraphics(gca,'MyFig300.eps','Resolution',300)
The code makes a figure similar to yours: nine stacked plots, plus a rectangle that spans 5 plots. It saves the figure as an eps file with specified resolution. The rectangle in the eps file (atached) looks like it is in the same place as in the Matlab figure.
dpb
dpb on 11 Jul 2023
@William Rose, the point about annotation being able to cross axes boundaries is a good one; hadn't really thought about that the stackedplot is multiple axes even though don't show..

Sign in to comment.

Products


Release

R2023a

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!