From 84061dfb9a8b6a27907997f2c05c8f4735ed2693 Mon Sep 17 00:00:00 2001 From: "P. L. Lim" <2090236+pllim@users.noreply.github.com> Date: Wed, 27 Sep 2023 17:40:40 -0400 Subject: [PATCH] Backport PR #2431: BUG: Moment map retains WCS on write --- CHANGES.rst | 2 ++ .../plugins/moment_maps/moment_maps.py | 8 +++-- .../moment_maps/tests/test_moment_maps.py | 34 ++++++++++++++++--- 3 files changed, 38 insertions(+), 6 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index d2db985f07..bd172928df 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -10,6 +10,8 @@ Bug Fixes Cubeviz ^^^^^^^ +- Fixed moment map losing WCS when being written out to FITS file. [#2431] + Imviz ^^^^^ diff --git a/jdaviz/configs/cubeviz/plugins/moment_maps/moment_maps.py b/jdaviz/configs/cubeviz/plugins/moment_maps/moment_maps.py index 99b04a2d9d..93243585e4 100644 --- a/jdaviz/configs/cubeviz/plugins/moment_maps/moment_maps.py +++ b/jdaviz/configs/cubeviz/plugins/moment_maps/moment_maps.py @@ -5,7 +5,7 @@ from astropy.nddata import CCDData from traitlets import Unicode, Bool, observe -from specutils import Spectrum1D, manipulation, analysis +from specutils import manipulation, analysis from jdaviz.core.custom_traitlets import IntHandleEmpty from jdaviz.core.events import SnackbarMessage @@ -101,7 +101,11 @@ def calculate_moment(self, add_data=True): Whether to add the resulting data object to the app according to ``add_results``. """ # Retrieve the data cube and slice out desired region, if specified - cube = self.dataset.get_object(cls=Spectrum1D, statistic=None) + if "_orig_spec" in self.dataset.selected_obj.meta: + cube = self.dataset.selected_obj.meta["_orig_spec"] + else: + cube = self.dataset.selected_obj + spec_min, spec_max = self.spectral_subset.selected_min_max(cube) slab = manipulation.spectral_slab(cube, spec_min, spec_max) diff --git a/jdaviz/configs/cubeviz/plugins/moment_maps/tests/test_moment_maps.py b/jdaviz/configs/cubeviz/plugins/moment_maps/tests/test_moment_maps.py index 240bba4fe9..c267f3f23b 100644 --- a/jdaviz/configs/cubeviz/plugins/moment_maps/tests/test_moment_maps.py +++ b/jdaviz/configs/cubeviz/plugins/moment_maps/tests/test_moment_maps.py @@ -1,18 +1,22 @@ import os +import warnings +from pathlib import Path import pytest from astropy.io import fits from astropy.nddata import CCDData from astropy.wcs import WCS +from astroquery.mast import Observations from numpy.testing import assert_allclose from jdaviz.configs.cubeviz.plugins.moment_maps.moment_maps import MomentMap -@pytest.mark.filterwarnings('ignore:No observer defined on WCS') def test_moment_calculation(cubeviz_helper, spectrum1d_cube, tmpdir): dc = cubeviz_helper.app.data_collection - cubeviz_helper.load_data(spectrum1d_cube, data_label='test') + with warnings.catch_warnings(): + warnings.filterwarnings("ignore", message="No observer defined on WCS.*") + cubeviz_helper.load_data(spectrum1d_cube, data_label='test') flux_viewer = cubeviz_helper.app.get_viewer(cubeviz_helper._default_flux_viewer_reference_name) # Since we are not really displaying, need this to trigger GUI stuff. @@ -95,7 +99,6 @@ def test_moment_calculation(cubeviz_helper, spectrum1d_cube, tmpdir): "204.9998877673 27.0001000000 (deg)") -@pytest.mark.filterwarnings('ignore:No observer defined on WCS') def test_write_momentmap(cubeviz_helper, spectrum1d_cube, tmp_path): ''' Test writing a moment map out to a FITS file on disk ''' @@ -105,7 +108,9 @@ def test_write_momentmap(cubeviz_helper, spectrum1d_cube, tmp_path): existing_sentinel_text = "This is a simulated, existing file on disk" test_file.write_text(existing_sentinel_text) - cubeviz_helper.load_data(spectrum1d_cube, data_label='test') + with warnings.catch_warnings(): + warnings.filterwarnings("ignore", message="No observer defined on WCS.*") + cubeviz_helper.load_data(spectrum1d_cube, data_label='test') plugin = cubeviz_helper.plugins['Moment Maps'] moment = plugin.calculate_moment() @@ -135,3 +140,24 @@ def test_write_momentmap(cubeviz_helper, spectrum1d_cube, tmp_path): plugin._obj.filename = "fake_path/test_file.fits" with pytest.raises(ValueError, match="Invalid path"): plugin._obj.vue_save_as_fits() + + +@pytest.mark.remote_data +def test_momentmap_nirspec_prism(cubeviz_helper, tmp_path): + uri = "mast:jwst/product/jw02732-o003_t002_nirspec_prism-clear_s3d.fits" + download_path = str(tmp_path / Path(uri).name) + Observations.download_file(uri, local_path=download_path) + + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + cubeviz_helper.load_data(download_path) + plugin = cubeviz_helper.plugins['Moment Maps'] + plugin.calculate_moment() + assert isinstance(plugin._obj.moment.wcs, WCS) + + # Because cube axes order is re-arranged by specutils on load, this gets confusing. + # There is a swapaxes within Moment Map WCS calculation. + sky_moment = plugin._obj.moment.wcs.pixel_to_world(50, 30) + sky_cube = cubeviz_helper.app.data_collection[0].meta["_orig_spec"].wcs.celestial.pixel_to_world(30, 50) # noqa: E501 + assert_allclose((sky_moment.ra.deg, sky_moment.dec.deg), + (sky_cube.ra.deg, sky_cube.dec.deg))