4

I have a filled contour plot, which I wish to save as an .svg or .pdf file. The following is a simplified example. I want to rasterize the contour plot itself (the colorful part!), while keeping everything else (all axes, labels etc.) as vector graphics.

import numpy as np
import matplotlib.pylab as plt

x = np.linspace(0, 2*np.pi, 100)
y = np.linspace(0, 2*np.pi, 100)
xi, yi = np.meshgrid(x, y)
zi = np.cos(xi)**2 + np.sin(yi)**2

plt.figure()
plt.contourf(xi, yi, zi, rasterized=True)
plt.savefig('fig.svg', dpi=100)

Output

However, when I inspect fig.svg or open it for editing in Inkscape (I am able to ungroup the filled contour into vector shapes) it is clear that rasterization has not worked!

That's fine for such a simple plot, but if my plot has a higher number of contour levels (below) the vector image will need many many curves and the filesize would be much bigger.

plt.close()
plt.figure()
plt.contourf(xi, yi, zi, 100, rasterized=True)
plt.savefig('fig.svg', dpi=100)

enter image description here

Can someone please suggest a solution and explain why this rasterized=True flag has not done what I require?

feedMe
  • 3,431
  • 2
  • 36
  • 61
  • `contourf()` returns a [`QuadContourSet`](https://matplotlib.org/api/contour_api.html#matplotlib.contour.QuadContourSet) object. This object, unlike objects descending from the `Artist` class does not have a *rasterized* property. – Diziet Asahi Dec 07 '17 at 20:55

1 Answers1

4

I just found this is a duplicate of this question.

Using rasterized=True as argument to contour or contourf should show a

UserWarning: The following kwargs were not used by contour: 'rasterized'

In order to rasterize a contour plot, you need to rasterize its individual parts, i.e.

cs = plt.contour(...) 
for c in cs.collections:
    c.set_rasterized(True)

The example from the question would hence look like

import numpy as np
import matplotlib.pylab as plt

x = np.linspace(0, 2*np.pi, 100)
y = np.linspace(0, 2*np.pi, 100)
xi, yi = np.meshgrid(x, y)
zi = np.cos(xi)**2 + np.sin(yi)**2

plt.figure()
cs = plt.contourf(xi, yi, zi)

for c in cs.collections:
    c.set_rasterized(True)

plt.savefig('fig.svg', dpi=100)
ImportanceOfBeingErnest
  • 321,279
  • 53
  • 665
  • 712
  • I do not see the UserWarning you mentioned. I'm using python 2.7.13 and matplotlib 2.0.2 within conda 4.3.30. – feedMe Dec 08 '17 at 08:10
  • Actually, your suggestion (of looping through cs.collections) has not worked for me. Did you test it? I have had more luck with the zorder method on https://stackoverflow.com/questions/37020842/reducing-size-of-vectorized-contourplot but I need to tweak that to avoid the axes and labels themselves being rasterized. – feedMe Dec 08 '17 at 08:21
  • Yes the method here is working for me. I tested with matplotlib version 2.1, but since in the duplicate it is reported to be working as well, it should already be good with version 1.5. In how far is it not working? – ImportanceOfBeingErnest Dec 08 '17 at 08:36
  • It doesn't work insomuch as when I open the SVG file in Inkscape I can see that the contours are still individual vector shapes. I have upgraded to matplotlib 2.1.0 and still the same. I am using `cs = plt.contourf(xi, yi, zi, 100) for c in cs.collections: c.set_rasterized(True) plt.savefig('original.svg', dpi=100)` – feedMe Dec 08 '17 at 08:49
  • The individual contours would still be individual items in the svg file, but they would be rasterized. So you already gain a factor of 4 in filesize for the code above run with `levels=100`. – ImportanceOfBeingErnest Dec 08 '17 at 08:54
  • Oh you are correct, they are individual raster shapes, not vectors! Sorry I missed that. I don't know why but I just expected them to be combined into one raster already. I don't suppose there is a quick, inbuilt way to combine all those rasters into one? As it is, the zorder method does that anyway and so is a better solution, I think. – feedMe Dec 08 '17 at 09:02
  • Yes, indeed. Rasterizing anything below a certain zorder will give you a single raster image. Using negative zorder should leave the axes vectorized, just as in the linked answer, correct? – ImportanceOfBeingErnest Dec 08 '17 at 09:06
  • Yes, the axes are vectorized as in the example! Again, I was expecting/wanting something slightly different, i.e. editable text labels. As it is, the text is vectorized but as paths. I have now added `plt.rcParams['svg.fonttype'] = 'none'` to my script as suggested here https://stackoverflow.com/questions/14600948/matplotlib-plot-outputs-text-as-paths-and-cannot-be-converted-to-latex-by-inks It seems everything is as I wanted now. Thanks! – feedMe Dec 08 '17 at 09:24