Making a static legend, independent of the actual plot - matlab

I'm plotting two surfaces in Matlab, the ground level which is constant and the water level which is changing in time.
I use an if statement to change the colormap of the water with increasing depth, determined by two thresholds (in the code below the thresholds are represented by the array C)
if max(C(:)) < 2
colormap([0.5 0.25 0 ; 0 0.8 1]);
elseif max(C(:)) >= 2 && max(C(:)) < 3
colormap([0.5 0.25 0 ; 0 0.8 1 ; 0 0.5 0.65]);
elseif max(C(:)) >= 3
colormap([0.5 0.25 0 ; 0 0.8 1 ; 0 0.5 0.65 ; 0 0.2 0.3]);
end
The result is that when the water level is below threshold 1 it is a light blue, between the two thresholds it turns darker, and above threshold 2 it turns darker yet.
My problem is that when I draw the legend
legend('ground','water');
... I only get one color representing the water, and it changes according to the max depth. How do I make a static legend showing all four categories with a color example:
(brown) Ground
(light blue) Water level beneath threshold 1
(standard blue) Water level between thresholds
(dark blue) Water level above threshold 2
The only solution I can think of is making 3 neat little patches somewhere by the axis (where it won't be noticed) in the right blue colors and then making the legend point at them and not the water surface. But that would be a real crappy solution...

You can display a colorbar():
C = [1 2 3 4];
if max(C(:)) < 2
colormap([0.5 0.25 0 ; 0 0.8 1]);
elseif max(C(:)) >= 2 && max(C(:)) < 3
colormap([0.5 0.25 0 ; 0 0.8 1 ; 0 0.5 0.65]);
elseif max(C(:)) >= 3
colormap([0.5 0.25 0 ; 0 0.8 1 ; 0 0.5 0.65 ; 0 0.2 0.3]);
end
% whatever to make your plot
imagesc(C);
% display a colorbar
cb_ax = colorbar;
% label it appropriately
set(cb_ax, 'YTick', [1:4]*3/4+5/8, 'YTickLabels', {'A', 'B', 'C', 'D'});

Related

How to draw weighted convex hull in Matlab

Suppose I have a Matlab array PE of size Ex1. The elements of PE are between 0 and 1 and they sum up to one.
Take another array PEY of size ExY. The elements of PEY are one or zero. Moreover, for each row, there exists at least a one. Y=3.
For example
clear
E=4;
Y=3;
PE=[1/2; 1/6; 1/6; 1/6];
PEY=[1 0 0; 0 1 1; 1 1 1; 0 1 1];
Now, consider the simplex with vertices (1,0,0), (0,1,0), and (0,0,1)
patch([0 0 1],[0 1 0],[1 0 0],[0.8 0.8 0.8]);
axis equal
axis([0 1 0 1 0 1])
view(120,30)
I want to draw a convex subset A of such simplex. A is constructed as follows.
STEP 1: We construct an Ex1 cell array PEY_expanded such that, for each e-th row of PEY that has more than a 1, we write down all admissible 1xY vectors containing just a 1 and stack them in PEY_expanded{e}.
PEY_expanded=cell(E,1);
for e=1:E
if isequal(PEY(e,:),[1 1 1])
PEY_expanded{e}=[1 0 0; 0 1 0; 0 0 1];
elseif isequal(PEY(e,:),[1 1 0])
PEY_expanded{e}=[1 0 0; 0 1 0];
elseif isequal(PEY(e,:),[1 0 1])
PEY_expanded{e}=[1 0 0; 0 0 1];
elseif isequal(PEY(e,:),[0 1 1])
PEY_expanded{e}=[0 1 0; 0 0 1];
else
PEY_expanded{e}=PEY(e,:);
end
end
STEP 2: Take Cartesian product PEY_expanded{1} x PEY_expanded{2} x ... PEY_expanded{E} and get PEY_cartesian.
Note: the code below is specific for E=4 and not general
size_PEY_expanded=zeros(E,1);
for e=1:E
size_PEY_expanded(e)=size(PEY_expanded{e},1);
end
[a,b,c,d]=ndgrid(1: size_PEY_expanded(1),1: size_PEY_expanded(2),...
1: size_PEY_expanded(3), 1: size_PEY_expanded(4));
PEY_Cartesian= [PEY_expanded{1}(a,:),PEY_expanded{2}(b,:),...
PEY_expanded{3}(c,:), PEY_expanded{4}(d,:)];
PEY_Cartesian_rearranged=cell(prod(size_PEY_expanded),1);
for i=1:prod(size_PEY_expanded)
PEY_Cartesian_rearranged{i}=[PEY_Cartesian(i,1:3); PEY_Cartesian(i,4:6);...
PEY_Cartesian(i,7:9); PEY_Cartesian(i,10:end)];
end
PEY_Cartesian=PEY_Cartesian_rearranged;
STEP 3: For each possible cell of PEY_Cartesian, for y=1,...,Y, weight PEY_Cartesian{i}(e,y) by PE(e) and then sum across e.
PY=zeros(prod(size_PEY_expanded),Y);
for i=1:prod(size_PEY_expanded)
for y=1:Y
temp=0;
for e=1:E
temp=temp+PE(e)*PEY_Cartesian{i}(e,y);
end
PY(i,y)=temp;
end
end
STEP 4: Draw the region A that is the convex hull of the rows of PY (black region in the picture)
%Need https://fr.mathworks.com/matlabcentral/fileexchange/37004-suite-of-functions-to-perform-uniform-sampling-of-a-sphere
close all
patch([0 0 1],[0 1 0],[1 0 0],[0.8 0.8 0.8]);
axis equal
axis([0 1 0 1 0 1])
view(120,30)
hold on
T = delaunayTriangulation(PY);
K = convexHull(T);
patch('Faces',K,'Vertices',T.Points,'FaceColor','k','edgecolor','k');
hold on
QUESTION:
The algorithm above is unfeasible for large E. In my actual case I have E=216, for example. In particular, step 2 is unfeasible.
Could you suggest an easier way to proceed? Given that A is a convex region, maybe there is some shortcut I'm unable to see.

Color discrimination of matrix connected components

In Matlab, I have a matrix M, say:
M=[0 0 2 2 0 0
0 0 2 2 0 3
1 1 2 2 3 3
1 1 0 0 0 0
1 1 0 0 0 0];
with some connected components labeled 1,2 and 3.
I need to discriminate the components (1, 2 and 3) by using different colors (red, green and blue for example). Any help to do this. Thanks in advance
You can use image and colormap. From the documentation of the former,
image(C) displays the data in array C as an image. Each element of C
specifies the color for 1 pixel of the image.
When C is a 2-dimensional m-by-n matrix, the elements of C are used as
indices into the current colormap to determine the color. For 'direct' CDataMapping (the default),
values in C are treated as colormap indices (1-based if double, 0-based
if uint8 or uint16).
Thererfore, you only need to call image(M+1), so that the values start at 1; and then define a suitable colormap. The colormap is a 3-column matrix, where each row defines a color in terms of its R, G, B components.
M = [0 0 2 2 0 0;0 0 2 2 0 3;1 1 2 2 3 3;1 1 0 0 0 0;1 1 0 0 0 0];
imagesc(M+1) % add 1 so that values start at 1, not 0
cmap = [1 1 1; % white
.7 0 0; % dark red
0 .7 0; % dark green
0 0 .7]; % dark blue
colormap(cmap) % set colormap
axis tight % avoid white space around the values
axis equal % aspect ratio 1:1

Values do not match data in matlab geographic plot

I am plotting trajectories in Matlab using contourf. I am having an issue with the colors matching the data. I have posted my current image below. All areas that do not have data should be white as they are zero which I specifically specified in the script(they are currently blue-ish which is the the 0.1 to 1 range). In addition, values that are yellow, should be in the blue range(<1). Any suggestions?
Here is the part of my script where I do the plotting:
axesm('mercator', 'MapLatLim', latlim, 'MapLonLim', lonlim,...
'Frame', 'on', 'Grid', 'on', 'MeridianLabel', 'on', 'ParallelLabel', 'on')
setm(gca,'mlabelparallel',-20)
load coastlines
Contours = [0.001 0.01 0.1 1 10 100];
[c,h] = contourfm(latlim, lonlim, u, log(Contours));
colorbar('YTick', log(Contours), 'YTickLabel', Contours);
myColorMap = jet(256).^.3;
myColorMap(1,:) = [1];
colormap(myColorMap)
colorbar
caxis(log([Contours(1) Contours(length(Contours))]));
colorbar('FontSize', 12, 'YTick', log(Contours), 'YTickLabel', Contours);
geoshow(coastlat, coastlon,'Color', 'k')
Contour level, V, in contourfm(lat,lon,Z, V) does not scale your data or colour. It works in a different way than what you thought.
Let's see one example first:
u = rand(8)+0.1; u(1:2,:) = 0; u(5:6,:) = 10; u(7:8,:) = 100;
V = [0,1,40,100];
contourfm([0,1], [0,1], u, V);
mycm = jet(256).^.3; mycm(1,:) = 1;
colormap(mycm)
contourcbar('FontSize', 12, 'YTick', V, 'YTickLabel', V);
where u is
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0.1947 0.6616 0.2413 0.6511 0.4403 0.9112 1.0016 0.5654
0.8422 0.3159 0.5695 0.6478 0.9933 0.1686 0.8387 0.5362
10 10 10 10 10 10 10 10
10 10 10 10 10 10 10 10
100 100 100 100 100 100 100 100
100 100 100 100 100 100 100 100
As you can see, for V = [0,1,40,100] all values from 0 to 1 are white, values from 1 to 40 are cyan and above are red.
Therefore, you must scale your u then assign appropriate contour level. Use contourcbar instead of colorbar to check the colours first.
Apart from the problem with contour level, I suspect the u parameter contains negative values. The colour at the bottom of the colour bar is always assigned to the minimum z value. You must ensure 0 is the minimum value in u, i.e. remove the negative values.

MATLAB calculate area of shape on plot

I Create a plot using imagesc. The X/Y axis are longitude and latitude respectively. The Z values are the intensity of the images for the image shown below. What I'd like to be able to do is calculate the area in each of the polygons shown. Can anybody recommend a straightforward (or any) method in accomplishing this?
EDIT
Forgot to include image.
Below is a toy example. It hinges on the assumption that the Z values are different inside the objects from outside (here: not 0). Also here I assume a straight divider at column 4, but the same principle (applying a mask) can be applied with other boundaries. This also assumes that the values are equidistant along x and y axes, but the question does not state the opposite. If that is not the case, a little more work using bsxfun is needed.
A = [0 2 0 0 0 2 0
3 5 3 0 1 4 0
1 4 0 0 3 2 3
2 3 0 0 0 4 2
0 2 6 0 1 6 1
0 3 0 0 2 3 0
0 0 0 0 0 0 0];
area_per_pix = 0.5; % or whatever
% plot it
cm = parula(10);
cm(1, :) = [1 1 1];
figure(1);
clf
imagesc(A);
colormap(cm);
% divider
dv_idx = 4;
left_object = A(:, 1:(dv_idx-1));
left_mask = left_object > 0; % threshold object
num_pix_left = sum(left_mask(:));
% right object, different method
right_mask = repmat((1:size(A, 2)) > dv_idx, size(A, 1), 1);
right_mask = (A > 0) & right_mask;
num_pix_right = sum(right_mask(:));
fprintf('The left object is %.2f units large, the right one %.2f units.\n', ...
num_pix_left * area_per_pix, num_pix_right * area_per_pix);
This might be helpful: http://se.mathworks.com/matlabcentral/answers/35501-surface-area-from-a-z-matrix
He has not used imagesc, but it's a similar problem.

scale part of an axis in matlab

I have the following image which I want to have the depth axis range like below :
(10 9.5 9 8.5 8 7.5 7 6 5 3 2 1 0.9 0.8 0.7 0.6 0.5 0.4 0.3 0.2 0.1 0) to show the data between depth 1 and 0 in larger scale, and I have the following code
depths = [10 5 1 0.5 0; 10 5 1 0.5 0] % these are the real depths in meter
contourf(points,depths,RFU15102013_BloomAsMainPoint);
set(gca, 'XTick', points(1) : points(2), 'XTickLabel',{ 'LSB1', 'LSB2'});
ylabel('Depth(m)');
xlabel('Points');
title('Date: 15.10.2013');
this is the image :
how can I do that?
EDIT1
Real Data:
RFU15102013_BloomAsMainPoint = [ 2.71 1.23 1.30 1.20 14.37 ; 2.51 1.36 1.01 1.24 1.15];
points = [1 1 1 1 1; 2 2 2 2 2 ];
depths = [10 5 1 0.5 0; 10 5 1 0.5 0];
As most of a data changes around zero it could be enough to change scaling of Y axis. Here is an example
close all; clear all;
z = [ 2.71 1.23 1.30 1.20 14.37 ; 2.51 1.36 1.01 1.24 1.15];
x = repmat([1; 2], 1, 5);
y = repmat([10 5 1 0.5 0], 2, 1);
% plotting with equally spaced y-s
h = subplot(1,2,1);
contourf(x,y,z);
y2 = log(y + 0.25);
yTicks = linspace(min(y2(1,:)), max(y2(1,:)), 10);
% plotting with logarithmically spaced y-s
h = subplot(1,2,2)
contourf(x,y2,z);
set(h,'YTick', yTicks)
set(h,'YTickLabel', exp(yTicks) - 0.25);
print('-dpng','scaling.png')
The result
This way any monotonic continuous function for axis scaling can be applied.
You could use UIMAGE - UIMAGESC from the mathworks file exchange and set the y values to emphaisize points in 1 to 0 range.