0

I am trying to adjust the color limits of a contour using contourf and caxis, but the number of colors in the contour does not update. Please see the example below.

Z is an array of values ranging from -170 to 80. I want to plot 20 contours of the values between -10 and 10. To do so, I use the following code:

h = figure('windowstyle', 'docked');
contourf(mesh_x, mesh_y, Z, 20)
colormap(jet(20))
colorbar
caxis([-10 10])

Below is a picture of the result. The number of contour lines is correctly updated (there are 20 of them), but the contour colors do not follow.

enter image description here

What should I change in the code to get the expected result?

Andrey Rubshtein
  • 20,795
  • 11
  • 69
  • 104
Iodestar
  • 198
  • 2
  • 13
  • What is the expected result? Looks fine to me... – David Jan 07 '15 at 23:31
  • Changing the color coding of `Z` with `caxis` **does not** update your isolines created before with `contourf`. You said `Z` range is [-170 80]. When you call `contourf` the function define 20 isolines in this range. You then restrict the color coding to the range [-10 10] so all the values of `Z` which are above or below this interval will use the saturated color (blue for all values of Z<-10, and dark red for all values of Z>10) regardless on which side of an isoline they are. – Hoki Jan 07 '15 at 23:57
  • I expect the values below -10 or above 10 to be in the saturated color, but I would also expect to see 20 different colors inside this range. Is there a way to update the isolines to match the caxis range? – Iodestar Jan 08 '15 at 00:56

2 Answers2

1

As said in the comment, the command caxis only update the colormap of the figure but will not modify your isolines previously created with contourf.

since I don't have your data I build my example on the function peaks, which I scale to be within range [-170 80]

%% // generate sample data set
x = linspace(0,1) ; [mesh_x , mesh_y] = meshgrid(x,x) ;
Z = peaks(100) ; Z(Z<0) = Z(Z<0) * -170./min(min(Z)) ; Z(Z>0) = Z(Z>0) * 80./max(max(Z)) ;

This, along with your current code, produce the following figure which indeed exhibit the same problem than in your example:

peaks170figure


If you want your 20 isolines to be within the range [-10 10], you have 2 options, with slightly different output.

Option 1: modify call to contourf

The function contourf has a parameter which allows you to specify where to place the isolines, so the simplest way is just to call contourf with a parameter which will specify 20 isolines in the range [-10 10]

%% // method 1 : specify isolevels explicitely
h = figure ;
isolevels = linspace(-10,10,20) ; 
contourf(mesh_x, mesh_y, Z, isolevels )
colormap(jet(20)) ; colorbar ; caxis([-10 10])

Which produces:

peaksmethod1

Now your isolines are evenly spread in the range you specified. I am not too sure why the values < -10 appear in white while the values>10 appear in the right saturated color.

edit: see the comment. To have the lowest contour filled you have to add an artificial isoline lower or equal to your lowest point in the surface. So if you replace your call to contourf by:

contourf(mesh_x, mesh_y, Z, [min(min(Z)) isolevels] ) %// use this if you want the lowest contour to be filled

This will produce the same output than the solution below


Option 2: Saturate the underlying dataset yourself

It may not be the most elegant option, but at least it gets rid of the "white patch" problem in the solution above. The idea is to create another Z2, which will be a copy of Z for the range [-10 10] but every value outside of this range will be snapped to the range limit.

%% // method 2 : saturate the data yourself
h = figure ;
Z2 = Z ; 
Z2(Z<=-10) = -10 ;  %// snap every value<-10 to the value: -10
Z2(Z>= 10) =  10 ;  %// snap every value>10 to the value: 10

contourf(mesh_x, mesh_y, Z2, 20)
colormap(jet(20)) ; colorbar ; caxis([-10 10])

This time your modify the underlying dataset to suit your need so you can call contourf the same way your called it initially. This will produce the following output:

peaksmanual

Hoki
  • 11,637
  • 1
  • 24
  • 43
  • my impression is that `contourf` doesn't specify colors according to `(Z >= array(i) && Z < array(i+1))`, but rather `(Z >= array(i))` where `array` is `LevelList` and then it plots the next contour over it. That is why in your case, all values of `Z` above `10` are plotted in the color with the maximum value, but all values of `Z` below `10` appear white because they are not in the `LevelList`. please correct me if i am wrong – Guddu Jan 08 '15 at 13:20
  • @Guddu. You tickled my curiosity and I went to dig in the `contourf` code (_Matlab R2013a_). I found a small paragraph of code which ***intentionally*** doesn't fill the blocks under the lowest contour line. It is even explained by a comment :`Don't fill contours below the lowest level specified in nv. To fill all contours, specify a value of nv lower than the minimum of the surface.`. So it seems the only way to get that filled is (1) add an extra isolevel at the min of the surface), (2) trick it with my option 2, or (3) overload `contourf` with a copy that does fill the lowest contour. – Hoki Jan 08 '15 at 13:35
  • Wow, thank you a lot for your detailed answer. I ended up manually saturating the data and obtained the desired result. – Iodestar Jan 08 '15 at 21:35
0

the magic field is LevelList field in contourf function. n is number of colors you want

x=0:0.01:1;
y=0:0.01:1;
[xx,yy]=meshgrid(x,y);
zz=xx.^2+yy.^2;

n=5;
tclist=linspace(min(zz(:)),max(zz(:)),n+1);
tclist=tclist(1:n);

[C,h]=contourf(xx,yy,zz,'LevelList',tclist);
colormap(jet(n))
colorbar
caxis([min(zz(:)) max(zz(:))]);
Guddu
  • 2,325
  • 2
  • 18
  • 23