How to draw an arrow using non normalized coordinates?

I use: annotation('arrow',X,Y), and I tried to change units, but it is always normalized. How to use data units?

4 件のコメント

KSSV
KSSV 2017 年 6 月 27 日
arrow it is not a inbuilt function....what is the source of the function?
Walter Roberson
Walter Roberson 2017 年 6 月 27 日
Mr M.
Mr M. 2017 年 6 月 28 日
yes, but I cannot figure out how to use non-normalized units with annotation('arrow')
Kaluri
Kaluri 2024 年 5 月 23 日
編集済み: Walter Roberson 2024 年 11 月 15 日
Yes using normalised coordinated is a big problems. For cartesion frame work we have ds2nfu.m is available and works good but I struggled to solve this for polarplot(theta,rho) not polar(theta,rho). Aspect ratio is a problem. Please use this code below. I have modified the original code.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
clc; clear all;
% Dr Kaluri V Ranga Rao life senior member IEEE princeton kaluri@ieee.org
close all;
theta = linspace(0,360,180);
max = 1;
r = linspace(max,0.2,180);
s = 60; e = 20;
ra = [r(s) r(e)];
th = [theta(s) theta(e)];
h = polarplot(theta*pi/180,r,'.-r',th*pi/180,ra,'ob');
for k=1:180
[xaf,yaf] = polarDS2nfu([theta(k) theta(k)]*pi/180,[r(k)*0.5 r(k)]);
hT = annotation('arrow',xaf,yaf);
pause(0.5);
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Michelle Hirsch
% mhirsch@mathworks.com
% Copyright 2006-2014 The MathWorks, Inc
% updated for polar kaluri@ieee.org
%I modified this and works well for polarplot
function varargout = polarDS2nfu(varargin)
% polarDS2NFU Convert data space polar units into normalized figure units.
%
% [Xf, Yf] = DS2NFU(X, Y) converts X,Y are theta,r polar coordinates from
% data space to normalized figure units, using the current axes. This is
% useful as input for ANNOTATION.
%
% POSf = DS2NFU(POS) converts 4-element position vector, POS from
% data space to normalized figure units, using the current axes. The
% position vector has the form [Xo Yo Width Height], as defined here:
%
% web(['jar:file:D:/Applications/MATLAB/R2006a/help/techdoc/' ...
% 'help.jar!/creating_plots/axes_pr4.html'], '-helpbrowser')
%
% [Xf, Yf] = DS2NFU(HAX, X, Y) converts X,Y coordinates from
% data space to normalized figure units, on specified axes HAX.
%
% POSf = DS2NFU(HAX, POS) converts 4-element position vector, POS from
% data space to normalized figure units, using the current axes.
%
% Ex.
% % Create some data
% t = 0:.1:4*pi;
% s = sin(t);
%
% % Add an annotation requiring (x,y) coordinate vectors
% plot(t,s);ylim([-1.2 1.2])
% xa = [1.6 2]*pi;
% ya = [0 0];
% [xaf,yaf] = ds2nfu(xa,ya);
% annotation('arrow',xaf,yaf)
%
% % Add an annotation requiring a position vector
% pose = [4*pi/2 .9 pi .2];
% posef = ds2nfu(pose);
% annotation('ellipse',posef)
%
% % Add annotations on a figure with multiple axes
% figure;
% hAx1 = subplot(211);
% plot(t,s);ylim([-1.2 1.2])
% hAx2 = subplot(212);
% plot(t,-s);ylim([-1.2 1.2])
% [xaf,yaf] = ds2nfu(hAx1,xa,ya);
% annotation('arrow',xaf,yaf)
% pose = [4*pi/2 -1.1 pi .2];
% posef = ds2nfu(hAx2,pose);
% annotation('ellipse',posef)
%% Process inputs
error(nargchk(1, 3, nargin))
aspectRatio = 1.15;
XaspectRatio = 0.69;
% Determine if axes handle is specified
if length(varargin{1})== 1 && ishandle(varargin{1}) && strcmp(get(varargin{1},'type'),'axes')
hAx = varargin{1};
varargin = varargin(2:end);
else
hAx = gca;
end;
errmsg = ['Invalid input. Coordinates must be specified as 1 four-element \n' ...
'position vector or 2 equal length (x,y) vectors.'];
% Proceed with remaining inputs
if length(varargin)==1 % Must be 4 elt POS vector
pos = varargin{1};
if length(pos) ~=4,
error(errmsg);
end;
else
[th,r] = deal(varargin{:});
[x y] = pol2cart([th(1) th(2)],[r(1) r(2)]*aspectRatio);
x = x*XaspectRatio;
if length(x) ~= length(y)
error(errmsg)
end
end
%% Get limits
axun = get(hAx,'Units');
set(hAx,'Units','normalized');
axpos = get(hAx,'Position');
ax = axis(hAx);
axlim =[-ax(4) ax(4) -ax(4)*aspectRatio ax(4)*aspectRatio];
axwidth = diff(axlim(1:2));
axheight = diff(axlim(3:4));
%% Transform data
if exist('x','var')
varargout{1} = (x-axlim(1))*axpos(3)/axwidth + axpos(1);
varargout{2} = (y-axlim(3))*axpos(4)/axheight + axpos(2);
else
pos(1) = (pos(1)-axlim(1))/axwidth*axpos(3) + axpos(1);
pos(2) = (pos(2)-axlim(3))/axheight*axpos(4) + axpos(2);
pos(3) = pos(3)*axpos(3)/axwidth;
pos(4) = pos(4)*axpos(4)/axheight;
varargout{1} = pos;
end
%% Restore axes units
set(hAx,'Units',axun)

