Place annotations using axis coordinates from a date-axis

11 views (last 30 days)
Hello there,
I have another problem... I want to place annotations in my plot, see the figure below.
Now, as one can see the x-axis uses datenum-numbers. How can I position the annotations using these coordinates? Each of the vertical lines should be placed at 00:00 (12PM), the dates in the center of the resulting box.
This is how far I got:
clc
clear all
close all
% Get the data
filepath=''; % Note: Im realen File gibt es natürlich einen Pfad
fprintf('Path: %s\n', filepath)
fprintf('\nLoad totaldev ...\n')
totaldev = xlsread(strcat(filepath,'totaldev.xlsx'));
fprintf(' ...done!\nLoad plant_load ...')
plant_load = xlsread(strcat(filepath,'load.xlsx'));
fprintf(' ...done!\n')
data=[plant_load(:,1),plant_load(:,2),totaldev(:,2)];
data(:,1)=x2mdate(data(:,1));
fprintf('Prepare data ...\n')
% Remove values below 0 and above 100 (plant load is 0% to 100%
data(any(data(:,2)<0,2),:)=[];
data(any(data(:,2)>100,2),:)=[];
% Remove values below -100 and above 100 (error can't be more than +-100%)
data(any(data(:,3)<-100,2),:)=[];
data(any(data(:,3)>100,2),:)=[];
% Compute average of time series
% Sample rate is 2sec, so 30 values cover a minute
data_avg=[data(:,1), movavg(data(:,2),30,30), movavg(data(:,3),30,30)];
data_avg(1:30,2)=data(1:30,2);
data_avg(1:30,3)=data(1:30,3);
fprintf(' ...done!\n')
fprintf('\nCreate plot')
% Plot 'em
plot(data_avg(:,1),data_avg(:,2),data(:,1),data_avg(:,3))
NumTicks = 20;
L = get(gca,'XLim');
set(gca,'XTick',linspace(L(1),L(2),NumTicks))
datetick('x','HH:MM','keepticks','keeplimits');
The function movavg will be replaced by tomorrow. The data is as follows (example) ([Time; Data1; Data2]):
735728,605428241 80,6717800000000 11,6650710000000
735728,605451389 80,6717800000000 9,12566720000000
735728,605474537 80,1416200000000 9,24074830000000
735728,605497685 80,1416200000000 9,20919460000000
735728,605520833 80,1416200000000 9,47081590000000
735728,605543982 80,3848000000000 9,28115980000000
735728,605567130 80,3848000000000 8,55264740000000
735728,605590278 80,3848000000000 9,47107710000000
735728,605613426 80,5608400000000 9,42203640000000
735728,605636574 80,6196400000000 9,32575490000000
735728,605659722 80,6196400000000 9,25049830000000
735728,605682870 80,6998100000000 9,48914560000000
735728,605706019 80,6998100000000 9,52505890000000
735728,605729167 80,6998100000000 9,12462120000000
735728,605752315 80,1338500000000 8,44201380000000
735728,605775463 80,1338500000000 8,38791130000000
735728,605798611 79,9584800000000 8,67874090000000
735728,605821759 79,9584800000000 8,92738190000000
735728,605844908 79,5133300000000 9,50254300000000
I have no idea now how to place the annotations using the code. Since I have to plot this figure a few times I don't want to set the annotations by hand each time ...
I hope someone can help =)
Cheers from Germany

Accepted Answer

dpb
dpb on 27 May 2014
Edited: dpb on 27 May 2014
A start...basically, you just generate datenums that match the locations you want.
dt=datenum(2014,5,[10:12]); % datenums for the day tick locations
xt=repmat(dt,3,1); xt=xt(:); % and make an array of x and y locations
xt(3:3:end)=nan; % insert nan to break lines between locations
yt=repmat([-0.95 -1.1 nan]',3,1); % to draw the lines at those locations (*)
hl=line(xt,yt,'color','r','clipping','off'); % lines; made red so stand out
% write the text.
ht=text(dt+0.5,-1.2*ones(size(dt)),datestr(dt,'mm.dd.yyyy'),'horizontal','center','fontsize',8);
(*) The "trick" in generating the arrays is that of inserting the NaN between the line segments to avoid drawing from the end point of the first the diagonal to the first point of the second, etc., ...
On the location for the datestr, I didn't create the other vector of locations; just used the midnight location plus 0.5 day to get noon next day for demo purposes of the idea. That works correctly for the full days in the middle, otherwise you take the average of the two datenums for the beginning and end of the partial days at the two ends.
Also, in my playing here I used a ylim(-1 1)] and the y locations for the line and the text are based on that value--you'll want to retrieve the lower y limit and adjust based on it for your case. Alternatively, you can switch to normalized coordinates relative to the whole figure and retrieve the position of the axes and work out locations therefrom. annotation works on those coordinates so if choose to go that route instead you'll have to use those. I tend to just use the axes and smudge a little. Note to get the line to show outside the axes, must set its 'clipping' property to 'off'
  4 Comments
Marc
Marc on 28 May 2014
Edited: Marc on 28 May 2014
Alright - I examined the code and made changes back and forth and I am closer to the solution now.
This looks sweet so far. The only problem is this stupid line on the left hand side of the image (I marked it with that arrow). I can't get rid of it and I have no idea why.
When I was reading your newest date vector I was a bit confused on how it works. But I think I figured it out and modified the code so that it produces the image show above. The code is as follows.
fprintf('\nCreate plot')
% Plot 'em
%hold on
%plot(data(:,1),data(:,2),data(:,1),data(:,3))
%plot(data_avg(:,1),data_avg(:,2),'--k',data(:,1),data_avg(:,3),'--k')
plot(datam(:,1),datam(:,2),datam(:,1),datam(:,3))
xlim([datenum('09-May-2014 12:00') datenum('12-May-2014 12:00')])
NumTicks = 13;
L = get(gca,'XLim');
set(gca,'XTick',linspace(L(1),L(2),NumTicks))
datetick('x','HH:MM','keepticks','keeplimits');
set(gca,'YLim',[-10 100])
set(gca,'YGrid','on')
set(gca,'Clipping','off')
% Lines:
% Define the dates on which the lines should appear
dtl=[(datenum('09.05.2014','dd.mm.yy'):datenum('12.05.2014','dd.mm.yy'))];
% Compute the x coordinates of the dates
xt=repmat(dtl,3,1);
xt=xt(:);
% Compute the y coordinates of the dates
yt=repmat([NaN -16 -21 NaN -16 -21]',2,1);
% Draw the lines
hl=line(xt,yt,'color','k','clipping','off');
% Text:
% Define the dates on which the text should appear
dt=[[datenum('09.05.2014','dd.mm.yy')+0.65] [datenum('10.05.2014',...
'dd.mm.yy'):datenum('11.05.2014','dd.mm.yy')]+0.5 ...
[datenum('12.05.2014','dd.mm.yy')+0.35]];
% Write and place text
ht=text(dt,-19*ones(size(dt)),datestr(dt,'dd.mm.yy'),'horizontal',...
'center','fontsize',8);
I again have only posted the part that plots the data. Changes involve the xlim() setting for the image, so that the graph within the plot is closer to the left and right end as well as the modification of your code.
It'd be nice if you can take a look at it and tell me how the hack I can get rid of that line. Then I am quite happy.
I will have to modify that code again when plotting new data, but since I am working with clearly visible dates this is not too bad ;)
Thanks for your support!
Edit: Germany - 0 Points! I came back to the code after doing something else a few minutes ago and it popped into my eyes: I shouldn't use the 9th May as the first date to start with D'Oh Using the following solved my problem.
% Lines:
% Define the dates on which the lines should appear
dtl=[datenum('10.05.2014','dd.mm.yy'):datenum('12.05.2014','dd.mm.yy')];
% Compute the x coordinates of the dates
xt=repmat(dtl,3,1);
xt=xt(:);
% Compute the y coordinates of the dates
yt=repmat([NaN -16 -21]',3,1);
% Draw the lines
hl=line(xt,yt,'color','k','clipping','off');
Thank you very very much!
dpb
dpb on 28 May 2014
You're welcome...hacking on handle graphics is both frustrating and (sometimes) rewarding... :)
Just a couple of comments --
...reading your newest date vector I was a bit confused...
It wasn't intended as actual code but the outline of the values you need as being the quarter-day under the first full day, then the N full days at midnight/zero-hundred hours plus the half-day for noon, and finally the last day plus the quarter. Looks like you did get there, though, although I probably should have explained it wasn't intended to be code, just a map.
One problem I'm not sure of how it worked in the end, but it would be cleaner if the x and y vectors for the line were (using yt for example) were written as
yt=repmat([-16 -21 NaN].',3,1);
instead so the line segment is drawn and then there's the NaN breaking the line before the next grouping of three. That you had the NaN first in row was, I think, why you missed one earlier. You can get away w/ removing the last NaN as superfluous but it's simpler to just leave it.
Anyway, your end result does look very nice...

Sign in to comment.

More Answers (0)

Community Treasure Hunt

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

Start Hunting!