Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cubeviz to use wcs1d-fits from specutils to write out fitted cube #2094

Merged
merged 1 commit into from
Jul 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ Cubeviz

- Spectral Extraction: renamed ``collapse_to_spectrum(...)`` to ``extract(...)``. [#2859]

- Custom Spectrum1D writer format ``jdaviz-cube`` is removed. Use ``wcs1d-fits`` from
``specutils`` instead. [#2094]

Imviz
^^^^^

Expand Down
2 changes: 1 addition & 1 deletion docs/cubeviz/export_data.rst
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ where the mask (if available) is as defined in

.. code-block:: python

mydata.write("mydata.fits", format="jdaviz-cube")
mydata.write("mydata.fits", format="wcs1d-fits", hdu=1)


.. _cubeviz-export-model:
Expand Down
66 changes: 0 additions & 66 deletions jdaviz/configs/cubeviz/helper.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
import numpy as np
from astropy.io import fits
from astropy.io import registry as io_registry
from specutils import Spectrum1D
from specutils.io.registers import _astropy_has_priorities

from jdaviz.core.events import SnackbarMessage
from jdaviz.core.helpers import ImageConfigHelper
from jdaviz.configs.default.plugins.line_lists.line_list_mixin import LineListMixin
Expand Down Expand Up @@ -166,63 +160,3 @@ def get_aperture_photometry_results(self):

"""
return self.plugins['Aperture Photometry']._obj.export_table()


# TODO: We can remove this when specutils supports it, i.e.,
# https://github.com/astropy/specutils/issues/592 and
# https://github.com/astropy/specutils/pull/1009
# NOTE: Cannot use custom_write decorator from specutils because
# that involves asking user to manually put something in
# their ~/.specutils directory.

def jdaviz_cube_fitswriter(spectrum, file_name, **kwargs):
"""This is a custom writer for Spectrum1D data cube.
This writer is specifically targetting data cube
generated from Cubeviz plugins (e.g., cube fitting)
with FITS WCS. It writes out data in the following format
(with MASK only exist when applicable)::

No. Name Ver Type
0 PRIMARY 1 PrimaryHDU
1 SCI 1 ImageHDU (float32)
2 MASK 1 ImageHDU (uint16)

The FITS file generated by this writer does not need a
custom reader to be read back into Spectrum1D.

Examples
--------
To write out a Spectrum1D cube using this writer:

>>> spec.write("my_output.fits", format="jdaviz-cube", overwrite=True) # doctest: +SKIP

"""
pri_hdu = fits.PrimaryHDU()

flux = spectrum.flux
sci_hdu = fits.ImageHDU(flux.value.astype(np.float32))
sci_hdu.name = "SCI"
sci_hdu.header.update(spectrum.meta)
sci_hdu.header.update(spectrum.wcs.to_header())
sci_hdu.header['BUNIT'] = flux.unit.to_string(format='fits')

hlist = [pri_hdu, sci_hdu]

# https://specutils.readthedocs.io/en/latest/spectrum1d.html#including-masks
# Good: False or 0
# Bad: True or non-zero
if spectrum.mask is not None:
mask_hdu = fits.ImageHDU(spectrum.mask.astype(np.uint16))
mask_hdu.name = "MASK"
hlist.append(mask_hdu)

hdulist = fits.HDUList(hlist)
hdulist.writeto(file_name, **kwargs)


if _astropy_has_priorities():
kwargs = {"priority": 0}
else: # pragma: no cover
kwargs = {}
io_registry.register_writer(
"jdaviz-cube", Spectrum1D, jdaviz_cube_fitswriter, force=False, **kwargs)
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import pytest
from astropy import units as u
from astropy.io import fits
from astropy.io.registry.base import IORegistryError
from astropy.modeling import models
from astropy.nddata import StdDevUncertainty
from astropy.tests.helper import assert_quantity_allclose
Expand Down Expand Up @@ -251,7 +250,7 @@ def test_cube_fitting_backend(cubeviz_helper, unc, tmp_path):

# Check I/O roundtrip.
out_fn = tmp_path / "fitted_cube.fits"
fitted_spectrum.write(out_fn, format="jdaviz-cube", overwrite=True)
fitted_spectrum.write(out_fn, format="wcs1d-fits", hdu=1, flux_name="SCI", overwrite=True)
flux_unit_str = fitted_spectrum.flux.unit.to_string(format="fits")
coo_expected = fitted_spectrum.wcs.pixel_to_world(1, 0, 2)
with fits.open(out_fn) as pf:
Expand All @@ -268,12 +267,7 @@ def test_cube_fitting_backend(cubeviz_helper, unc, tmp_path):
assert_allclose([coo[1].ra.deg, coo[1].dec.deg],
[coo_expected[1].ra.deg, coo_expected[1].dec.deg])

# Our custom format is not registered to readers, just writers.
# You can read it back in without custom read. See "Cubeviz roundtrip" below.
with pytest.raises(IORegistryError, match="No reader defined"):
Spectrum1D.read(out_fn, format="jdaviz-cube")

# Check Cubeviz roundtrip.
# Check Cubeviz roundtrip. This should automatically go through wcs1d-fits reader.
cubeviz_helper.load_data(out_fn)
assert len(cubeviz_helper.app.data_collection) == 3
data_sci = cubeviz_helper.app.data_collection["fitted_cube.fits[SCI]"]
Expand Down