Control colorbar scale in MATLAB - matlab

Question: How do I specify color transitions in a custom MATLAB colorbar?
Specifically, I'd like to make the yellow (see below) cover more area of the colorbar (perhaps [19.5–21.5] or something close to that).
Using this answer, I was able to create a custom colorbar in MATLAB. I'm trying to understand this answer as it might be relevant.
I have attempted approaches from this answer and reviewed this answer & this one and was unable to accomplish my goal.
It is clear I am missing something.
Full representative example below
% MATLAB 2017a
% Data
X = [22.6 22.8 22.6 20.45 22.3 18.15 19.95 20.8].';
Y = [84 89 63 81 68 83 77 52].';
Z = [23.0 22.695 21.1450 21.5 22.09 20.5 22.075 20.915].';
% Create custom colormap
% Reference: https://stackoverflow.com/questions/24488378/how-to-map-a-specific-value-into-rgb-color-code-in-matlab/24488819#24488819
col3 = [0 1 0]; %G
col2 = [1 1 0]; %Y
col1 = [1 0 0]; %R
n1 = 20; n2 = 20;
cmap=[linspace(col1(1),col2(1),n1);linspace(col1(2),col2(2),n1);linspace(col1(3),col2(3),n1)];
cmap(:,end+1:end+n2)=[linspace(col2(1),col3(1),n2);linspace(col2(2),col3(2),n2);linspace(col2(3),col3(3),n2)];
cmap = cmap.';
% Plot
colormap(cmap), hold on, box on
p = scatter(X,Y,[],Z,'filled','DisplayName','Data3');
cb = colorbar;
cb.Limits = [18 23];
cb.Ticks = [18:1:23];
% Cosmetics
p.MarkerEdgeColor = 'k';
xlabel('X')
ylabel('Y')
cb.Label.String = 'Z';

I think all that you're missing is a call to caxis to specify the minimum and maximum values to map the color range to:
caxis([18 23]);
Note that the following line...
cb.Limits = [18 23];
... only changes the tick limits displayed on the colorbar, but doesn't change anything about how the data is mapped to the color range. The caxis function is how you control that (in the above case, mapping the value of 18 to one end and the value of 23 to the other). By default, your code was mapping the minimum and maximum values in Z to the color range (20.5 and 23, respectively). When you then set the tick limits on the color bar to a larger range, it just filled it in with the last color in the color map, in this case red. That's why you see so much of it.
Bonus
Just because you might be interested, you could also use interpolation via the interp1 function to easily generate your color map like so:
cmap = interp1([1 0 0; 1 1 0; 0 1 0], linspace(1, 3, 41));

Related

Range of values for colormap

I want to make a colormap including three ticklabels: Low, Intermediate and High. However, these labels should correspond to a range of values. Low = 0-50, Intermediate = 50-100 and High = 100 - maxvalue.
The code I have so far is written below, however it goes wrong when I want to define the Ticks. Could someone help me how to implement the ranges in to the ticks?
Thanks a lot!
figure, imshow(result);
[maxval] = max(result(:));
[minval] = min(result(:));
% red, yellow, green
cmap = [0 1 0; 1 1 0; 1 0 0];
colormap(cmap);
h = colorbar;
caxis([minval maxval]);
set(h, 'Ticks',[0:1:50, 50:1:100, 100:1:maxval])
set(h,'Ticklabels',{'Low','Intermediate','High'})
Rather than you colormap only having three values, you'll want to repeat each value in your colormap several times so that it covers the whole range.
Also, you have defined your ticks to be an array of many arrays (0:1:50 creates an array of 50 values). Each value in the array used for the 'Tick' property is a separate tick.
Instead of passing in arrays, you'll want to take the mean of each range and use that as the tick location.
Something like this should accomplish what you want.
crange = 0:maxval;
cmap = zeros(numel(crange), 3);
cmap(:,1) = crange >= 50;
cmap(:,2) = crange < 100;
colormap(cmap);
h = colorbar;
caxis([0 maxval]);
set(h, 'Ticks', [25 75 mean([100 maxval])], ...
'TickLabels', {'Low', 'Intermediate', 'High'});

