Search code examples
matplotlibclippingcontourfimage-maskingimage-clipping

Masking/clipping part of a contour plot above a specified curve with python


I have a contour plot which is produced using Triangulation/TriInterpolation and contourf, shown in the image below. I also have the two curves plotted on either side of the contour which are the boundary for where data should not go past. As can be seen in the bottom left corner, the data is spilling over the left hand line (due to triangulation attempting to connect the triangles I'm sure) and I want to find a way to use the left hand line to force everything above it to be masked or clipped as I will be adding more data which will undoubtably do the same.

I have attempted using clipping through matplotlib.patches and through normal masking but can't seem to figure it out.

The data for the left line is as such:

leftx = [0.319,0.373,0.407,0.432,0.452,0.469,0.483,0.495,0.506,0.516,0.525,0.533,0.541,0.548,0.554,0.560,0.566,0.571,0.576,0.580,0.585,0.589,0.593,0.597,0.600,0.604,0.607,0.610,0.613,0.616]

lefty = [0.5,1.0,1.5,2.0,2.5,3.0,3.5,4.0,4.5,5.0,5.5,6.0,6.5,7.0,7.5,8.0,8.5,9.0,9.5,10.0,10.5,11.0,11.5,12.0,12.5,13.0,13.5,14.0,14.5,15.0]

and the grid for the triangulation/contourf was

xi = np.linspace(0, 1.2, 1000)
yi = np.linspace(0, 15.0, 1000)
Xi, Yi = np.meshgrid(xi, yi)
triang = tri.Triangulation(xdata,ydata)
interpolator = tri.LinearTriInterpolator(triang, zdata)
zi = interpolator(Xi, Yi)

How do I do this? Many thanks in advance! :)

Contour Plot


Solution

  • You can to create a clip_path similar to this post. The clip shape can be created with the left curve and the reverse of the right curve.

    Note that the clip_path needs to be added to the plot. This is needed to make sure all points get transformed to the correct axes. It can be drawn with color='none' to have it invisible.

    Here is some test code demonstrating how it could work in your situation:

    import matplotlib.pyplot as plt
    from matplotlib import path as mpath
    from matplotlib import patches as mpatches
    import numpy as np
    
    N = 100
    xdata = np.random.uniform(0, 1.2, N)
    ydata = np.random.uniform(0, 15, N)
    zdata = np.random.uniform(0, 90, N)
    
    leftx = np.array([0.319, 0.373, 0.407, 0.432, 0.452, 0.469, 0.483, 0.495, 0.506, 0.516, 0.525, 0.533, 0.541, 0.548, 0.554, 0.560, 0.566, 0.571, 0.576, 0.580, 0.585, 0.589, 0.593, 0.597, 0.600, 0.604, 0.607, 0.610, 0.613, 0.616])
    lefty = np.array([0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 5.5, 6.0, 6.5, 7.0, 7.5, 8.0, 8.5, 9.0, 9.5, 10.0, 10.5, 11.0, 11.5, 12.0, 12.5, 13.0, 13.5, 14.0, 14.5, 15.0])
    rightx = leftx + 0.6
    righty = lefty
    vertices = np.vstack([np.concatenate([leftx, rightx[::-1]]), np.concatenate([lefty, righty[::-1]])]).T
    poly_codes = [mpath.Path.MOVETO] + (len(vertices) - 1) * [mpath.Path.LINETO]
    path = mpath.Path(vertices, poly_codes)
    clip_patch = mpatches.PathPatch(path, facecolor='none', edgecolor='none')
    plt.gca().add_patch(clip_patch)
    
    cont = plt.tricontourf(xdata, ydata, zdata, levels=10, cmap='hot')
    for c in cont.collections:
        c.set_clip_path(clip_patch)
    
    plt.show()
    

    example plot