How to label ('vertically') points in graph - matlab

I would like to add labels to some points plotted using the command scatter. For the sake of simplicity, let's say I have only one point:
x = 10;
pointSize = 100;
fontSize = 20;
P = scatter(x, 0, pointSize, [0,0,0], 'filled');
text(x, 0, 'pointLabel',...
'HorizontalAlignment', 'center',...
'VerticalAlignment', 'bottom',...
'FontSize', fontSize);
The problem with the previous commands is that the text pointLabel overlaps with the point P depending on the values assigned to the properties pointsize and fontSize.
I have read the documentation of the text command, but the examples only show how to put a label horizontally aligned with a specific point in the diagram. If the alignment needs to be horizontal it is easy, but I could not find a general way to compute the y coordinate of the label pointLabel from the values of the other dimensions.
Clearly I can reach a good alignment by testing various combinations of values, but I am looking for a general solution.
Is there anyone who can help me?

This assumes you are using >=R2014b, though it can also be accomplished in older versions using set and get commands.
When a text object is created, its default units are data coordinates, but those can be changed. In your case, I'd go with points.
x = 10;
pointSize = 100;
fontSize = 20;
P = scatter(x, 0, pointSize, [0,0,0], 'filled');
t = text(x, 0, 'pointLabel',...
'HorizontalAlignment', 'center',...
'VerticalAlignment', 'bottom',...
'FontSize', fontSize);
% It's always a good idea to switch back to the default units, so remember them.
originalUnits = t.Units;
t.Units = 'points';
% Shift the text up by the sqrt(pi)/2 times the radius of the point
t.Position(2) = t.Position(2) + sqrt(pointSize)/2;
t.Units = originalUnits;
Check out Text Properties for more info. If you want to get really sophisticated, you can use the read-only property Extent and your known marker size and position to calculate when a label is overlapping one of your points. Since the default unit is in data space, no conversions are necessary.
If you're working with an older version of MATLAB, all of these options and properties are still available, you just have to work a little harder to use them. For instance, you can't direction set the position as above, but you would instead use get to assign it to a temporary variable, change it, and then use set to update. More lines of code, but ultimately the same effect.

Related

Consistently generate line at at a specified angle as measured by user in MATLAB

