From 650d0b347737c8e62cb6a11e8a0b64cd2ee4cd64 Mon Sep 17 00:00:00 2001 From: "P. L. Lim" <2090236+pllim@users.noreply.github.com> Date: Tue, 11 Apr 2023 16:28:48 -0400 Subject: [PATCH 01/11] Update Mosviz parser doc and fix NIRSpec auto-load. Add change log. Ignore non-FITS files in NIRSpec glob. Grab source RA/Dec from 1D spec first. --- CHANGES.rst | 3 + docs/mosviz/import_data.rst | 102 +++++++++++++++-------- jdaviz/configs/mosviz/helper.py | 28 +++++-- jdaviz/configs/mosviz/plugins/parsers.py | 69 ++++++++++----- 4 files changed, 141 insertions(+), 61 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index f30fa0f23b..032e0ca99a 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -30,6 +30,9 @@ Imviz Mosviz ^^^^^^ +- NIRSpec automatic loader now can take a single image as input, instead of requiring + the number of cutouts to be the same as the number of 1D spectra. [#2146] + Specviz ^^^^^^^ diff --git a/docs/mosviz/import_data.rst b/docs/mosviz/import_data.rst index 95f78a3a9c..983ce46bd3 100644 --- a/docs/mosviz/import_data.rst +++ b/docs/mosviz/import_data.rst @@ -4,40 +4,22 @@ Importing Data into Mosviz ************************** -Mosviz provides two different ways to load data: auto-recognition directory -loading and manual loading. +Mosviz provides two different ways to load data: -Automatic Directory Loading ---------------------------- -Mosviz provides instrument-specific directory parsers for select instruments. At this -time, Mosviz supports automatic parsing for the following instruments: - -* JWST NIRSpec (levels 2 and 3) -* JWST NIRISS -* JWST NIRCam - -The NIRSpec parser expects a directory with either level 2 files: - -* ``*_cal.fits`` : Single file containing level 2 2D spectra for all objects. -* ``*_x1d.fits`` : Single file containing level 2 1D spectra for all objects. +* :ref:`mosviz-import-auto-dir` +* :ref:`mosviz-import-manual-loading` -or level 3 files: - -* ``*_s2d.fits`` : N files containing level 3 2D spectra, where N is the number of objects. -* ``*_x1d.fits`` : N files containing level 3 1D spectra, where N is the number of objects. - -In either the level 2 or 3 case, the NIRSpec data directory may contain a sub-directory -named ``images``, ``cutouts``, or ``mosviz_cutouts``. This sub-directory should contain FITS files -containing images corresponding to each target, which may be sourced from a non-JWST telescope. +.. _mosviz-import-auto-dir: -The NIRISS parser expects a directory with the following types of files: +Automatic Directory Loading +=========================== -* ``*_i2d.fits`` : Level 3 2D images from the ``calwebb_image3`` imaging pipeline -* ``*_cat.ecsv`` : Level 3 source catalog from the ``calwebb_image3`` imaging pipeline **(For best performance, it's recommended that your directory only contain one.)** -* ``*_cal.fits`` : Level 2 2D spectra in vertical (R) and horizontal (C) orientations from the ``calwebb_spec2`` spectroscopic pipeline *(C spectra are shown first in 2D viewer by default.)* -* ``*_x1d.fits`` : Level 2 1D spectra in vertical (R) and horizontal (C) orientations from the ``calwebb_spec2`` spectroscopic pipeline *(C spectra are shown first in 1D viewer by default.)* +Mosviz provides instrument-specific directory parsers for select instruments. At this +time, Mosviz supports automatic parsing for the following instruments: -The NIRCam parser expects ``*_cal.fits`` and ``*_x1d`` files in the same format as the NIRISS parser. +* :ref:`mosviz-import-auto-dir-nirspec` +* :ref:`mosviz-import-auto-dir-niriss` +* :ref:`mosviz-import-auto-dir-nircam` In a Jupyter context (notebook or Lab), you must specify the instrument with a directory as such: @@ -67,13 +49,53 @@ and for NIRISS: jdaviz mosviz /path/to/my/data --instrument=niriss -Specifying a data directory is required to start Mosviz from the command line. - -If a directory is input in either case without specifying an instrument, Mosviz will +Specifying a data directory and an instrument are required to start Mosviz from the command line. +If a directory is entered without specifying an instrument, Mosviz will raise an error. +.. _mosviz-import-auto-dir-nirspec: + +JWST NIRSpec (levels 2 and 3) +----------------------------- + +The NIRSpec parser expects a directory with either level 2 files: + +* ``*_cal.fits`` : Single file containing level 2 2D spectra for all objects. +* ``*_x1d.fits`` : Single file containing level 2 1D spectra for all objects. + +or level 3 files: + +* ``*_s2d.fits`` : N files containing level 3 2D spectra, where N is the number of objects. +* ``*_x1d.fits`` : N files containing level 3 1D spectra, where N is the number of objects. + +In either the level 2 or 3 case, the NIRSpec data directory may contain a sub-directory +named ``images``, ``cutouts``, or ``mosviz_cutouts``. This sub-directory should contain FITS files +containing images corresponding to each target, which may be sourced from a non-JWST telescope. +If it only contains a single image, the same image would be used for all the spectra. + +.. _mosviz-import-auto-dir-niriss: + +JWST NIRISS +----------- + +The NIRISS parser expects a directory with the following types of files: + +* ``*_i2d.fits`` : Level 3 2D images from the ``calwebb_image3`` imaging pipeline +* ``*_cat.ecsv`` : Level 3 source catalog from the ``calwebb_image3`` imaging pipeline **(For best performance, it's recommended that your directory only contain one.)** +* ``*_cal.fits`` : Level 2 2D spectra in vertical (R) and horizontal (C) orientations from the ``calwebb_spec2`` spectroscopic pipeline *(C spectra are shown first in 2D viewer by default.)* +* ``*_x1d.fits`` : Level 2 1D spectra in vertical (R) and horizontal (C) orientations from the ``calwebb_spec2`` spectroscopic pipeline *(C spectra are shown first in 1D viewer by default.)* + +.. _mosviz-import-auto-dir-nircam: + +JWST NIRCam +----------- + +The NIRCam parser expects ``*_cal.fits`` and ``*_x1d`` files in the same format as the NIRISS parser. + +.. _mosviz-import-manual-loading: + Manual Loading --------------- +============== If an automatic parser is not provided yet for your data, Mosviz provides manual loading by specifying which files are which, and the associations between them. This is done by @@ -95,5 +117,17 @@ directory that contains all the files for the dataset to be loaded: spectra_1d = ['target1_1d.fits', 'target2_1d.fits'] spectra_2d = ['target1_2d.fits', 'target2_2d.fits'] images = ['target1_img.fits', 'target2_img.fits'] - mosviz.load_data(spectra_1d, spectra_2d, images) + mosviz.load_data(spectra_1d=spectra_1d, spectra_2d=spectra_2d, images=images) + mosviz.show() + +Alternative, if you want all the spectra to share a single image (e.g., a mosaic): + +.. code-block:: python + + from jdaviz import Mosviz + mosviz = Mosviz() + spectra_1d = ['target1_1d.fits', 'target2_1d.fits'] + spectra_2d = ['target1_2d.fits', 'target2_2d.fits'] + image = 'mymosaic.fits' + mosviz.load_data(spectra_1d=spectra_1d, spectra_2d=spectra_2d, images=image) mosviz.show() diff --git a/jdaviz/configs/mosviz/helper.py b/jdaviz/configs/mosviz/helper.py index 68afb8e813..3a7bc09ed3 100644 --- a/jdaviz/configs/mosviz/helper.py +++ b/jdaviz/configs/mosviz/helper.py @@ -590,7 +590,7 @@ def load_metadata(self): """ self.app.load_data(file_obj=None, parser_reference="mosviz-metadata-parser") - def load_1d_spectra(self, data_obj, data_labels=None): + def load_1d_spectra(self, data_obj, data_labels=None, add_redshift_column=False): """ Load and parse a set of 1D spectra objects. @@ -604,12 +604,15 @@ def load_1d_spectra(self, data_obj, data_labels=None): String representing the label for the data item loaded via ``data_obj``. Can be a list of strings representing data labels for each item in ``data_obj`` if ``data_obj`` is a list. + add_redshift_column : bool + Add redshift column to Mosviz table. """ super().load_data(data_obj, parser_reference="mosviz-spec1d-parser", data_labels=data_labels) - self._add_redshift_column() + if add_redshift_column: + self._add_redshift_column() - def load_2d_spectra(self, data_obj, data_labels=None): + def load_2d_spectra(self, data_obj, data_labels=None, add_redshift_column=False): """ Load and parse a set of 2D spectra objects. @@ -623,22 +626,28 @@ def load_2d_spectra(self, data_obj, data_labels=None): String representing the label for the data item loaded via ``data_obj``. Can be a list of strings representing data labels for each item in ``data_obj`` if ``data_obj`` is a list. + add_redshift_column : bool + Add redshift column to Mosviz table. """ super().load_data(data_obj, parser_reference="mosviz-spec2d-parser", data_labels=data_labels) - self._add_redshift_column() + if add_redshift_column: + self._add_redshift_column() - def load_jwst_directory(self, data_obj, data_labels=None, instrument=None): + def load_jwst_directory(self, data_obj, data_labels=None, instrument=None, + add_redshift_column=False): + """Load NIRISS or NIRCam data from a directory.""" self.app.auto_link = False super().load_data(data_obj, parser_reference="mosviz-niriss-parser", instrument=instrument) self.link_table_data(data_obj) - self._add_redshift_column() + if add_redshift_column: + self._add_redshift_column() self.app.auto_link = True - def load_images(self, data_obj, data_labels=None, share_image=0): + def load_images(self, data_obj, data_labels=None, share_image=0, add_redshift_column=False): """ Load and parse a set of image objects. If providing a file path, it must be readable by ``astropy.io.fits``. @@ -659,10 +668,13 @@ def load_images(self, data_obj, data_labels=None, share_image=0): different row in the table does not reload the displayed image. Currently, if non-zero, the provided number must match the number of spectra. + add_redshift_column : bool + Add redshift column to Mosviz table. """ super().load_data(data_obj, parser_reference="mosviz-image-parser", data_labels=data_labels, share_image=share_image) - self._add_redshift_column() + if add_redshift_column: + self._add_redshift_column() def get_column_names(self, visible=None): """ diff --git a/jdaviz/configs/mosviz/plugins/parsers.py b/jdaviz/configs/mosviz/plugins/parsers.py index a04ffd90a9..fe577db5ae 100644 --- a/jdaviz/configs/mosviz/plugins/parsers.py +++ b/jdaviz/configs/mosviz/plugins/parsers.py @@ -154,38 +154,44 @@ def mos_nirspec_directory_parser(app, data_obj, data_labels=None): # Load spectra level3_path = Path(data_obj) - for file_path in glob.iglob(str(level3_path / '*')): + for p in sorted(level3_path.glob('*.fits*')): + file_path = str(p) if 'x1d' in file_path or 'c1d' in file_path: spectra_1d.append(file_path) elif 's2d' in file_path: spectra_2d.append(file_path) - spectra_1d.sort() - spectra_2d.sort() - mos_spec1d_parser(app, spectra_1d) + n_specs = mos_spec1d_parser(app, spectra_1d) mos_spec2d_parser(app, spectra_2d) # Load images, if present image_path = None # Potential names of subdirectories where images are stored - for image_dir_name in ["cutouts", "mosviz_cutouts", "images"]: - if os.path.isdir(Path(str(level3_path / image_dir_name))): - image_path = Path(str(level3_path / image_dir_name)) + for image_dir_name in ("cutouts", "mosviz_cutouts", "images"): + cur_path = level3_path / image_dir_name + if cur_path.is_dir(): + image_path = cur_path break if image_path is not None: - images = sorted([file_path for file_path in glob.iglob(str(image_path / '*'))]) + images = sorted(image_path.glob('*.fits*')) + n_images = len(images) # The amount of images needs to be equal to the amount of rows # of the other columns in the table - if len(images) == len(spectra_1d): - mos_image_parser(app, images) + if n_images == 1: + if n_specs > 1: + kwargs = {'share_image': n_specs} + else: + kwargs = {} + mos_image_parser(app, str(images[0]), **kwargs) + elif n_images == n_specs: + mos_image_parser(app, list(map(str, images))) else: - msg = ("The number of images in this directory does not match the" - " number of spectra 1d and 2d files, please make the " - "amounts equal or load images separately.") - msg = SnackbarMessage(msg, color='warning', sender=app) - app.hub.broadcast(msg) + app.hub.broadcast(SnackbarMessage( + "The number of images in this directory does not match the " + "number of spectra 1d and 2d files, please make the " + "amounts equal or load images separately.", color='warning', sender=app)) mos_meta_parser(app) @@ -205,8 +211,13 @@ def mos_spec1d_parser(app, data_obj, data_labels=None, the mosviz table. data_labels : str, optional The label applied to the glue data component. - """ + Returns + ------- + n_specs : int + Number of data objects loaded. + + """ if isinstance(data_labels, str): data_labels = [data_labels] @@ -269,6 +280,12 @@ def mos_spec2d_parser(app, data_obj, data_labels=None, add_to_table=True, The extension in the FITS file that contains the data to be loaded. transpose : bool, optional Flag to transpose the data array before loading. + + Returns + ------- + n_specs : int + Number of data objects loaded. + """ spectrum_2d_viewer_reference_name = ( app._jdaviz_helper._default_spectrum_2d_viewer_reference_name @@ -476,8 +493,10 @@ def mos_meta_parser(app, data_obj=None, ids=None): with app.data_collection.delay_link_manager_update(): current_columns = [comp.label for comp in app.data_collection['MOS Table'].main_components] + has_1d = "1D Spectra" in current_columns + # source name can be taken from 1d spectra - if "1D Spectra" in current_columns: + if has_1d: names = _get_source_identifiers(app, "1D Spectra", data_obj, ids) _add_to_table(app, names, "Identifier") @@ -490,8 +509,20 @@ def mos_meta_parser(app, data_obj=None, ids=None): _add_to_table(app, filters_gratings, "Filter/Grating") - # source name and coordinates are taken from image headers, if present - if "Images" in current_columns: + # Grab target sky coordinates from 1D spectrum, if possible. + # This has to happen after Filter/Grating because columns are insertion-ordered. + if has_1d: + ra = query_metadata_by_component(app, "SRCRA", "1D Spectra", False) + dec = query_metadata_by_component(app, "SRCDEC", "1D Spectra", False) + if all(ra) and all(dec): + _add_to_table(app, ra, "R.A.") + _add_to_table(app, dec, "Dec.") + + # Refresh current_columns for Images check + current_columns = [comp.label for comp in app.data_collection['MOS Table'].main_components] + + # If not in 1D spectrum, source coordinates are taken from image headers, if present. + if "R.A." not in current_columns and "Images" in current_columns: ra = query_metadata_by_component(app, "OBJ_RA", "Images", FALLBACK_NAME) dec = query_metadata_by_component(app, "OBJ_DEC", "Images", FALLBACK_NAME) _add_to_table(app, ra, "R.A.") From 4f25fddc523c99c9f6f312cf8785891a713d2253 Mon Sep 17 00:00:00 2001 From: "P. L. Lim" <2090236+pllim@users.noreply.github.com> Date: Wed, 12 Apr 2023 16:23:30 -0400 Subject: [PATCH 02/11] Update Mosviz example notebook --- notebooks/MosvizExample.ipynb | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/notebooks/MosvizExample.ipynb b/notebooks/MosvizExample.ipynb index 1c5524036a..ee836fd95c 100644 --- a/notebooks/MosvizExample.ipynb +++ b/notebooks/MosvizExample.ipynb @@ -201,11 +201,12 @@ "source": [ "# *** OR ***\n", "# Enable and run this cell if you want to load them separately.\n", + "# On the last loader call, also add redshift column.\n", "\n", "mosviz.load_metadata(images) \n", "mosviz.load_1d_spectra(spectra_1d)\n", "mosviz.load_2d_spectra(spectra_2d)\n", - "mosviz.load_images(images)" + "mosviz.load_images(images, add_redshift_column=True)" ] }, { @@ -240,9 +241,10 @@ "source": [ "# *** OR ***\n", "# Enable and run this cell if you want to load them separately.\n", + "# On the last loader call, also add redshift column.\n", "\n", "mosviz_no_images.load_1d_spectra(spectra_1d)\n", - "mosviz_no_images.load_2d_spectra(spectra_2d)" + "mosviz_no_images.load_2d_spectra(spectra_2d, add_redshift_column=True)" ] }, { @@ -313,7 +315,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.7" + "version": "3.10.8" } }, "nbformat": 4, From 8542eee7c12a5fca32602d293a4123494afd7139 Mon Sep 17 00:00:00 2001 From: "P. L. Lim" <2090236+pllim@users.noreply.github.com> Date: Wed, 12 Apr 2023 16:53:14 -0400 Subject: [PATCH 03/11] Update tests and remove unnecessary ignoring of warnings that are not there. --- .../configs/mosviz/tests/test_data_loading.py | 11 ++++------- jdaviz/configs/mosviz/tests/test_helper.py | 18 +++++++----------- jdaviz/configs/mosviz/tests/test_parsers.py | 6 ------ 3 files changed, 11 insertions(+), 24 deletions(-) diff --git a/jdaviz/configs/mosviz/tests/test_data_loading.py b/jdaviz/configs/mosviz/tests/test_data_loading.py index 64b968625f..ddf48ec987 100644 --- a/jdaviz/configs/mosviz/tests/test_data_loading.py +++ b/jdaviz/configs/mosviz/tests/test_data_loading.py @@ -31,10 +31,9 @@ def test_load_spectrum1d(mosviz_helper, spectrum1d): mosviz_helper.load_1d_spectra([1, 2, 3]) -@pytest.mark.filterwarnings('ignore') def test_load_image(mosviz_helper, mos_image): label = "Test Image" - mosviz_helper.load_images(mos_image, data_labels=label) + mosviz_helper.load_images(mos_image, data_labels=label, add_redshift_column=True) assert len(mosviz_helper.app.data_collection) == 2 assert mosviz_helper.app.data_collection[0].label == "MOS Table" @@ -55,7 +54,7 @@ def test_load_image(mosviz_helper, mos_image): def test_load_spectrum_collection(mosviz_helper, spectrum_collection): labels = [f"Test Spectrum Collection {i}" for i in range(5)] - mosviz_helper.load_1d_spectra(spectrum_collection, data_labels=labels) + mosviz_helper.load_1d_spectra(spectrum_collection, data_labels=labels, add_redshift_column=True) # +1 for the table viewer assert len(mosviz_helper.app.data_collection) == len(spectrum_collection) + 1 @@ -76,7 +75,7 @@ def test_load_list_of_spectrum1d(mosviz_helper, spectrum1d): spectra = [spectrum1d] * 3 labels = [f"Test Spectrum 1D {i}" for i in range(3)] - mosviz_helper.load_1d_spectra(spectra, data_labels=labels) + mosviz_helper.load_1d_spectra(spectra, data_labels=labels, add_redshift_column=True) assert len(mosviz_helper.app.data_collection) == 4 assert mosviz_helper.app.data_collection[0].label == "MOS Table" @@ -92,7 +91,6 @@ def test_load_list_of_spectrum1d(mosviz_helper, spectrum1d): assert isinstance(data[labels[0]], Spectrum1D) -@pytest.mark.filterwarnings('ignore') def test_load_mos_spectrum2d(mosviz_helper, mos_spectrum2d): label = "Test 2D Spectrum" @@ -112,7 +110,6 @@ def test_load_mos_spectrum2d(mosviz_helper, mos_spectrum2d): assert data[label].shape == (1024, 15) -@pytest.mark.filterwarnings('ignore') @pytest.mark.parametrize('label', [None, "Test Label"]) def test_load_multi_image_spec(mosviz_helper, mos_image, spectrum1d, mos_spectrum2d, label): spectra1d = [spectrum1d] * 3 @@ -169,7 +166,7 @@ def test_load_single_image_multi_spec(mosviz_helper, mos_image, spectrum1d, mos_ # Test that loading is still possible after previous crash: # https://github.com/spacetelescope/jdaviz/issues/364 with pytest.raises(ValueError, match="incompatible with the dimensions of this data:"): - mosviz_helper.load_data(spectra1d, spectra2d, images=[]) + mosviz_helper.load_data(spectra1d, spectra2d, images=[mos_image, mos_image]) mosviz_helper.load_data(spectra1d, spectra2d, images=mos_image, images_label=label) diff --git a/jdaviz/configs/mosviz/tests/test_helper.py b/jdaviz/configs/mosviz/tests/test_helper.py index ebab1adef2..60a2a7ecdb 100644 --- a/jdaviz/configs/mosviz/tests/test_helper.py +++ b/jdaviz/configs/mosviz/tests/test_helper.py @@ -7,13 +7,12 @@ from jdaviz.configs.specviz2d.helper import Specviz2d -@pytest.mark.filterwarnings('ignore') def test_viewer_axis_link(mosviz_helper, mos_spectrum1d, mos_spectrum2d): label1d = "Test 1D Spectrum" mosviz_helper.load_1d_spectra(mos_spectrum1d, data_labels=label1d) label2d = "Test 2D Spectrum" - mosviz_helper.load_2d_spectra(mos_spectrum2d, data_labels=label2d) + mosviz_helper.load_2d_spectra(mos_spectrum2d, data_labels=label2d, add_redshift_column=True) table = mosviz_helper.app.get_viewer('table-viewer') table.widget_table.vue_on_row_clicked(0) @@ -30,7 +29,7 @@ def test_viewer_axis_link(mosviz_helper, mos_spectrum1d, mos_spectrum2d): def test_to_csv(tmp_path, mosviz_helper, spectrum_collection): labels = [f"Test Spectrum Collection {i}" for i in range(5)] - mosviz_helper.load_1d_spectra(spectrum_collection, data_labels=labels) + mosviz_helper.load_1d_spectra(spectrum_collection, data_labels=labels, add_redshift_column=True) mosviz_helper.to_csv(filename=str(tmp_path / "MOS_data.csv")) @@ -49,7 +48,6 @@ def test_to_csv(tmp_path, mosviz_helper, spectrum_collection): assert found_rows == 5 -@pytest.mark.filterwarnings('ignore') def test_table_scrolling(mosviz_helper, mos_image, spectrum1d, mos_spectrum2d): spectra1d = [spectrum1d] * 2 spectra2d = [mos_spectrum2d] * 2 @@ -69,7 +67,6 @@ def test_table_scrolling(mosviz_helper, mos_image, spectrum1d, mos_spectrum2d): assert table.widget_table.highlighted == 1 -@pytest.mark.filterwarnings('ignore') def test_column_visibility(mosviz_helper, mos_image, spectrum1d, mos_spectrum2d): spectra1d = [spectrum1d] * 2 spectra2d = [mos_spectrum2d] * 2 @@ -92,7 +89,6 @@ def test_column_visibility(mosviz_helper, mos_image, spectrum1d, mos_spectrum2d) assert 'Redshift' not in mosviz_helper.get_column_names(True) -@pytest.mark.filterwarnings('ignore') def test_custom_columns(mosviz_helper, mos_image, spectrum1d, mos_spectrum2d): spectra1d = [spectrum1d] * 2 spectra2d = [mos_spectrum2d] * 2 @@ -122,7 +118,6 @@ def test_custom_columns(mosviz_helper, mos_image, spectrum1d, mos_spectrum2d): assert "Redshift" not in mosviz_helper.get_column_names(True) -@pytest.mark.filterwarnings('ignore') def test_redshift_column(mosviz_helper, mos_image, spectrum1d, mos_spectrum2d): spectra1d = [spectrum1d] * 2 spectra2d = [mos_spectrum2d] * 2 @@ -130,11 +125,12 @@ def test_redshift_column(mosviz_helper, mos_image, spectrum1d, mos_spectrum2d): mosviz_helper.load_data(spectra1d, spectra2d, images=mos_image) mosviz_helper.update_column("Redshift", 0.1, row=0) - assert_allclose(list(mosviz_helper.specviz.get_spectra().values())[0].redshift.value, 0.1) + all_spectra = mosviz_helper.specviz.get_spectra(apply_slider_redshift=True) + assert_allclose(list(all_spectra.values())[0].redshift.value, 0.1) assert isinstance(mosviz_helper.specviz2d, Specviz2d) - assert_allclose(mosviz_helper.get_spectrum_1d().redshift.value, 0.1) - assert_allclose(mosviz_helper.get_spectrum_2d().redshift.value, 0.1) - assert_allclose(mosviz_helper.get_spectrum_1d(row=1).redshift.value, 0.0) + assert_allclose(mosviz_helper.get_spectrum_1d(apply_slider_redshift=True).redshift.value, 0.1) + assert_allclose(mosviz_helper.get_spectrum_2d(apply_slider_redshift=True).redshift.value, 0.1) + assert_allclose(mosviz_helper.get_spectrum_1d(apply_slider_redshift=True, row=1).redshift.value, 0.0) # noqa: E501 def test_plugin_user_apis(mosviz_helper): diff --git a/jdaviz/configs/mosviz/tests/test_parsers.py b/jdaviz/configs/mosviz/tests/test_parsers.py index 5e690e4d79..56cda033a3 100644 --- a/jdaviz/configs/mosviz/tests/test_parsers.py +++ b/jdaviz/configs/mosviz/tests/test_parsers.py @@ -7,7 +7,6 @@ from jdaviz.utils import PRIHDR_KEY, COMMENTCARD_KEY -@pytest.mark.filterwarnings('ignore') @pytest.mark.remote_data @pytest.mark.parametrize('instrument_arg', ('nirspec', None)) def test_nirspec_parser(mosviz_helper, tmp_path, instrument_arg): @@ -83,7 +82,6 @@ def test_nirspec_parser(mosviz_helper, tmp_path, instrument_arg): data_label=data_label) -@pytest.mark.filterwarnings('ignore') @pytest.mark.remote_data def test_nirspec_level2_parser(mosviz_helper, tmp_path): ''' @@ -105,7 +103,6 @@ def test_nirspec_level2_parser(mosviz_helper, tmp_path): @pytest.mark.remote_data -@pytest.mark.filterwarnings('ignore', match="'(MJy/sr)^2' did not parse as fits unit") def test_niriss_parser(mosviz_helper, tmp_path): ''' Tests loading a NIRISS dataset @@ -177,7 +174,6 @@ def test_niriss_parser(mosviz_helper, tmp_path): @pytest.mark.remote_data -@pytest.mark.filterwarnings('ignore', match="'(MJy/sr)^2' did not parse as fits unit") def test_nircam_parser(mosviz_helper, tmp_path): ''' Tests loading a NIRCam dataset @@ -216,8 +212,6 @@ def test_missing_srctype(mosviz_helper, tmp_path): This dataset was our original simulated NIRISS dataset that is missing SRCTYPE. - NOTE: Under some conditions, a warning is raised when TemporaryDirectory attempts to - clean itself up. Most likely a race condition between TempDir and pytest ''' # Download data From c4da906eb3234d9d6681c80e5830bb0afc23d0d5 Mon Sep 17 00:00:00 2001 From: "P. L. Lim" <2090236+pllim@users.noreply.github.com> Date: Thu, 13 Apr 2023 11:10:08 -0400 Subject: [PATCH 04/11] Fix single image loading for mix-and-match Update change log --- CHANGES.rst | 2 + jdaviz/configs/mosviz/helper.py | 109 ++++++++++++------ .../configs/mosviz/tests/test_data_loading.py | 12 +- notebooks/MosvizExample.ipynb | 8 +- 4 files changed, 89 insertions(+), 42 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 032e0ca99a..67c32ed7e4 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -72,6 +72,8 @@ Imviz Mosviz ^^^^^^ +- Fixed several data loader bugs for uncommon use cases. [#2146] + Specviz ^^^^^^^ diff --git a/jdaviz/configs/mosviz/helper.py b/jdaviz/configs/mosviz/helper.py index 3a7bc09ed3..1686bb8944 100644 --- a/jdaviz/configs/mosviz/helper.py +++ b/jdaviz/configs/mosviz/helper.py @@ -1,3 +1,4 @@ +import os import warnings from copy import deepcopy from pathlib import Path @@ -18,7 +19,8 @@ from jdaviz.configs.specviz.helper import _apply_redshift_to_spectra from jdaviz.configs.specviz2d import Specviz2d from jdaviz.configs.mosviz.plugins import jwst_header_to_skyregion -from jdaviz.configs.mosviz.plugins.parsers import FALLBACK_NAME +from jdaviz.configs.mosviz.plugins.parsers import ( + FALLBACK_NAME, mos_spec1d_parser, mos_spec2d_parser) from jdaviz.configs.default.plugins.line_lists.line_list_mixin import LineListMixin __all__ = ['Mosviz'] @@ -406,7 +408,7 @@ def _get_sp_attribute(table_data, row, attr, fill=None): def load_data(self, spectra_1d=None, spectra_2d=None, images=None, spectra_1d_label=None, spectra_2d_label=None, - images_label=None, *args, **kwargs): + images_label=None, directory=None, instrument=None): """ Load and parse a set of MOS spectra and images. @@ -445,43 +447,53 @@ def load_data(self, spectra_1d=None, spectra_2d=None, images=None, directory : str, optional Instead of loading lists of spectra and images, the path to a directory containing all files for a single JWST observation may be given. + If this is provided, all the above inputs are ignored. instrument : {'niriss', 'nircam', 'nirspec'}, optional - Required if ``directory`` is specified. Value is not case sensitive. + Required and only used if ``directory`` is specified. Value is not case sensitive. """ # Link data after everything is loaded self.app.auto_link = False allow_link_table = True - directory = kwargs.pop('directory', None) - instrument = kwargs.pop('instrument', None) - if instrument is not None: + if isinstance(instrument, str): instrument = instrument.lower() - if directory is not None and Path(directory).is_dir(): - if instrument not in ('nirspec', 'niriss', 'nircam'): - raise ValueError( - "Ambiguous MOS Instrument: Only JWST NIRSpec, NIRCam, and " - f"NIRISS folder parsing are currently supported but got '{instrument}'") - if instrument == "nirspec": - super().load_data(directory, parser_reference="mosviz-nirspec-directory-parser") - else: # niriss or nircam - self.load_jwst_directory(directory, instrument=instrument) - elif directory is not None and is_zipfile(str(directory)): - raise TypeError("Please extract your data first and provide the directory") + if images is not None and not isinstance(images, (list, tuple)): + single_image = True + else: + single_image = False + + if directory is not None: + if is_zipfile(str(directory)): + raise TypeError("Please extract your data first and provide the directory") + elif os.path.isdir(directory): + if instrument not in ('nirspec', 'niriss', 'nircam'): + raise ValueError( + "Ambiguous MOS Instrument: Only JWST NIRSpec, NIRCam, and " + f"NIRISS folder parsing are currently supported but got '{instrument}'") + if instrument == "nirspec": + super().load_data(directory, parser_reference="mosviz-nirspec-directory-parser") + else: # niriss or nircam + self.load_jwst_directory(directory, instrument=instrument) + else: + raise NotImplementedError(f"{directory} is not a directory") + + # For the following, always load in this order: 1d, 2d, images, metadata elif (spectra_1d is not None and spectra_2d is not None and images is not None): - # If we have a single image for multiple spectra, tell the table viewer - if not isinstance(images, (list, tuple)) and isinstance(spectra_1d, (list, tuple)): + n_specs = self.load_1d_spectra(spectra_1d, spectra_1d_label) + self.load_2d_spectra(spectra_2d, spectra_2d_label) + + # If we have a single image for multiple spectra, tell the table viewer. + if single_image and n_specs > 1: self._shared_image = True self.app.get_viewer(self._default_table_viewer_reference_name)._shared_image = True - self.load_images(images, images_label, share_image=len(spectra_1d)) + self.load_images(images, images_label, share_image=n_specs) else: self.load_images(images, images_label) - self.load_2d_spectra(spectra_2d, spectra_2d_label) - self.load_1d_spectra(spectra_1d, spectra_1d_label) self.load_metadata() elif spectra_1d is not None and spectra_2d is not None: @@ -490,13 +502,29 @@ def load_data(self, spectra_1d=None, spectra_2d=None, images=None, self.load_metadata() elif spectra_1d and images: - self.load_1d_spectra(spectra_1d, spectra_1d_label) - self.load_images(images, images_label) + n_specs = self.load_1d_spectra(spectra_1d, spectra_1d_label) + + # If we have a single image for multiple spectra, tell the table viewer. + if single_image and n_specs > 1: + self._shared_image = True + self.app.get_viewer(self._default_table_viewer_reference_name)._shared_image = True + self.load_images(images, images_label, share_image=n_specs) + else: + self.load_images(images, images_label) + allow_link_table = False elif spectra_2d and images: - self.load_2d_spectra(spectra_2d, spectra_2d_label) - self.load_images(images, images_label) + n_specs = self.load_2d_spectra(spectra_2d, spectra_2d_label) + + # If we have a single image for multiple spectra, tell the table viewer. + if single_image and n_specs > 1: + self._shared_image = True + self.app.get_viewer(self._default_table_viewer_reference_name)._shared_image = True + self.load_images(images, images_label, share_image=n_specs) + else: + self.load_images(images, images_label) + allow_link_table = False elif spectra_1d: @@ -508,10 +536,7 @@ def load_data(self, spectra_1d=None, spectra_2d=None, images=None, allow_link_table = False else: - self.app.hub.broadcast(SnackbarMessage( - "Warning: Please set valid values for the load_data() method", - color='warning', sender=self)) - return + raise NotImplementedError("Please set valid values for the Mosviz.load_data() method") if allow_link_table: self.link_table_data(None) @@ -534,8 +559,8 @@ def load_data(self, spectra_1d=None, spectra_2d=None, images=None, self.app.get_viewer(self._default_table_viewer_reference_name).figure_widget.highlighted = 0 # Notify the user that this all loaded successfully - loaded_msg = SnackbarMessage("MOS data loaded successfully", color="success", sender=self) - self.app.hub.broadcast(loaded_msg) + self.app.hub.broadcast(SnackbarMessage( + "MOS data loaded successfully", color="success", sender=self)) self._default_visible_columns = self.get_column_names(True) @@ -606,11 +631,17 @@ def load_1d_spectra(self, data_obj, data_labels=None, add_redshift_column=False) for each item in ``data_obj`` if ``data_obj`` is a list. add_redshift_column : bool Add redshift column to Mosviz table. + + Returns + ------- + n_specs : int + Number of data objects loaded. + """ - super().load_data(data_obj, parser_reference="mosviz-spec1d-parser", - data_labels=data_labels) + n_specs = mos_spec1d_parser(self.app, data_obj, data_labels=data_labels) if add_redshift_column: self._add_redshift_column() + return n_specs def load_2d_spectra(self, data_obj, data_labels=None, add_redshift_column=False): """ @@ -628,11 +659,17 @@ def load_2d_spectra(self, data_obj, data_labels=None, add_redshift_column=False) for each item in ``data_obj`` if ``data_obj`` is a list. add_redshift_column : bool Add redshift column to Mosviz table. + + Returns + ------- + n_specs : int + Number of data objects loaded. + """ - super().load_data(data_obj, parser_reference="mosviz-spec2d-parser", - data_labels=data_labels) + n_specs = mos_spec2d_parser(self.app, data_obj, data_labels=data_labels) if add_redshift_column: self._add_redshift_column() + return n_specs def load_jwst_directory(self, data_obj, data_labels=None, instrument=None, add_redshift_column=False): diff --git a/jdaviz/configs/mosviz/tests/test_data_loading.py b/jdaviz/configs/mosviz/tests/test_data_loading.py index ddf48ec987..9a5ab2ca5d 100644 --- a/jdaviz/configs/mosviz/tests/test_data_loading.py +++ b/jdaviz/configs/mosviz/tests/test_data_loading.py @@ -209,7 +209,7 @@ def test_load_single_image_multi_spec(mosviz_helper, mos_image, spectrum1d, mos_ label_mouseover._viewer_mouse_event(spec2d_viewer, {'event': 'mousemove', 'domain': {'x': 10, 'y': 100}}) assert label_mouseover.as_text() == ('Pixel x=00010.0 y=00100.0 Value +8.12986e-01', '', '') - assert label_mouseover.icon == 'b' + assert label_mouseover.icon == 'c' # need to trigger a mouseleave or mouseover to reset the traitlets label_mouseover._viewer_mouse_event(spec1d_viewer, {'event': 'mouseenter'}) @@ -218,7 +218,7 @@ def test_load_single_image_multi_spec(mosviz_helper, mos_image, spectrum1d, mos_ assert label_mouseover.as_text() == ('Cursor 7.00000e+03, 1.70000e+02', 'Wave 6.88889e+03 Angstrom (4 pix)', 'Flux 1.35436e+01 Jy') - assert label_mouseover.icon == 'c' + assert label_mouseover.icon == 'b' def test_zip_error(mosviz_helper, tmp_path): @@ -232,3 +232,11 @@ def test_zip_error(mosviz_helper, tmp_path): with pytest.raises(TypeError, match="Please extract"): mosviz_helper.load_data(directory=str(zip_path)) + + +def test_invalid_inputs(mosviz_helper): + with pytest.raises(NotImplementedError, match=r".*not a directory"): + mosviz_helper.load_data(directory="foo") + + with pytest.raises(NotImplementedError, match="Please set valid values"): + mosviz_helper.load_data() diff --git a/notebooks/MosvizExample.ipynb b/notebooks/MosvizExample.ipynb index ee836fd95c..2b667033e8 100644 --- a/notebooks/MosvizExample.ipynb +++ b/notebooks/MosvizExample.ipynb @@ -201,12 +201,12 @@ "source": [ "# *** OR ***\n", "# Enable and run this cell if you want to load them separately.\n", - "# On the last loader call, also add redshift column.\n", + "# On the last data loader call, also add redshift column.\n", "\n", - "mosviz.load_metadata(images) \n", "mosviz.load_1d_spectra(spectra_1d)\n", "mosviz.load_2d_spectra(spectra_2d)\n", - "mosviz.load_images(images, add_redshift_column=True)" + "mosviz.load_images(images, add_redshift_column=True)\n", + "mosviz.load_metadata(images)" ] }, { @@ -241,7 +241,7 @@ "source": [ "# *** OR ***\n", "# Enable and run this cell if you want to load them separately.\n", - "# On the last loader call, also add redshift column.\n", + "# On the last data loader call, also add redshift column.\n", "\n", "mosviz_no_images.load_1d_spectra(spectra_1d)\n", "mosviz_no_images.load_2d_spectra(spectra_2d, add_redshift_column=True)" From 1836b8b49f1840ea90d03f057705b63f7afc3d47 Mon Sep 17 00:00:00 2001 From: "P. L. Lim" <2090236+pllim@users.noreply.github.com> Date: Thu, 13 Apr 2023 14:53:21 -0400 Subject: [PATCH 05/11] Fix Mosviz image zoom to target and disable image viewer axes --- jdaviz/configs/mosviz/helper.py | 96 +++++++++++++----------- jdaviz/configs/mosviz/plugins/parsers.py | 2 + jdaviz/configs/mosviz/plugins/viewers.py | 29 ++++--- 3 files changed, 74 insertions(+), 53 deletions(-) diff --git a/jdaviz/configs/mosviz/helper.py b/jdaviz/configs/mosviz/helper.py index 1686bb8944..c0d38a86b3 100644 --- a/jdaviz/configs/mosviz/helper.py +++ b/jdaviz/configs/mosviz/helper.py @@ -290,31 +290,32 @@ def _row_click_message_handler(self, msg): def _handle_image_zoom(self, msg): mos_data = self.app.data_collection['MOS Table'] - # trigger zooming the image, if there is an image - if mos_data.find_component_id("Images") is not None: - if msg.shared_image: - center, height = self._zoom_to_object_params(msg) - else: - try: - center, height = self._zoom_to_slit_params(msg) - except IndexError: - # If there's nothing in the spectrum2d viewer, we can't get slit info - return - - if center is None or height is None: - # Can't zoom if we couldn't figure out where to zoom (e.g. if RA/Dec not in table) - return + if mos_data.find_component_id("Images") is None: + return + + imview = self.app.get_viewer(self._default_image_viewer_reference_name) - imview = self.app.get_viewer(self._default_image_viewer_reference_name) + # trigger zooming the image, if there is an image + if msg.shared_image: + center, height = self._zoom_to_object_params(msg) + else: + center, height = self._zoom_to_slit_params(msg) - image_axis_ratio = ((imview.axis_x.scale.max - imview.axis_x.scale.min) / - (imview.axis_y.scale.max - imview.axis_y.scale.min)) + if height is not None: + image_axis_ratio = (abs(imview.state.x_max - imview.state.x_min) / + abs(imview.state.y_max - imview.state.y_min)) + xh = image_axis_ratio * height + cur_xcen = (imview.state.x_min + imview.state.x_max) * 0.5 + cur_ycen = (imview.state.y_min + imview.state.y_max) * 0.5 with delay_callback(imview.state, 'x_min', 'x_max', 'y_min', 'y_max'): - imview.state.x_min = center[0] - image_axis_ratio*height - imview.state.y_min = center[1] - height - imview.state.x_max = center[0] + image_axis_ratio*height - imview.state.y_max = center[1] + height + imview.state.x_min = cur_xcen - xh + imview.state.y_min = cur_ycen - height + imview.state.x_max = cur_xcen + xh + imview.state.y_max = cur_ycen + height + + if center is not None: + imview.center_on(center) def _handle_flipped_data(self): # Workaround for flipped data @@ -327,7 +328,6 @@ def _handle_flipped_data(self): def _zoom_to_object_params(self, msg): table_data = self.app.data_collection['MOS Table'] - imview = self.app.get_viewer(self._default_image_viewer_reference_name) specview = self.app.get_viewer(self._default_spectrum_2d_viewer_reference_name) if ("R.A." not in table_data.component_ids() or @@ -340,12 +340,16 @@ def _zoom_to_object_params(self, msg): if (ra == FALLBACK_NAME) or (dec == FALLBACK_NAME): return None, None - pixel_height = 0.5*(specview.axis_y.scale.max - specview.axis_y.scale.min) - point = SkyCoord(ra*u.deg, dec*u.deg) + try: + pixel_height = abs(specview.axis_y.scale.max - specview.axis_y.scale.min) * 0.5 + except Exception: + pixel_height = None + else: + if pixel_height < 1: + pixel_height = None + sky = SkyCoord(ra, dec, unit='deg') - pix = imview.layers[0].layer.coords.world_to_pixel(point) - - return pix, pixel_height + return sky, pixel_height def _zoom_to_slit_params(self, msg): imview = self.app.get_viewer(self._default_image_viewer_reference_name) @@ -353,20 +357,15 @@ def _zoom_to_slit_params(self, msg): try: sky_region = jwst_header_to_skyregion(specview.layers[0].layer.meta) - except KeyError: + except Exception: # If the header didn't have slit params, can't zoom to it. return None, None - ra = sky_region.center.ra.deg - dec = sky_region.center.dec.deg - - pix = imview.layers[0].layer.coords.world_to_pixel(sky_region.center) - # Height of slit in decimal degrees - height = sky_region.height.deg - - upper = imview.layers[0].layer.coords.world_to_pixel(SkyCoord(ra*u.deg, - (dec + height)*u.deg)) - pixel_height = upper[1] - pix[1] + sky = sky_region.center + w = imview.layers[0].layer.coords + pix = w.world_to_pixel(sky) + upper = w.world_to_pixel(SkyCoord(sky.ra, sky.dec + sky_region.height)) + pixel_height = abs(upper[1] - pix[1]) # y return pix, pixel_height @@ -487,10 +486,13 @@ def load_data(self, spectra_1d=None, spectra_2d=None, images=None, self.load_2d_spectra(spectra_2d, spectra_2d_label) # If we have a single image for multiple spectra, tell the table viewer. - if single_image and n_specs > 1: + if single_image: self._shared_image = True self.app.get_viewer(self._default_table_viewer_reference_name)._shared_image = True - self.load_images(images, images_label, share_image=n_specs) + if n_specs > 1: + self.load_images(images, images_label, share_image=n_specs) + else: + self.load_images(images, images_label) else: self.load_images(images, images_label) @@ -505,10 +507,13 @@ def load_data(self, spectra_1d=None, spectra_2d=None, images=None, n_specs = self.load_1d_spectra(spectra_1d, spectra_1d_label) # If we have a single image for multiple spectra, tell the table viewer. - if single_image and n_specs > 1: + if single_image: self._shared_image = True self.app.get_viewer(self._default_table_viewer_reference_name)._shared_image = True - self.load_images(images, images_label, share_image=n_specs) + if n_specs > 1: + self.load_images(images, images_label, share_image=n_specs) + else: + self.load_images(images, images_label) else: self.load_images(images, images_label) @@ -518,10 +523,13 @@ def load_data(self, spectra_1d=None, spectra_2d=None, images=None, n_specs = self.load_2d_spectra(spectra_2d, spectra_2d_label) # If we have a single image for multiple spectra, tell the table viewer. - if single_image and n_specs > 1: + if single_image: self._shared_image = True self.app.get_viewer(self._default_table_viewer_reference_name)._shared_image = True - self.load_images(images, images_label, share_image=n_specs) + if n_specs > 1: + self.load_images(images, images_label, share_image=n_specs) + else: + self.load_images(images, images_label) else: self.load_images(images, images_label) diff --git a/jdaviz/configs/mosviz/plugins/parsers.py b/jdaviz/configs/mosviz/plugins/parsers.py index fe577db5ae..137785a667 100644 --- a/jdaviz/configs/mosviz/plugins/parsers.py +++ b/jdaviz/configs/mosviz/plugins/parsers.py @@ -180,6 +180,8 @@ def mos_nirspec_directory_parser(app, data_obj, data_labels=None): # The amount of images needs to be equal to the amount of rows # of the other columns in the table if n_images == 1: + app._jdaviz_helper._shared_image = True + app.get_viewer(app._jdaviz_helper._default_table_viewer_reference_name)._shared_image = True # noqa: E501 if n_specs > 1: kwargs = {'share_image': n_specs} else: diff --git a/jdaviz/configs/mosviz/plugins/viewers.py b/jdaviz/configs/mosviz/plugins/viewers.py index 98a89d810d..e70e585e70 100644 --- a/jdaviz/configs/mosviz/plugins/viewers.py +++ b/jdaviz/configs/mosviz/plugins/viewers.py @@ -6,6 +6,7 @@ from jdaviz.core.events import (AddDataToViewerMessage, RemoveDataFromViewerMessage, TableClickMessage) +from jdaviz.core.astrowidgets_api import AstrowidgetsImageViewerMixin from jdaviz.core.registries import viewer_registry from jdaviz.core.freezable_state import FreezableBqplotImageViewerState from jdaviz.configs.default.plugins.viewers import JdavizViewerMixin @@ -15,7 +16,7 @@ @viewer_registry("mosviz-image-viewer", label="Image 2D (Mosviz)") -class MosvizImageView(JdavizViewerMixin, BqplotImageView): +class MosvizImageView(JdavizViewerMixin, BqplotImageView, AstrowidgetsImageViewerMixin): # categories: zoom resets, zoom, pan, subset, select tools, shortcuts tools_nested = [ ['jdaviz:homezoom', 'jdaviz:prevzoom'], @@ -28,23 +29,33 @@ class MosvizImageView(JdavizViewerMixin, BqplotImageView): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) + self.init_astrowidgets_api() self._subscribe_to_layers_update() + self.state.show_axes = False # Axes are wrong anyway + self.figure.fig_margin = {'left': 0, 'bottom': 0, 'top': 0, 'right': 0} + def data(self, cls=None): return [layer_state.layer # .get_object(cls=cls or self.default_class) for layer_state in self.state.layers if hasattr(layer_state, 'layer') and isinstance(layer_state.layer, BaseData)] - def set_plot_axes(self): - self.figure.axes[1].tick_format = None - self.figure.axes[0].tick_format = None + def _mark_targets(self): + table_data = self.jdaviz_app.data_collection['MOS Table'] - self.figure.axes[1].label = "y: pixels" - self.figure.axes[0].label = "x: pixels" + if ("R.A." not in table_data.component_ids() or + "Dec." not in table_data.component_ids()): + return - # Make it so y axis label is not covering tick numbers. - self.figure.axes[1].label_offset = "-50" + from astropy.coordinates import SkyCoord + from astropy.table import QTable + + ra = table_data["R.A."] + dec = table_data["Dec."] + sky = SkyCoord(ra, dec, unit='deg') + t = QTable({'coord': sky}) + self.add_markers(t, use_skycoord=True, marker_name='Targets') @viewer_registry("mosviz-profile-2d-viewer", label="Spectrum 2D (Mosviz)") @@ -134,7 +145,7 @@ def redraw(self): else: components_str = [cid.label for cid in self.figure_widget.data.main_components] hidden = [] - for colname in ['Images', '1D Spectra', '2D Spectra']: + for colname in ('Images', '1D Spectra', '2D Spectra'): if colname in components_str: hidden.append(self.figure_widget.data.id[colname]) self.figure_widget.hidden_components = hidden From 2efa57cf38a659de422f595fb7e0108bdf514c50 Mon Sep 17 00:00:00 2001 From: "P. L. Lim" <2090236+pllim@users.noreply.github.com> Date: Mon, 17 Apr 2023 13:09:50 -0400 Subject: [PATCH 06/11] BUG: images not returning Path objects --- jdaviz/configs/mosviz/plugins/parsers.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/jdaviz/configs/mosviz/plugins/parsers.py b/jdaviz/configs/mosviz/plugins/parsers.py index 137785a667..6b6259cea7 100644 --- a/jdaviz/configs/mosviz/plugins/parsers.py +++ b/jdaviz/configs/mosviz/plugins/parsers.py @@ -1,6 +1,5 @@ from collections.abc import Iterable import csv -import glob import os from pathlib import Path import warnings @@ -699,7 +698,7 @@ def _id_files_by_datamodl(label_dict, filepaths, catalog_key=None): if fp.is_dir(): # Potential names of subdirectories where images are stored if fp.name in ("cutouts", "mosviz_cutouts", "images"): - images = sorted([file_path for file_path in glob.iglob(str(fp / '*'))]) + images = sorted(fp.glob('*.fits*')) label_dict['Direct Image'] = images else: continue From f2b4b3788a45fbb2f33c765b4fbb6656559f0e7d Mon Sep 17 00:00:00 2001 From: "P. L. Lim" <2090236+pllim@users.noreply.github.com> Date: Tue, 18 Apr 2023 14:47:18 -0400 Subject: [PATCH 07/11] Fix English typo Co-authored-by: Ricky O'Steen <39831871+rosteen@users.noreply.github.com> --- docs/mosviz/import_data.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/mosviz/import_data.rst b/docs/mosviz/import_data.rst index 983ce46bd3..74621ca579 100644 --- a/docs/mosviz/import_data.rst +++ b/docs/mosviz/import_data.rst @@ -120,7 +120,7 @@ directory that contains all the files for the dataset to be loaded: mosviz.load_data(spectra_1d=spectra_1d, spectra_2d=spectra_2d, images=images) mosviz.show() -Alternative, if you want all the spectra to share a single image (e.g., a mosaic): +Alternatively, if you want all the spectra to share a single image (e.g., a mosaic): .. code-block:: python From 50e2dd1ff10a9f718c192740b9d01bd18e1eb830 Mon Sep 17 00:00:00 2001 From: "P. L. Lim" <2090236+pllim@users.noreply.github.com> Date: Tue, 18 Apr 2023 16:31:07 -0400 Subject: [PATCH 08/11] Add a note about _mark_targets that it is only for debugging --- jdaviz/configs/mosviz/plugins/viewers.py | 1 + 1 file changed, 1 insertion(+) diff --git a/jdaviz/configs/mosviz/plugins/viewers.py b/jdaviz/configs/mosviz/plugins/viewers.py index e70e585e70..db04f8ba34 100644 --- a/jdaviz/configs/mosviz/plugins/viewers.py +++ b/jdaviz/configs/mosviz/plugins/viewers.py @@ -41,6 +41,7 @@ def data(self, cls=None): if hasattr(layer_state, 'layer') and isinstance(layer_state.layer, BaseData)] + # NOTE: This is currently only for debugging. It is not used in app. def _mark_targets(self): table_data = self.jdaviz_app.data_collection['MOS Table'] From 485200caa9f5a08c4b0ea4288d7c812e02a76cf5 Mon Sep 17 00:00:00 2001 From: "P. L. Lim" <2090236+pllim@users.noreply.github.com> Date: Tue, 18 Apr 2023 17:04:23 -0400 Subject: [PATCH 09/11] Move local imports up and rename xh variable --- jdaviz/configs/mosviz/helper.py | 6 +++--- jdaviz/configs/mosviz/plugins/viewers.py | 5 ++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/jdaviz/configs/mosviz/helper.py b/jdaviz/configs/mosviz/helper.py index c0d38a86b3..eefc35a968 100644 --- a/jdaviz/configs/mosviz/helper.py +++ b/jdaviz/configs/mosviz/helper.py @@ -304,14 +304,14 @@ def _handle_image_zoom(self, msg): if height is not None: image_axis_ratio = (abs(imview.state.x_max - imview.state.x_min) / abs(imview.state.y_max - imview.state.y_min)) - xh = image_axis_ratio * height + x_height = image_axis_ratio * height cur_xcen = (imview.state.x_min + imview.state.x_max) * 0.5 cur_ycen = (imview.state.y_min + imview.state.y_max) * 0.5 with delay_callback(imview.state, 'x_min', 'x_max', 'y_min', 'y_max'): - imview.state.x_min = cur_xcen - xh + imview.state.x_min = cur_xcen - x_height imview.state.y_min = cur_ycen - height - imview.state.x_max = cur_xcen + xh + imview.state.x_max = cur_xcen + x_height imview.state.y_max = cur_ycen + height if center is not None: diff --git a/jdaviz/configs/mosviz/plugins/viewers.py b/jdaviz/configs/mosviz/plugins/viewers.py index db04f8ba34..9f943ae348 100644 --- a/jdaviz/configs/mosviz/plugins/viewers.py +++ b/jdaviz/configs/mosviz/plugins/viewers.py @@ -1,3 +1,5 @@ +from astropy.coordinates import SkyCoord +from astropy.table import QTable from glue.core import BaseData from glue_jupyter.bqplot.image import BqplotImageView from glue_jupyter.table import TableViewer @@ -49,9 +51,6 @@ def _mark_targets(self): "Dec." not in table_data.component_ids()): return - from astropy.coordinates import SkyCoord - from astropy.table import QTable - ra = table_data["R.A."] dec = table_data["Dec."] sky = SkyCoord(ra, dec, unit='deg') From c36b7b927bd0dd0f21e5eb9dc2f6a3e057510819 Mon Sep 17 00:00:00 2001 From: "P. L. Lim" <2090236+pllim@users.noreply.github.com> Date: Tue, 18 Apr 2023 17:29:20 -0400 Subject: [PATCH 10/11] TST: Warning gone from test_slice --- jdaviz/configs/cubeviz/plugins/slice/tests/test_slice.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/jdaviz/configs/cubeviz/plugins/slice/tests/test_slice.py b/jdaviz/configs/cubeviz/plugins/slice/tests/test_slice.py index 2ccd36d9ff..bc30f7d8cb 100644 --- a/jdaviz/configs/cubeviz/plugins/slice/tests/test_slice.py +++ b/jdaviz/configs/cubeviz/plugins/slice/tests/test_slice.py @@ -56,8 +56,7 @@ def test_slice(cubeviz_helper, spectrum1d_cube): # test setting a static 2d image to the "watched" flux viewer to make sure it disconnects mm = app.get_tray_item_from_name('cubeviz-moment-maps') mm.add_to_viewer_selected = 'flux-viewer' - with pytest.warns(UserWarning, match='No observer defined on WCS'): - mm.vue_calculate_moment() + mm.vue_calculate_moment() assert len(sl._watched_viewers) == 2 assert len(sl._indicator_viewers) == 1 From 036203746c6f209ece3d8939f7c13d7e23782681 Mon Sep 17 00:00:00 2001 From: "P. L. Lim" <2090236+pllim@users.noreply.github.com> Date: Tue, 18 Apr 2023 17:42:41 -0400 Subject: [PATCH 11/11] OMG now warning is back so we just blindly ignore, not caring if it shows up or not --- jdaviz/configs/cubeviz/plugins/slice/tests/test_slice.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/jdaviz/configs/cubeviz/plugins/slice/tests/test_slice.py b/jdaviz/configs/cubeviz/plugins/slice/tests/test_slice.py index bc30f7d8cb..f642cfd370 100644 --- a/jdaviz/configs/cubeviz/plugins/slice/tests/test_slice.py +++ b/jdaviz/configs/cubeviz/plugins/slice/tests/test_slice.py @@ -1,3 +1,5 @@ +import warnings + import pytest import numpy as np @@ -56,7 +58,9 @@ def test_slice(cubeviz_helper, spectrum1d_cube): # test setting a static 2d image to the "watched" flux viewer to make sure it disconnects mm = app.get_tray_item_from_name('cubeviz-moment-maps') mm.add_to_viewer_selected = 'flux-viewer' - mm.vue_calculate_moment() + with warnings.catch_warnings(): + warnings.filterwarnings('ignore', message=r'.*No observer defined on WCS.*') + mm.vue_calculate_moment() assert len(sl._watched_viewers) == 2 assert len(sl._indicator_viewers) == 1