Thursday, May 17, 2012

multiple colormaps in matlab

How do I use multiple colormaps in a single matlab figure? This is probably one of the biggest frustrations that almost all matlab users have to deal with. I would argue that this shortcoming of matlab --- combined with the default jet colormap --- have led to an entire generation of ugly scientific figures.

I used to use freezeColors and cbfreeze to make plots and colorbars with different colormaps. However, now that I use export_fig regularly, these do not work. (The incompatibility has to do with different renderers. Export_fig uses the painters renderer (aka. "the good renderer") but freezeColors does not allow the painters renderer to be used.)

My (super awesome) officemates, Nick and Noel, have come up with a solution for this problem. They use the fact that contourf essentially plots separate patch objects for each level of the colormap. They extract the coordinates from contourf and then manually plot each layer as a patch object with a specified color. This way, they avoid using a colormap entirely when they plot the figure. Sounds more complicated than it is. Here's an example of some code that Nick wrote. (Hopefully, this will be turned into a function soon, but for now, you can use this as inspiration.)


cvec=-.5:.5:10.5;
%make a "colormap"
tcmap=flipud(cbrewer('div', 'RdYlBu',length(cvec),'linear'));
%contourf it
[con conhand]=contourf(rgrid/1000,grdDepths(stind:end),TEMP_W_MASK,cvec,'linecolor','none');hold on

%get the patch object handles:
p=get(conhand,'children');
thechild=get(p,'CData');   
cdat=cell2mat(thechild);
%loop through and manually set facecolor of each patch to the colormap you  made:
for i=1:length(cvec)
   set(p(cdat==cvec(i)),'
Facecolor',tcmap(i,:))  
end
%%%%%%%%%%
%now you need to make a fake colorbar using the same trick: 

%define the colorbar axes location just above the subplot you are working with:
lbwh=get(gca,'Position'); 

Tcb=axes('Position',[lbwh(1) (lbwh(2)+lbwh(4)+.005) lbwh(3) .04]);

%contourf your fake colorbar: 

[con conhand]=contourf(cvec',[0 1],[cvec; cvec],cvec,'linecolor','none');
p=get(conhand,'children');
thechild=get(p,'CData');   
cdat=cell2mat(thechild);
% AGAIN, loop through and manually set facecolor of each patch to the colormap you  made (this makes a horizontal colorbar):
for i=1:length(cvec)
   set(p(cdat==cvec(i)),'
Facecolor',tcmap(i,:))  
end
%set the colorbar axes to whatever you want
set(gca,'yticklabel',[],'xaxislocation','top','xtick',[0:3:9])
 
 
I used a variation of this code to make some figures and it worked great. Here's an example:
 

2 comments:

  1. This was hugely helpful! Thanks. I've created a function called contourfjw.m to bundle this. It's at http://marine.rutgers.edu/wilkin/wip/matlab for anyone interested.

    One thing I encountered with some data sets (not all - for reasons I could not fathom) is that CDAT can have a last entry that does not exactly match a contour level, and it then does not get assigned a new color because cdat==cvec(i) is never true. I changed the logic to find a "nearest" color table entry instead.

    ReplyDelete