Skip to content

WCSAxes containing rasterized polygons displays axes frame in wrong position on figure unless grid is shown #12568

Closed

Description

@lpsinger

Description

I am trying to create a WCSAxes plot that has a huge number of polygon artists on it. I want to set the polygons to be rasterized so that the image loads quickly in PDF viewers. Much to my surprise, I find that adding even a single rasterized polygon to a WCSAxes plot causes the axes frame to be drawn in the wrong place on the figure.

Even more puzzling, adding a grid to the axes causes the frame to be drawn in the correct position!

I notice that as I change the rcparam figure.dpi, the position and size of the incorrectly-drawn frame changes.

Expected behavior

This is what the output looks like when there is a grid present. Observe that the frame is drawn correctly.

grid-True

Actual behavior

This is what the output looks like when there is no grid present. Observe that the frame is drawn with the wrong position and size on the figure, well offset from the axes themselves.

grid-False

Steps to Reproduce

Here is a minimal example to reproduce the bug.

import matplotlib.pyplot as plt

from astropy.wcs import WCS
from astropy.io import fits
from astropy.utils.data import get_pkg_data_filename

filename = get_pkg_data_filename('galactic_center/gc_msx_e.fits')

hdu = fits.open(filename)[0]
wcs = WCS(hdu.header)

for grid in [False, True]:
    fig, ax = plt.subplots(subplot_kw=dict(projection=wcs))

    ax.imshow(hdu.data, vmin=-2.e-5, vmax=2.e-4, origin='lower')
    ax.add_patch(plt.Polygon(
        [[-0.25, -0.25], [0.25, -0.25], [0.25, 0.25], [-0.25, 0.25]],
        transform=ax.get_transform('world'),
        rasterized=True))

    if grid:
        ax.grid(color='white', ls='solid')

    fig.savefig(f'grid-{grid}.pdf')

System Details

>>> import platform; print(platform.platform())
macOS-11.6-x86_64-i386-64bit
>>> import sys; print("Python", sys.version)
Python 3.8.12 (default, Nov 13 2021, 08:16:46) 
[Clang 12.0.5 (clang-1205.0.22.11)]
>>> import numpy; print("Numpy", numpy.__version__)
Numpy 1.21.4
>>> import erfa; print("pyerfa", erfa.__version__)
pyerfa 2.0.0.1
>>> import astropy; print("astropy", astropy.__version__)
astropy 5.0
>>> import scipy; print("Scipy", scipy.__version__)
Scipy 1.7.3
>>> import matplotlib; print("Matplotlib", matplotlib.__version__)
Matplotlib 3.5.0

Activity

  1. dstansby commented on Dec 19, 2021

    @dstansby
    Contributor

    Do you have a high-DPI screen (including a retina screen Mac)? This looks suspiciously like HiDPI issues we've been seeing recently (matplotlib/matplotlib#21931). If you use matplotlib < 3.5 is it still an issue?

  2. lpsinger commented on Dec 20, 2021

    @lpsinger
    ContributorAuthor

    I am using a Mac with a Retina display. However this is still broken on matplotlib 3.4.3.

  3. dstansby commented on Dec 20, 2021

    @dstansby
    Contributor

    I can reproduce this, and it's not present using matplotlib 3.3.4. Just bisecting this to a Matplotlib change now...

  4. dstansby commented on Dec 20, 2021

    @dstansby
    Contributor

    Bisects to matplotlib/matplotlib@b6a2739 from matplotlib/matplotlib#17159, so it's not a HiDPI issue, but (as said in the original report) related to Matplotlib raster output.

  5. pllim commented on Dec 21, 2021

    @pllim
    Member

    So... Is this a matplotlib bug, or something we have to fix here?

  6. dstansby commented on Dec 21, 2021

    @dstansby
    Contributor

    I think it's a Matplotlib bug? The issue seems to be a mismatch between the DPI used to layout PDF files (72) and the DPI of a figure (100 by default) that is used to produce rasterized elements. For now a workaround is to do fig.savefig(f'grid-{grid}.pdf', dpi=72), and I'll do some more digging in Matplotlib.

  7. dstansby commented on Dec 21, 2021

    @dstansby
    Contributor

    Some more digging: I think the issue is that various WCSAxes have their 'pixel' attributes set before draw time, but this is a Bad Idea because the pixel coordinates of a given artist depend on the figure DPI, which is only set in stone at draw time. Unfortunately that means it's an astropy issue 😞 . Will update when I've got something more.

  8. dstansby commented on Dec 22, 2021

    @dstansby
    Contributor

    It looks like there might actually be both astropy and Matplotlib bugs 🙈 . The astropy fix is in #12621, and I'll report back if/once I find a Matplotlib fix.

    With #12621 both spines, all ticks, and the y-axis labels in this example are drawn correctly, but not the x-axis labels because for some reason they're still being drawn with the wrong DPI...

  9. dstansby commented on Dec 22, 2021

    @dstansby
    Contributor

    I really got hooked by the rabbit hole here! There is not a Matplotlib bug (phew), but there is another WCSAxes bug that needs fixing. The solution is that all the custom draw() methods in WCSAxes need a @matplotlib.artist.allow_rasterization decorator to allow the correct DPI to be set when each individual component is drawn.

    This is however going to need some technically breaking API changes to draw() methods in WCSAxes, as the MPL decorator only works with the signature draw(self, renderer), whereas some WCSAxes methods have more arguments which will need to be removed. So I think that the way forward here is:

    • Setting the figure DPI to 72 hides this issue. This forces the DPI of rasterized elements to be 72 DPI.
    • Hopefully this will get fixed in the next major release of astropy, allowing the DPI of the rasterized elements to be something other than 72
  10. pllim commented on Dec 22, 2021

    @pllim
    Member

    I think your proposal is very reasonable. Thanks for investigating!

    What do you think, @astrofrog , @Cadair , or @larrybradley ?

  11. lpsinger commented on Dec 23, 2021

    @lpsinger
    ContributorAuthor

    I, for one, require rasterized elements at much higher resolutions than 72 DPI. We're not talking of hard-coding that in wcsaxes, are we?

  12. dstansby commented on Dec 23, 2021

    @dstansby
    Contributor

    Sorry for not being clear - setting to 72 DPI is the only case where this isn't currently an issue. I'm working towards solving the bugs that will allow you to use much higher resolutions than 72 DPI.

  13. dstansby commented on Dec 30, 2021

    @dstansby
    Contributor

    Well here's the full fix: main...dstansby:wcsaxes-rasterize - it was certainly a fun rabbit hole! I created another issue to track all of these changes, which is #12672. My plan is to tackle several PRs and associated reviews one at a time instead of trying to get a large PR merged.

  14. dstansby commented on Jan 10, 2024

    @dstansby
    Contributor

    This is fixed in the latest version of astropy, so can be closed 🥳

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

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions