From 17f542c5c0433bf8a407d0325d7f0950e3247656 Mon Sep 17 00:00:00 2001 From: "Pey Lian Lim (Github)" <2090236+pllim@users.noreply.github.com> Date: Wed, 18 May 2022 17:04:19 -0400 Subject: [PATCH] Inherit FITS header comment cards. Have show_primary be more sticky. --- jdaviz/configs/cubeviz/plugins/parsers.py | 16 +++--- .../metadata_viewer/metadata_viewer.py | 28 +++++++--- .../metadata_viewer/metadata_viewer.vue | 2 + .../tests/test_metadata_viewer.py | 53 ++++++++++++++----- jdaviz/configs/imviz/plugins/parsers.py | 10 ++-- jdaviz/configs/mosviz/plugins/parsers.py | 31 +++++------ jdaviz/configs/mosviz/tests/test_parsers.py | 7 ++- jdaviz/configs/specviz/plugins/parsers.py | 9 ++-- jdaviz/configs/specviz2d/plugins/parsers.py | 6 +-- jdaviz/tests/test_metadata.py | 52 ++++++++++++++++++ jdaviz/utils.py | 27 +++++++++- 11 files changed, 179 insertions(+), 62 deletions(-) create mode 100644 jdaviz/tests/test_metadata.py diff --git a/jdaviz/configs/cubeviz/plugins/parsers.py b/jdaviz/configs/cubeviz/plugins/parsers.py index b0aaf07ef8..bce4ed9277 100644 --- a/jdaviz/configs/cubeviz/plugins/parsers.py +++ b/jdaviz/configs/cubeviz/plugins/parsers.py @@ -9,7 +9,7 @@ from specutils import Spectrum1D from jdaviz.core.registries import data_parser_registry -from jdaviz.utils import PRIHDR_KEY +from jdaviz.utils import standardize_metadata, PRIHDR_KEY __all__ = ['parse_data'] @@ -117,9 +117,9 @@ def _parse_hdulist(app, hdulist, file_name=None): flux = hdu.data << flux_unit - metadata = dict(hdu.header) + metadata = standardize_metadata(hdu.header) if hdu.name != 'PRIMARY' and 'PRIMARY' in hdulist: - metadata[PRIHDR_KEY] = dict(hdulist['PRIMARY'].header) + metadata[PRIHDR_KEY] = standardize_metadata(hdulist['PRIMARY'].header) try: sc = Spectrum1D(flux=flux, wcs=wcs, meta=metadata) @@ -167,9 +167,9 @@ def _parse_jwst_s3d(app, hdulist, data_label, ext='SCI', viewer_name='flux-viewe flux = hdulist[ext].data << unit wcs = WCS(hdulist['SCI'].header, hdulist) # Everything uses SCI WCS - metadata = dict(hdulist[ext].header) + metadata = standardize_metadata(hdulist[ext].header) if hdulist[ext].name != 'PRIMARY' and 'PRIMARY' in hdulist: - metadata[PRIHDR_KEY] = dict(hdulist['PRIMARY'].header) + metadata[PRIHDR_KEY] = standardize_metadata(hdulist['PRIMARY'].header) data = Spectrum1D(flux, wcs=wcs, meta=metadata) @@ -205,10 +205,10 @@ def _parse_esa_s3d(app, hdulist, data_label, ext='DATA', viewer_name='flux-viewe flux = np.moveaxis(flux, 0, -1) flux = np.swapaxes(flux, 0, 1) - metadata = dict(hdulist[ext].header) + metadata = standardize_metadata(hdulist[ext].header) metadata.update(wcs_dict) # To be internally consistent if hdulist[ext].name != 'PRIMARY' and 'PRIMARY' in hdulist: - metadata[PRIHDR_KEY] = dict(hdulist['PRIMARY'].header) + metadata[PRIHDR_KEY] = standardize_metadata(hdulist['PRIMARY'].header) data = Spectrum1D(flux, wcs=wcs, meta=metadata) @@ -241,7 +241,7 @@ def _parse_spectrum1d_3d(app, file_obj, data_label=None): flux = np.moveaxis(flux, 1, 0) - s1d = Spectrum1D(flux=flux, wcs=file_obj.wcs, meta=file_obj.meta) + s1d = Spectrum1D(flux=flux, wcs=file_obj.wcs, meta=standardize_metadata(file_obj.meta)) cur_data_label = f"{data_label}[{attr.upper()}]" app.add_data(s1d, cur_data_label) diff --git a/jdaviz/configs/default/plugins/metadata_viewer/metadata_viewer.py b/jdaviz/configs/default/plugins/metadata_viewer/metadata_viewer.py index a8e368e731..f683217bfe 100644 --- a/jdaviz/configs/default/plugins/metadata_viewer/metadata_viewer.py +++ b/jdaviz/configs/default/plugins/metadata_viewer/metadata_viewer.py @@ -2,7 +2,7 @@ from jdaviz.core.registries import tray_registry from jdaviz.core.template_mixin import TemplateMixin, DatasetSelectMixin -from jdaviz.utils import PRIHDR_KEY +from jdaviz.utils import PRIHDR_KEY, COMMENTCARD_KEY __all__ = ['MetadataViewer'] @@ -13,6 +13,7 @@ class MetadataViewer(TemplateMixin, DatasetSelectMixin): has_metadata = Bool(False).tag(sync=True) has_primary = Bool(False).tag(sync=True) show_primary = Bool(False).tag(sync=True) + has_comments = Bool(False).tag(sync=True) metadata = List([]).tag(sync=True) def __init__(self, *args, **kwargs): @@ -24,6 +25,7 @@ def reset(self): self.has_metadata = False self.has_primary = False self.show_primary = False + self.has_comments = False self.metadata = [] @observe("dataset_selected") @@ -38,9 +40,9 @@ def show_metadata(self, event): self.has_primary = True else: self.has_primary = False + self.show_primary = False - self.show_primary = False - self.find_public_metadata(data.meta, primary_only=False) + self.find_public_metadata(data.meta, primary_only=self.show_primary) @observe("show_primary") def handle_show_primary(self, event): @@ -61,8 +63,7 @@ def find_public_metadata(self, meta, primary_only=False): if PRIHDR_KEY in meta: meta = meta[PRIHDR_KEY] else: - self.metadata = [] - self.has_metadata = False + self.reset() return d = flatten_nested_dict(meta) @@ -73,8 +74,23 @@ def find_public_metadata(self, meta, primary_only=False): if badkey in d: del d[badkey] + if COMMENTCARD_KEY in meta: + self.has_comments = True + + def get_comment(key): + if key in meta[COMMENTCARD_KEY]._header: + val = meta[COMMENTCARD_KEY][key] + else: + val = '' + return val + else: + self.has_comments = False + + def get_comment(key): + return '' + # TODO: Option to not sort? - public_meta = sorted(zip(d.keys(), map(str, d.values()))) + public_meta = sorted(zip(d.keys(), map(str, d.values()), map(get_comment, d.keys()))) if len(public_meta) > 0: self.metadata = public_meta self.has_metadata = True diff --git a/jdaviz/configs/default/plugins/metadata_viewer/metadata_viewer.vue b/jdaviz/configs/default/plugins/metadata_viewer/metadata_viewer.vue index c59495c35e..394c6050cb 100644 --- a/jdaviz/configs/default/plugins/metadata_viewer/metadata_viewer.vue +++ b/jdaviz/configs/default/plugins/metadata_viewer/metadata_viewer.vue @@ -28,6 +28,7 @@ Key Value + Comment {{ item[0] }} {{ item[1] }} + {{ item[2] }} diff --git a/jdaviz/configs/default/plugins/metadata_viewer/tests/test_metadata_viewer.py b/jdaviz/configs/default/plugins/metadata_viewer/tests/test_metadata_viewer.py index 0f6875a515..33ec26697e 100644 --- a/jdaviz/configs/default/plugins/metadata_viewer/tests/test_metadata_viewer.py +++ b/jdaviz/configs/default/plugins/metadata_viewer/tests/test_metadata_viewer.py @@ -1,56 +1,82 @@ import numpy as np import pytest +from astropy.io import fits from astropy.nddata import NDData from jdaviz.configs.default.plugins.metadata_viewer.metadata_viewer import MetadataViewer -from jdaviz.utils import PRIHDR_KEY def test_view_dict(imviz_helper): mv = MetadataViewer(app=imviz_helper.app) - arr = np.zeros((2, 2)) + arr = np.zeros((2, 2), dtype=np.float32) ndd_1 = NDData(arr, meta={ 'EXTNAME': 'SCI', 'EXTVER': 1, 'BAR': 10.0, '_hidden': 'no show', 'HISTORY': 'Hmm', '': 'Invalid', 'FOO': '', 'COMMENT': 'a test', 'BOZO': None}) ndd_2 = NDData(arr, meta={ 'EXTNAME': 'ASDF', 'REF': {'bar': 10.0, 'foo': {'1': '', '2': [1, 2]}}}) - ndd_3 = NDData(arr, meta={ - 'EXTVER': 1, 'EXTNAME': 'SCI', - PRIHDR_KEY: {'EXTNAME': 'PRIMARY', 'APERTURE': '#TODO', 'COMMENT': 'a test'}}) + + # MEF + ndd_3 = fits.HDUList([fits.PrimaryHDU(), fits.ImageHDU(arr)]) + ndd_3[1].name = 'DATA' + ndd_4 = fits.HDUList([fits.PrimaryHDU(), fits.ImageHDU(arr)]) + ndd_4[0].header['APERTURE'] = ('#TODO', 'Aperture') + ndd_4[1].name = 'DATA' + imviz_helper.load_data(ndd_1, data_label='has_simple_meta') imviz_helper.load_data(ndd_2, data_label='has_nested_meta') imviz_helper.load_data(ndd_3, data_label='has_primary') + imviz_helper.load_data(ndd_4, data_label='has_primary_2') imviz_helper.load_data(arr, data_label='no_meta') assert mv.dataset.labels == ['has_simple_meta[DATA]', 'has_nested_meta[DATA]', - 'has_primary[DATA]', 'no_meta'] + 'has_primary[DATA,1]', 'has_primary_2[DATA,1]', 'no_meta'] mv.dataset_selected = 'has_simple_meta[DATA]' assert not mv.has_primary assert not mv.show_primary + assert not mv.has_comments assert mv.has_metadata assert mv.metadata == [ - ('BAR', '10.0'), ('BOZO', 'None'), ('EXTNAME', 'SCI'), - ('EXTVER', '1'), ('FOO', '')], mv.metadata + ('BAR', '10.0', ''), ('BOZO', 'None', ''), ('EXTNAME', 'SCI', ''), + ('EXTVER', '1', ''), ('FOO', '', '')] mv.dataset_selected = 'has_nested_meta[DATA]' assert not mv.has_primary assert not mv.show_primary + assert not mv.has_comments assert mv.has_metadata assert mv.metadata == [ - ('EXTNAME', 'ASDF'), ('REF.bar', '10.0'), - ('REF.foo.1', ''), ('REF.foo.2.0', '1'), ('REF.foo.2.1', '2')], mv.metadata + ('EXTNAME', 'ASDF', ''), ('REF.bar', '10.0', ''), + ('REF.foo.1', '', ''), ('REF.foo.2.0', '1', ''), ('REF.foo.2.1', '2', '')] - mv.dataset_selected = 'has_primary[DATA]' + mv.dataset_selected = 'has_primary[DATA,1]' assert mv.has_primary assert not mv.show_primary + assert mv.has_comments assert mv.has_metadata - assert mv.metadata == [('EXTNAME', 'SCI'), ('EXTVER', '1')] + assert mv.metadata == [('BITPIX', '-32', 'array data type'), + ('EXTNAME', 'DATA', 'extension name'), + ('GCOUNT', '1', 'number of groups'), + ('NAXIS', '2', 'number of array dimensions'), + ('NAXIS1', '2', ''), ('NAXIS2', '2', ''), + ('PCOUNT', '0', 'number of parameters'), + ('XTENSION', 'IMAGE', 'Image extension')] mv.show_primary = True - assert mv.metadata == [('APERTURE', '#TODO'), ('EXTNAME', 'PRIMARY')] + assert mv.metadata == [('BITPIX', '8', 'array data type'), ('EXTEND', 'True', ''), + ('NAXIS', '0', 'number of array dimensions'), + ('SIMPLE', 'True', 'conforms to FITS standard')] + + mv.dataset_selected = 'has_primary_2[DATA,1]' + assert mv.show_primary # Make sure it sticks if possible + assert mv.has_comments + assert mv.metadata == [('APERTURE', '#TODO', 'Aperture'), + ('BITPIX', '8', 'array data type'), ('EXTEND', 'True', ''), + ('NAXIS', '0', 'number of array dimensions'), + ('SIMPLE', 'True', 'conforms to FITS standard')] mv.dataset_selected = 'no_meta' assert not mv.has_primary assert not mv.show_primary + assert not mv.has_comments assert not mv.has_metadata assert mv.metadata == [] @@ -65,5 +91,6 @@ def test_view_invalid(imviz_helper): assert mv.dataset_selected == '' assert not mv.has_primary assert not mv.show_primary + assert not mv.has_comments assert not mv.has_metadata assert mv.metadata == [] diff --git a/jdaviz/configs/imviz/plugins/parsers.py b/jdaviz/configs/imviz/plugins/parsers.py index 56c291ec3f..f08a4115e0 100644 --- a/jdaviz/configs/imviz/plugins/parsers.py +++ b/jdaviz/configs/imviz/plugins/parsers.py @@ -12,7 +12,7 @@ from jdaviz.core.registries import data_parser_registry from jdaviz.core.events import SnackbarMessage -from jdaviz.utils import PRIHDR_KEY +from jdaviz.utils import standardize_metadata, PRIHDR_KEY __all__ = ['parse_data'] @@ -230,7 +230,7 @@ def _jwst2data(file_obj, ext, data_label): with AsdfInFits.open(file_obj) as af: dm = af.tree dm_meta = af.tree["meta"] - data.meta.update(dm_meta) + data.meta.update(standardize_metadata(dm_meta)) if unit_attr in dm_meta: bunit = _validate_bunit(dm_meta[unit_attr], raise_error=False) @@ -287,8 +287,8 @@ def _hdu2data(hdu, data_label, hdulist, include_wcs=True): data = Data(label=new_data_label) if hdulist is not None and hdu.name != 'PRIMARY' and 'PRIMARY' in hdulist: - data.meta[PRIHDR_KEY] = dict(hdulist['PRIMARY'].header) - data.meta.update(dict(hdu.header)) + data.meta[PRIHDR_KEY] = standardize_metadata(hdulist['PRIMARY'].header) + data.meta.update(standardize_metadata(hdu.header)) if include_wcs: data.coords = WCS(hdu.header, hdulist) component = Component.autotyped(hdu.data, units=bunit) @@ -310,7 +310,7 @@ def _nddata_to_glue_data(ndd, data_label): comp_label = attrib.upper() cur_label = f'{data_label}[{comp_label}]' cur_data = Data(label=cur_label) - cur_data.meta.update(ndd.meta) + cur_data.meta.update(standardize_metadata(ndd.meta)) if ndd.wcs is not None: cur_data.coords = ndd.wcs raw_arr = arr diff --git a/jdaviz/configs/mosviz/plugins/parsers.py b/jdaviz/configs/mosviz/plugins/parsers.py index ee8bdf9e3a..ba2bc639cf 100644 --- a/jdaviz/configs/mosviz/plugins/parsers.py +++ b/jdaviz/configs/mosviz/plugins/parsers.py @@ -17,7 +17,7 @@ from jdaviz.configs.imviz.plugins.parsers import get_image_data_iterator from jdaviz.core.registries import data_parser_registry from jdaviz.core.events import SnackbarMessage -from jdaviz.utils import PRIHDR_KEY +from jdaviz.utils import standardize_metadata, PRIHDR_KEY __all__ = ['mos_spec1d_parser', 'mos_spec2d_parser', 'mos_image_parser'] @@ -223,9 +223,7 @@ def mos_spec1d_parser(app, data_obj, data_labels=None): for cur_data, cur_label in zip(data_obj, data_labels): # Make metadata layout conform with other viz. - if 'header' in cur_data.meta: - cur_data.meta.update(cur_data.meta['header']) - del cur_data.meta['header'] + cur_data.meta = standardize_metadata(cur_data.meta) app.add_data(cur_data, cur_label, notify_done=False) @@ -259,8 +257,8 @@ def _parse_as_spectrum1d(path): data = hdulist[1].data header = hdulist[1].header wcs = WCS(header) - metadata = dict(header) - metadata[PRIHDR_KEY] = dict(hdulist[0].header) + metadata = standardize_metadata(header) + metadata[PRIHDR_KEY] = standardize_metadata(hdulist[0].header) return Spectrum1D(data, wcs=wcs, meta=metadata) # Coerce into list-like object @@ -292,9 +290,7 @@ def _parse_as_spectrum1d(path): data = _parse_as_spectrum1d(data) # Make metadata layout conform with other viz. - if 'header' in data.meta: - data.meta.update(data.meta['header']) - del data.meta['header'] + data.meta = standardize_metadata(data.meta) # Set the instrument # TODO: this should not be set to nirspec for all datasets @@ -500,10 +496,10 @@ def _get_source_identifiers_by_hdu(hdus, filepaths=None, header_keys=['SOURCEID' # Fallback 1: filepath if only one is given # Fallback 2: filepath at indx, if list of files given # Fallback 3: If nothing else, just our fallback value - src_name = \ - os.path.basename(filepaths) if type(filepaths) is str \ - else os.path.basename(filepaths[indx]) if type(filepaths) is list \ - else FALLBACK_NAME + src_name = ( + os.path.basename(filepaths) if type(filepaths) is str + else os.path.basename(filepaths[indx]) if type(filepaths) is list + else FALLBACK_NAME) src_names.append(src_name) except Exception: # Source ID lookup shouldn't ever prevent target from loading. Downgrade all errors to @@ -639,7 +635,8 @@ def mos_niriss_parser(app, data_dir, obs_label=""): if temp[sci].header["SPORDER"] == 1: data = temp[sci].data - meta = temp[sci].header + meta = standardize_metadata(temp[sci].header) + meta[PRIHDR_KEY] = standardize_metadata(temp[0].header) # The wavelength is stored in a WAVELENGTH HDU. This is # a 2D array, but in order to be able to use Spectrum1D @@ -671,7 +668,7 @@ def mos_niriss_parser(app, data_dir, obs_label=""): # TODO: Remove this once valid SRCTYPE values are present in all headers for hdu in temp: if ("SRCTYPE" in hdu.header and - (hdu.header["SRCTYPE"] in ["POINT", "EXTENDED"])): + (hdu.header["SRCTYPE"] in ("POINT", "EXTENDED"))): pass else: hdu.header["SRCTYPE"] = "EXTENDED" @@ -686,9 +683,7 @@ def mos_niriss_parser(app, data_dir, obs_label=""): for spec in specs: # Make metadata layout conform with other viz. - if 'header' in spec.meta: - spec.meta.update(spec.meta['header']) - del spec.meta['header'] + spec.meta = standardize_metadata(spec.meta) if spec.meta['SPORDER'] == 1 and spec.meta['EXTNAME'] == "EXTRACT1D": label = f"{filter_name} Source {spec.meta['SOURCEID']} spec1d {orientation}" diff --git a/jdaviz/configs/mosviz/tests/test_parsers.py b/jdaviz/configs/mosviz/tests/test_parsers.py index b8ede93f58..d48d8c5d75 100644 --- a/jdaviz/configs/mosviz/tests/test_parsers.py +++ b/jdaviz/configs/mosviz/tests/test_parsers.py @@ -4,7 +4,7 @@ import pytest from astropy.utils.data import download_file -from jdaviz.utils import PRIHDR_KEY +from jdaviz.utils import PRIHDR_KEY, COMMENTCARD_KEY # This is another version of test_niriss_loader in test_data_loading.py @@ -27,16 +27,19 @@ def test_niriss_parser(mosviz_helper, tmpdir): dc_0 = mosviz_helper.app.data_collection[0] assert dc_0.label == "Image canucs F150W" assert PRIHDR_KEY not in dc_0.meta + assert COMMENTCARD_KEY not in dc_0.meta assert dc_0.meta['bunit_data'] == 'MJy/sr' # ASDF metadata dc_1 = mosviz_helper.app.data_collection[1] assert dc_1.label == 'F150W Source 1 spec2d C' - assert PRIHDR_KEY not in dc_1.meta + assert PRIHDR_KEY in dc_1.meta + assert COMMENTCARD_KEY in dc_1.meta assert dc_1.meta['SOURCEID'] == 1 dc_40 = mosviz_helper.app.data_collection[40] assert dc_40.label == 'F150W Source 1 spec1d C' assert PRIHDR_KEY not in dc_40.meta + assert COMMENTCARD_KEY in dc_40.meta assert 'header' not in dc_40.meta assert dc_40.meta['FILTER'] == 'GR150C' diff --git a/jdaviz/configs/specviz/plugins/parsers.py b/jdaviz/configs/specviz/plugins/parsers.py index 2315ea7a39..cf58384d75 100644 --- a/jdaviz/configs/specviz/plugins/parsers.py +++ b/jdaviz/configs/specviz/plugins/parsers.py @@ -8,6 +8,7 @@ from specutils import Spectrum1D, SpectrumList, SpectrumCollection from jdaviz.core.registries import data_parser_registry +from jdaviz.utils import standardize_metadata __all__ = ["specviz_spectrum1d_parser"] @@ -99,9 +100,7 @@ def specviz_spectrum1d_parser(app, data, data_label=None, format=None, show_in_v spectral_axis=spec.spectral_axis.to(current_unit)) # Make metadata layout conform with other viz. - if 'header' in spec.meta: - spec.meta.update(spec.meta['header']) - del spec.meta['header'] + spec.meta = standardize_metadata(spec.meta) app.add_data(spec, data_label[i]) @@ -136,9 +135,7 @@ def specviz_spectrum1d_parser(app, data, data_label=None, format=None, show_in_v uncertainty=unc) # Make metadata layout conform with other viz. - if 'header' in spec.meta: - spec.meta.update(spec.meta['header']) - del spec.meta['header'] + spec.meta = standardize_metadata(spec.meta) # needs perhaps a better way to label the combined spectrum label = "Combined " + data_label[0] diff --git a/jdaviz/configs/specviz2d/plugins/parsers.py b/jdaviz/configs/specviz2d/plugins/parsers.py index 5a04b2f418..df99a7fcfc 100644 --- a/jdaviz/configs/specviz2d/plugins/parsers.py +++ b/jdaviz/configs/specviz2d/plugins/parsers.py @@ -6,7 +6,7 @@ import numpy as np from jdaviz.core.registries import data_parser_registry -from jdaviz.utils import PRIHDR_KEY +from jdaviz.utils import standardize_metadata, PRIHDR_KEY __all__ = ['spec2d_1d_parser'] @@ -59,8 +59,8 @@ def spec2d_1d_parser(app, data_obj, data_label=None, show_in_viewer=True): # we use it here as well, even though the actual unit is pixels spectral_axis = np.arange(1, flux.size + 1, 1) * u.m - metadata = dict(header) - metadata[PRIHDR_KEY] = dict(prihdr) + metadata = standardize_metadata(header) + metadata[PRIHDR_KEY] = standardize_metadata(prihdr) data_obj = Spectrum1D(flux, spectral_axis=spectral_axis, meta=metadata) diff --git a/jdaviz/tests/test_metadata.py b/jdaviz/tests/test_metadata.py new file mode 100644 index 0000000000..ef618a6939 --- /dev/null +++ b/jdaviz/tests/test_metadata.py @@ -0,0 +1,52 @@ +import pytest +from astropy.io import fits + +from jdaviz.utils import standardize_metadata, COMMENTCARD_KEY + + +def test_metadata_plain_dict(): + metadata = {'a': 1, 'b': 2, 'c': {'d': 42}} + out_meta = standardize_metadata(metadata) + assert out_meta == metadata + + # Make sure input is unchanged. + del out_meta['c'] + assert out_meta == {'a': 1, 'b': 2} + assert 'c' in metadata + + +def test_metadata_nested_fits_header(): + hdu = fits.PrimaryHDU() + metadata = {'a': 1, 'header': hdu.header} + out_meta = standardize_metadata(metadata) + assert 'header' not in out_meta + assert sorted(out_meta.keys()) == ['BITPIX', 'EXTEND', 'NAXIS', 'SIMPLE', COMMENTCARD_KEY, 'a'] + assert out_meta[COMMENTCARD_KEY]['BITPIX'] == 'array data type' + assert 'BITPIX' in out_meta[COMMENTCARD_KEY]._header + assert 'a' not in out_meta[COMMENTCARD_KEY]._header + + # Make sure input is unchanged. + assert 'header' in metadata + + +def test_metadata_fits_header(): + hdu = fits.PrimaryHDU() + out_meta = standardize_metadata(hdu.header) + assert sorted(out_meta.keys()) == ['BITPIX', 'EXTEND', 'NAXIS', 'SIMPLE', COMMENTCARD_KEY] + assert out_meta[COMMENTCARD_KEY]['BITPIX'] == 'array data type' + assert 'BITPIX' in out_meta[COMMENTCARD_KEY]._header + + # Make sure input is unchanged. + del out_meta['BITPIX'] + assert 'BITPIX' in hdu.header + + # Make sure you can still nest it afterwards if you want. + # Similar logic is used to separate primary header in Metadata Viewer plugin. + hdu_2 = fits.ImageHDU() + out_meta['image_meta'] = standardize_metadata(hdu_2.header) + assert out_meta['image_meta']['XTENSION'] == 'IMAGE' + + +def test_metadata_invalid(): + with pytest.raises(TypeError, match='metadata must be dictionary or FITS header'): + standardize_metadata([1, 2, 3]) diff --git a/jdaviz/utils.py b/jdaviz/utils.py index 5a3521f716..741bab6077 100644 --- a/jdaviz/utils.py +++ b/jdaviz/utils.py @@ -1,13 +1,17 @@ +import os import time import threading from collections import deque -import os + +from astropy.io import fits from ipyvue import watch + __all__ = [] # For Metadata Viewer plugin internal use only. PRIHDR_KEY = '_primary_header' +COMMENTCARD_KEY = '_fits_comment_card' class SnackbarQueue: @@ -126,3 +130,24 @@ def bqplot_clear_figure(fig): fig.marks = [] fig.axes = [] setattr(fig, 'axis_registry', {}) + + +def standardize_metadata(metadata): + """Standardize given metadata so it can be viewed in + Metadata Viewer plugin. The input can be plain + dictionary or FITS header object. Output is just a plain + dictionary. + """ + if isinstance(metadata, fits.Header): + out_meta = dict(metadata) + out_meta[COMMENTCARD_KEY] = metadata.comments + elif isinstance(metadata, dict): + out_meta = metadata.copy() + # specutils nests it but we do not want nesting + if 'header' in metadata and isinstance(metadata['header'], fits.Header): + out_meta.update(standardize_metadata(metadata['header'])) + del out_meta['header'] + else: + raise TypeError('metadata must be dictionary or FITS header') + + return out_meta