Modify colorbar ticks and color range

This question is following on from a previous question about HSV color space.
Let's say I have two arrays A and B, where A are my data points (2D) of interest to be shown in the colorbar and B is an RGB image transformed from the HSV color space where: Hue is in the interval [0.25-1] (corresponding to normalized A values 0.25-1), Saturation = 1, Value in interval [0-1] (corresponding to some other values).
When displaying B with imshow, I want to create a matching colorbar with ticks that correspond to the value range from A.
First difficulty that I'm facing is that I want my Hue to be in the interval [0.25-1] and hence I only need a certain part of the hsv colorbar to be displayed.
Second difficulty is that I need to match the value range from A to the colorbar.
Example code:
A = rand(30,30)*0.4; % Values range from 0 - 0.4
X = rand(30,30)*100+100; % Values range from 100 - 200
A_n = A / (max(A(:))/0.75) + 0.25; % "Normalize", with range 0.25 - 1
X_n = X / max(X(:)); % Normalize, range 0 - 1
colorRGB = NaN([size(A),3]); % preallocate
for ii = 1:size(A,1)
for jj = 1:size(A,2)
colorRGB(ii,jj,:) = hsv2rgb([A_n(ii,jj),1,X_n(ii,jj)]); % turn into RGB
end
end
imshow(colorRGB), % display image
colormap hsv; cb = colorbar();
In the example you can see that the colourbar covers the whole hsv range and has ticks from 0 - 1.
What I want it to be is showing only the upper 75% of the hsv range with ticks from 0 to max(A(:))
The correct colorbar assuming that max(A(:)) = 0.35 should look like this:
(you can see that I just cropped it, but that should not be necessary either)
In order to do that you need 2 things. First crop the colorbar, bu setting its limits. Secondly, change the text in the labels of the colobar, but to make sure they are in the rigth places, you also need to set the positions of them manually. Hopefully the code makes sense:
cb = colorbar();
set(cb, 'ylim', [25 100])
set(cb, 'XTick', [25:15:100]) % modify values if you preffer
set(cb,'XTickLabel',strsplit(num2str([0.25:0.15:1])));

What plotting software to use: 2D polar plot with unique data

I have my (example) data in the following format:
R_min R_max θ_min θ_min Zones
0 260 0 1.57 114
260 270 0 1.57 106
270 320 0 1.57 107
As you can see, I have "zones" (areas) that are created from R_min to R_max that sweep from theta_min to theta_max. Each row of data represents an area that I want to plot with a corresponding color based on the zone number. In this simple case, the data I show above would look like the following picture:
What plotting software should I use to accomplish this? I have been investigating the following options:
MATLAB. I am having trouble finding exactly what I need, but have found features like http://www.mathworks.com/help/symbolic/mupad_ref/plot-density.html?searchHighlight=plot%3A%3Adensity
Gnuplot. My issue with Gnuplot is the lack of documentation.
Are there other programs or a better way to compile my data to make my task-at-hand doable?
My real data set has thousands of rows of data and not nearly as simple as a quarter circle rainbow.
Here is one possible solution with gnuplot. That uses the circles plotting style to draw the overlapping wedges at the origin with a specified radius. That requires you to have your data sorted by descending maximum radius, and that you have no gaps.
Here is a possible script:
set xrange [0:350]
set yrange [0:350]
set size ratio -1
set style fill solid noborder
set palette defined (106 'blue', 107 'yellow', 114 'magenta')
set cbrange [106:114]
unset colorbox
plot 'test.txt' using (0):(0):2:($3*180/pi):($4*180/pi):5 with circles linecolor palette notitle
with the result (with 4.6.4):
Some more remarks:
The radius of the circles is given in units of the x-axis, but the y-axis isn't adapted accordingly. That's why you must set both xrange, yrange and even the ratio of the two axes with set size ratio -1.
Using the palette for coloring is one option, other options like using linecolor variable or linecolor rgb variable, are explained e.g. in gnuplot candlestick red and green fill.
On Unix systems, the sorting could also be done on-the-fly with e.g.
plot '< sort -r test.txt' ...
It's actually easy to do that with Matlab using simple trigonometry and the fill function:
% R_min R_max θ_min θ_min Zones
data = [
0 260 0 1.57 114
260 270 0 1.57 106
270 320 0 1.57 107];
% Define a color table, indexed by the "Zones" column
colors = {};
colors{114} = [1.0 0.0 0.5];
colors{106} = [0.7 0.0 1.0];
colors{107} = [1.0 1.0 0.0];
% Define the resolution of the plot (more points = more round)
nPoints = 100;
clf;
hold on;
for i = 1:size(data, 1)
% Extract the data from the i'th row. There's no need for this, you
% could access it directly below, but it makes the code more clean. :)
r_min = data(i,1);
r_max = data(i,2);
theta_min = data(i,3);
theta_max = data(i,4);
color = data(i, 5);
% First, get the sine and cosine between theta_min and theta_max
sin_theta = sin(linspace(theta_min, theta_max, nPoints));
cos_theta = cos(linspace(theta_min, theta_max, nPoints));
% Now, draw a semi-circle with radius = r_min and merge this
% semi-circle with another with radius = r_max, but reversed, so that
% it begins where the previous semi-circle ended.
x = [sin_theta * r_min sin_theta(end:-1:1) * r_max];
y = [cos_theta * r_min cos_theta(end:-1:1) * r_max];
% Draw the polygon.
fill(x,y, colors{color}, 'EdgeColor', colors{color});
end
hold off;
axis equal;
grid;
maxRadius = max(data(:,2));
axis([-maxRadius maxRadius -maxRadius maxRadius]);
Result:

Matlab: Missing labels in bar chart