サインインしてコメントする。

 採用された回答

KarlHoff
KarlHoff 2020 年 6 月 26 日
編集済み: KarlHoff 2020 年 8 月 7 日

14 投票

For me, using MATLAB R2018b,
the following works to produce an arrow at a location specified in data coordinates:
anArrow = annotation('arrow') ;
anArrow.Parent = gca; % or any other existing axes or figure
%EDIT thanks to @Moshe:
%anArrow.Position = [x_start, y_start, x_end, y_end] ;
anArrow.Position = [x_start, y_start, delta_x, delta_y] ;
The big advantage is that the arrow remains where it is with respect to other plot elements, even if the limits of the plot change afterwards.
This is not the case with all the approaches that convert data coordinates into other Units (Pixels, Inches, Normalized)

4 件のコメント

Moshe
Moshe 2020 年 8 月 6 日
This is a great solution, but x_end and y_end should be in fact delta_x and delta_y, i.e., the difference between the end and start coordinates.
Jean-Michel
Jean-Michel 2020 年 9 月 2 日
Thanks for that , I have been fighting quiver and annotation to get similare result! it would be a nice addition to the docuementation
German Elias Oggier
German Elias Oggier 2021 年 5 月 13 日
Thanks!
Jonas Junker
Jonas Junker 2024 年 11 月 25 日
Thank you very much for this explanations. I have been fighting with annotating arrows for hours!

サインインしてコメントする。

その他の回答 (8 件)

MICHAEL MUTWIRI
MICHAEL MUTWIRI 2021 年 8 月 21 日

2 投票

Create the 'annotation' object for each subplot and edit their properties using dot notation.
For the X and Y properties you use similar values as your graph cordinates. The x-end and y_end are the tip of the arrow.
Below is a tested sample code
%Create sample data
x = linspace(0,2*pi,1e3);
y = sin(x); % Plotted of first subplot
z = cos(x); % Plotted of second subplot
fg1=figure(1);
% Specify different textarrows for different subplots
%%%**************** SUBPLOT 1 ********************
subplot(2,1,1);
plot(x,y,'k')
xlabel('x')
ylabel('Amplitude')
title('Sin(x)')
ylim([-1.1 1.1])
% Define X-Beginning and ending x-coordinates
x_start =pi-1;x_end = pi;
%Y- Beginning and ending y-coordinates
y_start =sin(x_end);y_end = sin(x_end);
anArrow = annotation('textarrow');
anArrow.Parent = gca;
anArrow.X = [x_start,x_end]; % set the x-property
anArrow.Y = [y_start ,y_end];
anArrow.String = 'sin(\pi)';
anArrow.Color = 'red';
%%%**************** SUBPLOT 2 ********************
subplot(2,1,2);
plot(x,z,'k')
xlabel('x')
ylabel('Amplitude ')
ylim([-1.1 1.1])
% Define X-Beginning and ending x-coordinates
x_start =1.5*pi-1;x_end = 1.5*pi;
%Y- Beginning and ending y-coordinates
y_start =cos(x_end);y_end = cos(x_end);
anArrow = annotation('textarrow');
anArrow.Parent = gca;
anArrow.X = [x_start,x_end]; % set the x-property
anArrow.Y = [y_start ,y_end];
anArrow.String = 'cos(3\pi/2)';
anArrow.Color = 'green';

1 件のコメント

Bill Tubbs
Bill Tubbs 2023 年 3 月 22 日
編集済み: Bill Tubbs 2023 年 3 月 22 日
Thanks. This works. Strange that you can't use axes co-ordinate system with the annotate command. It has a 'Units' argument but I can't find an appropriate value for this argument other than the default which is 'normalized' (where is the documentation?).

サインインしてコメントする。

Walter Roberson
Walter Roberson 2017 年 6 月 27 日

1 投票

8 件のコメント

