Home > OS >  How to create a color bar with two colors and a specific breakpoint Matlab
How to create a color bar with two colors and a specific breakpoint Matlab

Time:01-13

For the final 3D surface output, I need to show a legend of two colors with X>breakpoint being blue and Y<breakpoint being green. I have been able to graph the two colored plot, but am having a hard time figuring out how to split up the legend according to my breakpoint.

I have been using the colormap tool but have not been successful as it is still not using my defined breakpoint. The colors need to be two defined regions with no shading.

surf(xa,ya,Profile,'EdgeColor','none')
breakpoint = 1;
colors = [0 0 1; 0 1 1];
colormap(colors);
colorbar

CodePudding user response:

One way to get an only 2 colors surface is to use the Color argument of the surf(...) function.

You can build a color matrix ColorData, the same size as your data, with only 2 values:

  • where Profile < breakpoint => ColorData=0
  • where Profile >= breakpoint => ColorData=1

Then if you plot the surface with the color argument specified, and apply a 2 colors colormap, all the levels are going to adjust themselves.

However, if we leave it like that the colorbar ticks are now invalid, they go from 0 to 1 instead of showing the full range of data. It's ok though, you can create a set of meaningful labels and overide the default one.

In code it looks like (Thank you Wolfie for the demo data) :

% Set up a quick surface ( Thank you Wolfie :-) )
[xa,ya] = meshgrid( pi:0.1:2*pi, 0:0.1:2*pi );
Profile = cos(xa) * 2   sin(ya)   1;

%% define colormap and breakpoint
cmap = [0 1 0 ; 0 1 1];    
breakpoint = 1 ;

%% create a color matrix with only 2 values:
%   where Profile < breakpoint => ColorData=0
%   where Profile > breakpoint => ColorData=1
ColorData= zeros(size(Profile)) ;
ColorData(Profile>=breakpoint) = 1 ;

%% Plot the surface, specifying the color matrix we want to have applied
hs = surf( xa, ya, Profile, ColorData ) ;
colormap(cmap) ;
hb = colorbar ;

%% Now adjust colorbar ticks and labels
cticks = [0.25 0.5 0.75] ; % positions of the ticks we keep

% build labels
bpstr = num2str(breakpoint) ;
cticklabels = {['<' bpstr] ; bpstr ; ['>' bpstr]}

% apply
hb.Ticks = cticks ;
hb.TickLabels = cticklabels ;

The result for breakpoint=1 and breakpoint=1.75 are shown below:

enter image description here

The downside of that method is that on the colorbar the breakpoint is always going to sit in the middle of the colorbar, even if your actual breakpoint is closer to the lower or upper boundary of your data values.

CodePudding user response:

You need to give a colour map with more points, specifically with the "middle colour" of the map centred around your breakpoint, but with that colour being in the correct point of the colour map relative to the minimum and maximum values of your surface.

So if your desired breakpoint is 1/4 of the way between the min and max values of the surface, you could have a colour map with 100 rows where the 25th row contains the middle/breakpoint colour.

You can achieve this by interpolating from a an array including your breakpoint to something with consistent intervals. Please see the commented code below

% Set up a quick surface
[xa,ya] = meshgrid( pi:0.1:2*pi, 0:0.1:2*pi );
prof = cos(xa) * 2   sin(ya)   1;
figure(1); clf;
surf( xa, ya, prof );

% Set the breakpoint value
breakpoint = 1;
% Get the min and max values of the mesh, need this for scaling
minp = min( prof(:) );
maxp = max( prof(:) );
% Set up the colour map from a start and end colour
cstart = [0,0,1];
cend = [0,1,1];
% The average colour should happen at the breakpoint, so calculate it
cavg = mean( [cstart; cend] );
% Set up an interpolation, from our non-uniform colour array including the
% breakpoint to a nice evenly spaced colour map which changes the same
colours = [cstart; cavg; cend];
breakpoints = [minp; breakpoint; maxp];
colours = interp1( breakpoints, colours, linspace(minp,maxp,100) );

% Set the colour map
colormap( colours );
colorbar;

If you want a "sharp" delineation instead of a gradient then you can change the settings for interp1 to use previous instead of linear interpolation

colours = interp1( breakpoints, colours, linspace(minp,maxp,100), 'previous', 'extrap' );

Plot for breakpoint = 2

breakpoint = 2 plot

Plot for breakpoint = -1

breakpoint = -1 plot

plot for breakpoint = 1 but with nearest instead of linear interp

plot with sharp change in colour

You could condense the colour map generation part of the code slightly, but I think this makes it a bit less clear what's going on

% Set the breakpoint value
breakpoint = 1;
% Get the min and max values of the mesh, need this for scaling
minp = min( prof(:) );
maxp = max( prof(:) );
% Get the interpolated ratio of one colour vs the other
ratio = interp1( [minp,breakpoint,maxp], [0,0.5,1], linspace(minp,maxp,100) ).';
% Create colour map by combining two colours in this ratio
colours = [0,0,1].*(1-ratio)   [0,1,1].*ratio;

CodePudding user response:

You can center your color map around your breakpoint using caxis or ax.CLim, then reset the colorbar axes using its Limits property.

[X,Y,Z] = peaks(50);
surf(X,Y,Z)
colormap([0 1 0;0 1 1])
breakpoint = 3;

% This
caxis([-.1 .1] breakpoint)

% Or this
% ax = gca;
% ax.CLim = [-.1 .1] breakpoint;

cb = colorbar;
cb.Limits = [min(Z,[],'all'),max(Z,[],'all')];
  • Related