With Matlab 2012 and 2013, I found that setting XTickLabel on a bar chart only works with up to 15 bars. If there are more bars, labels are missing, as shown below.
Plotting 15 bars:
N = 15;
x = 1:N;
labels = num2str(x', '%d');
bar(x);
set(gca, 'XTickLabel', labels);
Plotting 16 bars:
N = 16;
x = 1:N;
labels = num2str(x', '%d');
bar(x);
set(gca, 'XTickLabel', labels);
For N > 15, it will always only display 10 labels.
Does anyone else experience this? Any work-arounds? I need all labels because I am plotting discrete categories and not a continuous function.
This happens because the tick labels have to match the ticks themselves. In the example you gave with N = 16; and x = 1:N;, MATLAB automatically makes the following XTicks (on your and my machines, at least):
>> xticks = get(gca,'xtick')
xticks =
0 2 4 6 8 10 12 14 16 18
>> numel(xticks)
ans =
10
Just 10 ticks for the 16 different bars. Thus, when you run set(gca, 'XTickLabel', labels); with labels = num2str(x', '%d'); (16 labels), it gives the second figure you showed with the wrong labels and ticks before/after the bars (at positions 0 and 18).
To set a tick label for each bar, you also need to set the ticks to match:
set(gca,'XTick',x) % this alone should be enough
set(gca,'XTickLabel',labels);
Then you will get the desired result:
For whatever reason, 16 seems to be the magic number at which MathWorks decided XTicks should not be drawn for each bar, leaving it up to the user to set them if needed.

How to combine a custom color range with colorbar in MATLAB?

I'd like to have a surface plot like the one below, but with a proper colorbar.
This is my code:
[X,Y,Z] = peaks(30);
[maxval dummy] = max(Z(:));
[minval dummy] = min(Z(:));
crange = 1.5;
% red, yellow, green
cmap = [1 0 0; 1 1 0; 0 1 0];
colormap(cmap);
colors = zeros(size(Z));
colors(Z <= -crange) = 1; % red (1)
colors(Z > -crange & Z < crange) = 2; % yellow (2)
colors(Z >= crange) = 3; % green (3)
surf(X,Y,Z, colors);
axis([-3 3 -3 3 -10 10]);
%cbh = colorbar('YGrid','on');
%caxis([minval-0.1 maxval+0.1]);
%set(cbh,'YTick',[minval -crange crange maxval]);
So far I had no luck in adding a colorbar where the colors (green,yellow,red) are aligned according to my custom range (green[8 ... 1.5],yellow[1.5 ... -1.5], red[-1.5 ... -6.4]). Instead, when I uncomment the last three lines,
a colorbar with linearly aligned colors shows up and the colors in my plot are aligned according to the colorbar and not to my custom range.
Now, what I'd like to have is that the colors in the colorbar match my custom ticks and that the plot looks like in the first picture.
The problem is that you specify the colors of each point in the surf plot by yourself, so they're not related to the z-value as is by default. The colorbar therefor is constructed only based on the color numbers, being 1 to 3. These therefor also show up as the default ticks of the colorbar (before you change them.
As you found out, you can set the ticks manually, and in the same way you can 'cheat' and use yticklabels:
figure
colormap(cmap);
surf(X,Y,Z, colors);
axis([-3 3 -3 3 -10 10]);
cbh = colorbar('YGrid','on');
set(cbh,'ytick',linspace(1,3,4));
set(cbh,'yticklabel',arrayfun(#num2str,[minval -crange crange maxval],'uni',false));
Or another way is to simply use caxis, but then the colors of the plot are linearly defined by the minmax values. So with this you can't set your non-linear ranges.
Illustration:
figure
colormap(cmap);
surf(X,Y,Z);
axis([-3 3 -3 3 -10 10]);
caxis([minval-0.1 maxval+0.1]);
cbh=colorbar
set(cbh,'YTick',[minval -crange crange maxval]);
So after all, I think my first method (using yticklabels) is the only way of doing what you want.
I know this is a crazy old post, but it came up as I was looking for answers. And here would be my answer (which has to assume unfortunately that the graduations on the color bar are equal size).
So given that you have made the color map, which is only 3 colors, the next part is algebra. The caxis follows a simple formula which is determined by the how many colors are in the color bar, and your min and max range.
index = fix((C-cmin)/(cmax-cmin)*m)+1;
So index will refer to the index in the color map (m = 3 since that is the length of your colormap), and what I would do is make C= 1.5, decide if you want it symmetrical (you are solving for cmax and cmin and is easier if both are x), and make index=2 (since you only have 3 colors, that should mean solving for 1.5 would give you the cmin/cmax to set which would make 1.5 the cutoff between yellow and red. You should be able to set the display range value somewhere, which will set the colormap appropriately (though probably not the labels).
Sorry to reply to such an old post, but maybe this will help others.
I came across this question when finding solutions for a problem that I had. Anyway this question helped me to get a solution for my problem after making little modifications on written codes.
At the same time I like to suggest some changes for the code so that user can get the required plot with necessary colors and the appropriate color bar also.
Here is the code,
[X,Y,Z] = peaks(30);
[maxval dummy] = max(Z(:));
[minval dummy] = min(Z(:));
crange=1.5;
% red, yellow, green
cmap = [1 0 0; 1 1 0; 0 1 0];
colormap(cmap);
colors = zeros(size(Z));
colors(Z <= -crange) = minval-0.1; % red (1)
colors(Z > -crange & Z < crange) = crange; % yellow (2)
colors(Z >= crange) = maxval+0.1; % green (3)
surf(X,Y,Z, colors);
axis([-3 3 -3 3 -10 10]);
cbh = colorbar('YGrid','on');
caxis([minval-0.1 maxval+0.1]);
set(cbh,'YTick',[minval -crange crange maxval]);
Plot