Walter Roberson
Walter Roberson 2017 年 6 月 28 日
annotation('arrow', X, Y, 'Units', 'pixels')
There is, however, no way to directly specify Data units.
Suppose that Xdata and Ydata are your arrow positions, where what you would like to do is
annotation('arrow', Xdata, Ydata, 'Units', 'data') %will not work
but that is not permitted.
You can proceed as:
ax = gca;
%get axes drawing area position in pixels relative to figure
oldunits = get(ax, 'Units');
set(ax, 'Units', 'pixels');
axpos = get(ax, 'Position');
set(ax, 'Units', oldunits);
%get axes drawing area in data units
ax_xlim = xlim(ax);
ax_ylim = ylim(ax);
ax_pixels_per_xdata = axpos(3) ./ diff(ax_xlim);
ax_pixels_per_ydata = axpos(4) ./ diff(ax_ylim);
%these are figure-relative
Xpixels = (Xdata - ax_xlim(1)) .* ax_pixels_per_xdata + axpos(1);
Ypixels = (Ydata - ax_ylim(1)) .* ax_pixels_per_ydata + axpos(2);
annotation('arrow', Xpixels, Ypixels, 'Units', 'pixels')
I did not test the code.
Walter Roberson
Walter Roberson 2018 年 8 月 23 日
There are several File Exchange contributions to draw arrows in data coordinates. annotation() creates a hidden full figure axes on top of everything else (except the cursor I guess)
Conrado Neto
Conrado Neto 2019 年 4 月 14 日
Thanks, this code worked for me!
Eric LePage
Eric LePage 2019 年 11 月 3 日
It would be helpful if MATLAB provided annotation available for data coordinates. Doing this fix for position works for me, if there is only one set of axes in the figure but not for multiple axes. It's inconsistent to have provided text(X,Y,str) using X and Y in data coordinates and not annotation using data coordintes. Why is MATLAB still so riddled with such anachronisms? You typically get your data processed then need to spend the next couple of days tweaking the look at a very low level, splattering one's disk with all the dozens of unsatisfactory plots.
Adam Danz
Adam Danz 2019 年 11 月 3 日
編集済み: Adam Danz 2019 年 11 月 3 日
100% agreed with Eric LePage. annotation() will be much more useful if we could use data units.
Updated link to the "Units" property of arrows, from Walter's answer:
Eric LePage
Eric LePage 2019 年 11 月 4 日
The problem is particularly acute for 3D plots, where the axis labels remain square to the figure, i.e. not included in the 3D geometry and don't follow the axes around as you make use of the marvellous real-time panning capability. Trying to get the labels to stay aligned with the axes can be done but only for a final plot. There seems no top-down approach to the routines which MATLAB provides. Computers should work for the user, not the reverse!
Timon Rayis
Timon Rayis 2019 年 11 月 11 日
Any developments with R2019b? annotations with non normalized coordinates like text ?
Adam Danz
Adam Danz 2019 年 11 月 11 日
Check out release notes for any updates on any release.

サインインしてコメントする。

marcus yoder
marcus yoder 2018 年 8 月 23 日
編集済み: marcus yoder 2018 年 8 月 23 日

1 投票

I tested the code by Walter Robinson and had to make a few changes to get it to work.
function obj = dataArrow(Xdata,Ydata,ax)
%This function will draw an arrow on the plot for the specified data.
%The inputs are
oldunits = get(ax, 'Units');
set(ax, 'Units', 'Normalized');
axpos = ax.CurrentAxes.Position;
set(ax, 'Units', oldunits);
%get axes drawing area in data units
ax_xlim = ax.CurrentAxes.XLim;
ax_ylim = ax.CurrentAxes.YLim;
ax_per_xdata = axpos(3) ./ diff(ax_xlim);
ax_per_ydata = axpos(4) ./ diff(ax_ylim);
%these are figure-relative
Xpixels = (Xdata - ax_xlim(1)) .* ax_per_xdata + axpos(1);
Ypixels = (Ydata - ax_ylim(1)) .* ax_per_ydata + axpos(2);
obj = annotation('arrow', Xpixels, Ypixels, 'Units', 'pixels');
end
Robert
Robert 2019 年 9 月 7 日

1 投票

I wanted to do something similar, here's an example that adds a double arrow between the x-values 1 and 5 with y-values 5 in a simple plot:
pos=[.1,.1,.85,.85];
figure;ax=axes('position',pos);plot(1:10)
x=[1,5];y=[5,5];
rx=xlim(ax);ry=ylim(ax);
cx=pos(3)/diff(rx);cy=pos(4)/diff(ry)
annotation('doublearrow',pos(1)+cx*(x-rx(1)),pos(2)+cy*(y-ry(1)))
MichailM
MichailM 2020 年 4 月 4 日

1 投票

