Skip to content

contourf workaround#7261

Description

@rcomer

馃摪 Custom Issue

iplt.contourf has a longstanding workaround to improve the look of antialiased plots.

iris/lib/iris/plot.py

Lines 1134 to 1185 in cefe4b7

# Matplotlib produces visible seams between anti-aliased polygons.
# But if the polygons are virtually opaque then we can cover the seams
# by drawing anti-aliased lines *underneath* the polygon joins.
if hasattr(result, "get_antialiased"):
# Matplotlib v3.8 onwards.
antialiased = any(result.get_antialiased())
# Figure out the colours and alpha level for the contour plot
colors = result.get_facecolor()
alpha = result.alpha or colors[0][3]
# Define a zorder just *below* the polygons to ensure we minimise any boundary shift.
zorder = result.zorder - 0.1
else:
antialiased = result.antialiased
# Figure out the alpha level for the contour plot
alpha = result.alpha or result.collections[0].get_facecolor()[0][3]
colors = [c[0] for c in result.tcolors]
# Define a zorder just *below* the polygons to ensure we minimise any boundary shift.
zorder = result.collections[0].zorder - 0.1
# If the contours are anti-aliased and mostly opaque then draw lines under
# the seams.
if antialiased and alpha > 0.95:
levels = result.levels
if result.extend == "neither":
levels = levels[1:-1]
colors = colors[:-1]
elif result.extend == "min":
levels = levels[:-1]
colors = colors[:-1]
elif result.extend == "max":
levels = levels[1:]
colors = colors[:-1]
else:
colors = colors[:-1]
if len(levels) > 0 and np.nanmax(cube.data) > levels[0]:
axes = kwargs.get("axes", None)
contour(
cube,
levels=levels,
colors=colors,
antialiased=True,
zorder=zorder,
coords=coords,
axes=axes,
)
# Restore the current "image" to 'result' rather than the mappable
# resulting from the additional call to contour().
if axes:
axes._sci(result)
else:
plt.sci(result)

I believe matplotlib/matplotlib#32256 will make this workaround redundant for its stated purpose from Matplotlib v3.12 馃帀

However, the workaround also affects plots saved to pdf. Adapting the code from test_mapping.py::TestMappingSubRegion::test_simple

import cartopy.crs as ccrs
import iris
import iris.plot as iplt
import matplotlib.pyplot as plt


cube = iris.load_cube('[path-to-iris-test-data]/test_data/PP/aPProt1/rotatedMHtimecube.pp')
cube = cube[0][::10][::10]

plt.figure(layout='constrained')

plt.subplot(221)
plt.title("Default")
iplt.contourf(cube)
plt.gca().coastlines("110m")

# Second sub-plot
plt.subplot(222, projection=ccrs.Mollweide(central_longitude=120))
plt.title("Molleweide")
iplt.contourf(cube)
plt.gca().coastlines("110m")

# Third sub-plot (the projection part is redundant, but a useful
# test none-the-less)
ax = plt.subplot(223, projection=iplt.default_projection(cube))
plt.title("Native")
iplt.contour(cube)
ax.coastlines("110m")

# Fourth sub-plot
ax = plt.subplot(2, 2, 4, projection=ccrs.PlateCarree())
plt.title("PlateCarree")
iplt.contourf(cube)
ax.coastlines("110m")

plt.savefig('test.pdf')

Running with current Iris main, we get

test_workaround.pdf

If I remove the workaround from Iris I get

test_no_workaround.pdf

Note that

  • Without the workaround, we do see seams between the filled contours, which may be undesirable to some users.
  • With the workaround, the right-hand plots show the ends of the added line contours, which also doesn't seem desirable.

As far as I understand image processing (not much), I do not think aliasing is relevant for pdf rendering. So I do not think it would make sense to keep the current workaround triggered by the antialiased keyword just for the pdf case (and possibly other vector graphics cases that I haven't checked).

Options

  1. Factor out the workaround into a public function, so that pdf users who value the old behaviour can opt-in and apply it directly.
  2. Encourage users to pass rasterized=True when they call contourf. Then the contours get converted to an image by the agg renderer (benefitting from matplotlib/32256) and the image is embedded into the pdf. This would mean users have to think about dpi to get a good quality. Here, I passed dpi=300 to savefig, and am using the branch from matplotlib/32256: test_no_workaround_rasterized.pdf
  3. Both 1 and 2 - i.e. factor out the workaround and also document the pros and cons of the two options so users can make an informed choice.
  4. Leave as it. I think this is the worst option because
    1. As stated above, having it triggered by the antialiased keyword doesn't really make sense for pdf
    2. We will be calling redundant code in many cases
    3. Silently creating and adding an extra artist to the axes just doesn't smell good in general
    4. Since our image tests all save to png, they likely wouldn't show any benefit of the workaround any more. We would at least need to find a different way to cover this.
  5. Anything else? Presumably contourf plots made outside of Iris have either been living with the seams or using some other workaround for a long time.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions