diff --git a/jdaviz/configs/cubeviz/plugins/moment_maps/moment_maps.py b/jdaviz/configs/cubeviz/plugins/moment_maps/moment_maps.py index 65f652fdc8..b0089f99d6 100644 --- a/jdaviz/configs/cubeviz/plugins/moment_maps/moment_maps.py +++ b/jdaviz/configs/cubeviz/plugins/moment_maps/moment_maps.py @@ -1,10 +1,11 @@ import os from pathlib import Path +import numpy as np +import specutils from astropy import units as u from astropy.nddata import CCDData -import numpy as np - +from astropy.utils import minversion from traitlets import Bool, List, Unicode, observe from specutils import manipulation, analysis, Spectrum1D @@ -24,6 +25,7 @@ __all__ = ['MomentMap'] +SPECUTILS_LT_1_15_1 = not minversion(specutils, "1.15.1.dev") spaxel = u.def_unit('spaxel', 1 * u.Unit("")) u.add_enabled_units([spaxel]) @@ -350,7 +352,11 @@ def calculate_moment(self, add_data=True): # convert unit string to u.Unit so moment map data can be converted flux_or_sb_display_unit = u.Unit(flux_sb_unit) - self.moment = self.moment.to(flux_or_sb_display_unit) + if SPECUTILS_LT_1_15_1: + moment_new_unit = flux_or_sb_display_unit + else: + moment_new_unit = flux_or_sb_display_unit * self.spectrum_viewer.state.x_display_unit # noqa: E501 + self.moment = self.moment.to(moment_new_unit) # Reattach the WCS so we can load the result self.moment = CCDData(self.moment, wcs=data_wcs) 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 eba105616b..0cc78f3479 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 @@ -2,14 +2,16 @@ import warnings from pathlib import Path +import numpy as np import pytest +from astropy import units as u from astropy.io import fits from astropy.nddata import CCDData from astropy.wcs import WCS -import astropy.units as u -import numpy as np -from numpy.testing import assert_allclose from glue.core.roi import XRangeROI +from numpy.testing import assert_allclose + +from jdaviz.configs.cubeviz.plugins.moment_maps.moment_maps import SPECUTILS_LT_1_15_1 def test_user_api(cubeviz_helper, spectrum1d_cube): @@ -49,6 +51,13 @@ def test_user_api(cubeviz_helper, spectrum1d_cube): def test_moment_calculation(cubeviz_helper, spectrum1d_cube, tmp_path): + if SPECUTILS_LT_1_15_1: + moment_unit = "Jy" + moment_value_str = "+8.00000e+00" + else: + moment_unit = "Jy m" + moment_value_str = "+6.40166e-10" + dc = cubeviz_helper.app.data_collection with warnings.catch_warnings(): warnings.filterwarnings("ignore", message="No observer defined on WCS.*") @@ -107,10 +116,11 @@ def test_moment_calculation(cubeviz_helper, spectrum1d_cube, tmp_path): label_mouseover._viewer_mouse_event(flux_viewer, {'event': 'mousemove', 'domain': {'x': 0, 'y': 0}}) - # Slice 0 has 8 pixels, this is Slice 1 # noqa - assert label_mouseover.as_text() == ("Pixel x=00.0 y=00.0 Value +8.00000e+00 Jy", - "World 13h39m59.9731s +27d00m00.3600s (ICRS)", - "204.9998877673 27.0001000000 (deg)") + # Slice 0 has 8 pixels, this is Slice 1 + assert label_mouseover.as_text() == ( + f"Pixel x=00.0 y=00.0 Value {moment_value_str} {moment_unit}", + "World 13h39m59.9731s +27d00m00.3600s (ICRS)", + "204.9998877673 27.0001000000 (deg)") assert mm._obj.filename == 'moment0_test_FLUX.fits' # Auto-populated on calculate. mm._obj.filename = str(tmp_path / mm._obj.filename) # But we want it in tmp_path for testing. @@ -136,9 +146,10 @@ def test_moment_calculation(cubeviz_helper, spectrum1d_cube, tmp_path): # Coordinate display should be unaffected. label_mouseover._viewer_mouse_event(flux_viewer, {'event': 'mousemove', 'domain': {'x': 0, 'y': 0}}) - assert label_mouseover.as_text() == ("Pixel x=00.0 y=00.0 Value +8.00000e+00 Jy", - "World 13h39m59.9731s +27d00m00.3600s (ICRS)", - "204.9998877673 27.0001000000 (deg)") + assert label_mouseover.as_text() == ( + f"Pixel x=00.0 y=00.0 Value {moment_value_str} {moment_unit}", + "World 13h39m59.9731s +27d00m00.3600s (ICRS)", + "204.9998877673 27.0001000000 (deg)") def test_moment_velocity_calculation(cubeviz_helper, spectrum1d_cube): @@ -279,6 +290,10 @@ def test_momentmap_nirspec_prism(cubeviz_helper, tmp_path): def test_correct_output_flux_or_sb_units(cubeviz_helper, spectrum1d_cube_custom_fluxunit): + if SPECUTILS_LT_1_15_1: + moment_unit = "Jy / sr" + else: + moment_unit = "Jy m / sr" # test that the output unit labels in the moment map plugin reflect any # changes made in the unit conversion plugin. @@ -304,7 +319,7 @@ def test_correct_output_flux_or_sb_units(cubeviz_helper, spectrum1d_cube_custom_ # check that calculated moment has the correct units mm.calculate_moment() - assert mm.moment.unit == 'MJy / sr' + assert mm.moment.unit == f'M{moment_unit}' # now change surface brightness units in the unit conversion plugin uc = cubeviz_helper.plugins["Unit Conversion"] @@ -318,4 +333,4 @@ def test_correct_output_flux_or_sb_units(cubeviz_helper, spectrum1d_cube_custom_ # and that calculated moment has the correct units mm.calculate_moment() - assert mm.moment.unit == 'Jy / sr' + assert mm.moment.unit == moment_unit diff --git a/jdaviz/configs/cubeviz/plugins/tests/test_cubeviz_aperphot.py b/jdaviz/configs/cubeviz/plugins/tests/test_cubeviz_aperphot.py index d714d5b4f1..20f31702d5 100644 --- a/jdaviz/configs/cubeviz/plugins/tests/test_cubeviz_aperphot.py +++ b/jdaviz/configs/cubeviz/plugins/tests/test_cubeviz_aperphot.py @@ -6,6 +6,8 @@ from numpy.testing import assert_allclose from regions import RectanglePixelRegion, PixCoord +from jdaviz.configs.cubeviz.plugins.moment_maps.moment_maps import SPECUTILS_LT_1_15_1 + def test_cubeviz_aperphot_cube_orig_flux(cubeviz_helper, image_cube_hdu_obj_microns): cubeviz_helper.load_data(image_cube_hdu_obj_microns, data_label="test") @@ -100,14 +102,21 @@ def test_cubeviz_aperphot_generated_2d_moment(cubeviz_helper, image_cube_hdu_obj row = cubeviz_helper.get_aperture_photometry_results()[0] # Basically, we should recover the input rectangle here. + if SPECUTILS_LT_1_15_1: + moment_sum = 540 * flux_unit + moment_mean = 36 * flux_unit + else: + moment_unit = flux_unit * u.um + moment_sum = 0.54 * moment_unit + moment_mean = 0.036 * moment_unit assert_allclose(row["xcenter"], 1 * u.pix) assert_allclose(row["ycenter"], 2 * u.pix) sky = row["sky_center"] assert_allclose(sky.ra.deg, 205.43985906934287) assert_allclose(sky.dec.deg, 27.003490103642033) - assert_allclose(row["sum"], 540 * flux_unit) # 3 (w) x 5 (h) x 36 (v) + assert_allclose(row["sum"], moment_sum) # 3 (w) x 5 (h) x 36 (v) assert_allclose(row["sum_aper_area"], 15 * (u.pix * u.pix)) # 3 (w) x 5 (h) - assert_allclose(row["mean"], 36 * flux_unit) + assert_allclose(row["mean"], moment_mean) assert np.isnan(row["slice_wave"]) # Moment 1 has no compatible unit, so should not be available for photometry. diff --git a/jdaviz/configs/imviz/plugins/aper_phot_simple/aper_phot_simple.py b/jdaviz/configs/imviz/plugins/aper_phot_simple/aper_phot_simple.py index 54490b6d4e..a189d33254 100644 --- a/jdaviz/configs/imviz/plugins/aper_phot_simple/aper_phot_simple.py +++ b/jdaviz/configs/imviz/plugins/aper_phot_simple/aper_phot_simple.py @@ -113,7 +113,8 @@ def valid_cubeviz_datasets(data): acceptable_types = ['spectral flux density wav', 'photon flux density wav', 'spectral flux density', - 'photon flux density'] + 'photon flux density', + 'energy flux'] # Moment map 0 return ((data.ndim in (2, 3)) and ((img_unit == (u.MJy / u.sr)) or (img_unit.physical_type in acceptable_types))) diff --git a/jdaviz/configs/imviz/tests/test_simple_aper_phot.py b/jdaviz/configs/imviz/tests/test_simple_aper_phot.py index 598ad733db..262d5d65c7 100644 --- a/jdaviz/configs/imviz/tests/test_simple_aper_phot.py +++ b/jdaviz/configs/imviz/tests/test_simple_aper_phot.py @@ -13,6 +13,7 @@ from jdaviz.configs.imviz.plugins.aper_phot_simple.aper_phot_simple import ( _curve_of_growth, _radial_profile) from jdaviz.configs.imviz.tests.utils import BaseImviz_WCS_WCS, BaseImviz_WCS_NoWCS +from jdaviz.tests.test_utils import PHOTUTILS_LT_1_12_1 class TestSimpleAperPhot(BaseImviz_WCS_WCS): @@ -333,6 +334,17 @@ def test_annulus_background(imviz_helper): gauss4 = make_4gaussians_image() # The background has a mean of 5 with noise ones = np.ones(gauss4.shape) + if PHOTUTILS_LT_1_12_1: + bg_4gauss_1 = 5.745596129482831 + bg_4gauss_2 = 5.13918435824334 + bg_4gauss_3 = 44.72559981461203 + bg_4gauss_4 = 4.89189 + else: + bg_4gauss_1 = 5.802287 + bg_4gauss_2 = 5.052332 + bg_4gauss_3 = 45.416834 + bg_4gauss_4 = 4.939397 + imviz_helper.load_data(gauss4, data_label='four_gaussians') imviz_helper.load_data(ones, data_label='ones') @@ -354,7 +366,7 @@ def test_annulus_background(imviz_helper): # Switch data phot_plugin.dataset_selected = 'four_gaussians' - assert_allclose(phot_plugin.background_value, 5.745596129482831) # Changed + assert_allclose(phot_plugin.background_value, bg_4gauss_1) # Changed # Draw ellipse on another object ellipse_1 = EllipsePixelRegion(center=PixCoord(x=20.5, y=37.5), width=41, height=15) @@ -378,19 +390,19 @@ def test_annulus_background(imviz_helper): phot_plugin.background_selected = 'Subset 4' # Check new annulus for four_gaussians - assert_allclose(phot_plugin.background_value, 5.13918435824334) # Changed + assert_allclose(phot_plugin.background_value, bg_4gauss_2) # Changed # Switch to manual, should not change phot_plugin.background_selected = 'Manual' - assert_allclose(phot_plugin.background_value, 5.13918435824334) + assert_allclose(phot_plugin.background_value, bg_4gauss_2) # Switch to Subset, should change a lot because this is not background area phot_plugin.background_selected = 'Subset 1' - assert_allclose(phot_plugin.background_value, 44.72559981461203) + assert_allclose(phot_plugin.background_value, bg_4gauss_3) # Switch back to annulus, should be same as before in same mode phot_plugin.background_selected = 'Subset 4' - assert_allclose(phot_plugin.background_value, 5.13918435824334) + assert_allclose(phot_plugin.background_value, bg_4gauss_2) # Edit the annulus and make sure background updates subset_plugin = imviz_helper.plugins["Subset Tools"]._obj @@ -400,7 +412,7 @@ def test_annulus_background(imviz_helper): subset_plugin._set_value_in_subset_definition(0, "Inner Radius (pixels)", "value", 40) subset_plugin._set_value_in_subset_definition(0, "Outer Radius (pixels)", "value", 45) subset_plugin.vue_update_subset() - assert_allclose(phot_plugin.background_value, 4.89189) + assert_allclose(phot_plugin.background_value, bg_4gauss_4) # NOTE: Extracting the cutout for radial profile is aperture diff --git a/jdaviz/core/tests/test_tools.py b/jdaviz/core/tests/test_tools.py index 912176a8ea..35622814b4 100644 --- a/jdaviz/core/tests/test_tools.py +++ b/jdaviz/core/tests/test_tools.py @@ -1,9 +1,12 @@ import time + +import numpy as np from astropy import units as u from astropy.nddata import NDData -from photutils.datasets import make_4gaussians_image -import numpy as np from numpy.testing import assert_allclose +from photutils.datasets import make_4gaussians_image + +from jdaviz.tests.test_utils import PHOTUTILS_LT_1_12_1 def test_boxzoom(cubeviz_helper, image_cube_hdu_obj_microns): @@ -96,10 +99,15 @@ def test_stretch_bounds_and_spline(imviz_helper): "domain": {"x": 11.639166666374734, "y": 970.9392968750001}, } - knots_after_drag_move = ( - [0.0, 0.1, 0.21712585033417825, 0.7, 1.0], - [0.0, 0.05, 0.2852214046563617, 0.9, 1.0], - ) + if PHOTUTILS_LT_1_12_1: + knots_after_drag_move = ( + [0.0, 0.1, 0.21712585033417825, 0.7, 1.0], + [0.0, 0.05, 0.2852214046563617, 0.9, 1.0], + ) + else: + knots_after_drag_move = ( + [0.0, 0.1, 0.21712585033417825, 0.7, 1.0], + [0.0, 0.05, 0.2698132855572785, 0.9, 1.0]) stretch_tool.on_mouse_event(knot_move_msg) diff --git a/jdaviz/tests/test_utils.py b/jdaviz/tests/test_utils.py index 639eecf121..f0866b6caf 100644 --- a/jdaviz/tests/test_utils.py +++ b/jdaviz/tests/test_utils.py @@ -1,10 +1,14 @@ import pytest import warnings +import photutils from asdf.exceptions import AsdfWarning +from astropy.utils import minversion from astropy.wcs import FITSFixedWarning from jdaviz import utils +PHOTUTILS_LT_1_12_1 = not minversion(photutils, "1.12.1.dev") + @pytest.mark.parametrize("test_input,expected", [(0, 'a'), (1, 'b'), (25, 'z'), (26, 'aa'), (701, 'zz'), (702, '{a')])