Consider the simple code below that generates a straight downward sloping line in MATLAB.
clear, clc, close all
t = 0:0.1:1;
y = -t+1;
plot(t,y)
ax = gca
This is a line with slope -1, so the (acute) angle between the horizontal axis and the line is 45 degrees. Except it isn't when you measure with a protractor on your monitor.
Without changing the range of values displayed on the x and y axes or the height of the figure window, how could I ensure I would measure 45 degrees from the horizontal axis to the line if I held a protractor up to the screen?
My current approach is to change the width of the figure window. In the limit as the figure window is infinitely thin, the line x is a vertical line. Conversely, if the figure window is stretched to the edges of a monitor, it flattens out. Somewhere in the middle, the line has the angle I want. I just can't find a good way to mathematically find this point and instantiate it in code.
Edit: A slightly more generic solution for any acute angle. (I didn't test obtuse angles.)
clear, clc, close all
ang_deg = 70;
slope = tand(ang_deg);
t = 0:0.1:1;
y = -slope*t+1;
f = figure;
f.Position(3) = f.Position(3)*1.5;
plot(t,y)
% For a given height, change the width
ax = gca;
ax.Units = 'pixels';
ax.Position(3) = ax.Position(4)/slope;
You can achieve that with
axis equal
which, according to the documentation,
uses the same length for the data units along each axis.
You may want to also use
axis tight
which
fits the axes box tightly around the data by setting the axis limits equal to the range of the data
Follow up your commands with a declaration that you'll be working in pixels, and then set the width to the height:
ax.Units = 'pixels';
ax.Position(3) = ax.Position(4);

Matlab plot marker label (NodeLabel) property

Is there any way to access and set plot marker property in Matlab?
In some case especially when user defined Marker is used (like the image below), it is necessary to set NodeLabel's position, font and color, to make it distinct in the figure.
g_obj = graph(sources, targets);
gp = plot(g_obj);
gp is a Matlab GraphPlot object and even though gp.NodeLabel is located in above layer, but has visual interference with user-defined markers' black lines and for example AL1, NAL1 and S6R2 are not readable.
Is there any way to set the Marker's font and position, using the gp itself?
I tried this solution which gives some flexibility, just copied the position and labels then used text instead of NodeLabel with more flexibility in color, font and etc.
%%---
gp = plot(graph_object,'Layout','layered');
labels = gp.NodeLabel;
gp.NodeLabel = [];
gp.LineStyle = 'none'; gp.Marker = 'none';
for i=1:length(labels)
text(gp.XData(i)+2, gp.YData(i)-5,labels(i),...
'fontsize', 8,'FontName', 'Arial', 'Color',[0 0.25 0],...
'FontWeight', 'bold');
end

How to change radial tick markers in Matlab polar plot?

I'd like to relabel the radial tick markers in the following polar log plot:
f = figure ;
t = 0:0.01: pi/2 ;
polar(t, 10 * log10(cos(t))/(50) + 1)
from 1, 0.8, 0.6, 0.4, 0.2 to 0, -10, -20, -30, -40 (i.e. radial dB ticks).
Trying some of the methods from Fixing the Radial Axis on MATLAB Polar Plots, I was able to relabel the markers provided my tick markers were positive and increasing.
I tried the following based on How to remove Rho labels from Matlab polar plot?
rho_labels = {'1' '0.8' '0.6' '0.4' '0.2'};
rho_labels2 = {'0' '-10' '-20' '-30' '-40'};
for r=1:length(rho_labels)
ff = findall(f, 'string', rho_labels{r}) ;
ff = rho_labels2{r} ;
end
but it also didn't work (seems to do nothing, so I suspect I'm operating on a copy of the find results not handled).
How can these tick markers be adjusted? Also, if I wanted a different number of concentric circles than 5, how can that be done (for example, 4 subdivisions with -40 dB at the "origin".)
Here is a way to rename the radial tick labels. Be warned that if there is a match between any radial and angular tick mark labels, both will be replaced and the angular labels will be wrong. But the angular tick labels are fixed as 0, 30, 60, ..., 330, so as long as the radial tick labels don't include these values, you should be fine.
What the code does is find all the text in the plot using findall, trim the blank spaces at the front of each string, then, for each entry in rho_labels, set the string entry corresponding to the tick label with that identifier to the corresponding entry in rho_labels2.
f = figure ;
t = 0:0.01: pi/2 ;
polar(t, 10 * log10(cos(t))/(50) + 1)
rho_labels = {'1' '0.8' '0.6' '0.4' '0.2'};
rho_labels2 = {'0' '-10' '-20' '-30' '-40'};
ff = findall(f,'type','text');
t=strtrim(get(ff,'String'));
for r=1:length(rho_labels)
set(ff(strcmp(t,rho_labels{r})),'String',rho_labels2{r})
end
To modify the number of rings, I think think of anything better than modifying polar.m and creating your own custom polarMOD.m function. If you do this you can also do the radial labels as well. In fact, there might be such a function on the MathWorks File Exchange.

Make background of annotation box in image semi-transparent

I am currently working on adding annotations to satellite images in MATLAB. Since the color underneath each text field can vary quite a lot, I want to use a background color under the text to make it easier to see and read.
However, when I do this, a lot of the terrain gets obscured. I though of trying to make the background color for each text box semi transparent, but hit a dead end trying to come up with a solution.
Any ideas? I was hoping for some UI-element where I could just set the 'facealpha' to maybe 0.5. I also need the text to support being rotated (as can be seen in the examples below).
Below is some example code and the resulting image. The workspace with the satellite data can also be found in the link:
Example workspace
figure(1);clf
imagesc(xx,yy,Map);
hold on
plot(xInspection,yInspection,'g.-')
% # Two ways of making a rotated text annotation.
% # Cant make background semi-transparent
testAnno= annotation('textarrow',[0.5 0.5],[0.5 0.5], ...
'string','textarrow annotation', ...
'HeadStyle','none','LineStyle', 'none',...
'TextRotation',asin(directionVec(1))*180/pi,...
'TextBackgroundColor',[0.7 0.7 0.7]);
testText = text(mean(xInspection),mean(yInspection),'text annotation', ...
'rotation',asin(directionVec(1))*180/pi, ...
'HorizontalAlignment','right', ...
'color',[0 0 0], ...
'backgroundcolor',[0.7 0.7 0.7], ...
'fontsize',8);
It doesn't look like either annotation or text return HgObjects that have BackgroundAlpha properties (they might exist but I wasn't able to find them using getundoc or by trying various different hacks).
I was able to get something working by drawing the background myself. Here is a simple proof of concept:
f = figure;
tObj = text(.5, .5, 'text object', 'FontSize', 20);
set(gca,'XLimMode', 'manual', 'YLimMode', 'manual'); % prevent the axes from resizing automatically
p = get(tObj, 'Extent'); %Get the outer position of the text
% now create a patch around the text object
pObj = patch([p(1) p(1) p(1)+p(3) p(1)+p(3)], [p(2) p(2)+p(4) p(2)+p(4) p(2)], 'r');
uistack(tObj, 'top'); % put the text object on top of the patch object
set(pObj , 'FaceAlpha', .2); % set the alpha of the patch face to .2
%Rotate the objects
set(tObj, 'Rotation', 20);
rotate(pObj, [0 0 1], 20);
I am afraid the only way you can do this is by not setting any color to your annotations, and then placing a patch in the background of each annotation. So something like this:
% Use completely transparent annotations
hA = annotation('textarrow', ..., 'TextBackgroundColor', 'none')
% Place a transparent patch exactly in the background of your annotation
hP = patch(X, Y, 'white', 'EdgeColor', 'none', 'FaceColor', 'white', ...
'alpha', 0.3)
% Ensure that your annotation is on top
uistack(hA, 'top')
But of course the big problem is to determine the correct coordinates of the patch (X and Y). Rotating is easy by simply multiplying your coordinates by a rotation matrix. However, finding the length and height of the patch and its central location is not that easy. You might be able to find some useful functions for this at Matlab central...

