Search code examples
matlabplotcontourfill

multiple matlab contour plots with one level


I have a number of 2d probability mass functions from 2 categories. I am trying to plot the contours to visualise them (for example at their half height, but doesn't really matter).

I don't want to use contourf to plot directly because I want to control the fill colour and opacity. So I am using contourc to generate xy coordinates, and am then using fill with these xy coordinates.

The problem is that the xy coordinates from the contourc function have strange numbers in them which cause the following strange vertices to be plotted.

enter image description here

At first I thought it was the odd contourmatrix format, but I don't think it is this as I am only asking for one value from contourc. For example...

contourmatrix = contourc(x, y, Z, [val, val]);
h = fill(contourmatrix(1,:), contourmatrix(2,:), 'r');

Does anyone know why the contourmatrix has these odd values in them when I am only asking for one contour?

UPDATE:

My problem seems might be a failure mode of contourc when the input 2D matrix is not 'smooth'. My source data is a large set of (x,y) points. Then I create a 2D matrix with some hist2d function. But when this is noisy the problem is exaggerated... enter image description here

But when I use a 2d kernel density function to result in a much smoother 2D function, the problem is lessened... enter image description here

The full process is a) I have a set of (x,y) points which form samples from a distribution b) I convert this into a 2D pmf c) create a contourmatrix using contourc d) plot using fill


Solution

  • Your graphic glitches are because of the way you use the data from the ContourMatrix. Even if you specify only one isolevel, this can result in several distinct filled area. So the ContourMatrix may contain data for several shapes.


    simple example:

    isolevel = 2 ;
    [X,Y,Z] = peaks ;
    [C,h] = contourf(X,Y,Z,[isolevel,isolevel]);
    

    Produces:

    contourf output


    Note that even if you specified only one isolevel to be drawn, this will result in 2 patches (2 shapes). Each has its own definition but they are both embedded in the ContourMatrix, so you have to parse it if you want to extract each shape coordinates individually.

    To prove the point, if I simply throw the full contour matrix to the patch function (the fill function will create patch objects anyway so I prefer to use the low level function when practical). I get the same glitch lines as you do:

    xc = X(1,:) ;
    yc = Y(:,1) ;
    c = contourc(xc,yc,Z,[isolevel,isolevel]);
    hold on
    hp = patch(c(1,1:end),c(2,1:end),'r','LineWidth',2) ;
    

    produces the same kind of glitches that you have:

    not parsed


    Now if you properly extract each shape coordinates without including the definition column, you get the proper shapes. The example below is one way to extract and draw each shape for inspiration but they are many ways to do it differently. You can certainly compact the code a lot but here I detailed the operations for clarity.

    The key is to read and understand how the ContourMatrix is build.

    parsed = false ;
    iShape = 1 ;
    while ~parsed
        %// get coordinates for each isolevel profile
        level   = c(1,1) ; %// current isolevel
        nPoints = c(2,1) ; %// number of coordinate points for this shape
    
        idx = 2:nPoints+1 ; %// prepare the column indices of this shape coordinates
        xp = c(1,idx) ;     %// retrieve shape x-values
        yp = c(2,idx) ;     %// retrieve shape y-values
        hp(iShape) = patch(xp,yp,'y','FaceAlpha',0.5) ; %// generate path object and save handle for future shape control.
    
        if size(c,2) > (nPoints+1)
            %// There is another shape to draw
            c(:,1:nPoints+1) = [] ; %// remove processed points from the contour matrix
            iShape = iShape+1 ;     %// increment shape counter
        else
           %// we are done => exit while loop
           parsed  = true ;
        end
    end
    grid on
    

    This will produce:

    enter image description here