Maybe a function like the below could help. The x and y inputs are actual coordinates on the plot. Here I just need to draw an arrow but you can modify it
function myarrow(x,y)
ax = gca;
axpos = get(ax, 'Position');
X = get(gca,'XLim');
Y = get(gca,'YLim');
difX = X(2) - X(1);
difY = Y(2) - Y(1);
newx = x./difX;
newy = y./difY;
annotation('arrow',[newx(1)*axpos(3)+axpos(1) newx(2)*axpos(3)+axpos(1)],[newy(1)*axpos(4)+axpos(2) newy(2)*axpos(4)+axpos(2)])
end
Marc Compere
Marc Compere 2021 年 8 月 14 日

1 投票

Scaling to achieve arrow annotations in axes units should be built into Matlab. The utility coord2norm() handles this easily.
This is a similar question with more discussions, but the short answer is: use coord2norm()

1 件のコメント

Adam Danz
Adam Danz 2021 年 8 月 15 日
Thanks for pointing out that function, Marc, I'm sure it will be helpful in many cases.
Since it's a static, once-and-done, conversion, the annotation object may no longer be in the correct position if there are any changes to the figure size, axis size or position, axis limits, or aspect ratios. Calling the function after all plotting is complete would help to solve some of those issues. A more robust solution would be to assign listeners that update annotation objects when a resize or reposition event occurs but really what we need is for MathWorks to update the annotation function to support data units or offer users an alternative.

サインインしてコメントする。

Vitaly Fedoseev
Vitaly Fedoseev 2021 年 5 月 26 日

0 投票

The following code (Matlab R2019a) draws an arrow in the plot coordinates from point P1 to point P2. Zoom in/out shifts position of the arrow:
P1=[10,-1]; %from point
P2=[70,2]; % to point
figure;
Xlim=[-1 110];
Ylim=[-2 3];
Pos = [0.10 0.55 0.85 0.4];
subplot('Position', Pos)
hold on
X_conv(1)=Pos(1)+(Pos(3))/(Xlim(2)-Xlim(1))*(P1(1)-Xlim(1));
X_conv(2)=Pos(1)+(Pos(3))/(Xlim(2)-Xlim(1))*(P2(1)-Xlim(1));
Y_conv(1)=Pos(2)+(Pos(4))/(Ylim(2)-Ylim(1))*(P1(2)-Ylim(1));
Y_conv(2)=Pos(2)+(Pos(4))/(Ylim(2)-Ylim(1))*(P2(2)-Ylim(1));
x=0:0.1:100;plot(x, sin(x));plot([-100 1000], P2(2)*[1 1]); plot(P2(1)*[1 1], [-100 100]);
plot(x, sin(x));plot([-100 1000], P1(2)*[1 1]); plot(P1(1)*[1 1], [-100 100])
xlim(Xlim)
ylim(Ylim)
annotation('arrow', X_conv, Y_conv)

3 件のコメント

Adam Danz
Adam Danz 2021 年 5 月 26 日
> Zoom in/out shifts position of the arrow:
Zooming shifts the position of the arrow relative to the data because the arrow coordinates are normalized to the figure and the arrow is a child of the figure, not the axes. Delete the axes using delete(gca) and you'll see that the arrow remains.
Vitaly Fedoseev
Vitaly Fedoseev 2021 年 6 月 1 日
Where should the line
delete(gca)
be inserted in the code above?
Walter Roberson
Walter Roberson 2021 年 6 月 1 日
It should not be added to the code. Adam is saying that if you wanted to illustrate that the arrow did not follow the axes, then you could delete the axes and observe that the arrow is still there.

サインインしてコメントする。

Bruce Jackson
Bruce Jackson 2022 年 10 月 26 日

0 投票

It is absurd that we have to jump through hoops or download submitted code to plot an arrow in data units on a plot.

2 件のコメント

Adam Danz
Adam Danz 2022 年 10 月 27 日
Depending on what you're looking for, you can already plot simple arrows in data units using text().
x = rand(1,5);
y = rand(1,5);
plot(x,y,'o')
text(x,y,repmat({char(8594)},size(x)), ...
'HorizontalAlignment', 'right', ...
'VerticalAlignment', 'middle', ...
'FontSize', 14)
Bruce Jackson
Bruce Jackson 2022 年 10 月 27 日
Thanks for the suggestion, but I would like to place and point arrows with a specified length and direction. The innovative use of UNICODE arrows unfortunately doesn't allow for more than four or maybe eight directions, I expect.
[The excellent coord2norm() function, written by user sco1, should be part of core MATLAB and not require me to download and install third-party code, which I'm attempting to avoid to have a marketable toolbox.]

サインインしてコメントする。

カテゴリ

質問済み:

2017 年 6 月 26 日

コメント済み:

2024 年 11 月 25 日

Community Treasure Hunt

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

Start Hunting!

Translated by