distribute same size circles evenly inside a square using Matlab

I have a figure of size 14 x 14 square drawn inside an axis of 20 x 20, in matlab.
I am trying to draw circles of radius 0.7 inside the square and need to arrange them evenly. I need to draw 233 circles. Please let me know how can I do it?
Currently I can draw them randomly but couldn't get 233 circle. Please see my below code.
Your reply is appreciated.
% Urban, sub urban, Rural areas
x_area =[3, 12, 6];
y_area = [6, 8, 16];
r_area = [1, 7, 2];
f = figure;
hAxs = axes('Parent',f);
hold on, box on, axis equal
xlabel('x')
ylabel('y','Rotation',0)
title('Compute the area of circles a vectorized way for several cicles')
axis([0 20 0 20])
rectangle('Position',[5,1,14,14])
rectangle('Position',[3,1,2,2])
rectangle('Position',[1,3,4,4])
hold on, box on, axis equal
a = 233;
x_base_urban = randi([6 18], 1, a);
b = rand([10 8], 1);
y_base_urban = randi([2 14],1, a);
r_base_urban = 0.9;
size_x = size(x_base_urban);
size_x = size_x(2);
size_y = size(y_base_urban);
size_y = size_y(2);
colour = rand(size_x,3);
for t = 1: size_x
plot(x_base_urban(t)+ r_base_urban.*cos(0:2*pi/100:2*pi),...
y_base_urban(t)+ r_base_urban.*sin(0:2*pi/100:2*pi),'parent',hAxs)
plot(x_base_urban(t),y_base_urban(t),'+','parent',hAxs)
end
Thanks
Randomly plotting everything won't work. Actually, if your circles can't overlap, nothing will work. To show that, just compare the results of following calculations:
lSquare = 14;
rCircle = 0.7;
nCircles = 233;
areaCircles = nCircles * pi * rCircle^2
areaSquare = lSquare^2
You will see that areaCircles > areaSquare, so it is impossible to fit them all in. On the other hand, if areaSquare >= areaCircles does not guarantee you that a solution exists!
Try your setup with a smaller example to come up with a solution. E.g. take a square box and a bunch of spherical objects (balls, marbles, oranges, apples, ... if need be) and try to fit as much of those in your box. If that works, you might even want to draw their positions on a sheet of paper before trying to implement it.
If you do this correctly, you will get an idea of how to stack round objects in a square container. That is also exactly what you need to do in your exercise. Then try to make a model/algorithm of what you did manually and implement that in MATLAB. It won't be hard, but you will need some small calculations: Pythagoras and intersection of circles.
I also suggest you use a function to draw a circle as #Andrey shows, so something of the form function drawCircle(center, radius). That allows you to keep complexity down.
If your circles can overlap, then the solution is quite easy: look at a circle as an object with a center point and distribute these center points evenly over the square. Don't use rand to do this, but calculate their positions yourself.
If you can't find a solution, I might expand my answer in a few days.
Without diving too deep into your code, I think that you need to add a hold function, after the first plot
for t = 1: size_x
plot(x_base_urban(t)+ r_base_urban.*cos(0:2*pi/100:2*pi),...
y_base_urban(t)+ r_base_urban.*sin(0:2*pi/100:2*pi),'parent',hAxs)
plot(x_base_urban(t),y_base_urban(t),'+','parent',hAxs)
hold(hAxs,'on');
end
By the way, the best way to draw circle is by using rectangle command.
rectangle('Curvature',[1 1],'Position',[1 3 4 5])
So you can create a PlotCircle function (like #EgonGeerardyn suggests) like this:
function plotCircle(x,y,r)
rectangle('Position',[x-r y-r 2*r 2*r],'Curvature',[1 1]);
end