The Woes of contourf patches, findobj, and Matlab 2014b.
Show older comments
Hello all, thanks in advance for help.
With the recent update to OS X Yosemite, and of course the required update to MATLAB 2014b, AND the graphics changes to 2014b, I am experiencing some problems with our legacy code.
Background: We have a script called cpatch.m (see below), which [before updating] used
findobj(gca,'type','patches');
on a contourf plot to recolor the patches so that they were a specific color corresponding to a specific contour interval (necessary for the accuracy of our science).
Problem: Contourf in 2014b creates an handle which no longer has any children. It used to be the case that hidden in the children, were the individual patches that one could recolor. Now, these patches do not exist, so one cannot recolor them. In MATLAB:
patches = findobj(gca,'type','patch')
returns
0x0 empty GraphicsPlaceholder array.
and hence is useless.
Ideal Solution: Can someone please tell me how to access the individual patches so that I can change their face color?
Code:
clev = 0:.2:1; % These are the contour levels
colors = bone(length(clev)-1);
cpt = [clev(1:end-1)' clev(2:end)'];
[C,H] = contourf(rand(10,10),clev);
patches = findobj(gca,'type','patch');
for ipatches=1:length(patches)
c = get(patches(ipatches),'cdata');
if length(c) <= 1
ii = find(cpt(:,1) <= c);
iwant = max(ii);
if length(iwant) >= 1
set(patches(ipatches),'facecolor',colors(iwant,:));
end
end
end
Please help! Thanks!
Answers (1)
Kelly Kearney
on 12 Jan 2015
Can I recommend contourfcmap? I believe it does exactly what your code used to do (assign precise colors to the shaded intervals in a contourf plot), and it works in R2014b.
If you look under the hood of that function, you can find the workaround to extract and recolor. Pre-2014b, contour objects were composed of patches. In 2014b, their underlying polygon components are TriangleStrips, rather than simple patches, and their properties are mostly hidden from the user. But you can still hack into it.
[X,Y,Z] = peaks;
[l, h] = contourf(X,Y,Z,4);
h.FacePrims(1).ColorData = uint8([255 0 0 255]'); % change first interval to red
12 Comments
Kellen
on 13 Jan 2015
Kelly Kearney
on 13 Jan 2015
Yes, the reverting-on-print thing seems to be pervasive in 2014b... If you try to make custom changes to certain objects in a way that "unlinks" the low-level object from it's original properties (such as changing the face color of a contour, or resizing a marker in a legend), that change gets undone when you call print (or anything that uses print, including saveas, export_fig, etc). Definitely a bug in my mind, though the Mathworks may not see it that way. Unfortunately, the actual reverting/relinking happens on a line of code in print.m that calls an inaccessible p-function, so I haven't been able to find a good way to stop it.
Two solutions:
- Save the figure using a screen capture (e.g. getframe). With the improved screen rendering in 2014b, this isn't as terrible an option as in older versions, but still not ideal, especially if you prefer to save to vector graphics.
- Don't rely on a Contour object. Instead, plot actual lines and patches, like the pre-2014b contourf used to. I offer this option in contourfcmap.m.
Kellen
on 13 Jan 2015
Kelly Kearney
on 13 Jan 2015
Interesting. You're right, using datenumbers along the x-axis wreaks havoc with the recolor method in 2014b... I'll have to look into that more. But if you use the calccontour method, neither the dates nor the color-reverting should be an issue.
If you do see color-reverting, the function must be reverting to the default recolor for some reason. Do you have the Mapping Toolbox? If so, perhaps it's a syntax issue? Can you show how you've set up your function call?
I just tested it again with the following example:
z = peaks(100);
t = datenum(2000,1,1) + (1:100);
y = 1:100;
clev = [-5 -3 -2:.5:2 3 5];
cmap = jet(12);
ax(1) = subplot(4,1,1);
[c,h] = contour(t,y,z, clev);
% pcolor(t,y,z); shading flat;
datetick('x');
title('contour');
ax(2) = subplot(4,1,2);
hc = contourfcmap(t,y,z,clev,cmap, 'method', 'calccontour');
datetick('x');
title('countourfcmap, calccontour');
try
ax(3) = subplot(4,1,3);
hc = contourfcmap(t,y,z,clev,cmap, 'method', 'recolor');
datetick('x');
catch
text(mean(get(ax(3),'xlim')), mean(get(ax(3),'ylim')), ...
'FAILED','fontsize', 30, 'horiz', 'center');
end
title('contourfcmap, recolor, x=dates');
ax(4) = subplot(4,1,4);
hc = contourfcmap(1:100,y,z,clev,cmap, 'method', 'recolor');
title('contourfcmap, recolor, x=1:nx');
set(ax(1:3), 'xlim', [t(1) t(end)]);
im = frame2im(getframe(gcf));
imwrite(im, 'test1.png', 'png');
export_fig('test2', gcf, '-png', '-nocrop');
Here's the result of the screenshot:

And here's the exported version:

As you can see, I'm replicating the bug with dates and recolor, and also experiencing the color-revert-on-print with the recolor version. But the version using calccontour properly renders both on screen and when printed.
Can you let me know if you see different results running this code snippet? And I'll look into the date bug.
Kellen
on 14 Jan 2015
Kelly Kearney
on 14 Jan 2015
I assume the portion that used to be NaNs is that upper triangular bit? When I look at your data, it seems those values are near 0 (-3.1415e-05). But you specify contour levels ranging from -45 to 50, so that value doesn't qualify as low (and instead is being shaded by the light blue you prescribed to the -5-0 interval).
I added the line
z(z == z(end,1)) = -999;
immediately after loading the .mat file, and now when plotted that region will be properly categorized a low.
As for the values lining up, I think you're just referring to the changed axis x-limits. That's just due to differing default behavior between contour and patch; contour sets the axis limits tight to the data, while patch allows a little space around the plotted objects. Adding
axis tight;
before you call datetick should fix that.
With those two small changes, I think the result is what you want:

-
I'll add the NaN problem to my bug list for contourfcmap.
Also, sorry the calccontour method is so slow for this particular example... the polygon-intersection calculation gets pretty resource-intensive with this many contour lines. Might be able to speed that up a lot if I call gpcmex directly rather than using polybool... or perhaps I need to rethink the algorithm there.
Kellen
on 21 Jan 2015
Kellen
on 21 Jan 2015
Kelly Kearney
on 22 Jan 2015
Edited: Kelly Kearney
on 22 Jan 2015
No problem. That example was very handy to work out a few remaining bugs in the function. NaNs are now handled properly (with all options), I changed the axis properties to mimic contour behavior with the calccontour method (tight axis limits, axis layered on top of patches), I found the source of the datenumber issue (TriangleStrip vertex coordinates are normalized to 1 when larger values are involved), and switching from polybool to directly calling gpcmex did indeed speed things up considerably.
Lauren Johnson
on 24 Mar 2015
Kelly,
You mentioned the datenumber issue in this comment. I was wondering if you found a way to override the normalization? I'm having issues with converting some code to 2014B, I end up getting contour plots that are blocks of data instead of curves. Even using the set(gca,'ydata',dateNumData) results in the block data.
Very informative post and answers. I will be looking at your contourfcmap code to see if it would be a good replacement for our current code. Thanks.
Kelly Kearney
on 25 Mar 2015
If by blocks of data, do you mean the contour lines seem to zig-zag in pixelated manner based on the underlying data resolution?
[x,y,z] = peaks(100);
dn = now;
clev = -8:8;
c = contourcs(x(1,:)+dn,y(:,1),z);
ax(1) = subplot(2,2,1);
contour(x+dn,y,z,clev);
ax(2) = subplot(2,2,2);
contourf(x+dn,y,z,clev);
ax(3) = subplot(2,2,3);
hold on;
arrayfun(@(X) plot(X.X, X.Y), c);
linkaxes(ax, 'x');

If so, then no, I haven't found a way around that behavior, which seems to be a bug in R2014b contour and contourf. Luckily, it does seem to be resolved in 2015a.
With contourfcmap, using the calccontour method will avoid the bug, since it plots lines using the output of contourc, and the zig-zaggy bug doesn't seem to manifest itself in countourc.
Lauren Johnson
on 27 Mar 2015
This zig-zag look is exactly what I am seeing. I will try in 2015a and avoid using 2014b. Thank you for your insight and quick response.
Categories
Find more on Creating, Deleting, and Querying Graphics Objects in Help Center and File Exchange
Products
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!