cds-astro / mocpy

Python library to easily create and manipulate MOCs (Multi-Order Coverage maps)
https://cds-astro.github.io/mocpy/
BSD 3-Clause "New" or "Revised" License
59 stars 33 forks source link

Multiple Subplotting Issue/help #139

Closed duytnguyendtn closed 3 months ago

duytnguyendtn commented 3 months ago

Hello mocpy maintainers!

I was wondering if I could get some help on plotting mocs in matplot lib. I was following the example on astropy table plotting and was trying to extend it. When a colleague tried to extend the figure to include multiple mocs, the field of view got cut off and started looking weirdly. I can reproduce the issue by just taking the example and looping it a few times:

Looped moc code ``` import matplotlib.pyplot as plt from astropy import units as u from astropy.coordinates import Angle, SkyCoord from astropy.visualization.wcsaxes.frame import EllipticalFrame from astroquery.vizier import Vizier from mocpy import MOC, WCS viz = Vizier(columns=["*", "_RAJ2000", "_DEJ2000"]) viz.ROW_LIMIT = -1 # sets the limit to infinity table = viz.get_catalogs("I/293/npm2cros")[0] moc = MOC.from_lonlat( table["_RAJ2000"].T * u.deg, table["_DEJ2000"].T * u.deg, max_norder=6, ) fig = plt.figure(dpi=400, figsize=(10,20)) loop_range = range(1,4) for index in loop_range: with WCS( fig, fov=160 * u.deg, center=SkyCoord(0, 0, unit="deg", frame="galactic"), coordsys="galactic", rotation=Angle(0, u.degree), projection="AIT", ) as wcs: ax = fig.add_subplot(max(loop_range), 1, index, projection=wcs, frame_class=EllipticalFrame) moc.fill( ax=ax, wcs=wcs, edgecolor="teal", facecolor="orange", linewidth=1.0, fill=True, alpha=0.8, ) moc.border(ax=ax, wcs=wcs, color="k") ax.grid(True) plt.tight_layout() ``` ![image](https://github.com/cds-astro/mocpy/assets/25206008/c592aae7-2a14-42d5-bd94-fe88993cbeff)

If I adjust the figsize to be square, it fills the entire frame, but still cuts off the data:

figsize=(20,20) ![image](https://github.com/cds-astro/mocpy/assets/25206008/4ef2641c-3bc3-4fe4-a28d-64a6097e524b)

I can't tell if the WCS projection is warping it for some reason, or if I'm doing something wrong with the plotting. Help would be greatly appreciated!

Thanks, Duy

ManonMarchand commented 3 months ago

Hi!

I would advise to use your own WCS ( that you can do with astropy) for this use case, as our wrapper only takes one float for the field of view. It's as if it was assumed that the figure should be a square.

I'm not really sure of the reasoning behind this choice that we made.

ManonMarchand commented 3 months ago

Marking this as a bug while investigating. However, with a proper WCS from astropy rather than our automatically generated one, you should be able to get subplots without issues.

ManonMarchand commented 3 months ago

Hello,

Here is a modified version of your script that does not use our simple WCS generation:

import matplotlib.pyplot as plt
from astropy import units as u
from astropy.coordinates import Angle, SkyCoord
from astropy.visualization.wcsaxes.frame import EllipticalFrame
from astropy.wcs import WCS
from astroquery.vizier import Vizier
from mocpy import MOC

viz = Vizier(columns=["*", "_RAJ2000", "_DEJ2000"])
viz.ROW_LIMIT = -1  # sets the limit to infinity
table = viz.get_catalogs("I/293/npm2cros")[0]

moc = MOC.from_lonlat(
    table["_RAJ2000"].T * u.deg,
    table["_DEJ2000"].T * u.deg,
    max_norder=6,
)

size = 324 # size of x axis (has to be = 0 % 4 for Aitoff)

wcs = WCS(
    {
        "naxis": 2,
        "naxis1": size,
        "naxis2": size / 2,
        "crpix1": size / 2 + 0.5, # wcs is defined on half pixels
        "crpix2": size / 4 + 0.5,
        "cdelt1": -1, # change this to change resolution (should keep ratio naxis/cdelt constant though)
        "cdelt2": 1,
        "ctype1": "GLON-AIT",
        "ctype2": "GLAT-AIT",
    },
)

fig = plt.figure()
loop_range = range(1,4)
for index in loop_range:

    ax = fig.add_subplot(max(loop_range), 1, index, projection=wcs, frame_class=EllipticalFrame)

    moc.fill(
        ax=ax,
        wcs=wcs,
        edgecolor="teal",
        facecolor="orange",
        linewidth=1.0,
        fill=True,
        alpha=0.8,
    )
    moc.border(ax=ax, wcs=wcs, color="k")

    # we set the axis off by half a pixel otherwise they are slightly outside the sphere
    # see astropy issue for the half-pixel trick https://github.com/astropy/astropy/issues/10201
    ax.set_xlim(-wcs.low_level_wcs.wcs.cdelt[0] / 2, size - wcs.low_level_wcs.wcs.cdelt[0] / 2)
    ax.set_ylim(-wcs.low_level_wcs.wcs.cdelt[1] / 2, size / 2 - wcs.low_level_wcs.wcs.cdelt[1] / 2)
    ax.set_aspect(1.0)

    ax.grid(True)

plt.tight_layout()
plt.show()

image

The documentation will change in the next version to suggest using native astropy WCS for all-sky plots. We also added the possibility to give a tuple of angles rather than a single one (which lead to always square views) in our own WCS class (see commit https://github.com/cds-astro/mocpy/commit/e545e13787d53b945f7c88dc7de23264e74f8c9f)

Thanks for opening the issue, hope this helps

ManonMarchand commented 3 months ago

I'll close the issue for now, but don't hesitate to re-open or to write a new one if you still have issues with plotting MOCs