Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Matplotlib set_clip_path requires patch to be plotted

I've just discovered a nice way to create a Matplotlib filled contour plot clipped to an arbitrary polygonal region. The method requires calling set_clip_path(patch) on each PathCollection instance in the QuadContourSet returned by Matplotlib's contourf() function. MWE:

import matplotlib.pyplot as plt
import numpy as np
import matplotlib.patches as mpatches
import matplotlib.path as mpath

# some arbitrary data to plot
xx, yy = np.meshgrid(np.linspace(-5, 5, 20), np.linspace(-10, 10, 20), copy=False)
zz = np.sqrt(xx ** 2 + yy ** 2)

poly_verts = [
    (0, 0),
    (-4, 7),
    (-4, -7),
    (4, -7),
    (4, 7),
    (0, 0)
]
poly_codes = [mpath.Path.MOVETO] + (len(poly_verts) - 2) * [mpath.Path.LINETO] +
mpath.Path.CLOSEPOLY]

# create a Path from the polygon vertices
path = mpath.Path(poly_verts, poly_codes)

# create a Patch from the path
patch = mpatches.PathPatch(path, facecolor='none', edgecolor='k')

plt.figure()
ax = plt.gca()
cont = plt.contourf(xx, yy, zz, 50)

# add the patch to the axes
ax.add_patch(patch)  ## TRY COMMENTING THIS OUT
for col in cont.collections:
    col.set_clip_path(patch)

plt.show()

Resulting plot

I'm confused about one aspect: if I comment out the line that plots the patch, then none of the clipping works and I end up with a blank plot. I presume that when calling the set_clip_path method with a patch on the PathCollection, the patch must have been added to the axes, but I don't understand why. Setting edgecolor='none' for patch creation is a fine workaround, but where's the fun in that?

Any thoughts?

like image 569
Gabriel Avatar asked Sep 05 '14 14:09

Gabriel


1 Answers

If the patch is not added to the axes, is cannot know according to which transform it should be handled. By adding it to the axes, you implicitely set the transform to the data transformation of the axes it is added to.
I guess this necessity becomes clear if you imagine to have several axes on a figure. Then the mpatches.PathPatch could potentially be used for any of those axes.

You may indeed set the patch invisible by setting the face- and edgecolor to "none"

patch = mpatches.PathPatch(path, facecolor='none', edgecolor='none')

or turning it invisible alltogether,

patch = mpatches.PathPatch(path, visible=False)

In case you really want to get rid of adding the patch to the axes, you may set the required transform manually

patch = mpatches.PathPatch(path, transform=ax.transData)

for col in cont.collections:
    col.set_clip_path(patch)

In this case there would not be any need to add it to the axes.

like image 175
ImportanceOfBeingErnest Avatar answered Oct 04 '22 22:10

ImportanceOfBeingErnest