From 78894c3ec4268cb6b78b5177495ffce84a798046 Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Mon, 4 May 2026 11:51:48 +0000 Subject: [PATCH 01/72] Make sensor attribute a set --- satpy/composites/aux_data.py | 2 +- satpy/composites/config_loader.py | 4 ++-- satpy/composites/core.py | 19 +++++------------ satpy/enhancements/enhancer.py | 23 +++++++++----------- satpy/modifiers/_crefl_utils.py | 6 ++++-- satpy/modifiers/atmosphere.py | 11 ++++++++-- satpy/modifiers/spectral.py | 7 ++++-- satpy/readers/abi_l1b.py | 2 +- satpy/readers/core/abi.py | 2 +- satpy/readers/core/file_handlers.py | 2 +- satpy/scene.py | 9 ++------ satpy/utils.py | 33 +++++++++++++++++++++++++++++ satpy/writers/core/base.py | 6 ++++-- satpy/writers/mitiff.py | 10 ++++----- 14 files changed, 82 insertions(+), 54 deletions(-) diff --git a/satpy/composites/aux_data.py b/satpy/composites/aux_data.py index 3179f826a5..3463c7368d 100644 --- a/satpy/composites/aux_data.py +++ b/satpy/composites/aux_data.py @@ -156,7 +156,7 @@ def __call__(self, *args, **kwargs): if self.area is None: raise AttributeError("Area definition needs to be configured") img.attrs["area"] = self.area - img.attrs["sensor"] = None + img.attrs["sensor"] = set() img.attrs["mode"] = "".join(img.bands.data) img.attrs.pop("modifiers", None) img.attrs.pop("calibration", None) diff --git a/satpy/composites/config_loader.py b/satpy/composites/config_loader.py index e5165ada01..6617cfc665 100644 --- a/satpy/composites/config_loader.py +++ b/satpy/composites/config_loader.py @@ -31,7 +31,7 @@ from satpy import DataID, DataQuery from satpy._config import config_search_paths, get_entry_points_config_dirs, glob_config from satpy.dataset.dataid import minimal_default_keys_config -from satpy.utils import recursive_dict_update +from satpy.utils import normalize_sensor_name, recursive_dict_update logger = logging.getLogger(__name__) @@ -268,7 +268,7 @@ def load_compositor_configs_for_sensor(sensor_name: str) -> tuple[dict[str, dict DataID key -> key properties """ - config_filename = sensor_name + ".yaml" + config_filename = normalize_sensor_name(sensor_name) + ".yaml" logger.debug("Looking for composites config file %s", config_filename) paths = get_entry_points_config_dirs("satpy.composites") composite_configs = config_search_paths( diff --git a/satpy/composites/core.py b/satpy/composites/core.py index 1b124eb88a..b568bc8819 100644 --- a/satpy/composites/core.py +++ b/satpy/composites/core.py @@ -28,7 +28,7 @@ from satpy.dataset import DataID, combine_metadata from satpy.dataset.dataid import minimal_default_keys_config -from satpy.utils import unify_chunks +from satpy.utils import get_sensors_from_attrs, unify_chunks LOG = logging.getLogger(__name__) @@ -433,20 +433,11 @@ def _concat_datasets(self, projectables, mode): return data - def _get_sensors(self, projectables): - sensor = set() + def _get_sensors(self, projectables) -> set[str]: + sensors = set() for projectable in projectables: - current_sensor = projectable.attrs.get("sensor", None) - if current_sensor: - if isinstance(current_sensor, (str, bytes)): - sensor.add(current_sensor) - else: - sensor |= current_sensor - if len(sensor) == 0: - sensor = None - elif len(sensor) == 1: - sensor = list(sensor)[0] - return sensor + sensors.update(get_sensors_from_attrs(projectable.attrs)) + return sensors def __call__( self, diff --git a/satpy/enhancements/enhancer.py b/satpy/enhancements/enhancer.py index b196c63e28..7a217bc82e 100644 --- a/satpy/enhancements/enhancer.py +++ b/satpy/enhancements/enhancer.py @@ -24,7 +24,7 @@ from satpy._config import config_search_paths, get_entry_points_config_dirs from satpy.decision_tree import DecisionTree -from satpy.utils import get_logger, recursive_dict_update +from satpy.utils import get_logger, get_sensors_from_attrs, normalize_sensor_name, recursive_dict_update LOG = get_logger(__name__) @@ -122,26 +122,23 @@ def __init__(self, enhancement_config_file=None): self.sensor_enhancement_configs = [] - def get_sensor_enhancement_config(self, sensor): + def get_sensor_enhancement_config(self, sensors: set[str]): """Get the sensor-specific config.""" - if isinstance(sensor, str): - # one single sensor - sensor = [sensor] - paths = get_entry_points_config_dirs("satpy.enhancements") - for sensor_name in sensor: - config_fn = os.path.join("enhancements", sensor_name + ".yaml") + for sensor_name in sensors: + basename = normalize_sensor_name(sensor_name) + ".yaml" + config_fn = os.path.join("enhancements", basename) config_files = config_search_paths(config_fn, search_dirs=paths) # Note: Enhancement configuration files can't overwrite individual # options, only entire sections are overwritten for config_file in config_files: yield config_file - def add_sensor_enhancements(self, sensor): + def add_sensor_enhancements(self, sensors: set[str]): """Add sensor-specific enhancements.""" # XXX: Should we just load all enhancements from the base directory? new_configs = [] - for config_file in self.get_sensor_enhancement_config(sensor): + for config_file in self.get_sensor_enhancement_config(sensors): if config_file not in self.sensor_enhancement_configs: self.sensor_enhancement_configs.append(config_file) new_configs.append(config_file) @@ -209,9 +206,9 @@ def get_enhanced_image(dataset, enhance=None, overlay=None, decorate=None, if enhancer is None or enhancer.enhancement_tree is None: LOG.debug("No enhancement being applied to dataset") else: - if dataset.attrs.get("sensor", None): - enhancer.add_sensor_enhancements(dataset.attrs["sensor"]) - + sensors = get_sensors_from_attrs(dataset.attrs) + if sensors: + enhancer.add_sensor_enhancements(sensors) enhancer.apply(img, **dataset.attrs) if overlay is not None: diff --git a/satpy/modifiers/_crefl_utils.py b/satpy/modifiers/_crefl_utils.py index b8a1d52a4b..b638bfbf2f 100644 --- a/satpy/modifiers/_crefl_utils.py +++ b/satpy/modifiers/_crefl_utils.py @@ -70,6 +70,7 @@ import xarray as xr from satpy.dataset.dataid import WavelengthRange +from satpy.utils import get_one_sensor_from_attrs, normalize_sensor_name LOG = logging.getLogger(__name__) @@ -282,7 +283,8 @@ def run_crefl(refl, :param avg_elevation: average elevation (usually pre-calculated and stored in CMGDEM.hdf) """ - runner_cls = _runner_class_for_sensor(refl.attrs["sensor"]) + sensor = get_one_sensor_from_attrs(refl.attrs) + runner_cls = _runner_class_for_sensor(sensor) runner = runner_cls(refl) corr_refl = runner(sensor_azimuth, sensor_zenith, solar_azimuth, solar_zenith, avg_elevation) return corr_refl @@ -384,7 +386,7 @@ def _run_crefl(self, mus, muv, phi, solar_zenith, sensor_zenith, height, coeffs) def _runner_class_for_sensor(sensor_name: str) -> Type[_CREFLRunner]: try: - return _SENSOR_TO_RUNNER[sensor_name] + return _SENSOR_TO_RUNNER[normalize_sensor_name(sensor_name)] except KeyError: raise NotImplementedError(f"Don't know how to apply CREFL to data from sensor {sensor_name}.") diff --git a/satpy/modifiers/atmosphere.py b/satpy/modifiers/atmosphere.py index c7144c27ca..1ed3f5d026 100644 --- a/satpy/modifiers/atmosphere.py +++ b/satpy/modifiers/atmosphere.py @@ -26,6 +26,7 @@ from satpy.modifiers import ModifierBase from satpy.modifiers._crefl import ReflectanceCorrector # noqa from satpy.modifiers.angles import compute_relative_azimuth, get_angles, get_satellite_zenith_angle +from satpy.utils import get_one_sensor_from_attrs, get_pyspectral_sensor_name logger = logging.getLogger(__name__) @@ -104,7 +105,10 @@ def __call__(self, projectables, optional_datasets=None, **info): logger.info("Removing Rayleigh scattering with atmosphere '%s' and " "aerosol type '%s' for '%s'", atmosphere, aerosol_type, vis.attrs["name"]) - corrector = Rayleigh(vis.attrs["platform_name"], vis.attrs["sensor"], + sensor = get_pyspectral_sensor_name( + get_one_sensor_from_attrs(vis.attrs) + ) + corrector = Rayleigh(vis.attrs["platform_name"], sensor, atmosphere=atmosphere, aerosol_type=aerosol_type) @@ -158,8 +162,11 @@ def __call__(self, projectables, optional_datasets=None, **info): satz = satz.data # get dask array underneath logger.info("Correction for limb cooling") + sensor = get_pyspectral_sensor_name( + get_one_sensor_from_attrs(band.attrs) + ) corrector = AtmosphericalCorrection(band.attrs["platform_name"], - band.attrs["sensor"]) + sensor) atm_corr = da.map_blocks(_call_mapped_correction, satz, band.data, corrector=corrector, diff --git a/satpy/modifiers/spectral.py b/satpy/modifiers/spectral.py index 402b5606d4..9e55972e0b 100644 --- a/satpy/modifiers/spectral.py +++ b/satpy/modifiers/spectral.py @@ -22,6 +22,7 @@ import xarray as xr from satpy.modifiers import ModifierBase +from satpy.utils import get_one_sensor_from_attrs, get_pyspectral_sensor_name try: from pyspectral.near_infrared_reflectance import Calculator @@ -131,8 +132,10 @@ def _init_reflectance_calculator(self, metadata): if not Calculator: logger.info("Couldn't load pyspectral") raise ImportError("No module named pyspectral.near_infrared_reflectance") - - reflectance_3x_calculator = Calculator(metadata["platform_name"], metadata["sensor"], metadata["name"], + sensor = get_pyspectral_sensor_name( + get_one_sensor_from_attrs(metadata) + ) + reflectance_3x_calculator = Calculator(metadata["platform_name"], sensor, metadata["name"], sunz_threshold=self.sun_zenith_threshold, masking_limit=self.masking_limit) return reflectance_3x_calculator diff --git a/satpy/readers/abi_l1b.py b/satpy/readers/abi_l1b.py index 48e82f6968..b144529ebe 100644 --- a/satpy/readers/abi_l1b.py +++ b/satpy/readers/abi_l1b.py @@ -73,7 +73,7 @@ def get_dataset(self, key, info): def _adjust_attrs(self, data, key): data.attrs.update({"platform_name": self.platform_name, - "sensor": self.sensor}) + "sensor": {self.sensor}}) # Add orbital parameters projection = self.nc["goes_imager_projection"] data.attrs["orbital_parameters"] = { diff --git a/satpy/readers/core/abi.py b/satpy/readers/core/abi.py index e50cd616c4..d51764dc38 100644 --- a/satpy/readers/core/abi.py +++ b/satpy/readers/core/abi.py @@ -111,7 +111,7 @@ def _rename_dims(nc): @property def sensor(self): """Get sensor name for current file handler.""" - return "abi" + return "ABI" def __getitem__(self, item): """Wrap `self.nc[item]` for better floating point precision. diff --git a/satpy/readers/core/file_handlers.py b/satpy/readers/core/file_handlers.py index ac6626959c..6917988aa3 100644 --- a/satpy/readers/core/file_handlers.py +++ b/satpy/readers/core/file_handlers.py @@ -155,7 +155,7 @@ def end_time(self): return self.filename_info.get("end_time", self.start_time) @property - def sensor_names(self): + def sensor_names(self) -> set: """List of sensors represented in this file.""" raise NotImplementedError diff --git a/satpy/scene.py b/satpy/scene.py index 8693cc77b4..afcdb5dbae 100644 --- a/satpy/scene.py +++ b/satpy/scene.py @@ -36,7 +36,7 @@ from satpy.dependency_tree import DependencyTree from satpy.node import CompositorNode, MissingDependencies, ReaderNode from satpy.readers.core.loading import load_readers -from satpy.utils import convert_remote_files_to_fsspec, get_storage_options_from_reader_kwargs +from satpy.utils import convert_remote_files_to_fsspec, get_sensors_from_attrs, get_storage_options_from_reader_kwargs LOG = logging.getLogger(__name__) @@ -197,12 +197,7 @@ def sensor_names(self) -> set[str]: def _contained_sensor_names(self) -> set[str]: sensor_names = set() for data_arr in self.values(): - if "sensor" not in data_arr.attrs: - continue - if isinstance(data_arr.attrs["sensor"], str): - sensor_names.add(data_arr.attrs["sensor"]) - elif isinstance(data_arr.attrs["sensor"], set): - sensor_names.update(data_arr.attrs["sensor"]) + sensor_names.update(get_sensors_from_attrs(data_arr.attrs)) return sensor_names @property diff --git a/satpy/utils.py b/satpy/utils.py index bdf9d77e4c..1173d0718a 100644 --- a/satpy/utils.py +++ b/satpy/utils.py @@ -940,3 +940,36 @@ def flatten_dict(d, parent_key="", sep="_"): else: items.append((new_key, v)) return dict(items) + + +def get_sensors_from_attrs(attrs: dict[str,Any]) -> set[str]: + """Get sensor names from dataset attributes.""" + return attrs.get("sensor", set()) + + +def normalize_sensor_name(sensor: str) -> str: + """Normalize sensor name for internal usage.""" + return sensor.replace("-", "").replace(" ", "_").replace("/", "-").lower() + + +def get_one_sensor_from_attrs(attrs: dict[str,Any]) -> str: + """Get a single sensor name from dataset attributes.""" + sensors = get_sensors_from_attrs(attrs) + if not sensors: + raise KeyError("No 'sensor' dataset attribute") + if len(sensors) > 1: + logger.warning(f"More than one sensor in dataset attributes, will use the first value: {sensors}") + return list(sensors)[0] + + +def get_pyspectral_sensor_name(sensor: str) -> str: + """Get sensor name expected by pyspectral.""" + return normalize_sensor_name(sensor) + + +def serialize_sensors(sensors: set[str]) -> str: + """Serialize a set of sensors.""" + return "-".join( + sensor.replace("-", "").replace(" ", "").replace("/", "").lower() + for sensor in sorted(sensors) + ) diff --git a/satpy/writers/core/base.py b/satpy/writers/core/base.py index e0a53d0f7e..f8c1c1616e 100644 --- a/satpy/writers/core/base.py +++ b/satpy/writers/core/base.py @@ -16,6 +16,7 @@ """Shared objects and base classes for writers.""" from __future__ import annotations +import contextlib import logging import os import typing @@ -23,6 +24,7 @@ from satpy.aux_download import DataDownloadMixin from satpy.plugin_base import Plugin +from satpy.utils import serialize_sensors from satpy.writers.core.compute import compute_writer_results, split_results if typing.TYPE_CHECKING: @@ -136,8 +138,8 @@ def create_filename_parser(self, base_dir): @staticmethod def _prepare_metadata_for_filename_formatting(attrs): - if isinstance(attrs.get("sensor"), set): - attrs["sensor"] = "-".join(sorted(attrs["sensor"])) + with contextlib.suppress(KeyError): + attrs["sensor"] = serialize_sensors(attrs["sensor"]) def get_filename(self, **kwargs): """Create a filename where output data will be saved. diff --git a/satpy/writers/mitiff.py b/satpy/writers/mitiff.py index 7788d4b78a..0ed78e1a6a 100644 --- a/satpy/writers/mitiff.py +++ b/satpy/writers/mitiff.py @@ -28,6 +28,7 @@ from satpy.dataset import DataID, DataQuery from satpy.enhancements.enhancer import get_enhanced_image +from satpy.utils import get_one_sensor_from_attrs from satpy.writers.core.image import ImageWriter if typing.TYPE_CHECKING: @@ -53,12 +54,9 @@ def _adjust_kwargs(dataset, kwargs): if "start_time" not in kwargs: kwargs["start_time"] = dataset.attrs["start_time"] if "sensor" not in kwargs: - kwargs["sensor"] = dataset.attrs["sensor"] - # Sensor attrs could be set. MITIFFs needing to handle sensor can only have one sensor - # Assume the first value of set as the sensor. - if isinstance(kwargs["sensor"], set): - LOG.warning("Sensor is set, will use the first value: %s", kwargs["sensor"]) - kwargs["sensor"] = (list(kwargs["sensor"]))[0] + # MITIFFs needing to handle sensor can only have one sensor + # Assume the first value of set as the sensor. + kwargs["sensor"] = get_one_sensor_from_attrs(dataset.attrs) class MITIFFWriter(ImageWriter): From dac04571a88774c8311dd8116540173d0be74afb Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Wed, 6 May 2026 08:10:15 +0000 Subject: [PATCH 02/72] Use instrument instead of sensor attribute --- satpy/composites/aux_data.py | 2 +- satpy/composites/config_loader.py | 4 +-- satpy/composites/core.py | 6 ++--- satpy/composites/fill.py | 4 +-- satpy/composites/glm.py | 2 +- satpy/dependency_tree.py | 2 +- satpy/enhancements/enhancer.py | 10 ++++---- satpy/modifiers/_crefl_utils.py | 9 ++++--- satpy/modifiers/atmosphere.py | 10 ++++---- satpy/modifiers/spectral.py | 6 ++--- satpy/scene.py | 8 ++++-- satpy/utils.py | 42 +++++++++++++++---------------- satpy/writers/core/base.py | 4 +-- satpy/writers/mitiff.py | 4 +-- 14 files changed, 59 insertions(+), 54 deletions(-) diff --git a/satpy/composites/aux_data.py b/satpy/composites/aux_data.py index 3463c7368d..d5d74afb45 100644 --- a/satpy/composites/aux_data.py +++ b/satpy/composites/aux_data.py @@ -156,7 +156,7 @@ def __call__(self, *args, **kwargs): if self.area is None: raise AttributeError("Area definition needs to be configured") img.attrs["area"] = self.area - img.attrs["sensor"] = set() + img.attrs["instruments"] = set() img.attrs["mode"] = "".join(img.bands.data) img.attrs.pop("modifiers", None) img.attrs.pop("calibration", None) diff --git a/satpy/composites/config_loader.py b/satpy/composites/config_loader.py index 6617cfc665..96bdc61b72 100644 --- a/satpy/composites/config_loader.py +++ b/satpy/composites/config_loader.py @@ -31,7 +31,7 @@ from satpy import DataID, DataQuery from satpy._config import config_search_paths, get_entry_points_config_dirs, glob_config from satpy.dataset.dataid import minimal_default_keys_config -from satpy.utils import normalize_sensor_name, recursive_dict_update +from satpy.utils import normalize_instrument_name, recursive_dict_update logger = logging.getLogger(__name__) @@ -268,7 +268,7 @@ def load_compositor_configs_for_sensor(sensor_name: str) -> tuple[dict[str, dict DataID key -> key properties """ - config_filename = normalize_sensor_name(sensor_name) + ".yaml" + config_filename = normalize_instrument_name(sensor_name) + ".yaml" logger.debug("Looking for composites config file %s", config_filename) paths = get_entry_points_config_dirs("satpy.composites") composite_configs = config_search_paths( diff --git a/satpy/composites/core.py b/satpy/composites/core.py index b568bc8819..5f7c772a86 100644 --- a/satpy/composites/core.py +++ b/satpy/composites/core.py @@ -28,7 +28,7 @@ from satpy.dataset import DataID, combine_metadata from satpy.dataset.dataid import minimal_default_keys_config -from satpy.utils import get_sensors_from_attrs, unify_chunks +from satpy.utils import get_instruments_from_attrs, unify_chunks LOG = logging.getLogger(__name__) @@ -436,7 +436,7 @@ def _concat_datasets(self, projectables, mode): def _get_sensors(self, projectables) -> set[str]: sensors = set() for projectable in projectables: - sensors.update(get_sensors_from_attrs(projectable.attrs)) + sensors.update(get_instruments_from_attrs(projectable.attrs)) return sensors def __call__( @@ -503,7 +503,7 @@ def _get_updated_attrs(self, datasets, attrs, mode): new_attrs.update(self.attrs) if resolution is not None: new_attrs["resolution"] = resolution - new_attrs["sensor"] = self._get_sensors(datasets) + new_attrs["instruments"] = self._get_sensors(datasets) new_attrs["mode"] = mode return new_attrs diff --git a/satpy/composites/fill.py b/satpy/composites/fill.py index bd5b51577e..3b47f975d4 100644 --- a/satpy/composites/fill.py +++ b/satpy/composites/fill.py @@ -406,9 +406,9 @@ def _combine_metadata_with_mode_and_sensor(self, # 'mode' is no longer valid after we've remove the 'A' # let the base class __call__ determine mode attrs.pop("mode", None) - if attrs.get("sensor") is None: + if attrs.get("instruments") is None: # sensor can be a set - attrs["sensor"] = self._get_sensors([foreground, background]) + attrs["instruments"] = self._get_sensors([foreground, background]) return attrs @staticmethod diff --git a/satpy/composites/glm.py b/satpy/composites/glm.py index 866e952698..ceeef76e04 100644 --- a/satpy/composites/glm.py +++ b/satpy/composites/glm.py @@ -97,7 +97,7 @@ def _update_attrs(self, new_data, background_layer, highlight_layer): new_data.attrs["units"] = 1 new_sensors = self._get_sensors((highlight_layer, background_layer)) new_data.attrs.update({ - "sensor": new_sensors, + "instruments": new_sensors, }) def __call__(self, projectables, optional_datasets=None, **attrs): diff --git a/satpy/dependency_tree.py b/satpy/dependency_tree.py index 97777a71e9..59da567c96 100644 --- a/satpy/dependency_tree.py +++ b/satpy/dependency_tree.py @@ -514,7 +514,7 @@ def get_modifier(self, comp_id): mloader, moptions = modifiers[modifier] moptions = moptions.copy() moptions.update(comp_id.to_dict()) - moptions["sensor"] = sensor_name + moptions["instrument"] = sensor_name compositors[comp_id] = mloader(_satpy_id=comp_id, **moptions) return compositors[comp_id] diff --git a/satpy/enhancements/enhancer.py b/satpy/enhancements/enhancer.py index 7a217bc82e..2bf48bf688 100644 --- a/satpy/enhancements/enhancer.py +++ b/satpy/enhancements/enhancer.py @@ -24,7 +24,7 @@ from satpy._config import config_search_paths, get_entry_points_config_dirs from satpy.decision_tree import DecisionTree -from satpy.utils import get_logger, get_sensors_from_attrs, normalize_sensor_name, recursive_dict_update +from satpy.utils import get_instruments_from_attrs, get_logger, normalize_instrument_name, recursive_dict_update LOG = get_logger(__name__) @@ -38,12 +38,12 @@ def __init__(self, *decision_dicts, **kwargs): ("name", "reader", "platform_name", - "sensor", + "instruments", "standard_name", "units", )) self.prefix = kwargs.pop("config_section", "enhancements") - multival_keys = kwargs.pop("multival_keys", ["sensor"]) + multival_keys = kwargs.pop("multival_keys", ["instruments"]) super(EnhancementDecisionTree, self).__init__( decision_dicts, match_keys, multival_keys) @@ -126,7 +126,7 @@ def get_sensor_enhancement_config(self, sensors: set[str]): """Get the sensor-specific config.""" paths = get_entry_points_config_dirs("satpy.enhancements") for sensor_name in sensors: - basename = normalize_sensor_name(sensor_name) + ".yaml" + basename = normalize_instrument_name(sensor_name) + ".yaml" config_fn = os.path.join("enhancements", basename) config_files = config_search_paths(config_fn, search_dirs=paths) # Note: Enhancement configuration files can't overwrite individual @@ -206,7 +206,7 @@ def get_enhanced_image(dataset, enhance=None, overlay=None, decorate=None, if enhancer is None or enhancer.enhancement_tree is None: LOG.debug("No enhancement being applied to dataset") else: - sensors = get_sensors_from_attrs(dataset.attrs) + sensors = get_instruments_from_attrs(dataset.attrs) if sensors: enhancer.add_sensor_enhancements(sensors) enhancer.apply(img, **dataset.attrs) diff --git a/satpy/modifiers/_crefl_utils.py b/satpy/modifiers/_crefl_utils.py index b638bfbf2f..1d3f4eb712 100644 --- a/satpy/modifiers/_crefl_utils.py +++ b/satpy/modifiers/_crefl_utils.py @@ -70,7 +70,7 @@ import xarray as xr from satpy.dataset.dataid import WavelengthRange -from satpy.utils import get_one_sensor_from_attrs, normalize_sensor_name +from satpy.utils import get_one_instrument_from_attrs, normalize_instrument_name LOG = logging.getLogger(__name__) @@ -283,7 +283,7 @@ def run_crefl(refl, :param avg_elevation: average elevation (usually pre-calculated and stored in CMGDEM.hdf) """ - sensor = get_one_sensor_from_attrs(refl.attrs) + sensor = get_one_instrument_from_attrs(refl.attrs) runner_cls = _runner_class_for_sensor(sensor) runner = runner_cls(refl) corr_refl = runner(sensor_azimuth, sensor_zenith, solar_azimuth, solar_zenith, avg_elevation) @@ -350,8 +350,9 @@ def _run_crefl(self, mus, muv, phi, solar_zenith, sensor_zenith, height, coeffs) class _VIIRSMODISCREFLRunner(_CREFLRunner): def _run_crefl(self, mus, muv, phi, solar_zenith, sensor_zenith, height, coeffs): + instrument = get_one_instrument_from_attrs(self._refl.attrs) return da.map_blocks(_run_crefl, self._refl.data, mus.data, muv.data, phi.data, - height, self._refl.attrs.get("sensor"), *coeffs, + height, instrument, *coeffs, meta=np.ndarray((), dtype=self._refl.dtype), chunks=self._refl.chunks, dtype=self._refl.dtype, ) @@ -386,7 +387,7 @@ def _run_crefl(self, mus, muv, phi, solar_zenith, sensor_zenith, height, coeffs) def _runner_class_for_sensor(sensor_name: str) -> Type[_CREFLRunner]: try: - return _SENSOR_TO_RUNNER[normalize_sensor_name(sensor_name)] + return _SENSOR_TO_RUNNER[normalize_instrument_name(sensor_name)] except KeyError: raise NotImplementedError(f"Don't know how to apply CREFL to data from sensor {sensor_name}.") diff --git a/satpy/modifiers/atmosphere.py b/satpy/modifiers/atmosphere.py index 1ed3f5d026..dbb8870da7 100644 --- a/satpy/modifiers/atmosphere.py +++ b/satpy/modifiers/atmosphere.py @@ -26,7 +26,7 @@ from satpy.modifiers import ModifierBase from satpy.modifiers._crefl import ReflectanceCorrector # noqa from satpy.modifiers.angles import compute_relative_azimuth, get_angles, get_satellite_zenith_angle -from satpy.utils import get_one_sensor_from_attrs, get_pyspectral_sensor_name +from satpy.utils import get_one_instrument_from_attrs, get_pyspectral_instrument_name logger = logging.getLogger(__name__) @@ -105,8 +105,8 @@ def __call__(self, projectables, optional_datasets=None, **info): logger.info("Removing Rayleigh scattering with atmosphere '%s' and " "aerosol type '%s' for '%s'", atmosphere, aerosol_type, vis.attrs["name"]) - sensor = get_pyspectral_sensor_name( - get_one_sensor_from_attrs(vis.attrs) + sensor = get_pyspectral_instrument_name( + get_one_instrument_from_attrs(vis.attrs) ) corrector = Rayleigh(vis.attrs["platform_name"], sensor, atmosphere=atmosphere, @@ -162,8 +162,8 @@ def __call__(self, projectables, optional_datasets=None, **info): satz = satz.data # get dask array underneath logger.info("Correction for limb cooling") - sensor = get_pyspectral_sensor_name( - get_one_sensor_from_attrs(band.attrs) + sensor = get_pyspectral_instrument_name( + get_one_instrument_from_attrs(band.attrs) ) corrector = AtmosphericalCorrection(band.attrs["platform_name"], sensor) diff --git a/satpy/modifiers/spectral.py b/satpy/modifiers/spectral.py index 9e55972e0b..67562f19e9 100644 --- a/satpy/modifiers/spectral.py +++ b/satpy/modifiers/spectral.py @@ -22,7 +22,7 @@ import xarray as xr from satpy.modifiers import ModifierBase -from satpy.utils import get_one_sensor_from_attrs, get_pyspectral_sensor_name +from satpy.utils import get_one_instrument_from_attrs, get_pyspectral_instrument_name try: from pyspectral.near_infrared_reflectance import Calculator @@ -132,8 +132,8 @@ def _init_reflectance_calculator(self, metadata): if not Calculator: logger.info("Couldn't load pyspectral") raise ImportError("No module named pyspectral.near_infrared_reflectance") - sensor = get_pyspectral_sensor_name( - get_one_sensor_from_attrs(metadata) + sensor = get_pyspectral_instrument_name( + get_one_instrument_from_attrs(metadata) ) reflectance_3x_calculator = Calculator(metadata["platform_name"], sensor, metadata["name"], sunz_threshold=self.sun_zenith_threshold, diff --git a/satpy/scene.py b/satpy/scene.py index afcdb5dbae..30452ee989 100644 --- a/satpy/scene.py +++ b/satpy/scene.py @@ -36,7 +36,11 @@ from satpy.dependency_tree import DependencyTree from satpy.node import CompositorNode, MissingDependencies, ReaderNode from satpy.readers.core.loading import load_readers -from satpy.utils import convert_remote_files_to_fsspec, get_sensors_from_attrs, get_storage_options_from_reader_kwargs +from satpy.utils import ( + convert_remote_files_to_fsspec, + get_instruments_from_attrs, + get_storage_options_from_reader_kwargs, +) LOG = logging.getLogger(__name__) @@ -197,7 +201,7 @@ def sensor_names(self) -> set[str]: def _contained_sensor_names(self) -> set[str]: sensor_names = set() for data_arr in self.values(): - sensor_names.update(get_sensors_from_attrs(data_arr.attrs)) + sensor_names.update(get_instruments_from_attrs(data_arr.attrs)) return sensor_names @property diff --git a/satpy/utils.py b/satpy/utils.py index 1173d0718a..81f55f7cc7 100644 --- a/satpy/utils.py +++ b/satpy/utils.py @@ -942,34 +942,34 @@ def flatten_dict(d, parent_key="", sep="_"): return dict(items) -def get_sensors_from_attrs(attrs: dict[str,Any]) -> set[str]: - """Get sensor names from dataset attributes.""" - return attrs.get("sensor", set()) +def get_instruments_from_attrs(attrs: dict[str,Any]) -> set[str]: + """Get instrument names from dataset attributes.""" + return attrs.get("instruments", set()) -def normalize_sensor_name(sensor: str) -> str: - """Normalize sensor name for internal usage.""" - return sensor.replace("-", "").replace(" ", "_").replace("/", "-").lower() +def normalize_instrument_name(instrument: str) -> str: + """Normalize instrument name for internal usage.""" + return instrument.replace("-", "").replace(" ", "_").replace("/", "-").lower() -def get_one_sensor_from_attrs(attrs: dict[str,Any]) -> str: - """Get a single sensor name from dataset attributes.""" - sensors = get_sensors_from_attrs(attrs) - if not sensors: - raise KeyError("No 'sensor' dataset attribute") - if len(sensors) > 1: - logger.warning(f"More than one sensor in dataset attributes, will use the first value: {sensors}") - return list(sensors)[0] +def get_one_instrument_from_attrs(attrs: dict[str,Any]) -> str: + """Get a single instrument name from dataset attributes.""" + instruments = get_instruments_from_attrs(attrs) + if not instruments: + raise KeyError("No 'instruments' dataset attribute") + if len(instruments) > 1: + logger.warning(f"More than one instrument in dataset attributes, will use the first value: {instruments}") + return list(instruments)[0] -def get_pyspectral_sensor_name(sensor: str) -> str: - """Get sensor name expected by pyspectral.""" - return normalize_sensor_name(sensor) +def get_pyspectral_instrument_name(instrument: str) -> str: + """Get instrument name expected by pyspectral.""" + return normalize_instrument_name(instrument) -def serialize_sensors(sensors: set[str]) -> str: - """Serialize a set of sensors.""" +def serialize_instruments(instruments: set[str]) -> str: + """Serialize a set of instruments.""" return "-".join( - sensor.replace("-", "").replace(" ", "").replace("/", "").lower() - for sensor in sorted(sensors) + instr.replace("-", "").replace(" ", "").replace("/", "").lower() + for instr in sorted(instruments) ) diff --git a/satpy/writers/core/base.py b/satpy/writers/core/base.py index f8c1c1616e..056b7732b8 100644 --- a/satpy/writers/core/base.py +++ b/satpy/writers/core/base.py @@ -24,7 +24,7 @@ from satpy.aux_download import DataDownloadMixin from satpy.plugin_base import Plugin -from satpy.utils import serialize_sensors +from satpy.utils import serialize_instruments from satpy.writers.core.compute import compute_writer_results, split_results if typing.TYPE_CHECKING: @@ -139,7 +139,7 @@ def create_filename_parser(self, base_dir): @staticmethod def _prepare_metadata_for_filename_formatting(attrs): with contextlib.suppress(KeyError): - attrs["sensor"] = serialize_sensors(attrs["sensor"]) + attrs["instruments"] = serialize_instruments(attrs["instruments"]) def get_filename(self, **kwargs): """Create a filename where output data will be saved. diff --git a/satpy/writers/mitiff.py b/satpy/writers/mitiff.py index 0ed78e1a6a..b8a38c955d 100644 --- a/satpy/writers/mitiff.py +++ b/satpy/writers/mitiff.py @@ -28,7 +28,7 @@ from satpy.dataset import DataID, DataQuery from satpy.enhancements.enhancer import get_enhanced_image -from satpy.utils import get_one_sensor_from_attrs +from satpy.utils import get_one_instrument_from_attrs from satpy.writers.core.image import ImageWriter if typing.TYPE_CHECKING: @@ -56,7 +56,7 @@ def _adjust_kwargs(dataset, kwargs): if "sensor" not in kwargs: # MITIFFs needing to handle sensor can only have one sensor # Assume the first value of set as the sensor. - kwargs["sensor"] = get_one_sensor_from_attrs(dataset.attrs) + kwargs["sensor"] = get_one_instrument_from_attrs(dataset.attrs) class MITIFFWriter(ImageWriter): From 8ffb67196c0e91b00652a46a1cbee554e34ee830 Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Wed, 6 May 2026 10:58:39 +0000 Subject: [PATCH 03/72] Add instrument attribute setter --- satpy/_config.py | 1 + satpy/composites/aux_data.py | 3 ++- satpy/composites/core.py | 4 ++-- satpy/composites/fill.py | 6 ++++-- satpy/enhancements/enhancer.py | 13 ++++++++++--- satpy/utils.py | 16 +++++++++++++++- satpy/writers/core/base.py | 6 ++++-- 7 files changed, 38 insertions(+), 11 deletions(-) diff --git a/satpy/_config.py b/satpy/_config.py index 47012742d6..0b2e6a57c0 100644 --- a/satpy/_config.py +++ b/satpy/_config.py @@ -54,6 +54,7 @@ "readers": { "clip_negative_radiances": False, }, + "instruments_key": "sensor" } # Satpy main configuration object diff --git a/satpy/composites/aux_data.py b/satpy/composites/aux_data.py index d5d74afb45..406a451295 100644 --- a/satpy/composites/aux_data.py +++ b/satpy/composites/aux_data.py @@ -23,6 +23,7 @@ import satpy from satpy.aux_download import DataDownloadMixin +from satpy.utils import set_instruments_attr from .core import GenericCompositor @@ -156,7 +157,7 @@ def __call__(self, *args, **kwargs): if self.area is None: raise AttributeError("Area definition needs to be configured") img.attrs["area"] = self.area - img.attrs["instruments"] = set() + set_instruments_attr(img.attrs, set()) img.attrs["mode"] = "".join(img.bands.data) img.attrs.pop("modifiers", None) img.attrs.pop("calibration", None) diff --git a/satpy/composites/core.py b/satpy/composites/core.py index 5f7c772a86..85b31d1869 100644 --- a/satpy/composites/core.py +++ b/satpy/composites/core.py @@ -28,7 +28,7 @@ from satpy.dataset import DataID, combine_metadata from satpy.dataset.dataid import minimal_default_keys_config -from satpy.utils import get_instruments_from_attrs, unify_chunks +from satpy.utils import get_instruments_from_attrs, set_instruments_attr, unify_chunks LOG = logging.getLogger(__name__) @@ -503,7 +503,7 @@ def _get_updated_attrs(self, datasets, attrs, mode): new_attrs.update(self.attrs) if resolution is not None: new_attrs["resolution"] = resolution - new_attrs["instruments"] = self._get_sensors(datasets) + set_instruments_attr(new_attrs, self._get_sensors(datasets)) new_attrs["mode"] = mode return new_attrs diff --git a/satpy/composites/fill.py b/satpy/composites/fill.py index 3b47f975d4..994d6437f1 100644 --- a/satpy/composites/fill.py +++ b/satpy/composites/fill.py @@ -26,6 +26,7 @@ import xarray as xr from satpy.dataset import combine_metadata +from satpy.utils import get_instruments_from_attrs, set_instruments_attr from .core import ( GenericCompositor, @@ -406,9 +407,10 @@ def _combine_metadata_with_mode_and_sensor(self, # 'mode' is no longer valid after we've remove the 'A' # let the base class __call__ determine mode attrs.pop("mode", None) - if attrs.get("instruments") is None: + if not get_instruments_from_attrs(attrs): # sensor can be a set - attrs["instruments"] = self._get_sensors([foreground, background]) + instruments = self._get_sensors([foreground, background]) + set_instruments_attr(attrs, instruments) return attrs @staticmethod diff --git a/satpy/enhancements/enhancer.py b/satpy/enhancements/enhancer.py index 2bf48bf688..291c12c166 100644 --- a/satpy/enhancements/enhancer.py +++ b/satpy/enhancements/enhancer.py @@ -24,7 +24,13 @@ from satpy._config import config_search_paths, get_entry_points_config_dirs from satpy.decision_tree import DecisionTree -from satpy.utils import get_instruments_from_attrs, get_logger, normalize_instrument_name, recursive_dict_update +from satpy.utils import ( + get_instruments_from_attrs, + get_instruments_key, + get_logger, + normalize_instrument_name, + recursive_dict_update, +) LOG = get_logger(__name__) @@ -34,16 +40,17 @@ class EnhancementDecisionTree(DecisionTree): def __init__(self, *decision_dicts, **kwargs): """Init the decision tree.""" + instr_key = get_instruments_key() match_keys = kwargs.pop("match_keys", ("name", "reader", "platform_name", - "instruments", + instr_key, "standard_name", "units", )) self.prefix = kwargs.pop("config_section", "enhancements") - multival_keys = kwargs.pop("multival_keys", ["instruments"]) + multival_keys = kwargs.pop("multival_keys", [instr_key]) super(EnhancementDecisionTree, self).__init__( decision_dicts, match_keys, multival_keys) diff --git a/satpy/utils.py b/satpy/utils.py index 81f55f7cc7..d1f9b31d7e 100644 --- a/satpy/utils.py +++ b/satpy/utils.py @@ -39,6 +39,8 @@ from numpy.typing import ArrayLike, DTypeLike from yaml import BaseLoader, UnsafeLoader +import satpy + _is_logging_on = False TRACE_LEVEL = 5 @@ -944,7 +946,8 @@ def flatten_dict(d, parent_key="", sep="_"): def get_instruments_from_attrs(attrs: dict[str,Any]) -> set[str]: """Get instrument names from dataset attributes.""" - return attrs.get("instruments", set()) + key = get_instruments_key() + return attrs.get(key, set()) def normalize_instrument_name(instrument: str) -> str: @@ -973,3 +976,14 @@ def serialize_instruments(instruments: set[str]) -> str: instr.replace("-", "").replace(" ", "").replace("/", "").lower() for instr in sorted(instruments) ) + + +def set_instruments_attr(attrs: dict[str,Any], instruments: set[str]|str) -> None: + """Set 'instruments' dataset atrribute.""" + key = get_instruments_key() + attrs[key] = instruments + + +def get_instruments_key(): + """Get key for instruments in dataset attributes.""" + return satpy.config.get("instruments_key") diff --git a/satpy/writers/core/base.py b/satpy/writers/core/base.py index 056b7732b8..c2f5f69f3b 100644 --- a/satpy/writers/core/base.py +++ b/satpy/writers/core/base.py @@ -24,7 +24,7 @@ from satpy.aux_download import DataDownloadMixin from satpy.plugin_base import Plugin -from satpy.utils import serialize_instruments +from satpy.utils import get_instruments_from_attrs, serialize_instruments, set_instruments_attr from satpy.writers.core.compute import compute_writer_results, split_results if typing.TYPE_CHECKING: @@ -139,7 +139,9 @@ def create_filename_parser(self, base_dir): @staticmethod def _prepare_metadata_for_filename_formatting(attrs): with contextlib.suppress(KeyError): - attrs["instruments"] = serialize_instruments(attrs["instruments"]) + instruments = get_instruments_from_attrs(attrs) + serialized = serialize_instruments(instruments) + set_instruments_attr(attrs, serialized) def get_filename(self, **kwargs): """Create a filename where output data will be saved. From 2d730ea73fd586217f6e322c88251dbc9d337f21 Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Wed, 6 May 2026 11:03:05 +0000 Subject: [PATCH 04/72] Reset ABI name --- satpy/readers/core/abi.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/satpy/readers/core/abi.py b/satpy/readers/core/abi.py index d51764dc38..e50cd616c4 100644 --- a/satpy/readers/core/abi.py +++ b/satpy/readers/core/abi.py @@ -111,7 +111,7 @@ def _rename_dims(nc): @property def sensor(self): """Get sensor name for current file handler.""" - return "ABI" + return "abi" def __getitem__(self, item): """Wrap `self.nc[item]` for better floating point precision. From 180a040d1add950511a052d4d95ed7a40a45f4bb Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Wed, 6 May 2026 12:04:14 +0000 Subject: [PATCH 05/72] Convert string type sensors to set for now --- satpy/utils.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/satpy/utils.py b/satpy/utils.py index d1f9b31d7e..c3a745d86b 100644 --- a/satpy/utils.py +++ b/satpy/utils.py @@ -947,7 +947,13 @@ def flatten_dict(d, parent_key="", sep="_"): def get_instruments_from_attrs(attrs: dict[str,Any]) -> set[str]: """Get instrument names from dataset attributes.""" key = get_instruments_key() - return attrs.get(key, set()) + try: + instruments = attrs[key] + if isinstance(instruments, str): + return set([instruments]) + return instruments + except KeyError: + return set() def normalize_instrument_name(instrument: str) -> str: From 46b08c29df381d506f44f70ce40e9911fecb9537 Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Wed, 6 May 2026 12:04:43 +0000 Subject: [PATCH 06/72] Fix tests --- satpy/tests/compositor_tests/test_aux_data.py | 4 ++-- satpy/tests/compositor_tests/test_core.py | 10 +++++----- satpy/tests/reader_tests/test_abi_l1b.py | 2 +- satpy/tests/scene_tests/test_load.py | 20 +++++++++---------- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/satpy/tests/compositor_tests/test_aux_data.py b/satpy/tests/compositor_tests/test_aux_data.py index 255231703e..d9e3657c66 100644 --- a/satpy/tests/compositor_tests/test_aux_data.py +++ b/satpy/tests/compositor_tests/test_aux_data.py @@ -83,7 +83,7 @@ def load(self, arg): filenames=[IMAGE_FILENAME]) register.assert_not_called() retrieve.assert_not_called() - assert res.attrs["sensor"] is None + assert res.attrs["sensor"] == set() assert "modifiers" not in res.attrs assert "calibration" not in res.attrs @@ -95,7 +95,7 @@ def load(self, arg): res = comp() Scene.assert_called_once_with(reader="generic_image", filenames=[os.path.join("data_dir", "foo.tif")]) - assert res.attrs["sensor"] is None + assert res.attrs["sensor"] == set() assert "modifiers" not in res.attrs assert "calibration" not in res.attrs diff --git a/satpy/tests/compositor_tests/test_core.py b/satpy/tests/compositor_tests/test_core.py index 315431aa4c..55f699aa42 100644 --- a/satpy/tests/compositor_tests/test_core.py +++ b/satpy/tests/compositor_tests/test_core.py @@ -277,13 +277,13 @@ def test_concat_datasets(self): def test_get_sensors(self): """Test getting sensors from the dataset attributes.""" res = self.comp._get_sensors([self.all_valid]) - assert res is None + assert res == set() dset1 = self.all_valid dset1.attrs["sensor"] = "foo" res = self.comp._get_sensors([dset1]) - assert res == "foo" + assert res == {"foo"} dset2 = self.first_invalid - dset2.attrs["sensor"] = "bar" + dset2.attrs["sensor"] = {"bar"} res = self.comp._get_sensors([dset1, dset2]) assert "foo" in res assert "bar" in res @@ -327,12 +327,12 @@ def test_call(self): """Test calling generic compositor.""" # Multiple datasets with extra attributes all_valid = self.all_valid - all_valid.attrs["sensor"] = "foo" + all_valid.attrs["sensor"] = {"foo"} attrs = {"foo": "bar", "resolution": 333} self.comp.attrs["resolution"] = None res = self.comp([self.all_valid, self.first_invalid], **attrs) # Verify attributes - assert res.attrs.get("sensor") == "foo" + assert res.attrs.get("sensor") == {"foo"} assert "foo" in res.attrs assert res.attrs.get("foo") == "bar" assert "units" not in res.attrs diff --git a/satpy/tests/reader_tests/test_abi_l1b.py b/satpy/tests/reader_tests/test_abi_l1b.py index bcb6d35041..855265dd21 100644 --- a/satpy/tests/reader_tests/test_abi_l1b.py +++ b/satpy/tests/reader_tests/test_abi_l1b.py @@ -371,7 +371,7 @@ def test_get_dataset(self, c01_data_arr): "scan_mode": "M4", "scene_abbr": "C", "scene_id": None, - "sensor": "abi", + "sensor": {"abi"}, "timeline_ID": None, "suffix": "suffix", "units": "W m-2 um-1 sr-1", diff --git a/satpy/tests/scene_tests/test_load.py b/satpy/tests/scene_tests/test_load.py index f3e862a46c..f39b416c8b 100644 --- a/satpy/tests/scene_tests/test_load.py +++ b/satpy/tests/scene_tests/test_load.py @@ -141,7 +141,7 @@ def test_available_when_sensor_none_in_preloaded_dataarrays(self): doesn't break available composite IDs. """ - scene = _scene_with_data_array_none_sensor() + scene = _scene_with_data_array_empty_sensor() available_comp_ids = scene.available_composite_ids() assert make_cid(name="static_image") in available_comp_ids @@ -645,31 +645,31 @@ def test_load_too_many(self): assert len(avail_comps) == 1 pytest.raises(KeyError, scene.load, [0.21]) - def test_load_when_sensor_none_in_preloaded_dataarrays(self): - """Test Scene loading when existing loaded arrays have sensor set to None. + def test_load_when_sensor_empty_in_preloaded_dataarrays(self): + """Test Scene loading when existing loaded arrays have empty sensor set. Some readers or composites (ex. static images) don't have a sensor and - developers choose to set it to `None`. This test makes sure this + developers choose to set it to `set()`. This test makes sure this doesn't break loading. """ - scene = _scene_with_data_array_none_sensor() + scene = _scene_with_data_array_empty_sensor() scene.load(["static_image"]) assert "static_image" in scene assert "my_data" in scene -def _scene_with_data_array_none_sensor(): +def _scene_with_data_array_empty_sensor(): scene = Scene(filenames=["fake1_1.txt"], reader="fake1") - scene["my_data"] = _data_array_none_sensor("my_data") + scene["my_data"] = _data_array_with_empty_sensor("my_data") return scene -def _data_array_none_sensor(name: str) -> xr.DataArray: - """Create a DataArray with sensor set to ``None``.""" +def _data_array_with_empty_sensor(name: str) -> xr.DataArray: + """Create a DataArray with empty sensor set.""" return xr.DataArray( da.zeros((2, 2)), attrs={ "name": name, - "sensor": None, + "sensor": set(), }) From fe1d232708a987fd92c00b1ffb241837b859d86d Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Wed, 6 May 2026 14:41:03 +0000 Subject: [PATCH 07/72] Update docstring --- satpy/utils.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/satpy/utils.py b/satpy/utils.py index c3a745d86b..96fc65869d 100644 --- a/satpy/utils.py +++ b/satpy/utils.py @@ -945,7 +945,12 @@ def flatten_dict(d, parent_key="", sep="_"): def get_instruments_from_attrs(attrs: dict[str,Any]) -> set[str]: - """Get instrument names from dataset attributes.""" + """Get instrument names from dataset attributes. + + String type attributes are converted to set. This can be + removed once all file handlers provide instruments as a + set. + """ key = get_instruments_key() try: instruments = attrs[key] From 32ee183d63e51f3f543994ad3e8fd2e962b9d8fd Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Wed, 6 May 2026 14:41:10 +0000 Subject: [PATCH 08/72] Restore accidental string replace --- satpy/dependency_tree.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/satpy/dependency_tree.py b/satpy/dependency_tree.py index 59da567c96..97777a71e9 100644 --- a/satpy/dependency_tree.py +++ b/satpy/dependency_tree.py @@ -514,7 +514,7 @@ def get_modifier(self, comp_id): mloader, moptions = modifiers[modifier] moptions = moptions.copy() moptions.update(comp_id.to_dict()) - moptions["instrument"] = sensor_name + moptions["sensor"] = sensor_name compositors[comp_id] = mloader(_satpy_id=comp_id, **moptions) return compositors[comp_id] From bb04e29e28d35dbab461fa33c77733f170a603a7 Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Mon, 4 May 2026 11:51:48 +0000 Subject: [PATCH 09/72] Make sensor attribute a set --- satpy/composites/aux_data.py | 2 +- satpy/composites/config_loader.py | 4 ++-- satpy/composites/core.py | 19 +++++------------ satpy/enhancements/enhancer.py | 23 +++++++++----------- satpy/modifiers/_crefl_utils.py | 6 ++++-- satpy/modifiers/atmosphere.py | 11 ++++++++-- satpy/modifiers/spectral.py | 7 ++++-- satpy/readers/abi_l1b.py | 2 +- satpy/readers/core/abi.py | 2 +- satpy/readers/core/file_handlers.py | 2 +- satpy/scene.py | 9 ++------ satpy/utils.py | 33 +++++++++++++++++++++++++++++ satpy/writers/core/base.py | 6 ++++-- satpy/writers/mitiff.py | 10 ++++----- 14 files changed, 82 insertions(+), 54 deletions(-) diff --git a/satpy/composites/aux_data.py b/satpy/composites/aux_data.py index 3179f826a5..3463c7368d 100644 --- a/satpy/composites/aux_data.py +++ b/satpy/composites/aux_data.py @@ -156,7 +156,7 @@ def __call__(self, *args, **kwargs): if self.area is None: raise AttributeError("Area definition needs to be configured") img.attrs["area"] = self.area - img.attrs["sensor"] = None + img.attrs["sensor"] = set() img.attrs["mode"] = "".join(img.bands.data) img.attrs.pop("modifiers", None) img.attrs.pop("calibration", None) diff --git a/satpy/composites/config_loader.py b/satpy/composites/config_loader.py index e5165ada01..6617cfc665 100644 --- a/satpy/composites/config_loader.py +++ b/satpy/composites/config_loader.py @@ -31,7 +31,7 @@ from satpy import DataID, DataQuery from satpy._config import config_search_paths, get_entry_points_config_dirs, glob_config from satpy.dataset.dataid import minimal_default_keys_config -from satpy.utils import recursive_dict_update +from satpy.utils import normalize_sensor_name, recursive_dict_update logger = logging.getLogger(__name__) @@ -268,7 +268,7 @@ def load_compositor_configs_for_sensor(sensor_name: str) -> tuple[dict[str, dict DataID key -> key properties """ - config_filename = sensor_name + ".yaml" + config_filename = normalize_sensor_name(sensor_name) + ".yaml" logger.debug("Looking for composites config file %s", config_filename) paths = get_entry_points_config_dirs("satpy.composites") composite_configs = config_search_paths( diff --git a/satpy/composites/core.py b/satpy/composites/core.py index 1b124eb88a..b568bc8819 100644 --- a/satpy/composites/core.py +++ b/satpy/composites/core.py @@ -28,7 +28,7 @@ from satpy.dataset import DataID, combine_metadata from satpy.dataset.dataid import minimal_default_keys_config -from satpy.utils import unify_chunks +from satpy.utils import get_sensors_from_attrs, unify_chunks LOG = logging.getLogger(__name__) @@ -433,20 +433,11 @@ def _concat_datasets(self, projectables, mode): return data - def _get_sensors(self, projectables): - sensor = set() + def _get_sensors(self, projectables) -> set[str]: + sensors = set() for projectable in projectables: - current_sensor = projectable.attrs.get("sensor", None) - if current_sensor: - if isinstance(current_sensor, (str, bytes)): - sensor.add(current_sensor) - else: - sensor |= current_sensor - if len(sensor) == 0: - sensor = None - elif len(sensor) == 1: - sensor = list(sensor)[0] - return sensor + sensors.update(get_sensors_from_attrs(projectable.attrs)) + return sensors def __call__( self, diff --git a/satpy/enhancements/enhancer.py b/satpy/enhancements/enhancer.py index b196c63e28..7a217bc82e 100644 --- a/satpy/enhancements/enhancer.py +++ b/satpy/enhancements/enhancer.py @@ -24,7 +24,7 @@ from satpy._config import config_search_paths, get_entry_points_config_dirs from satpy.decision_tree import DecisionTree -from satpy.utils import get_logger, recursive_dict_update +from satpy.utils import get_logger, get_sensors_from_attrs, normalize_sensor_name, recursive_dict_update LOG = get_logger(__name__) @@ -122,26 +122,23 @@ def __init__(self, enhancement_config_file=None): self.sensor_enhancement_configs = [] - def get_sensor_enhancement_config(self, sensor): + def get_sensor_enhancement_config(self, sensors: set[str]): """Get the sensor-specific config.""" - if isinstance(sensor, str): - # one single sensor - sensor = [sensor] - paths = get_entry_points_config_dirs("satpy.enhancements") - for sensor_name in sensor: - config_fn = os.path.join("enhancements", sensor_name + ".yaml") + for sensor_name in sensors: + basename = normalize_sensor_name(sensor_name) + ".yaml" + config_fn = os.path.join("enhancements", basename) config_files = config_search_paths(config_fn, search_dirs=paths) # Note: Enhancement configuration files can't overwrite individual # options, only entire sections are overwritten for config_file in config_files: yield config_file - def add_sensor_enhancements(self, sensor): + def add_sensor_enhancements(self, sensors: set[str]): """Add sensor-specific enhancements.""" # XXX: Should we just load all enhancements from the base directory? new_configs = [] - for config_file in self.get_sensor_enhancement_config(sensor): + for config_file in self.get_sensor_enhancement_config(sensors): if config_file not in self.sensor_enhancement_configs: self.sensor_enhancement_configs.append(config_file) new_configs.append(config_file) @@ -209,9 +206,9 @@ def get_enhanced_image(dataset, enhance=None, overlay=None, decorate=None, if enhancer is None or enhancer.enhancement_tree is None: LOG.debug("No enhancement being applied to dataset") else: - if dataset.attrs.get("sensor", None): - enhancer.add_sensor_enhancements(dataset.attrs["sensor"]) - + sensors = get_sensors_from_attrs(dataset.attrs) + if sensors: + enhancer.add_sensor_enhancements(sensors) enhancer.apply(img, **dataset.attrs) if overlay is not None: diff --git a/satpy/modifiers/_crefl_utils.py b/satpy/modifiers/_crefl_utils.py index 7e6ca0c50a..ca072f93f4 100644 --- a/satpy/modifiers/_crefl_utils.py +++ b/satpy/modifiers/_crefl_utils.py @@ -70,6 +70,7 @@ import xarray as xr from satpy.dataset.dataid import WavelengthRange +from satpy.utils import get_one_sensor_from_attrs, normalize_sensor_name LOG = logging.getLogger(__name__) @@ -282,7 +283,8 @@ def run_crefl(refl, :param avg_elevation: average elevation (usually pre-calculated and stored in CMGDEM.hdf) """ - runner_cls = _runner_class_for_sensor(refl.attrs["sensor"]) + sensor = get_one_sensor_from_attrs(refl.attrs) + runner_cls = _runner_class_for_sensor(sensor) runner = runner_cls(refl) corr_refl = runner(sensor_azimuth, sensor_zenith, solar_azimuth, solar_zenith, avg_elevation) return corr_refl @@ -384,7 +386,7 @@ def _run_crefl(self, mus, muv, phi, solar_zenith, sensor_zenith, height, coeffs) def _runner_class_for_sensor(sensor_name: str) -> Type[_CREFLRunner]: try: - return _SENSOR_TO_RUNNER[sensor_name] + return _SENSOR_TO_RUNNER[normalize_sensor_name(sensor_name)] except KeyError: raise NotImplementedError(f"Don't know how to apply CREFL to data from sensor {sensor_name}.") diff --git a/satpy/modifiers/atmosphere.py b/satpy/modifiers/atmosphere.py index c7144c27ca..1ed3f5d026 100644 --- a/satpy/modifiers/atmosphere.py +++ b/satpy/modifiers/atmosphere.py @@ -26,6 +26,7 @@ from satpy.modifiers import ModifierBase from satpy.modifiers._crefl import ReflectanceCorrector # noqa from satpy.modifiers.angles import compute_relative_azimuth, get_angles, get_satellite_zenith_angle +from satpy.utils import get_one_sensor_from_attrs, get_pyspectral_sensor_name logger = logging.getLogger(__name__) @@ -104,7 +105,10 @@ def __call__(self, projectables, optional_datasets=None, **info): logger.info("Removing Rayleigh scattering with atmosphere '%s' and " "aerosol type '%s' for '%s'", atmosphere, aerosol_type, vis.attrs["name"]) - corrector = Rayleigh(vis.attrs["platform_name"], vis.attrs["sensor"], + sensor = get_pyspectral_sensor_name( + get_one_sensor_from_attrs(vis.attrs) + ) + corrector = Rayleigh(vis.attrs["platform_name"], sensor, atmosphere=atmosphere, aerosol_type=aerosol_type) @@ -158,8 +162,11 @@ def __call__(self, projectables, optional_datasets=None, **info): satz = satz.data # get dask array underneath logger.info("Correction for limb cooling") + sensor = get_pyspectral_sensor_name( + get_one_sensor_from_attrs(band.attrs) + ) corrector = AtmosphericalCorrection(band.attrs["platform_name"], - band.attrs["sensor"]) + sensor) atm_corr = da.map_blocks(_call_mapped_correction, satz, band.data, corrector=corrector, diff --git a/satpy/modifiers/spectral.py b/satpy/modifiers/spectral.py index 402b5606d4..9e55972e0b 100644 --- a/satpy/modifiers/spectral.py +++ b/satpy/modifiers/spectral.py @@ -22,6 +22,7 @@ import xarray as xr from satpy.modifiers import ModifierBase +from satpy.utils import get_one_sensor_from_attrs, get_pyspectral_sensor_name try: from pyspectral.near_infrared_reflectance import Calculator @@ -131,8 +132,10 @@ def _init_reflectance_calculator(self, metadata): if not Calculator: logger.info("Couldn't load pyspectral") raise ImportError("No module named pyspectral.near_infrared_reflectance") - - reflectance_3x_calculator = Calculator(metadata["platform_name"], metadata["sensor"], metadata["name"], + sensor = get_pyspectral_sensor_name( + get_one_sensor_from_attrs(metadata) + ) + reflectance_3x_calculator = Calculator(metadata["platform_name"], sensor, metadata["name"], sunz_threshold=self.sun_zenith_threshold, masking_limit=self.masking_limit) return reflectance_3x_calculator diff --git a/satpy/readers/abi_l1b.py b/satpy/readers/abi_l1b.py index 48e82f6968..b144529ebe 100644 --- a/satpy/readers/abi_l1b.py +++ b/satpy/readers/abi_l1b.py @@ -73,7 +73,7 @@ def get_dataset(self, key, info): def _adjust_attrs(self, data, key): data.attrs.update({"platform_name": self.platform_name, - "sensor": self.sensor}) + "sensor": {self.sensor}}) # Add orbital parameters projection = self.nc["goes_imager_projection"] data.attrs["orbital_parameters"] = { diff --git a/satpy/readers/core/abi.py b/satpy/readers/core/abi.py index e50cd616c4..d51764dc38 100644 --- a/satpy/readers/core/abi.py +++ b/satpy/readers/core/abi.py @@ -111,7 +111,7 @@ def _rename_dims(nc): @property def sensor(self): """Get sensor name for current file handler.""" - return "abi" + return "ABI" def __getitem__(self, item): """Wrap `self.nc[item]` for better floating point precision. diff --git a/satpy/readers/core/file_handlers.py b/satpy/readers/core/file_handlers.py index ccf4f60bc5..54b57ebf1d 100644 --- a/satpy/readers/core/file_handlers.py +++ b/satpy/readers/core/file_handlers.py @@ -156,7 +156,7 @@ def end_time(self): return self.filename_info.get("end_time", self.start_time) @property - def sensor_names(self): + def sensor_names(self) -> set: """List of sensors represented in this file.""" raise NotImplementedError diff --git a/satpy/scene.py b/satpy/scene.py index 8693cc77b4..afcdb5dbae 100644 --- a/satpy/scene.py +++ b/satpy/scene.py @@ -36,7 +36,7 @@ from satpy.dependency_tree import DependencyTree from satpy.node import CompositorNode, MissingDependencies, ReaderNode from satpy.readers.core.loading import load_readers -from satpy.utils import convert_remote_files_to_fsspec, get_storage_options_from_reader_kwargs +from satpy.utils import convert_remote_files_to_fsspec, get_sensors_from_attrs, get_storage_options_from_reader_kwargs LOG = logging.getLogger(__name__) @@ -197,12 +197,7 @@ def sensor_names(self) -> set[str]: def _contained_sensor_names(self) -> set[str]: sensor_names = set() for data_arr in self.values(): - if "sensor" not in data_arr.attrs: - continue - if isinstance(data_arr.attrs["sensor"], str): - sensor_names.add(data_arr.attrs["sensor"]) - elif isinstance(data_arr.attrs["sensor"], set): - sensor_names.update(data_arr.attrs["sensor"]) + sensor_names.update(get_sensors_from_attrs(data_arr.attrs)) return sensor_names @property diff --git a/satpy/utils.py b/satpy/utils.py index bdf9d77e4c..1173d0718a 100644 --- a/satpy/utils.py +++ b/satpy/utils.py @@ -940,3 +940,36 @@ def flatten_dict(d, parent_key="", sep="_"): else: items.append((new_key, v)) return dict(items) + + +def get_sensors_from_attrs(attrs: dict[str,Any]) -> set[str]: + """Get sensor names from dataset attributes.""" + return attrs.get("sensor", set()) + + +def normalize_sensor_name(sensor: str) -> str: + """Normalize sensor name for internal usage.""" + return sensor.replace("-", "").replace(" ", "_").replace("/", "-").lower() + + +def get_one_sensor_from_attrs(attrs: dict[str,Any]) -> str: + """Get a single sensor name from dataset attributes.""" + sensors = get_sensors_from_attrs(attrs) + if not sensors: + raise KeyError("No 'sensor' dataset attribute") + if len(sensors) > 1: + logger.warning(f"More than one sensor in dataset attributes, will use the first value: {sensors}") + return list(sensors)[0] + + +def get_pyspectral_sensor_name(sensor: str) -> str: + """Get sensor name expected by pyspectral.""" + return normalize_sensor_name(sensor) + + +def serialize_sensors(sensors: set[str]) -> str: + """Serialize a set of sensors.""" + return "-".join( + sensor.replace("-", "").replace(" ", "").replace("/", "").lower() + for sensor in sorted(sensors) + ) diff --git a/satpy/writers/core/base.py b/satpy/writers/core/base.py index e0a53d0f7e..f8c1c1616e 100644 --- a/satpy/writers/core/base.py +++ b/satpy/writers/core/base.py @@ -16,6 +16,7 @@ """Shared objects and base classes for writers.""" from __future__ import annotations +import contextlib import logging import os import typing @@ -23,6 +24,7 @@ from satpy.aux_download import DataDownloadMixin from satpy.plugin_base import Plugin +from satpy.utils import serialize_sensors from satpy.writers.core.compute import compute_writer_results, split_results if typing.TYPE_CHECKING: @@ -136,8 +138,8 @@ def create_filename_parser(self, base_dir): @staticmethod def _prepare_metadata_for_filename_formatting(attrs): - if isinstance(attrs.get("sensor"), set): - attrs["sensor"] = "-".join(sorted(attrs["sensor"])) + with contextlib.suppress(KeyError): + attrs["sensor"] = serialize_sensors(attrs["sensor"]) def get_filename(self, **kwargs): """Create a filename where output data will be saved. diff --git a/satpy/writers/mitiff.py b/satpy/writers/mitiff.py index 7788d4b78a..0ed78e1a6a 100644 --- a/satpy/writers/mitiff.py +++ b/satpy/writers/mitiff.py @@ -28,6 +28,7 @@ from satpy.dataset import DataID, DataQuery from satpy.enhancements.enhancer import get_enhanced_image +from satpy.utils import get_one_sensor_from_attrs from satpy.writers.core.image import ImageWriter if typing.TYPE_CHECKING: @@ -53,12 +54,9 @@ def _adjust_kwargs(dataset, kwargs): if "start_time" not in kwargs: kwargs["start_time"] = dataset.attrs["start_time"] if "sensor" not in kwargs: - kwargs["sensor"] = dataset.attrs["sensor"] - # Sensor attrs could be set. MITIFFs needing to handle sensor can only have one sensor - # Assume the first value of set as the sensor. - if isinstance(kwargs["sensor"], set): - LOG.warning("Sensor is set, will use the first value: %s", kwargs["sensor"]) - kwargs["sensor"] = (list(kwargs["sensor"]))[0] + # MITIFFs needing to handle sensor can only have one sensor + # Assume the first value of set as the sensor. + kwargs["sensor"] = get_one_sensor_from_attrs(dataset.attrs) class MITIFFWriter(ImageWriter): From fbb55cf450df149dde5775058b23abb5d01f6a9e Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Wed, 6 May 2026 08:10:15 +0000 Subject: [PATCH 10/72] Use instrument instead of sensor attribute --- satpy/composites/aux_data.py | 2 +- satpy/composites/config_loader.py | 4 +-- satpy/composites/core.py | 6 ++--- satpy/composites/fill.py | 4 +-- satpy/composites/glm.py | 2 +- satpy/dependency_tree.py | 2 +- satpy/enhancements/enhancer.py | 10 ++++---- satpy/modifiers/_crefl_utils.py | 9 ++++--- satpy/modifiers/atmosphere.py | 10 ++++---- satpy/modifiers/spectral.py | 6 ++--- satpy/scene.py | 8 ++++-- satpy/utils.py | 42 +++++++++++++++---------------- satpy/writers/core/base.py | 4 +-- satpy/writers/mitiff.py | 4 +-- 14 files changed, 59 insertions(+), 54 deletions(-) diff --git a/satpy/composites/aux_data.py b/satpy/composites/aux_data.py index 3463c7368d..d5d74afb45 100644 --- a/satpy/composites/aux_data.py +++ b/satpy/composites/aux_data.py @@ -156,7 +156,7 @@ def __call__(self, *args, **kwargs): if self.area is None: raise AttributeError("Area definition needs to be configured") img.attrs["area"] = self.area - img.attrs["sensor"] = set() + img.attrs["instruments"] = set() img.attrs["mode"] = "".join(img.bands.data) img.attrs.pop("modifiers", None) img.attrs.pop("calibration", None) diff --git a/satpy/composites/config_loader.py b/satpy/composites/config_loader.py index 6617cfc665..96bdc61b72 100644 --- a/satpy/composites/config_loader.py +++ b/satpy/composites/config_loader.py @@ -31,7 +31,7 @@ from satpy import DataID, DataQuery from satpy._config import config_search_paths, get_entry_points_config_dirs, glob_config from satpy.dataset.dataid import minimal_default_keys_config -from satpy.utils import normalize_sensor_name, recursive_dict_update +from satpy.utils import normalize_instrument_name, recursive_dict_update logger = logging.getLogger(__name__) @@ -268,7 +268,7 @@ def load_compositor_configs_for_sensor(sensor_name: str) -> tuple[dict[str, dict DataID key -> key properties """ - config_filename = normalize_sensor_name(sensor_name) + ".yaml" + config_filename = normalize_instrument_name(sensor_name) + ".yaml" logger.debug("Looking for composites config file %s", config_filename) paths = get_entry_points_config_dirs("satpy.composites") composite_configs = config_search_paths( diff --git a/satpy/composites/core.py b/satpy/composites/core.py index b568bc8819..5f7c772a86 100644 --- a/satpy/composites/core.py +++ b/satpy/composites/core.py @@ -28,7 +28,7 @@ from satpy.dataset import DataID, combine_metadata from satpy.dataset.dataid import minimal_default_keys_config -from satpy.utils import get_sensors_from_attrs, unify_chunks +from satpy.utils import get_instruments_from_attrs, unify_chunks LOG = logging.getLogger(__name__) @@ -436,7 +436,7 @@ def _concat_datasets(self, projectables, mode): def _get_sensors(self, projectables) -> set[str]: sensors = set() for projectable in projectables: - sensors.update(get_sensors_from_attrs(projectable.attrs)) + sensors.update(get_instruments_from_attrs(projectable.attrs)) return sensors def __call__( @@ -503,7 +503,7 @@ def _get_updated_attrs(self, datasets, attrs, mode): new_attrs.update(self.attrs) if resolution is not None: new_attrs["resolution"] = resolution - new_attrs["sensor"] = self._get_sensors(datasets) + new_attrs["instruments"] = self._get_sensors(datasets) new_attrs["mode"] = mode return new_attrs diff --git a/satpy/composites/fill.py b/satpy/composites/fill.py index bd5b51577e..3b47f975d4 100644 --- a/satpy/composites/fill.py +++ b/satpy/composites/fill.py @@ -406,9 +406,9 @@ def _combine_metadata_with_mode_and_sensor(self, # 'mode' is no longer valid after we've remove the 'A' # let the base class __call__ determine mode attrs.pop("mode", None) - if attrs.get("sensor") is None: + if attrs.get("instruments") is None: # sensor can be a set - attrs["sensor"] = self._get_sensors([foreground, background]) + attrs["instruments"] = self._get_sensors([foreground, background]) return attrs @staticmethod diff --git a/satpy/composites/glm.py b/satpy/composites/glm.py index 866e952698..ceeef76e04 100644 --- a/satpy/composites/glm.py +++ b/satpy/composites/glm.py @@ -97,7 +97,7 @@ def _update_attrs(self, new_data, background_layer, highlight_layer): new_data.attrs["units"] = 1 new_sensors = self._get_sensors((highlight_layer, background_layer)) new_data.attrs.update({ - "sensor": new_sensors, + "instruments": new_sensors, }) def __call__(self, projectables, optional_datasets=None, **attrs): diff --git a/satpy/dependency_tree.py b/satpy/dependency_tree.py index 97777a71e9..59da567c96 100644 --- a/satpy/dependency_tree.py +++ b/satpy/dependency_tree.py @@ -514,7 +514,7 @@ def get_modifier(self, comp_id): mloader, moptions = modifiers[modifier] moptions = moptions.copy() moptions.update(comp_id.to_dict()) - moptions["sensor"] = sensor_name + moptions["instrument"] = sensor_name compositors[comp_id] = mloader(_satpy_id=comp_id, **moptions) return compositors[comp_id] diff --git a/satpy/enhancements/enhancer.py b/satpy/enhancements/enhancer.py index 7a217bc82e..2bf48bf688 100644 --- a/satpy/enhancements/enhancer.py +++ b/satpy/enhancements/enhancer.py @@ -24,7 +24,7 @@ from satpy._config import config_search_paths, get_entry_points_config_dirs from satpy.decision_tree import DecisionTree -from satpy.utils import get_logger, get_sensors_from_attrs, normalize_sensor_name, recursive_dict_update +from satpy.utils import get_instruments_from_attrs, get_logger, normalize_instrument_name, recursive_dict_update LOG = get_logger(__name__) @@ -38,12 +38,12 @@ def __init__(self, *decision_dicts, **kwargs): ("name", "reader", "platform_name", - "sensor", + "instruments", "standard_name", "units", )) self.prefix = kwargs.pop("config_section", "enhancements") - multival_keys = kwargs.pop("multival_keys", ["sensor"]) + multival_keys = kwargs.pop("multival_keys", ["instruments"]) super(EnhancementDecisionTree, self).__init__( decision_dicts, match_keys, multival_keys) @@ -126,7 +126,7 @@ def get_sensor_enhancement_config(self, sensors: set[str]): """Get the sensor-specific config.""" paths = get_entry_points_config_dirs("satpy.enhancements") for sensor_name in sensors: - basename = normalize_sensor_name(sensor_name) + ".yaml" + basename = normalize_instrument_name(sensor_name) + ".yaml" config_fn = os.path.join("enhancements", basename) config_files = config_search_paths(config_fn, search_dirs=paths) # Note: Enhancement configuration files can't overwrite individual @@ -206,7 +206,7 @@ def get_enhanced_image(dataset, enhance=None, overlay=None, decorate=None, if enhancer is None or enhancer.enhancement_tree is None: LOG.debug("No enhancement being applied to dataset") else: - sensors = get_sensors_from_attrs(dataset.attrs) + sensors = get_instruments_from_attrs(dataset.attrs) if sensors: enhancer.add_sensor_enhancements(sensors) enhancer.apply(img, **dataset.attrs) diff --git a/satpy/modifiers/_crefl_utils.py b/satpy/modifiers/_crefl_utils.py index ca072f93f4..c0d3a1e5c0 100644 --- a/satpy/modifiers/_crefl_utils.py +++ b/satpy/modifiers/_crefl_utils.py @@ -70,7 +70,7 @@ import xarray as xr from satpy.dataset.dataid import WavelengthRange -from satpy.utils import get_one_sensor_from_attrs, normalize_sensor_name +from satpy.utils import get_one_instrument_from_attrs, normalize_instrument_name LOG = logging.getLogger(__name__) @@ -283,7 +283,7 @@ def run_crefl(refl, :param avg_elevation: average elevation (usually pre-calculated and stored in CMGDEM.hdf) """ - sensor = get_one_sensor_from_attrs(refl.attrs) + sensor = get_one_instrument_from_attrs(refl.attrs) runner_cls = _runner_class_for_sensor(sensor) runner = runner_cls(refl) corr_refl = runner(sensor_azimuth, sensor_zenith, solar_azimuth, solar_zenith, avg_elevation) @@ -350,8 +350,9 @@ def _run_crefl(self, mus, muv, phi, solar_zenith, sensor_zenith, height, coeffs) class _VIIRSMODISCREFLRunner(_CREFLRunner): def _run_crefl(self, mus, muv, phi, solar_zenith, sensor_zenith, height, coeffs): + instrument = get_one_instrument_from_attrs(self._refl.attrs) return da.map_blocks(_run_crefl, self._refl.data, mus.data, muv.data, phi.data, - height, self._refl.attrs.get("sensor"), *coeffs, + height, instrument, *coeffs, meta=np.ndarray((), dtype=self._refl.dtype), chunks=self._refl.chunks, dtype=self._refl.dtype, ) @@ -386,7 +387,7 @@ def _run_crefl(self, mus, muv, phi, solar_zenith, sensor_zenith, height, coeffs) def _runner_class_for_sensor(sensor_name: str) -> Type[_CREFLRunner]: try: - return _SENSOR_TO_RUNNER[normalize_sensor_name(sensor_name)] + return _SENSOR_TO_RUNNER[normalize_instrument_name(sensor_name)] except KeyError: raise NotImplementedError(f"Don't know how to apply CREFL to data from sensor {sensor_name}.") diff --git a/satpy/modifiers/atmosphere.py b/satpy/modifiers/atmosphere.py index 1ed3f5d026..dbb8870da7 100644 --- a/satpy/modifiers/atmosphere.py +++ b/satpy/modifiers/atmosphere.py @@ -26,7 +26,7 @@ from satpy.modifiers import ModifierBase from satpy.modifiers._crefl import ReflectanceCorrector # noqa from satpy.modifiers.angles import compute_relative_azimuth, get_angles, get_satellite_zenith_angle -from satpy.utils import get_one_sensor_from_attrs, get_pyspectral_sensor_name +from satpy.utils import get_one_instrument_from_attrs, get_pyspectral_instrument_name logger = logging.getLogger(__name__) @@ -105,8 +105,8 @@ def __call__(self, projectables, optional_datasets=None, **info): logger.info("Removing Rayleigh scattering with atmosphere '%s' and " "aerosol type '%s' for '%s'", atmosphere, aerosol_type, vis.attrs["name"]) - sensor = get_pyspectral_sensor_name( - get_one_sensor_from_attrs(vis.attrs) + sensor = get_pyspectral_instrument_name( + get_one_instrument_from_attrs(vis.attrs) ) corrector = Rayleigh(vis.attrs["platform_name"], sensor, atmosphere=atmosphere, @@ -162,8 +162,8 @@ def __call__(self, projectables, optional_datasets=None, **info): satz = satz.data # get dask array underneath logger.info("Correction for limb cooling") - sensor = get_pyspectral_sensor_name( - get_one_sensor_from_attrs(band.attrs) + sensor = get_pyspectral_instrument_name( + get_one_instrument_from_attrs(band.attrs) ) corrector = AtmosphericalCorrection(band.attrs["platform_name"], sensor) diff --git a/satpy/modifiers/spectral.py b/satpy/modifiers/spectral.py index 9e55972e0b..67562f19e9 100644 --- a/satpy/modifiers/spectral.py +++ b/satpy/modifiers/spectral.py @@ -22,7 +22,7 @@ import xarray as xr from satpy.modifiers import ModifierBase -from satpy.utils import get_one_sensor_from_attrs, get_pyspectral_sensor_name +from satpy.utils import get_one_instrument_from_attrs, get_pyspectral_instrument_name try: from pyspectral.near_infrared_reflectance import Calculator @@ -132,8 +132,8 @@ def _init_reflectance_calculator(self, metadata): if not Calculator: logger.info("Couldn't load pyspectral") raise ImportError("No module named pyspectral.near_infrared_reflectance") - sensor = get_pyspectral_sensor_name( - get_one_sensor_from_attrs(metadata) + sensor = get_pyspectral_instrument_name( + get_one_instrument_from_attrs(metadata) ) reflectance_3x_calculator = Calculator(metadata["platform_name"], sensor, metadata["name"], sunz_threshold=self.sun_zenith_threshold, diff --git a/satpy/scene.py b/satpy/scene.py index afcdb5dbae..30452ee989 100644 --- a/satpy/scene.py +++ b/satpy/scene.py @@ -36,7 +36,11 @@ from satpy.dependency_tree import DependencyTree from satpy.node import CompositorNode, MissingDependencies, ReaderNode from satpy.readers.core.loading import load_readers -from satpy.utils import convert_remote_files_to_fsspec, get_sensors_from_attrs, get_storage_options_from_reader_kwargs +from satpy.utils import ( + convert_remote_files_to_fsspec, + get_instruments_from_attrs, + get_storage_options_from_reader_kwargs, +) LOG = logging.getLogger(__name__) @@ -197,7 +201,7 @@ def sensor_names(self) -> set[str]: def _contained_sensor_names(self) -> set[str]: sensor_names = set() for data_arr in self.values(): - sensor_names.update(get_sensors_from_attrs(data_arr.attrs)) + sensor_names.update(get_instruments_from_attrs(data_arr.attrs)) return sensor_names @property diff --git a/satpy/utils.py b/satpy/utils.py index 1173d0718a..81f55f7cc7 100644 --- a/satpy/utils.py +++ b/satpy/utils.py @@ -942,34 +942,34 @@ def flatten_dict(d, parent_key="", sep="_"): return dict(items) -def get_sensors_from_attrs(attrs: dict[str,Any]) -> set[str]: - """Get sensor names from dataset attributes.""" - return attrs.get("sensor", set()) +def get_instruments_from_attrs(attrs: dict[str,Any]) -> set[str]: + """Get instrument names from dataset attributes.""" + return attrs.get("instruments", set()) -def normalize_sensor_name(sensor: str) -> str: - """Normalize sensor name for internal usage.""" - return sensor.replace("-", "").replace(" ", "_").replace("/", "-").lower() +def normalize_instrument_name(instrument: str) -> str: + """Normalize instrument name for internal usage.""" + return instrument.replace("-", "").replace(" ", "_").replace("/", "-").lower() -def get_one_sensor_from_attrs(attrs: dict[str,Any]) -> str: - """Get a single sensor name from dataset attributes.""" - sensors = get_sensors_from_attrs(attrs) - if not sensors: - raise KeyError("No 'sensor' dataset attribute") - if len(sensors) > 1: - logger.warning(f"More than one sensor in dataset attributes, will use the first value: {sensors}") - return list(sensors)[0] +def get_one_instrument_from_attrs(attrs: dict[str,Any]) -> str: + """Get a single instrument name from dataset attributes.""" + instruments = get_instruments_from_attrs(attrs) + if not instruments: + raise KeyError("No 'instruments' dataset attribute") + if len(instruments) > 1: + logger.warning(f"More than one instrument in dataset attributes, will use the first value: {instruments}") + return list(instruments)[0] -def get_pyspectral_sensor_name(sensor: str) -> str: - """Get sensor name expected by pyspectral.""" - return normalize_sensor_name(sensor) +def get_pyspectral_instrument_name(instrument: str) -> str: + """Get instrument name expected by pyspectral.""" + return normalize_instrument_name(instrument) -def serialize_sensors(sensors: set[str]) -> str: - """Serialize a set of sensors.""" +def serialize_instruments(instruments: set[str]) -> str: + """Serialize a set of instruments.""" return "-".join( - sensor.replace("-", "").replace(" ", "").replace("/", "").lower() - for sensor in sorted(sensors) + instr.replace("-", "").replace(" ", "").replace("/", "").lower() + for instr in sorted(instruments) ) diff --git a/satpy/writers/core/base.py b/satpy/writers/core/base.py index f8c1c1616e..056b7732b8 100644 --- a/satpy/writers/core/base.py +++ b/satpy/writers/core/base.py @@ -24,7 +24,7 @@ from satpy.aux_download import DataDownloadMixin from satpy.plugin_base import Plugin -from satpy.utils import serialize_sensors +from satpy.utils import serialize_instruments from satpy.writers.core.compute import compute_writer_results, split_results if typing.TYPE_CHECKING: @@ -139,7 +139,7 @@ def create_filename_parser(self, base_dir): @staticmethod def _prepare_metadata_for_filename_formatting(attrs): with contextlib.suppress(KeyError): - attrs["sensor"] = serialize_sensors(attrs["sensor"]) + attrs["instruments"] = serialize_instruments(attrs["instruments"]) def get_filename(self, **kwargs): """Create a filename where output data will be saved. diff --git a/satpy/writers/mitiff.py b/satpy/writers/mitiff.py index 0ed78e1a6a..b8a38c955d 100644 --- a/satpy/writers/mitiff.py +++ b/satpy/writers/mitiff.py @@ -28,7 +28,7 @@ from satpy.dataset import DataID, DataQuery from satpy.enhancements.enhancer import get_enhanced_image -from satpy.utils import get_one_sensor_from_attrs +from satpy.utils import get_one_instrument_from_attrs from satpy.writers.core.image import ImageWriter if typing.TYPE_CHECKING: @@ -56,7 +56,7 @@ def _adjust_kwargs(dataset, kwargs): if "sensor" not in kwargs: # MITIFFs needing to handle sensor can only have one sensor # Assume the first value of set as the sensor. - kwargs["sensor"] = get_one_sensor_from_attrs(dataset.attrs) + kwargs["sensor"] = get_one_instrument_from_attrs(dataset.attrs) class MITIFFWriter(ImageWriter): From 9633197b1fce4d3bcc9f93de78dd9f0f9cb4f829 Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Wed, 6 May 2026 10:58:39 +0000 Subject: [PATCH 11/72] Add instrument attribute setter --- satpy/_config.py | 1 + satpy/composites/aux_data.py | 3 ++- satpy/composites/core.py | 4 ++-- satpy/composites/fill.py | 6 ++++-- satpy/enhancements/enhancer.py | 13 ++++++++++--- satpy/utils.py | 16 +++++++++++++++- satpy/writers/core/base.py | 6 ++++-- 7 files changed, 38 insertions(+), 11 deletions(-) diff --git a/satpy/_config.py b/satpy/_config.py index 47012742d6..0b2e6a57c0 100644 --- a/satpy/_config.py +++ b/satpy/_config.py @@ -54,6 +54,7 @@ "readers": { "clip_negative_radiances": False, }, + "instruments_key": "sensor" } # Satpy main configuration object diff --git a/satpy/composites/aux_data.py b/satpy/composites/aux_data.py index d5d74afb45..406a451295 100644 --- a/satpy/composites/aux_data.py +++ b/satpy/composites/aux_data.py @@ -23,6 +23,7 @@ import satpy from satpy.aux_download import DataDownloadMixin +from satpy.utils import set_instruments_attr from .core import GenericCompositor @@ -156,7 +157,7 @@ def __call__(self, *args, **kwargs): if self.area is None: raise AttributeError("Area definition needs to be configured") img.attrs["area"] = self.area - img.attrs["instruments"] = set() + set_instruments_attr(img.attrs, set()) img.attrs["mode"] = "".join(img.bands.data) img.attrs.pop("modifiers", None) img.attrs.pop("calibration", None) diff --git a/satpy/composites/core.py b/satpy/composites/core.py index 5f7c772a86..85b31d1869 100644 --- a/satpy/composites/core.py +++ b/satpy/composites/core.py @@ -28,7 +28,7 @@ from satpy.dataset import DataID, combine_metadata from satpy.dataset.dataid import minimal_default_keys_config -from satpy.utils import get_instruments_from_attrs, unify_chunks +from satpy.utils import get_instruments_from_attrs, set_instruments_attr, unify_chunks LOG = logging.getLogger(__name__) @@ -503,7 +503,7 @@ def _get_updated_attrs(self, datasets, attrs, mode): new_attrs.update(self.attrs) if resolution is not None: new_attrs["resolution"] = resolution - new_attrs["instruments"] = self._get_sensors(datasets) + set_instruments_attr(new_attrs, self._get_sensors(datasets)) new_attrs["mode"] = mode return new_attrs diff --git a/satpy/composites/fill.py b/satpy/composites/fill.py index 3b47f975d4..994d6437f1 100644 --- a/satpy/composites/fill.py +++ b/satpy/composites/fill.py @@ -26,6 +26,7 @@ import xarray as xr from satpy.dataset import combine_metadata +from satpy.utils import get_instruments_from_attrs, set_instruments_attr from .core import ( GenericCompositor, @@ -406,9 +407,10 @@ def _combine_metadata_with_mode_and_sensor(self, # 'mode' is no longer valid after we've remove the 'A' # let the base class __call__ determine mode attrs.pop("mode", None) - if attrs.get("instruments") is None: + if not get_instruments_from_attrs(attrs): # sensor can be a set - attrs["instruments"] = self._get_sensors([foreground, background]) + instruments = self._get_sensors([foreground, background]) + set_instruments_attr(attrs, instruments) return attrs @staticmethod diff --git a/satpy/enhancements/enhancer.py b/satpy/enhancements/enhancer.py index 2bf48bf688..291c12c166 100644 --- a/satpy/enhancements/enhancer.py +++ b/satpy/enhancements/enhancer.py @@ -24,7 +24,13 @@ from satpy._config import config_search_paths, get_entry_points_config_dirs from satpy.decision_tree import DecisionTree -from satpy.utils import get_instruments_from_attrs, get_logger, normalize_instrument_name, recursive_dict_update +from satpy.utils import ( + get_instruments_from_attrs, + get_instruments_key, + get_logger, + normalize_instrument_name, + recursive_dict_update, +) LOG = get_logger(__name__) @@ -34,16 +40,17 @@ class EnhancementDecisionTree(DecisionTree): def __init__(self, *decision_dicts, **kwargs): """Init the decision tree.""" + instr_key = get_instruments_key() match_keys = kwargs.pop("match_keys", ("name", "reader", "platform_name", - "instruments", + instr_key, "standard_name", "units", )) self.prefix = kwargs.pop("config_section", "enhancements") - multival_keys = kwargs.pop("multival_keys", ["instruments"]) + multival_keys = kwargs.pop("multival_keys", [instr_key]) super(EnhancementDecisionTree, self).__init__( decision_dicts, match_keys, multival_keys) diff --git a/satpy/utils.py b/satpy/utils.py index 81f55f7cc7..d1f9b31d7e 100644 --- a/satpy/utils.py +++ b/satpy/utils.py @@ -39,6 +39,8 @@ from numpy.typing import ArrayLike, DTypeLike from yaml import BaseLoader, UnsafeLoader +import satpy + _is_logging_on = False TRACE_LEVEL = 5 @@ -944,7 +946,8 @@ def flatten_dict(d, parent_key="", sep="_"): def get_instruments_from_attrs(attrs: dict[str,Any]) -> set[str]: """Get instrument names from dataset attributes.""" - return attrs.get("instruments", set()) + key = get_instruments_key() + return attrs.get(key, set()) def normalize_instrument_name(instrument: str) -> str: @@ -973,3 +976,14 @@ def serialize_instruments(instruments: set[str]) -> str: instr.replace("-", "").replace(" ", "").replace("/", "").lower() for instr in sorted(instruments) ) + + +def set_instruments_attr(attrs: dict[str,Any], instruments: set[str]|str) -> None: + """Set 'instruments' dataset atrribute.""" + key = get_instruments_key() + attrs[key] = instruments + + +def get_instruments_key(): + """Get key for instruments in dataset attributes.""" + return satpy.config.get("instruments_key") diff --git a/satpy/writers/core/base.py b/satpy/writers/core/base.py index 056b7732b8..c2f5f69f3b 100644 --- a/satpy/writers/core/base.py +++ b/satpy/writers/core/base.py @@ -24,7 +24,7 @@ from satpy.aux_download import DataDownloadMixin from satpy.plugin_base import Plugin -from satpy.utils import serialize_instruments +from satpy.utils import get_instruments_from_attrs, serialize_instruments, set_instruments_attr from satpy.writers.core.compute import compute_writer_results, split_results if typing.TYPE_CHECKING: @@ -139,7 +139,9 @@ def create_filename_parser(self, base_dir): @staticmethod def _prepare_metadata_for_filename_formatting(attrs): with contextlib.suppress(KeyError): - attrs["instruments"] = serialize_instruments(attrs["instruments"]) + instruments = get_instruments_from_attrs(attrs) + serialized = serialize_instruments(instruments) + set_instruments_attr(attrs, serialized) def get_filename(self, **kwargs): """Create a filename where output data will be saved. From 1d362c7fceec86b30f6f04599e005e29e805a865 Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Wed, 6 May 2026 11:03:05 +0000 Subject: [PATCH 12/72] Reset ABI name --- satpy/readers/core/abi.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/satpy/readers/core/abi.py b/satpy/readers/core/abi.py index d51764dc38..e50cd616c4 100644 --- a/satpy/readers/core/abi.py +++ b/satpy/readers/core/abi.py @@ -111,7 +111,7 @@ def _rename_dims(nc): @property def sensor(self): """Get sensor name for current file handler.""" - return "ABI" + return "abi" def __getitem__(self, item): """Wrap `self.nc[item]` for better floating point precision. From ae1d8e712b0a958ca86d552a544f5e55e302429b Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Wed, 6 May 2026 12:04:14 +0000 Subject: [PATCH 13/72] Convert string type sensors to set for now --- satpy/utils.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/satpy/utils.py b/satpy/utils.py index d1f9b31d7e..c3a745d86b 100644 --- a/satpy/utils.py +++ b/satpy/utils.py @@ -947,7 +947,13 @@ def flatten_dict(d, parent_key="", sep="_"): def get_instruments_from_attrs(attrs: dict[str,Any]) -> set[str]: """Get instrument names from dataset attributes.""" key = get_instruments_key() - return attrs.get(key, set()) + try: + instruments = attrs[key] + if isinstance(instruments, str): + return set([instruments]) + return instruments + except KeyError: + return set() def normalize_instrument_name(instrument: str) -> str: From d95a3f3492116fcac882c35a870d780abc81c3ed Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Wed, 6 May 2026 12:04:43 +0000 Subject: [PATCH 14/72] Fix tests --- satpy/tests/compositor_tests/test_aux_data.py | 4 ++-- satpy/tests/compositor_tests/test_core.py | 10 +++++----- satpy/tests/reader_tests/test_abi_l1b.py | 2 +- satpy/tests/scene_tests/test_load.py | 20 +++++++++---------- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/satpy/tests/compositor_tests/test_aux_data.py b/satpy/tests/compositor_tests/test_aux_data.py index 255231703e..d9e3657c66 100644 --- a/satpy/tests/compositor_tests/test_aux_data.py +++ b/satpy/tests/compositor_tests/test_aux_data.py @@ -83,7 +83,7 @@ def load(self, arg): filenames=[IMAGE_FILENAME]) register.assert_not_called() retrieve.assert_not_called() - assert res.attrs["sensor"] is None + assert res.attrs["sensor"] == set() assert "modifiers" not in res.attrs assert "calibration" not in res.attrs @@ -95,7 +95,7 @@ def load(self, arg): res = comp() Scene.assert_called_once_with(reader="generic_image", filenames=[os.path.join("data_dir", "foo.tif")]) - assert res.attrs["sensor"] is None + assert res.attrs["sensor"] == set() assert "modifiers" not in res.attrs assert "calibration" not in res.attrs diff --git a/satpy/tests/compositor_tests/test_core.py b/satpy/tests/compositor_tests/test_core.py index 315431aa4c..55f699aa42 100644 --- a/satpy/tests/compositor_tests/test_core.py +++ b/satpy/tests/compositor_tests/test_core.py @@ -277,13 +277,13 @@ def test_concat_datasets(self): def test_get_sensors(self): """Test getting sensors from the dataset attributes.""" res = self.comp._get_sensors([self.all_valid]) - assert res is None + assert res == set() dset1 = self.all_valid dset1.attrs["sensor"] = "foo" res = self.comp._get_sensors([dset1]) - assert res == "foo" + assert res == {"foo"} dset2 = self.first_invalid - dset2.attrs["sensor"] = "bar" + dset2.attrs["sensor"] = {"bar"} res = self.comp._get_sensors([dset1, dset2]) assert "foo" in res assert "bar" in res @@ -327,12 +327,12 @@ def test_call(self): """Test calling generic compositor.""" # Multiple datasets with extra attributes all_valid = self.all_valid - all_valid.attrs["sensor"] = "foo" + all_valid.attrs["sensor"] = {"foo"} attrs = {"foo": "bar", "resolution": 333} self.comp.attrs["resolution"] = None res = self.comp([self.all_valid, self.first_invalid], **attrs) # Verify attributes - assert res.attrs.get("sensor") == "foo" + assert res.attrs.get("sensor") == {"foo"} assert "foo" in res.attrs assert res.attrs.get("foo") == "bar" assert "units" not in res.attrs diff --git a/satpy/tests/reader_tests/test_abi_l1b.py b/satpy/tests/reader_tests/test_abi_l1b.py index bcb6d35041..855265dd21 100644 --- a/satpy/tests/reader_tests/test_abi_l1b.py +++ b/satpy/tests/reader_tests/test_abi_l1b.py @@ -371,7 +371,7 @@ def test_get_dataset(self, c01_data_arr): "scan_mode": "M4", "scene_abbr": "C", "scene_id": None, - "sensor": "abi", + "sensor": {"abi"}, "timeline_ID": None, "suffix": "suffix", "units": "W m-2 um-1 sr-1", diff --git a/satpy/tests/scene_tests/test_load.py b/satpy/tests/scene_tests/test_load.py index f3e862a46c..f39b416c8b 100644 --- a/satpy/tests/scene_tests/test_load.py +++ b/satpy/tests/scene_tests/test_load.py @@ -141,7 +141,7 @@ def test_available_when_sensor_none_in_preloaded_dataarrays(self): doesn't break available composite IDs. """ - scene = _scene_with_data_array_none_sensor() + scene = _scene_with_data_array_empty_sensor() available_comp_ids = scene.available_composite_ids() assert make_cid(name="static_image") in available_comp_ids @@ -645,31 +645,31 @@ def test_load_too_many(self): assert len(avail_comps) == 1 pytest.raises(KeyError, scene.load, [0.21]) - def test_load_when_sensor_none_in_preloaded_dataarrays(self): - """Test Scene loading when existing loaded arrays have sensor set to None. + def test_load_when_sensor_empty_in_preloaded_dataarrays(self): + """Test Scene loading when existing loaded arrays have empty sensor set. Some readers or composites (ex. static images) don't have a sensor and - developers choose to set it to `None`. This test makes sure this + developers choose to set it to `set()`. This test makes sure this doesn't break loading. """ - scene = _scene_with_data_array_none_sensor() + scene = _scene_with_data_array_empty_sensor() scene.load(["static_image"]) assert "static_image" in scene assert "my_data" in scene -def _scene_with_data_array_none_sensor(): +def _scene_with_data_array_empty_sensor(): scene = Scene(filenames=["fake1_1.txt"], reader="fake1") - scene["my_data"] = _data_array_none_sensor("my_data") + scene["my_data"] = _data_array_with_empty_sensor("my_data") return scene -def _data_array_none_sensor(name: str) -> xr.DataArray: - """Create a DataArray with sensor set to ``None``.""" +def _data_array_with_empty_sensor(name: str) -> xr.DataArray: + """Create a DataArray with empty sensor set.""" return xr.DataArray( da.zeros((2, 2)), attrs={ "name": name, - "sensor": None, + "sensor": set(), }) From 2107f44cb32206b7622db402bb3deaa99dd79095 Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Wed, 6 May 2026 14:41:03 +0000 Subject: [PATCH 15/72] Update docstring --- satpy/utils.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/satpy/utils.py b/satpy/utils.py index c3a745d86b..96fc65869d 100644 --- a/satpy/utils.py +++ b/satpy/utils.py @@ -945,7 +945,12 @@ def flatten_dict(d, parent_key="", sep="_"): def get_instruments_from_attrs(attrs: dict[str,Any]) -> set[str]: - """Get instrument names from dataset attributes.""" + """Get instrument names from dataset attributes. + + String type attributes are converted to set. This can be + removed once all file handlers provide instruments as a + set. + """ key = get_instruments_key() try: instruments = attrs[key] From 78e0de97b39bcf1e9efc152069289def6f74fc4b Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Wed, 6 May 2026 14:41:10 +0000 Subject: [PATCH 16/72] Restore accidental string replace --- satpy/dependency_tree.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/satpy/dependency_tree.py b/satpy/dependency_tree.py index 59da567c96..97777a71e9 100644 --- a/satpy/dependency_tree.py +++ b/satpy/dependency_tree.py @@ -514,7 +514,7 @@ def get_modifier(self, comp_id): mloader, moptions = modifiers[modifier] moptions = moptions.copy() moptions.update(comp_id.to_dict()) - moptions["instrument"] = sensor_name + moptions["sensor"] = sensor_name compositors[comp_id] = mloader(_satpy_id=comp_id, **moptions) return compositors[comp_id] From 32a8e024b7508ab44ab557782f04cbc4dc282c3b Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Thu, 7 May 2026 09:39:50 +0000 Subject: [PATCH 17/72] Replace direct attribute access with setter --- satpy/composites/glm.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/satpy/composites/glm.py b/satpy/composites/glm.py index ceeef76e04..e5d1000269 100644 --- a/satpy/composites/glm.py +++ b/satpy/composites/glm.py @@ -22,6 +22,7 @@ import xarray as xr from satpy.enhancements.enhancer import get_enhanced_image +from satpy.utils import set_instruments_attr from .core import GenericCompositor @@ -96,9 +97,7 @@ def _update_attrs(self, new_data, background_layer, highlight_layer): new_data.attrs = background_layer.attrs.copy() new_data.attrs["units"] = 1 new_sensors = self._get_sensors((highlight_layer, background_layer)) - new_data.attrs.update({ - "instruments": new_sensors, - }) + set_instruments_attr(new_data.attrs, new_sensors) def __call__(self, projectables, optional_datasets=None, **attrs): """Create RGBA image with highlighted pixels.""" From e4a53b6f167cc6649333821d36be440c64e64724 Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Thu, 7 May 2026 10:18:50 +0000 Subject: [PATCH 18/72] Restore enhancer keyword arguments --- satpy/enhancements/enhancer.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/satpy/enhancements/enhancer.py b/satpy/enhancements/enhancer.py index 291c12c166..b0680c9bfb 100644 --- a/satpy/enhancements/enhancer.py +++ b/satpy/enhancements/enhancer.py @@ -26,7 +26,6 @@ from satpy.decision_tree import DecisionTree from satpy.utils import ( get_instruments_from_attrs, - get_instruments_key, get_logger, normalize_instrument_name, recursive_dict_update, @@ -40,17 +39,16 @@ class EnhancementDecisionTree(DecisionTree): def __init__(self, *decision_dicts, **kwargs): """Init the decision tree.""" - instr_key = get_instruments_key() match_keys = kwargs.pop("match_keys", ("name", "reader", "platform_name", - instr_key, + "sensor", "standard_name", "units", )) self.prefix = kwargs.pop("config_section", "enhancements") - multival_keys = kwargs.pop("multival_keys", [instr_key]) + multival_keys = kwargs.pop("multival_keys", ["sensor"]) super(EnhancementDecisionTree, self).__init__( decision_dicts, match_keys, multival_keys) From db7731ddedf59ca72822c16f15e607af1ccabc5f Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Thu, 7 May 2026 12:01:08 +0000 Subject: [PATCH 19/72] Add deprecation warnings for sensor attribute --- satpy/utils.py | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/satpy/utils.py b/satpy/utils.py index 96fc65869d..f051d7d7fe 100644 --- a/satpy/utils.py +++ b/satpy/utils.py @@ -951,14 +951,26 @@ def get_instruments_from_attrs(attrs: dict[str,Any]) -> set[str]: removed once all file handlers provide instruments as a set. """ - key = get_instruments_key() - try: - instruments = attrs[key] - if isinstance(instruments, str): - return set([instruments]) - return instruments - except KeyError: - return set() + legacy = attrs.get("sensor", set()) + instruments = attrs.get("instruments", legacy) + if legacy: + warnings.warn( + "Satpy will ignore the 'sensor' attribute as of v1.1. " + "Use the 'instruments' attribute instead.", + DeprecationWarning, + stacklevel=2 + ) + if isinstance(instruments, str): + warnings.warn( + "Converting 'instruments' attribute from string to set. " + "This will result in an error in v1.1, when Satpy will require " + "set type instruments attributes.", + DeprecationWarning, + stacklevel=2 + ) + instruments = set([instruments]) + return instruments + def normalize_instrument_name(instrument: str) -> str: @@ -970,7 +982,7 @@ def get_one_instrument_from_attrs(attrs: dict[str,Any]) -> str: """Get a single instrument name from dataset attributes.""" instruments = get_instruments_from_attrs(attrs) if not instruments: - raise KeyError("No 'instruments' dataset attribute") + raise KeyError("No 'instruments' in dataset attribute") if len(instruments) > 1: logger.warning(f"More than one instrument in dataset attributes, will use the first value: {instruments}") return list(instruments)[0] From 8d50a8d9525969727f22a758982464c94937f72c Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Thu, 7 May 2026 12:02:00 +0000 Subject: [PATCH 20/72] Add tests for instrument utilities --- satpy/tests/test_utils.py | 69 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/satpy/tests/test_utils.py b/satpy/tests/test_utils.py index fbb917b948..2c69479a56 100644 --- a/satpy/tests/test_utils.py +++ b/satpy/tests/test_utils.py @@ -28,14 +28,20 @@ import pytest import xarray as xr +import satpy from satpy.utils import ( angle2xyz, datetime64_to_pydatetime, + get_instruments_from_attrs, get_legacy_chunk_size, + get_one_instrument_from_attrs, get_satpos, import_error_helper, lonlat2xyz, + normalize_instrument_name, proj_units_to_meters, + serialize_instruments, + set_instruments_attr, xyz2angle, xyz2lonlat, ) @@ -662,3 +668,66 @@ def test_flatten_dict(): "b_d_e": 1, "b_d_f_g": [1, 2]} assert flatten_dict(d) == expected + + +class TestInstrumentsUtils: + """Test instruments attribute utilities.""" + + @pytest.mark.parametrize( + ("attrs", "expected"), + [ + ({"instruments": {"myinstr"}}, {"myinstr"}), + ({}, set()), + ] + ) + def test_get_instruments_from_attrs(self, attrs, expected): + """Test getting instruments from dataset attributes.""" + assert get_instruments_from_attrs(attrs) == expected + + @pytest.mark.parametrize( + ("attrs", "expected"), + [ + ({"sensor": "myinstr"}, {"myinstr"}), + ({"sensor": {"myinstr"}}, {"myinstr"}), + ({"instruments": "myinstr"}, {"myinstr"}), + ] + ) + def test_get_instruments_from_attrs_with_warning(self, attrs, expected): + """Test deprecation warnings when getting instruments.""" + with pytest.warns(DeprecationWarning, match="v1.1"): + assert get_instruments_from_attrs(attrs) == expected + + def test_normalize_instrument_name(self): + """Test instrument name normalization.""" + instr = "My Instrument-123/1" + expected = "my_instrument123-1" + assert normalize_instrument_name(instr) == expected + + def test_serialize_instruments(self): + """Test instrument set serialization.""" + instruments = {"My Instrument-123/1", "ABI"} + expected = "abi-myinstrument1231" + assert serialize_instruments(instruments) == expected + + def test_set_instruments_attr(self): + """Test setting instruments attribute.""" + attrs = {"instruments": {"myinstrument"}} + new_instruments = {"i1", "i2"} + with satpy.config.set(instruments_key="instruments"): + set_instruments_attr(attrs, new_instruments) + assert attrs["instruments"] == new_instruments + + def test_get_one_instrument_from_attrs(self): + """Test getting a single instrument from dataset attributes.""" + attrs = {"instruments": {"i1"}} + with satpy.config.set(instruments_key="instruments"): + assert get_one_instrument_from_attrs(attrs) == "i1" + + def test_get_one_instrument_from_attrs_with_warning(self, caplog): + """Test warnings when getting a single instrument.""" + attrs = {"instruments": {"i1", "i2"}} + with satpy.config.set(instruments_key="instruments"): + get_one_instrument_from_attrs(attrs) + assert "More than one" in caplog.text + with pytest.raises(KeyError): + get_one_instrument_from_attrs({}) From f0970e39eec8d1b2c288fccc605f8bf6eabdadf1 Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Fri, 8 May 2026 10:13:15 +0000 Subject: [PATCH 21/72] Move instrument helpers to their own module --- satpy/_instruments.py | 91 +++++++++++++++++++++++++++++++ satpy/composites/aux_data.py | 4 +- satpy/composites/config_loader.py | 5 +- satpy/composites/core.py | 7 ++- satpy/composites/fill.py | 6 +- satpy/composites/glm.py | 4 +- satpy/enhancements/enhancer.py | 7 +-- satpy/modifiers/_crefl_utils.py | 8 +-- satpy/modifiers/atmosphere.py | 10 ++-- satpy/modifiers/spectral.py | 6 +- satpy/scene.py | 4 +- satpy/tests/test_instruments.py | 81 +++++++++++++++++++++++++++ satpy/tests/test_utils.py | 69 ----------------------- satpy/utils.py | 70 ------------------------ satpy/writers/core/base.py | 8 +-- satpy/writers/mitiff.py | 4 +- 16 files changed, 209 insertions(+), 175 deletions(-) create mode 100644 satpy/_instruments.py create mode 100644 satpy/tests/test_instruments.py diff --git a/satpy/_instruments.py b/satpy/_instruments.py new file mode 100644 index 0000000000..3c846c2869 --- /dev/null +++ b/satpy/_instruments.py @@ -0,0 +1,91 @@ +# Copyright (c) 2026 Satpy developers +# +# This file is part of satpy. +# +# satpy is free software: you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation, either version 3 of the License, or (at your option) any later +# version. +# +# satpy is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with +# satpy. If not, see . +"""Helpers for accessing and modifying instrument attributes.""" + +import logging +import warnings +from typing import Any + +import satpy + +logger = logging.getLogger(__name__) + +def get_instruments_from_attrs(attrs: dict[str,Any]) -> set[str]: + """Get instrument names from dataset attributes. + + String type attributes are converted to set. This can be + removed once all file handlers provide instruments as a + set. + """ + legacy = attrs.get("sensor", set()) + instruments = attrs.get("instruments", legacy) + if legacy: + warnings.warn( + "Satpy will ignore the 'sensor' attribute as of v1.1. " + "Use the 'instruments' attribute instead.", + DeprecationWarning, + stacklevel=2 + ) + if isinstance(instruments, str): + warnings.warn( + "Converting 'instruments' attribute from string to set. " + "This will result in an error in v1.1, when Satpy will require " + "set type instruments attributes.", + DeprecationWarning, + stacklevel=2 + ) + instruments = set([instruments]) + return instruments + + + +def normalize_instrument_name(instrument: str) -> str: + """Normalize instrument name for internal usage.""" + return instrument.replace("-", "").replace(" ", "_").replace("/", "-").lower() + + +def get_one_instrument_from_attrs(attrs: dict[str,Any]) -> str: + """Get a single instrument name from dataset attributes.""" + instruments = get_instruments_from_attrs(attrs) + if not instruments: + raise KeyError("No 'instruments' in dataset attribute") + if len(instruments) > 1: + logger.warning(f"More than one instrument in dataset attributes, will use the first value: {instruments}") + return list(instruments)[0] + + +def get_pyspectral_instrument_name(instrument: str) -> str: + """Get instrument name expected by pyspectral.""" + return normalize_instrument_name(instrument) + + +def serialize_instruments(instruments: set[str]) -> str: + """Serialize a set of instruments.""" + return "-".join( + instr.replace("-", "").replace(" ", "").replace("/", "").lower() + for instr in sorted(instruments) + ) + + +def set_instruments_attr(attrs: dict[str,Any], instruments: set[str]|str) -> None: + """Set 'instruments' dataset atrribute.""" + key = get_instruments_key() + attrs[key] = instruments + + +def get_instruments_key(): + """Get key for instruments in dataset attributes.""" + return satpy.config.get("instruments_key") diff --git a/satpy/composites/aux_data.py b/satpy/composites/aux_data.py index 406a451295..6b1724241f 100644 --- a/satpy/composites/aux_data.py +++ b/satpy/composites/aux_data.py @@ -22,8 +22,8 @@ import os import satpy +import satpy._instruments as instru from satpy.aux_download import DataDownloadMixin -from satpy.utils import set_instruments_attr from .core import GenericCompositor @@ -157,7 +157,7 @@ def __call__(self, *args, **kwargs): if self.area is None: raise AttributeError("Area definition needs to be configured") img.attrs["area"] = self.area - set_instruments_attr(img.attrs, set()) + instru.set_instruments_attr(img.attrs, set()) img.attrs["mode"] = "".join(img.bands.data) img.attrs.pop("modifiers", None) img.attrs.pop("calibration", None) diff --git a/satpy/composites/config_loader.py b/satpy/composites/config_loader.py index 96bdc61b72..e8a1ca192d 100644 --- a/satpy/composites/config_loader.py +++ b/satpy/composites/config_loader.py @@ -28,10 +28,11 @@ from yaml import UnsafeLoader import satpy +import satpy._instruments as instru from satpy import DataID, DataQuery from satpy._config import config_search_paths, get_entry_points_config_dirs, glob_config from satpy.dataset.dataid import minimal_default_keys_config -from satpy.utils import normalize_instrument_name, recursive_dict_update +from satpy.utils import recursive_dict_update logger = logging.getLogger(__name__) @@ -268,7 +269,7 @@ def load_compositor_configs_for_sensor(sensor_name: str) -> tuple[dict[str, dict DataID key -> key properties """ - config_filename = normalize_instrument_name(sensor_name) + ".yaml" + config_filename = instru.normalize_instrument_name(sensor_name) + ".yaml" logger.debug("Looking for composites config file %s", config_filename) paths = get_entry_points_config_dirs("satpy.composites") composite_configs = config_search_paths( diff --git a/satpy/composites/core.py b/satpy/composites/core.py index 85b31d1869..c44c289cd9 100644 --- a/satpy/composites/core.py +++ b/satpy/composites/core.py @@ -26,9 +26,10 @@ import numpy as np import xarray as xr +import satpy._instruments as instru from satpy.dataset import DataID, combine_metadata from satpy.dataset.dataid import minimal_default_keys_config -from satpy.utils import get_instruments_from_attrs, set_instruments_attr, unify_chunks +from satpy.utils import unify_chunks LOG = logging.getLogger(__name__) @@ -436,7 +437,7 @@ def _concat_datasets(self, projectables, mode): def _get_sensors(self, projectables) -> set[str]: sensors = set() for projectable in projectables: - sensors.update(get_instruments_from_attrs(projectable.attrs)) + sensors.update(instru.get_instruments_from_attrs(projectable.attrs)) return sensors def __call__( @@ -503,7 +504,7 @@ def _get_updated_attrs(self, datasets, attrs, mode): new_attrs.update(self.attrs) if resolution is not None: new_attrs["resolution"] = resolution - set_instruments_attr(new_attrs, self._get_sensors(datasets)) + instru.set_instruments_attr(new_attrs, self._get_sensors(datasets)) new_attrs["mode"] = mode return new_attrs diff --git a/satpy/composites/fill.py b/satpy/composites/fill.py index 994d6437f1..b10c80ab43 100644 --- a/satpy/composites/fill.py +++ b/satpy/composites/fill.py @@ -25,8 +25,8 @@ import numpy as np import xarray as xr +import satpy._instruments as instru from satpy.dataset import combine_metadata -from satpy.utils import get_instruments_from_attrs, set_instruments_attr from .core import ( GenericCompositor, @@ -407,10 +407,10 @@ def _combine_metadata_with_mode_and_sensor(self, # 'mode' is no longer valid after we've remove the 'A' # let the base class __call__ determine mode attrs.pop("mode", None) - if not get_instruments_from_attrs(attrs): + if not instru.get_instruments_from_attrs(attrs): # sensor can be a set instruments = self._get_sensors([foreground, background]) - set_instruments_attr(attrs, instruments) + instru.set_instruments_attr(attrs, instruments) return attrs @staticmethod diff --git a/satpy/composites/glm.py b/satpy/composites/glm.py index e5d1000269..6c5ff5f26f 100644 --- a/satpy/composites/glm.py +++ b/satpy/composites/glm.py @@ -21,8 +21,8 @@ import xarray as xr +import satpy._instruments as instru from satpy.enhancements.enhancer import get_enhanced_image -from satpy.utils import set_instruments_attr from .core import GenericCompositor @@ -97,7 +97,7 @@ def _update_attrs(self, new_data, background_layer, highlight_layer): new_data.attrs = background_layer.attrs.copy() new_data.attrs["units"] = 1 new_sensors = self._get_sensors((highlight_layer, background_layer)) - set_instruments_attr(new_data.attrs, new_sensors) + instru.set_instruments_attr(new_data.attrs, new_sensors) def __call__(self, projectables, optional_datasets=None, **attrs): """Create RGBA image with highlighted pixels.""" diff --git a/satpy/enhancements/enhancer.py b/satpy/enhancements/enhancer.py index b0680c9bfb..a26085c289 100644 --- a/satpy/enhancements/enhancer.py +++ b/satpy/enhancements/enhancer.py @@ -22,12 +22,11 @@ import yaml from yaml import UnsafeLoader +import satpy._instruments as instru from satpy._config import config_search_paths, get_entry_points_config_dirs from satpy.decision_tree import DecisionTree from satpy.utils import ( - get_instruments_from_attrs, get_logger, - normalize_instrument_name, recursive_dict_update, ) @@ -131,7 +130,7 @@ def get_sensor_enhancement_config(self, sensors: set[str]): """Get the sensor-specific config.""" paths = get_entry_points_config_dirs("satpy.enhancements") for sensor_name in sensors: - basename = normalize_instrument_name(sensor_name) + ".yaml" + basename = instru.normalize_instrument_name(sensor_name) + ".yaml" config_fn = os.path.join("enhancements", basename) config_files = config_search_paths(config_fn, search_dirs=paths) # Note: Enhancement configuration files can't overwrite individual @@ -211,7 +210,7 @@ def get_enhanced_image(dataset, enhance=None, overlay=None, decorate=None, if enhancer is None or enhancer.enhancement_tree is None: LOG.debug("No enhancement being applied to dataset") else: - sensors = get_instruments_from_attrs(dataset.attrs) + sensors = instru.get_instruments_from_attrs(dataset.attrs) if sensors: enhancer.add_sensor_enhancements(sensors) enhancer.apply(img, **dataset.attrs) diff --git a/satpy/modifiers/_crefl_utils.py b/satpy/modifiers/_crefl_utils.py index c0d3a1e5c0..5a9c3796a0 100644 --- a/satpy/modifiers/_crefl_utils.py +++ b/satpy/modifiers/_crefl_utils.py @@ -69,8 +69,8 @@ import numpy as np import xarray as xr +import satpy._instruments as instru from satpy.dataset.dataid import WavelengthRange -from satpy.utils import get_one_instrument_from_attrs, normalize_instrument_name LOG = logging.getLogger(__name__) @@ -283,7 +283,7 @@ def run_crefl(refl, :param avg_elevation: average elevation (usually pre-calculated and stored in CMGDEM.hdf) """ - sensor = get_one_instrument_from_attrs(refl.attrs) + sensor = instru.get_one_instrument_from_attrs(refl.attrs) runner_cls = _runner_class_for_sensor(sensor) runner = runner_cls(refl) corr_refl = runner(sensor_azimuth, sensor_zenith, solar_azimuth, solar_zenith, avg_elevation) @@ -350,7 +350,7 @@ def _run_crefl(self, mus, muv, phi, solar_zenith, sensor_zenith, height, coeffs) class _VIIRSMODISCREFLRunner(_CREFLRunner): def _run_crefl(self, mus, muv, phi, solar_zenith, sensor_zenith, height, coeffs): - instrument = get_one_instrument_from_attrs(self._refl.attrs) + instrument = instru.get_one_instrument_from_attrs(self._refl.attrs) return da.map_blocks(_run_crefl, self._refl.data, mus.data, muv.data, phi.data, height, instrument, *coeffs, meta=np.ndarray((), dtype=self._refl.dtype), @@ -387,7 +387,7 @@ def _run_crefl(self, mus, muv, phi, solar_zenith, sensor_zenith, height, coeffs) def _runner_class_for_sensor(sensor_name: str) -> Type[_CREFLRunner]: try: - return _SENSOR_TO_RUNNER[normalize_instrument_name(sensor_name)] + return _SENSOR_TO_RUNNER[instru.normalize_instrument_name(sensor_name)] except KeyError: raise NotImplementedError(f"Don't know how to apply CREFL to data from sensor {sensor_name}.") diff --git a/satpy/modifiers/atmosphere.py b/satpy/modifiers/atmosphere.py index dbb8870da7..de68dbd976 100644 --- a/satpy/modifiers/atmosphere.py +++ b/satpy/modifiers/atmosphere.py @@ -23,10 +23,10 @@ import numpy as np import xarray as xr +import satpy._instruments as instru from satpy.modifiers import ModifierBase from satpy.modifiers._crefl import ReflectanceCorrector # noqa from satpy.modifiers.angles import compute_relative_azimuth, get_angles, get_satellite_zenith_angle -from satpy.utils import get_one_instrument_from_attrs, get_pyspectral_instrument_name logger = logging.getLogger(__name__) @@ -105,8 +105,8 @@ def __call__(self, projectables, optional_datasets=None, **info): logger.info("Removing Rayleigh scattering with atmosphere '%s' and " "aerosol type '%s' for '%s'", atmosphere, aerosol_type, vis.attrs["name"]) - sensor = get_pyspectral_instrument_name( - get_one_instrument_from_attrs(vis.attrs) + sensor = instru.get_pyspectral_instrument_name( + instru.get_one_instrument_from_attrs(vis.attrs) ) corrector = Rayleigh(vis.attrs["platform_name"], sensor, atmosphere=atmosphere, @@ -162,8 +162,8 @@ def __call__(self, projectables, optional_datasets=None, **info): satz = satz.data # get dask array underneath logger.info("Correction for limb cooling") - sensor = get_pyspectral_instrument_name( - get_one_instrument_from_attrs(band.attrs) + sensor = instru.get_pyspectral_instrument_name( + instru.get_one_instrument_from_attrs(band.attrs) ) corrector = AtmosphericalCorrection(band.attrs["platform_name"], sensor) diff --git a/satpy/modifiers/spectral.py b/satpy/modifiers/spectral.py index 67562f19e9..3a37aa8579 100644 --- a/satpy/modifiers/spectral.py +++ b/satpy/modifiers/spectral.py @@ -21,8 +21,8 @@ import xarray as xr +import satpy._instruments as instru from satpy.modifiers import ModifierBase -from satpy.utils import get_one_instrument_from_attrs, get_pyspectral_instrument_name try: from pyspectral.near_infrared_reflectance import Calculator @@ -132,8 +132,8 @@ def _init_reflectance_calculator(self, metadata): if not Calculator: logger.info("Couldn't load pyspectral") raise ImportError("No module named pyspectral.near_infrared_reflectance") - sensor = get_pyspectral_instrument_name( - get_one_instrument_from_attrs(metadata) + sensor = instru.get_pyspectral_instrument_name( + instru.get_one_instrument_from_attrs(metadata) ) reflectance_3x_calculator = Calculator(metadata["platform_name"], sensor, metadata["name"], sunz_threshold=self.sun_zenith_threshold, diff --git a/satpy/scene.py b/satpy/scene.py index 30452ee989..f43757a4a4 100644 --- a/satpy/scene.py +++ b/satpy/scene.py @@ -29,6 +29,7 @@ from pyresample.geometry import AreaDefinition, BaseDefinition, CoordinateDefinition, SwathDefinition from xarray import DataArray +import satpy._instruments as instru from satpy.area import get_area_def from satpy.composites.config_loader import load_compositor_configs_for_sensors from satpy.composites.core import IncompatibleAreas @@ -38,7 +39,6 @@ from satpy.readers.core.loading import load_readers from satpy.utils import ( convert_remote_files_to_fsspec, - get_instruments_from_attrs, get_storage_options_from_reader_kwargs, ) @@ -201,7 +201,7 @@ def sensor_names(self) -> set[str]: def _contained_sensor_names(self) -> set[str]: sensor_names = set() for data_arr in self.values(): - sensor_names.update(get_instruments_from_attrs(data_arr.attrs)) + sensor_names.update(instru.get_instruments_from_attrs(data_arr.attrs)) return sensor_names @property diff --git a/satpy/tests/test_instruments.py b/satpy/tests/test_instruments.py new file mode 100644 index 0000000000..473aad6a9d --- /dev/null +++ b/satpy/tests/test_instruments.py @@ -0,0 +1,81 @@ +# Copyright (c) 2026 Satpy developers +# +# This file is part of satpy. +# +# satpy is free software: you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation, either version 3 of the License, or (at your option) any later +# version. +# +# satpy is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with +# satpy. If not, see . +"""Unit tests for instrument helpers.""" + +import pytest + +import satpy +import satpy._instruments as instru + + +@pytest.mark.parametrize( + ("attrs", "expected"), + [ + ({"instruments": {"myinstr"}}, {"myinstr"}), + ({}, set()), + ] +) +def test_get_instruments_from_attrs(attrs, expected): + """Test getting instruments from dataset attributes.""" + assert instru.get_instruments_from_attrs(attrs) == expected + +@pytest.mark.parametrize( + ("attrs", "expected"), + [ + ({"sensor": "myinstr"}, {"myinstr"}), + ({"sensor": {"myinstr"}}, {"myinstr"}), + ({"instruments": "myinstr"}, {"myinstr"}), + ] +) +def test_get_instruments_from_attrs_with_warning(attrs, expected): + """Test deprecation warnings when getting instruments.""" + with pytest.warns(DeprecationWarning, match="v1.1"): + assert instru.get_instruments_from_attrs(attrs) == expected + +def test_normalize_instrument_name(): + """Test instrument name normalization.""" + instr = "My Instrument-123/1" + expected = "my_instrument123-1" + assert instru.normalize_instrument_name(instr) == expected + +def test_serialize_instruments(): + """Test instrument set serialization.""" + instruments = {"My Instrument-123/1", "ABI"} + expected = "abi-myinstrument1231" + assert instru.serialize_instruments(instruments) == expected + +def test_set_instruments_attr(): + """Test setting instruments attribute.""" + attrs = {"instruments": {"myinstrument"}} + new_instruments = {"i1", "i2"} + with satpy.config.set(instruments_key="instruments"): + instru.set_instruments_attr(attrs, new_instruments) + assert attrs["instruments"] == new_instruments + +def test_get_one_instrument_from_attrs(): + """Test getting a single instrument from dataset attributes.""" + attrs = {"instruments": {"i1"}} + with satpy.config.set(instruments_key="instruments"): + assert instru.get_one_instrument_from_attrs(attrs) == "i1" + +def test_get_one_instrument_from_attrs_with_warning(caplog): + """Test warnings when getting a single instrument.""" + attrs = {"instruments": {"i1", "i2"}} + with satpy.config.set(instruments_key="instruments"): + instru.get_one_instrument_from_attrs(attrs) + assert "More than one" in caplog.text + with pytest.raises(KeyError): + instru.get_one_instrument_from_attrs({}) diff --git a/satpy/tests/test_utils.py b/satpy/tests/test_utils.py index 2c69479a56..fbb917b948 100644 --- a/satpy/tests/test_utils.py +++ b/satpy/tests/test_utils.py @@ -28,20 +28,14 @@ import pytest import xarray as xr -import satpy from satpy.utils import ( angle2xyz, datetime64_to_pydatetime, - get_instruments_from_attrs, get_legacy_chunk_size, - get_one_instrument_from_attrs, get_satpos, import_error_helper, lonlat2xyz, - normalize_instrument_name, proj_units_to_meters, - serialize_instruments, - set_instruments_attr, xyz2angle, xyz2lonlat, ) @@ -668,66 +662,3 @@ def test_flatten_dict(): "b_d_e": 1, "b_d_f_g": [1, 2]} assert flatten_dict(d) == expected - - -class TestInstrumentsUtils: - """Test instruments attribute utilities.""" - - @pytest.mark.parametrize( - ("attrs", "expected"), - [ - ({"instruments": {"myinstr"}}, {"myinstr"}), - ({}, set()), - ] - ) - def test_get_instruments_from_attrs(self, attrs, expected): - """Test getting instruments from dataset attributes.""" - assert get_instruments_from_attrs(attrs) == expected - - @pytest.mark.parametrize( - ("attrs", "expected"), - [ - ({"sensor": "myinstr"}, {"myinstr"}), - ({"sensor": {"myinstr"}}, {"myinstr"}), - ({"instruments": "myinstr"}, {"myinstr"}), - ] - ) - def test_get_instruments_from_attrs_with_warning(self, attrs, expected): - """Test deprecation warnings when getting instruments.""" - with pytest.warns(DeprecationWarning, match="v1.1"): - assert get_instruments_from_attrs(attrs) == expected - - def test_normalize_instrument_name(self): - """Test instrument name normalization.""" - instr = "My Instrument-123/1" - expected = "my_instrument123-1" - assert normalize_instrument_name(instr) == expected - - def test_serialize_instruments(self): - """Test instrument set serialization.""" - instruments = {"My Instrument-123/1", "ABI"} - expected = "abi-myinstrument1231" - assert serialize_instruments(instruments) == expected - - def test_set_instruments_attr(self): - """Test setting instruments attribute.""" - attrs = {"instruments": {"myinstrument"}} - new_instruments = {"i1", "i2"} - with satpy.config.set(instruments_key="instruments"): - set_instruments_attr(attrs, new_instruments) - assert attrs["instruments"] == new_instruments - - def test_get_one_instrument_from_attrs(self): - """Test getting a single instrument from dataset attributes.""" - attrs = {"instruments": {"i1"}} - with satpy.config.set(instruments_key="instruments"): - assert get_one_instrument_from_attrs(attrs) == "i1" - - def test_get_one_instrument_from_attrs_with_warning(self, caplog): - """Test warnings when getting a single instrument.""" - attrs = {"instruments": {"i1", "i2"}} - with satpy.config.set(instruments_key="instruments"): - get_one_instrument_from_attrs(attrs) - assert "More than one" in caplog.text - with pytest.raises(KeyError): - get_one_instrument_from_attrs({}) diff --git a/satpy/utils.py b/satpy/utils.py index f051d7d7fe..bdf9d77e4c 100644 --- a/satpy/utils.py +++ b/satpy/utils.py @@ -39,8 +39,6 @@ from numpy.typing import ArrayLike, DTypeLike from yaml import BaseLoader, UnsafeLoader -import satpy - _is_logging_on = False TRACE_LEVEL = 5 @@ -942,71 +940,3 @@ def flatten_dict(d, parent_key="", sep="_"): else: items.append((new_key, v)) return dict(items) - - -def get_instruments_from_attrs(attrs: dict[str,Any]) -> set[str]: - """Get instrument names from dataset attributes. - - String type attributes are converted to set. This can be - removed once all file handlers provide instruments as a - set. - """ - legacy = attrs.get("sensor", set()) - instruments = attrs.get("instruments", legacy) - if legacy: - warnings.warn( - "Satpy will ignore the 'sensor' attribute as of v1.1. " - "Use the 'instruments' attribute instead.", - DeprecationWarning, - stacklevel=2 - ) - if isinstance(instruments, str): - warnings.warn( - "Converting 'instruments' attribute from string to set. " - "This will result in an error in v1.1, when Satpy will require " - "set type instruments attributes.", - DeprecationWarning, - stacklevel=2 - ) - instruments = set([instruments]) - return instruments - - - -def normalize_instrument_name(instrument: str) -> str: - """Normalize instrument name for internal usage.""" - return instrument.replace("-", "").replace(" ", "_").replace("/", "-").lower() - - -def get_one_instrument_from_attrs(attrs: dict[str,Any]) -> str: - """Get a single instrument name from dataset attributes.""" - instruments = get_instruments_from_attrs(attrs) - if not instruments: - raise KeyError("No 'instruments' in dataset attribute") - if len(instruments) > 1: - logger.warning(f"More than one instrument in dataset attributes, will use the first value: {instruments}") - return list(instruments)[0] - - -def get_pyspectral_instrument_name(instrument: str) -> str: - """Get instrument name expected by pyspectral.""" - return normalize_instrument_name(instrument) - - -def serialize_instruments(instruments: set[str]) -> str: - """Serialize a set of instruments.""" - return "-".join( - instr.replace("-", "").replace(" ", "").replace("/", "").lower() - for instr in sorted(instruments) - ) - - -def set_instruments_attr(attrs: dict[str,Any], instruments: set[str]|str) -> None: - """Set 'instruments' dataset atrribute.""" - key = get_instruments_key() - attrs[key] = instruments - - -def get_instruments_key(): - """Get key for instruments in dataset attributes.""" - return satpy.config.get("instruments_key") diff --git a/satpy/writers/core/base.py b/satpy/writers/core/base.py index c2f5f69f3b..2582414475 100644 --- a/satpy/writers/core/base.py +++ b/satpy/writers/core/base.py @@ -22,9 +22,9 @@ import typing import warnings +import satpy._instruments as instru from satpy.aux_download import DataDownloadMixin from satpy.plugin_base import Plugin -from satpy.utils import get_instruments_from_attrs, serialize_instruments, set_instruments_attr from satpy.writers.core.compute import compute_writer_results, split_results if typing.TYPE_CHECKING: @@ -139,9 +139,9 @@ def create_filename_parser(self, base_dir): @staticmethod def _prepare_metadata_for_filename_formatting(attrs): with contextlib.suppress(KeyError): - instruments = get_instruments_from_attrs(attrs) - serialized = serialize_instruments(instruments) - set_instruments_attr(attrs, serialized) + instruments = instru.get_instruments_from_attrs(attrs) + serialized = instru.serialize_instruments(instruments) + instru.set_instruments_attr(attrs, serialized) def get_filename(self, **kwargs): """Create a filename where output data will be saved. diff --git a/satpy/writers/mitiff.py b/satpy/writers/mitiff.py index b8a38c955d..11803ee8e8 100644 --- a/satpy/writers/mitiff.py +++ b/satpy/writers/mitiff.py @@ -26,9 +26,9 @@ import numpy as np from PIL import Image, ImagePalette +import satpy._instruments as instru from satpy.dataset import DataID, DataQuery from satpy.enhancements.enhancer import get_enhanced_image -from satpy.utils import get_one_instrument_from_attrs from satpy.writers.core.image import ImageWriter if typing.TYPE_CHECKING: @@ -56,7 +56,7 @@ def _adjust_kwargs(dataset, kwargs): if "sensor" not in kwargs: # MITIFFs needing to handle sensor can only have one sensor # Assume the first value of set as the sensor. - kwargs["sensor"] = get_one_instrument_from_attrs(dataset.attrs) + kwargs["sensor"] = instru.get_one_instrument_from_attrs(dataset.attrs) class MITIFFWriter(ImageWriter): From bddff400ced80af7950f438d3d5d32a035537da8 Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Fri, 8 May 2026 09:08:48 +0000 Subject: [PATCH 22/72] Deprecate sensor attribute in enhancements --- doc/source/enhancements.rst | 8 +++++--- satpy/enhancements/enhancer.py | 18 +++++++++++++++++- satpy/etc/enhancements/abi.yaml | 24 ++++++++++++------------ satpy/etc/enhancements/ahi.yaml | 4 ++-- satpy/etc/enhancements/amsr2.yaml | 24 ++++++++++++------------ 5 files changed, 48 insertions(+), 30 deletions(-) diff --git a/doc/source/enhancements.rst b/doc/source/enhancements.rst index 87cdecb23f..cb9b7e8f60 100644 --- a/doc/source/enhancements.rst +++ b/doc/source/enhancements.rst @@ -20,8 +20,10 @@ as well. See :ref:`component_configuration` for more information. Enhancements can be defined in a ``generic.yaml`` file that is always loaded for all data or in an instrument-specific file (e.g. ``seviri.yaml``) -corresponding to the ``.attrs["sensor"]`` metadata of the ``DataArray`` being -processed. Generic enhancements are loaded first followed by sensor-specific +corresponding to the ``.attrs["instruments"]`` metadata of the ``DataArray`` +being processed. For the filename, instruments are normalized using +:meth:`satpy._instruments.normalize_instrument_name`. +Generic enhancements are loaded first followed by instrument-specific enhancement files. Enhancement YAML Format @@ -95,7 +97,7 @@ implementation depends on the following keys: 1. ``name`` 2. ``reader`` 3. ``platform_name`` -4. ``sensor`` +4. ``instruments`` 5. ``standard_name`` 6. ``units`` diff --git a/satpy/enhancements/enhancer.py b/satpy/enhancements/enhancer.py index a26085c289..f43778ddb1 100644 --- a/satpy/enhancements/enhancer.py +++ b/satpy/enhancements/enhancer.py @@ -17,6 +17,7 @@ from __future__ import annotations import os +import warnings from pathlib import Path import yaml @@ -43,14 +44,29 @@ def __init__(self, *decision_dicts, **kwargs): "reader", "platform_name", "sensor", + "instruments", "standard_name", "units", )) self.prefix = kwargs.pop("config_section", "enhancements") - multival_keys = kwargs.pop("multival_keys", ["sensor"]) + multival_keys = kwargs.pop("multival_keys", ["sensor", "instruments"]) + self._check_deprecated_keys(match_keys, multival_keys) super(EnhancementDecisionTree, self).__init__( decision_dicts, match_keys, multival_keys) + def _check_deprecated_keys(self, match_keys, multival_keys): + user_provided_sensor = any( + "sensor" in keys + for keys in [match_keys, multival_keys] + ) + if user_provided_sensor: + warnings.warn( + "The 'sensor' attribute will be removed in v1.1. " + "Use 'instrument' instead.", + DeprecationWarning, + stacklevel=2 + ) + def add_config_to_tree(self, *decision_dict: str | Path | dict) -> None: """Add configuration to tree.""" conf: dict = {} diff --git a/satpy/etc/enhancements/abi.yaml b/satpy/etc/enhancements/abi.yaml index 9920f842a3..1986d8ddeb 100644 --- a/satpy/etc/enhancements/abi.yaml +++ b/satpy/etc/enhancements/abi.yaml @@ -1,7 +1,7 @@ enhancements: cimss_true_color: standard_name: cimss_true_color - sensor: abi + instrument: abi operations: - name: linear_stretch method: !!python/name:satpy.enhancements.contrast.stretch @@ -35,7 +35,7 @@ enhancements: true_color_with_night_fires: standard_name: true_color_with_night_fires - sensor: abi + instrument: abi operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch @@ -77,7 +77,7 @@ enhancements: ash_abi: ## RGB Ash recipe source: http://rammb.cira.colostate.edu/training/visit/quick_guides/GOES_Ash_RGB.pdf standard_name: ash - sensor: abi + instrument: abi operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch @@ -89,7 +89,7 @@ enhancements: dust_abi: ## RGB Dust recipe source: http://rammb.cira.colostate.edu/training/visit/quick_guides/Dust_RGB_Quick_Guide.pdf standard_name: dust - sensor: abi + instrument: abi operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch @@ -105,7 +105,7 @@ enhancements: convection_abi: ## RGB Convection recipe source: http://rammb.cira.colostate.edu/training/visit/quick_guides/QuickGuide_GOESR_DayConvectionRGB_final.pdf standard_name: convection - sensor: abi + instrument: abi operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch @@ -142,7 +142,7 @@ enhancements: night_microphysics_abi: ## RGB Nighttime Microphysics recipe source: http://rammb.cira.colostate.edu/training/visit/quick_guides/QuickGuide_GOESR_NtMicroRGB_final.pdf standard_name: night_microphysics - sensor: abi + instrument: abi operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch @@ -154,7 +154,7 @@ enhancements: night_microphysics_tropical_abi: ## RGB Nighttime Microphysics recipe source: http://rammb.cira.colostate.edu/training/visit/quick_guides/QuickGuide_GOESR_NtMicroRGB_final.pdf standard_name: night_microphysics_tropical - sensor: abi + instrument: abi operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch @@ -170,7 +170,7 @@ enhancements: land_cloud_fire: ## RGB Day Land Cloud Fire recipe source: http://rammb.cira.colostate.edu/training/visit/quick_guides/QuickGuide_GOESR_DayLandCloudFireRGB_final.pdf standard_name: land_cloud_fire - sensor: abi + instrument: abi operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch @@ -179,7 +179,7 @@ enhancements: land_cloud: ## RGB Day Land Cloud Fire recipe source: http://rammb.cira.colostate.edu/training/visit/quick_guides/QuickGuide_GOESR_daylandcloudRGB_final.pdf standard_name: land_cloud - sensor: abi + instrument: abi operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch @@ -191,7 +191,7 @@ enhancements: # IR with white clouds highlighted_brightness_temperature: standard_name: highlighted_toa_brightness_temperature - sensor: abi + instrument: abi operations: - name: btemp_threshold method: !!python/name:satpy.enhancements.contrast.btemp_threshold @@ -308,7 +308,7 @@ enhancements: ## RGB Recipe: https://rammb2.cira.colostate.edu/wp-content/uploads/2024/11/GOES-BlowingSnowRGB1_QuickGuide_24April2024.pdf ## Modified to match recommendations from RGB Workshop 2025 standard_name: day_blowing_snow - sensor: abi + instrument: abi operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch @@ -323,7 +323,7 @@ enhancements: day_cloud_type: # Recipe PDF: http://cimss.ssec.wisc.edu/goes/OCLOFactSheetPDFs/ABIQuickGuide_Day_Cloud_Type_RGB.pdf standard_name: day_cloud_type - sensor: abi + instrument: abi operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch diff --git a/satpy/etc/enhancements/ahi.yaml b/satpy/etc/enhancements/ahi.yaml index 5496be3cd8..cadd3ba863 100644 --- a/satpy/etc/enhancements/ahi.yaml +++ b/satpy/etc/enhancements/ahi.yaml @@ -12,7 +12,7 @@ enhancements: day_severe_storms: standard_name: day_severe_storms - sensor: ahi + instrument: ahi operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch @@ -27,7 +27,7 @@ enhancements: night_microphysics_tropical: standard_name: night_microphysics_tropical - sensor: ahi + instrument: ahi operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch diff --git a/satpy/etc/enhancements/amsr2.yaml b/satpy/etc/enhancements/amsr2.yaml index eb56f944ec..a1c860a4b3 100644 --- a/satpy/etc/enhancements/amsr2.yaml +++ b/satpy/etc/enhancements/amsr2.yaml @@ -3,28 +3,28 @@ enhancements: # https://www.ospo.noaa.gov/Products/atmosphere/gpds/maps.html?GPRR#gpdsMaps gaasp_clw: name: CLW - sensor: amsr2 + instrument: amsr2 operations: - name: linear_stretch method: !!python/name:satpy.enhancements.contrast.stretch kwargs: {stretch: 'crude', min_stretch: 0.0, max_stretch: 0.5} gaasp_sst: name: SST - sensor: amsr2 + instrument: amsr2 operations: - name: linear_stretch method: !!python/name:satpy.enhancements.contrast.stretch kwargs: {stretch: 'crude', min_stretch: -5.0, max_stretch: 35} gaasp_tpw: name: TPW - sensor: amsr2 + instrument: amsr2 operations: - name: linear_stretch method: !!python/name:satpy.enhancements.contrast.stretch kwargs: {stretch: 'crude', min_stretch: 0.0, max_stretch: 75.0} gaasp_wspd: name: WSPD - sensor: amsr2 + instrument: amsr2 operations: - name: linear_stretch method: !!python/name:satpy.enhancements.contrast.stretch @@ -32,56 +32,56 @@ enhancements: # Snow_Cover unscaled (category product) gaasp_snow_depth: name: Snow_Depth - sensor: amsr2 + instrument: amsr2 operations: - name: linear_stretch method: !!python/name:satpy.enhancements.contrast.stretch kwargs: {stretch: 'crude', min_stretch: 0.0, max_stretch: 150.0} gaasp_swe: name: SWE - sensor: amsr2 + instrument: amsr2 operations: - name: linear_stretch method: !!python/name:satpy.enhancements.contrast.stretch kwargs: {stretch: 'crude', min_stretch: 0.0, max_stretch: 16.0} gaasp_soil_moisture: name: Soil_Moisture - sensor: amsr2 + instrument: amsr2 operations: - name: linear_stretch method: !!python/name:satpy.enhancements.contrast.stretch kwargs: {stretch: 'crude', min_stretch: 0.0, max_stretch: 100.0} gaasp_ice_concentration_nh: name: NASA_Team_2_Ice_Concentration_NH - sensor: amsr2 + instrument: amsr2 operations: - name: linear_stretch method: !!python/name:satpy.enhancements.contrast.stretch kwargs: {stretch: 'crude', min_stretch: 0.0, max_stretch: 100.0} gaasp_ice_concentration_sh: name: NASA_Team_2_Ice_Concentration_SH - sensor: amsr2 + instrument: amsr2 operations: - name: linear_stretch method: !!python/name:satpy.enhancements.contrast.stretch kwargs: {stretch: 'crude', min_stretch: 0.0, max_stretch: 100.0} # gaasp_latency_nh: # name: Latency_NH -# sensor: amsr2 +# instrument: amsr2 # operations: # - name: linear_stretch # method: !!python/name:satpy.enhancements.contrast.stretch # kwargs: {stretch: 'crude', min_stretch: 0.0, max_stretch: 100.0} # gaasp_latency_sh: # name: Latency_SH -# sensor: amsr2 +# instrument: amsr2 # operations: # - name: linear_stretch # method: !!python/name:satpy.enhancements.contrast.stretch # kwargs: {stretch: 'crude', min_stretch: 0.0, max_stretch: 100.0} gaasp_rain_rate: name: Rain_Rate - sensor: amsr2 + instrument: amsr2 operations: - name: linear_stretch method: !!python/name:satpy.enhancements.contrast.stretch From 9ca9b79b9d689e94481e568ada6f4f7fc09c8d27 Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Fri, 8 May 2026 11:38:33 +0000 Subject: [PATCH 23/72] Remove explicit sensor usage in dependency tree --- satpy/dependency_tree.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/satpy/dependency_tree.py b/satpy/dependency_tree.py index 97777a71e9..c250987802 100644 --- a/satpy/dependency_tree.py +++ b/satpy/dependency_tree.py @@ -23,6 +23,7 @@ import numpy as np +import satpy._instruments as instru from satpy import DataID, DatasetDict from satpy.dataset import ModifierTuple, create_filtered_query from satpy.dataset.data_dict import TooManyResults, get_key @@ -504,6 +505,7 @@ def get_compositor(self, key): def get_modifier(self, comp_id): """Get a modifer.""" # create a DataID for the compositor we are generating + instr_key = instru.get_instruments_key() modifier = comp_id["modifiers"][-1] for sensor_name in sorted(self.modifiers): modifiers = self.modifiers[sensor_name] @@ -514,7 +516,7 @@ def get_modifier(self, comp_id): mloader, moptions = modifiers[modifier] moptions = moptions.copy() moptions.update(comp_id.to_dict()) - moptions["sensor"] = sensor_name + moptions[instr_key] = sensor_name compositors[comp_id] = mloader(_satpy_id=comp_id, **moptions) return compositors[comp_id] From 1ffe5b0a1c7699bba84f69cd8c8433199bc8009d Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Mon, 11 May 2026 15:08:50 +0000 Subject: [PATCH 24/72] Ensure compatibility in enhancement decision tree --- satpy/enhancements/enhancer.py | 31 +++++++++++++++---------------- satpy/etc/enhancements/abi.yaml | 24 ++++++++++++------------ satpy/etc/enhancements/ahi.yaml | 4 ++-- satpy/etc/enhancements/amsr2.yaml | 24 ++++++++++++------------ 4 files changed, 41 insertions(+), 42 deletions(-) diff --git a/satpy/enhancements/enhancer.py b/satpy/enhancements/enhancer.py index f43778ddb1..7a76f70a19 100644 --- a/satpy/enhancements/enhancer.py +++ b/satpy/enhancements/enhancer.py @@ -43,36 +43,22 @@ def __init__(self, *decision_dicts, **kwargs): ("name", "reader", "platform_name", - "sensor", "instruments", "standard_name", "units", )) self.prefix = kwargs.pop("config_section", "enhancements") - multival_keys = kwargs.pop("multival_keys", ["sensor", "instruments"]) - self._check_deprecated_keys(match_keys, multival_keys) + multival_keys = kwargs.pop("multival_keys", ["instruments"]) super(EnhancementDecisionTree, self).__init__( decision_dicts, match_keys, multival_keys) - def _check_deprecated_keys(self, match_keys, multival_keys): - user_provided_sensor = any( - "sensor" in keys - for keys in [match_keys, multival_keys] - ) - if user_provided_sensor: - warnings.warn( - "The 'sensor' attribute will be removed in v1.1. " - "Use 'instrument' instead.", - DeprecationWarning, - stacklevel=2 - ) - def add_config_to_tree(self, *decision_dict: str | Path | dict) -> None: """Add configuration to tree.""" conf: dict = {} for config_file in decision_dict: config_dict = self._get_config_dict_from_user(config_file) recursive_dict_update(conf, config_dict) + self._ensure_compat(conf) self._build_tree(conf) def _get_config_dict_from_user(self, config_file: str | Path | dict) -> dict: @@ -103,6 +89,18 @@ def _get_yaml_enhancement_dict(self, config_file: str | Path) -> dict: LOG.debug(f"Adding enhancement configuration from file: {config_file}") return enhancement_section + def _ensure_compat(self, config_dict: dict) -> None: + for enh_name, enh_config in config_dict.items(): + if "sensor" in enh_config: + warnings.warn( + "Renaming the 'sensor' attribute from enhancement YAML file " + "to 'instruments'. This will raise an exception in Satpy " + "v1.1 when the 'sensor' attribute will be removed.", + DeprecationWarning, + stacklevel=3 + ) + instru.set_instruments_attr(config_dict[enh_name], enh_config["sensor"]) + def find_match(self, **query_dict): """Find a match.""" try: @@ -113,6 +111,7 @@ def find_match(self, **query_dict): (query_dict.get("uid", None),)) + class Enhancer: """Helper class to get enhancement information for images.""" diff --git a/satpy/etc/enhancements/abi.yaml b/satpy/etc/enhancements/abi.yaml index 1986d8ddeb..cc7d2af42e 100644 --- a/satpy/etc/enhancements/abi.yaml +++ b/satpy/etc/enhancements/abi.yaml @@ -1,7 +1,7 @@ enhancements: cimss_true_color: standard_name: cimss_true_color - instrument: abi + instruments: [abi] operations: - name: linear_stretch method: !!python/name:satpy.enhancements.contrast.stretch @@ -35,7 +35,7 @@ enhancements: true_color_with_night_fires: standard_name: true_color_with_night_fires - instrument: abi + sensor: [abi] operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch @@ -77,7 +77,7 @@ enhancements: ash_abi: ## RGB Ash recipe source: http://rammb.cira.colostate.edu/training/visit/quick_guides/GOES_Ash_RGB.pdf standard_name: ash - instrument: abi + instruments: [abi] operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch @@ -89,7 +89,7 @@ enhancements: dust_abi: ## RGB Dust recipe source: http://rammb.cira.colostate.edu/training/visit/quick_guides/Dust_RGB_Quick_Guide.pdf standard_name: dust - instrument: abi + instruments: [abi] operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch @@ -105,7 +105,7 @@ enhancements: convection_abi: ## RGB Convection recipe source: http://rammb.cira.colostate.edu/training/visit/quick_guides/QuickGuide_GOESR_DayConvectionRGB_final.pdf standard_name: convection - instrument: abi + instruments: [abi] operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch @@ -142,7 +142,7 @@ enhancements: night_microphysics_abi: ## RGB Nighttime Microphysics recipe source: http://rammb.cira.colostate.edu/training/visit/quick_guides/QuickGuide_GOESR_NtMicroRGB_final.pdf standard_name: night_microphysics - instrument: abi + instruments: [abi] operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch @@ -154,7 +154,7 @@ enhancements: night_microphysics_tropical_abi: ## RGB Nighttime Microphysics recipe source: http://rammb.cira.colostate.edu/training/visit/quick_guides/QuickGuide_GOESR_NtMicroRGB_final.pdf standard_name: night_microphysics_tropical - instrument: abi + instruments: [abi] operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch @@ -170,7 +170,7 @@ enhancements: land_cloud_fire: ## RGB Day Land Cloud Fire recipe source: http://rammb.cira.colostate.edu/training/visit/quick_guides/QuickGuide_GOESR_DayLandCloudFireRGB_final.pdf standard_name: land_cloud_fire - instrument: abi + instruments: [abi] operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch @@ -179,7 +179,7 @@ enhancements: land_cloud: ## RGB Day Land Cloud Fire recipe source: http://rammb.cira.colostate.edu/training/visit/quick_guides/QuickGuide_GOESR_daylandcloudRGB_final.pdf standard_name: land_cloud - instrument: abi + instruments: [abi] operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch @@ -191,7 +191,7 @@ enhancements: # IR with white clouds highlighted_brightness_temperature: standard_name: highlighted_toa_brightness_temperature - instrument: abi + instruments: [abi] operations: - name: btemp_threshold method: !!python/name:satpy.enhancements.contrast.btemp_threshold @@ -308,7 +308,7 @@ enhancements: ## RGB Recipe: https://rammb2.cira.colostate.edu/wp-content/uploads/2024/11/GOES-BlowingSnowRGB1_QuickGuide_24April2024.pdf ## Modified to match recommendations from RGB Workshop 2025 standard_name: day_blowing_snow - instrument: abi + instruments: [abi] operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch @@ -323,7 +323,7 @@ enhancements: day_cloud_type: # Recipe PDF: http://cimss.ssec.wisc.edu/goes/OCLOFactSheetPDFs/ABIQuickGuide_Day_Cloud_Type_RGB.pdf standard_name: day_cloud_type - instrument: abi + instruments: [abi] operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch diff --git a/satpy/etc/enhancements/ahi.yaml b/satpy/etc/enhancements/ahi.yaml index cadd3ba863..3f73b9c1a4 100644 --- a/satpy/etc/enhancements/ahi.yaml +++ b/satpy/etc/enhancements/ahi.yaml @@ -12,7 +12,7 @@ enhancements: day_severe_storms: standard_name: day_severe_storms - instrument: ahi + instruments: [ahi] operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch @@ -27,7 +27,7 @@ enhancements: night_microphysics_tropical: standard_name: night_microphysics_tropical - instrument: ahi + instruments: [ahi] operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch diff --git a/satpy/etc/enhancements/amsr2.yaml b/satpy/etc/enhancements/amsr2.yaml index a1c860a4b3..788b52cc2c 100644 --- a/satpy/etc/enhancements/amsr2.yaml +++ b/satpy/etc/enhancements/amsr2.yaml @@ -3,28 +3,28 @@ enhancements: # https://www.ospo.noaa.gov/Products/atmosphere/gpds/maps.html?GPRR#gpdsMaps gaasp_clw: name: CLW - instrument: amsr2 + instruments: [amsr2] operations: - name: linear_stretch method: !!python/name:satpy.enhancements.contrast.stretch kwargs: {stretch: 'crude', min_stretch: 0.0, max_stretch: 0.5} gaasp_sst: name: SST - instrument: amsr2 + instruments: [amsr2] operations: - name: linear_stretch method: !!python/name:satpy.enhancements.contrast.stretch kwargs: {stretch: 'crude', min_stretch: -5.0, max_stretch: 35} gaasp_tpw: name: TPW - instrument: amsr2 + instruments: [amsr2] operations: - name: linear_stretch method: !!python/name:satpy.enhancements.contrast.stretch kwargs: {stretch: 'crude', min_stretch: 0.0, max_stretch: 75.0} gaasp_wspd: name: WSPD - instrument: amsr2 + instruments: [amsr2] operations: - name: linear_stretch method: !!python/name:satpy.enhancements.contrast.stretch @@ -32,56 +32,56 @@ enhancements: # Snow_Cover unscaled (category product) gaasp_snow_depth: name: Snow_Depth - instrument: amsr2 + instruments: [amsr2] operations: - name: linear_stretch method: !!python/name:satpy.enhancements.contrast.stretch kwargs: {stretch: 'crude', min_stretch: 0.0, max_stretch: 150.0} gaasp_swe: name: SWE - instrument: amsr2 + instruments: [amsr2] operations: - name: linear_stretch method: !!python/name:satpy.enhancements.contrast.stretch kwargs: {stretch: 'crude', min_stretch: 0.0, max_stretch: 16.0} gaasp_soil_moisture: name: Soil_Moisture - instrument: amsr2 + instruments: [amsr2] operations: - name: linear_stretch method: !!python/name:satpy.enhancements.contrast.stretch kwargs: {stretch: 'crude', min_stretch: 0.0, max_stretch: 100.0} gaasp_ice_concentration_nh: name: NASA_Team_2_Ice_Concentration_NH - instrument: amsr2 + instruments: [amsr2] operations: - name: linear_stretch method: !!python/name:satpy.enhancements.contrast.stretch kwargs: {stretch: 'crude', min_stretch: 0.0, max_stretch: 100.0} gaasp_ice_concentration_sh: name: NASA_Team_2_Ice_Concentration_SH - instrument: amsr2 + instruments: [amsr2] operations: - name: linear_stretch method: !!python/name:satpy.enhancements.contrast.stretch kwargs: {stretch: 'crude', min_stretch: 0.0, max_stretch: 100.0} # gaasp_latency_nh: # name: Latency_NH -# instrument: amsr2 +# instruments: [amsr2] # operations: # - name: linear_stretch # method: !!python/name:satpy.enhancements.contrast.stretch # kwargs: {stretch: 'crude', min_stretch: 0.0, max_stretch: 100.0} # gaasp_latency_sh: # name: Latency_SH -# instrument: amsr2 +# instruments: [amsr2] # operations: # - name: linear_stretch # method: !!python/name:satpy.enhancements.contrast.stretch # kwargs: {stretch: 'crude', min_stretch: 0.0, max_stretch: 100.0} gaasp_rain_rate: name: Rain_Rate - instrument: amsr2 + instruments: [amsr2] operations: - name: linear_stretch method: !!python/name:satpy.enhancements.contrast.stretch From 184d538ef388c52137119844090be1becfa783b1 Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper <1991007+sfinkens@users.noreply.github.com> Date: Mon, 11 May 2026 17:11:26 +0200 Subject: [PATCH 25/72] Update doc/source/enhancements.rst Co-authored-by: David Hoese --- doc/source/enhancements.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/enhancements.rst b/doc/source/enhancements.rst index cb9b7e8f60..2892b0ef73 100644 --- a/doc/source/enhancements.rst +++ b/doc/source/enhancements.rst @@ -97,7 +97,7 @@ implementation depends on the following keys: 1. ``name`` 2. ``reader`` 3. ``platform_name`` -4. ``instruments`` +4. ``instruments`` (previously ``sensor`` in Satpy <1.0) 5. ``standard_name`` 6. ``units`` From f4bcd2fa55f679962b0726395e872f58277f0ce8 Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Mon, 11 May 2026 15:18:03 +0000 Subject: [PATCH 26/72] Improve deprecation warning message --- satpy/enhancements/enhancer.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/satpy/enhancements/enhancer.py b/satpy/enhancements/enhancer.py index 7a76f70a19..5ff97445c0 100644 --- a/satpy/enhancements/enhancer.py +++ b/satpy/enhancements/enhancer.py @@ -93,13 +93,16 @@ def _ensure_compat(self, config_dict: dict) -> None: for enh_name, enh_config in config_dict.items(): if "sensor" in enh_config: warnings.warn( - "Renaming the 'sensor' attribute from enhancement YAML file " - "to 'instruments'. This will raise an exception in Satpy " - "v1.1 when the 'sensor' attribute will be removed.", + "Renaming the 'sensor' enhancement attribute to 'instruments'. " + "This will raise an exception in Satpy v1.1 when the 'sensor' " + "attribute will be removed. To silence this warning, change " + "'sensor' to 'instruments' (type list) in your enhancement " + "YAML file.", DeprecationWarning, stacklevel=3 ) - instru.set_instruments_attr(config_dict[enh_name], enh_config["sensor"]) + instruments = instru.get_instruments_from_attrs(enh_config) + instru.set_instruments_attr(config_dict[enh_name], instruments) def find_match(self, **query_dict): """Find a match.""" From 91b753bece2f767fe4efc8c2503a92cc833b8a1b Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Mon, 11 May 2026 15:57:24 +0000 Subject: [PATCH 27/72] Keep single instruments as string instead of set --- satpy/enhancements/enhancer.py | 10 +++---- satpy/etc/enhancements/abi.yaml | 24 +++++++-------- satpy/etc/enhancements/ahi.yaml | 4 +-- satpy/etc/enhancements/amsr2.yaml | 26 ++++++++-------- .../tests/enhancement_tests/test_enhancer.py | 30 +++++++++---------- 5 files changed, 47 insertions(+), 47 deletions(-) diff --git a/satpy/enhancements/enhancer.py b/satpy/enhancements/enhancer.py index 5ff97445c0..52c6a7eb9e 100644 --- a/satpy/enhancements/enhancer.py +++ b/satpy/enhancements/enhancer.py @@ -95,14 +95,14 @@ def _ensure_compat(self, config_dict: dict) -> None: warnings.warn( "Renaming the 'sensor' enhancement attribute to 'instruments'. " "This will raise an exception in Satpy v1.1 when the 'sensor' " - "attribute will be removed. To silence this warning, change " - "'sensor' to 'instruments' (type list) in your enhancement " - "YAML file.", + "attribute will be removed. To silence this warning, rename " + "'sensor' to 'instruments' in your enhancement YAML file.", DeprecationWarning, stacklevel=3 ) - instruments = instru.get_instruments_from_attrs(enh_config) - instru.set_instruments_attr(config_dict[enh_name], instruments) + # Not using set_instruments_attr() here to keep the order. + instr_key = instru.get_instruments_key() + config_dict[enh_name][instr_key] = enh_config["sensor"] def find_match(self, **query_dict): """Find a match.""" diff --git a/satpy/etc/enhancements/abi.yaml b/satpy/etc/enhancements/abi.yaml index cc7d2af42e..7952c30a83 100644 --- a/satpy/etc/enhancements/abi.yaml +++ b/satpy/etc/enhancements/abi.yaml @@ -1,7 +1,7 @@ enhancements: cimss_true_color: standard_name: cimss_true_color - instruments: [abi] + instruments: abi operations: - name: linear_stretch method: !!python/name:satpy.enhancements.contrast.stretch @@ -35,7 +35,7 @@ enhancements: true_color_with_night_fires: standard_name: true_color_with_night_fires - sensor: [abi] + instruments: abi operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch @@ -77,7 +77,7 @@ enhancements: ash_abi: ## RGB Ash recipe source: http://rammb.cira.colostate.edu/training/visit/quick_guides/GOES_Ash_RGB.pdf standard_name: ash - instruments: [abi] + instruments: abi operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch @@ -89,7 +89,7 @@ enhancements: dust_abi: ## RGB Dust recipe source: http://rammb.cira.colostate.edu/training/visit/quick_guides/Dust_RGB_Quick_Guide.pdf standard_name: dust - instruments: [abi] + instruments: abi operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch @@ -105,7 +105,7 @@ enhancements: convection_abi: ## RGB Convection recipe source: http://rammb.cira.colostate.edu/training/visit/quick_guides/QuickGuide_GOESR_DayConvectionRGB_final.pdf standard_name: convection - instruments: [abi] + instruments: abi operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch @@ -142,7 +142,7 @@ enhancements: night_microphysics_abi: ## RGB Nighttime Microphysics recipe source: http://rammb.cira.colostate.edu/training/visit/quick_guides/QuickGuide_GOESR_NtMicroRGB_final.pdf standard_name: night_microphysics - instruments: [abi] + instruments: abi operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch @@ -154,7 +154,7 @@ enhancements: night_microphysics_tropical_abi: ## RGB Nighttime Microphysics recipe source: http://rammb.cira.colostate.edu/training/visit/quick_guides/QuickGuide_GOESR_NtMicroRGB_final.pdf standard_name: night_microphysics_tropical - instruments: [abi] + instruments: abi operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch @@ -170,7 +170,7 @@ enhancements: land_cloud_fire: ## RGB Day Land Cloud Fire recipe source: http://rammb.cira.colostate.edu/training/visit/quick_guides/QuickGuide_GOESR_DayLandCloudFireRGB_final.pdf standard_name: land_cloud_fire - instruments: [abi] + instruments: abi operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch @@ -179,7 +179,7 @@ enhancements: land_cloud: ## RGB Day Land Cloud Fire recipe source: http://rammb.cira.colostate.edu/training/visit/quick_guides/QuickGuide_GOESR_daylandcloudRGB_final.pdf standard_name: land_cloud - instruments: [abi] + instruments: abi operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch @@ -191,7 +191,7 @@ enhancements: # IR with white clouds highlighted_brightness_temperature: standard_name: highlighted_toa_brightness_temperature - instruments: [abi] + instruments: abi operations: - name: btemp_threshold method: !!python/name:satpy.enhancements.contrast.btemp_threshold @@ -308,7 +308,7 @@ enhancements: ## RGB Recipe: https://rammb2.cira.colostate.edu/wp-content/uploads/2024/11/GOES-BlowingSnowRGB1_QuickGuide_24April2024.pdf ## Modified to match recommendations from RGB Workshop 2025 standard_name: day_blowing_snow - instruments: [abi] + instruments: abi operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch @@ -323,7 +323,7 @@ enhancements: day_cloud_type: # Recipe PDF: http://cimss.ssec.wisc.edu/goes/OCLOFactSheetPDFs/ABIQuickGuide_Day_Cloud_Type_RGB.pdf standard_name: day_cloud_type - instruments: [abi] + instruments: abi operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch diff --git a/satpy/etc/enhancements/ahi.yaml b/satpy/etc/enhancements/ahi.yaml index 3f73b9c1a4..ce97bca324 100644 --- a/satpy/etc/enhancements/ahi.yaml +++ b/satpy/etc/enhancements/ahi.yaml @@ -12,7 +12,7 @@ enhancements: day_severe_storms: standard_name: day_severe_storms - instruments: [ahi] + instruments: ahi operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch @@ -27,7 +27,7 @@ enhancements: night_microphysics_tropical: standard_name: night_microphysics_tropical - instruments: [ahi] + instruments: ahi operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch diff --git a/satpy/etc/enhancements/amsr2.yaml b/satpy/etc/enhancements/amsr2.yaml index 788b52cc2c..023b71420e 100644 --- a/satpy/etc/enhancements/amsr2.yaml +++ b/satpy/etc/enhancements/amsr2.yaml @@ -1,30 +1,30 @@ -enhancements: +abiamsr2enhancements: # GAASP enhancements based on PNGs at: # https://www.ospo.noaa.gov/Products/atmosphere/gpds/maps.html?GPRR#gpdsMaps gaasp_clw: name: CLW - instruments: [amsr2] + instruments: amsr2 operations: - name: linear_stretch method: !!python/name:satpy.enhancements.contrast.stretch kwargs: {stretch: 'crude', min_stretch: 0.0, max_stretch: 0.5} gaasp_sst: name: SST - instruments: [amsr2] + instruments: amsr2 operations: - name: linear_stretch method: !!python/name:satpy.enhancements.contrast.stretch kwargs: {stretch: 'crude', min_stretch: -5.0, max_stretch: 35} gaasp_tpw: name: TPW - instruments: [amsr2] + instruments: amsr2 operations: - name: linear_stretch method: !!python/name:satpy.enhancements.contrast.stretch kwargs: {stretch: 'crude', min_stretch: 0.0, max_stretch: 75.0} gaasp_wspd: name: WSPD - instruments: [amsr2] + instruments: amsr2 operations: - name: linear_stretch method: !!python/name:satpy.enhancements.contrast.stretch @@ -32,56 +32,56 @@ enhancements: # Snow_Cover unscaled (category product) gaasp_snow_depth: name: Snow_Depth - instruments: [amsr2] + instruments: amsr2 operations: - name: linear_stretch method: !!python/name:satpy.enhancements.contrast.stretch kwargs: {stretch: 'crude', min_stretch: 0.0, max_stretch: 150.0} gaasp_swe: name: SWE - instruments: [amsr2] + instruments: amsr2 operations: - name: linear_stretch method: !!python/name:satpy.enhancements.contrast.stretch kwargs: {stretch: 'crude', min_stretch: 0.0, max_stretch: 16.0} gaasp_soil_moisture: name: Soil_Moisture - instruments: [amsr2] + instruments: amsr2 operations: - name: linear_stretch method: !!python/name:satpy.enhancements.contrast.stretch kwargs: {stretch: 'crude', min_stretch: 0.0, max_stretch: 100.0} gaasp_ice_concentration_nh: name: NASA_Team_2_Ice_Concentration_NH - instruments: [amsr2] + instruments: amsr2 operations: - name: linear_stretch method: !!python/name:satpy.enhancements.contrast.stretch kwargs: {stretch: 'crude', min_stretch: 0.0, max_stretch: 100.0} gaasp_ice_concentration_sh: name: NASA_Team_2_Ice_Concentration_SH - instruments: [amsr2] + instruments: amsr2 operations: - name: linear_stretch method: !!python/name:satpy.enhancements.contrast.stretch kwargs: {stretch: 'crude', min_stretch: 0.0, max_stretch: 100.0} # gaasp_latency_nh: # name: Latency_NH -# instruments: [amsr2] +# instruments: amsr2 # operations: # - name: linear_stretch # method: !!python/name:satpy.enhancements.contrast.stretch # kwargs: {stretch: 'crude', min_stretch: 0.0, max_stretch: 100.0} # gaasp_latency_sh: # name: Latency_SH -# instruments: [amsr2] +# instruments: amsr2 # operations: # - name: linear_stretch # method: !!python/name:satpy.enhancements.contrast.stretch # kwargs: {stretch: 'crude', min_stretch: 0.0, max_stretch: 100.0} gaasp_rain_rate: name: Rain_Rate - instruments: [amsr2] + instruments: amsr2 operations: - name: linear_stretch method: !!python/name:satpy.enhancements.contrast.stretch diff --git a/satpy/tests/enhancement_tests/test_enhancer.py b/satpy/tests/enhancement_tests/test_enhancer.py index c175f11a9b..bcb1504058 100644 --- a/satpy/tests/enhancement_tests/test_enhancer.py +++ b/satpy/tests/enhancement_tests/test_enhancer.py @@ -144,7 +144,7 @@ class TestComplexSensorEnhancerConfigs(_BaseCustomEnhancementConfigTests): enhancements: test1_sensor1_specific: name: test1 - sensor: test_sensor1 + instruments: test_sensor1 operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch @@ -160,14 +160,14 @@ class TestComplexSensorEnhancerConfigs(_BaseCustomEnhancementConfigTests): kwargs: {stretch: crude, min_stretch: 0, max_stretch: 100} test1_sensor2_specific: name: test1 - sensor: test_sensor2 + instruments: test_sensor2 operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch kwargs: {stretch: crude, min_stretch: 0, max_stretch: 50} exact_multisensor_comp: name: my_comp - sensor: [test_sensor1, test_sensor2] + instruments: [test_sensor1, test_sensor2] operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch @@ -183,7 +183,7 @@ def test_multisensor_choice(self, test_configs_path): ds = DataArray(np.arange(1, 11.).reshape((2, 5)), attrs={ "name": "test1", - "sensor": {"test_sensor2", "test_sensor1"}, + "instruments": {"test_sensor2", "test_sensor1"}, "mode": "L" }, dims=["y", "x"]) @@ -206,7 +206,7 @@ def test_multisensor_exact(self, test_configs_path): ds = DataArray(np.arange(1, 11.).reshape((2, 5)), attrs={ "name": "my_comp", - "sensor": {"test_sensor2", "test_sensor1"}, + "instruments": {"test_sensor2", "test_sensor1"}, "mode": "L" }, dims=["y", "x"]) @@ -227,7 +227,7 @@ def test_enhance_bad_query_value(self): from satpy.enhancements.enhancer import Enhancer, get_enhanced_image ds = DataArray(np.arange(1, 11.).reshape((2, 5)), - attrs=dict(name=["I", "am", "invalid"], sensor="test_sensor2", mode="L"), + attrs=dict(name=["I", "am", "invalid"], instruments={"test_sensor2"}, mode="L"), dims=["y", "x"]) e = Enhancer() assert e.enhancement_tree is not None @@ -282,7 +282,7 @@ def test_enhance_empty_config(self, test_configs_path): from satpy.enhancements.enhancer import Enhancer, get_enhanced_image ds = DataArray(np.arange(1, 11.).reshape((2, 5)), - attrs=dict(sensor="test_empty", mode="L"), + attrs=dict(instruments={"test_empty"}, mode="L"), dims=["y", "x"]) e = Enhancer() assert e.enhancement_tree is not None @@ -296,7 +296,7 @@ def test_enhance_with_sensor_no_entry(self, test_configs_path): from satpy.enhancements.enhancer import Enhancer, get_enhanced_image ds = DataArray(np.arange(1, 11.).reshape((2, 5)), - attrs=dict(sensor="test_sensor2", mode="L"), + attrs=dict(instruments={"test_sensor2"}, mode="L"), dims=["y", "x"]) e = Enhancer() assert e.enhancement_tree is not None @@ -311,7 +311,7 @@ def test_no_enhance(self): from satpy.enhancements.enhancer import get_enhanced_image ds = DataArray(np.arange(1, 11.).reshape((2, 5)), - attrs=dict(name="test1", sensor="test_sensor", mode="L"), + attrs=dict(name="test1", instruments={"test_sensor"}, mode="L"), dims=["y", "x"]) img = get_enhanced_image(ds, enhance=False) np.testing.assert_allclose(img.data.data.compute().squeeze(), ds.data) @@ -320,7 +320,7 @@ def test_writer_no_enhance(self): """Test turning off enhancements with writer.""" from xarray import DataArray ds = DataArray(np.arange(1, 11.).reshape((2, 5)), - attrs=dict(name="test1", sensor="test_sensor", mode="L"), + attrs=dict(name="test1", instruments={"test_sensor"}, mode="L"), dims=["y", "x"]) writer = _CustomImageWriter(enhance=False) writer.save_datasets((ds,), compute=False) @@ -333,7 +333,7 @@ def test_writer_custom_enhance(self): from satpy.enhancements.enhancer import Enhancer ds = DataArray(np.arange(1, 11.).reshape((2, 5)), - attrs=dict(name="test1", sensor="test_sensor", mode="L"), + attrs=dict(name="test1", instruments={"test_sensor"}, mode="L"), dims=["y", "x"]) enhance = Enhancer() writer = _CustomImageWriter(enhance=enhance) @@ -347,7 +347,7 @@ def test_enhance_with_sensor_entry(self, test_configs_path): from satpy.enhancements.enhancer import Enhancer, get_enhanced_image ds = DataArray(np.arange(1, 11.).reshape((2, 5)), - attrs=dict(name="test1", sensor="test_sensor", mode="L"), + attrs=dict(name="test1", instruments={"test_sensor"}, mode="L"), dims=["y", "x"]) e = Enhancer() assert e.enhancement_tree is not None @@ -359,7 +359,7 @@ def test_enhance_with_sensor_entry(self, test_configs_path): 1.) ds = DataArray(da.arange(1, 11., chunks=5).reshape((2, 5)), - attrs=dict(name="test1", sensor="test_sensor", mode="L"), + attrs=dict(name="test1", instruments={"test_sensor"}, mode="L"), dims=["y", "x"]) e = Enhancer() assert e.enhancement_tree is not None @@ -376,7 +376,7 @@ def test_enhance_with_sensor_entry2(self, test_configs_path): from satpy.enhancements.enhancer import Enhancer, get_enhanced_image ds = DataArray(np.arange(1, 11.).reshape((2, 5)), attrs=dict(name="test1", units="kelvin", - sensor="test_sensor", mode="L"), + instruments={"test_sensor"}, mode="L"), dims=["y", "x"]) e = Enhancer() assert e.enhancement_tree is not None @@ -432,7 +432,7 @@ def _get_test_data_array(self): ds = DataArray(np.arange(1, 11.).reshape((2, 5)), attrs={ "name": "test1", - "sensor": "test_sensor1", + "instruments": {"test_sensor1"}, "mode": "L", }, dims=["y", "x"]) From 2f69a1e82345b452e9565af437d7db689f1e1d89 Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Mon, 11 May 2026 16:37:27 +0000 Subject: [PATCH 28/72] Use instrument attribute setter --- satpy/enhancements/enhancer.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/satpy/enhancements/enhancer.py b/satpy/enhancements/enhancer.py index 52c6a7eb9e..5f46c9ba84 100644 --- a/satpy/enhancements/enhancer.py +++ b/satpy/enhancements/enhancer.py @@ -100,9 +100,7 @@ def _ensure_compat(self, config_dict: dict) -> None: DeprecationWarning, stacklevel=3 ) - # Not using set_instruments_attr() here to keep the order. - instr_key = instru.get_instruments_key() - config_dict[enh_name][instr_key] = enh_config["sensor"] + instru.set_instruments_attr(config_dict[enh_name], enh_config["sensor"]) def find_match(self, **query_dict): """Find a match.""" From 287a8ab95f32c67ecdedec3537c2fa7ea77b4a34 Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Mon, 11 May 2026 16:45:11 +0000 Subject: [PATCH 29/72] Fix typo --- satpy/etc/enhancements/amsr2.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/satpy/etc/enhancements/amsr2.yaml b/satpy/etc/enhancements/amsr2.yaml index 023b71420e..4a0ac05412 100644 --- a/satpy/etc/enhancements/amsr2.yaml +++ b/satpy/etc/enhancements/amsr2.yaml @@ -1,4 +1,4 @@ -abiamsr2enhancements: +enhancements: # GAASP enhancements based on PNGs at: # https://www.ospo.noaa.gov/Products/atmosphere/gpds/maps.html?GPRR#gpdsMaps gaasp_clw: From 806169814b176c5cd34d061ac1da658b2d8f4863 Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Mon, 11 May 2026 16:46:02 +0000 Subject: [PATCH 30/72] Fix whitespace --- satpy/enhancements/enhancer.py | 1 - 1 file changed, 1 deletion(-) diff --git a/satpy/enhancements/enhancer.py b/satpy/enhancements/enhancer.py index 5f46c9ba84..ac6f1cde13 100644 --- a/satpy/enhancements/enhancer.py +++ b/satpy/enhancements/enhancer.py @@ -112,7 +112,6 @@ def find_match(self, **query_dict): (query_dict.get("uid", None),)) - class Enhancer: """Helper class to get enhancement information for images.""" From cc6bcbd218115f3a5d842ad9d20f54e2fbf08a84 Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Mon, 11 May 2026 10:35:27 +0000 Subject: [PATCH 31/72] Replace sensor with instruments in file handlers --- satpy/_config.py | 2 +- satpy/etc/readers/mtsat2-imager_hrit.yaml | 12 ++--- satpy/etc/readers/mviri_l1b_fiduceo_nc.yaml | 10 ++-- satpy/readers/aapp_l1b.py | 4 +- satpy/readers/abi_l1b.py | 2 +- satpy/readers/abi_l2_nc.py | 2 +- satpy/readers/acspo.py | 10 ++-- satpy/readers/agri_l1.py | 2 +- satpy/readers/ahi_l2_nc.py | 4 +- satpy/readers/ami_l1b.py | 4 +- satpy/readers/amsr2_l1b.py | 2 +- satpy/readers/amsr2_l2_gaasp.py | 4 +- satpy/readers/atms_l1b_nc.py | 2 +- satpy/readers/avhrr_l1b_gaclac.py | 8 +-- satpy/readers/clavrx.py | 14 +++--- satpy/readers/core/abi.py | 2 +- satpy/readers/core/eum.py | 5 +- satpy/readers/core/hdfeos.py | 2 +- satpy/readers/core/landsat.py | 20 ++++---- satpy/readers/core/li_nc.py | 4 +- satpy/readers/core/vii_nc.py | 2 +- satpy/readers/core/viirs_atms_sdr.py | 4 +- satpy/readers/electrol_hrit.py | 2 +- satpy/readers/epic_l1b_h5.py | 4 +- satpy/readers/eps_l1b.py | 4 +- satpy/readers/eum_l2_bufr.py | 4 +- satpy/readers/eum_l2_grib.py | 5 +- satpy/readers/fci_l2_nc.py | 4 +- satpy/readers/geocat.py | 16 +++--- satpy/readers/ghi_l1.py | 2 +- satpy/readers/ghrsst_l2.py | 6 ++- satpy/readers/ghrsst_l3c_sst.py | 6 +-- satpy/readers/glm_l2.py | 4 +- satpy/readers/gms/gms5_vissr_l1b.py | 5 +- satpy/readers/goes_imager_hrit.py | 34 ++----------- satpy/readers/goes_imager_nc.py | 6 +-- satpy/readers/grib.py | 6 +-- satpy/readers/hrit_jma.py | 11 +++-- satpy/readers/iasi_l2.py | 8 +-- satpy/readers/iasi_l2_so2_bufr.py | 2 +- satpy/readers/iasi_ng_l2_nc.py | 4 +- satpy/readers/ici_l1b_nc.py | 2 +- satpy/readers/insat3d_img_l1b_h5.py | 2 +- satpy/readers/mersi_l1b.py | 43 ++++++++-------- satpy/readers/mimic_TPW2_nc.py | 2 +- satpy/readers/mirs.py | 54 ++++++++++++--------- satpy/readers/msu_gsa_l1b.py | 4 +- satpy/readers/mviri_l1b_fiduceo_nc.py | 2 +- satpy/readers/mwr_l1b.py | 8 +-- satpy/readers/mwr_l1c.py | 7 +-- satpy/readers/mws_l1b.py | 3 +- satpy/readers/nucaps.py | 9 ++-- satpy/readers/nwcsaf_nc.py | 51 +++++++++---------- satpy/readers/oceancolorcci_l3_nc.py | 2 +- satpy/readers/olci_nc.py | 10 ++-- satpy/readers/omps_edr.py | 4 +- satpy/readers/osisaf_l3_nc.py | 2 +- satpy/readers/satpy_cf_nc.py | 2 +- satpy/readers/scmi.py | 6 +-- satpy/readers/seadas_l2.py | 18 +++---- satpy/readers/seviri_l1b_hrit.py | 2 +- satpy/readers/seviri_l1b_icare.py | 8 +-- satpy/readers/seviri_l1b_native.py | 2 +- satpy/readers/seviri_l1b_nc.py | 2 +- satpy/readers/sgli_l1b.py | 2 +- satpy/readers/slstr_l1b.py | 4 +- satpy/readers/smos_l2_wind.py | 2 +- satpy/readers/tropomi_l2.py | 6 +-- satpy/readers/viirs_compact.py | 2 +- satpy/readers/viirs_edr.py | 4 +- satpy/readers/viirs_edr_active_fires.py | 6 +-- satpy/readers/viirs_edr_flood.py | 6 +-- satpy/readers/viirs_l1b.py | 4 +- satpy/readers/viirs_l2.py | 4 +- satpy/readers/virr_l1b.py | 2 +- 75 files changed, 264 insertions(+), 277 deletions(-) diff --git a/satpy/_config.py b/satpy/_config.py index 0b2e6a57c0..96457c8498 100644 --- a/satpy/_config.py +++ b/satpy/_config.py @@ -54,7 +54,7 @@ "readers": { "clip_negative_radiances": False, }, - "instruments_key": "sensor" + "instruments_key": "instruments" } # Satpy main configuration object diff --git a/satpy/etc/readers/mtsat2-imager_hrit.yaml b/satpy/etc/readers/mtsat2-imager_hrit.yaml index 6158f92ece..f2998fcbb2 100644 --- a/satpy/etc/readers/mtsat2-imager_hrit.yaml +++ b/satpy/etc/readers/mtsat2-imager_hrit.yaml @@ -13,7 +13,7 @@ reader: status: Beta supports_fsspec: false - sensors: [mtsat2_imager] + sensors: [IMAGER (MTSAT-2)] reader: !!python/name:satpy.readers.core.yaml_reader.GEOSegmentYAMLReader file_types: @@ -86,7 +86,7 @@ file_types: datasets: VIS: name: VIS - sensor: mtsat2_imager + instruments: [IMAGER (MTSAT-2)] wavelength: [0.55, 0.675, 0.80] resolution: 1000 calibration: @@ -100,7 +100,7 @@ datasets: IR1: name: IR1 - sensor: mtsat2_imager + instruments: [IMAGER (MTSAT-2)] wavelength: [10.3, 10.8, 11.3] resolution: 4000 calibration: @@ -114,7 +114,7 @@ datasets: IR2: name: IR2 - sensor: mtsat2_imager + instruments: [IMAGER (MTSAT-2)] wavelength: [11.5, 12.0, 12.5] resolution: 4000 calibration: @@ -128,7 +128,7 @@ datasets: IR3: name: IR3 - sensor: mtsat2_imager + instruments: [IMAGER (MTSAT-2)] wavelength: [6.5, 6.75, 7.0] resolution: 4000 calibration: @@ -142,7 +142,7 @@ datasets: IR4: name: IR4 - sensor: mtsat2_imager + instruments: [IMAGER (MTSAT-2)] wavelength: [3.5, 3.75, 4.0] resolution: 4000 calibration: diff --git a/satpy/etc/readers/mviri_l1b_fiduceo_nc.yaml b/satpy/etc/readers/mviri_l1b_fiduceo_nc.yaml index a9d753ecb9..9f371684b9 100644 --- a/satpy/etc/readers/mviri_l1b_fiduceo_nc.yaml +++ b/satpy/etc/readers/mviri_l1b_fiduceo_nc.yaml @@ -12,24 +12,24 @@ reader: For documentation see: http://doi.org/10.15770/EUM_SEC_CLM_0009 . status: Beta supports_fsspec: false - sensors: [mviri] + instruments: [MVIRI] reader: !!python/name:satpy.readers.core.yaml_reader.GEOFlippableFileYAMLReader file_types: nc_easy: file_reader: !!python/name:satpy.readers.mviri_l1b_fiduceo_nc.FiduceoMviriEasyFcdrFileHandler file_patterns: [ - 'FIDUCEO_FCDR_{level}_{sensor}_{platform}-{projection_longitude:f}_{start_time:%Y%m%d%H%M}_{end_time:%Y%m%d%H%M}_EASY_{processor_version}_{format_version}.nc', + 'FIDUCEO_FCDR_{level}_{instrument}_{platform}-{projection_longitude:f}_{start_time:%Y%m%d%H%M}_{end_time:%Y%m%d%H%M}_EASY_{processor_version}_{format_version}.nc', # Example: FIDUCEO_FCDR_L15_MVIRI_MET7-57.0_201701201000_201701201030_EASY_v2.6_fv3.1.nc - '{sensor}_FCDR-EASY_{level}_{platform}-E{projection_longitude:s}_{start_time:%Y%m%d%H%M}_{end_time:%Y%m%d%H%M}_{release}.nc' + '{instrument}_FCDR-EASY_{level}_{platform}-E{projection_longitude:s}_{start_time:%Y%m%d%H%M}_{end_time:%Y%m%d%H%M}_{release}.nc' # Example: MVIRI_FCDR-EASY_L15_MET7-E0000_200607060600_200607060630_0200.nc ] nc_full: file_reader: !!python/name:satpy.readers.mviri_l1b_fiduceo_nc.FiduceoMviriFullFcdrFileHandler file_patterns: [ - 'FIDUCEO_FCDR_{level}_{sensor}_{platform}-{projection_longitude:f}_{start_time:%Y%m%d%H%M}_{end_time:%Y%m%d%H%M}_FULL_{processor_version}_{format_version}.nc', + 'FIDUCEO_FCDR_{level}_{instrument}_{platform}-{projection_longitude:f}_{start_time:%Y%m%d%H%M}_{end_time:%Y%m%d%H%M}_FULL_{processor_version}_{format_version}.nc', # Example: FIDUCEO_FCDR_L15_MVIRI_MET7-57.0_201701201000_201701201030_FULL_v2.6_fv3.1.nc - '{sensor}_FCDR-FULL_{level}_{platform}-E{projection_longitude:s}_{start_time:%Y%m%d%H%M}_{end_time:%Y%m%d%H%M}_{release}.nc' + '{instrument}_FCDR-FULL_{level}_{platform}-E{projection_longitude:s}_{start_time:%Y%m%d%H%M}_{end_time:%Y%m%d%H%M}_{release}.nc' # Example: MVIRI_FCDR-FULL_L15_MET7-E0000_200607060600_200607060630_0200.nc ] diff --git a/satpy/readers/aapp_l1b.py b/satpy/readers/aapp_l1b.py index cf47155271..96b5b940c8 100644 --- a/satpy/readers/aapp_l1b.py +++ b/satpy/readers/aapp_l1b.py @@ -117,7 +117,7 @@ def end_time(self): def _update_dataset_attributes(self, dataset, key, info): dataset.attrs.update({"platform_name": self.platform_name, - "sensor": self.sensor}) + "instruments": {self.sensor}}) dataset.attrs.update(key.to_dict()) for meta_key in ("standard_name", "units"): if meta_key in info: @@ -183,7 +183,7 @@ def __init__(self, filename, filename_info, filetype_info): self.active_channels = self._get_active_channels() self._get_platform_name(AVHRR_PLATFORM_IDS2NAMES) - self.sensor = "avhrr-3" + self.sensor = "AVHRR/3" self._get_all_interpolated_angles = functools.lru_cache(maxsize=10)( self._get_all_interpolated_angles_uncached diff --git a/satpy/readers/abi_l1b.py b/satpy/readers/abi_l1b.py index b144529ebe..5930dd59f8 100644 --- a/satpy/readers/abi_l1b.py +++ b/satpy/readers/abi_l1b.py @@ -73,7 +73,7 @@ def get_dataset(self, key, info): def _adjust_attrs(self, data, key): data.attrs.update({"platform_name": self.platform_name, - "sensor": {self.sensor}}) + "instruments": {self.sensor}}) # Add orbital parameters projection = self.nc["goes_imager_projection"] data.attrs["orbital_parameters"] = { diff --git a/satpy/readers/abi_l2_nc.py b/satpy/readers/abi_l2_nc.py index 572d39de3e..faebb5ea90 100644 --- a/satpy/readers/abi_l2_nc.py +++ b/satpy/readers/abi_l2_nc.py @@ -74,7 +74,7 @@ def _update_data_arr_with_filename_attrs(self, variable): _units = variable.attrs["units"] if "units" in variable.attrs else None variable.attrs.update({ "platform_name": self.platform_name, - "sensor": self.sensor, + "instruments": {self.sensor}, "units": _units, "orbital_parameters": { "satellite_nominal_latitude": float(self.nc["nominal_satellite_subpoint_lat"]), diff --git a/satpy/readers/acspo.py b/satpy/readers/acspo.py index dc2fe07dca..4ccb35114f 100644 --- a/satpy/readers/acspo.py +++ b/satpy/readers/acspo.py @@ -48,9 +48,9 @@ ROWS_PER_SCAN = { - "modis": 10, - "viirs": 16, - "avhrr": None, + "MODIS": 10, + "VIIRS": 16, + "AVHRR": None, } @@ -81,7 +81,7 @@ def sensor_name(self): res = self["/attr/sensor"] if isinstance(res, np.ndarray): res = str(res.astype(str)) - return res.lower() + return res def get_shape(self, ds_id, ds_info): """Get numpy array shape for the specified dataset. @@ -134,7 +134,7 @@ def get_metadata(self, dataset_id, ds_info): "shape": shape, "units": units, "platform_name": self.platform_name, - "sensor": self.sensor_name, + "instruments": {self.sensor_name}, "standard_name": standard_name, "resolution": resolution, "rows_per_scan": rows_per_scan, diff --git a/satpy/readers/agri_l1.py b/satpy/readers/agri_l1.py index 3fdd7d21e0..69fa126cda 100644 --- a/satpy/readers/agri_l1.py +++ b/satpy/readers/agri_l1.py @@ -61,7 +61,7 @@ def adjust_attrs(self, data, ds_info): """Adjust the attrs of the data.""" satname = self.PLATFORM_NAMES.get(self["/attr/Satellite Name"], self["/attr/Satellite Name"]) data.attrs.update({"platform_name": satname, - "sensor": self["/attr/Sensor Identification Code"].lower(), + "instruments": {self["/attr/Sensor Identification Code"]}, "orbital_parameters": { "satellite_nominal_latitude": self["/attr/NOMCenterLat"].item(), "satellite_nominal_longitude": self["/attr/NOMCenterLon"].item(), diff --git a/satpy/readers/ahi_l2_nc.py b/satpy/readers/ahi_l2_nc.py index bdf2b9e2b6..645a377a8b 100644 --- a/satpy/readers/ahi_l2_nc.py +++ b/satpy/readers/ahi_l2_nc.py @@ -73,7 +73,7 @@ def __init__(self, filename, filename_info, filetype_info): if self.nc.attrs["cdm_data_type"] != EXPECTED_DATA_AREA: raise ValueError("File is not a full disk scene") - self.sensor = self.nc.attrs["instrument_name"].lower() + self.sensor = self.nc.attrs["instrument_name"] self.nlines = self.nc.sizes["Columns"] self.ncols = self.nc.sizes["Rows"] self.platform_name = self.nc.attrs["satellite_name"] @@ -104,7 +104,7 @@ def get_dataset(self, key, info): variable = variable.drop_vars("Latitude") variable = variable.drop_vars("Longitude") - variable.attrs["sensor"] = self.sensor + variable.attrs["instruments"] = {self.sensor} variable.attrs["platform_name"] = self.platform_name variable.attrs["platform_shortname"] = self.platform_shortname variable.attrs["start_time"] = self.start_time diff --git a/satpy/readers/ami_l1b.py b/satpy/readers/ami_l1b.py index a67ef79261..073c7c3cd3 100644 --- a/satpy/readers/ami_l1b.py +++ b/satpy/readers/ami_l1b.py @@ -105,7 +105,7 @@ def __init__(self, filename, filename_info, filetype_info, platform_shortname = self.nc.attrs["satellite_name"] self.platform_name = PLATFORM_NAMES.get(platform_shortname) - self.sensor = "ami" + self.sensor = "AMI" self.band_name = filetype_info["file_type"].upper() self.allow_conditional_pixels = allow_conditional_pixels calib_mode_choices = ("FILE", "PYSPECTRAL", "GSICS") @@ -233,7 +233,7 @@ def get_dataset(self, dataset_id, ds_info): attrs.update(dataset_id.to_dict()) attrs["orbital_parameters"] = self.get_orbital_parameters() attrs["platform_name"] = self.platform_name - attrs["sensor"] = self.sensor + attrs["instruments"] = {self.sensor} data.attrs = attrs return data diff --git a/satpy/readers/amsr2_l1b.py b/satpy/readers/amsr2_l1b.py index 64f00e20e1..a359cb43c4 100644 --- a/satpy/readers/amsr2_l1b.py +++ b/satpy/readers/amsr2_l1b.py @@ -32,7 +32,7 @@ def get_metadata(self, ds_id, ds_info): "shape": self.get_shape(ds_id, ds_info), "units": self[var_path + "/attr/UNIT"], "platform_name": self["/attr/PlatformShortName"], - "sensor": self["/attr/SensorShortName"].lower(), + "instruments": {self["/attr/SensorShortName"]}, "start_orbit": int(self["/attr/StartOrbitNumber"]), "end_orbit": int(self["/attr/StopOrbitNumber"]), }) diff --git a/satpy/readers/amsr2_l2_gaasp.py b/satpy/readers/amsr2_l2_gaasp.py index d2eb335730..e903997794 100644 --- a/satpy/readers/amsr2_l2_gaasp.py +++ b/satpy/readers/amsr2_l2_gaasp.py @@ -130,7 +130,7 @@ def end_time(self): @property def sensor_names(self): """Sensors who have data in this file.""" - return {self.nc.attrs["instrument_name"].lower()} + return {self.nc.attrs["instrument_name"]} @property def platform_name(self): @@ -192,7 +192,7 @@ def get_dataset(self, dataid, ds_info): attrs.update({ "platform_name": self.platform_name, - "sensor": sorted(self.sensor_names)[0], + "instruments": self.sensor_names, "start_time": self.start_time, "end_time": self.end_time, }) diff --git a/satpy/readers/atms_l1b_nc.py b/satpy/readers/atms_l1b_nc.py index dea9da1395..472cfcb7e7 100644 --- a/satpy/readers/atms_l1b_nc.py +++ b/satpy/readers/atms_l1b_nc.py @@ -75,7 +75,7 @@ def attrs(self): "start_time": self.start_time, "end_time": self.end_time, "platform_name": self.platform_name, - "sensor": self.sensor, + "instruments": {self.sensor}, } @staticmethod diff --git a/satpy/readers/avhrr_l1b_gaclac.py b/satpy/readers/avhrr_l1b_gaclac.py index bf50032559..e2de709b7b 100644 --- a/satpy/readers/avhrr_l1b_gaclac.py +++ b/satpy/readers/avhrr_l1b_gaclac.py @@ -109,21 +109,21 @@ def __init__(self, filename, filename_info, filetype_info, # noqa: D417 else: self.reader_class = LACKLMReader self.chn_dict = AVHRR3_CHANNEL_NAMES - self.sensor = "avhrr-3" + self.sensor = "AVHRR/3" elif self._is_avhrr2(): if filename_info.get("transfer_mode") == "GHRR": self.reader_class = GACPODReader else: self.reader_class = LACPODReader self.chn_dict = AVHRR2_CHANNEL_NAMES - self.sensor = "avhrr-2" + self.sensor = "AVHRR/2" else: if filename_info.get("transfer_mode") == "GHRR": self.reader_class = GACPODReader else: self.reader_class = LACPODReader self.chn_dict = AVHRR_CHANNEL_NAMES - self.sensor = "avhrr" + self.sensor = "AVHRR" self.filename_info = filename_info def _is_avhrr2(self): @@ -318,7 +318,7 @@ def _update_attrs(self, res): res.attrs[attr] = self.reader.meta_data[attr] res.attrs["platform_name"] = self.reader.spacecraft_name res.attrs["orbit_number"] = self.filename_info.get("orbit_number", None) - res.attrs["sensor"] = self.sensor + res.attrs["instruments"] = {self.sensor} try: res.attrs["orbital_parameters"] = {"tle": self.reader.get_tle_lines()} except (IndexError, RuntimeError): diff --git a/satpy/readers/clavrx.py b/satpy/readers/clavrx.py index 258af2ab4f..a215ae2044 100644 --- a/satpy/readers/clavrx.py +++ b/satpy/readers/clavrx.py @@ -42,12 +42,12 @@ } SENSORS = { - "MODIS": "modis", - "VIIRS": "viirs", - "AVHRR": "avhrr", - "AHI": "ahi", - "ABI": "abi", - "GOES-RU-IMAGER": "abi", + "MODIS": "MODIS", + "VIIRS": "VIIRS", + "AVHRR": "AVHRR", + "AHI": "AHI", + "ABI": "ABI", + "GOES-RU-IMAGER": "ABI", } PLATFORMS = { "SNPP": "npp", @@ -301,7 +301,7 @@ def get_metadata(sensor: str, platform: str, attrs: dict, ds_info: dict) -> dict attr_info["units"] = CF_UNITS[u] if u.lower() == "none": attr_info["units"] = "1" - attr_info["sensor"] = sensor + attr_info["instruments"] = {sensor} attr_info["platform_name"] = platform rps = _get_rows_per_scan(sensor) if rps: diff --git a/satpy/readers/core/abi.py b/satpy/readers/core/abi.py index e50cd616c4..d51764dc38 100644 --- a/satpy/readers/core/abi.py +++ b/satpy/readers/core/abi.py @@ -111,7 +111,7 @@ def _rename_dims(nc): @property def sensor(self): """Get sensor name for current file handler.""" - return "abi" + return "ABI" def __getitem__(self, item): """Wrap `self.nc[item]` for better floating point precision. diff --git a/satpy/readers/core/eum.py b/satpy/readers/core/eum.py index fe4579301d..ef60f950dd 100644 --- a/satpy/readers/core/eum.py +++ b/satpy/readers/core/eum.py @@ -26,7 +26,10 @@ time_cds = time_cds_short + [("Microseconds", ">u2")] time_cds_expanded = time_cds + [("Nanoseconds", ">u2")] issue_revision = [("Issue", np.uint16), ("Revision", np.uint16)] - +WMO_INSTRUMENT_NAMES = { + "fci": "FCI", + "seviri": "SEVIRI" +} def timecds2datetime(tcds): """Convert time_cds-variables to datetime-object. diff --git a/satpy/readers/core/hdfeos.py b/satpy/readers/core/hdfeos.py index 991ac3fd7d..2f452d5eb1 100644 --- a/satpy/readers/core/hdfeos.py +++ b/satpy/readers/core/hdfeos.py @@ -262,7 +262,7 @@ def _add_satpy_metadata(self, data_id: DataID, data_arr: xr.DataArray): """Add metadata that is specific to Satpy.""" new_attrs = { "platform_name": self.metadata_platform_name, - "sensor": "modis", + "instruments": {"MODIS"}, } res = data_id["resolution"] diff --git a/satpy/readers/core/landsat.py b/satpy/readers/core/landsat.py index 796a890e74..ceef47e20e 100644 --- a/satpy/readers/core/landsat.py +++ b/satpy/readers/core/landsat.py @@ -179,7 +179,7 @@ def _check_channel_and_reader(self, name): raise ValueError(f"Requested channel {name} does not match the reader channel {self.channel}") def _check_oli_tirs_spectral(self, name): - if self.sensor != "OLI_TIRS": + if self.sensor != {"OLI", "TIRS"}: return if name not in self.spectral_bands: return @@ -188,7 +188,7 @@ def _check_oli_tirs_spectral(self, name): raise ValueError(f"Requested channel {name} is not available in this granule") def _check_oli_tirs_thermal(self, name): - if self.sensor != "OLI_TIRS": + if self.sensor != {"OLI", "TIRS"}: return if name not in self.thermal_bands: return @@ -212,7 +212,7 @@ def _collect_attrs(self, data, name, info): attrs["perc_cloud_cover"] = self._mda.cloud_cover # Add platform / sensor attributes attrs["platform_name"] = self.platform_name - attrs["sensor"] = self.sensor + attrs["instruments"] = self.sensor # Apply attrs from YAML if "standard_name" in info: attrs["standard_name"] = info["standard_name"] @@ -310,7 +310,7 @@ def thermal_bands(self): @property def sensor(self): """Sensor name.""" - return "OLI_TIRS" + return {"OLI", "TIRS"} class OLITIRSL2CHReader(BaseLandsatL2Reader): @@ -329,7 +329,7 @@ def thermal_bands(self): @property def sensor(self): """Sensor name.""" - return "OLI_TIRS" + return {"OLI", "TIRS"} class ETMCHReader(BaseLandsatL1Reader): @@ -348,7 +348,7 @@ def thermal_bands(self): @property def sensor(self): """Sensor name.""" - return "ETM+" + return {"ETM+"} class ETML2CHReader(BaseLandsatL2Reader): @@ -367,7 +367,7 @@ def thermal_bands(self): @property def sensor(self): """Sensor name.""" - return "ETM+" + return {"ETM+"} class TMCHReader(BaseLandsatL1Reader): @@ -386,7 +386,7 @@ def thermal_bands(self): @property def sensor(self): """Sensor name.""" - return "TM" + return {"TM"} class TML2CHReader(BaseLandsatL2Reader): @@ -405,7 +405,7 @@ def thermal_bands(self): @property def sensor(self): """Sensor name.""" - return "TM" + return {"TM"} class MSSCHReader(BaseLandsatL1Reader): @@ -427,7 +427,7 @@ def thermal_bands(self): @property def sensor(self): """Sensor name.""" - return "MSS" + return {"MSS"} def available_datasets(self, configured_datasets=None): """Set up wavelength to B4 band.""" diff --git a/satpy/readers/core/li_nc.py b/satpy/readers/core/li_nc.py index bc694d7828..ba59f464b5 100644 --- a/satpy/readers/core/li_nc.py +++ b/satpy/readers/core/li_nc.py @@ -218,7 +218,7 @@ def __init__(self, filename, filename_info, filetype_info, cache_handle=True): self.processing_level = filetype_info.get("processing_level", "L0") # This class will only provide support for the LI sensor: - self.sensors = {"li"} + self.sensors = {"LI"} # Set of dataset names explicitly provided by this file handler: # This set is required to filter the retrieval of datasets later in the @@ -520,7 +520,7 @@ def register_dataset(self, var_name, oc_name=None): ds_info = { "name": ds_name, "variable_name": var_name, - "sensor": "li", + "instruments": self.sensor_names, "platform_name": platform_name_translate[platform], "file_type": self.filetype_info["file_type"] } diff --git a/satpy/readers/core/vii_nc.py b/satpy/readers/core/vii_nc.py index 92aaa1c014..a1b2acb546 100644 --- a/satpy/readers/core/vii_nc.py +++ b/satpy/readers/core/vii_nc.py @@ -195,7 +195,7 @@ def _get_global_attributes(self): "end_time": self.end_time, "spacecraft_name": self.spacecraft_name, "ssp_lon": self.ssp_lon, - "sensor": self.sensor, + "instruments": {self.sensor}, "filename_start_time": self.filename_info["sensing_start_time"], "filename_end_time": self.filename_info["sensing_end_time"], "platform_name": self.spacecraft_name, diff --git a/satpy/readers/core/viirs_atms_sdr.py b/satpy/readers/core/viirs_atms_sdr.py index fdc8fbd12d..b1627db7ca 100644 --- a/satpy/readers/core/viirs_atms_sdr.py +++ b/satpy/readers/core/viirs_atms_sdr.py @@ -163,7 +163,7 @@ def sensor_name(self): default = "Data_Products/{dataset_group}/attr/Instrument_Short_Name" sensor_path = self.filetype_info.get( "sensor_name", default).format(dataset_group=dataset_group) - return self[sensor_path].lower() + return self[sensor_path] def scale_swath_data(self, data, scaling_factors, dataset_group): """Scale swath data using scaling factors and offsets. @@ -276,7 +276,7 @@ def _update_data_attributes(self, data, dataset_id, ds_info): i.update(ds_info) i.update({ "platform_name": self.platform_name, - "sensor": self.sensor_name, + "instruments": {self.sensor_name}, "start_orbit": self.start_orbit_number, "end_orbit": self.end_orbit_number, "units": output_units, diff --git a/satpy/readers/electrol_hrit.py b/satpy/readers/electrol_hrit.py index 0d46067db4..479a18ff67 100644 --- a/satpy/readers/electrol_hrit.py +++ b/satpy/readers/electrol_hrit.py @@ -286,7 +286,7 @@ def get_dataset(self, key, info): res.attrs["standard_name"] = info["standard_name"] res.attrs["wavelength"] = info["wavelength"] res.attrs["platform_name"] = self.platform_name - res.attrs["sensor"] = "msu-gs" + res.attrs["instruments"] = {"MSU-GS"} res.attrs["orbital_parameters"] = { "satellite_nominal_longitude": self.mda["orbital_parameters"]["satellite_nominal_longitude"], "satellite_nominal_latitude": 0., diff --git a/satpy/readers/epic_l1b_h5.py b/satpy/readers/epic_l1b_h5.py index 59f67bde30..e90bf69591 100644 --- a/satpy/readers/epic_l1b_h5.py +++ b/satpy/readers/epic_l1b_h5.py @@ -69,7 +69,7 @@ def __init__(self, filename, filename_info, filetype_info): """Init filehandler.""" super(DscovrEpicL1BH5FileHandler, self).__init__(filename, filename_info, filetype_info) - self.sensor = "epic" + self.sensor = "EPIC" self.platform_name = "DSCOVR" @property @@ -111,6 +111,6 @@ def get_dataset(self, dataset_id, ds_info): def _update_metadata(self, band): band = band.rename({band.dims[0]: "x", band.dims[1]: "y"}) - band.attrs.update({"platform_name": self.platform_name, "sensor": self.sensor}) + band.attrs.update({"platform_name": self.platform_name, "instruments": {self.sensor}}) return band diff --git a/satpy/readers/eps_l1b.py b/satpy/readers/eps_l1b.py index be437eff92..ae60bb6eb3 100644 --- a/satpy/readers/eps_l1b.py +++ b/satpy/readers/eps_l1b.py @@ -141,7 +141,7 @@ class EPSAVHRRFile(BaseFileHandler): "M02": "Metop-A", "M03": "Metop-C", } - sensors = {"AVHR": "avhrr-3"} + sensors = {"AVHR": "AVHRR/3"} units = {"reflectance": "%", "brightness_temperature": "K", @@ -289,7 +289,7 @@ def get_dataset(self, key, info): return dataset.attrs["platform_name"] = self.platform_name - dataset.attrs["sensor"] = self.sensor_name + dataset.attrs["instruments"] = {self.sensor_name} if "calibration" in key: dataset.attrs["units"] = self.units[key["calibration"]] dataset.attrs.update(info) diff --git a/satpy/readers/eum_l2_bufr.py b/satpy/readers/eum_l2_bufr.py index 95f378378a..92918a9aa8 100644 --- a/satpy/readers/eum_l2_bufr.py +++ b/satpy/readers/eum_l2_bufr.py @@ -34,7 +34,7 @@ from satpy.area import get_area_def from satpy.readers.core._geos_area import get_geos_area_naming -from satpy.readers.core.eum import get_service_mode, recarray2dict +from satpy.readers.core.eum import WMO_INSTRUMENT_NAMES, get_service_mode, recarray2dict from satpy.readers.core.file_handlers import BaseFileHandler from satpy.readers.core.seviri import mpef_product_header from satpy.utils import get_legacy_chunk_size @@ -314,7 +314,7 @@ def _construct_area_def(self, dataset_id): def _add_attributes(self, xarr, dataset_info): """Add dataset attributes to xarray.""" - xarr.attrs["sensor"] = self.sensor_name.upper() + xarr.attrs["instruments"] = {WMO_INSTRUMENT_NAMES[self.sensor_name]} xarr.attrs["platform_name"] = self.platform_name xarr.attrs["ssp_lon"] = self.ssp_lon if ("resolution" not in dataset_info) or (dataset_info["resolution"] is None): diff --git a/satpy/readers/eum_l2_grib.py b/satpy/readers/eum_l2_grib.py index d4d581052b..50bb99772b 100644 --- a/satpy/readers/eum_l2_grib.py +++ b/satpy/readers/eum_l2_grib.py @@ -30,7 +30,7 @@ import xarray as xr from satpy.readers.core._geos_area import get_area_definition, get_geos_area_naming -from satpy.readers.core.eum import get_service_mode +from satpy.readers.core.eum import WMO_INSTRUMENT_NAMES, get_service_mode from satpy.readers.core.fci import calculate_area_extent as fci_calculate_area_extent from satpy.readers.core.file_handlers import BaseFileHandler from satpy.readers.core.seviri import PLATFORM_DICT as SEVIRI_PLATFORM_DICT @@ -292,7 +292,8 @@ def _get_attributes(self): "projection_longitude": self._ssp_lon } - attributes = {"orbital_parameters": orbital_parameters, "sensor": self.sensor, + attributes = {"orbital_parameters": orbital_parameters, + "instruments": {WMO_INSTRUMENT_NAMES[self.sensor]}, "platform_name": self.PLATFORM_NAME} return attributes diff --git a/satpy/readers/fci_l2_nc.py b/satpy/readers/fci_l2_nc.py index 730d1ae7ae..a138eae768 100644 --- a/satpy/readers/fci_l2_nc.py +++ b/satpy/readers/fci_l2_nc.py @@ -27,7 +27,7 @@ from satpy._compat import cached_property from satpy.area import get_area_def from satpy.readers.core._geos_area import get_geos_area_naming, make_ext -from satpy.readers.core.eum import get_service_mode +from satpy.readers.core.eum import WMO_INSTRUMENT_NAMES, get_service_mode from satpy.readers.core.fci import platform_name_translate from satpy.readers.core.file_handlers import BaseFileHandler from satpy.utils import get_legacy_chunk_size @@ -82,7 +82,7 @@ def _get_global_attributes(self, product_type="pixel"): "filename": self.filename, "spacecraft_name": platform_name_translate.get(self.spacecraft_name, self.spacecraft_name), "ssp_lon": self.ssp_lon, - "sensor": self.sensor_name, + "instruments": {WMO_INSTRUMENT_NAMES[self.sensor_name]}, "platform_name": platform_name_translate.get(self.spacecraft_name, self.spacecraft_name) } diff --git a/satpy/readers/geocat.py b/satpy/readers/geocat.py index efd7ead0c5..5fe9d971c9 100644 --- a/satpy/readers/geocat.py +++ b/satpy/readers/geocat.py @@ -81,19 +81,19 @@ def __init__(self, filename, filename_info, filetype_info, xarray_kwargs=kwargs["xarray_kwargs"]) sensors = { - "goes": "goes_imager", - "himawari8": "ahi", - "goes16": "abi", # untested - "goesr": "abi", # untested + "goes": "GOES Imager", + "himawari8": "AHI", + "goes16": "ABI", # untested + "goesr": "ABI", # untested } platforms: dict[str, str] = { } resolutions = { - "abi": { + "ABI": { 1: 1002.0086577437705, 2: 2004.0173154875411, }, - "ahi": { + "AHI": { 1: 999.9999820317674, # assumption 2: 1999.999964063535, 4: 3999.99992812707, @@ -129,7 +129,7 @@ def _get_proj(self, platform, ref_lon): @property def sensor_names(self): """Get sensor names.""" - return [self.get_sensor(self["/attr/Sensor_Name"])] + return {self.get_sensor(self["/attr/Sensor_Name"])} @property def start_time(self): @@ -292,7 +292,7 @@ def get_metadata(self, dataset_id, ds_info): # CF compliance info["units"] = CF_UNITS[u] - info["sensor"] = self.get_sensor(self["/attr/Sensor_Name"]) + info["instruments"] = self.sensor_names info["platform_name"] = self.get_platform(self["/attr/Platform_Name"]) info["resolution"] = dataset_id["resolution"] if var_name == "pixel_longitude": diff --git a/satpy/readers/ghi_l1.py b/satpy/readers/ghi_l1.py index 2b12d1974e..f999c3d628 100644 --- a/satpy/readers/ghi_l1.py +++ b/satpy/readers/ghi_l1.py @@ -63,7 +63,7 @@ def adjust_attrs(self, data, ds_info): """Adjust the attrs of the data.""" satname = self.PLATFORM_NAMES.get(self["/attr/Satellite Name"], self["/attr/Satellite Name"]) data.attrs.update({"platform_name": satname, - "sensor": self["/attr/Sensor Identification Code"].lower(), + "instruments": {self["/attr/Sensor Identification Code"]}, "orbital_parameters": { "satellite_nominal_latitude": self["/attr/NOMSubSatLat"].item(), "satellite_nominal_longitude": self["/attr/NOMSubSatLon"].item(), diff --git a/satpy/readers/ghrsst_l2.py b/satpy/readers/ghrsst_l2.py index c5afd300a0..e543b37310 100644 --- a/satpy/readers/ghrsst_l2.py +++ b/satpy/readers/ghrsst_l2.py @@ -77,7 +77,9 @@ def _is_sst_file(name): def get_dataset(self, key, info): """Get any available dataset.""" stdname = info.get("standard_name") - return self.nc[stdname].squeeze() + data_array = self.nc[stdname].squeeze() + data_array.attrs["instruments"] = {self.sensor} + return data_array @property def start_time(self): @@ -92,7 +94,7 @@ def end_time(self): @property def sensor(self): """Get the sensor name.""" - return self.nc.attrs["sensor"].lower() + return self.nc.attrs["sensor"] def __del__(self): """Close the tarfile object.""" diff --git a/satpy/readers/ghrsst_l3c_sst.py b/satpy/readers/ghrsst_l3c_sst.py index 5f00230bb5..e33cb9988f 100644 --- a/satpy/readers/ghrsst_l3c_sst.py +++ b/satpy/readers/ghrsst_l3c_sst.py @@ -30,8 +30,8 @@ logger = logging.getLogger(__name__) PLATFORM_NAME = {"NPP": "Suomi-NPP", } -SENSOR_NAME = {"VIIRS": "viirs", - "AVHRR": "avhrr/3"} +SENSOR_NAME = {"VIIRS": "VIIRS", + "AVHRR": "AVHRR/3"} class GHRSST_OSISAFL2(NetCDF4FileHandler): @@ -78,7 +78,7 @@ def get_dataset(self, dataset_id, ds_info, out=None): ds_info.update({ "units": ds_info.get("units", file_units), "platform_name": PLATFORM_NAME.get(self["/attr/platform"], self["/attr/platform"]), - "sensor": SENSOR_NAME.get(self["/attr/sensor"], self["/attr/sensor"]), + "instruments": {SENSOR_NAME.get(self["/attr/sensor"], self["/attr/sensor"])}, }) ds_info.update(dataset_id.to_dict()) cls = ds_info.pop("container", Dataset) diff --git a/satpy/readers/glm_l2.py b/satpy/readers/glm_l2.py index 621f9c6dc4..eda3313212 100644 --- a/satpy/readers/glm_l2.py +++ b/satpy/readers/glm_l2.py @@ -49,7 +49,7 @@ class NCGriddedGLML2(NC_ABI_BASE): @property def sensor(self): """Get sensor name for current file handler.""" - return "glm" + return "GLM" @property def start_time(self): @@ -77,7 +77,7 @@ def get_dataset(self, key, info): logger.debug("Reading in get_dataset %s.", key["name"]) res = self[key["name"]] res.attrs.update({"platform_name": self.platform_name, - "sensor": self.sensor}) + "instruments": {self.sensor}}) res.attrs.update(self.filename_info) # Add orbital parameters diff --git a/satpy/readers/gms/gms5_vissr_l1b.py b/satpy/readers/gms/gms5_vissr_l1b.py index e0b0c77a01..ee248a2ac9 100644 --- a/satpy/readers/gms/gms5_vissr_l1b.py +++ b/satpy/readers/gms/gms5_vissr_l1b.py @@ -158,6 +158,7 @@ import numpy as np import xarray as xr +import satpy._instruments as instru import satpy.readers.core._geos_area as geos_area import satpy.readers.gms.gms5_vissr_format as fmt import satpy.readers.gms.gms5_vissr_navigation as nav @@ -285,7 +286,7 @@ def _get_nominal_shape(self): def _get_mda(self): return { "platform": self._mode_block["satellite_name"].decode().strip().upper(), - "sensor": "VISSR", + "instruments": {"VISSR"}, "time_parameters": self._get_time_parameters(), "orbital_parameters": self._get_orbital_parameters(), } @@ -762,7 +763,7 @@ def _get_name_dict(self, dataset_id): name_dict = geos_area.get_geos_area_naming( { "platform_name": self.metadata["platform"], - "instrument_name": self.metadata["sensor"], + "instrument_name": instru.get_one_instrument_from_attrs(self.metadata), "service_name": "western-pacific", "service_desc": "Western Pacific", "resolution": dataset_id["resolution"], diff --git a/satpy/readers/goes_imager_hrit.py b/satpy/readers/goes_imager_hrit.py index 81ceb61974..eeadc4def6 100644 --- a/satpy/readers/goes_imager_hrit.py +++ b/satpy/readers/goes_imager_hrit.py @@ -35,6 +35,7 @@ from satpy.readers.core._geos_area import get_area_definition, get_area_extent, get_geos_area_naming from satpy.readers.core.eum import recarray2dict, time_cds_short +from satpy.readers.core.goes_imager import ALTITUDE, EQUATOR_RADIUS, INSTRUMENTS, POLE_RADIUS, SPACECRAFTS from satpy.readers.core.hrit import ( HRITFileHandler, ancillary_text, @@ -50,10 +51,6 @@ class CalibrationError(Exception): logger = logging.getLogger("hrit_goes") -# Geometric constants [meters] -EQUATOR_RADIUS = 6378169.00 -POLE_RADIUS = 6356583.80 -ALTITUDE = 35785831.00 # goes implementation: key_header = np.dtype([("key_number", "u1"), @@ -342,30 +339,6 @@ def process_prologue(self): C1 = 1.19104273e-5 C2 = 1.43877523 -SPACECRAFTS = { - # these are GP_SC_ID - 18007: "GOES-7", - 18008: "GOES-8", - 18009: "GOES-9", - 18010: "GOES-10", - 18011: "GOES-11", - 18012: "GOES-12", - 18013: "GOES-13", - 18014: "GOES-14", - 18015: "GOES-15", - # these are in block-0 - 7: "GOES-7", - 8: "GOES-8", - 9: "GOES-9", - 10: "GOES-10", - 11: "GOES-11", - 12: "GOES-12", - 13: "GOES-13", - 14: "GOES-14", - 15: "GOES-15"} - -SENSOR_NAME = "goes_imager" - class HRITGOESFileHandler(HRITFileHandler): """GOES HRIT format reader.""" @@ -386,6 +359,7 @@ def __init__(self, filename, filename_info, filetype_info, satellite_id = self.prologue["SatelliteID"] self.platform_name = SPACECRAFTS[satellite_id] + self.instrument = INSTRUMENTS[self.platform_name] def get_dataset(self, key, info): """Get the data from the files.""" @@ -399,7 +373,7 @@ def get_dataset(self, key, info): new_attrs.update(res.attrs) res.attrs = new_attrs res.attrs["platform_name"] = self.platform_name - res.attrs["sensor"] = SENSOR_NAME + res.attrs["instruments"] = {self.instrument} res.attrs["orbital_parameters"] = {"projection_longitude": self.mda["projection_parameters"]["SSP_longitude"], "projection_latitude": 0.0, "projection_altitude": ALTITUDE} @@ -470,7 +444,7 @@ def _get_proj_dict(self, dataset_id): loff = nlines - loff name_dict = get_geos_area_naming({ "platform_name": self.platform_name, - "instrument_name": SENSOR_NAME, + "instrument_name": self.instrument, # Partial scans are padded to full disk "service_name": "FD", "service_desc": "Full Disk", diff --git a/satpy/readers/goes_imager_nc.py b/satpy/readers/goes_imager_nc.py index ad51665a86..6603d97fc6 100644 --- a/satpy/readers/goes_imager_nc.py +++ b/satpy/readers/goes_imager_nc.py @@ -234,8 +234,8 @@ import xarray as xr from satpy.readers.core.file_handlers import BaseFileHandler +from satpy.readers.core.goes_imager import ALTITUDE, EQUATOR_RADIUS, INSTRUMENTS, POLE_RADIUS, SPACECRAFTS from satpy.readers.core.utils import bbox, get_geostationary_angle_extent -from satpy.readers.goes_imager_hrit import ALTITUDE, EQUATOR_RADIUS, POLE_RADIUS, SPACECRAFTS from satpy.utils import get_legacy_chunk_size logger = logging.getLogger(__name__) @@ -615,11 +615,11 @@ def __init__(self, filename, filename_info, filetype_info, geo_data=None): decode_cf=True, mask_and_scale=False, chunks={"xc": CHUNK_SIZE, "yc": CHUNK_SIZE}) - self.sensor = "goes_imager" self.nlines = self.nc.sizes["yc"] self.ncols = self.nc.sizes["xc"] self.platform_name = self._get_platform_name( self.nc.attrs["Satellite Sensor"]) + self.instrument = INSTRUMENTS[self.platform_name] self.platform_shortname = self.platform_name.replace("-", "").lower() self.gvar_channel = int(self.nc["bands"].item()) self.sector = self._get_sector(channel=self.gvar_channel, @@ -942,7 +942,7 @@ def _update_metadata(self, data, ds_info): # Metadata discovered from the file. data.attrs.update( {"platform_name": self.platform_name, - "sensor": self.sensor, + "instruments": {self.instrument}, "sector": self.sector, "orbital_parameters": {"yaw_flip": self.meta["yaw_flip"]}} ) diff --git a/satpy/readers/grib.py b/satpy/readers/grib.py index 4f24c44ab1..1c866055b3 100644 --- a/satpy/readers/grib.py +++ b/satpy/readers/grib.py @@ -285,8 +285,7 @@ def get_metadata(self, msg, ds_info): "units": "units", "modelName": "modelName", "valid_min": "minimum", - "valid_max": "maximum", - "sensor": "modelName"} + "valid_max": "maximum"} ds_info.update({ "filename": self.filename, @@ -294,7 +293,8 @@ def get_metadata(self, msg, ds_info): "centreDescription": center_description, "start_time": start_time, "end_time": end_time, - "platform_name": "unknown"}) + "platform_name": "unknown", + "instruments": {msg["modelName"]}}) for key in key_dicts: if key_dicts[key] in msg.keys(): diff --git a/satpy/readers/hrit_jma.py b/satpy/readers/hrit_jma.py index 555c6a9444..067491efcf 100644 --- a/satpy/readers/hrit_jma.py +++ b/satpy/readers/hrit_jma.py @@ -99,6 +99,7 @@ import numpy as np import xarray as xr +import satpy._instruments as instru import satpy.utils from satpy.readers.core._geos_area import get_area_definition, get_area_extent from satpy.readers.core.hrit import ( @@ -176,9 +177,9 @@ "GEOS(145.00)": MTSAT2, } SENSORS = { - MTSAT1R: "jami", - MTSAT2: "mtsat2_imager", - HIMAWARI8: "ahi" + MTSAT1R: "JAMI", + MTSAT2: "IMAGER (MTSAT-2)", + HIMAWARI8: "AHI" } @@ -373,7 +374,8 @@ def get_dataset(self, key, info): # Filenames of segmented data is identical for MTSAT-1R, MTSAT-2 # and Himawari-8/9. Make sure we have the correct reader for the data # at hand. - self._check_sensor_platform_consistency(info["sensor"]) + instrument = instru.get_one_instrument_from_attrs(info) + self._check_sensor_platform_consistency(instrument) # Calibrate and mask space pixels res = self._mask_space(self.calibrate(res, key["calibration"])) @@ -384,6 +386,7 @@ def get_dataset(self, key, info): # Update attributes res.attrs.update(info) + res.attrs["instruments"] = set(res.attrs["instruments"]) res.attrs["platform_name"] = self.platform res.attrs["orbital_parameters"] = { "projection_longitude": float(self.mda["projection_parameters"]["SSP_longitude"]), diff --git a/satpy/readers/iasi_l2.py b/satpy/readers/iasi_l2.py index 30d2ff849d..e1b11b3796 100644 --- a/satpy/readers/iasi_l2.py +++ b/satpy/readers/iasi_l2.py @@ -105,14 +105,10 @@ def __init__(self, filename, filename_info, filetype_info): self.finfo = filename_info self.lons = None self.lats = None - self.sensor = "iasi" + self.sensor = "IASI" short_name = filename_info["platform_id"] self.platform_name = SHORT_NAMES.get(short_name, short_name) - self.mda = {} - self.mda["platform_name"] = self.platform_name - self.mda["sensor"] = "iasi" - @property def start_time(self): """Get the start time.""" @@ -136,7 +132,7 @@ def get_dataset(self, key, info): else: m_data = read_geo(fid, key) m_data.attrs.update(info) - m_data.attrs["sensor"] = self.sensor + m_data.attrs["instruments"] = {self.sensor} m_data.attrs["platform_name"] = self.platform_name return m_data diff --git a/satpy/readers/iasi_l2_so2_bufr.py b/satpy/readers/iasi_l2_so2_bufr.py index 459a0d0b52..b66c0c3de1 100644 --- a/satpy/readers/iasi_l2_so2_bufr.py +++ b/satpy/readers/iasi_l2_so2_bufr.py @@ -230,7 +230,7 @@ def get_dataset(self, dataset_id, dataset_info): arr[arr == dataset_info["fill_value"]] = np.nan xarr = xr.DataArray(arr, dims=["y", "x"], name=dataset_info["name"]) - xarr.attrs["sensor"] = "IASI" + xarr.attrs["instruments"] = {"IASI"} xarr.attrs["platform_name"] = self.platform_name xarr.attrs.update(dataset_info) diff --git a/satpy/readers/iasi_ng_l2_nc.py b/satpy/readers/iasi_ng_l2_nc.py index 12ce3644e8..929b05c8b6 100644 --- a/satpy/readers/iasi_ng_l2_nc.py +++ b/satpy/readers/iasi_ng_l2_nc.py @@ -50,7 +50,7 @@ def __init__(self, filename, filename_info, filetype_info, **kwargs): filename, filename_info, filetype_info, auto_maskandscale=True, **kwargs ) - self.sensors = {"iasi_ng"} + self.sensors = {"IASI-NG"} self.dataset_infos = None self.variable_desc = {} @@ -108,7 +108,7 @@ def register_dataset(self, ds_name, desc): ds_infos = { "name": ds_name, - "sensor": "iasi_ng", + "instruments": self.sensors, "file_type": self.filetype_info["file_type"], } diff --git a/satpy/readers/ici_l1b_nc.py b/satpy/readers/ici_l1b_nc.py index 4238e93dbd..76fa938d4e 100644 --- a/satpy/readers/ici_l1b_nc.py +++ b/satpy/readers/ici_l1b_nc.py @@ -435,7 +435,7 @@ def _get_global_attributes(self): "end_time": self.end_time, "spacecraft_name": self.platform_name, "ssp_lon": self.ssp_lon, - "sensor": self.sensor, + "instruments": {self.sensor}, "filename_start_time": self.filename_info["sensing_start_time"], "filename_end_time": self.filename_info["sensing_end_time"], "platform_name": self.platform_name, diff --git a/satpy/readers/insat3d_img_l1b_h5.py b/satpy/readers/insat3d_img_l1b_h5.py index 8eaf596b59..46d650e83e 100644 --- a/satpy/readers/insat3d_img_l1b_h5.py +++ b/satpy/readers/insat3d_img_l1b_h5.py @@ -159,7 +159,7 @@ def get_dataset(self, ds_id, ds_info): satellite_nominal_altitude=float(ds.attrs["Nominal_Altitude(km)"]), satellite_actual_altitude=float(ds.attrs["Observed_Altitude(km)"])) darr.attrs["platform_name"] = "insat-3d" - darr.attrs["sensor"] = "imager" + darr.attrs["instruments"] = {"IMAGER (INSAT)"} darr = darr.squeeze() return darr diff --git a/satpy/readers/mersi_l1b.py b/satpy/readers/mersi_l1b.py index b76c397fe6..fda050960c 100644 --- a/satpy/readers/mersi_l1b.py +++ b/satpy/readers/mersi_l1b.py @@ -35,14 +35,13 @@ from satpy.readers.core.hdf5 import HDF5FileHandler N_TOT_IR_CHANS_LL = 6 -PLATFORMS_INSTRUMENTS = {"FY-3A": "mersi-1", - "FY-3B": "mersi-1", - "FY-3C": "mersi-1", - "FY-3D": "mersi-2", - "FY-3E": "mersi-ll", - "FY-3F": "mersi-3", - "FY-3G": "mersi-rm"} - +PLATFORMS_INSTRUMENTS = {"FY-3A": "MERSI-1", + "FY-3B": "MERSI-1", + "FY-3C": "MERSI-1", + "FY-3D": "MERSI-2", + "FY-3E": "MERSI-LL", + "FY-3F": "MERSI-3", + "FY-3G": "MERSI-RM"} class MERSIL1B(HDF5FileHandler): """MERSI-1/MERSI-2/MERSI-LL/MERSI-RM L1B file reader.""" @@ -77,7 +76,7 @@ def platform_name(self): def get_refl_mult(self): """Get reflectance multiplier.""" - if self.sensor_name == "mersi-rm": + if self.sensor_name == "MERSI-RM": # MERSI-RM has reflectance in the range 0-1, so we need to convert return 100. else: @@ -94,7 +93,7 @@ def _get_single_slope_intercept(self, slope, intercept, cal_index): def _get_coefficients(self, cal_key, cal_index): """Get VIS calibration coeffs from calibration datasets.""" # Only one VIS band for MERSI-LL - coeffs = self[cal_key][cal_index] if self.sensor_name != "mersi-ll" else self[cal_key] + coeffs = self[cal_key][cal_index] if self.sensor_name != "MERSI-LL" else self[cal_key] slope = coeffs.attrs.pop("Slope", None) intercept = coeffs.attrs.pop("Intercept", None) if slope is not None: @@ -124,7 +123,7 @@ def _get_dn_corrections(self, data, band_index, dataset_id, attrs): slope = slope[band_index] intercept = intercept[band_index] # There's a bug in slope for MERSI-1 IR band - slope = 0.01 if self.sensor_name == "mersi-1" and dataset_id["name"] == "5" else slope + slope = 0.01 if self.sensor_name == "MERSI-1" and dataset_id["name"] == "5" else slope data = data * slope + intercept return data @@ -155,7 +154,7 @@ def get_dataset(self, dataset_id, ds_info): # to SI units m^-1, mW*m^-3*str^-1. wave_number = 1. / (dataset_id["wavelength"][1] / 1e6) # MERSI-1 doesn't have additional corrections - calibration_index = None if self.sensor_name == "mersi-1" else ds_info["calibration_index"] + calibration_index = None if self.sensor_name == "MERSI-1" else ds_info["calibration_index"] data = self._get_bt_dataset(data, calibration_index, wave_number) data.attrs = attrs @@ -167,7 +166,7 @@ def get_dataset(self, dataset_id, ds_info): data.attrs.update({ "platform_name": self.platform_name, - "sensor": self.sensor_name, + "instruments": {self.sensor_name}, }) return data @@ -186,10 +185,10 @@ def _mask_data(self, data, dataset_id, attrs): try: # Due to a bug in the valid_range upper limit in the 10.8(24) and 12.0(25) # in the HDF data, this is hardcoded here. - valid_range[1] = 25000 if self.sensor_name == "mersi-2" and dataset_id["name"] in ["24", "25"] and \ + valid_range[1] = 25000 if self.sensor_name == "MERSI-2" and dataset_id["name"] in ["24", "25"] and \ valid_range[1] == 4095 else valid_range[1] # Similar bug also found in MERSI-1 - valid_range[1] = 25000 if self.sensor_name == "mersi-1" and dataset_id["name"] == "5" and \ + valid_range[1] = 25000 if self.sensor_name == "MERSI-1" and dataset_id["name"] == "5" and \ valid_range[1] == 4095 else valid_range[1] # typically bad_values == 65535, saturated == 65534 # dead detector == 65533 @@ -217,7 +216,7 @@ def _get_ref_dataset(self, data, ds_info): # Only FY-3A/B stores VIS calibration coefficients in attributes coeffs = self._get_coefficients_mersi1(ds_info["calibration_index"]) if self.platform_name in ["FY-3A", "FY-3B"] else self._get_coefficients(ds_info["calibration_key"], ds_info.get("calibration_index", None)) - data = coeffs[0] + coeffs[1] * data + coeffs[2] * data ** 2 if self.sensor_name != "mersi-ll" else \ + data = coeffs[0] + coeffs[1] * data + coeffs[2] * data ** 2 if self.sensor_name != "MERSI-LL" else \ data * np.pi / coeffs[0] * 100 data = data * self.get_refl_mult() @@ -241,10 +240,10 @@ def _get_rad_dataset(self, data, ds_info, datset_id): mersi_2_vis = [str(i) for i in range(1, 20)] mersi_rm_vis = [str(i) for i in range(1, 6)] - if self.sensor_name == "mersi-2" and datset_id["name"] in mersi_2_vis: + if self.sensor_name == "MERSI-2" and datset_id["name"] in mersi_2_vis: E0 = self["/attr/Solar_Irradiance"] rad = self._get_ref_dataset(data, ds_info) / 100 * E0[mersi_2_vis.index(datset_id["name"])] / np.pi - elif self.sensor_name == "mersi-rm" and datset_id["name"] in mersi_rm_vis: + elif self.sensor_name == "MERSI-RM" and datset_id["name"] in mersi_rm_vis: E0 = self._get_coefficients("Calibration/Solar_Irradiance", mersi_rm_vis.index(datset_id["name"])) rad = self._get_ref_dataset(data, ds_info) / 100 * E0 / np.pi else: @@ -278,14 +277,14 @@ def _get_bt_dataset(self, data, calibration_index, wave_number): data = data.where(data != 0) # additional corrections from the file - if self.sensor_name == "mersi-1": + if self.sensor_name == "MERSI-1": # https://img.nsmc.org.cn/PORTAL/NSMC/DATASERVICE/SRF/FY3C/FY3C_MERSI_SRF.rar corr_coeff_a = 1.0047 corr_coeff_b = -0.8549 - elif self.sensor_name == "mersi-2": + elif self.sensor_name == "MERSI-2": corr_coeff_a = float(self["/attr/TBB_Trans_Coefficient_A"][calibration_index]) corr_coeff_b = float(self["/attr/TBB_Trans_Coefficient_B"][calibration_index]) - elif self.sensor_name == "mersi-ll": + elif self.sensor_name == "MERSI-LL": # MERSI-LL stores these coefficients differently try: coeffs = self["/attr/TBB_Trans_Coefficient"] @@ -298,7 +297,7 @@ def _get_bt_dataset(self, data, calibration_index, wave_number): corr_coeff_a = 0 if corr_coeff_a != 0: - data = (data - corr_coeff_b) / corr_coeff_a if self.sensor_name != "mersi-1" else \ + data = (data - corr_coeff_b) / corr_coeff_a if self.sensor_name != "MERSI-1" else \ data * corr_coeff_a + corr_coeff_b # some bands have 0 counts for the first N columns and # seem to be invalid data points diff --git a/satpy/readers/mimic_TPW2_nc.py b/satpy/readers/mimic_TPW2_nc.py index acdd1295ec..b7429a66f9 100644 --- a/satpy/readers/mimic_TPW2_nc.py +++ b/satpy/readers/mimic_TPW2_nc.py @@ -149,7 +149,7 @@ def get_metadata(self, data, info): metadata.update(info) metadata.update({ "platform_shortname": "aggregated microwave", - "sensor": "mimic", + "instruments": {"MIMIC"}, "start_time": self.start_time, "end_time": self.end_time, }) diff --git a/satpy/readers/mirs.py b/satpy/readers/mirs.py index f7ce23f827..e9a9ca00b3 100644 --- a/satpy/readers/mirs.py +++ b/satpy/readers/mirs.py @@ -46,7 +46,7 @@ def get_resource_string(mod_part, file_part): POLO_V = 2 POLO_H = 3 -amsu = "amsu-mhs" + PLATFORMS = {"n18": "NOAA-18", "n19": "NOAA-19", "np": "NOAA-19", @@ -65,25 +65,29 @@ def get_resource_string(mod_part, file_part): "f18": "DMSP-F18", "gpm": "GPM", } -SENSOR = {"n18": amsu, - "n19": amsu, - "n20": "atms", - "n21": "atms", - "n22": "atms", - "n23": "atms", - "n24": "atms", - "np": amsu, - "m1": amsu, - "m2": amsu, - "m3": amsu, - "ma1": amsu, - "ma2": amsu, - "ma3": amsu, - "npp": "atms", - "jpss": "atms", - "f17": "ssmis", - "f18": "ssmis", - "gpm": "GPI", +amsua_mhs = {"AMSU-A", "MHS"} +atms = {"ATMS"} +ssmis = {"SSMIS"} +gmi = {"GMI"} +SENSOR = {"n18": amsua_mhs, + "n19": amsua_mhs, + "n20": atms, + "n21": atms, + "n22": atms, + "n23": atms, + "n24": atms, + "np": amsua_mhs, + "m1": amsua_mhs, + "m2": amsua_mhs, + "m3": amsua_mhs, + "ma1": amsua_mhs, + "ma2": amsua_mhs, + "ma3": amsua_mhs, + "npp": atms, + "jpss": atms, + "f17": ssmis, + "f18": ssmis, + "gpm": gmi, } @@ -262,7 +266,11 @@ def _get_sensor(self): @property def sensor_names(self): """Return standard sensor names for the file's data.""" - return list(set(SENSOR.values())) + return set( + instr + for instr_set in SENSOR.values() + for instr in instr_set + ) @property def start_time(self): @@ -319,7 +327,7 @@ def update_metadata(self, ds_info): metadata = {} metadata.update(ds_info) metadata.update({ - "sensor": self.sensor, + "instruments": self.sensor, "platform_name": self.platform_name, "start_time": self.start_time, "end_time": self.end_time, @@ -415,7 +423,7 @@ def get_dataset(self, ds_id, ds_info): data = data.rename(new_name_or_name_dict=ds_info["name"]) data, ds_info = self.apply_attributes(data, ds_info) - if self.sensor.lower() == "atms" and self.limb_correction: + if self.sensor.lower() == atms and self.limb_correction: sfc_type_mask = self["Sfc_type"] data = limb_correct_atms_bt(data, sfc_type_mask, self._get_coeff_filenames, diff --git a/satpy/readers/msu_gsa_l1b.py b/satpy/readers/msu_gsa_l1b.py index a141ea5069..ecc1b35502 100644 --- a/satpy/readers/msu_gsa_l1b.py +++ b/satpy/readers/msu_gsa_l1b.py @@ -64,7 +64,7 @@ def satellite_longitude(self): @property def sensor_name(self): """Sensor name is hardcoded.""" - sensor = "msu_gsa" + sensor = "MSU-GS/A" return sensor @property @@ -104,7 +104,7 @@ def get_dataset(self, dataset_id, ds_info): data.attrs = attrs data.attrs.update({ "platform_name": self.platform_name, - "sensor": self.sensor_name, + "instruments": {self.sensor_name}, "sat_altitude": self.satellite_altitude, "sat_latitude": self.satellite_latitude, "sat_longitude": self.satellite_longitude, diff --git a/satpy/readers/mviri_l1b_fiduceo_nc.py b/satpy/readers/mviri_l1b_fiduceo_nc.py index 858e8d4388..75af95a689 100644 --- a/satpy/readers/mviri_l1b_fiduceo_nc.py +++ b/satpy/readers/mviri_l1b_fiduceo_nc.py @@ -718,7 +718,7 @@ def _update_attrs(self, ds, info): """Update dataset attributes.""" ds.attrs.update(info) ds.attrs.update({"platform": self.filename_info["platform"], - "sensor": self.filename_info["sensor"]}) + "instruments": {self.filename_info["instrument"]}}) ds.attrs["raw_metadata"] = self.nc.attrs ds.attrs["orbital_parameters"] = self._get_orbital_parameters() diff --git a/satpy/readers/mwr_l1b.py b/satpy/readers/mwr_l1b.py index ce918ce4a5..39ba265a9d 100644 --- a/satpy/readers/mwr_l1b.py +++ b/satpy/readers/mwr_l1b.py @@ -112,8 +112,10 @@ def end_time(self): def sensor(self): """Get the sensor name.""" # This should have been self["/attr/instrument"] - # But the sensor name is currently incorrect in the ESA level-1b files - return "mwr" + # But the sensor name is currently incorrect in the ESA level-1b files. + # Also, MWR is not a valid WMO acronym. OSCAR lists "MWR (Sterna)", "MWR (AWS)" + # etc. But to avoid enhancement/composite duplication we stick to "MWR". + return "MWR" @property def platform_name(self): @@ -191,7 +193,7 @@ def get_dataset(self, dataset_id, dataset_info): "sub_satellite_longitude_end": self.sub_satellite_longitude_end} data_array.attrs["platform_name"] = self.platform_name - data_array.attrs["sensor"] = self.sensor + data_array.attrs["instruments"] = {self.sensor} data_array.attrs["orbit_number"] = self.orbit_start return data_array diff --git a/satpy/readers/mwr_l1c.py b/satpy/readers/mwr_l1c.py index aec5fec224..6a2c7e3fca 100644 --- a/satpy/readers/mwr_l1c.py +++ b/satpy/readers/mwr_l1c.py @@ -72,7 +72,9 @@ def sensor(self): """Get the sensor name.""" # This should have been self["/attr/instrument"] # But the sensor name is currently incorrect in the ESA level-1b files - return "mwr" + # Also, MWR is not a valid WMO acronym. OSCAR lists "MWR (Sterna)", "MWR (AWS)" + # etc. But to avoid enhancement/composite duplication we stick to "MWR". + return "MWR" def get_dataset(self, dataset_id, dataset_info): """Get the data.""" @@ -91,10 +93,9 @@ def get_dataset(self, dataset_id, dataset_info): data_array.attrs.update(dataset_info) data_array.attrs["platform_name"] = self.platform_name - data_array.attrs["sensor"] = self.sensor + data_array.attrs["instruments"] = {self.sensor} return data_array - def _get_navigation_data(self, dataset_id, dataset_info): """Get the navigation (geolocation) data.""" geo_data = self[dataset_info["file_key"]] diff --git a/satpy/readers/mws_l1b.py b/satpy/readers/mws_l1b.py index 9cc8babddf..6849f8475d 100644 --- a/satpy/readers/mws_l1b.py +++ b/satpy/readers/mws_l1b.py @@ -220,7 +220,7 @@ def _get_dataset_channel(self, key, dataset_info): dataset_attrs.update(dataset_info) dataset_attrs.update({ "platform_name": self.platform_name, - "sensor": self.sensor, + "instruments": {self.sensor}, "orbital_parameters": {"sub_satellite_latitude_start": self.sub_satellite_latitude_start, "sub_satellite_longitude_start": self.sub_satellite_longitude_start, "sub_satellite_latitude_end": self.sub_satellite_latitude_end, @@ -267,7 +267,6 @@ def _get_global_attributes(self): "start_time": self.start_time, "end_time": self.end_time, "spacecraft_name": self.platform_name, - "sensor": self.sensor, "filename_start_time": self.filename_info["start_time"], "filename_end_time": self.filename_info["end_time"], "platform_name": self.platform_name, diff --git a/satpy/readers/nucaps.py b/satpy/readers/nucaps.py index 4408ec54a1..8e0244eeab 100644 --- a/satpy/readers/nucaps.py +++ b/satpy/readers/nucaps.py @@ -134,12 +134,9 @@ def sensor_names(self): """Return standard sensor or instrument name for the file's data.""" try: res = self["/attr/instrument_name"] - res = [x.strip() for x in res.split(",")] - if len(res) == 1: - return res[0].lower() + return {x.strip() for x in res.split(",")} except KeyError: - res = ["CrIS", "ATMS", "VIIRS"] - return set(name.lower() for name in res) + return {"CrIS", "ATMS", "VIIRS"} def get_shape(self, ds_id, ds_info): """Return data array shape for item specified.""" @@ -172,7 +169,7 @@ def get_metadata(self, dataset_id, ds_info): "shape": shape, "units": ds_info.get("units", file_units), "platform_name": self.platform_name, - "sensor": self.sensor_names, + "instruments": self.sensor_names, "start_orbit": self.start_orbit_number, "end_orbit": self.end_orbit_number, }) diff --git a/satpy/readers/nwcsaf_nc.py b/satpy/readers/nwcsaf_nc.py index fa5d29fdbe..17c26b6ae2 100644 --- a/satpy/readers/nwcsaf_nc.py +++ b/satpy/readers/nwcsaf_nc.py @@ -45,29 +45,30 @@ V2025_PERSPECTIVE_POINT_HEIGHT = 35786400.0 + SENSOR = { - "NOAA-19": "avhrr-3", - "NOAA-18": "avhrr-3", - "NOAA-15": "avhrr-3", - "Metop-A": "avhrr-3", - "Metop-B": "avhrr-3", - "Metop-C": "avhrr-3", - "EOS-Aqua": "modis", - "EOS-Terra": "modis", - "Suomi-NPP": "viirs", - "NOAA-20": "viirs", - "NOAA-21": "viirs", - "NOAA-22": "viirs", - "NOAA-23": "viirs", - "JPSS-1": "viirs", - "Metop-SG-A1": "metimage", - "Metop-SG-A2": "metimage", - "Metop-SG-A3": "metimage", - "GOES-16": "abi", - "GOES-17": "abi", - "Himawari-8": "ahi", - "Himawari-9": "ahi", - "Meteosat-12": "fci", + "NOAA-19": "AVHRR/3", + "NOAA-18": "AVHRR/3", + "NOAA-15": "AVHRR/3", + "Metop-A": "AVHRR/3", + "Metop-B": "AVHRR/3", + "Metop-C": "AVHRR/3", + "EOS-Aqua": "MODIS", + "EOS-Terra": "MODIS", + "Suomi-NPP": "VIIRS", + "NOAA-20": "VIIRS", + "NOAA-21": "VIIRS", + "NOAA-22": "VIIRS", + "NOAA-23": "VIIRS", + "JPSS-1": "VIIRS", + "Metop-SG-A1": "METimage", + "Metop-SG-A2": "METimage", + "Metop-SG-A3": "METimage", + "GOES-16": "ABI", + "GOES-17": "ABI", + "Himawari-8": "AHI", + "Himawari-9": "AHI", + "Meteosat-12": "FCI", } @@ -105,7 +106,7 @@ def __init__(self, filename, filename_info, filetype_info): self.pps = False self.platform_name = None - self.sensor = None + self.sensor = {} self.file_key_prefix = filetype_info.get("file_key_prefix", "") try: @@ -134,7 +135,7 @@ def set_platform_and_sensor(self, **kwargs): self.platform_name = kwargs["platform_name"] self.pps = True - self.sensor = set([SENSOR.get(self.platform_name, "seviri")]) + self.sensor = set([SENSOR.get(self.platform_name, "SEVIRI")]) def remove_timedim(self, var): """Remove time dimension from dataset.""" @@ -227,7 +228,7 @@ def scale_dataset(self, variable, info): variable.attrs.pop("scale_factor", None) variable.attrs.update({"platform_name": self.platform_name, - "sensor": self.sensor}) + "instruments": {self.sensor}}) if not variable.attrs.get("standard_name", "").endswith("status_flag"): # TODO: do we really need to add units to everything ? diff --git a/satpy/readers/oceancolorcci_l3_nc.py b/satpy/readers/oceancolorcci_l3_nc.py index 16bbc6d223..bf5fb4c652 100644 --- a/satpy/readers/oceancolorcci_l3_nc.py +++ b/satpy/readers/oceancolorcci_l3_nc.py @@ -74,7 +74,7 @@ def _update_attrs(self, dataset, dataset_info): """Update dataset attributes.""" dataset.attrs.update(self[dataset_info["nc_key"]].attrs) dataset.attrs.update(dataset_info) - dataset.attrs["sensor"] = "merged" + dataset.attrs["instruments"] = {"SeaWiFS", "MERIS", "MODIS", "VIIRS"} dataset.attrs["composite_period"] = self.composite_period # remove attributes from original file which don't apply anymore dataset.attrs.pop("nc_key") diff --git a/satpy/readers/olci_nc.py b/satpy/readers/olci_nc.py index 724e5db26e..9450e8611b 100644 --- a/satpy/readers/olci_nc.py +++ b/satpy/readers/olci_nc.py @@ -129,7 +129,7 @@ def __init__(self, filename, filename_info, filetype_info, engine=None, **kwargs self._end_time = filename_info["end_time"] # TODO: get metadata from the manifest file (xfdumanifest.xml) self.platform_name = PLATFORM_NAMES[filename_info["mission_id"]] - self.sensor = "olci" + self.sensor = "OLCI" @cached_property def nc(self): @@ -233,7 +233,7 @@ def get_dataset(self, key, info): dataset.attrs["units"] = "%" dataset.attrs["platform_name"] = self.platform_name - dataset.attrs["sensor"] = self.sensor + dataset.attrs["instruments"] = {self.sensor} dataset.attrs.update(key.to_dict()) return dataset @@ -265,7 +265,7 @@ def get_dataset(self, key, info): dataset = self.getbitmask(dataset, self.mask_items) dataset.attrs["platform_name"] = self.platform_name - dataset.attrs["sensor"] = self.sensor + dataset.attrs["instruments"] = {self.sensor} dataset.attrs.update(key.to_dict()) if self.unlog: dataset = self.delog(dataset) @@ -372,7 +372,7 @@ def get_dataset(self, key, info): values = self.nc[self.datasets[key["name"]]] values.attrs["platform_name"] = self.platform_name - values.attrs["sensor"] = self.sensor + values.attrs["instruments"] = {self.sensor} values.attrs.update(key.to_dict()) return values @@ -442,7 +442,7 @@ def get_dataset(self, key, info): values = self.nc[key["name"]] values.attrs["platform_name"] = self.platform_name - values.attrs["sensor"] = self.sensor + values.attrs["instruments"] = {self.sensor} values.attrs.update(key.to_dict()) return values diff --git a/satpy/readers/omps_edr.py b/satpy/readers/omps_edr.py index e430092ec6..ce6e610d66 100644 --- a/satpy/readers/omps_edr.py +++ b/satpy/readers/omps_edr.py @@ -102,7 +102,7 @@ def platform_name(self): @property def sensor_name(self): """Get the sensor name.""" - return self.nc.instrument.lower() + return self.nc.instrument def get_metadata(self, dataset_id, ds_info): """Get the metadata.""" @@ -113,7 +113,7 @@ def get_metadata(self, dataset_id, ds_info): info.update( { "platform_name": self.platform_name, - "sensor": self.sensor_name, + "instruments": {self.sensor_name}, "orbital_parameters": { "start_orbit": self.start_orbit_number, "end_orbit": self.end_orbit_number, diff --git a/satpy/readers/osisaf_l3_nc.py b/satpy/readers/osisaf_l3_nc.py index 6eaae125f7..41e0ea2228 100644 --- a/satpy/readers/osisaf_l3_nc.py +++ b/satpy/readers/osisaf_l3_nc.py @@ -170,7 +170,7 @@ def get_dataset(self, dataset_id, ds_info): ds_info.update({ "units": ds_info.get("units", file_units), "platform_name": self._get_platname(), - "sensor": self._get_instname() + "instruments": {self._get_instname()} }) ds_info.update(dataset_id.to_dict()) data.attrs.update(ds_info) diff --git a/satpy/readers/satpy_cf_nc.py b/satpy/readers/satpy_cf_nc.py index 54aebccbbe..028c63b803 100644 --- a/satpy/readers/satpy_cf_nc.py +++ b/satpy/readers/satpy_cf_nc.py @@ -216,7 +216,7 @@ def sensor_names(self): sensors = set() for _, ds_info in self.available_datasets(): try: - sensors.add(ds_info["sensor"]) + sensors.update(ds_info["instruments"]) except KeyError: continue return sensors diff --git a/satpy/readers/scmi.py b/satpy/readers/scmi.py index 1e5f35a98a..d9c1a3cd71 100644 --- a/satpy/readers/scmi.py +++ b/satpy/readers/scmi.py @@ -83,12 +83,12 @@ def _get_sensor(self): is_h8 = "H8" in self.platform_name is_h9 = "H9" in self.platform_name is_ahi = is_h8 or is_h9 - return "ahi" if is_ahi else "abi" + return "AHI" if is_ahi else "ABI" @property def sensor_names(self): """Get the sensor names.""" - return [self.sensor] + return {self.sensor} def __getitem__(self, item): """Wrap around `self.nc[item]`. @@ -156,7 +156,7 @@ def get_dataset(self, key, info): # set up all the attributes that might be useful to the user/satpy data.attrs.update({"platform_name": self.platform_name, - "sensor": data.attrs.get("sensor", self.sensor), + "instruments": {data.attrs.get("sensor", self.sensor)}, }) if "satellite_longitude" in self.nc.attrs: data.attrs["orbital_parameters"] = { diff --git a/satpy/readers/seadas_l2.py b/satpy/readers/seadas_l2.py index bdfb5fc00b..9e5eddc087 100644 --- a/satpy/readers/seadas_l2.py +++ b/satpy/readers/seadas_l2.py @@ -44,17 +44,17 @@ def __init__(self, filename, filename_info, filetype_info, apply_quality_flags=F self.apply_quality_flags = apply_quality_flags and self.l2_flags_var_name in self def _add_satpy_metadata(self, data): - data.attrs["sensor"] = self.sensor_names + data.attrs["instruments"] = self.sensor_names data.attrs["platform_name"] = self._platform_name() data.attrs["rows_per_scan"] = self._rows_per_scan() return data def _rows_per_scan(self): - if "modis" in self.sensor_names: + if "MODIS" in self.sensor_names: return 10 - if "viirs" in self.sensor_names: + if "VIIRS" in self.sensor_names: return 16 - if "oci" in self.sensor_names: + if "OCI" in self.sensor_names: return 0 raise ValueError(f"Don't know how to read data for sensors: {self.sensor_names}") @@ -81,11 +81,11 @@ def end_time(self): def sensor_names(self): """Get sensor for the current file's data.""" # Example: MODISA or VIIRSN or VIIRSJ1 - sensor_name = self[self.sensor_attr_name].lower() - if sensor_name.startswith("modis"): - return {"modis"} - if sensor_name.startswith("viirs"): - return {"viirs"} + sensor_name = self[self.sensor_attr_name] + if sensor_name.startswith("MODIS"): + return {"MODIS"} + if sensor_name.startswith("VIIRS"): + return {"VIIRS"} # Example: OCI return {sensor_name} diff --git a/satpy/readers/seviri_l1b_hrit.py b/satpy/readers/seviri_l1b_hrit.py index b3811d6f6c..cfa052ebd8 100644 --- a/satpy/readers/seviri_l1b_hrit.py +++ b/satpy/readers/seviri_l1b_hrit.py @@ -775,7 +775,7 @@ def _update_attrs(self, res, info): res.attrs["wavelength"] = info["wavelength"] res.attrs["standard_name"] = info["standard_name"] res.attrs["platform_name"] = self.platform_name - res.attrs["sensor"] = "seviri" + res.attrs["instruments"] = {"SEVIRI"} res.attrs["nominal_start_time"] = self.nominal_start_time res.attrs["nominal_end_time"] = self.nominal_end_time res.attrs["time_parameters"] = { diff --git a/satpy/readers/seviri_l1b_icare.py b/satpy/readers/seviri_l1b_icare.py index b12b3cf084..2041edd9e8 100644 --- a/satpy/readers/seviri_l1b_icare.py +++ b/satpy/readers/seviri_l1b_icare.py @@ -101,10 +101,10 @@ def sensor_name(self): # the sensor and platform names are stored together, eg: MSG1/SEVIRI attr = self["/attr/Sensors"] if isinstance(attr, np.ndarray): - attr = str(attr.astype(str)).lower() + attr = str(attr.astype(str)) else: - attr = attr.lower() - plat = attr[0:4] + attr = attr + plat = attr[0:4].lower() sens = attr[5:] # icare uses non-standard platform names if plat == "msg1": @@ -222,7 +222,7 @@ def get_metadata(self, data, ds_info): "start_time": self.start_time, "end_time": self.end_time, "platform_name": self.sensor_name[0], - "sensor": self.sensor_name[1], + "instruments": {self.sensor_name[1]}, "zone": self.zone, "projection_altitude": self.alt, "cfac": geoloc[0], diff --git a/satpy/readers/seviri_l1b_native.py b/satpy/readers/seviri_l1b_native.py index f33a1ef448..01b2c4d253 100644 --- a/satpy/readers/seviri_l1b_native.py +++ b/satpy/readers/seviri_l1b_native.py @@ -707,7 +707,7 @@ def _update_attrs(self, dataset, dataset_info): dataset.attrs["wavelength"] = dataset_info["wavelength"] dataset.attrs["standard_name"] = dataset_info["standard_name"] dataset.attrs["platform_name"] = self.mda["platform_name"] - dataset.attrs["sensor"] = "seviri" + dataset.attrs["instruments"] = {"SEVIRI"} dataset.attrs["georef_offset_corrected"] = self.mda[ "offset_corrected"] dataset.attrs["time_parameters"] = { diff --git a/satpy/readers/seviri_l1b_nc.py b/satpy/readers/seviri_l1b_nc.py index 781762e555..0ef6c073cf 100644 --- a/satpy/readers/seviri_l1b_nc.py +++ b/satpy/readers/seviri_l1b_nc.py @@ -233,7 +233,7 @@ def _update_attrs(self, dataset, dataset_info): dataset.attrs.update(self.nc[dataset_info["nc_key"]].attrs) dataset.attrs.update(dataset_info) dataset.attrs["platform_name"] = "Meteosat-" + SATNUM[self.platform_id] - dataset.attrs["sensor"] = "seviri" + dataset.attrs["instruments"] = {"SEVIRI"} dataset.attrs["orbital_parameters"] = { "projection_longitude": self.mda["projection_parameters"]["ssp_longitude"], "projection_latitude": 0., diff --git a/satpy/readers/sgli_l1b.py b/satpy/readers/sgli_l1b.py index ded709b468..b9344575e8 100644 --- a/satpy/readers/sgli_l1b.py +++ b/satpy/readers/sgli_l1b.py @@ -89,7 +89,7 @@ def get_dataset(self, key, info): dataset = self.prepare_dataset(key, dataset) dataset.attrs["platform_name"] = "GCOM-C1" - dataset.attrs["sensor"] = "sgli" + dataset.attrs["instruments"] = {"SGLI"} dataset.attrs["units"] = info["units"] dataset.attrs["standard_name"] = info["standard_name"] return dataset diff --git a/satpy/readers/slstr_l1b.py b/satpy/readers/slstr_l1b.py index 510aa46ee4..0cf132d263 100644 --- a/satpy/readers/slstr_l1b.py +++ b/satpy/readers/slstr_l1b.py @@ -260,7 +260,7 @@ def __init__(self, filename, filename_info, filetype_info): # TODO: get metadata from the manifest file (xfdumanifest.xml) self.platform_name = PLATFORM_NAMES[filename_info["mission_id"]] - self.sensor = "slstr" + self.sensor = "SLSTR" self.view = filename_info["view"] self._start_time = filename_info["start_time"] self._end_time = filename_info["end_time"] @@ -341,7 +341,7 @@ def get_dataset(self, key, info): dims=["y", "x"], attrs=variable.attrs) variable.attrs["platform_name"] = self.platform_name - variable.attrs["sensor"] = self.sensor + variable.attrs["instruments"] = {self.sensor} if "units" not in variable.attrs: variable.attrs["units"] = "degrees" diff --git a/satpy/readers/smos_l2_wind.py b/satpy/readers/smos_l2_wind.py index 5c8bf51e99..3e4b901be7 100644 --- a/satpy/readers/smos_l2_wind.py +++ b/satpy/readers/smos_l2_wind.py @@ -67,7 +67,7 @@ def get_metadata(self, data, ds_info): metadata.update({ "platform_shortname": self.platform_shortname, "platform_name": self.platform_name, - "sensor": self["/attr/instrument"], + "instruments": {self["/attr/instrument"]}, "start_time": self.start_time, "end_time": self.end_time, "level": self["/attr/processing_level"], diff --git a/satpy/readers/tropomi_l2.py b/satpy/readers/tropomi_l2.py index e0e49a9e36..6c53e74b4f 100644 --- a/satpy/readers/tropomi_l2.py +++ b/satpy/readers/tropomi_l2.py @@ -77,8 +77,8 @@ def sensor(self): """Get sensor.""" res = self["/attr/sensor"] if isinstance(res, np.ndarray): - return str(res.astype(str)).lower() - return res.lower() + return str(res.astype(str)) + return res @property def sensor_names(self): @@ -174,7 +174,7 @@ def get_metadata(self, data, ds_info): metadata.update(ds_info) metadata.update({ "platform_shortname": self.platform_shortname, - "sensor": self.sensor, + "instruments": self.sensor_names, "start_time": self.start_time, "end_time": self.end_time, "time_coverage_start": self.time_coverage_start, diff --git a/satpy/readers/viirs_compact.py b/satpy/readers/viirs_compact.py index d27fa9d629..5a3905ee98 100644 --- a/satpy/readers/viirs_compact.py +++ b/satpy/readers/viirs_compact.py @@ -136,7 +136,7 @@ def __init__(self, filename, filename_info, filetype_info): self.mda = {} short_name = np2str(self.h5f.attrs["Platform_Short_Name"]) self.mda["platform_name"] = short_names.get(short_name, short_name) - self.mda["sensor"] = "viirs" + self.mda["instruments"] = {"VIIRS"} def __del__(self): """Close file handlers when we are done.""" diff --git a/satpy/readers/viirs_edr.py b/satpy/readers/viirs_edr.py index 18397c35bc..c5956aaf76 100644 --- a/satpy/readers/viirs_edr.py +++ b/satpy/readers/viirs_edr.py @@ -121,7 +121,7 @@ def __init__(self, filename, filename_info, filetype_info, **kwargs): self.nc["Longitude"].attrs.update({"standard_name": "longitude"}) self.algorithm_version = filename_info["platform_shortname"] - self.sensor_name = "viirs" + self.sensor_name = "VIIRS" def rows_per_scans(self, data_arr: xr.DataArray) -> int: """Get number of array rows per instrument scan based on data resolution.""" @@ -177,7 +177,7 @@ def _sanitize_metadata(self, data_arr: xr.DataArray, info: dict) -> xr.DataArray data_arr.attrs["standard_name"] = info["standard_name"] self._decode_flag_meanings(data_arr) data_arr.attrs["platform_name"] = self.platform_name - data_arr.attrs["sensor"] = self.sensor_name + data_arr.attrs["instruments"] = {self.sensor_name} data_arr.attrs["rows_per_scan"] = self.rows_per_scans(data_arr) return data_arr diff --git a/satpy/readers/viirs_edr_active_fires.py b/satpy/readers/viirs_edr_active_fires.py index 2d221334b3..5465651fb7 100644 --- a/satpy/readers/viirs_edr_active_fires.py +++ b/satpy/readers/viirs_edr_active_fires.py @@ -76,7 +76,7 @@ def get_dataset(self, dsid: DataID, dsinfo: dict) -> xr.DataArray: # noqa: D417 data.attrs["units"] = "K" data.attrs["platform_name"] = PLATFORM_MAP.get(self.filename_info["satellite_name"].upper(), "unknown") - data.attrs["sensor"] = self.sensor_name + data.attrs["instruments"] = {self.sensor_name} return data @@ -93,7 +93,7 @@ def end_time(self): @property def sensor_name(self): """Name of sensor for this file.""" - return self["/attr/instrument_name"].lower() + return self["/attr/instrument_name"] @property def platform_name(self): @@ -122,7 +122,7 @@ def __init__(self, filename, filename_info, filetype_info): def get_dataset(self, dsid, dsinfo): """Get requested data as DataArray.""" ds = self[dsid["name"]].to_dask_array(lengths=True) - data = xr.DataArray(ds, dims=("y",), attrs={"platform_name": self.platform_name, "sensor": "VIIRS"}) + data = xr.DataArray(ds, dims=("y",), attrs={"platform_name": self.platform_name, "instruments": {"VIIRS"}}) for key in ("units", "standard_name", "flag_meanings", "flag_values", "_FillValue"): # we only want to add information that isn't present already if key in dsinfo and key not in data.attrs: diff --git a/satpy/readers/viirs_edr_flood.py b/satpy/readers/viirs_edr_flood.py index c2815c8959..6f8c719022 100644 --- a/satpy/readers/viirs_edr_flood.py +++ b/satpy/readers/viirs_edr_flood.py @@ -41,8 +41,8 @@ def sensor_name(self): """Get sensor name.""" sensor = self["/attr/SensorIdentifyCode"] if isinstance(sensor, np.ndarray): - return str(sensor.astype(str)).lower() - return sensor.lower() + return str(sensor.astype(str)) + return sensor @property def platform_name(self): @@ -58,7 +58,7 @@ def get_metadata(self, data, ds_info): metadata.update(data.attrs) metadata.update(ds_info) metadata.update({ - "sensor": self.sensor_name, + "instruments": {self.sensor_name}, "platform_name": self.platform_name, "start_time": self.start_time, "end_time": self.end_time, diff --git a/satpy/readers/viirs_l1b.py b/satpy/readers/viirs_l1b.py index 5c4b5cacbc..388e247c0d 100644 --- a/satpy/readers/viirs_l1b.py +++ b/satpy/readers/viirs_l1b.py @@ -70,7 +70,7 @@ def platform_name(self): @property def sensor_name(self): """Get sensor name.""" - return self["/attr/instrument"].lower() + return self["/attr/instrument"] def adjust_scaling_factors(self, factors, file_units, output_units): """Adjust scaling factors.""" @@ -191,7 +191,7 @@ def get_metadata(self, dataset_id, ds_info): "units": ds_info.get("units", file_units), "file_units": file_units, "platform_name": self.platform_name, - "sensor": self.sensor_name, + "instruments": {self.sensor_name}, "day_night": self["/attr/DayNightFlag"], "orbital_parameters": orb_param, "start_orbit": self.start_orbit_number, diff --git a/satpy/readers/viirs_l2.py b/satpy/readers/viirs_l2.py index 13ac6784bc..bbfd880f13 100644 --- a/satpy/readers/viirs_l2.py +++ b/satpy/readers/viirs_l2.py @@ -71,7 +71,7 @@ def platform_name(self): @property def sensor_name(self): """Get sensor name.""" - return self["/attr/instrument"].lower() + return self["/attr/instrument"] def _get_dataset_file_units(self, ds_info, var_path): file_units = ds_info.get("units") @@ -106,7 +106,7 @@ def get_metadata(self, dataset_id, ds_info): { "file_units": file_units, "platform_name": self.platform_name, - "sensor": self.sensor_name, + "instruments": {self.sensor_name}, "start_orbit": self.start_orbit_number, "end_orbit": self.end_orbit_number, } diff --git a/satpy/readers/virr_l1b.py b/satpy/readers/virr_l1b.py index 37506b3340..703c14da59 100644 --- a/satpy/readers/virr_l1b.py +++ b/satpy/readers/virr_l1b.py @@ -118,7 +118,7 @@ def get_dataset(self, dataset_id, ds_info): data = data.rename(new_dims) # use lowercase sensor name to be consistent with the rest of satpy data.attrs.update({"platform_name": self["/attr/Satellite Name"], - "sensor": self["/attr/Sensor Identification Code"].lower()}) + "instruments": {self["/attr/Sensor Identification Code"]}}) data.attrs.update(ds_info) units = self.get(file_key + "/attr/units") if units is not None and str(units).lower() != "none": From ad763f249ae5eb65fcaf49e367060a3c8deab8e0 Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Wed, 13 May 2026 13:45:41 +0000 Subject: [PATCH 32/72] Replace sensor with instruments in file handlers --- satpy/etc/readers/ahi_hrit.yaml | 34 +- satpy/etc/readers/fci_l1c_nc.yaml | 418 +++++++++--------- satpy/etc/readers/grib.yaml | 2 +- satpy/etc/readers/iasi_l2.yaml | 2 +- satpy/etc/readers/mtsat2-imager_hrit.yaml | 2 +- satpy/readers/clavrx.py | 18 +- satpy/readers/eum_l2_bufr.py | 2 +- satpy/readers/eum_l2_grib.py | 2 +- satpy/readers/fci_l2_nc.py | 2 +- satpy/readers/geocat.py | 2 +- satpy/readers/gms/gms5_vissr_l1b.py | 2 +- satpy/readers/goes_imager_hrit.py | 2 +- satpy/readers/grib.py | 7 +- satpy/readers/mirs.py | 2 +- satpy/readers/mws_l1b.py | 1 + satpy/readers/nwcsaf_hrw_nc.py | 2 +- satpy/readers/nwcsaf_nc.py | 2 +- satpy/readers/osisaf_l3_nc.py | 16 +- satpy/readers/virr_l1b.py | 1 - satpy/tests/compositor_tests/test_aux_data.py | 4 +- satpy/tests/compositor_tests/test_core.py | 14 +- satpy/tests/compositor_tests/test_fill.py | 6 +- .../reader_tests/gms/test_gms5_vissr_l1b.py | 2 +- .../modis_tests/test_modis_l1b.py | 2 +- .../reader_tests/modis_tests/test_modis_l2.py | 2 +- .../modis_tests/test_modis_l3_mcd12q1.py | 2 +- .../reader_tests/seviri_l1b_hrit_setup.py | 2 +- satpy/tests/reader_tests/test_abi_l1b.py | 2 +- satpy/tests/reader_tests/test_abi_l2_nc.py | 4 +- satpy/tests/reader_tests/test_acspo.py | 2 +- satpy/tests/reader_tests/test_agri_l1.py | 2 +- satpy/tests/reader_tests/test_ahi_hrit.py | 10 +- satpy/tests/reader_tests/test_ahi_l2_nc.py | 2 +- satpy/tests/reader_tests/test_ami_l1b.py | 6 +- satpy/tests/reader_tests/test_amsr2_l1b.py | 2 +- .../tests/reader_tests/test_amsr2_l2_gaasp.py | 2 +- satpy/tests/reader_tests/test_atms_l1b_nc.py | 6 +- satpy/tests/reader_tests/test_aws1_mwr_l1b.py | 4 +- satpy/tests/reader_tests/test_aws1_mwr_l1c.py | 2 +- .../test_clavrx/test_clavrx_geohdf.py | 2 +- .../test_clavrx/test_clavrx_nc.py | 2 +- .../test_clavrx/test_clavrx_polarhdf.py | 2 +- .../tests/reader_tests/test_electrol_hrit.py | 2 +- satpy/tests/reader_tests/test_eps_l1b.py | 12 +- .../reader_tests/test_eps_sterna_mwr_l1b.py | 2 +- satpy/tests/reader_tests/test_eum_l2_grib.py | 4 +- satpy/tests/reader_tests/test_fci_l1c_nc.py | 10 +- satpy/tests/reader_tests/test_fci_l2_nc.py | 6 +- satpy/tests/reader_tests/test_ghi_l1.py | 2 +- satpy/tests/reader_tests/test_ghrsst_l2.py | 2 +- satpy/tests/reader_tests/test_glm_l2.py | 4 +- .../reader_tests/test_goes_imager_nc_noaa.py | 2 +- satpy/tests/reader_tests/test_iasi_l2.py | 4 +- .../tests/reader_tests/test_iasi_ng_l2_nc.py | 2 +- satpy/tests/reader_tests/test_ici_l1b_nc.py | 2 +- .../reader_tests/test_insat3d_img_l1b_h5.py | 2 +- satpy/tests/reader_tests/test_li_l2_nc.py | 6 +- .../reader_tests/test_mimic_TPW2_lowres.py | 6 +- .../tests/reader_tests/test_mimic_TPW2_nc.py | 2 +- .../reader_tests/test_mviri_l1b_fiduceo_nc.py | 4 +- satpy/tests/reader_tests/test_mws_l1b_nc.py | 2 +- satpy/tests/reader_tests/test_nucaps.py | 4 +- .../tests/reader_tests/test_nwcsaf_hrw_nc.py | 4 +- satpy/tests/reader_tests/test_nwcsaf_nc.py | 14 +- .../reader_tests/test_oceancolorcci_l3_nc.py | 8 +- satpy/tests/reader_tests/test_oci_l2_bgc.py | 2 +- satpy/tests/reader_tests/test_olci_nc.py | 2 +- satpy/tests/reader_tests/test_osisaf_l3.py | 2 + satpy/tests/reader_tests/test_satpy_cf_nc.py | 4 +- satpy/tests/reader_tests/test_seadas_l2.py | 10 +- .../reader_tests/test_seviri_l1b_native.py | 2 +- .../tests/reader_tests/test_seviri_l1b_nc.py | 2 +- satpy/tests/reader_tests/test_sgli_l1b.py | 2 +- satpy/tests/reader_tests/test_smos_l2_wind.py | 2 +- satpy/tests/reader_tests/test_tropomi_l2.py | 2 +- satpy/tests/reader_tests/test_vii_base_nc.py | 2 +- satpy/tests/reader_tests/test_viirs_edr.py | 2 +- .../test_viirs_edr_active_fires.py | 8 +- satpy/tests/reader_tests/test_viirs_l1b.py | 14 +- satpy/tests/reader_tests/test_viirs_l2.py | 2 +- satpy/tests/reader_tests/test_virr_l1b.py | 2 +- .../tests/writer_tests/test_core/test_base.py | 6 +- 82 files changed, 390 insertions(+), 385 deletions(-) diff --git a/satpy/etc/readers/ahi_hrit.yaml b/satpy/etc/readers/ahi_hrit.yaml index cb9411044e..239ebd0bc1 100644 --- a/satpy/etc/readers/ahi_hrit.yaml +++ b/satpy/etc/readers/ahi_hrit.yaml @@ -9,7 +9,7 @@ reader: description: Reader for the JMA Himawari AHI Level 1 data in HRIT format status: Nominal supports_fsspec: false - sensors: [ahi] + instruments: [AHI] reader: !!python/name:satpy.readers.core.yaml_reader.GEOSegmentYAMLReader # file pattern keys to sort files by with 'satpy.utils.group_files' group_keys: ['start_time', 'area'] @@ -228,7 +228,7 @@ file_types: datasets: B01: name: B01 - sensor: ahi + instruments: [AHI] wavelength: [0.45,0.47,0.49] resolution: 1000 calibration: @@ -242,7 +242,7 @@ datasets: B02: name: B02 - sensor: ahi + instruments: [AHI] wavelength: [0.49,0.51,0.53] resolution: 1000 calibration: @@ -256,7 +256,7 @@ datasets: B03: name: B03 - sensor: ahi + instruments: [AHI] wavelength: [0.62,0.64,0.66] resolution: 1000 calibration: @@ -270,7 +270,7 @@ datasets: B04: name: B04 - sensor: ahi + instruments: [AHI] wavelength: [0.85, 0.86, 0.87] resolution: 4000 calibration: @@ -284,7 +284,7 @@ datasets: B05: name: B05 - sensor: ahi + instruments: [AHI] wavelength: [1.5, 1.6, 1.7] resolution: 4000 calibration: @@ -298,7 +298,7 @@ datasets: B06: name: B06 - sensor: ahi + instruments: [AHI] wavelength: [2.2, 2.3, 2.4] resolution: 4000 calibration: @@ -315,7 +315,7 @@ datasets: resolution: 4000: {file_type: [hrit_b07_ir4_seg, hrit_b07_ir4_fd]} 2000: {file_type: [hrit_b07_seg, hrit_b07_fd]} - sensor: ahi + instruments: [AHI] wavelength: [3.7, 3.9, 4.1] calibration: brightness_temperature: @@ -327,7 +327,7 @@ datasets: B08: name: B08 - sensor: ahi + instruments: [AHI] wavelength: [6.0, 6.2, 6.4] resolution: 4000 calibration: @@ -341,7 +341,7 @@ datasets: B09: name: B09 - sensor: ahi + instruments: [AHI] wavelength: [6.7, 6.9, 7.1] resolution: 4000 calibration: @@ -355,7 +355,7 @@ datasets: B10: name: B10 - sensor: ahi + instruments: [AHI] wavelength: [7.1, 7.3, 7.5] resolution: 4000 calibration: @@ -369,7 +369,7 @@ datasets: B11: name: B11 - sensor: ahi + instruments: [AHI] wavelength: [8.4, 8.6, 8.8] resolution: 4000 calibration: @@ -383,7 +383,7 @@ datasets: B12: name: B12 - sensor: ahi + instruments: [AHI] wavelength: [9.4, 9.6, 9.8] resolution: 4000 calibration: @@ -397,7 +397,7 @@ datasets: B13: name: B13 - sensor: ahi + instruments: [AHI] wavelength: [10.2, 10.4, 10.6] resolution: 4000 calibration: @@ -411,7 +411,7 @@ datasets: B14: name: B14 - sensor: ahi + instruments: [AHI] wavelength: [11.0, 11.2, 11.4] resolution: 4000 calibration: @@ -425,7 +425,7 @@ datasets: B15: name: B15 - sensor: ahi + instruments: [AHI] wavelength: [12.2, 12.4, 12.6] resolution: 4000 calibration: @@ -439,7 +439,7 @@ datasets: B16: name: B16 - sensor: ahi + instruments: [AHI] wavelength: [13.1, 13.3, 13.5] resolution: 4000 calibration: diff --git a/satpy/etc/readers/fci_l1c_nc.yaml b/satpy/etc/readers/fci_l1c_nc.yaml index bc481b542a..c3f447d599 100644 --- a/satpy/etc/readers/fci_l1c_nc.yaml +++ b/satpy/etc/readers/fci_l1c_nc.yaml @@ -9,7 +9,7 @@ reader: status: Beta for full-disc and RSS FDHSI, HRFI, African dissemination format, special scans, IDPF-I and IQT-I processing facilities. supports_fsspec: true reader: !!python/name:satpy.readers.core.yaml_reader.GEOVariableSegmentYAMLReader - sensors: [fci] + instruments: [FCI] # Source: MTG FCI L1 Product User Guide [FCIL1PUG] # https://www.eumetsat.int/media/45923 @@ -254,7 +254,7 @@ file_types: datasets: vis_04: name: vis_04 - sensor: fci + instruments: [FCI] wavelength: [0.384, 0.444, 0.504] resolution: 1000: { file_type: fci_l1c_fdhsi } @@ -272,7 +272,7 @@ datasets: vis_05: name: vis_05 - sensor: fci + instruments: [FCI] wavelength: [0.470, 0.510, 0.550] resolution: 1000: { file_type: fci_l1c_fdhsi } @@ -290,7 +290,7 @@ datasets: vis_06: name: vis_06 - sensor: fci + instruments: [FCI] wavelength: [0.590, 0.640, 0.690] resolution: 500: { file_type: fci_l1c_hrfi } @@ -309,7 +309,7 @@ datasets: vis_08: name: vis_08 - sensor: fci + instruments: [FCI] wavelength: [0.815, 0.865, 0.915] resolution: 1000: { file_type: fci_l1c_fdhsi } @@ -327,7 +327,7 @@ datasets: vis_09: name: vis_09 - sensor: fci + instruments: [FCI] wavelength: [0.894, 0.914, 0.934] resolution: 1000: { file_type: fci_l1c_fdhsi } @@ -345,7 +345,7 @@ datasets: nir_13: name: nir_13 - sensor: fci + instruments: [FCI] wavelength: [1.350, 1.380, 1.410] resolution: 1000: { file_type: fci_l1c_fdhsi } @@ -363,7 +363,7 @@ datasets: nir_16: name: nir_16 - sensor: fci + instruments: [FCI] wavelength: [1.560, 1.610, 1.660] resolution: 1000: { file_type: fci_l1c_fdhsi } @@ -381,7 +381,7 @@ datasets: nir_22: name: nir_22 - sensor: fci + instruments: [FCI] wavelength: [2.200, 2.250, 2.300] resolution: 500: { file_type: fci_l1c_hrfi } @@ -400,7 +400,7 @@ datasets: ir_38: name: ir_38 - sensor: fci + instruments: [FCI] wavelength: [3.400, 3.800, 4.200] resolution: 1000: { file_type: fci_l1c_hrfi } @@ -419,7 +419,7 @@ datasets: wv_63: name: wv_63 - sensor: fci + instruments: [FCI] wavelength: [5.300, 6.300, 7.300] resolution: 2000: { file_type: fci_l1c_fdhsi } @@ -437,7 +437,7 @@ datasets: wv_73: name: wv_73 - sensor: fci + instruments: [FCI] wavelength: [6.850, 7.350, 7.850] resolution: 2000: { file_type: fci_l1c_fdhsi } @@ -455,7 +455,7 @@ datasets: ir_87: name: ir_87 - sensor: fci + instruments: [FCI] wavelength: [8.300, 8.700, 9.100] resolution: 2000: { file_type: fci_l1c_fdhsi } @@ -473,7 +473,7 @@ datasets: ir_97: name: ir_97 - sensor: fci + instruments: [FCI] wavelength: [9.360, 9.660, 9.960] resolution: 2000: { file_type: fci_l1c_fdhsi } @@ -491,7 +491,7 @@ datasets: ir_105: name: ir_105 - sensor: fci + instruments: [FCI] wavelength: [9.800, 10.500, 11.200] resolution: 1000: { file_type: fci_l1c_hrfi } @@ -510,7 +510,7 @@ datasets: ir_123: name: ir_123 - sensor: fci + instruments: [FCI] wavelength: [11.800, 12.300, 12.800] resolution: 2000: { file_type: fci_l1c_fdhsi } @@ -528,7 +528,7 @@ datasets: ir_133: name: ir_133 - sensor: fci + instruments: [FCI] wavelength: [12.700, 13.300, 13.900] resolution: 2000: { file_type: fci_l1c_fdhsi } @@ -546,21 +546,21 @@ datasets: vis_04_pixel_quality: name: vis_04_pixel_quality - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_04 } vis_05_pixel_quality: name: vis_05_pixel_quality - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_05 } vis_06_pixel_quality: name: vis_06_pixel_quality - sensor: fci + instruments: [FCI] resolution: 500: { file_type: fci_l1c_hrfi } 1000: { file_type: [fci_l1c_fdhsi, fci_l1c_af_vis_06_1km] } @@ -568,35 +568,35 @@ datasets: vis_08_pixel_quality: name: vis_08_pixel_quality - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_08 } vis_09_pixel_quality: name: vis_09_pixel_quality - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_09 } nir_13_pixel_quality: name: nir_13_pixel_quality - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_nir_13 } nir_16_pixel_quality: name: nir_16_pixel_quality - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_nir_16 } nir_22_pixel_quality: name: nir_22_pixel_quality - sensor: fci + instruments: [FCI] resolution: 500: { file_type: fci_l1c_hrfi } 1000: { file_type: fci_l1c_fdhsi } @@ -604,7 +604,7 @@ datasets: ir_38_pixel_quality: name: ir_38_pixel_quality - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_hrfi } 2000: { file_type: fci_l1c_fdhsi } @@ -612,35 +612,35 @@ datasets: wv_63_pixel_quality: name: wv_63_pixel_quality - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_wv_63 } wv_73_pixel_quality: name: wv_73_pixel_quality - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_wv_73 } ir_87_pixel_quality: name: ir_87_pixel_quality - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_87 } ir_97_pixel_quality: name: ir_97_pixel_quality - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_97 } ir_105_pixel_quality: name: ir_105_pixel_quality - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_hrfi } 2000: { file_type: fci_l1c_fdhsi } @@ -648,35 +648,35 @@ datasets: ir_123_pixel_quality: name: ir_123_pixel_quality - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_123 } ir_133_pixel_quality: name: ir_133_pixel_quality - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_133 } vis_04_index_map: name: vis_04_index_map - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_04 } vis_05_index_map: name: vis_05_index_map - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_05 } vis_06_index_map: name: vis_06_index_map - sensor: fci + instruments: [FCI] resolution: 500: { file_type: fci_l1c_hrfi } 1000: { file_type: [fci_l1c_fdhsi, fci_l1c_af_vis_06_1km] } @@ -684,35 +684,35 @@ datasets: vis_08_index_map: name: vis_08_index_map - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_08 } vis_09_index_map: name: vis_09_index_map - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_09 } nir_13_index_map: name: nir_13_index_map - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_nir_13 } nir_16_index_map: name: nir_16_index_map - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_nir_16 } nir_22_index_map: name: nir_22_index_map - sensor: fci + instruments: [FCI] resolution: 500: { file_type: fci_l1c_hrfi } 1000: { file_type: fci_l1c_fdhsi } @@ -720,7 +720,7 @@ datasets: ir_38_index_map: name: ir_38_index_map - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_hrfi } 2000: { file_type: fci_l1c_fdhsi } @@ -728,35 +728,35 @@ datasets: wv_63_index_map: name: wv_63_index_map - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_wv_63 } wv_73_index_map: name: wv_73_index_map - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_wv_73 } ir_87_index_map: name: ir_87_index_map - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_87 } ir_97_index_map: name: ir_97_index_map - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_97 } ir_105_index_map: name: ir_105_index_map - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_hrfi } 2000: { file_type: fci_l1c_fdhsi } @@ -764,14 +764,14 @@ datasets: ir_123_index_map: name: ir_123_index_map - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_123 } ir_133_index_map: name: ir_133_index_map - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_133 } @@ -779,7 +779,7 @@ datasets: vis_04_time: name: vis_04_time units: s - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_04 } @@ -787,7 +787,7 @@ datasets: vis_05_time: name: vis_05_time units: s - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_05 } @@ -795,7 +795,7 @@ datasets: vis_06_time: name: vis_06_time units: s - sensor: fci + instruments: [FCI] resolution: 500: { file_type: fci_l1c_hrfi } 1000: { file_type: [fci_l1c_fdhsi, fci_l1c_af_vis_06_1km] } @@ -804,7 +804,7 @@ datasets: vis_08_time: name: vis_08_time units: s - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_08 } @@ -812,7 +812,7 @@ datasets: vis_09_time: name: vis_09_time units: s - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_09 } @@ -820,7 +820,7 @@ datasets: nir_13_time: name: nir_13_time units: s - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_nir_13 } @@ -828,7 +828,7 @@ datasets: nir_16_time: name: nir_16_time units: s - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_nir_16 } @@ -836,7 +836,7 @@ datasets: nir_22_time: name: nir_22_time units: s - sensor: fci + instruments: [FCI] resolution: 500: { file_type: fci_l1c_hrfi } 1000: { file_type: fci_l1c_fdhsi } @@ -845,7 +845,7 @@ datasets: ir_38_time: name: ir_38_time units: s - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_hrfi } 2000: { file_type: fci_l1c_fdhsi } @@ -854,7 +854,7 @@ datasets: wv_63_time: name: wv_63_time units: s - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_wv_63 } @@ -862,7 +862,7 @@ datasets: wv_73_time: name: wv_73_time units: s - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_wv_73 } @@ -870,7 +870,7 @@ datasets: ir_87_time: name: ir_87_time units: s - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_87 } @@ -878,7 +878,7 @@ datasets: ir_97_time: name: ir_97_time units: s - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_97 } @@ -886,7 +886,7 @@ datasets: ir_105_time: name: ir_105_time units: s - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_hrfi } 2000: { file_type: fci_l1c_fdhsi } @@ -895,7 +895,7 @@ datasets: ir_123_time: name: ir_123_time units: s - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_123 } @@ -903,28 +903,28 @@ datasets: ir_133_time: name: ir_133_time units: s - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_133 } vis_04_swath_direction: name: vis_04_swath_direction - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_04 } vis_05_swath_direction: name: vis_05_swath_direction - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_05 } vis_06_swath_direction: name: vis_06_swath_direction - sensor: fci + instruments: [FCI] resolution: 500: { file_type: fci_l1c_hrfi } 1000: { file_type: [fci_l1c_fdhsi, fci_l1c_af_vis_06_1km] } @@ -932,35 +932,35 @@ datasets: vis_08_swath_direction: name: vis_08_swath_direction - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_08 } vis_09_swath_direction: name: vis_09_swath_direction - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_09 } nir_13_swath_direction: name: nir_13_swath_direction - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_nir_13 } nir_16_swath_direction: name: nir_16_swath_direction - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_nir_16 } nir_22_swath_direction: name: nir_22_swath_direction - sensor: fci + instruments: [FCI] resolution: 500: { file_type: fci_l1c_hrfi } 1000: { file_type: fci_l1c_fdhsi } @@ -968,7 +968,7 @@ datasets: ir_38_swath_direction: name: ir_38_swath_direction - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_hrfi } 2000: { file_type: fci_l1c_fdhsi } @@ -976,35 +976,35 @@ datasets: wv_63_swath_direction: name: wv_63_swath_direction - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_wv_63 } wv_73_swath_direction: name: wv_73_swath_direction - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_wv_73 } ir_87_swath_direction: name: ir_87_swath_direction - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_87 } ir_97_swath_direction: name: ir_97_swath_direction - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_97 } ir_105_swath_direction: name: ir_105_swath_direction - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_hrfi } 2000: { file_type: fci_l1c_fdhsi } @@ -1012,35 +1012,35 @@ datasets: ir_123_swath_direction: name: ir_123_swath_direction - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_123 } ir_133_swath_direction: name: ir_133_swath_direction - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_133 } vis_04_swath_number: name: vis_04_swath_number - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_04 } vis_05_swath_number: name: vis_05_swath_number - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_05 } vis_06_swath_number: name: vis_06_swath_number - sensor: fci + instruments: [FCI] resolution: 500: { file_type: fci_l1c_hrfi } 1000: { file_type: [fci_l1c_fdhsi, fci_l1c_af_vis_06_1km] } @@ -1048,35 +1048,35 @@ datasets: vis_08_swath_number: name: vis_08_swath_number - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_08 } vis_09_swath_number: name: vis_09_swath_number - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_09 } nir_13_swath_number: name: nir_13_swath_number - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_nir_13 } nir_16_swath_number: name: nir_16_swath_number - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_nir_16 } nir_22_swath_number: name: nir_22_swath_number - sensor: fci + instruments: [FCI] resolution: 500: { file_type: fci_l1c_hrfi } 1000: { file_type: fci_l1c_fdhsi } @@ -1084,7 +1084,7 @@ datasets: ir_38_swath_number: name: ir_38_swath_number - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_hrfi } 2000: { file_type: fci_l1c_fdhsi } @@ -1092,35 +1092,35 @@ datasets: wv_63_swath_number: name: wv_63_swath_number - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_wv_63 } wv_73_swath_number: name: wv_73_swath_number - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_wv_73 } ir_87_swath_number: name: ir_87_swath_number - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_87 } ir_97_swath_number: name: ir_97_swath_number - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_97 } ir_105_swath_number: name: ir_105_swath_number - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_hrfi } 2000: { file_type: fci_l1c_fdhsi } @@ -1128,14 +1128,14 @@ datasets: ir_123_swath_number: name: ir_123_swath_number - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_123 } ir_133_swath_number: name: ir_133_swath_number - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_133 } @@ -1143,7 +1143,7 @@ datasets: vis_04_subsatellite_latitude: name: vis_04_subsatellite_latitude units: deg - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_04 } @@ -1151,7 +1151,7 @@ datasets: vis_05_subsatellite_latitude: name: vis_05_subsatellite_latitude units: deg - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_05 } @@ -1159,7 +1159,7 @@ datasets: vis_06_subsatellite_latitude: name: vis_06_subsatellite_latitude units: deg - sensor: fci + instruments: [FCI] resolution: 500: { file_type: fci_l1c_hrfi } 1000: { file_type: [fci_l1c_fdhsi, fci_l1c_af_vis_06_1km] } @@ -1168,7 +1168,7 @@ datasets: vis_08_subsatellite_latitude: name: vis_08_subsatellite_latitude units: deg - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_08 } @@ -1176,7 +1176,7 @@ datasets: vis_09_subsatellite_latitude: name: vis_09_subsatellite_latitude units: deg - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_09 } @@ -1184,7 +1184,7 @@ datasets: nir_13_subsatellite_latitude: name: nir_13_subsatellite_latitude units: deg - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_nir_13 } @@ -1192,7 +1192,7 @@ datasets: nir_16_subsatellite_latitude: name: nir_16_subsatellite_latitude units: deg - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_nir_16 } @@ -1200,7 +1200,7 @@ datasets: nir_22_subsatellite_latitude: name: nir_22_subsatellite_latitude units: deg - sensor: fci + instruments: [FCI] resolution: 500: { file_type: fci_l1c_hrfi } 1000: { file_type: fci_l1c_fdhsi } @@ -1209,7 +1209,7 @@ datasets: ir_38_subsatellite_latitude: name: ir_38_subsatellite_latitude units: deg - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_hrfi } 2000: { file_type: fci_l1c_fdhsi } @@ -1218,7 +1218,7 @@ datasets: wv_63_subsatellite_latitude: name: wv_63_subsatellite_latitude units: deg - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_wv_63 } @@ -1226,7 +1226,7 @@ datasets: wv_73_subsatellite_latitude: name: wv_73_subsatellite_latitude units: deg - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_wv_73 } @@ -1234,7 +1234,7 @@ datasets: ir_87_subsatellite_latitude: name: ir_87_subsatellite_latitude units: deg - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_87 } @@ -1242,7 +1242,7 @@ datasets: ir_97_subsatellite_latitude: name: ir_97_subsatellite_latitude units: deg - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_97 } @@ -1250,7 +1250,7 @@ datasets: ir_105_subsatellite_latitude: name: ir_105_subsatellite_latitude units: deg - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_hrfi } 2000: { file_type: fci_l1c_fdhsi } @@ -1259,7 +1259,7 @@ datasets: ir_123_subsatellite_latitude: name: ir_123_subsatellite_latitude units: deg - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_123 } @@ -1267,7 +1267,7 @@ datasets: ir_133_subsatellite_latitude: name: ir_133_subsatellite_latitude units: deg - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_133 } @@ -1275,7 +1275,7 @@ datasets: vis_04_subsatellite_longitude: name: vis_04_subsatellite_longitude units: deg - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_04 } @@ -1283,7 +1283,7 @@ datasets: vis_05_subsatellite_longitude: name: vis_05_subsatellite_longitude units: deg - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_05 } @@ -1291,7 +1291,7 @@ datasets: vis_06_subsatellite_longitude: name: vis_06_subsatellite_longitude units: deg - sensor: fci + instruments: [FCI] resolution: 500: { file_type: fci_l1c_hrfi } 1000: { file_type: [fci_l1c_fdhsi, fci_l1c_af_vis_06_1km] } @@ -1300,7 +1300,7 @@ datasets: vis_08_subsatellite_longitude: name: vis_08_subsatellite_longitude units: deg - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_08 } @@ -1308,7 +1308,7 @@ datasets: vis_09_subsatellite_longitude: name: vis_09_subsatellite_longitude units: deg - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_09 } @@ -1316,7 +1316,7 @@ datasets: nir_13_subsatellite_longitude: name: nir_13_subsatellite_longitude units: deg - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_nir_13 } @@ -1324,7 +1324,7 @@ datasets: nir_16_subsatellite_longitude: name: nir_16_subsatellite_longitude units: deg - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_nir_16 } @@ -1332,7 +1332,7 @@ datasets: nir_22_subsatellite_longitude: name: nir_22_subsatellite_longitude units: deg - sensor: fci + instruments: [FCI] resolution: 500: { file_type: fci_l1c_hrfi } 1000: { file_type: fci_l1c_fdhsi } @@ -1341,7 +1341,7 @@ datasets: ir_38_subsatellite_longitude: name: ir_38_subsatellite_longitude units: deg - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_hrfi } 2000: { file_type: fci_l1c_fdhsi } @@ -1350,7 +1350,7 @@ datasets: wv_63_subsatellite_longitude: name: wv_63_subsatellite_longitude units: deg - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_wv_63 } @@ -1358,7 +1358,7 @@ datasets: wv_73_subsatellite_longitude: name: wv_73_subsatellite_longitude units: deg - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_wv_73 } @@ -1366,7 +1366,7 @@ datasets: ir_87_subsatellite_longitude: name: ir_87_subsatellite_longitude units: deg - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_87 } @@ -1374,7 +1374,7 @@ datasets: ir_97_subsatellite_longitude: name: ir_97_subsatellite_longitude units: deg - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_97 } @@ -1382,7 +1382,7 @@ datasets: ir_105_subsatellite_longitude: name: ir_105_subsatellite_longitude units: deg - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_hrfi } 2000: { file_type: fci_l1c_fdhsi } @@ -1391,7 +1391,7 @@ datasets: ir_123_subsatellite_longitude: name: ir_123_subsatellite_longitude units: deg - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_123 } @@ -1399,7 +1399,7 @@ datasets: ir_133_subsatellite_longitude: name: ir_133_subsatellite_longitude units: deg - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_133 } @@ -1407,7 +1407,7 @@ datasets: vis_04_subsolar_latitude: name: vis_04_subsolar_latitude units: deg - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_04 } @@ -1415,7 +1415,7 @@ datasets: vis_05_subsolar_latitude: name: vis_05_subsolar_latitude units: deg - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_05 } @@ -1423,7 +1423,7 @@ datasets: vis_06_subsolar_latitude: name: vis_06_subsolar_latitude units: deg - sensor: fci + instruments: [FCI] resolution: 500: { file_type: fci_l1c_hrfi } 1000: { file_type: [fci_l1c_fdhsi, fci_l1c_af_vis_06_1km] } @@ -1432,7 +1432,7 @@ datasets: vis_08_subsolar_latitude: name: vis_08_subsolar_latitude units: deg - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_08 } @@ -1440,7 +1440,7 @@ datasets: vis_09_subsolar_latitude: name: vis_09_subsolar_latitude units: deg - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_09 } @@ -1448,7 +1448,7 @@ datasets: nir_13_subsolar_latitude: name: nir_13_subsolar_latitude units: deg - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_nir_13 } @@ -1456,7 +1456,7 @@ datasets: nir_16_subsolar_latitude: name: nir_16_subsolar_latitude units: deg - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_nir_16 } @@ -1464,7 +1464,7 @@ datasets: nir_22_subsolar_latitude: name: nir_22_subsolar_latitude units: deg - sensor: fci + instruments: [FCI] resolution: 500: { file_type: fci_l1c_hrfi } 1000: { file_type: fci_l1c_fdhsi } @@ -1473,7 +1473,7 @@ datasets: ir_38_subsolar_latitude: name: ir_38_subsolar_latitude units: deg - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_hrfi } 2000: { file_type: fci_l1c_fdhsi } @@ -1482,7 +1482,7 @@ datasets: wv_63_subsolar_latitude: name: wv_63_subsolar_latitude units: deg - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_wv_63 } @@ -1490,7 +1490,7 @@ datasets: wv_73_subsolar_latitude: name: wv_73_subsolar_latitude units: deg - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_wv_73 } @@ -1498,7 +1498,7 @@ datasets: ir_87_subsolar_latitude: name: ir_87_subsolar_latitude units: deg - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_87 } @@ -1506,7 +1506,7 @@ datasets: ir_97_subsolar_latitude: name: ir_97_subsolar_latitude units: deg - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_97 } @@ -1514,7 +1514,7 @@ datasets: ir_105_subsolar_latitude: name: ir_105_subsolar_latitude units: deg - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_hrfi } 2000: { file_type: fci_l1c_fdhsi } @@ -1523,7 +1523,7 @@ datasets: ir_123_subsolar_latitude: name: ir_123_subsolar_latitude units: deg - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_123 } @@ -1531,7 +1531,7 @@ datasets: ir_133_subsolar_latitude: name: ir_133_subsolar_latitude units: deg - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_133 } @@ -1539,7 +1539,7 @@ datasets: vis_04_subsolar_longitude: name: vis_04_subsolar_longitude units: deg - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_04 } @@ -1547,7 +1547,7 @@ datasets: vis_05_subsolar_longitude: name: vis_05_subsolar_longitude units: deg - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_05 } @@ -1555,7 +1555,7 @@ datasets: vis_06_subsolar_longitude: name: vis_06_subsolar_longitude units: deg - sensor: fci + instruments: [FCI] resolution: 500: { file_type: fci_l1c_hrfi } 1000: { file_type: [fci_l1c_fdhsi, fci_l1c_af_vis_06_1km] } @@ -1564,7 +1564,7 @@ datasets: vis_08_subsolar_longitude: name: vis_08_subsolar_longitude units: deg - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_08 } @@ -1572,7 +1572,7 @@ datasets: vis_09_subsolar_longitude: name: vis_09_subsolar_longitude units: deg - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_09 } @@ -1580,7 +1580,7 @@ datasets: nir_13_subsolar_longitude: name: nir_13_subsolar_longitude units: deg - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_nir_13 } @@ -1588,7 +1588,7 @@ datasets: nir_16_subsolar_longitude: name: nir_16_subsolar_longitude units: deg - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_nir_16 } @@ -1596,7 +1596,7 @@ datasets: nir_22_subsolar_longitude: name: nir_22_subsolar_longitude units: deg - sensor: fci + instruments: [FCI] resolution: 500: { file_type: fci_l1c_hrfi } 1000: { file_type: fci_l1c_fdhsi } @@ -1605,7 +1605,7 @@ datasets: ir_38_subsolar_longitude: name: ir_38_subsolar_longitude units: deg - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_hrfi } 2000: { file_type: fci_l1c_fdhsi } @@ -1614,7 +1614,7 @@ datasets: wv_63_subsolar_longitude: name: wv_63_subsolar_longitude units: deg - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_wv_63 } @@ -1622,7 +1622,7 @@ datasets: wv_73_subsolar_longitude: name: wv_73_subsolar_longitude units: deg - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_wv_73 } @@ -1630,7 +1630,7 @@ datasets: ir_87_subsolar_longitude: name: ir_87_subsolar_longitude units: deg - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_87 } @@ -1638,7 +1638,7 @@ datasets: ir_97_subsolar_longitude: name: ir_97_subsolar_longitude units: deg - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_97 } @@ -1646,7 +1646,7 @@ datasets: ir_105_subsolar_longitude: name: ir_105_subsolar_longitude units: deg - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_hrfi } 2000: { file_type: fci_l1c_fdhsi } @@ -1655,7 +1655,7 @@ datasets: ir_123_subsolar_longitude: name: ir_123_subsolar_longitude units: deg - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_123 } @@ -1663,7 +1663,7 @@ datasets: ir_133_subsolar_longitude: name: ir_133_subsolar_longitude units: deg - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_133 } @@ -1671,7 +1671,7 @@ datasets: vis_04_platform_altitude: name: vis_04_platform_altitude units: m - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_04 } @@ -1679,7 +1679,7 @@ datasets: vis_05_platform_altitude: name: vis_05_platform_altitude units: m - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_05 } @@ -1687,7 +1687,7 @@ datasets: vis_06_platform_altitude: name: vis_06_platform_altitude units: m - sensor: fci + instruments: [FCI] resolution: 500: { file_type: fci_l1c_hrfi } 1000: { file_type: [fci_l1c_fdhsi, fci_l1c_af_vis_06_1km] } @@ -1696,7 +1696,7 @@ datasets: vis_08_platform_altitude: name: vis_08_platform_altitude units: m - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_08 } @@ -1704,7 +1704,7 @@ datasets: vis_09_platform_altitude: name: vis_09_platform_altitude units: m - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_09 } @@ -1712,7 +1712,7 @@ datasets: nir_13_platform_altitude: name: nir_13_platform_altitude units: m - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_nir_13 } @@ -1720,7 +1720,7 @@ datasets: nir_16_platform_altitude: name: nir_16_platform_altitude units: m - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_nir_16 } @@ -1728,7 +1728,7 @@ datasets: nir_22_platform_altitude: name: nir_22_platform_altitude units: m - sensor: fci + instruments: [FCI] resolution: 500: { file_type: fci_l1c_hrfi } 1000: { file_type: fci_l1c_fdhsi } @@ -1737,7 +1737,7 @@ datasets: ir_38_platform_altitude: name: ir_38_platform_altitude units: m - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_hrfi } 2000: { file_type: fci_l1c_fdhsi } @@ -1746,7 +1746,7 @@ datasets: wv_63_platform_altitude: name: wv_63_platform_altitude units: m - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_wv_63 } @@ -1754,7 +1754,7 @@ datasets: wv_73_platform_altitude: name: wv_73_platform_altitude units: m - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_wv_73 } @@ -1762,7 +1762,7 @@ datasets: ir_87_platform_altitude: name: ir_87_platform_altitude units: m - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_87 } @@ -1770,7 +1770,7 @@ datasets: ir_97_platform_altitude: name: ir_97_platform_altitude units: m - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_97 } @@ -1778,7 +1778,7 @@ datasets: ir_105_platform_altitude: name: ir_105_platform_altitude units: m - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_hrfi } 2000: { file_type: fci_l1c_fdhsi } @@ -1787,7 +1787,7 @@ datasets: ir_123_platform_altitude: name: ir_123_platform_altitude units: m - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_123 } @@ -1795,7 +1795,7 @@ datasets: ir_133_platform_altitude: name: ir_133_platform_altitude units: m - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_133 } @@ -1803,7 +1803,7 @@ datasets: vis_04_earth_sun_distance: name: vis_04_earth_sun_distance units: km - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: [fci_l1c_af_vis_04, fci_l1c_fdhsi] } 3000: { file_type: fci_l1c_af_vis_04 } @@ -1811,7 +1811,7 @@ datasets: vis_05_earth_sun_distance: name: vis_05_earth_sun_distance units: km - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_05 } @@ -1819,7 +1819,7 @@ datasets: vis_06_earth_sun_distance: name: vis_06_earth_sun_distance units: km - sensor: fci + instruments: [FCI] resolution: 500: { file_type: fci_l1c_hrfi } 1000: { file_type: [fci_l1c_fdhsi, fci_l1c_af_vis_06_1km] } @@ -1828,7 +1828,7 @@ datasets: vis_08_earth_sun_distance: name: vis_08_earth_sun_distance units: km - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_08 } @@ -1836,7 +1836,7 @@ datasets: vis_09_earth_sun_distance: name: vis_09_earth_sun_distance units: km - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_09 } @@ -1844,7 +1844,7 @@ datasets: nir_13_earth_sun_distance: name: nir_13_earth_sun_distance units: km - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_nir_13 } @@ -1852,7 +1852,7 @@ datasets: nir_16_earth_sun_distance: name: nir_16_earth_sun_distance units: km - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_nir_16 } @@ -1860,7 +1860,7 @@ datasets: nir_22_earth_sun_distance: name: nir_22_earth_sun_distance units: km - sensor: fci + instruments: [FCI] resolution: 500: { file_type: fci_l1c_hrfi } 1000: { file_type: fci_l1c_fdhsi } @@ -1869,7 +1869,7 @@ datasets: ir_38_earth_sun_distance: name: ir_38_earth_sun_distance units: km - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_hrfi } 2000: { file_type: fci_l1c_fdhsi } @@ -1878,7 +1878,7 @@ datasets: wv_63_earth_sun_distance: name: wv_63_earth_sun_distance units: km - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_wv_63 } @@ -1886,7 +1886,7 @@ datasets: wv_73_earth_sun_distance: name: wv_73_earth_sun_distance units: km - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_wv_73 } @@ -1894,7 +1894,7 @@ datasets: ir_87_earth_sun_distance: name: ir_87_earth_sun_distance units: km - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_87 } @@ -1902,7 +1902,7 @@ datasets: ir_97_earth_sun_distance: name: ir_97_earth_sun_distance units: km - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_97 } @@ -1910,7 +1910,7 @@ datasets: ir_105_earth_sun_distance: name: ir_105_earth_sun_distance units: km - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_hrfi } 2000: { file_type: fci_l1c_fdhsi } @@ -1919,7 +1919,7 @@ datasets: ir_123_earth_sun_distance: name: ir_123_earth_sun_distance units: km - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_123 } @@ -1927,7 +1927,7 @@ datasets: ir_133_earth_sun_distance: name: ir_133_earth_sun_distance units: km - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_133 } @@ -1935,7 +1935,7 @@ datasets: vis_04_sun_satellite_distance: name: vis_04_sun_satellite_distance units: km - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_04 } @@ -1943,7 +1943,7 @@ datasets: vis_05_sun_satellite_distance: name: vis_05_sun_satellite_distance units: km - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_05 } @@ -1951,7 +1951,7 @@ datasets: vis_06_sun_satellite_distance: name: vis_06_sun_satellite_distance units: km - sensor: fci + instruments: [FCI] resolution: 500: { file_type: fci_l1c_hrfi } 1000: { file_type: [fci_l1c_fdhsi, fci_l1c_af_vis_06_1km] } @@ -1960,7 +1960,7 @@ datasets: vis_08_sun_satellite_distance: name: vis_08_sun_satellite_distance units: km - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_08 } @@ -1968,7 +1968,7 @@ datasets: vis_09_sun_satellite_distance: name: vis_09_sun_satellite_distance units: km - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_09 } @@ -1976,7 +1976,7 @@ datasets: nir_13_sun_satellite_distance: name: nir_13_sun_satellite_distance units: km - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_nir_13 } @@ -1984,7 +1984,7 @@ datasets: nir_16_sun_satellite_distance: name: nir_16_sun_satellite_distance units: km - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_nir_16 } @@ -1992,7 +1992,7 @@ datasets: nir_22_sun_satellite_distance: name: nir_22_sun_satellite_distance units: km - sensor: fci + instruments: [FCI] resolution: 500: { file_type: fci_l1c_hrfi } 1000: { file_type: fci_l1c_fdhsi } @@ -2001,7 +2001,7 @@ datasets: ir_38_sun_satellite_distance: name: ir_38_sun_satellite_distance units: km - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_hrfi } 2000: { file_type: fci_l1c_fdhsi } @@ -2010,7 +2010,7 @@ datasets: wv_63_sun_satellite_distance: name: wv_63_sun_satellite_distance units: km - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_wv_63 } @@ -2018,7 +2018,7 @@ datasets: wv_73_sun_satellite_distance: name: wv_73_sun_satellite_distance units: km - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_wv_73 } @@ -2026,7 +2026,7 @@ datasets: ir_87_sun_satellite_distance: name: ir_87_sun_satellite_distance units: km - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_87 } @@ -2034,7 +2034,7 @@ datasets: ir_97_sun_satellite_distance: name: ir_97_sun_satellite_distance units: km - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_97 } @@ -2042,7 +2042,7 @@ datasets: ir_105_sun_satellite_distance: name: ir_105_sun_satellite_distance units: km - sensor: fci + instruments: [FCI] resolution: 1000: { file_type: fci_l1c_hrfi } 2000: { file_type: fci_l1c_fdhsi } @@ -2051,7 +2051,7 @@ datasets: ir_123_sun_satellite_distance: name: ir_123_sun_satellite_distance units: km - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_123 } @@ -2059,7 +2059,7 @@ datasets: ir_133_sun_satellite_distance: name: ir_133_sun_satellite_distance units: km - sensor: fci + instruments: [FCI] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_133 } diff --git a/satpy/etc/readers/grib.yaml b/satpy/etc/readers/grib.yaml index 1009ed291f..29c78d86da 100644 --- a/satpy/etc/readers/grib.yaml +++ b/satpy/etc/readers/grib.yaml @@ -6,7 +6,7 @@ reader: status: Beta supports_fsspec: false reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader - sensors: [unknown] + instruments: [unknown] data_identification_keys: name: required: true diff --git a/satpy/etc/readers/iasi_l2.yaml b/satpy/etc/readers/iasi_l2.yaml index 927e880ed0..d6bc5b7aff 100644 --- a/satpy/etc/readers/iasi_l2.yaml +++ b/satpy/etc/readers/iasi_l2.yaml @@ -6,7 +6,7 @@ reader: status: Alpha supports_fsspec: false reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader - sensors: [iasi] + instruments: [IASI] default_datasets: datasets: diff --git a/satpy/etc/readers/mtsat2-imager_hrit.yaml b/satpy/etc/readers/mtsat2-imager_hrit.yaml index f2998fcbb2..4d90df916b 100644 --- a/satpy/etc/readers/mtsat2-imager_hrit.yaml +++ b/satpy/etc/readers/mtsat2-imager_hrit.yaml @@ -13,7 +13,7 @@ reader: status: Beta supports_fsspec: false - sensors: [IMAGER (MTSAT-2)] + instruments: [IMAGER (MTSAT-2)] reader: !!python/name:satpy.readers.core.yaml_reader.GEOSegmentYAMLReader file_types: diff --git a/satpy/readers/clavrx.py b/satpy/readers/clavrx.py index a215ae2044..43f9938558 100644 --- a/satpy/readers/clavrx.py +++ b/satpy/readers/clavrx.py @@ -60,26 +60,26 @@ "G18": "GOES-18", } ROWS_PER_SCAN = { - "viirs": 16, - "modis": 10, + "VIIRS": 16, + "MODIS": 10, } NADIR_RESOLUTION = { - "viirs": 742, - "modis": 1000, - "avhrr": 1050, - "ahi": 2000, - "abi": 2004, + "VIIRS": 742, + "MODIS": 1000, + "AVHRR": 1050, + "AHI": 2000, + "ABI": 2004, } CHANNEL_ALIASES = { - "abi": {"refl_0_47um_nom": {"name": "C01", "wavelength": 0.47, "modifiers": ("sunz_corrected",)}, + "ABI": {"refl_0_47um_nom": {"name": "C01", "wavelength": 0.47, "modifiers": ("sunz_corrected",)}, "refl_0_65um_nom": {"name": "C02", "wavelength": 0.64, "modifiers": ("sunz_corrected",)}, "refl_0_86um_nom": {"name": "C03", "wavelength": 0.865, "modifiers": ("sunz_corrected",)}, "refl_1_38um_nom": {"name": "C04", "wavelength": 1.38, "modifiers": ("sunz_corrected",)}, "refl_1_60um_nom": {"name": "C05", "wavelength": 1.61, "modifiers": ("sunz_corrected",)}, "refl_2_10um_nom": {"name": "C06", "wavelength": 2.25, "modifiers": ("sunz_corrected",)}, }, - "viirs": {"refl_0_65um_nom": {"name": "I01", "wavelength": 0.64, "modifiers": ("sunz_corrected",)}, + "VIIRS": {"refl_0_65um_nom": {"name": "I01", "wavelength": 0.64, "modifiers": ("sunz_corrected",)}, "refl_1_38um_nom": {"name": "M09", "wavelength": 1.38, "modifiers": ("sunz_corrected",)}, "refl_1_60um_nom": {"name": "I03", "wavelength": 1.61, "modifiers": ("sunz_corrected",)} } diff --git a/satpy/readers/eum_l2_bufr.py b/satpy/readers/eum_l2_bufr.py index 92918a9aa8..b1160a00f5 100644 --- a/satpy/readers/eum_l2_bufr.py +++ b/satpy/readers/eum_l2_bufr.py @@ -314,7 +314,7 @@ def _construct_area_def(self, dataset_id): def _add_attributes(self, xarr, dataset_info): """Add dataset attributes to xarray.""" - xarr.attrs["instruments"] = {WMO_INSTRUMENT_NAMES[self.sensor_name]} + xarr.attrs["instruments"] = {WMO_INSTRUMENT_NAMES.get(self.sensor_name, self.sensor_name)} xarr.attrs["platform_name"] = self.platform_name xarr.attrs["ssp_lon"] = self.ssp_lon if ("resolution" not in dataset_info) or (dataset_info["resolution"] is None): diff --git a/satpy/readers/eum_l2_grib.py b/satpy/readers/eum_l2_grib.py index 50bb99772b..cbd1b8383a 100644 --- a/satpy/readers/eum_l2_grib.py +++ b/satpy/readers/eum_l2_grib.py @@ -293,7 +293,7 @@ def _get_attributes(self): } attributes = {"orbital_parameters": orbital_parameters, - "instruments": {WMO_INSTRUMENT_NAMES[self.sensor]}, + "instruments": {WMO_INSTRUMENT_NAMES.get(self.sensor, self.sensor)}, "platform_name": self.PLATFORM_NAME} return attributes diff --git a/satpy/readers/fci_l2_nc.py b/satpy/readers/fci_l2_nc.py index a138eae768..5e8b889a8a 100644 --- a/satpy/readers/fci_l2_nc.py +++ b/satpy/readers/fci_l2_nc.py @@ -82,7 +82,7 @@ def _get_global_attributes(self, product_type="pixel"): "filename": self.filename, "spacecraft_name": platform_name_translate.get(self.spacecraft_name, self.spacecraft_name), "ssp_lon": self.ssp_lon, - "instruments": {WMO_INSTRUMENT_NAMES[self.sensor_name]}, + "instruments": {WMO_INSTRUMENT_NAMES.get(self.sensor_name, self.sensor_name)}, "platform_name": platform_name_translate.get(self.spacecraft_name, self.spacecraft_name) } diff --git a/satpy/readers/geocat.py b/satpy/readers/geocat.py index 5fe9d971c9..47aee761ad 100644 --- a/satpy/readers/geocat.py +++ b/satpy/readers/geocat.py @@ -81,7 +81,7 @@ def __init__(self, filename, filename_info, filetype_info, xarray_kwargs=kwargs["xarray_kwargs"]) sensors = { - "goes": "GOES Imager", + "goes": "IMAGER (GOES 12-15)", "himawari8": "AHI", "goes16": "ABI", # untested "goesr": "ABI", # untested diff --git a/satpy/readers/gms/gms5_vissr_l1b.py b/satpy/readers/gms/gms5_vissr_l1b.py index ee248a2ac9..2f7f9691ac 100644 --- a/satpy/readers/gms/gms5_vissr_l1b.py +++ b/satpy/readers/gms/gms5_vissr_l1b.py @@ -286,7 +286,7 @@ def _get_nominal_shape(self): def _get_mda(self): return { "platform": self._mode_block["satellite_name"].decode().strip().upper(), - "instruments": {"VISSR"}, + "instruments": {"VISSR (Himawari-5)"}, "time_parameters": self._get_time_parameters(), "orbital_parameters": self._get_orbital_parameters(), } diff --git a/satpy/readers/goes_imager_hrit.py b/satpy/readers/goes_imager_hrit.py index eeadc4def6..7b07507cf4 100644 --- a/satpy/readers/goes_imager_hrit.py +++ b/satpy/readers/goes_imager_hrit.py @@ -444,7 +444,7 @@ def _get_proj_dict(self, dataset_id): loff = nlines - loff name_dict = get_geos_area_naming({ "platform_name": self.platform_name, - "instrument_name": self.instrument, + "instrument_name": "goes_imager", # Partial scans are padded to full disk "service_name": "FD", "service_desc": "Full Disk", diff --git a/satpy/readers/grib.py b/satpy/readers/grib.py index 1c866055b3..db1d770cc7 100644 --- a/satpy/readers/grib.py +++ b/satpy/readers/grib.py @@ -285,7 +285,8 @@ def get_metadata(self, msg, ds_info): "units": "units", "modelName": "modelName", "valid_min": "minimum", - "valid_max": "maximum"} + "valid_max": "maximum", + "instruments": "modelName"} ds_info.update({ "filename": self.filename, @@ -293,8 +294,7 @@ def get_metadata(self, msg, ds_info): "centreDescription": center_description, "start_time": start_time, "end_time": end_time, - "platform_name": "unknown", - "instruments": {msg["modelName"]}}) + "platform_name": "unknown"}) for key in key_dicts: if key_dicts[key] in msg.keys(): @@ -302,6 +302,7 @@ def get_metadata(self, msg, ds_info): else: ds_info[key] = "unknown" + ds_info["instruments"] = {ds_info["instruments"]} return ds_info def get_dataset(self, dataset_id, ds_info): diff --git a/satpy/readers/mirs.py b/satpy/readers/mirs.py index e9a9ca00b3..6718822aa0 100644 --- a/satpy/readers/mirs.py +++ b/satpy/readers/mirs.py @@ -423,7 +423,7 @@ def get_dataset(self, ds_id, ds_info): data = data.rename(new_name_or_name_dict=ds_info["name"]) data, ds_info = self.apply_attributes(data, ds_info) - if self.sensor.lower() == atms and self.limb_correction: + if self.sensor == {"ATMS"} and self.limb_correction: sfc_type_mask = self["Sfc_type"] data = limb_correct_atms_bt(data, sfc_type_mask, self._get_coeff_filenames, diff --git a/satpy/readers/mws_l1b.py b/satpy/readers/mws_l1b.py index 6849f8475d..f65528ad19 100644 --- a/satpy/readers/mws_l1b.py +++ b/satpy/readers/mws_l1b.py @@ -267,6 +267,7 @@ def _get_global_attributes(self): "start_time": self.start_time, "end_time": self.end_time, "spacecraft_name": self.platform_name, + "instruments": {self.sensor}, "filename_start_time": self.filename_info["start_time"], "filename_end_time": self.filename_info["end_time"], "platform_name": self.platform_name, diff --git a/satpy/readers/nwcsaf_hrw_nc.py b/satpy/readers/nwcsaf_hrw_nc.py index e74776a880..35ecf93f38 100644 --- a/satpy/readers/nwcsaf_hrw_nc.py +++ b/satpy/readers/nwcsaf_hrw_nc.py @@ -241,7 +241,7 @@ def __init__(self, filename, filename_info, filetype_info, merge_channels=False) self.filetype_info = filetype_info self.merge_channels = merge_channels self.platform_name = PLATFORM_NAMES.get(self.h5f.attrs["satellite_identifier"].astype(str)) - self.sensor = SENSOR.get(self.platform_name, "seviri") + self.sensor = SENSOR.get(self.platform_name, "SEVIRI") self.lons = {} self.lats = {} # Imaging period, which is set after reading any data, and used to calculate end time diff --git a/satpy/readers/nwcsaf_nc.py b/satpy/readers/nwcsaf_nc.py index 17c26b6ae2..d38feed33d 100644 --- a/satpy/readers/nwcsaf_nc.py +++ b/satpy/readers/nwcsaf_nc.py @@ -228,7 +228,7 @@ def scale_dataset(self, variable, info): variable.attrs.pop("scale_factor", None) variable.attrs.update({"platform_name": self.platform_name, - "instruments": {self.sensor}}) + "instruments": self.sensor}) if not variable.attrs.get("standard_name", "").endswith("status_flag"): # TODO: do we really need to add units to everything ? diff --git a/satpy/readers/osisaf_l3_nc.py b/satpy/readers/osisaf_l3_nc.py index 41e0ea2228..f825d8affd 100644 --- a/satpy/readers/osisaf_l3_nc.py +++ b/satpy/readers/osisaf_l3_nc.py @@ -170,21 +170,25 @@ def get_dataset(self, dataset_id, ds_info): ds_info.update({ "units": ds_info.get("units", file_units), "platform_name": self._get_platname(), - "instruments": {self._get_instname()} + "instruments": self._get_instruments() }) ds_info.update(dataset_id.to_dict()) data.attrs.update(ds_info) return data - def _get_instname(self): - """Get instrument name.""" + def _get_instruments(self): + """Get instrument names.""" try: - return self["/attr/instrument_name"] + instruments = self["/attr/instrument_name"] except KeyError: try: - return self["/attr/sensor"] + instruments = self["/attr/sensor"] except KeyError: - return "unknown_sensor" + instruments = "unknown_sensor" + return set( + instr.strip() + for instr in instruments.split(",") + ) def _get_platname(self): """Get platform name.""" diff --git a/satpy/readers/virr_l1b.py b/satpy/readers/virr_l1b.py index 703c14da59..31b355d5fb 100644 --- a/satpy/readers/virr_l1b.py +++ b/satpy/readers/virr_l1b.py @@ -116,7 +116,6 @@ def get_dataset(self, dataset_id, ds_info): data = data * slope + intercept new_dims = {old: new for old, new in zip(data.dims, ("y", "x"))} data = data.rename(new_dims) - # use lowercase sensor name to be consistent with the rest of satpy data.attrs.update({"platform_name": self["/attr/Satellite Name"], "instruments": {self["/attr/Sensor Identification Code"]}}) data.attrs.update(ds_info) diff --git a/satpy/tests/compositor_tests/test_aux_data.py b/satpy/tests/compositor_tests/test_aux_data.py index d9e3657c66..55222e63ae 100644 --- a/satpy/tests/compositor_tests/test_aux_data.py +++ b/satpy/tests/compositor_tests/test_aux_data.py @@ -83,7 +83,7 @@ def load(self, arg): filenames=[IMAGE_FILENAME]) register.assert_not_called() retrieve.assert_not_called() - assert res.attrs["sensor"] == set() + assert res.attrs["instruments"] == set() assert "modifiers" not in res.attrs assert "calibration" not in res.attrs @@ -95,7 +95,7 @@ def load(self, arg): res = comp() Scene.assert_called_once_with(reader="generic_image", filenames=[os.path.join("data_dir", "foo.tif")]) - assert res.attrs["sensor"] == set() + assert res.attrs["instruments"] == set() assert "modifiers" not in res.attrs assert "calibration" not in res.attrs diff --git a/satpy/tests/compositor_tests/test_core.py b/satpy/tests/compositor_tests/test_core.py index 55f699aa42..0eaa378bb1 100644 --- a/satpy/tests/compositor_tests/test_core.py +++ b/satpy/tests/compositor_tests/test_core.py @@ -199,19 +199,19 @@ def test_call(self): """Test calling the compositor.""" # Dataset with extra attributes all_valid = self.all_valid - all_valid.attrs["sensor"] = "foo" + all_valid.attrs["instruments"] = {"foo"} attrs = { "foo": "bar", "resolution": 333, "units": "K", - "sensor": {"fake_sensor1", "fake_sensor2"}, + "instruments": {"fake_sensor1", "fake_sensor2"}, "calibration": "BT", "wavelength": 10.8 } self.comp.attrs["resolution"] = None res = self.comp([all_valid], **attrs) # Verify attributes - assert res.attrs.get("sensor") == "foo" + assert res.attrs.get("instruments") == {"foo"} assert "foo" in res.attrs assert res.attrs.get("foo") == "bar" assert "units" in res.attrs @@ -279,11 +279,11 @@ def test_get_sensors(self): res = self.comp._get_sensors([self.all_valid]) assert res == set() dset1 = self.all_valid - dset1.attrs["sensor"] = "foo" + dset1.attrs["instruments"] = {"foo"} res = self.comp._get_sensors([dset1]) assert res == {"foo"} dset2 = self.first_invalid - dset2.attrs["sensor"] = {"bar"} + dset2.attrs["instruments"] = {"bar"} res = self.comp._get_sensors([dset1, dset2]) assert "foo" in res assert "bar" in res @@ -327,12 +327,12 @@ def test_call(self): """Test calling generic compositor.""" # Multiple datasets with extra attributes all_valid = self.all_valid - all_valid.attrs["sensor"] = {"foo"} + all_valid.attrs["instruments"] = {"foo"} attrs = {"foo": "bar", "resolution": 333} self.comp.attrs["resolution"] = None res = self.comp([self.all_valid, self.first_invalid], **attrs) # Verify attributes - assert res.attrs.get("sensor") == {"foo"} + assert res.attrs.get("instruments") == {"foo"} assert "foo" in res.attrs assert res.attrs.get("foo") == "bar" assert "units" not in res.attrs diff --git a/satpy/tests/compositor_tests/test_fill.py b/satpy/tests/compositor_tests/test_fill.py index 1cbc2c1dc5..036f2f748e 100644 --- a/satpy/tests/compositor_tests/test_fill.py +++ b/satpy/tests/compositor_tests/test_fill.py @@ -364,13 +364,13 @@ def test_multiple_sensors(self): dims=("bands", "y", "x"), coords={"bands": [c for c in attrs["mode"]]}, attrs=attrs.copy()) - foreground.attrs["sensor"] = "abi" + foreground.attrs["instruments"] = {"ABI"} background = xr.DataArray(da.ones((1, 2, 2)), dims=("bands", "y", "x"), coords={"bands": [c for c in attrs["mode"]]}, attrs=attrs.copy()) - background.attrs["sensor"] = "glm" + background.attrs["instruments"] = {"GLM"} res = comp([foreground, background]) assert res.attrs["area"] == "foo" np.testing.assert_allclose(res, np.array([[1., 0.5], [0., 1.]])) assert res.attrs["mode"] == "L" - assert res.attrs["sensor"] == {"abi", "glm"} + assert res.attrs["instruments"] == {"ABI", "GLM"} diff --git a/satpy/tests/reader_tests/gms/test_gms5_vissr_l1b.py b/satpy/tests/reader_tests/gms/test_gms5_vissr_l1b.py index b86f5e78b6..61a9109625 100644 --- a/satpy/tests/reader_tests/gms/test_gms5_vissr_l1b.py +++ b/satpy/tests/reader_tests/gms/test_gms5_vissr_l1b.py @@ -519,7 +519,7 @@ def attrs_exp(self, area_def_exp): return { "yaml": "info", "platform": "GMS-5", - "sensor": "VISSR", + "instruments": {"VISSR (Himawari-5)"}, "time_parameters": { "nominal_start_time": dt.datetime(1995, 10, 10), "nominal_end_time": dt.datetime(1995, 10, 10, 0, 25), diff --git a/satpy/tests/reader_tests/modis_tests/test_modis_l1b.py b/satpy/tests/reader_tests/modis_tests/test_modis_l1b.py index 94a21c2ef8..aeb28c30ac 100644 --- a/satpy/tests/reader_tests/modis_tests/test_modis_l1b.py +++ b/satpy/tests/reader_tests/modis_tests/test_modis_l1b.py @@ -46,7 +46,7 @@ def _check_shared_metadata(data_arr): - assert data_arr.attrs["sensor"] == "modis" + assert data_arr.attrs["instruments"] == {"MODIS"} assert data_arr.attrs["platform_name"] == "Terra" assert "rows_per_scan" in data_arr.attrs assert isinstance(data_arr.attrs["rows_per_scan"], int) diff --git a/satpy/tests/reader_tests/modis_tests/test_modis_l2.py b/satpy/tests/reader_tests/modis_tests/test_modis_l2.py index d827e18215..06685bbfd3 100644 --- a/satpy/tests/reader_tests/modis_tests/test_modis_l2.py +++ b/satpy/tests/reader_tests/modis_tests/test_modis_l2.py @@ -43,7 +43,7 @@ def _check_shared_metadata(data_arr, expect_area=False): - assert data_arr.attrs["sensor"] == "modis" + assert data_arr.attrs["instruments"] == {"MODIS"} assert data_arr.attrs["platform_name"] == "Terra" assert "rows_per_scan" in data_arr.attrs assert isinstance(data_arr.attrs["rows_per_scan"], int) diff --git a/satpy/tests/reader_tests/modis_tests/test_modis_l3_mcd12q1.py b/satpy/tests/reader_tests/modis_tests/test_modis_l3_mcd12q1.py index 12729ea83c..95adee302f 100644 --- a/satpy/tests/reader_tests/modis_tests/test_modis_l3_mcd12q1.py +++ b/satpy/tests/reader_tests/modis_tests/test_modis_l3_mcd12q1.py @@ -41,7 +41,7 @@ def test_metadata(self, modis_l3_nasa_mcd12q1_file): ds_name = "LC_Type2" scene.load([ds_name]) assert scene[ds_name].attrs["area"].description == "Tiled sinusoidal L3 MODIS area" - assert scene[ds_name].attrs["sensor"] == "modis" + assert scene[ds_name].attrs["instruments"] == {"MODIS"} def test_scene_available_datasets(self, modis_l3_nasa_mcd12q1_file): """Test that datasets are available.""" diff --git a/satpy/tests/reader_tests/seviri_l1b_hrit_setup.py b/satpy/tests/reader_tests/seviri_l1b_hrit_setup.py index 39073aad85..b7451b51d9 100644 --- a/satpy/tests/reader_tests/seviri_l1b_hrit_setup.py +++ b/satpy/tests/reader_tests/seviri_l1b_hrit_setup.py @@ -230,7 +230,7 @@ def get_attrs_exp(projection_longitude=0.0): "wavelength": "wavelength", "standard_name": "standard_name", "platform_name": "Meteosat-11", - "sensor": "seviri", + "instruments": {"SEVIRI"}, "orbital_parameters": {"projection_longitude": projection_longitude, "projection_latitude": 0., "projection_altitude": 35785831.0, diff --git a/satpy/tests/reader_tests/test_abi_l1b.py b/satpy/tests/reader_tests/test_abi_l1b.py index 855265dd21..8e9fdb8f20 100644 --- a/satpy/tests/reader_tests/test_abi_l1b.py +++ b/satpy/tests/reader_tests/test_abi_l1b.py @@ -371,7 +371,7 @@ def test_get_dataset(self, c01_data_arr): "scan_mode": "M4", "scene_abbr": "C", "scene_id": None, - "sensor": {"abi"}, + "instruments": {"ABI"}, "timeline_ID": None, "suffix": "suffix", "units": "W m-2 um-1 sr-1", diff --git a/satpy/tests/reader_tests/test_abi_l2_nc.py b/satpy/tests/reader_tests/test_abi_l2_nc.py index 0f7132f9b7..7f6c845de7 100644 --- a/satpy/tests/reader_tests/test_abi_l2_nc.py +++ b/satpy/tests/reader_tests/test_abi_l2_nc.py @@ -160,7 +160,7 @@ def test_get_dataset(self, obs_type, ds_func, var_name, var_attrs, fh_kwargs): "scan_mode": "M3", "scene_abbr": "C", "scene_id": None, - "sensor": "abi", + "instruments": {"ABI"}, "timeline_ID": None, } exp_attrs.update(var_attrs) @@ -222,7 +222,7 @@ def test_mcmip_get_dataset(self, xr_, product, exp_metadata): "scan_mode": "M6", "scene_abbr": "F", "scene_id": None, - "sensor": "abi", + "instruments": {"ABI"}, "timeline_ID": None, "start_time": dt.datetime(2017, 9, 20, 17, 30, 40, 800000), "end_time": dt.datetime(2017, 9, 20, 17, 41, 17, 500000), diff --git a/satpy/tests/reader_tests/test_acspo.py b/satpy/tests/reader_tests/test_acspo.py index cd441a0e85..e4a9ed0eaf 100644 --- a/satpy/tests/reader_tests/test_acspo.py +++ b/satpy/tests/reader_tests/test_acspo.py @@ -166,7 +166,7 @@ def test_load_every_dataset(self, var_name, cloud_clearable, cloud_clear): d = datasets[var_name] assert d.shape == DEFAULT_FILE_SHAPE assert d.dims == ("y", "x") - assert d.attrs["sensor"] == "viirs" + assert d.attrs["instruments"] == {"VIIRS"} assert d.attrs["rows_per_scan"] == 16 dask_data = d.data np_data = dask_data.compute() diff --git a/satpy/tests/reader_tests/test_agri_l1.py b/satpy/tests/reader_tests/test_agri_l1.py index 4ad06cef20..386e24d52f 100644 --- a/satpy/tests/reader_tests/test_agri_l1.py +++ b/satpy/tests/reader_tests/test_agri_l1.py @@ -380,7 +380,7 @@ def test_agri_for_one_resolution(self, resolution_to_test, satname): def _check_calibration_and_units(self, band_names, result): for band_name in band_names: band_number = int(band_name[-2:]) - assert result[band_name].attrs["sensor"].islower() + assert result[band_name].attrs["instruments"] == {"AGRI"} assert result[band_name].shape == (2, 5) np.testing.assert_allclose(result[band_name].values, self.expected[band_number], equal_nan=True) self._check_units(band_name, result) diff --git a/satpy/tests/reader_tests/test_ahi_hrit.py b/satpy/tests/reader_tests/test_ahi_hrit.py index 25f7b585b0..929245a237 100644 --- a/satpy/tests/reader_tests/test_ahi_hrit.py +++ b/satpy/tests/reader_tests/test_ahi_hrit.py @@ -396,7 +396,7 @@ def test_calibrate(tmp_path, calibration): key, { "units": units, - "sensor": "ahi", + "instruments": ["AHI"], }, ) @@ -430,7 +430,7 @@ def test_mask_space(tmp_path): reader = HRITJMAFileHandler(hrit_path, {"start_time": dt.datetime.now()}, {}) key = make_dataid(name="VIS", calibration="counts") - res = reader.get_dataset(key, {"units": "1", "sensor": "ahi"}) + res = reader.get_dataset(key, {"units": "1", "instruments": ["AHI"]}) res_np = res.data.compute() assert res_np.dtype == res.dtype assert res_np.dtype == np.float32 @@ -462,13 +462,13 @@ def test_get_dataset(tmp_path): key = make_dataid(name="VIS", calibration="reflectance") with mock.patch.object(reader, "_mask_space", wraps=reader._mask_space) as mask_space, \ mock.patch.object(reader, "calibrate", wraps=reader.calibrate) as calibrate: - res = reader.get_dataset(key, {"units": "%", "sensor": "ahi"}) + res = reader.get_dataset(key, {"units": "%", "instruments": ["AHI"]}) mask_space.assert_called() calibrate.assert_called() # Check attributes assert res.attrs["units"] == "%" - assert res.attrs["sensor"] == "ahi" + assert res.attrs["instruments"] == {"AHI"} assert res.attrs["platform_name"] == HIMAWARI8 assert res.attrs["orbital_parameters"] == {"projection_longitude": 140.7, "projection_latitude": 0.0, @@ -485,7 +485,7 @@ def test_sensor_mismatch(tmp_path, caplog): reader = HRITJMAFileHandler(hrit_path, {"start_time": dt.datetime.now()}, {}) key = make_dataid(name="VIS", calibration="reflectance") - reader.get_dataset(key, {"units": "%", "sensor": "jami"}) + reader.get_dataset(key, {"units": "%", "instruments": ["JAMI"]}) assert "Sensor-Platform mismatch" in caplog.text hrit_path.unlink() diff --git a/satpy/tests/reader_tests/test_ahi_l2_nc.py b/satpy/tests/reader_tests/test_ahi_l2_nc.py index ae091a5fab..3d0cf7659e 100644 --- a/satpy/tests/reader_tests/test_ahi_l2_nc.py +++ b/satpy/tests/reader_tests/test_ahi_l2_nc.py @@ -115,7 +115,7 @@ def test_load_data(himl2_filename): clmk = fh.get_dataset(clmk_id, {"file_key": "CloudMask"}) np.testing.assert_allclose(clmk.data, clmk_data) assert clmk.dtype == np.uint16 - assert clmk.attrs["sensor"] == "ahi" + assert clmk.attrs["instruments"] == {"AHI"} assert clmk.attrs["platform_name"] == "Himawari-9" assert clmk.attrs["platform_shortname"] == "h09" assert isinstance(clmk.attrs["start_time"], dt.datetime) diff --git a/satpy/tests/reader_tests/test_ami_l1b.py b/satpy/tests/reader_tests/test_ami_l1b.py index 041bf454fd..a746c14d8e 100644 --- a/satpy/tests/reader_tests/test_ami_l1b.py +++ b/satpy/tests/reader_tests/test_ami_l1b.py @@ -244,7 +244,7 @@ def test_get_dataset(self, fake_vis_reader): exp = {"calibration": "radiance", "modifiers": (), "platform_name": "GEO-KOMPSAT-2A", - "sensor": "ami", + "instruments": {"AMI"}, "units": "W m-2 um-1 sr-1"} for key, val in exp.items(): assert val == res.attrs[key] @@ -285,7 +285,7 @@ def test_get_dataset_vis(self, fake_vis_reader): exp = {"calibration": "reflectance", "modifiers": (), "platform_name": "GEO-KOMPSAT-2A", - "sensor": "ami", + "instruments": {"AMI"}, "units": "%"} for key, val in exp.items(): assert val == res.attrs[key] @@ -303,7 +303,7 @@ def test_get_dataset_counts(self, fake_vis_reader): exp = {"calibration": "counts", "modifiers": (), "platform_name": "GEO-KOMPSAT-2A", - "sensor": "ami", + "instruments": {"AMI"}, "units": "1"} for key, val in exp.items(): assert val == res.attrs[key] diff --git a/satpy/tests/reader_tests/test_amsr2_l1b.py b/satpy/tests/reader_tests/test_amsr2_l1b.py index d45543369f..a91a4a31c7 100644 --- a/satpy/tests/reader_tests/test_amsr2_l1b.py +++ b/satpy/tests/reader_tests/test_amsr2_l1b.py @@ -160,7 +160,7 @@ def test_load_basic(self): assert d.attrs["area"] is not None assert d.attrs["area"].lons.shape == (DEFAULT_FILE_SHAPE[0], DEFAULT_FILE_SHAPE[1] // 2) assert d.attrs["area"].lats.shape == (DEFAULT_FILE_SHAPE[0], DEFAULT_FILE_SHAPE[1] // 2) - assert d.attrs["sensor"] == "amsr2" + assert d.attrs["instruments"] == {"AMSR2"} assert d.attrs["platform_name"] == "GCOM-W1" def test_load_89ghz(self): diff --git a/satpy/tests/reader_tests/test_amsr2_l2_gaasp.py b/satpy/tests/reader_tests/test_amsr2_l2_gaasp.py index 71de9ce7bb..e94365723b 100644 --- a/satpy/tests/reader_tests/test_amsr2_l2_gaasp.py +++ b/satpy/tests/reader_tests/test_amsr2_l2_gaasp.py @@ -316,7 +316,7 @@ def _check_attrs(data_arr): assert "scale_factor" not in attrs assert "add_offset" not in attrs assert attrs["platform_name"] == "GCOM-W1" - assert attrs["sensor"] == "amsr2" + assert attrs["instruments"] == {"AMSR2"} assert attrs["start_time"] == dt.datetime(2020, 8, 12, 5, 58, 31) assert attrs["end_time"] == dt.datetime(2020, 8, 12, 6, 7, 1) diff --git a/satpy/tests/reader_tests/test_atms_l1b_nc.py b/satpy/tests/reader_tests/test_atms_l1b_nc.py index 9dc7b210d3..0a56d4b2c3 100644 --- a/satpy/tests/reader_tests/test_atms_l1b_nc.py +++ b/satpy/tests/reader_tests/test_atms_l1b_nc.py @@ -104,7 +104,7 @@ def test_antenna_temperature(self, reader, atms_fake_dataset): ("start_time", dt.datetime(2000, 1, 2, 3, 4, 5)), ("end_time", dt.datetime(2000, 1, 2, 4, 5, 6)), ("platform_name", "JPSS-1"), - ("sensor", "ATMS"), + ("instruments", {"ATMS"}), ]) def test_attrs(self, reader, param, expect): """Test attributes.""" @@ -139,7 +139,7 @@ def test_drop_coords(self, reader): ("start_time", dt.datetime(2000, 1, 2, 3, 4, 5)), ("end_time", dt.datetime(2000, 1, 2, 4, 5, 6)), ("platform_name", "JPSS-1"), - ("sensor", "ATMS"), + ("instruments", {"ATMS"}), ("creation_time", dt.datetime(2020, 1, 2, 3, 4, 5)), ("type", "test_data"), ("name", "test"), @@ -175,4 +175,4 @@ def test_get_dataset(self, reader): np.full((2, 3), 100.), ) assert dataset.dims == ("y", "x") - assert dataset.attrs["sensor"] == "ATMS" + assert dataset.attrs["instruments"] == {"ATMS"} diff --git a/satpy/tests/reader_tests/test_aws1_mwr_l1b.py b/satpy/tests/reader_tests/test_aws1_mwr_l1b.py index e388b6186e..2426ac708a 100644 --- a/satpy/tests/reader_tests/test_aws1_mwr_l1b.py +++ b/satpy/tests/reader_tests/test_aws1_mwr_l1b.py @@ -36,7 +36,7 @@ def test_orbit_number_start_end(aws_mwr_handler): def test_metadata(aws_mwr_handler): """Test that the metadata is read correctly.""" - assert aws_mwr_handler.sensor == "mwr" + assert aws_mwr_handler.sensor == "MWR" assert aws_mwr_handler.platform_name == PLATFORM_NAME @@ -60,7 +60,7 @@ def test_get_channel_data(aws_mwr_handler, fake_mwr_data_array): assert res.attrs["orbital_parameters"]["sub_satellite_longitude_end"] == 296.79 assert res.dims == ("y", "x") assert "n_channels" not in res.coords - assert res.attrs["sensor"] == "mwr" + assert res.attrs["instruments"] == {"MWR"} assert res.attrs["platform_name"] == PLATFORM_NAME diff --git a/satpy/tests/reader_tests/test_aws1_mwr_l1c.py b/satpy/tests/reader_tests/test_aws1_mwr_l1c.py index ea4fc49c5f..dc6e3f254c 100644 --- a/satpy/tests/reader_tests/test_aws1_mwr_l1c.py +++ b/satpy/tests/reader_tests/test_aws1_mwr_l1c.py @@ -56,7 +56,7 @@ def test_get_channel_data(aws_mwr_l1c_handler, fake_mwr_data_array): assert "y" in res.dims assert res.dims == ("y", "x") assert "n_channels" not in res.coords - assert res.attrs["sensor"] == "mwr" + assert res.attrs["instruments"] == {"MWR"} assert res.attrs["platform_name"] == PLATFORM_NAME diff --git a/satpy/tests/reader_tests/test_clavrx/test_clavrx_geohdf.py b/satpy/tests/reader_tests/test_clavrx/test_clavrx_geohdf.py index 75b2d85ede..baa88c44ff 100644 --- a/satpy/tests/reader_tests/test_clavrx/test_clavrx_geohdf.py +++ b/satpy/tests/reader_tests/test_clavrx/test_clavrx_geohdf.py @@ -242,5 +242,5 @@ def test_load_all_new_donor(self): assert isinstance(v.attrs["area"], AreaDefinition) assert v.attrs["area"].is_geostationary is True assert v.attrs["platform_name"] == "himawari8" - assert v.attrs["sensor"] == "ahi" + assert v.attrs["instruments"] == {"AHI"} assert datasets["variable3"].attrs.get("flag_meanings") is not None diff --git a/satpy/tests/reader_tests/test_clavrx/test_clavrx_nc.py b/satpy/tests/reader_tests/test_clavrx/test_clavrx_nc.py index 08687d55cc..34a4571045 100644 --- a/satpy/tests/reader_tests/test_clavrx/test_clavrx_nc.py +++ b/satpy/tests/reader_tests/test_clavrx/test_clavrx_nc.py @@ -228,7 +228,7 @@ def test_load_all_new_donor(self, filenames, loadable_ids): for v in datasets.values(): assert isinstance(v.area, AreaDefinition) assert v.platform_name == "GOES-16" - assert v.sensor == "abi" + assert v.instruments == {"ABI"} assert "calibration" not in v.attrs assert "rows_per_scan" not in v.coords.get("longitude").attrs diff --git a/satpy/tests/reader_tests/test_clavrx/test_clavrx_polarhdf.py b/satpy/tests/reader_tests/test_clavrx/test_clavrx_polarhdf.py index e405412e8c..1a7e87872c 100644 --- a/satpy/tests/reader_tests/test_clavrx/test_clavrx_polarhdf.py +++ b/satpy/tests/reader_tests/test_clavrx/test_clavrx_polarhdf.py @@ -252,7 +252,7 @@ def test_load_all(self): for v in datasets.values(): assert v.attrs["units"] in ["1", "%"] assert v.attrs["platform_name"] == "npp" - assert v.attrs["sensor"] == "viirs" + assert v.attrs["instruments"] == {"VIIRS"} assert isinstance(v.attrs["area"], SwathDefinition) assert v.attrs["area"].lons.attrs["rows_per_scan"] == 16 assert v.attrs["area"].lats.attrs["rows_per_scan"] == 16 diff --git a/satpy/tests/reader_tests/test_electrol_hrit.py b/satpy/tests/reader_tests/test_electrol_hrit.py index 6a328275fd..ac29a99b77 100644 --- a/satpy/tests/reader_tests/test_electrol_hrit.py +++ b/satpy/tests/reader_tests/test_electrol_hrit.py @@ -195,7 +195,7 @@ def test_get_dataset(self, calibrate_mock, *mocks): "projection_latitude": 0.0, "projection_altitude": 35785831.00}, "platform_name": "Electro", - "sensor": "msu-gs"}) + "instruments": {"MSU-GS"}}) assert dict(output.attrs, **attrs_exp) == output.attrs def test_calibrate(self, *mocks): diff --git a/satpy/tests/reader_tests/test_eps_l1b.py b/satpy/tests/reader_tests/test_eps_l1b.py index 16c7cfe27b..609eec9f64 100644 --- a/satpy/tests/reader_tests/test_eps_l1b.py +++ b/satpy/tests/reader_tests/test_eps_l1b.py @@ -120,7 +120,7 @@ def test_dataset(self): res = self.fh.get_dataset(did, {}) assert isinstance(res, xr.DataArray) assert res.attrs["platform_name"] == "Metop-C" - assert res.attrs["sensor"] == "avhrr-3" + assert res.attrs["instruments"] == {"AVHRR/3"} assert res.attrs["name"] == "1" assert res.attrs["calibration"] == "reflectance" assert res.attrs["units"] == "%" @@ -129,7 +129,7 @@ def test_dataset(self): res = self.fh.get_dataset(did, {}) assert isinstance(res, xr.DataArray) assert res.attrs["platform_name"] == "Metop-C" - assert res.attrs["sensor"] == "avhrr-3" + assert res.attrs["instruments"] == {"AVHRR/3"} assert res.attrs["name"] == "4" assert res.attrs["calibration"] == "brightness_temperature" assert res.attrs["units"] == "K" @@ -140,7 +140,7 @@ def test_get_dataset_radiance(self): res = self.fh.get_dataset(did, {}) assert isinstance(res, xr.DataArray) assert res.attrs["platform_name"] == "Metop-C" - assert res.attrs["sensor"] == "avhrr-3" + assert res.attrs["instruments"] == {"AVHRR/3"} assert res.attrs["name"] == "1" assert res.attrs["calibration"] == "radiance" assert res.attrs["units"] == "W m^-2 sr^-1" @@ -151,7 +151,7 @@ def test_navigation(self): res = self.fh.get_dataset(did, {}) assert isinstance(res, xr.DataArray) assert res.attrs["platform_name"] == "Metop-C" - assert res.attrs["sensor"] == "avhrr-3" + assert res.attrs["instruments"] == {"AVHRR/3"} assert res.attrs["name"] == "longitude" def test_angles(self): @@ -160,7 +160,7 @@ def test_angles(self): res = self.fh.get_dataset(did, {}) assert isinstance(res, xr.DataArray) assert res.attrs["platform_name"] == "Metop-C" - assert res.attrs["sensor"] == "avhrr-3" + assert res.attrs["instruments"] == {"AVHRR/3"} assert res.attrs["name"] == "solar_zenith_angle" def test_clould_flags(self): @@ -169,7 +169,7 @@ def test_clould_flags(self): res = self.fh.get_dataset(did, {}) assert isinstance(res, xr.DataArray) assert res.attrs["platform_name"] == "Metop-C" - assert res.attrs["sensor"] == "avhrr-3" + assert res.attrs["instruments"] == {"AVHRR/3"} assert res.attrs["name"] == "cloud_flags" @mock.patch("satpy.readers.eps_l1b.EPSAVHRRFile.__getitem__") diff --git a/satpy/tests/reader_tests/test_eps_sterna_mwr_l1b.py b/satpy/tests/reader_tests/test_eps_sterna_mwr_l1b.py index 43abdf74d4..86a440ace2 100644 --- a/satpy/tests/reader_tests/test_eps_sterna_mwr_l1b.py +++ b/satpy/tests/reader_tests/test_eps_sterna_mwr_l1b.py @@ -66,5 +66,5 @@ def test_try_get_data_not_in_file(eps_sterna_mwr_handler): def test_metadata(eps_sterna_mwr_handler): """Test that the metadata is read correctly.""" - assert eps_sterna_mwr_handler.sensor == "mwr" + assert eps_sterna_mwr_handler.sensor == "MWR" assert eps_sterna_mwr_handler.platform_name == "ST01" diff --git a/satpy/tests/reader_tests/test_eum_l2_grib.py b/satpy/tests/reader_tests/test_eum_l2_grib.py index 4172cf0ea0..cd40705e97 100644 --- a/satpy/tests/reader_tests/test_eum_l2_grib.py +++ b/satpy/tests/reader_tests/test_eum_l2_grib.py @@ -149,7 +149,7 @@ def test_seviri_data_reading(da_, xr_, setup_reader): "orbital_parameters": { "projection_longitude": 9.5 }, - "sensor": "seviri", + "instruments": {"SEVIRI"}, "platform_name": "Meteosat-11" } assert attributes == expected_attributes @@ -250,7 +250,7 @@ def test_fci_data_reading(da_, xr_, setup_reader): "orbital_parameters": { "projection_longitude": 0.0 }, - "sensor": "fci", + "instruments": {"FCI"}, "platform_name": "MTG-i1" } assert attributes == expected_attributes diff --git a/satpy/tests/reader_tests/test_fci_l1c_nc.py b/satpy/tests/reader_tests/test_fci_l1c_nc.py index 9bb70834ed..428389c9eb 100644 --- a/satpy/tests/reader_tests/test_fci_l1c_nc.py +++ b/satpy/tests/reader_tests/test_fci_l1c_nc.py @@ -193,7 +193,7 @@ "orbital_parameters", "file_type", "name", - "sensor", + "instruments", "resolution", } @@ -1120,7 +1120,7 @@ def test_load_aux_data(self, reader_configs, fh_param): assert "orbital_parameters" in res[aux].attrs.keys() assert "file_type" in res[aux].attrs.keys() assert "name" in res[aux].attrs.keys() - assert "sensor" in res[aux].attrs.keys() + assert "instruments" in res[aux].attrs.keys() assert "resolution" in res[aux].attrs.keys() @pytest.mark.parametrize("fh_param", [(lazy_fixture("FakeFCIFileHandlerFDHSI_fixture")), @@ -1314,9 +1314,9 @@ def test_load_composite(self): # in the tests.compositor_tests package from satpy.composites.config_loader import load_compositor_configs_for_sensors - comps, mods = load_compositor_configs_for_sensors(["fci"]) - assert len(comps["fci"]) > 0 - assert len(mods["fci"]) > 0 + comps, mods = load_compositor_configs_for_sensors(["FCI"]) + assert len(comps["FCI"]) > 0 + assert len(mods["FCI"]) > 0 class TestFCIL1cNCReaderBadData: diff --git a/satpy/tests/reader_tests/test_fci_l2_nc.py b/satpy/tests/reader_tests/test_fci_l2_nc.py index 2761e92c60..ac374ff7c3 100644 --- a/satpy/tests/reader_tests/test_fci_l2_nc.py +++ b/satpy/tests/reader_tests/test_fci_l2_nc.py @@ -132,7 +132,7 @@ def test_all_basic(self): "filename": self.test_file, "spacecraft_name": "TEST_PLATFORM", "ssp_lon": 0.0, - "sensor": "test_data_source", + "instruments": {"test_data_source"}, "platform_name": "TEST_PLATFORM" } assert global_attributes == expected_global_attributes @@ -338,7 +338,7 @@ def test_all_basic(self): "filename": self.seg_test_file, "spacecraft_name": "TEST_FCI_PLATFORM", "ssp_lon": 0.0, - "sensor": "test_fci_data_source", + "instruments": {"test_fci_data_source"}, "platform_name": "TEST_FCI_PLATFORM" } assert global_attributes == expected_global_attributes @@ -627,7 +627,7 @@ def test_all_basic(self, amv_filehandler, amv_file): expected_global_attributes = { "filename": amv_file, "spacecraft_name": "TEST_PLATFORM", - "sensor": "test_data_source", + "instruments": {"test_data_source"}, "platform_name": "TEST_PLATFORM", "channel": "test_channel", "ssp_lon": 0.0, diff --git a/satpy/tests/reader_tests/test_ghi_l1.py b/satpy/tests/reader_tests/test_ghi_l1.py index 33991620cf..6f32c3bfc4 100644 --- a/satpy/tests/reader_tests/test_ghi_l1.py +++ b/satpy/tests/reader_tests/test_ghi_l1.py @@ -344,7 +344,7 @@ def test_ghi_for_one_resolution(self, resolution_to_test): def _check_calibration_and_units(self, band_names, result): for band_name in band_names: - assert result[band_name].attrs["sensor"].islower() + assert result[band_name].attrs["instruments"] == {"GHI"} assert result[band_name].shape == (2, 5) np.testing.assert_allclose(result[band_name].values, self.expected[band_name], equal_nan=True) self._check_units(band_name, result) diff --git a/satpy/tests/reader_tests/test_ghrsst_l2.py b/satpy/tests/reader_tests/test_ghrsst_l2.py index b4cabccfa4..facf8accbb 100644 --- a/satpy/tests/reader_tests/test_ghrsst_l2.py +++ b/satpy/tests/reader_tests/test_ghrsst_l2.py @@ -132,7 +132,7 @@ def test_get_sensor(self, tmp_path): self.fake_dataset.to_netcdf(os.fspath(tmp_filepath)) test = GHRSSTL2FileHandler(os.fspath(tmp_filepath), filename_info, None) - assert test.sensor == "viirs" + assert test.sensor == "VIIRS" def test_get_start_and_end_times(self, tmp_path): """Test retrieval of the sensor name from the netCDF file.""" diff --git a/satpy/tests/reader_tests/test_glm_l2.py b/satpy/tests/reader_tests/test_glm_l2.py index 0c73dda231..780f9da33e 100644 --- a/satpy/tests/reader_tests/test_glm_l2.py +++ b/satpy/tests/reader_tests/test_glm_l2.py @@ -154,7 +154,7 @@ def test_get_dataset(self): "scene_abbr": "C", "scene_id": None, "spatial_resolution": "2km at nadir", - "sensor": "glm", + "instruments": {"GLM"}, "timeline_ID": None, "grid_mapping": "goes_imager_projection", "standard_name": "flash_extent_density", @@ -185,7 +185,7 @@ def test_get_dataset_dqf(self): "scene_abbr": "C", "scene_id": None, "spatial_resolution": "2km at nadir", - "sensor": "glm", + "instruments": {"GLM"}, "timeline_ID": None, "grid_mapping": "goes_imager_projection", "units": "1", diff --git a/satpy/tests/reader_tests/test_goes_imager_nc_noaa.py b/satpy/tests/reader_tests/test_goes_imager_nc_noaa.py index 1049d814dd..4f83a69bdf 100644 --- a/satpy/tests/reader_tests/test_goes_imager_nc_noaa.py +++ b/satpy/tests/reader_tests/test_goes_imager_nc_noaa.py @@ -403,7 +403,7 @@ def test_get_dataset_counts(self): "projection_altitude": ALTITUDE, "yaw_flip": True}, "platform_name": "GOES-15", - "sensor": "goes_imager", + "instruments": {"IMAGER (GOES 12-15)"}, "sector": UNKNOWN_SECTOR, "nadir_row": 1, "nadir_col": 2, diff --git a/satpy/tests/reader_tests/test_iasi_l2.py b/satpy/tests/reader_tests/test_iasi_l2.py index 3f0e618119..fe0fb28ccd 100644 --- a/satpy/tests/reader_tests/test_iasi_l2.py +++ b/satpy/tests/reader_tests/test_iasi_l2.py @@ -157,7 +157,7 @@ def test_scene(test_data): assert scn.start_time is not None assert scn.end_time is not None assert scn.sensor_names - assert "iasi" in scn.sensor_names + assert "IASI" in scn.sensor_names def test_scene_load_available_datasets(test_data): @@ -200,8 +200,6 @@ def test_init(test_data, iasi_filehandler): assert iasi_filehandler.finfo == FNAME_INFO assert iasi_filehandler.lons is None assert iasi_filehandler.lats is None - assert iasi_filehandler.mda["platform_name"] == "Metop-B" - assert iasi_filehandler.mda["sensor"] == "iasi" def test_time_properties(iasi_filehandler): diff --git a/satpy/tests/reader_tests/test_iasi_ng_l2_nc.py b/satpy/tests/reader_tests/test_iasi_ng_l2_nc.py index 3480e57f77..4e694ccd91 100644 --- a/satpy/tests/reader_tests/test_iasi_ng_l2_nc.py +++ b/satpy/tests/reader_tests/test_iasi_ng_l2_nc.py @@ -309,7 +309,7 @@ def test_sensing_times(self, twv_handler): def test_sensor_names(self, twv_handler): """Test that the handler reports iasi_ng as sensor.""" - assert twv_handler.sensor_names == {"iasi_ng"} + assert twv_handler.sensor_names == {"IASI-NG"} def test_available_datasets(self, twv_scene): """Test the list of available datasets in scene.""" diff --git a/satpy/tests/reader_tests/test_ici_l1b_nc.py b/satpy/tests/reader_tests/test_ici_l1b_nc.py index 550d964008..cdc18bf8f9 100644 --- a/satpy/tests/reader_tests/test_ici_l1b_nc.py +++ b/satpy/tests/reader_tests/test_ici_l1b_nc.py @@ -522,7 +522,7 @@ def test_get_global_attributes(self, reader): "end_time": dt.datetime(2000, 1, 2, 4, 5, 6), "spacecraft_name": "SGB", "ssp_lon": None, - "sensor": "ICI", + "instruments": {"ICI"}, "filename_start_time": dt.datetime(2000, 1, 1, 1, 0), "filename_end_time": dt.datetime(2000, 1, 1, 2, 0), "platform_name": "SGB", diff --git a/satpy/tests/reader_tests/test_insat3d_img_l1b_h5.py b/satpy/tests/reader_tests/test_insat3d_img_l1b_h5.py index cf6850e92e..e1e37e2968 100644 --- a/satpy/tests/reader_tests/test_insat3d_img_l1b_h5.py +++ b/satpy/tests/reader_tests/test_insat3d_img_l1b_h5.py @@ -259,7 +259,7 @@ def test_insat3d_has_orbital_parameters(insat_filehandler): assert "satellite_nominal_altitude" in darr.attrs["orbital_parameters"] assert "satellite_actual_altitude" in darr.attrs["orbital_parameters"] assert "platform_name" in darr.attrs - assert "sensor" in darr.attrs + assert darr.attrs["instruments"] == {"IMAGER (INSAT)"} def test_filehandler_returns_coords(insat_filehandler): diff --git a/satpy/tests/reader_tests/test_li_l2_nc.py b/satpy/tests/reader_tests/test_li_l2_nc.py index 0ac3c79517..bf6428c7c7 100644 --- a/satpy/tests/reader_tests/test_li_l2_nc.py +++ b/satpy/tests/reader_tests/test_li_l2_nc.py @@ -259,8 +259,8 @@ def test_filename_infos(self, filetype_infos): # Should have some datasets: assert len(handler.provided_datasets) > 0 - # Sensor names should be just 'li' - assert handler.sensor_names == {"li"} + # Sensor names should be just 'LI' + assert handler.sensor_names == {"LI"} # check product type: assert handler.product_type == "2-AF" @@ -279,7 +279,7 @@ def test_platform_name_and_sensor(self, filetype_infos): dsid = make_dataid(name="flash_duration") dset = handler.get_dataset(dsid) assert dset.attrs["platform_name"] == "Meteosat-12" - assert dset.attrs["sensor"] == "li" + assert dset.attrs["instruments"] == {"LI"} def test_var_path_exists(self, filetype_infos): """Test variable_path_exists from li reader.""" diff --git a/satpy/tests/reader_tests/test_mimic_TPW2_lowres.py b/satpy/tests/reader_tests/test_mimic_TPW2_lowres.py index e2696b420d..914c101488 100644 --- a/satpy/tests/reader_tests/test_mimic_TPW2_lowres.py +++ b/satpy/tests/reader_tests/test_mimic_TPW2_lowres.py @@ -120,7 +120,7 @@ def test_load_mimic_float(self, mimic_file): assert len(ds) == len(float_variables) for d in ds.values(): assert d.attrs["platform_shortname"] == "aggregated microwave" - assert d.attrs["sensor"] == "mimic" + assert d.attrs["instruments"] == {"MIMIC"} assert d.attrs["units"] == "mm" assert "area" in d.attrs assert d.attrs["area"] is not None @@ -135,7 +135,7 @@ def test_load_mimic_timedelta(self, mimic_file): assert len(ds) == len(date_variables) for d in ds.values(): assert d.attrs["platform_shortname"] == "aggregated microwave" - assert d.attrs["sensor"] == "mimic" + assert d.attrs["instruments"] == {"MIMIC"} assert d.attrs["units"] == "minutes" assert "area" in d.attrs assert d.attrs["area"] is not None @@ -151,7 +151,7 @@ def test_load_mimic_ubyte(self, mimic_file): assert len(ds) == len(ubyte_variables) for d in ds.values(): assert d.attrs["platform_shortname"] == "aggregated microwave" - assert d.attrs["sensor"] == "mimic" + assert d.attrs["instruments"] == {"MIMIC"} assert "source_key" in d.attrs assert "area" in d.attrs assert d.attrs["area"] is not None diff --git a/satpy/tests/reader_tests/test_mimic_TPW2_nc.py b/satpy/tests/reader_tests/test_mimic_TPW2_nc.py index 6a8f5a1f2e..3cd1523192 100644 --- a/satpy/tests/reader_tests/test_mimic_TPW2_nc.py +++ b/satpy/tests/reader_tests/test_mimic_TPW2_nc.py @@ -97,7 +97,7 @@ def test_load_mimic(self, mimic_file): assert len(ds) == 1 for d in ds.values(): assert d.attrs["platform_shortname"] == "aggregated microwave" - assert d.attrs["sensor"] == "mimic" + assert d.attrs["instruments"] == {"MIMIC"} assert "area" in d.attrs assert "units" in d.attrs assert d.attrs["area"] is not None diff --git a/satpy/tests/reader_tests/test_mviri_l1b_fiduceo_nc.py b/satpy/tests/reader_tests/test_mviri_l1b_fiduceo_nc.py index 501592d030..249ed0fb70 100644 --- a/satpy/tests/reader_tests/test_mviri_l1b_fiduceo_nc.py +++ b/satpy/tests/reader_tests/test_mviri_l1b_fiduceo_nc.py @@ -51,7 +51,7 @@ attrs_exp: dict = { "platform": "MET7", "raw_metadata": {"foo": "bar"}, - "sensor": "MVIRI", + "instruments": {"MVIRI"}, "orbital_parameters": { "projection_longitude": 57.0, "projection_latitude": 0.0, @@ -372,7 +372,7 @@ def fixture_file_handler(fake_file, request, projection_longitude): fh_class, filename=fake_file, filename_info={"platform": "MET7", - "sensor": "MVIRI", + "instrument": "MVIRI", "projection_longitude": projection_longitude}, filetype_info={"foo": "bar"}, mask_bad_quality=True, diff --git a/satpy/tests/reader_tests/test_mws_l1b_nc.py b/satpy/tests/reader_tests/test_mws_l1b_nc.py index 07066346b9..47764c4d90 100644 --- a/satpy/tests/reader_tests/test_mws_l1b_nc.py +++ b/satpy/tests/reader_tests/test_mws_l1b_nc.py @@ -359,7 +359,7 @@ def test_get_global_attributes(self, reader): "start_time": dt.datetime(2000, 1, 2, 3, 4, 5), "end_time": dt.datetime(2000, 1, 2, 4, 5, 6), "spacecraft_name": "Metop-SG-A1", - "sensor": "MWS", + "instruments": {"MWS"}, "filename_start_time": dt.datetime(2000, 1, 1, 1, 0), "filename_end_time": dt.datetime(2000, 1, 1, 2, 0), "platform_name": "Metop-SG-A1", diff --git a/satpy/tests/reader_tests/test_nucaps.py b/satpy/tests/reader_tests/test_nucaps.py index 866f11b825..8f25d27b70 100644 --- a/satpy/tests/reader_tests/test_nucaps.py +++ b/satpy/tests/reader_tests/test_nucaps.py @@ -214,7 +214,7 @@ def test_load_nonpressure_based(self): # self.assertNotEqual(v.info['resolution'], 0) # self.assertEqual(v.info['units'], 'degrees') assert v.ndim == 1 - assert v.attrs["sensor"] == set(["cris", "atms", "viirs"]) + assert v.attrs["instruments"] == set(["CrIS", "ATMS", "VIIRS"]) assert isinstance(v.attrs["start_time"], datetime.datetime) assert isinstance(v.attrs["end_time"], datetime.datetime) @@ -413,7 +413,7 @@ def test_load_nonpressure_based(self): assert len(datasets) == 5 for v in datasets.values(): assert v.ndim == 1 - assert v.attrs["sensor"] == set(["cris", "atms", "viirs"]) + assert v.attrs["instruments"] == set(["CrIS", "ATMS", "VIIRS"]) assert isinstance(v.attrs["start_time"], datetime.datetime) assert isinstance(v.attrs["end_time"], datetime.datetime) diff --git a/satpy/tests/reader_tests/test_nwcsaf_hrw_nc.py b/satpy/tests/reader_tests/test_nwcsaf_hrw_nc.py index 7eedc42a61..fdaddce136 100644 --- a/satpy/tests/reader_tests/test_nwcsaf_hrw_nc.py +++ b/satpy/tests/reader_tests/test_nwcsaf_hrw_nc.py @@ -185,7 +185,7 @@ def test_hrw_handler_init(hrw_file): assert fh.filename_info == FILENAME_INFO assert fh.filetype_info == FILETYPE_INFO assert fh.platform_name is not None - assert fh.sensor == "seviri" + assert fh.sensor == "SEVIRI" def test_available_datasets(hrw_file): @@ -289,7 +289,7 @@ def test_hrw_handler_init_v2025(hrw_v2025_file): assert fh.filename_info == FILENAME_INFO_V2025 assert fh.filetype_info == FILETYPE_INFO assert fh.platform_name is not None - assert fh.sensor == "fci" + assert fh.sensor == "FCI" def test_available_datasets_v2025(hrw_v2025_file): diff --git a/satpy/tests/reader_tests/test_nwcsaf_nc.py b/satpy/tests/reader_tests/test_nwcsaf_nc.py index 71431cecdc..f72ed626cd 100644 --- a/satpy/tests/reader_tests/test_nwcsaf_nc.py +++ b/satpy/tests/reader_tests/test_nwcsaf_nc.py @@ -309,19 +309,19 @@ def nwcsaf_old_geo_ct_filehandler(nwcsaf_old_geo_ct_filename): class TestNcNWCSAFGeo: """Test the NcNWCSAF reader for Geo products.""" - @pytest.mark.parametrize(("platform", "instrument"), [("Metop-B", "avhrr-3"), - ("NOAA-20", "viirs"), - ("Himawari-8", "ahi"), - ("GOES-17", "abi"), - ("Meteosat-11", "seviri")]) + @pytest.mark.parametrize(("platform", "instrument"), [("Metop-B", "AVHRR/3"), + ("NOAA-20", "VIIRS"), + ("Himawari-8", "AHI"), + ("GOES-17", "ABI"), + ("Meteosat-11", "SEVIRI")]) def test_sensor_name_platform(self, nwcsaf_geo_ct_filehandler, platform, instrument): """Test that the correct sensor name is being set.""" nwcsaf_geo_ct_filehandler.set_platform_and_sensor(platform_name=platform) assert nwcsaf_geo_ct_filehandler.sensor == set([instrument]) assert nwcsaf_geo_ct_filehandler.sensor_names == set([instrument]) - @pytest.mark.parametrize(("platform", "instrument"), [("GOES16", "abi"), - ("MSG4", "seviri")]) + @pytest.mark.parametrize(("platform", "instrument"), [("GOES16", "ABI"), + ("MSG4", "SEVIRI")]) def test_sensor_name_sat_id(self, nwcsaf_geo_ct_filehandler, platform, instrument): """Test that the correct sensor name is being set.""" nwcsaf_geo_ct_filehandler.set_platform_and_sensor(sat_id=platform) diff --git a/satpy/tests/reader_tests/test_oceancolorcci_l3_nc.py b/satpy/tests/reader_tests/test_oceancolorcci_l3_nc.py index 6c0cae30a4..42ec26eea0 100644 --- a/satpy/tests/reader_tests/test_oceancolorcci_l3_nc.py +++ b/satpy/tests/reader_tests/test_oceancolorcci_l3_nc.py @@ -202,7 +202,7 @@ def test_get_dataset_monthly_allprods(self, fake_dataset, fake_file_dict): assert len(res) == len(ds_list_all) for curds in ds_list_all: np.testing.assert_allclose(res[curds].values, fake_dataset[ds_dict[curds]].values) - assert res[curds].attrs["sensor"] == "merged" + assert res[curds].attrs["instruments"] == {"SeaWiFS", "MERIS", "MODIS", "VIIRS"} assert res[curds].attrs["composite_period"] == "monthly" def test_get_dataset_8d_iopprods(self, fake_dataset, fake_file_dict): @@ -214,7 +214,7 @@ def test_get_dataset_8d_iopprods(self, fake_dataset, fake_file_dict): assert len(res) == len(ds_list_iop) for curds in ds_list_iop: np.testing.assert_allclose(res[curds].values, fake_dataset[ds_dict[curds]].values) - assert res[curds].attrs["sensor"] == "merged" + assert res[curds].attrs["instruments"] == {"SeaWiFS", "MERIS", "MODIS", "VIIRS"} assert res[curds].attrs["composite_period"] == "8-day" def test_get_dataset_1d_kprods(self, fake_dataset, fake_file_dict): @@ -226,7 +226,7 @@ def test_get_dataset_1d_kprods(self, fake_dataset, fake_file_dict): assert len(res) == len(ds_list_kd) for curds in ds_list_kd: np.testing.assert_allclose(res[curds].values, fake_dataset[ds_dict[curds]].values) - assert res[curds].attrs["sensor"] == "merged" + assert res[curds].attrs["instruments"] == {"SeaWiFS", "MERIS", "MODIS", "VIIRS"} assert res[curds].attrs["composite_period"] == "daily" def test_get_dataset_5d_allprods(self, fake_dataset, fake_file_dict): @@ -238,7 +238,7 @@ def test_get_dataset_5d_allprods(self, fake_dataset, fake_file_dict): assert len(res) == len(ds_list_all) for curds in ds_list_all: np.testing.assert_allclose(res[curds].values, fake_dataset[ds_dict[curds]].values) - assert res[curds].attrs["sensor"] == "merged" + assert res[curds].attrs["instruments"] == {"SeaWiFS", "MERIS", "MODIS", "VIIRS"} assert res[curds].attrs["composite_period"] == "5-day" def test_start_time(self, fake_file_dict): diff --git a/satpy/tests/reader_tests/test_oci_l2_bgc.py b/satpy/tests/reader_tests/test_oci_l2_bgc.py index 80a3d8ca56..f1242cf6e0 100644 --- a/satpy/tests/reader_tests/test_oci_l2_bgc.py +++ b/satpy/tests/reader_tests/test_oci_l2_bgc.py @@ -59,7 +59,7 @@ def test_load_chlor_a(self, oci_l2_bgc_netcdf, apply_quality_flags): data_arr = scene["chlor_a"] assert data_arr.dims == ("y", "x") assert data_arr.attrs["platform_name"] == "PACE" - assert data_arr.attrs["sensor"] == {"oci"} + assert data_arr.attrs["instruments"] == {"OCI"} assert data_arr.attrs["units"] == "mg m^-3" assert data_arr.dtype.type == np.float32 assert isinstance(data_arr.attrs["area"], SwathDefinition) diff --git a/satpy/tests/reader_tests/test_olci_nc.py b/satpy/tests/reader_tests/test_olci_nc.py index a458734d9d..e354908f0d 100644 --- a/satpy/tests/reader_tests/test_olci_nc.py +++ b/satpy/tests/reader_tests/test_olci_nc.py @@ -278,7 +278,7 @@ def test_chl_nn(self, mocked_dataset): coords={"rows": np.arange(5), "columns": np.arange(6)}, attrs=attr_dict) - ds_info = {"name": "chl_nn", "sensor": "olci", "resolution": 300, + ds_info = {"name": "chl_nn", "instruments": {"OLCI"}, "resolution": 300, "standard_name": "algal_pigment_concentration", "units": "lg(re mg.m-3)", "coordinates": ("longitude", "latitude"), "file_type": "esa_l2_chl_nn", "nc_key": "CHL_NN", "modifiers": ()} diff --git a/satpy/tests/reader_tests/test_osisaf_l3.py b/satpy/tests/reader_tests/test_osisaf_l3.py index 106687a509..b2b2fe8623 100644 --- a/satpy/tests/reader_tests/test_osisaf_l3.py +++ b/satpy/tests/reader_tests/test_osisaf_l3.py @@ -173,6 +173,8 @@ def test_get_dataset(self, tmp_path): test_ds = np.where(test_ds > self.maxv, np.nan, test_ds) test_ds = test_ds / self.scl + self.add np.testing.assert_allclose(res.values, test_ds) + if "sensor" in self.fake_dataset.attrs: + assert res.attrs["instruments"] == {"AVHRR", "VIIRS"} with pytest.raises(KeyError): test.get_dataset(DataQuery(name="erroneous dataset"), {"standard_name": "erroneous dataset"}) diff --git a/satpy/tests/reader_tests/test_satpy_cf_nc.py b/satpy/tests/reader_tests/test_satpy_cf_nc.py index 52550ce1ec..e5e490a180 100644 --- a/satpy/tests/reader_tests/test_satpy_cf_nc.py +++ b/satpy/tests/reader_tests/test_satpy_cf_nc.py @@ -61,7 +61,7 @@ def _create_test_netcdf(filename, resolution=742): "resolution": resolution}) scene = Scene() - scene.attrs["sensor"] = ["viirs"] + scene.attrs["instruments"] = {"VIIRS"} scene_dict = { "lat": lat, "lon": lon, @@ -256,7 +256,7 @@ def datasets(vis006, ir_108, qual_flags, lonlats, prefix_data, swath_data): def cf_scene(datasets, common_attrs): """Create a cf scene.""" scene = Scene() - scene.attrs["sensor"] = ["avhrr-1", "avhrr-2", "avhrr-3"] + scene.attrs["instruments"] = {"AVHRR/1", "AVHRR/2", "AVHRR/3"} for key in datasets: scene[key] = datasets[key] if key != "swath_data": diff --git a/satpy/tests/reader_tests/test_seadas_l2.py b/satpy/tests/reader_tests/test_seadas_l2.py index eed0713366..e2c245c293 100644 --- a/satpy/tests/reader_tests/test_seadas_l2.py +++ b/satpy/tests/reader_tests/test_seadas_l2.py @@ -229,10 +229,10 @@ def test_scene_available_datasets(self, input_files): @pytest.mark.parametrize( ("input_files", "exp_plat", "exp_sensor", "exp_rps"), [ - (lazy_fixture("seadas_l2_modis_chlor_a"), "Aqua", {"modis"}, 10), - (lazy_fixture("seadas_l2_viirs_npp_chlor_a"), "Suomi-NPP", {"viirs"}, 16), - (lazy_fixture("seadas_l2_viirs_j01_chlor_a"), "NOAA-20", {"viirs"}, 16), - (lazy_fixture("seadas_l2_modis_chlor_a_netcdf"), "Terra", {"modis"}, 10), + (lazy_fixture("seadas_l2_modis_chlor_a"), "Aqua", {"MODIS"}, 10), + (lazy_fixture("seadas_l2_viirs_npp_chlor_a"), "Suomi-NPP", {"VIIRS"}, 16), + (lazy_fixture("seadas_l2_viirs_j01_chlor_a"), "NOAA-20", {"VIIRS"}, 16), + (lazy_fixture("seadas_l2_modis_chlor_a_netcdf"), "Terra", {"MODIS"}, 10), ]) @pytest.mark.parametrize("apply_quality_flags", [False, True]) def test_load_chlor_a(self, input_files, exp_plat, exp_sensor, exp_rps, apply_quality_flags): @@ -243,7 +243,7 @@ def test_load_chlor_a(self, input_files, exp_plat, exp_sensor, exp_rps, apply_qu data_arr = scene["chlor_a"] assert data_arr.dims == ("y", "x") assert data_arr.attrs["platform_name"] == exp_plat - assert data_arr.attrs["sensor"] == exp_sensor + assert data_arr.attrs["instruments"] == exp_sensor assert data_arr.attrs["units"] == "mg m^-3" assert data_arr.dtype.type == np.float32 assert isinstance(data_arr.attrs["area"], SwathDefinition) diff --git a/satpy/tests/reader_tests/test_seviri_l1b_native.py b/satpy/tests/reader_tests/test_seviri_l1b_native.py index c2a5ffee9e..0be4f9055f 100644 --- a/satpy/tests/reader_tests/test_seviri_l1b_native.py +++ b/satpy/tests/reader_tests/test_seviri_l1b_native.py @@ -1052,7 +1052,7 @@ def _exp_data_array(): }, "georef_offset_corrected": True, "platform_name": "MSG-3", - "sensor": "seviri", + "instruments": {"SEVIRI"}, "units": "1", "wavelength": (1, 2, 3), "standard_name": "counts", diff --git a/satpy/tests/reader_tests/test_seviri_l1b_nc.py b/satpy/tests/reader_tests/test_seviri_l1b_nc.py index cbd66b10be..859cbb7213 100644 --- a/satpy/tests/reader_tests/test_seviri_l1b_nc.py +++ b/satpy/tests/reader_tests/test_seviri_l1b_nc.py @@ -332,7 +332,7 @@ def test_get_dataset(self, file_handler, channel, calibration, mask_bad_quality_ }, "georef_offset_corrected": True, "platform_name": "Meteosat-11", - "sensor": "seviri", + "instruments": {"SEVIRI"}, "units": "units", "wavelength": "wavelength", "standard_name": "standard_name" diff --git a/satpy/tests/reader_tests/test_sgli_l1b.py b/satpy/tests/reader_tests/test_sgli_l1b.py index d959f810a7..faf5adc260 100644 --- a/satpy/tests/reader_tests/test_sgli_l1b.py +++ b/satpy/tests/reader_tests/test_sgli_l1b.py @@ -188,7 +188,7 @@ def test_get_dataset_counts(sgli_vn_file): assert np.allclose(res, FULL_KM_ARRAY & MASK) assert res.dtype == np.uint16 assert res.attrs["platform_name"] == "GCOM-C1" - assert res.attrs["sensor"] == "sgli" + assert res.attrs["instruments"] == {"SGLI"} def test_get_dataset_for_unknown_channel(sgli_vn_file): """Test that counts can be extracted from a file.""" diff --git a/satpy/tests/reader_tests/test_smos_l2_wind.py b/satpy/tests/reader_tests/test_smos_l2_wind.py index 3255406124..92cb750dce 100644 --- a/satpy/tests/reader_tests/test_smos_l2_wind.py +++ b/satpy/tests/reader_tests/test_smos_l2_wind.py @@ -91,7 +91,7 @@ def test_load_wind_speed(self, smos_l2_file): assert len(ds) == 1 for d in ds.values(): assert d.attrs["platform_shortname"] == "SM" - assert d.attrs["sensor"] == "MIRAS" + assert d.attrs["instruments"] == {"MIRAS"} assert "area" in d.attrs assert d.attrs["area"] is not None assert "y" in d.dims diff --git a/satpy/tests/reader_tests/test_tropomi_l2.py b/satpy/tests/reader_tests/test_tropomi_l2.py index c9d96a136f..ee33f29b03 100644 --- a/satpy/tests/reader_tests/test_tropomi_l2.py +++ b/satpy/tests/reader_tests/test_tropomi_l2.py @@ -119,7 +119,7 @@ def test_load_no2(self, tropomi_no2_file: Path): assert len(ds) == 1 for d in ds.values(): assert d.attrs["platform_shortname"] == "S5P" - assert d.attrs["sensor"] == "tropomi" + assert d.attrs["instruments"] == {"TROPOMI"} assert d.attrs["time_coverage_start"] == dt.datetime(2018, 7, 9, 17, 25, 34) assert d.attrs["time_coverage_end"] == dt.datetime(2018, 7, 9, 18, 23, 4) assert "area" in d.attrs diff --git a/satpy/tests/reader_tests/test_vii_base_nc.py b/satpy/tests/reader_tests/test_vii_base_nc.py index 9948a135c8..9c90427525 100644 --- a/satpy/tests/reader_tests/test_vii_base_nc.py +++ b/satpy/tests/reader_tests/test_vii_base_nc.py @@ -177,7 +177,7 @@ def test_file_reading(self): "end_time": expected_end_time, "spacecraft_name": "test_spacecraft", "ssp_lon": None, - "sensor": "test_instrument", + "instruments": {"test_instrument"}, "filename_start_time": datetime.datetime(year=2017, month=9, day=20, hour=12, minute=30, second=30), "filename_end_time": datetime.datetime(year=2017, month=9, day=20, diff --git a/satpy/tests/reader_tests/test_viirs_edr.py b/satpy/tests/reader_tests/test_viirs_edr.py index b234fe89b6..81d2d4ec71 100644 --- a/satpy/tests/reader_tests/test_viirs_edr.py +++ b/satpy/tests/reader_tests/test_viirs_edr.py @@ -633,7 +633,7 @@ def _array_checks(data_arr: xr.DataArray, dtype: npt.Dtype = np.float32, multipl def _shared_metadata_checks(data_arr: xr.DataArray) -> None: is_mband_res = _is_mband_res(data_arr) exp_rps = 16 if is_mband_res else 32 - assert data_arr.attrs["sensor"] == "viirs" + assert data_arr.attrs["instruments"] == {"VIIRS"} assert data_arr.attrs["rows_per_scan"] == exp_rps lons = data_arr.attrs["area"].lons diff --git a/satpy/tests/reader_tests/test_viirs_edr_active_fires.py b/satpy/tests/reader_tests/test_viirs_edr_active_fires.py index c4a9dfda90..21584cad01 100644 --- a/satpy/tests/reader_tests/test_viirs_edr_active_fires.py +++ b/satpy/tests/reader_tests/test_viirs_edr_active_fires.py @@ -208,7 +208,7 @@ def test_load_dataset(self): for v in datasets.values(): assert v.attrs["units"] == "MW" assert v.attrs["platform_name"] == "NOAA-21" - assert v.attrs["sensor"] == "viirs" + assert v.attrs["instruments"] == {"VIIRS"} class TestImgVIIRSActiveFiresNetCDF4(unittest.TestCase): @@ -265,7 +265,7 @@ def test_load_dataset(self): for v in datasets.values(): assert v.attrs["units"] == "MW" assert v.attrs["platform_name"] == "Suomi-NPP" - assert v.attrs["sensor"] == "viirs" + assert v.attrs["instruments"] == {"VIIRS"} @mock.patch("satpy.readers.viirs_edr_active_fires.dd.read_csv") @@ -321,7 +321,7 @@ def test_load_dataset(self, csv_mock): for v in datasets.values(): assert v.attrs["units"] == "MW" assert v.attrs["platform_name"] == "NOAA-20" - assert v.attrs["sensor"] == "VIIRS" + assert v.attrs["instruments"] == {"VIIRS"} @mock.patch("satpy.readers.viirs_edr_active_fires.dd.read_csv") @@ -379,4 +379,4 @@ def test_load_dataset(self, mock_obj): for v in datasets.values(): assert v.attrs["units"] == "MW" assert v.attrs["platform_name"] == "Suomi-NPP" - assert v.attrs["sensor"] == "VIIRS" + assert v.attrs["instruments"] == {"VIIRS"} diff --git a/satpy/tests/reader_tests/test_viirs_l1b.py b/satpy/tests/reader_tests/test_viirs_l1b.py index 19317a0df1..c4d4251855 100644 --- a/satpy/tests/reader_tests/test_viirs_l1b.py +++ b/satpy/tests/reader_tests/test_viirs_l1b.py @@ -218,7 +218,7 @@ def test_load_every_m_band_bt(self): assert v.attrs["rows_per_scan"] == 2 assert v.attrs["area"].lons.attrs["rows_per_scan"] == 2 assert v.attrs["area"].lats.attrs["rows_per_scan"] == 2 - assert v.attrs["sensor"] == "viirs" + assert v.attrs["instruments"] == {"VIIRS"} assert "scale_factor" not in v.attrs assert "add_offset" not in v.attrs @@ -249,7 +249,7 @@ def test_load_every_m_band_refl(self): assert v.attrs["rows_per_scan"] == 2 assert v.attrs["area"].lons.attrs["rows_per_scan"] == 2 assert v.attrs["area"].lats.attrs["rows_per_scan"] == 2 - assert v.attrs["sensor"] == "viirs" + assert v.attrs["instruments"] == {"VIIRS"} assert "scale_factor" not in v.attrs assert "add_offset" not in v.attrs @@ -286,7 +286,7 @@ def test_load_every_m_band_rad(self): assert v.attrs["rows_per_scan"] == 2 assert v.attrs["area"].lons.attrs["rows_per_scan"] == 2 assert v.attrs["area"].lats.attrs["rows_per_scan"] == 2 - assert v.attrs["sensor"] == "viirs" + assert v.attrs["instruments"] == {"VIIRS"} assert v.attrs["day_night"] == "Day" assert v.attrs["orbital_parameters"]["start_direction"] == "Descending" assert v.attrs["orbital_parameters"]["end_direction"] == "Ascending" @@ -316,7 +316,7 @@ def test_load_i_band_angles(self): assert len(datasets) == 4 for v in datasets.values(): assert v.attrs["resolution"] == 371 - assert v.attrs["sensor"] == "viirs" + assert v.attrs["instruments"] == {"VIIRS"} assert "scale_factor" not in v.attrs assert "add_offset" not in v.attrs @@ -337,7 +337,7 @@ def test_load_dnb_radiance(self): assert v.attrs["rows_per_scan"] == 2 assert v.attrs["area"].lons.attrs["rows_per_scan"] == 2 assert v.attrs["area"].lats.attrs["rows_per_scan"] == 2 - assert v.attrs["sensor"] == "viirs" + assert v.attrs["instruments"] == {"VIIRS"} assert "scale_factor" not in v.attrs assert "add_offset" not in v.attrs @@ -363,7 +363,7 @@ def test_load_dnb_angles(self): assert v.attrs["rows_per_scan"] == 2 assert v.attrs["area"].lons.attrs["rows_per_scan"] == 2 assert v.attrs["area"].lats.attrs["rows_per_scan"] == 2 - assert v.attrs["sensor"] == "viirs" + assert v.attrs["instruments"] == {"VIIRS"} assert "scale_factor" not in v.attrs assert "add_offset" not in v.attrs @@ -387,7 +387,7 @@ def test_available_datasets_quality_flags(self): assert len(datasets) == 2 for v in datasets.values(): assert v.attrs["units"] == "1" - assert v.attrs["sensor"] == "viirs" + assert v.attrs["instruments"] == {"VIIRS"} assert "scale_factor" not in v.attrs assert "add_offset" not in v.attrs diff --git a/satpy/tests/reader_tests/test_viirs_l2.py b/satpy/tests/reader_tests/test_viirs_l2.py index 25ec661b79..3f230a1b46 100644 --- a/satpy/tests/reader_tests/test_viirs_l2.py +++ b/satpy/tests/reader_tests/test_viirs_l2.py @@ -130,7 +130,7 @@ def test_load_l2_files(self, filename, datasets): for d in loaded_datasets.values(): assert d.shape == DEFAULT_FILE_SHAPE assert d.dims == ("y", "x") - assert d.attrs["sensor"] == "viirs" + assert d.attrs["instruments"] == {"VIIRS"} d_np = d.compute() assert d.dtype == d_np.dtype assert d.dtype == np.float32 diff --git a/satpy/tests/reader_tests/test_virr_l1b.py b/satpy/tests/reader_tests/test_virr_l1b.py index f065ba8804..8334f0fb3a 100644 --- a/satpy/tests/reader_tests/test_virr_l1b.py +++ b/satpy/tests/reader_tests/test_virr_l1b.py @@ -134,7 +134,7 @@ def _fy3_helper(self, platform_name, reader, Emissive_units): ds = datasets[dataset["name"]] attributes = ds.attrs assert isinstance(ds.data, da.Array) - assert "virr" == attributes["sensor"] + assert attributes["instruments"] == {"VIRR"} assert platform_name == attributes["platform_name"] assert datetime.datetime(2018, 12, 25, 21, 41, 47, 90000) == attributes["start_time"] assert datetime.datetime(2018, 12, 25, 21, 47, 28, 254000) == attributes["end_time"] diff --git a/satpy/tests/writer_tests/test_core/test_base.py b/satpy/tests/writer_tests/test_core/test_base.py index 029d828973..6622425c56 100644 --- a/satpy/tests/writer_tests/test_core/test_base.py +++ b/satpy/tests/writer_tests/test_core/test_base.py @@ -46,12 +46,12 @@ def setup_method(self): attrs={ "name": "test", "start_time": dt.datetime(2018, 1, 1, 0, 0, 0), - "sensor": "fake_sensor", + "instruments": {"fake_sensor"}, "area": adef, } ) ds2 = ds1.copy() - ds2.attrs["sensor"] = {"fake_sensor1", "fake_sensor2"} + ds2.attrs["instruments"] = {"fake_sensor1", "fake_sensor2"} self.scn = Scene() self.scn["test"] = ds1 self.scn["test2"] = ds2 @@ -76,7 +76,7 @@ def test_save_dataset_static_filename(self): [ ("geotiff_{name}_{start_time:%Y%m%d_%H%M%S}.tif", ["geotiff_test_20180101_000000.tif", "geotiff_test2_20180101_000000.tif"]), - ("geotiff_{name}_{sensor}.tif", + ("geotiff_{name}_{instruments}.tif", ["geotiff_test_fake_sensor.tif", "geotiff_test2_fake_sensor1-fake_sensor2.tif"]), ] ) From b448d8390eea5d0fc01bf52f9479756b81887005 Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Wed, 13 May 2026 15:53:52 +0000 Subject: [PATCH 33/72] Replace sensors with instruments in yaml reader --- satpy/readers/core/yaml_reader.py | 24 +++++++++++++++++------- satpy/tests/test_yaml_reader.py | 10 +++++----- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/satpy/readers/core/yaml_reader.py b/satpy/readers/core/yaml_reader.py index 1982aac346..cb6904be4c 100644 --- a/satpy/readers/core/yaml_reader.py +++ b/satpy/readers/core/yaml_reader.py @@ -148,8 +148,18 @@ def __init__(self, config_dict): filetype_info["file_patterns"] = file_patterns self.file_patterns.extend(file_patterns) - if "sensors" in self.info and not isinstance(self.info["sensors"], (list, tuple)): - self.info["sensors"] = [self.info["sensors"]] + if "sensors" in self.info: + warnings.warn( + "Renaming the 'sensors' reader attribute to 'instruments'. " + "This will raise an exception in Satpy v1.1 when the 'sensors' " + "attribute will be removed. To silence this warning, rename " + "'sensors' to 'instruments' in your reader YAML file.", + DeprecationWarning, + stacklevel=3 + ) + self.info["instruments"] = self.info["sensors"] + if "instruments" in self.info and not isinstance(self.info["instruments"], (list, tuple)): + self.info["instruments"] = [self.info["instruments"]] self.datasets = self.config.get("datasets", {}) self._id_keys = self.info.get("data_identification_keys", default_id_keys_config) self._co_keys = self.info.get("coord_identification_keys", default_co_keys_config) @@ -166,7 +176,7 @@ def from_config_files(cls, *config_files, **reader_kwargs): @property def sensor_names(self): """Names of sensors whose data is being loaded by this reader.""" - return self.info["sensors"] or [] + return self.info["instruments"] or [] @property def all_dataset_ids(self): @@ -218,7 +228,7 @@ def supports_sensor(self, sensor): Returns True is *sensor* is None. """ - if sensor and not (set(self.info.get("sensors")) & + if sensor and not (set(self.info.get("instruments")) & set(listify_string(sensor))): return False return True @@ -488,7 +498,7 @@ def __init__(self, def sensor_names(self): """Names of sensors whose data is being loaded by this reader.""" if not self.file_handlers: - return self.info["sensors"] + return self.info["instruments"] file_handlers = (handlers[0] for handlers in self.file_handlers.values()) @@ -499,7 +509,7 @@ def sensor_names(self): except NotImplementedError: continue if not sensor_names: - return self.info["sensors"] + return self.info["instruments"] return sorted(sensor_names) @property @@ -819,7 +829,7 @@ def _make_swath_definition_from_lons_lats(self, lons, lats): sdef = None if sdef is None: sdef = SwathDefinition(lons, lats) - sensor_str = "_".join(self.info["sensors"]) + sensor_str = "_".join(self.info["instruments"]) shape_str = "_".join(map(str, lons.shape)) sdef.name = "{}_{}_{}_{}".format(sensor_str, shape_str, lons.attrs.get("name", lons.name), diff --git a/satpy/tests/test_yaml_reader.py b/satpy/tests/test_yaml_reader.py index 86e539737e..c90fba0cc0 100644 --- a/satpy/tests/test_yaml_reader.py +++ b/satpy/tests/test_yaml_reader.py @@ -41,7 +41,7 @@ MHS_YAML_READER_DICT = { "reader": {"name": "mhs_l1c_aapp", "description": "AAPP l1c Reader for AMSU-B/MHS data", - "sensors": ["mhs"], + "instruments": ["mhs"], "data_identification_keys": {"name": {"required": True}, "frequency_double_sideband": {"type": FrequencyDoubleSideBand}, @@ -206,7 +206,7 @@ def setUp(self): patterns = ["a{something:3s}.bla", "a0{something:2s}.bla"] res_dict = {"reader": {"name": "fake", - "sensors": ["canon"]}, + "instruments": ["canon"]}, "file_types": {"ftype1": {"name": "ft1", "file_patterns": patterns, "file_reader": DummyReader}}, @@ -300,7 +300,7 @@ def setUp(self): """Prepare a reader instance with a fake config.""" patterns = ["a{something:3s}.bla"] res_dict = {"reader": {"name": "fake", - "sensors": ["canon"]}, + "instruments": ["canon"]}, "file_types": {"ftype1": {"name": "ft1", "file_reader": BaseFileHandler, "file_patterns": patterns}}, @@ -561,7 +561,7 @@ def setUp(self): """Prepare a reader instance with a fake config.""" patterns = ["a{something:3s}.bla"] res_dict = {"reader": {"name": "fake", - "sensors": ["canon"]}, + "instruments": ["canon"]}, "file_types": {"ftype1": {"name": "ft1", "file_reader": BaseFileHandler, "file_patterns": patterns}}, @@ -639,7 +639,7 @@ def setUp(self): patterns2 = ["b.nc"] patterns3 = ["geo.nc"] res_dict = {"reader": {"name": "fake", - "sensors": ["canon"]}, + "instruments": ["canon"]}, "file_types": {"ftype1": {"name": "ft1", "file_patterns": patterns1}, "ftype2": {"name": "ft2", From cddd65960979bab41a3759e2922ba199f66ba425 Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Wed, 13 May 2026 15:56:26 +0000 Subject: [PATCH 34/72] Add helper for GOES Imager --- satpy/readers/core/goes_imager.py | 59 +++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 satpy/readers/core/goes_imager.py diff --git a/satpy/readers/core/goes_imager.py b/satpy/readers/core/goes_imager.py new file mode 100644 index 0000000000..e7d87e79fb --- /dev/null +++ b/satpy/readers/core/goes_imager.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Copyright (c) 2018 Satpy developers +# +# This file is part of satpy. +# +# satpy is free software: you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation, either version 3 of the License, or (at your option) any later +# version. +# +# satpy is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with +# satpy. If not, see . +"""Common functionality for GOES Imager readers.""" + +SPACECRAFTS = { + # these are GP_SC_ID + 18007: "GOES-7", + 18008: "GOES-8", + 18009: "GOES-9", + 18010: "GOES-10", + 18011: "GOES-11", + 18012: "GOES-12", + 18013: "GOES-13", + 18014: "GOES-14", + 18015: "GOES-15", + # these are in block-0 + 7: "GOES-7", + 8: "GOES-8", + 9: "GOES-9", + 10: "GOES-10", + 11: "GOES-11", + 12: "GOES-12", + 13: "GOES-13", + 14: "GOES-14", + 15: "GOES-15"} +VISSR = "VISSR" +IMAGER_8_11 = "IMAGER (GOES 8-11)" +IMAGER_12_15 = "IMAGER (GOES 12-15)" +INSTRUMENTS = { + "GOES-7": VISSR, + "GOES-8": IMAGER_8_11, + "GOES-9": IMAGER_8_11, + "GOES-10": IMAGER_8_11, + "GOES-11": IMAGER_8_11, + "GOES-12": IMAGER_12_15, + "GOES-13": IMAGER_12_15, + "GOES-14": IMAGER_12_15, + "GOES-15": IMAGER_12_15, +} + +# Geometric constants [meters] +EQUATOR_RADIUS = 6378169.00 +POLE_RADIUS = 6356583.80 +ALTITUDE = 35785831.00 From 69d6e187e65805492b61e863aaec6ea71e62beb0 Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Wed, 13 May 2026 16:22:52 +0000 Subject: [PATCH 35/72] Fix test --- satpy/etc/readers/viirs_compact.yaml | 56 ++++++++++++++-------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/satpy/etc/readers/viirs_compact.yaml b/satpy/etc/readers/viirs_compact.yaml index 173491dabb..23a4d903e8 100644 --- a/satpy/etc/readers/viirs_compact.yaml +++ b/satpy/etc/readers/viirs_compact.yaml @@ -6,7 +6,7 @@ reader: status: Nominal supports_fsspec: false reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader - sensors: [viirs] + instruments: [VIIRS] default_datasets: datasets: @@ -40,7 +40,7 @@ datasets: M01: name: M01 - sensor: viirs + instruments: [VIIRS] wavelength: [0.402,0.412,0.422] resolution: 742 calibration: @@ -55,7 +55,7 @@ datasets: M02: name: M02 - sensor: viirs + instruments: [VIIRS] wavelength: [0.436,0.445,0.454] resolution: 742 calibration: @@ -70,7 +70,7 @@ datasets: M03: name: M03 - sensor: viirs + instruments: [VIIRS] wavelength: [0.478,0.488,0.498] resolution: 742 calibration: @@ -85,7 +85,7 @@ datasets: M04: name: M04 - sensor: viirs + instruments: [VIIRS] wavelength: [0.545,0.555,0.565] resolution: 742 calibration: @@ -100,7 +100,7 @@ datasets: M05: name: M05 - sensor: viirs + instruments: [VIIRS] wavelength: [0.662,0.672,0.682] resolution: 742 calibration: @@ -115,7 +115,7 @@ datasets: M06: name: M06 - sensor: viirs + instruments: [VIIRS] wavelength: [0.739,0.746,0.754] resolution: 742 calibration: @@ -130,7 +130,7 @@ datasets: M07: name: M07 - sensor: viirs + instruments: [VIIRS] wavelength: [0.846,0.865,0.885] resolution: 742 calibration: @@ -145,7 +145,7 @@ datasets: M08: name: M08 - sensor: viirs + instruments: [VIIRS] wavelength: [1.230,1.240,1.250] resolution: 742 calibration: @@ -160,7 +160,7 @@ datasets: M09: name: M09 - sensor: viirs + instruments: [VIIRS] resolution: 742 wavelength: [1.371,1.378,1.386] calibration: @@ -175,7 +175,7 @@ datasets: M10: name: M10 - sensor: viirs + instruments: [VIIRS] wavelength: [1.580,1.610,1.640] resolution: 742 calibration: @@ -190,7 +190,7 @@ datasets: M11: name: M11 - sensor: viirs + instruments: [VIIRS] resolution: 742 wavelength: [2.225,2.250,2.275] calibration: @@ -205,7 +205,7 @@ datasets: M12: name: M12 - sensor: viirs + instruments: [VIIRS] wavelength: [3.610,3.700,3.790] resolution: 742 calibration: @@ -220,7 +220,7 @@ datasets: M13: name: M13 - sensor: viirs + instruments: [VIIRS] wavelength: [3.973,4.050,4.128] resolution: 742 calibration: @@ -235,7 +235,7 @@ datasets: M14: name: M14 - sensor: viirs + instruments: [VIIRS] resolution: 742 wavelength: [8.400,8.550,8.700] calibration: @@ -250,7 +250,7 @@ datasets: M15: name: M15 - sensor: viirs + instruments: [VIIRS] resolution: 742 wavelength: [10.263,10.763,11.263] calibration: @@ -265,7 +265,7 @@ datasets: M16: name: M16 - sensor: viirs + instruments: [VIIRS] wavelength: [11.538,12.013,12.489] resolution: 742 calibration: @@ -280,7 +280,7 @@ datasets: DNB: name: DNB - sensor: viirs + instruments: [VIIRS] wavelength: [0.500,0.700,0.900] resolution: 743 calibration: @@ -292,7 +292,7 @@ datasets: satellite_azimuth_angle: name: satellite_azimuth_angle - sensor: viirs + instruments: [VIIRS] resolution: 742 file_type: compact_m units: degree @@ -301,7 +301,7 @@ datasets: solar_azimuth_angle: name: solar_azimuth_angle - sensor: viirs + instruments: [VIIRS] resolution: 742 file_type: compact_m units: degree @@ -310,7 +310,7 @@ datasets: satellite_zenith_angle: name: satellite_zenith_angle - sensor: viirs + instruments: [VIIRS] resolution: 742 file_type: compact_m units: degree @@ -319,7 +319,7 @@ datasets: solar_zenith_angle: name: solar_zenith_angle - sensor: viirs + instruments: [VIIRS] resolution: 742 file_type: compact_m units: degree @@ -328,7 +328,7 @@ datasets: satellite_azimuth_angle_dnb: name: dnb_satellite_azimuth_angle - sensor: viirs + instruments: [VIIRS] resolution: 743 file_type: compact_dnb units: degree @@ -337,7 +337,7 @@ datasets: solar_azimuth_angle_dnb: name: dnb_solar_azimuth_angle - sensor: viirs + instruments: [VIIRS] resolution: 743 file_type: compact_dnb units: degree @@ -346,7 +346,7 @@ datasets: satellite_zenith_angle_dnb: name: dnb_satellite_zenith_angle - sensor: viirs + instruments: [VIIRS] resolution: 743 file_type: compact_dnb units: degree @@ -355,7 +355,7 @@ datasets: solar_zenith_angle_dnb: name: dnb_solar_zenith_angle - sensor: viirs + instruments: [VIIRS] resolution: 743 file_type: compact_dnb units: degree @@ -364,7 +364,7 @@ datasets: lunar_zenith_angle_dnb: name: dnb_lunar_zenith_angle - sensor: viirs + instruments: [VIIRS] resolution: 743 file_type: compact_dnb units: degree @@ -373,7 +373,7 @@ datasets: lunar_azimuth_angle_dnb: name: dnb_lunar_azimuth_angle - sensor: viirs + instruments: [VIIRS] resolution: 743 file_type: compact_dnb units: degree From 0e32aab52e2fbce7d3941b505353e76f5757cb83 Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Mon, 18 May 2026 15:04:51 +0000 Subject: [PATCH 36/72] Use Enum for WMO instrument names --- .pre-commit-config.yaml | 2 +- satpy/_instruments.py | 64 +++++++++++++++++++ satpy/readers/aapp_l1b.py | 5 +- satpy/readers/aapp_mhs_amsub_l1c.py | 5 +- satpy/readers/abi_l1b.py | 2 +- satpy/readers/abi_l2_nc.py | 2 +- satpy/readers/acspo.py | 9 +-- satpy/readers/ahi_hsd.py | 3 +- satpy/readers/atms_l1b_nc.py | 2 +- satpy/readers/avhrr_l1b_gaclac.py | 7 +- satpy/readers/clavrx.py | 58 +++++++++-------- satpy/readers/core/abi.py | 3 +- satpy/readers/core/eum.py | 6 +- satpy/readers/core/goes_imager.py | 23 ++++--- satpy/readers/core/hdfeos.py | 3 +- satpy/readers/core/landsat.py | 19 +++--- satpy/readers/core/li_nc.py | 6 +- satpy/readers/core/vii_nc.py | 2 +- satpy/readers/core/viirs_atms_sdr.py | 2 +- satpy/readers/electrol_hrit.py | 3 +- satpy/readers/epic_l1b_h5.py | 5 +- satpy/readers/eps_l1b.py | 3 +- satpy/readers/eum_l2_grib.py | 2 +- satpy/readers/fci_l2_nc.py | 2 +- satpy/readers/geocat.py | 13 ++-- satpy/readers/ghrsst_l3c_sst.py | 7 +- satpy/readers/glm_l2.py | 5 +- satpy/readers/gms/gms5_vissr_l1b.py | 3 +- satpy/readers/goes_imager_nc.py | 2 +- satpy/readers/hrit_jma.py | 7 +- satpy/readers/iasi_l2.py | 3 +- satpy/readers/iasi_l2_so2_bufr.py | 4 +- satpy/readers/iasi_ng_l2_nc.py | 6 +- satpy/readers/ici_l1b_nc.py | 2 +- satpy/readers/insat3d_img_l1b_h5.py | 3 +- satpy/readers/maia.py | 5 +- satpy/readers/meris_nc_sen3.py | 11 ++-- satpy/readers/mersi_l1b.py | 43 +++++++------ satpy/readers/mirs.py | 14 ++-- satpy/readers/msu_gsa_l1b.py | 6 +- satpy/readers/mwr_l1b.py | 5 +- satpy/readers/mwr_l1c.py | 5 +- satpy/readers/mws_l1b.py | 4 +- satpy/readers/nucaps.py | 6 +- satpy/readers/nwcsaf_hrw_nc.py | 3 +- satpy/readers/nwcsaf_nc.py | 47 +++++++------- satpy/readers/oceancolorcci_l3_nc.py | 3 +- satpy/readers/olci_nc.py | 3 +- satpy/readers/omps_edr.py | 2 +- satpy/readers/pace_oci_l1b_nc.py | 3 +- satpy/readers/scmi.py | 6 +- satpy/readers/seadas_l2.py | 11 ++-- satpy/readers/seviri_l1b_hrit.py | 3 +- satpy/readers/seviri_l1b_native.py | 3 +- satpy/readers/seviri_l1b_nc.py | 3 +- satpy/readers/sgli_l1b.py | 4 +- satpy/readers/slstr_l1b.py | 3 +- satpy/readers/tropomi_l2.py | 3 +- satpy/readers/viirs_compact.py | 3 +- satpy/readers/viirs_edr.py | 3 +- satpy/readers/viirs_edr_active_fires.py | 7 +- satpy/readers/viirs_edr_flood.py | 2 +- satpy/readers/viirs_l1b.py | 2 +- satpy/readers/viirs_l2.py | 2 +- .../reader_tests/test_aapp_mhs_amsub_l1c.py | 5 +- 65 files changed, 317 insertions(+), 196 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 38aeed0b47..fbb852bb20 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -27,7 +27,7 @@ repos: - types-setuptools - types-PyYAML - types-requests - args: ["--python-version", "3.10", "--ignore-missing-imports"] + args: ["--python-version", "3.11", "--ignore-missing-imports"] - repo: https://github.com/pycqa/isort rev: 9.0.0a3 hooks: diff --git a/satpy/_instruments.py b/satpy/_instruments.py index 3c846c2869..44cd1d590a 100644 --- a/satpy/_instruments.py +++ b/satpy/_instruments.py @@ -17,6 +17,7 @@ import logging import warnings +from enum import StrEnum from typing import Any import satpy @@ -89,3 +90,66 @@ def set_instruments_attr(attrs: dict[str,Any], instruments: set[str]|str) -> Non def get_instruments_key(): """Get key for instruments in dataset attributes.""" return satpy.config.get("instruments_key") + + + +class OSCAR(StrEnum): + """WMO OSCAR instrument names.""" + ABI = "ABI" + AHI = "AHI" + AMSR_2 = "AMSR2" + AMSU_A = "AMSU-A" + AMSU_B = "AMSU-B" + ATMS = "ATMS" + AVHRR = "AVHRR" + AVHRR_2 = "AVHRR/2" + AVHRR_3 = "AVHRR/3" + CRIS = "CrIS" + EPIC = "EPIC" + ETM_PLUS = "ETM+" + FCI = "FCI" + GLM = "GLM" + GMI = "GMI" + IASI = "IASI" + IASI_NG = "IASI-NG" + IMAGER_GOES_12_15 = "IMAGER (GOES 12-15)" + IMAGER_GOES_8_11 = "IMAGER (GOES 8-11)" + IMAGER_INSAT = "IMAGER (INSAT)" + IMAGER_MTSAT_2 = "IMAGER (MTSAT-2)" + JAMI = "JAMI" + LI = "LI" + MERIS = "MERIS" + MERSI_1 = "MERSI-1" + MERSI_2 = "MERSI-2" + MERSI_3 = "MERSI-3" + MERSI_LL = "MERSI-LL" + MERSI_RM = "MERSI-RM" + METIMAGE = "METimage" + MHS = "MHS" + MODIS = "MODIS" + MSS = "MSS" + MSU_GS = "MSU-GS" + MSU_GS_A = "MSU-GS/A" + MVIRI = "MVIRI" + # OSCAR lists "MWR (Sterna)", "MWR (AWS)" etc. + # But to avoid enhancement/composite duplication + # we just use "MWR". + MWR = "MWR" + OCI = "OCI" + OLCI = "OLCI" + OLI = "OLI" + SEAWIFS = "SeaWiFS" + SEVIRI = "SEVIRI" + SGLI = "SGLI" + SLSTR = "SLSTR" + SSMIS = "SSMIS" + TIRS = "TIRS" + TM = "TM" + VIIRS = "VIIRS" + VISSR = "VISSR" + VISSR_HIMAWARI_5 = "VISSR (Himawari-5)" + + +def enum_to_str(instruments: set[StrEnum]) -> set[str]: + """Convert OSCAR enums to string.""" + return {str(i) for i in instruments} diff --git a/satpy/readers/aapp_l1b.py b/satpy/readers/aapp_l1b.py index 96b5b940c8..25aa2ff39c 100644 --- a/satpy/readers/aapp_l1b.py +++ b/satpy/readers/aapp_l1b.py @@ -35,6 +35,7 @@ import xarray as xr from dask import delayed +from satpy._instruments import OSCAR from satpy.readers.core.file_handlers import BaseFileHandler from satpy.utils import get_chunk_size_limit @@ -117,7 +118,7 @@ def end_time(self): def _update_dataset_attributes(self, dataset, key, info): dataset.attrs.update({"platform_name": self.platform_name, - "instruments": {self.sensor}}) + "instruments": {str(self.sensor)}}) dataset.attrs.update(key.to_dict()) for meta_key in ("standard_name", "units"): if meta_key in info: @@ -183,7 +184,7 @@ def __init__(self, filename, filename_info, filetype_info): self.active_channels = self._get_active_channels() self._get_platform_name(AVHRR_PLATFORM_IDS2NAMES) - self.sensor = "AVHRR/3" + self.sensor = OSCAR.AVHRR_3 self._get_all_interpolated_angles = functools.lru_cache(maxsize=10)( self._get_all_interpolated_angles_uncached diff --git a/satpy/readers/aapp_mhs_amsub_l1c.py b/satpy/readers/aapp_mhs_amsub_l1c.py index a05d70e501..75d2204d05 100644 --- a/satpy/readers/aapp_mhs_amsub_l1c.py +++ b/satpy/readers/aapp_mhs_amsub_l1c.py @@ -27,6 +27,7 @@ import dask.array as da import numpy as np +from satpy._instruments import OSCAR from satpy.readers.aapp_l1b import AAPPL1BaseFileHandler, create_xarray from satpy.utils import get_legacy_chunk_size @@ -81,9 +82,9 @@ def _set_filedata_layout(self): def _get_sensorname(self): """Get the sensor name from the header.""" if self._header["instrument"][0] == 11: - self.sensor = "amsub" + self.sensor = OSCAR.AMSU_B elif self._header["instrument"][0] == 12: - self.sensor = "mhs" + self.sensor = OSCAR.MHS else: raise IOError("Sensor neither MHS nor AMSU-B!") diff --git a/satpy/readers/abi_l1b.py b/satpy/readers/abi_l1b.py index 5930dd59f8..4ddb425ade 100644 --- a/satpy/readers/abi_l1b.py +++ b/satpy/readers/abi_l1b.py @@ -73,7 +73,7 @@ def get_dataset(self, key, info): def _adjust_attrs(self, data, key): data.attrs.update({"platform_name": self.platform_name, - "instruments": {self.sensor}}) + "instruments": {str(self.sensor)}}) # Add orbital parameters projection = self.nc["goes_imager_projection"] data.attrs["orbital_parameters"] = { diff --git a/satpy/readers/abi_l2_nc.py b/satpy/readers/abi_l2_nc.py index faebb5ea90..969510c6ae 100644 --- a/satpy/readers/abi_l2_nc.py +++ b/satpy/readers/abi_l2_nc.py @@ -74,7 +74,7 @@ def _update_data_arr_with_filename_attrs(self, variable): _units = variable.attrs["units"] if "units" in variable.attrs else None variable.attrs.update({ "platform_name": self.platform_name, - "instruments": {self.sensor}, + "instruments": {str(self.sensor)}, "units": _units, "orbital_parameters": { "satellite_nominal_latitude": float(self.nc["nominal_satellite_subpoint_lat"]), diff --git a/satpy/readers/acspo.py b/satpy/readers/acspo.py index 4ccb35114f..6fed617f38 100644 --- a/satpy/readers/acspo.py +++ b/satpy/readers/acspo.py @@ -42,15 +42,16 @@ import numpy as np +from satpy._instruments import OSCAR from satpy.readers.core.netcdf import NetCDF4FileHandler LOG = logging.getLogger(__name__) ROWS_PER_SCAN = { - "MODIS": 10, - "VIIRS": 16, - "AVHRR": None, + OSCAR.MODIS: 10, + OSCAR.VIIRS: 16, + OSCAR.AVHRR: None, } @@ -134,7 +135,7 @@ def get_metadata(self, dataset_id, ds_info): "shape": shape, "units": units, "platform_name": self.platform_name, - "instruments": {self.sensor_name}, + "instruments": {str(self.sensor_name)}, "standard_name": standard_name, "resolution": resolution, "rows_per_scan": rows_per_scan, diff --git a/satpy/readers/ahi_hsd.py b/satpy/readers/ahi_hsd.py index 0208e862c0..a85e655b4b 100644 --- a/satpy/readers/ahi_hsd.py +++ b/satpy/readers/ahi_hsd.py @@ -70,6 +70,7 @@ import xarray as xr from satpy._compat import cached_property +from satpy._instruments import OSCAR from satpy.readers.core._geos_area import get_area_definition, get_area_extent from satpy.readers.core.file_handlers import BaseFileHandler from satpy.readers.core.utils import ( @@ -394,7 +395,7 @@ def __init__(self, filename, filename_info, filetype_info, count=1)[0] self.platform_name = np2str(self.basic_info["satellite"]) self.observation_area = np2str(self.basic_info["observation_area"]) - self.sensor = "ahi" + self.sensor = OSCAR.AHI self.mask_space = mask_space self.band_name = filetype_info["file_type"][4:].upper() calib_mode_choices = ("NOMINAL", "UPDATE") diff --git a/satpy/readers/atms_l1b_nc.py b/satpy/readers/atms_l1b_nc.py index 472cfcb7e7..075d4de91d 100644 --- a/satpy/readers/atms_l1b_nc.py +++ b/satpy/readers/atms_l1b_nc.py @@ -75,7 +75,7 @@ def attrs(self): "start_time": self.start_time, "end_time": self.end_time, "platform_name": self.platform_name, - "instruments": {self.sensor}, + "instruments": {str(self.sensor)}, } @staticmethod diff --git a/satpy/readers/avhrr_l1b_gaclac.py b/satpy/readers/avhrr_l1b_gaclac.py index e2de709b7b..d88c53c962 100644 --- a/satpy/readers/avhrr_l1b_gaclac.py +++ b/satpy/readers/avhrr_l1b_gaclac.py @@ -42,6 +42,7 @@ from pygac.lac_klm import LACKLMReader from pygac.lac_pod import LACPODReader +from satpy._instruments import OSCAR from satpy.readers.core.file_handlers import BaseFileHandler from satpy.utils import datetime64_to_pydatetime, get_legacy_chunk_size @@ -109,21 +110,21 @@ def __init__(self, filename, filename_info, filetype_info, # noqa: D417 else: self.reader_class = LACKLMReader self.chn_dict = AVHRR3_CHANNEL_NAMES - self.sensor = "AVHRR/3" + self.sensor = OSCAR.AVHRR_3 elif self._is_avhrr2(): if filename_info.get("transfer_mode") == "GHRR": self.reader_class = GACPODReader else: self.reader_class = LACPODReader self.chn_dict = AVHRR2_CHANNEL_NAMES - self.sensor = "AVHRR/2" + self.sensor = OSCAR.AVHRR_2 else: if filename_info.get("transfer_mode") == "GHRR": self.reader_class = GACPODReader else: self.reader_class = LACPODReader self.chn_dict = AVHRR_CHANNEL_NAMES - self.sensor = "AVHRR" + self.sensor = OSCAR.AVHRR self.filename_info = filename_info def _is_avhrr2(self): diff --git a/satpy/readers/clavrx.py b/satpy/readers/clavrx.py index 43f9938558..7af89328f4 100644 --- a/satpy/readers/clavrx.py +++ b/satpy/readers/clavrx.py @@ -29,6 +29,7 @@ import xarray as xr from pyresample import geometry +from satpy._instruments import OSCAR from satpy.readers.core.file_handlers import BaseFileHandler from satpy.readers.core.hdf4 import SDS, HDF4FileHandler from satpy.utils import get_legacy_chunk_size @@ -42,12 +43,12 @@ } SENSORS = { - "MODIS": "MODIS", - "VIIRS": "VIIRS", - "AVHRR": "AVHRR", - "AHI": "AHI", - "ABI": "ABI", - "GOES-RU-IMAGER": "ABI", + "MODIS": OSCAR.MODIS, + "VIIRS": OSCAR.VIIRS, + "AVHRR": OSCAR.AVHRR, + "AHI": OSCAR.AHI, + "ABI": OSCAR.ABI, + "GOES-RU-IMAGER": OSCAR.ABI, } PLATFORMS = { "SNPP": "npp", @@ -60,29 +61,31 @@ "G18": "GOES-18", } ROWS_PER_SCAN = { - "VIIRS": 16, - "MODIS": 10, + OSCAR.VIIRS: 16, + OSCAR.MODIS: 10, } NADIR_RESOLUTION = { - "VIIRS": 742, - "MODIS": 1000, - "AVHRR": 1050, - "AHI": 2000, - "ABI": 2004, + OSCAR.VIIRS: 742, + OSCAR.MODIS: 1000, + OSCAR.AVHRR: 1050, + OSCAR.AHI: 2000, + OSCAR.ABI: 2004, } CHANNEL_ALIASES = { - "ABI": {"refl_0_47um_nom": {"name": "C01", "wavelength": 0.47, "modifiers": ("sunz_corrected",)}, - "refl_0_65um_nom": {"name": "C02", "wavelength": 0.64, "modifiers": ("sunz_corrected",)}, - "refl_0_86um_nom": {"name": "C03", "wavelength": 0.865, "modifiers": ("sunz_corrected",)}, - "refl_1_38um_nom": {"name": "C04", "wavelength": 1.38, "modifiers": ("sunz_corrected",)}, - "refl_1_60um_nom": {"name": "C05", "wavelength": 1.61, "modifiers": ("sunz_corrected",)}, - "refl_2_10um_nom": {"name": "C06", "wavelength": 2.25, "modifiers": ("sunz_corrected",)}, - }, - "VIIRS": {"refl_0_65um_nom": {"name": "I01", "wavelength": 0.64, "modifiers": ("sunz_corrected",)}, - "refl_1_38um_nom": {"name": "M09", "wavelength": 1.38, "modifiers": ("sunz_corrected",)}, - "refl_1_60um_nom": {"name": "I03", "wavelength": 1.61, "modifiers": ("sunz_corrected",)} - } + OSCAR.ABI: { + "refl_0_47um_nom": {"name": "C01", "wavelength": 0.47, "modifiers": ("sunz_corrected",)}, + "refl_0_65um_nom": {"name": "C02", "wavelength": 0.64, "modifiers": ("sunz_corrected",)}, + "refl_0_86um_nom": {"name": "C03", "wavelength": 0.865, "modifiers": ("sunz_corrected",)}, + "refl_1_38um_nom": {"name": "C04", "wavelength": 1.38, "modifiers": ("sunz_corrected",)}, + "refl_1_60um_nom": {"name": "C05", "wavelength": 1.61, "modifiers": ("sunz_corrected",)}, + "refl_2_10um_nom": {"name": "C06", "wavelength": 2.25, "modifiers": ("sunz_corrected",)}, + }, + OSCAR.VIIRS: { + "refl_0_65um_nom": {"name": "I01", "wavelength": 0.64, "modifiers": ("sunz_corrected",)}, + "refl_1_38um_nom": {"name": "M09", "wavelength": 1.38, "modifiers": ("sunz_corrected",)}, + "refl_1_60um_nom": {"name": "I03", "wavelength": 1.61, "modifiers": ("sunz_corrected",)} + } } @@ -221,7 +224,7 @@ def _find_input_nc(filename: str, sensor: str, l1b_base: str) -> str: if os.path.exists(l1b_filename): return str(l1b_filename) - if sensor == "AHI": + if sensor == OSCAR.AHI: glob_pat = os.path.join(dirname, l1b_base + "*R20*.nc") else: glob_pat = os.path.join(dirname, l1b_base + "*.nc") @@ -409,7 +412,7 @@ def _is_polar(self): l1b_att, inst_att = (str(self.file_content.get("/attr/L1B", None)), str(self.file_content.get("/attr/sensor", None))) - return (inst_att != "AHI" and "GOES" not in inst_att) or (l1b_att is None) + return (inst_att != OSCAR.AHI and "GOES" not in inst_att) or (l1b_att is None) def get_area_def(self, key): """Get the area definition of the data at hand.""" @@ -518,7 +521,8 @@ def _is_polar(self): l1b_att, inst_att = (str(self.nc.attrs.get("L1B", None)), str(self.nc.attrs.get("sensor", None))) - return (inst_att not in ["ABI", "AHI"] and "GOES" not in inst_att) or (l1b_att is None) + abi_ahi = [OSCAR.ABI, OSCAR.AHI] + return (inst_att not in abi_ahi and "GOES" not in inst_att) or (l1b_att is None) def get_area_def(self, key): """Get the area definition of the data at hand.""" diff --git a/satpy/readers/core/abi.py b/satpy/readers/core/abi.py index d51764dc38..140cfe3e71 100644 --- a/satpy/readers/core/abi.py +++ b/satpy/readers/core/abi.py @@ -28,6 +28,7 @@ from pyresample import geometry from satpy._compat import cached_property +from satpy._instruments import OSCAR from satpy.readers.core.file_handlers import BaseFileHandler from satpy.readers.core.remote import open_file_or_filename from satpy.utils import get_dask_chunk_size_in_bytes @@ -111,7 +112,7 @@ def _rename_dims(nc): @property def sensor(self): """Get sensor name for current file handler.""" - return "ABI" + return OSCAR.ABI def __getitem__(self, item): """Wrap `self.nc[item]` for better floating point precision. diff --git a/satpy/readers/core/eum.py b/satpy/readers/core/eum.py index ef60f950dd..1fde5d0b7c 100644 --- a/satpy/readers/core/eum.py +++ b/satpy/readers/core/eum.py @@ -21,14 +21,16 @@ import numpy as np +from satpy._instruments import OSCAR + # 6 bytes, 8 bytes, 10 bytes time_cds_short = [("Days", ">u2"), ("Milliseconds", ">u4")] time_cds = time_cds_short + [("Microseconds", ">u2")] time_cds_expanded = time_cds + [("Nanoseconds", ">u2")] issue_revision = [("Issue", np.uint16), ("Revision", np.uint16)] WMO_INSTRUMENT_NAMES = { - "fci": "FCI", - "seviri": "SEVIRI" + "fci": OSCAR.FCI, + "seviri": OSCAR.SEVIRI } def timecds2datetime(tcds): diff --git a/satpy/readers/core/goes_imager.py b/satpy/readers/core/goes_imager.py index e7d87e79fb..1f5a01a816 100644 --- a/satpy/readers/core/goes_imager.py +++ b/satpy/readers/core/goes_imager.py @@ -17,6 +17,8 @@ # satpy. If not, see . """Common functionality for GOES Imager readers.""" +from satpy._instruments import OSCAR + SPACECRAFTS = { # these are GP_SC_ID 18007: "GOES-7", @@ -38,19 +40,16 @@ 13: "GOES-13", 14: "GOES-14", 15: "GOES-15"} -VISSR = "VISSR" -IMAGER_8_11 = "IMAGER (GOES 8-11)" -IMAGER_12_15 = "IMAGER (GOES 12-15)" INSTRUMENTS = { - "GOES-7": VISSR, - "GOES-8": IMAGER_8_11, - "GOES-9": IMAGER_8_11, - "GOES-10": IMAGER_8_11, - "GOES-11": IMAGER_8_11, - "GOES-12": IMAGER_12_15, - "GOES-13": IMAGER_12_15, - "GOES-14": IMAGER_12_15, - "GOES-15": IMAGER_12_15, + "GOES-7": OSCAR.VISSR, + "GOES-8": OSCAR.IMAGER_GOES_8_11, + "GOES-9": OSCAR.IMAGER_GOES_8_11, + "GOES-10": OSCAR.IMAGER_GOES_8_11, + "GOES-11": OSCAR.IMAGER_GOES_8_11, + "GOES-12": OSCAR.IMAGER_GOES_12_15, + "GOES-13": OSCAR.IMAGER_GOES_12_15, + "GOES-14": OSCAR.IMAGER_GOES_12_15, + "GOES-15": OSCAR.IMAGER_GOES_12_15, } # Geometric constants [meters] diff --git a/satpy/readers/core/hdfeos.py b/satpy/readers/core/hdfeos.py index 2f452d5eb1..03389546bb 100644 --- a/satpy/readers/core/hdfeos.py +++ b/satpy/readers/core/hdfeos.py @@ -34,6 +34,7 @@ from pyhdf.SD import SD from satpy import DataID +from satpy._instruments import OSCAR from satpy.readers.core.file_handlers import BaseFileHandler from satpy.utils import normalize_low_res_chunks @@ -262,7 +263,7 @@ def _add_satpy_metadata(self, data_id: DataID, data_arr: xr.DataArray): """Add metadata that is specific to Satpy.""" new_attrs = { "platform_name": self.metadata_platform_name, - "instruments": {"MODIS"}, + "instruments": {OSCAR.MODIS.value}, } res = data_id["resolution"] diff --git a/satpy/readers/core/landsat.py b/satpy/readers/core/landsat.py index ceef47e20e..4af59e3a1e 100644 --- a/satpy/readers/core/landsat.py +++ b/satpy/readers/core/landsat.py @@ -43,6 +43,7 @@ import rioxarray # noqa: F401 # need by xarray with the engine rasterio import xarray as xr +from satpy._instruments import OSCAR from satpy.readers.core.file_handlers import BaseFileHandler from satpy.readers.core.remote import open_file_or_filename @@ -179,7 +180,7 @@ def _check_channel_and_reader(self, name): raise ValueError(f"Requested channel {name} does not match the reader channel {self.channel}") def _check_oli_tirs_spectral(self, name): - if self.sensor != {"OLI", "TIRS"}: + if self.sensor != {OSCAR.OLI, OSCAR.TIRS}: return if name not in self.spectral_bands: return @@ -188,7 +189,7 @@ def _check_oli_tirs_spectral(self, name): raise ValueError(f"Requested channel {name} is not available in this granule") def _check_oli_tirs_thermal(self, name): - if self.sensor != {"OLI", "TIRS"}: + if self.sensor != {OSCAR.OLI, OSCAR.TIRS}: return if name not in self.thermal_bands: return @@ -310,7 +311,7 @@ def thermal_bands(self): @property def sensor(self): """Sensor name.""" - return {"OLI", "TIRS"} + return {OSCAR.OLI, OSCAR.TIRS} class OLITIRSL2CHReader(BaseLandsatL2Reader): @@ -329,7 +330,7 @@ def thermal_bands(self): @property def sensor(self): """Sensor name.""" - return {"OLI", "TIRS"} + return {OSCAR.OLI, OSCAR.TIRS} class ETMCHReader(BaseLandsatL1Reader): @@ -348,7 +349,7 @@ def thermal_bands(self): @property def sensor(self): """Sensor name.""" - return {"ETM+"} + return {OSCAR.ETM_PLUS} class ETML2CHReader(BaseLandsatL2Reader): @@ -367,7 +368,7 @@ def thermal_bands(self): @property def sensor(self): """Sensor name.""" - return {"ETM+"} + return {OSCAR.ETM_PLUS} class TMCHReader(BaseLandsatL1Reader): @@ -386,7 +387,7 @@ def thermal_bands(self): @property def sensor(self): """Sensor name.""" - return {"TM"} + return {OSCAR.TM} class TML2CHReader(BaseLandsatL2Reader): @@ -405,7 +406,7 @@ def thermal_bands(self): @property def sensor(self): """Sensor name.""" - return {"TM"} + return {OSCAR.TM} class MSSCHReader(BaseLandsatL1Reader): @@ -427,7 +428,7 @@ def thermal_bands(self): @property def sensor(self): """Sensor name.""" - return {"MSS"} + return {OSCAR.MSS} def available_datasets(self, configured_datasets=None): """Set up wavelength to B4 band.""" diff --git a/satpy/readers/core/li_nc.py b/satpy/readers/core/li_nc.py index ba59f464b5..39b3a613f1 100644 --- a/satpy/readers/core/li_nc.py +++ b/satpy/readers/core/li_nc.py @@ -191,6 +191,8 @@ import xarray as xr from pyproj import Proj +import satpy._instruments as instru +from satpy._instruments import OSCAR from satpy.readers.core.fci import platform_name_translate from satpy.readers.core.netcdf import NetCDF4FsspecFileHandler @@ -218,7 +220,7 @@ def __init__(self, filename, filename_info, filetype_info, cache_handle=True): self.processing_level = filetype_info.get("processing_level", "L0") # This class will only provide support for the LI sensor: - self.sensors = {"LI"} + self.sensors = {OSCAR.LI} # Set of dataset names explicitly provided by this file handler: # This set is required to filter the retrieval of datasets later in the @@ -520,7 +522,7 @@ def register_dataset(self, var_name, oc_name=None): ds_info = { "name": ds_name, "variable_name": var_name, - "instruments": self.sensor_names, + "instruments": instru.enum_to_str(self.sensor_names), "platform_name": platform_name_translate[platform], "file_type": self.filetype_info["file_type"] } diff --git a/satpy/readers/core/vii_nc.py b/satpy/readers/core/vii_nc.py index a1b2acb546..8502fa802c 100644 --- a/satpy/readers/core/vii_nc.py +++ b/satpy/readers/core/vii_nc.py @@ -195,7 +195,7 @@ def _get_global_attributes(self): "end_time": self.end_time, "spacecraft_name": self.spacecraft_name, "ssp_lon": self.ssp_lon, - "instruments": {self.sensor}, + "instruments": {str(self.sensor)}, "filename_start_time": self.filename_info["sensing_start_time"], "filename_end_time": self.filename_info["sensing_end_time"], "platform_name": self.spacecraft_name, diff --git a/satpy/readers/core/viirs_atms_sdr.py b/satpy/readers/core/viirs_atms_sdr.py index b1627db7ca..bf07e4a3a8 100644 --- a/satpy/readers/core/viirs_atms_sdr.py +++ b/satpy/readers/core/viirs_atms_sdr.py @@ -276,7 +276,7 @@ def _update_data_attributes(self, data, dataset_id, ds_info): i.update(ds_info) i.update({ "platform_name": self.platform_name, - "instruments": {self.sensor_name}, + "instruments": {str(self.sensor_name)}, "start_orbit": self.start_orbit_number, "end_orbit": self.end_orbit_number, "units": output_units, diff --git a/satpy/readers/electrol_hrit.py b/satpy/readers/electrol_hrit.py index 479a18ff67..8d8a7782ae 100644 --- a/satpy/readers/electrol_hrit.py +++ b/satpy/readers/electrol_hrit.py @@ -30,6 +30,7 @@ import numpy as np import xarray as xr +from satpy._instruments import OSCAR from satpy.readers.core._geos_area import get_area_definition, get_area_extent from satpy.readers.core.hrit import ( HRITFileHandler, @@ -286,7 +287,7 @@ def get_dataset(self, key, info): res.attrs["standard_name"] = info["standard_name"] res.attrs["wavelength"] = info["wavelength"] res.attrs["platform_name"] = self.platform_name - res.attrs["instruments"] = {"MSU-GS"} + res.attrs["instruments"] = {OSCAR.MSU_GS.value} res.attrs["orbital_parameters"] = { "satellite_nominal_longitude": self.mda["orbital_parameters"]["satellite_nominal_longitude"], "satellite_nominal_latitude": 0., diff --git a/satpy/readers/epic_l1b_h5.py b/satpy/readers/epic_l1b_h5.py index e90bf69591..4275137130 100644 --- a/satpy/readers/epic_l1b_h5.py +++ b/satpy/readers/epic_l1b_h5.py @@ -44,6 +44,7 @@ import dask.array as da import numpy as np +from satpy._instruments import OSCAR from satpy.readers.core.hdf5 import HDF5FileHandler logger = logging.getLogger(__name__) @@ -69,7 +70,7 @@ def __init__(self, filename, filename_info, filetype_info): """Init filehandler.""" super(DscovrEpicL1BH5FileHandler, self).__init__(filename, filename_info, filetype_info) - self.sensor = "EPIC" + self.sensor = OSCAR.EPIC self.platform_name = "DSCOVR" @property @@ -111,6 +112,6 @@ def get_dataset(self, dataset_id, ds_info): def _update_metadata(self, band): band = band.rename({band.dims[0]: "x", band.dims[1]: "y"}) - band.attrs.update({"platform_name": self.platform_name, "instruments": {self.sensor}}) + band.attrs.update({"platform_name": self.platform_name, "instruments": {str(self.sensor)}}) return band diff --git a/satpy/readers/eps_l1b.py b/satpy/readers/eps_l1b.py index ae60bb6eb3..080e2e4738 100644 --- a/satpy/readers/eps_l1b.py +++ b/satpy/readers/eps_l1b.py @@ -29,6 +29,7 @@ from satpy._compat import cached_property from satpy._config import get_config_path +from satpy._instruments import OSCAR from satpy.readers.core.file_handlers import BaseFileHandler from satpy.readers.core.xmlformat import XMLFormat from satpy.utils import get_legacy_chunk_size @@ -141,7 +142,7 @@ class EPSAVHRRFile(BaseFileHandler): "M02": "Metop-A", "M03": "Metop-C", } - sensors = {"AVHR": "AVHRR/3"} + sensors = {"AVHR": OSCAR.AVHRR_3} units = {"reflectance": "%", "brightness_temperature": "K", diff --git a/satpy/readers/eum_l2_grib.py b/satpy/readers/eum_l2_grib.py index cbd1b8383a..412973dd1a 100644 --- a/satpy/readers/eum_l2_grib.py +++ b/satpy/readers/eum_l2_grib.py @@ -293,7 +293,7 @@ def _get_attributes(self): } attributes = {"orbital_parameters": orbital_parameters, - "instruments": {WMO_INSTRUMENT_NAMES.get(self.sensor, self.sensor)}, + "instruments": {str(WMO_INSTRUMENT_NAMES.get(self.sensor, self.sensor))}, "platform_name": self.PLATFORM_NAME} return attributes diff --git a/satpy/readers/fci_l2_nc.py b/satpy/readers/fci_l2_nc.py index 5e8b889a8a..e5c77372c3 100644 --- a/satpy/readers/fci_l2_nc.py +++ b/satpy/readers/fci_l2_nc.py @@ -82,7 +82,7 @@ def _get_global_attributes(self, product_type="pixel"): "filename": self.filename, "spacecraft_name": platform_name_translate.get(self.spacecraft_name, self.spacecraft_name), "ssp_lon": self.ssp_lon, - "instruments": {WMO_INSTRUMENT_NAMES.get(self.sensor_name, self.sensor_name)}, + "instruments": {str(WMO_INSTRUMENT_NAMES.get(self.sensor_name, self.sensor_name))}, "platform_name": platform_name_translate.get(self.spacecraft_name, self.spacecraft_name) } diff --git a/satpy/readers/geocat.py b/satpy/readers/geocat.py index 47aee761ad..d0da9d90e8 100644 --- a/satpy/readers/geocat.py +++ b/satpy/readers/geocat.py @@ -37,6 +37,7 @@ from pyproj import Proj from pyresample import geometry +from satpy._instruments import OSCAR from satpy.readers.core.netcdf import NetCDF4FileHandler LOG = logging.getLogger(__name__) @@ -81,19 +82,19 @@ def __init__(self, filename, filename_info, filetype_info, xarray_kwargs=kwargs["xarray_kwargs"]) sensors = { - "goes": "IMAGER (GOES 12-15)", - "himawari8": "AHI", - "goes16": "ABI", # untested - "goesr": "ABI", # untested + "goes": OSCAR.IMAGER_GOES_12_15, + "himawari8": OSCAR.AHI, + "goes16": OSCAR.ABI, # untested + "goesr": OSCAR.ABI, # untested } platforms: dict[str, str] = { } resolutions = { - "ABI": { + OSCAR.ABI: { 1: 1002.0086577437705, 2: 2004.0173154875411, }, - "AHI": { + OSCAR.AHI: { 1: 999.9999820317674, # assumption 2: 1999.999964063535, 4: 3999.99992812707, diff --git a/satpy/readers/ghrsst_l3c_sst.py b/satpy/readers/ghrsst_l3c_sst.py index e33cb9988f..c2ffb3b55c 100644 --- a/satpy/readers/ghrsst_l3c_sst.py +++ b/satpy/readers/ghrsst_l3c_sst.py @@ -24,14 +24,15 @@ import numpy as np +from satpy._instruments import OSCAR from satpy.dataset import Dataset from satpy.readers.core.netcdf import NetCDF4FileHandler logger = logging.getLogger(__name__) PLATFORM_NAME = {"NPP": "Suomi-NPP", } -SENSOR_NAME = {"VIIRS": "VIIRS", - "AVHRR": "AVHRR/3"} +SENSOR_NAME = {"VIIRS": OSCAR.VIIRS, + "AVHRR": OSCAR.AVHRR_3} class GHRSST_OSISAFL2(NetCDF4FileHandler): @@ -78,7 +79,7 @@ def get_dataset(self, dataset_id, ds_info, out=None): ds_info.update({ "units": ds_info.get("units", file_units), "platform_name": PLATFORM_NAME.get(self["/attr/platform"], self["/attr/platform"]), - "instruments": {SENSOR_NAME.get(self["/attr/sensor"], self["/attr/sensor"])}, + "instruments": {str(SENSOR_NAME.get(self["/attr/sensor"], self["/attr/sensor"]))}, }) ds_info.update(dataset_id.to_dict()) cls = ds_info.pop("container", Dataset) diff --git a/satpy/readers/glm_l2.py b/satpy/readers/glm_l2.py index eda3313212..fc1ad20105 100644 --- a/satpy/readers/glm_l2.py +++ b/satpy/readers/glm_l2.py @@ -30,6 +30,7 @@ import numpy as np +from satpy._instruments import OSCAR from satpy.readers.core.abi import NC_ABI_BASE logger = logging.getLogger(__name__) @@ -49,7 +50,7 @@ class NCGriddedGLML2(NC_ABI_BASE): @property def sensor(self): """Get sensor name for current file handler.""" - return "GLM" + return OSCAR.GLM @property def start_time(self): @@ -77,7 +78,7 @@ def get_dataset(self, key, info): logger.debug("Reading in get_dataset %s.", key["name"]) res = self[key["name"]] res.attrs.update({"platform_name": self.platform_name, - "instruments": {self.sensor}}) + "instruments": {str(self.sensor)}}) res.attrs.update(self.filename_info) # Add orbital parameters diff --git a/satpy/readers/gms/gms5_vissr_l1b.py b/satpy/readers/gms/gms5_vissr_l1b.py index 2f7f9691ac..91418aaaab 100644 --- a/satpy/readers/gms/gms5_vissr_l1b.py +++ b/satpy/readers/gms/gms5_vissr_l1b.py @@ -162,6 +162,7 @@ import satpy.readers.core._geos_area as geos_area import satpy.readers.gms.gms5_vissr_format as fmt import satpy.readers.gms.gms5_vissr_navigation as nav +from satpy._instruments import OSCAR from satpy.readers.core.file_handlers import BaseFileHandler from satpy.readers.core.utils import generic_open from satpy.readers.hrit_jma import mjd2datetime64 @@ -286,7 +287,7 @@ def _get_nominal_shape(self): def _get_mda(self): return { "platform": self._mode_block["satellite_name"].decode().strip().upper(), - "instruments": {"VISSR (Himawari-5)"}, + "instruments": {OSCAR.VISSR_HIMAWARI_5.value}, "time_parameters": self._get_time_parameters(), "orbital_parameters": self._get_orbital_parameters(), } diff --git a/satpy/readers/goes_imager_nc.py b/satpy/readers/goes_imager_nc.py index 6603d97fc6..a008b4ca77 100644 --- a/satpy/readers/goes_imager_nc.py +++ b/satpy/readers/goes_imager_nc.py @@ -942,7 +942,7 @@ def _update_metadata(self, data, ds_info): # Metadata discovered from the file. data.attrs.update( {"platform_name": self.platform_name, - "instruments": {self.instrument}, + "instruments": {str(self.instrument)}, "sector": self.sector, "orbital_parameters": {"yaw_flip": self.meta["yaw_flip"]}} ) diff --git a/satpy/readers/hrit_jma.py b/satpy/readers/hrit_jma.py index 067491efcf..971946bc90 100644 --- a/satpy/readers/hrit_jma.py +++ b/satpy/readers/hrit_jma.py @@ -101,6 +101,7 @@ import satpy._instruments as instru import satpy.utils +from satpy._instruments import OSCAR from satpy.readers.core._geos_area import get_area_definition, get_area_extent from satpy.readers.core.hrit import ( HRITFileHandler, @@ -177,9 +178,9 @@ "GEOS(145.00)": MTSAT2, } SENSORS = { - MTSAT1R: "JAMI", - MTSAT2: "IMAGER (MTSAT-2)", - HIMAWARI8: "AHI" + MTSAT1R: OSCAR.JAMI, + MTSAT2: OSCAR.IMAGER_MTSAT_2, + HIMAWARI8: OSCAR.AHI } diff --git a/satpy/readers/iasi_l2.py b/satpy/readers/iasi_l2.py index e1b11b3796..85dfa5bd14 100644 --- a/satpy/readers/iasi_l2.py +++ b/satpy/readers/iasi_l2.py @@ -23,6 +23,7 @@ import numpy as np import xarray as xr +from satpy._instruments import OSCAR from satpy.readers.core.file_handlers import BaseFileHandler from satpy.readers.core.netcdf import NetCDF4FsspecFileHandler from satpy.utils import get_legacy_chunk_size @@ -105,7 +106,7 @@ def __init__(self, filename, filename_info, filetype_info): self.finfo = filename_info self.lons = None self.lats = None - self.sensor = "IASI" + self.sensor = OSCAR.IASI short_name = filename_info["platform_id"] self.platform_name = SHORT_NAMES.get(short_name, short_name) diff --git a/satpy/readers/iasi_l2_so2_bufr.py b/satpy/readers/iasi_l2_so2_bufr.py index b66c0c3de1..ba31d12bd5 100644 --- a/satpy/readers/iasi_l2_so2_bufr.py +++ b/satpy/readers/iasi_l2_so2_bufr.py @@ -92,6 +92,8 @@ import numpy as np import xarray as xr +from satpy._instruments import OSCAR + try: import eccodes as ec except ImportError as e: @@ -230,7 +232,7 @@ def get_dataset(self, dataset_id, dataset_info): arr[arr == dataset_info["fill_value"]] = np.nan xarr = xr.DataArray(arr, dims=["y", "x"], name=dataset_info["name"]) - xarr.attrs["instruments"] = {"IASI"} + xarr.attrs["instruments"] = {OSCAR.IASI} xarr.attrs["platform_name"] = self.platform_name xarr.attrs.update(dataset_info) diff --git a/satpy/readers/iasi_ng_l2_nc.py b/satpy/readers/iasi_ng_l2_nc.py index 929b05c8b6..e93f34d897 100644 --- a/satpy/readers/iasi_ng_l2_nc.py +++ b/satpy/readers/iasi_ng_l2_nc.py @@ -38,6 +38,8 @@ import pandas as pd import xarray as xr +import satpy._instruments as instru +from satpy._instruments import OSCAR from satpy.readers.core.netcdf import NetCDF4FsspecFileHandler @@ -50,7 +52,7 @@ def __init__(self, filename, filename_info, filetype_info, **kwargs): filename, filename_info, filetype_info, auto_maskandscale=True, **kwargs ) - self.sensors = {"IASI-NG"} + self.sensors = {OSCAR.IASI_NG} self.dataset_infos = None self.variable_desc = {} @@ -108,7 +110,7 @@ def register_dataset(self, ds_name, desc): ds_infos = { "name": ds_name, - "instruments": self.sensors, + "instruments": instru.enum_to_str(self.sensors), "file_type": self.filetype_info["file_type"], } diff --git a/satpy/readers/ici_l1b_nc.py b/satpy/readers/ici_l1b_nc.py index 76fa938d4e..3713351468 100644 --- a/satpy/readers/ici_l1b_nc.py +++ b/satpy/readers/ici_l1b_nc.py @@ -435,7 +435,7 @@ def _get_global_attributes(self): "end_time": self.end_time, "spacecraft_name": self.platform_name, "ssp_lon": self.ssp_lon, - "instruments": {self.sensor}, + "instruments": {str(self.sensor)}, "filename_start_time": self.filename_info["sensing_start_time"], "filename_end_time": self.filename_info["sensing_end_time"], "platform_name": self.platform_name, diff --git a/satpy/readers/insat3d_img_l1b_h5.py b/satpy/readers/insat3d_img_l1b_h5.py index 46d650e83e..5f4f9d9179 100644 --- a/satpy/readers/insat3d_img_l1b_h5.py +++ b/satpy/readers/insat3d_img_l1b_h5.py @@ -9,6 +9,7 @@ import xarray as xr from xarray.core.datatree import DataTree +from satpy._instruments import OSCAR from satpy.readers.core.file_handlers import BaseFileHandler LUT_SUFFIXES = {"vis": ("RADIANCE", "ALBEDO"), @@ -159,7 +160,7 @@ def get_dataset(self, ds_id, ds_info): satellite_nominal_altitude=float(ds.attrs["Nominal_Altitude(km)"]), satellite_actual_altitude=float(ds.attrs["Observed_Altitude(km)"])) darr.attrs["platform_name"] = "insat-3d" - darr.attrs["instruments"] = {"IMAGER (INSAT)"} + darr.attrs["instruments"] = {OSCAR.IMAGER_INSAT} darr = darr.squeeze() return darr diff --git a/satpy/readers/maia.py b/satpy/readers/maia.py index 2e979b51bd..0893c9efdb 100644 --- a/satpy/readers/maia.py +++ b/satpy/readers/maia.py @@ -32,6 +32,7 @@ import numpy as np from xarray import DataArray +from satpy._instruments import OSCAR from satpy.readers.core.file_handlers import BaseFileHandler from satpy.utils import get_legacy_chunk_size @@ -111,9 +112,9 @@ def read(self, filename): def get_platform(self, platform): """Get the platform.""" if self.file_content["sat_id"] in (14,): - return "viirs" + return OSCAR.VIIRS else: - return "avhrr" + return OSCAR.AVHRR @property def start_time(self): diff --git a/satpy/readers/meris_nc_sen3.py b/satpy/readers/meris_nc_sen3.py index fa69dad2cc..15afde4a40 100644 --- a/satpy/readers/meris_nc_sen3.py +++ b/satpy/readers/meris_nc_sen3.py @@ -29,6 +29,7 @@ import numpy as np +from satpy._instruments import OSCAR from satpy.readers.olci_nc import NCOLCI2, BitFlags, NCOLCIAngles, NCOLCIBase, NCOLCIMeteo logger = logging.getLogger(__name__) @@ -40,7 +41,7 @@ class NCMERISCal(NCOLCIBase): def __init__(self, filename, filename_info, filetype_info): """Init the meris reader base.""" super(NCMERISCal, self).__init__(filename, filename_info, filetype_info) - self.sensor = "meris" + self.sensor = OSCAR.MERIS class NCMERISGeo(NCOLCIBase): @@ -49,7 +50,7 @@ class NCMERISGeo(NCOLCIBase): def __init__(self, filename, filename_info, filetype_info): """Init the meris reader base.""" super(NCMERISGeo, self).__init__(filename, filename_info, filetype_info) - self.sensor = "meris" + self.sensor = OSCAR.MERIS class NCMERIS2(NCOLCI2): @@ -58,7 +59,7 @@ class NCMERIS2(NCOLCI2): def __init__(self, filename, filename_info, filetype_info): """Init the file handler.""" super(NCMERIS2, self).__init__(filename, filename_info, filetype_info) - self.sensor = "meris" + self.sensor = OSCAR.MERIS self.reflectance_prefix = "M" self.reflectance_suffix = "_rho_w" @@ -86,7 +87,7 @@ class NCMERISAngles(NCOLCIAngles): def __init__(self, filename, filename_info, filetype_info): """Init the file handler.""" super(NCMERISAngles, self).__init__(filename, filename_info, filetype_info) - self.sensor = "meris" + self.sensor = OSCAR.MERIS class NCMERISMeteo(NCOLCIMeteo): @@ -95,4 +96,4 @@ class NCMERISMeteo(NCOLCIMeteo): def __init__(self, filename, filename_info, filetype_info): """Init the file handler.""" super(NCMERISMeteo, self).__init__(filename, filename_info, filetype_info) - self.sensor = "meris" + self.sensor = OSCAR.MERIS diff --git a/satpy/readers/mersi_l1b.py b/satpy/readers/mersi_l1b.py index fda050960c..da7dee5535 100644 --- a/satpy/readers/mersi_l1b.py +++ b/satpy/readers/mersi_l1b.py @@ -32,16 +32,17 @@ import numpy as np from pyspectral.blackbody import blackbody_wn_rad2temp as rad2temp +from satpy._instruments import OSCAR from satpy.readers.core.hdf5 import HDF5FileHandler N_TOT_IR_CHANS_LL = 6 -PLATFORMS_INSTRUMENTS = {"FY-3A": "MERSI-1", - "FY-3B": "MERSI-1", - "FY-3C": "MERSI-1", - "FY-3D": "MERSI-2", - "FY-3E": "MERSI-LL", - "FY-3F": "MERSI-3", - "FY-3G": "MERSI-RM"} +PLATFORMS_INSTRUMENTS = {"FY-3A": OSCAR.MERSI_1, + "FY-3B": OSCAR.MERSI_1, + "FY-3C": OSCAR.MERSI_1, + "FY-3D": OSCAR.MERSI_2, + "FY-3E": OSCAR.MERSI_LL, + "FY-3F": OSCAR.MERSI_3, + "FY-3G": OSCAR.MERSI_RM} class MERSIL1B(HDF5FileHandler): """MERSI-1/MERSI-2/MERSI-LL/MERSI-RM L1B file reader.""" @@ -76,7 +77,7 @@ def platform_name(self): def get_refl_mult(self): """Get reflectance multiplier.""" - if self.sensor_name == "MERSI-RM": + if self.sensor_name == OSCAR.MERSI_RM: # MERSI-RM has reflectance in the range 0-1, so we need to convert return 100. else: @@ -93,7 +94,7 @@ def _get_single_slope_intercept(self, slope, intercept, cal_index): def _get_coefficients(self, cal_key, cal_index): """Get VIS calibration coeffs from calibration datasets.""" # Only one VIS band for MERSI-LL - coeffs = self[cal_key][cal_index] if self.sensor_name != "MERSI-LL" else self[cal_key] + coeffs = self[cal_key][cal_index] if self.sensor_name != OSCAR.MERSI_LL else self[cal_key] slope = coeffs.attrs.pop("Slope", None) intercept = coeffs.attrs.pop("Intercept", None) if slope is not None: @@ -123,7 +124,7 @@ def _get_dn_corrections(self, data, band_index, dataset_id, attrs): slope = slope[band_index] intercept = intercept[band_index] # There's a bug in slope for MERSI-1 IR band - slope = 0.01 if self.sensor_name == "MERSI-1" and dataset_id["name"] == "5" else slope + slope = 0.01 if self.sensor_name == OSCAR.MERSI_1 and dataset_id["name"] == "5" else slope data = data * slope + intercept return data @@ -154,7 +155,7 @@ def get_dataset(self, dataset_id, ds_info): # to SI units m^-1, mW*m^-3*str^-1. wave_number = 1. / (dataset_id["wavelength"][1] / 1e6) # MERSI-1 doesn't have additional corrections - calibration_index = None if self.sensor_name == "MERSI-1" else ds_info["calibration_index"] + calibration_index = None if self.sensor_name == OSCAR.MERSI_1 else ds_info["calibration_index"] data = self._get_bt_dataset(data, calibration_index, wave_number) data.attrs = attrs @@ -166,7 +167,7 @@ def get_dataset(self, dataset_id, ds_info): data.attrs.update({ "platform_name": self.platform_name, - "instruments": {self.sensor_name}, + "instruments": {str(self.sensor_name)}, }) return data @@ -185,10 +186,10 @@ def _mask_data(self, data, dataset_id, attrs): try: # Due to a bug in the valid_range upper limit in the 10.8(24) and 12.0(25) # in the HDF data, this is hardcoded here. - valid_range[1] = 25000 if self.sensor_name == "MERSI-2" and dataset_id["name"] in ["24", "25"] and \ + valid_range[1] = 25000 if self.sensor_name == OSCAR.MERSI_2 and dataset_id["name"] in ["24", "25"] and \ valid_range[1] == 4095 else valid_range[1] # Similar bug also found in MERSI-1 - valid_range[1] = 25000 if self.sensor_name == "MERSI-1" and dataset_id["name"] == "5" and \ + valid_range[1] = 25000 if self.sensor_name == OSCAR.MERSI_1 and dataset_id["name"] == "5" and \ valid_range[1] == 4095 else valid_range[1] # typically bad_values == 65535, saturated == 65534 # dead detector == 65533 @@ -216,7 +217,7 @@ def _get_ref_dataset(self, data, ds_info): # Only FY-3A/B stores VIS calibration coefficients in attributes coeffs = self._get_coefficients_mersi1(ds_info["calibration_index"]) if self.platform_name in ["FY-3A", "FY-3B"] else self._get_coefficients(ds_info["calibration_key"], ds_info.get("calibration_index", None)) - data = coeffs[0] + coeffs[1] * data + coeffs[2] * data ** 2 if self.sensor_name != "MERSI-LL" else \ + data = coeffs[0] + coeffs[1] * data + coeffs[2] * data ** 2 if self.sensor_name != OSCAR.MERSI_LL else \ data * np.pi / coeffs[0] * 100 data = data * self.get_refl_mult() @@ -240,10 +241,10 @@ def _get_rad_dataset(self, data, ds_info, datset_id): mersi_2_vis = [str(i) for i in range(1, 20)] mersi_rm_vis = [str(i) for i in range(1, 6)] - if self.sensor_name == "MERSI-2" and datset_id["name"] in mersi_2_vis: + if self.sensor_name == OSCAR.MERSI_2 and datset_id["name"] in mersi_2_vis: E0 = self["/attr/Solar_Irradiance"] rad = self._get_ref_dataset(data, ds_info) / 100 * E0[mersi_2_vis.index(datset_id["name"])] / np.pi - elif self.sensor_name == "MERSI-RM" and datset_id["name"] in mersi_rm_vis: + elif self.sensor_name == OSCAR.MERSI_RM and datset_id["name"] in mersi_rm_vis: E0 = self._get_coefficients("Calibration/Solar_Irradiance", mersi_rm_vis.index(datset_id["name"])) rad = self._get_ref_dataset(data, ds_info) / 100 * E0 / np.pi else: @@ -277,14 +278,14 @@ def _get_bt_dataset(self, data, calibration_index, wave_number): data = data.where(data != 0) # additional corrections from the file - if self.sensor_name == "MERSI-1": + if self.sensor_name == OSCAR.MERSI_1: # https://img.nsmc.org.cn/PORTAL/NSMC/DATASERVICE/SRF/FY3C/FY3C_MERSI_SRF.rar corr_coeff_a = 1.0047 corr_coeff_b = -0.8549 - elif self.sensor_name == "MERSI-2": + elif self.sensor_name == OSCAR.MERSI_2: corr_coeff_a = float(self["/attr/TBB_Trans_Coefficient_A"][calibration_index]) corr_coeff_b = float(self["/attr/TBB_Trans_Coefficient_B"][calibration_index]) - elif self.sensor_name == "MERSI-LL": + elif self.sensor_name == OSCAR.MERSI_LL: # MERSI-LL stores these coefficients differently try: coeffs = self["/attr/TBB_Trans_Coefficient"] @@ -297,7 +298,7 @@ def _get_bt_dataset(self, data, calibration_index, wave_number): corr_coeff_a = 0 if corr_coeff_a != 0: - data = (data - corr_coeff_b) / corr_coeff_a if self.sensor_name != "MERSI-1" else \ + data = (data - corr_coeff_b) / corr_coeff_a if self.sensor_name != OSCAR.MERSI_1 else \ data * corr_coeff_a + corr_coeff_b # some bands have 0 counts for the first N columns and # seem to be invalid data points diff --git a/satpy/readers/mirs.py b/satpy/readers/mirs.py index 6718822aa0..89389674b9 100644 --- a/satpy/readers/mirs.py +++ b/satpy/readers/mirs.py @@ -27,6 +27,8 @@ import numpy as np import xarray as xr +import satpy._instruments as instru +from satpy._instruments import OSCAR from satpy.aux_download import retrieve from satpy.readers.core.file_handlers import BaseFileHandler from satpy.utils import get_legacy_chunk_size @@ -65,10 +67,10 @@ def get_resource_string(mod_part, file_part): "f18": "DMSP-F18", "gpm": "GPM", } -amsua_mhs = {"AMSU-A", "MHS"} -atms = {"ATMS"} -ssmis = {"SSMIS"} -gmi = {"GMI"} +amsua_mhs = {OSCAR.AMSU_A, OSCAR.MHS} +atms = {OSCAR.ATMS} +ssmis = {OSCAR.SSMIS} +gmi = {OSCAR.GMI} SENSOR = {"n18": amsua_mhs, "n19": amsua_mhs, "n20": atms, @@ -327,7 +329,7 @@ def update_metadata(self, ds_info): metadata = {} metadata.update(ds_info) metadata.update({ - "instruments": self.sensor, + "instruments": instru.enum_to_str(self.sensor), "platform_name": self.platform_name, "start_time": self.start_time, "end_time": self.end_time, @@ -423,7 +425,7 @@ def get_dataset(self, ds_id, ds_info): data = data.rename(new_name_or_name_dict=ds_info["name"]) data, ds_info = self.apply_attributes(data, ds_info) - if self.sensor == {"ATMS"} and self.limb_correction: + if self.sensor == atms and self.limb_correction: sfc_type_mask = self["Sfc_type"] data = limb_correct_atms_bt(data, sfc_type_mask, self._get_coeff_filenames, diff --git a/satpy/readers/msu_gsa_l1b.py b/satpy/readers/msu_gsa_l1b.py index ecc1b35502..d443e0b3a0 100644 --- a/satpy/readers/msu_gsa_l1b.py +++ b/satpy/readers/msu_gsa_l1b.py @@ -30,6 +30,7 @@ import numpy as np +from satpy._instruments import OSCAR from satpy.readers.core.hdf5 import HDF5FileHandler @@ -64,8 +65,7 @@ def satellite_longitude(self): @property def sensor_name(self): """Sensor name is hardcoded.""" - sensor = "MSU-GS/A" - return sensor + return OSCAR.MSU_GS_A @property def platform_name(self): @@ -104,7 +104,7 @@ def get_dataset(self, dataset_id, ds_info): data.attrs = attrs data.attrs.update({ "platform_name": self.platform_name, - "instruments": {self.sensor_name}, + "instruments": {str(self.sensor_name)}, "sat_altitude": self.satellite_altitude, "sat_latitude": self.satellite_latitude, "sat_longitude": self.satellite_longitude, diff --git a/satpy/readers/mwr_l1b.py b/satpy/readers/mwr_l1b.py index 39ba265a9d..3d1b508549 100644 --- a/satpy/readers/mwr_l1b.py +++ b/satpy/readers/mwr_l1b.py @@ -57,6 +57,7 @@ import xarray as xr +from satpy._instruments import OSCAR from satpy.readers.core.netcdf import NetCDF4FileHandler MWR_CHANNEL_NAMES = [str(i) for i in range(1, 20)] @@ -113,9 +114,7 @@ def sensor(self): """Get the sensor name.""" # This should have been self["/attr/instrument"] # But the sensor name is currently incorrect in the ESA level-1b files. - # Also, MWR is not a valid WMO acronym. OSCAR lists "MWR (Sterna)", "MWR (AWS)" - # etc. But to avoid enhancement/composite duplication we stick to "MWR". - return "MWR" + return OSCAR.MWR @property def platform_name(self): diff --git a/satpy/readers/mwr_l1c.py b/satpy/readers/mwr_l1c.py index 6a2c7e3fca..11087844bd 100644 --- a/satpy/readers/mwr_l1c.py +++ b/satpy/readers/mwr_l1c.py @@ -42,6 +42,7 @@ """ +from satpy._instruments import OSCAR from satpy.readers.mwr_l1b import MWR_CHANNEL_NAMES, AWS_EPS_Sterna_BaseFileHandler, mask_and_scale NAVIGATION_DATASET_NAMES = ["satellite_zenith_angle", @@ -72,9 +73,7 @@ def sensor(self): """Get the sensor name.""" # This should have been self["/attr/instrument"] # But the sensor name is currently incorrect in the ESA level-1b files - # Also, MWR is not a valid WMO acronym. OSCAR lists "MWR (Sterna)", "MWR (AWS)" - # etc. But to avoid enhancement/composite duplication we stick to "MWR". - return "MWR" + return OSCAR.MWR def get_dataset(self, dataset_id, dataset_info): """Get the data.""" diff --git a/satpy/readers/mws_l1b.py b/satpy/readers/mws_l1b.py index f65528ad19..1ac09627c5 100644 --- a/satpy/readers/mws_l1b.py +++ b/satpy/readers/mws_l1b.py @@ -220,7 +220,7 @@ def _get_dataset_channel(self, key, dataset_info): dataset_attrs.update(dataset_info) dataset_attrs.update({ "platform_name": self.platform_name, - "instruments": {self.sensor}, + "instruments": {str(self.sensor)}, "orbital_parameters": {"sub_satellite_latitude_start": self.sub_satellite_latitude_start, "sub_satellite_longitude_start": self.sub_satellite_longitude_start, "sub_satellite_latitude_end": self.sub_satellite_latitude_end, @@ -267,7 +267,7 @@ def _get_global_attributes(self): "start_time": self.start_time, "end_time": self.end_time, "spacecraft_name": self.platform_name, - "instruments": {self.sensor}, + "instruments": {str(self.sensor)}, "filename_start_time": self.filename_info["start_time"], "filename_end_time": self.filename_info["end_time"], "platform_name": self.platform_name, diff --git a/satpy/readers/nucaps.py b/satpy/readers/nucaps.py index 8e0244eeab..4cbc338070 100644 --- a/satpy/readers/nucaps.py +++ b/satpy/readers/nucaps.py @@ -40,6 +40,8 @@ import pandas as pd import xarray as xr +import satpy._instruments as instru +from satpy._instruments import OSCAR from satpy.readers.core.netcdf import NetCDF4FileHandler from satpy.readers.core.yaml_reader import FileYAMLReader @@ -136,7 +138,7 @@ def sensor_names(self): res = self["/attr/instrument_name"] return {x.strip() for x in res.split(",")} except KeyError: - return {"CrIS", "ATMS", "VIIRS"} + return {OSCAR.CRIS, OSCAR.ATMS, OSCAR.VIIRS} def get_shape(self, ds_id, ds_info): """Return data array shape for item specified.""" @@ -169,7 +171,7 @@ def get_metadata(self, dataset_id, ds_info): "shape": shape, "units": ds_info.get("units", file_units), "platform_name": self.platform_name, - "instruments": self.sensor_names, + "instruments": instru.enum_to_str(self.sensor_names), "start_orbit": self.start_orbit_number, "end_orbit": self.end_orbit_number, }) diff --git a/satpy/readers/nwcsaf_hrw_nc.py b/satpy/readers/nwcsaf_hrw_nc.py index 35ecf93f38..c659f1854b 100644 --- a/satpy/readers/nwcsaf_hrw_nc.py +++ b/satpy/readers/nwcsaf_hrw_nc.py @@ -110,6 +110,7 @@ import xarray as xr from packaging.version import Version +from satpy._instruments import OSCAR from satpy.readers.core.file_handlers import BaseFileHandler from satpy.readers.nwcsaf_nc import PLATFORM_NAMES, SENSOR, read_nwcsaf_time from satpy.utils import get_chunk_size_limit @@ -241,7 +242,7 @@ def __init__(self, filename, filename_info, filetype_info, merge_channels=False) self.filetype_info = filetype_info self.merge_channels = merge_channels self.platform_name = PLATFORM_NAMES.get(self.h5f.attrs["satellite_identifier"].astype(str)) - self.sensor = SENSOR.get(self.platform_name, "SEVIRI") + self.sensor = SENSOR.get(self.platform_name, OSCAR.SEVIRI) self.lons = {} self.lats = {} # Imaging period, which is set after reading any data, and used to calculate end time diff --git a/satpy/readers/nwcsaf_nc.py b/satpy/readers/nwcsaf_nc.py index d38feed33d..c7913cc5da 100644 --- a/satpy/readers/nwcsaf_nc.py +++ b/satpy/readers/nwcsaf_nc.py @@ -35,6 +35,7 @@ from pyproj import CRS from pyresample.geometry import AreaDefinition +from satpy._instruments import OSCAR from satpy.readers.core.file_handlers import BaseFileHandler from satpy.readers.core.utils import unzip_file from satpy.utils import get_chunk_size_limit @@ -47,28 +48,28 @@ SENSOR = { - "NOAA-19": "AVHRR/3", - "NOAA-18": "AVHRR/3", - "NOAA-15": "AVHRR/3", - "Metop-A": "AVHRR/3", - "Metop-B": "AVHRR/3", - "Metop-C": "AVHRR/3", - "EOS-Aqua": "MODIS", - "EOS-Terra": "MODIS", - "Suomi-NPP": "VIIRS", - "NOAA-20": "VIIRS", - "NOAA-21": "VIIRS", - "NOAA-22": "VIIRS", - "NOAA-23": "VIIRS", - "JPSS-1": "VIIRS", - "Metop-SG-A1": "METimage", - "Metop-SG-A2": "METimage", - "Metop-SG-A3": "METimage", - "GOES-16": "ABI", - "GOES-17": "ABI", - "Himawari-8": "AHI", - "Himawari-9": "AHI", - "Meteosat-12": "FCI", + "NOAA-19": OSCAR.AVHRR_3, + "NOAA-18": OSCAR.AVHRR_3, + "NOAA-15": OSCAR.AVHRR_3, + "Metop-A": OSCAR.AVHRR_3, + "Metop-B": OSCAR.AVHRR_3, + "Metop-C": OSCAR.AVHRR_3, + "EOS-Aqua": OSCAR.MODIS, + "EOS-Terra": OSCAR.MODIS, + "Suomi-NPP": OSCAR.VIIRS, + "NOAA-20": OSCAR.VIIRS, + "NOAA-21": OSCAR.VIIRS, + "NOAA-22": OSCAR.VIIRS, + "NOAA-23": OSCAR.VIIRS, + "JPSS-1": OSCAR.VIIRS, + "Metop-SG-A1": OSCAR.METIMAGE, + "Metop-SG-A2": OSCAR.METIMAGE, + "Metop-SG-A3": OSCAR.METIMAGE, + "GOES-16": OSCAR.ABI, + "GOES-17": OSCAR.ABI, + "Himawari-8": OSCAR.AHI, + "Himawari-9": OSCAR.AHI, + "Meteosat-12": OSCAR.FCI, } @@ -228,7 +229,7 @@ def scale_dataset(self, variable, info): variable.attrs.pop("scale_factor", None) variable.attrs.update({"platform_name": self.platform_name, - "instruments": self.sensor}) + "instruments": str(self.sensor)}) if not variable.attrs.get("standard_name", "").endswith("status_flag"): # TODO: do we really need to add units to everything ? diff --git a/satpy/readers/oceancolorcci_l3_nc.py b/satpy/readers/oceancolorcci_l3_nc.py index bf5fb4c652..b17d2c4ca2 100644 --- a/satpy/readers/oceancolorcci_l3_nc.py +++ b/satpy/readers/oceancolorcci_l3_nc.py @@ -31,6 +31,7 @@ import numpy as np from pyresample import geometry +from satpy._instruments import OSCAR from satpy.readers.core.netcdf import NetCDF4FileHandler logger = logging.getLogger(__name__) @@ -74,7 +75,7 @@ def _update_attrs(self, dataset, dataset_info): """Update dataset attributes.""" dataset.attrs.update(self[dataset_info["nc_key"]].attrs) dataset.attrs.update(dataset_info) - dataset.attrs["instruments"] = {"SeaWiFS", "MERIS", "MODIS", "VIIRS"} + dataset.attrs["instruments"] = {OSCAR.SEAWIFS.value, OSCAR.MERIS.value, OSCAR.MODIS.value, OSCAR.VIIRS.value} dataset.attrs["composite_period"] = self.composite_period # remove attributes from original file which don't apply anymore dataset.attrs.pop("nc_key") diff --git a/satpy/readers/olci_nc.py b/satpy/readers/olci_nc.py index 9450e8611b..1886eca645 100644 --- a/satpy/readers/olci_nc.py +++ b/satpy/readers/olci_nc.py @@ -47,6 +47,7 @@ import xarray as xr from satpy._compat import cached_property +from satpy._instruments import OSCAR from satpy.readers.core.file_handlers import BaseFileHandler from satpy.readers.core.remote import open_file_or_filename from satpy.utils import angle2xyz, get_legacy_chunk_size, xyz2angle @@ -129,7 +130,7 @@ def __init__(self, filename, filename_info, filetype_info, engine=None, **kwargs self._end_time = filename_info["end_time"] # TODO: get metadata from the manifest file (xfdumanifest.xml) self.platform_name = PLATFORM_NAMES[filename_info["mission_id"]] - self.sensor = "OLCI" + self.sensor = OSCAR.OLCI @cached_property def nc(self): diff --git a/satpy/readers/omps_edr.py b/satpy/readers/omps_edr.py index ce6e610d66..03dc72af50 100644 --- a/satpy/readers/omps_edr.py +++ b/satpy/readers/omps_edr.py @@ -113,7 +113,7 @@ def get_metadata(self, dataset_id, ds_info): info.update( { "platform_name": self.platform_name, - "instruments": {self.sensor_name}, + "instruments": {str(self.sensor_name)}, "orbital_parameters": { "start_orbit": self.start_orbit_number, "end_orbit": self.end_orbit_number, diff --git a/satpy/readers/pace_oci_l1b_nc.py b/satpy/readers/pace_oci_l1b_nc.py index 9225b715b7..91c3636f24 100644 --- a/satpy/readers/pace_oci_l1b_nc.py +++ b/satpy/readers/pace_oci_l1b_nc.py @@ -45,6 +45,7 @@ import xarray as xr from satpy._compat import cached_property +from satpy._instruments import OSCAR from satpy.readers.core.file_handlers import BaseFileHandler from satpy.readers.core.remote import open_file_or_filename from satpy.utils import get_legacy_chunk_size @@ -80,7 +81,7 @@ def __init__(self, filename, filename_info, filetype_info, **kwargs): """Init the oci reader base.""" super().__init__(filename, filename_info, filetype_info) self._engine = kwargs.get("engine", None) - self.sensor = "oci" + self.sensor = OSCAR.OCI # Get the per-band solar irradiance and central wavelength values, these are split across # three variables, one each for the blue, red and SWIR bands. diff --git a/satpy/readers/scmi.py b/satpy/readers/scmi.py index d9c1a3cd71..5ec223f625 100644 --- a/satpy/readers/scmi.py +++ b/satpy/readers/scmi.py @@ -49,6 +49,7 @@ import xarray as xr from pyresample import geometry +from satpy._instruments import OSCAR from satpy.readers.core.file_handlers import BaseFileHandler from satpy.utils import get_legacy_chunk_size @@ -83,7 +84,7 @@ def _get_sensor(self): is_h8 = "H8" in self.platform_name is_h9 = "H9" in self.platform_name is_ahi = is_h8 or is_h9 - return "AHI" if is_ahi else "ABI" + return OSCAR.AHI if is_ahi else OSCAR.ABI @property def sensor_names(self): @@ -155,8 +156,9 @@ def get_dataset(self, key, info): data.attrs["units"] = "%" # set up all the attributes that might be useful to the user/satpy + instrument = data.attrs.get("sensor", self.sensor) data.attrs.update({"platform_name": self.platform_name, - "instruments": {data.attrs.get("sensor", self.sensor)}, + "instruments": {str(instrument)}, }) if "satellite_longitude" in self.nc.attrs: data.attrs["orbital_parameters"] = { diff --git a/satpy/readers/seadas_l2.py b/satpy/readers/seadas_l2.py index 9e5eddc087..139f55fb2a 100644 --- a/satpy/readers/seadas_l2.py +++ b/satpy/readers/seadas_l2.py @@ -31,6 +31,7 @@ import datetime as dt +from satpy._instruments import OSCAR from satpy.readers.core.hdf4 import HDF4FileHandler from satpy.readers.core.netcdf import NetCDF4FileHandler @@ -50,11 +51,11 @@ def _add_satpy_metadata(self, data): return data def _rows_per_scan(self): - if "MODIS" in self.sensor_names: + if OSCAR.MODIS in self.sensor_names: return 10 - if "VIIRS" in self.sensor_names: + if OSCAR.VIIRS in self.sensor_names: return 16 - if "OCI" in self.sensor_names: + if OSCAR.OCI in self.sensor_names: return 0 raise ValueError(f"Don't know how to read data for sensors: {self.sensor_names}") @@ -83,9 +84,9 @@ def sensor_names(self): # Example: MODISA or VIIRSN or VIIRSJ1 sensor_name = self[self.sensor_attr_name] if sensor_name.startswith("MODIS"): - return {"MODIS"} + return {OSCAR.MODIS.value} if sensor_name.startswith("VIIRS"): - return {"VIIRS"} + return {OSCAR.VIIRS.value} # Example: OCI return {sensor_name} diff --git a/satpy/readers/seviri_l1b_hrit.py b/satpy/readers/seviri_l1b_hrit.py index cfa052ebd8..2b40c9ea62 100644 --- a/satpy/readers/seviri_l1b_hrit.py +++ b/satpy/readers/seviri_l1b_hrit.py @@ -224,6 +224,7 @@ import satpy.readers.core.utils as utils from satpy._compat import cached_property +from satpy._instruments import OSCAR from satpy.readers.core._geos_area import get_area_definition, get_area_extent, get_geos_area_naming from satpy.readers.core.eum import get_service_mode, recarray2dict, time_cds_short from satpy.readers.core.hrit import ( @@ -775,7 +776,7 @@ def _update_attrs(self, res, info): res.attrs["wavelength"] = info["wavelength"] res.attrs["standard_name"] = info["standard_name"] res.attrs["platform_name"] = self.platform_name - res.attrs["instruments"] = {"SEVIRI"} + res.attrs["instruments"] = {OSCAR.SEVIRI.value} res.attrs["nominal_start_time"] = self.nominal_start_time res.attrs["nominal_end_time"] = self.nominal_end_time res.attrs["time_parameters"] = { diff --git a/satpy/readers/seviri_l1b_native.py b/satpy/readers/seviri_l1b_native.py index 01b2c4d253..8c4281ade3 100644 --- a/satpy/readers/seviri_l1b_native.py +++ b/satpy/readers/seviri_l1b_native.py @@ -108,6 +108,7 @@ from pyresample import geometry from satpy._compat import cached_property +from satpy._instruments import OSCAR from satpy.readers.core._geos_area import get_area_definition, get_geos_area_naming from satpy.readers.core.eum import get_service_mode, recarray2dict, time_cds_short from satpy.readers.core.file_handlers import BaseFileHandler @@ -707,7 +708,7 @@ def _update_attrs(self, dataset, dataset_info): dataset.attrs["wavelength"] = dataset_info["wavelength"] dataset.attrs["standard_name"] = dataset_info["standard_name"] dataset.attrs["platform_name"] = self.mda["platform_name"] - dataset.attrs["instruments"] = {"SEVIRI"} + dataset.attrs["instruments"] = {OSCAR.SEVIRI.value} dataset.attrs["georef_offset_corrected"] = self.mda[ "offset_corrected"] dataset.attrs["time_parameters"] = { diff --git a/satpy/readers/seviri_l1b_nc.py b/satpy/readers/seviri_l1b_nc.py index 0ef6c073cf..56a0fd6870 100644 --- a/satpy/readers/seviri_l1b_nc.py +++ b/satpy/readers/seviri_l1b_nc.py @@ -23,6 +23,7 @@ import numpy as np from satpy._compat import cached_property +from satpy._instruments import OSCAR from satpy.readers.core._geos_area import get_area_definition, get_geos_area_naming from satpy.readers.core.eum import get_service_mode from satpy.readers.core.file_handlers import BaseFileHandler, open_dataset @@ -233,7 +234,7 @@ def _update_attrs(self, dataset, dataset_info): dataset.attrs.update(self.nc[dataset_info["nc_key"]].attrs) dataset.attrs.update(dataset_info) dataset.attrs["platform_name"] = "Meteosat-" + SATNUM[self.platform_id] - dataset.attrs["instruments"] = {"SEVIRI"} + dataset.attrs["instruments"] = {OSCAR.SEVIRI.value} dataset.attrs["orbital_parameters"] = { "projection_longitude": self.mda["projection_parameters"]["ssp_longitude"], "projection_latitude": 0., diff --git a/satpy/readers/sgli_l1b.py b/satpy/readers/sgli_l1b.py index b9344575e8..dd4cd0355d 100644 --- a/satpy/readers/sgli_l1b.py +++ b/satpy/readers/sgli_l1b.py @@ -37,6 +37,8 @@ import xarray as xr from dask.array.core import normalize_chunks +from satpy._instruments import OSCAR + # from satpy import CHUNK_SIZE from satpy.readers.core.file_handlers import BaseFileHandler @@ -89,7 +91,7 @@ def get_dataset(self, key, info): dataset = self.prepare_dataset(key, dataset) dataset.attrs["platform_name"] = "GCOM-C1" - dataset.attrs["instruments"] = {"SGLI"} + dataset.attrs["instruments"] = {OSCAR.SGLI.value} dataset.attrs["units"] = info["units"] dataset.attrs["standard_name"] = info["standard_name"] return dataset diff --git a/satpy/readers/slstr_l1b.py b/satpy/readers/slstr_l1b.py index 0cf132d263..63e13bd1fa 100644 --- a/satpy/readers/slstr_l1b.py +++ b/satpy/readers/slstr_l1b.py @@ -28,6 +28,7 @@ import numpy as np import xarray as xr +from satpy._instruments import OSCAR from satpy.readers.core.file_handlers import BaseFileHandler from satpy.utils import get_legacy_chunk_size @@ -260,7 +261,7 @@ def __init__(self, filename, filename_info, filetype_info): # TODO: get metadata from the manifest file (xfdumanifest.xml) self.platform_name = PLATFORM_NAMES[filename_info["mission_id"]] - self.sensor = "SLSTR" + self.sensor = OSCAR.SLSTR self.view = filename_info["view"] self._start_time = filename_info["start_time"] self._end_time = filename_info["end_time"] diff --git a/satpy/readers/tropomi_l2.py b/satpy/readers/tropomi_l2.py index 6c53e74b4f..6c536bb037 100644 --- a/satpy/readers/tropomi_l2.py +++ b/satpy/readers/tropomi_l2.py @@ -36,6 +36,7 @@ import numpy as np import xarray as xr +import satpy._instruments as instru from satpy.readers.core.netcdf import NetCDF4FileHandler from satpy.utils import get_legacy_chunk_size @@ -174,7 +175,7 @@ def get_metadata(self, data, ds_info): metadata.update(ds_info) metadata.update({ "platform_shortname": self.platform_shortname, - "instruments": self.sensor_names, + "instruments": instru.enum_to_str(self.sensor_names), "start_time": self.start_time, "end_time": self.end_time, "time_coverage_start": self.time_coverage_start, diff --git a/satpy/readers/viirs_compact.py b/satpy/readers/viirs_compact.py index 5a3905ee98..d3c3da4ea9 100644 --- a/satpy/readers/viirs_compact.py +++ b/satpy/readers/viirs_compact.py @@ -38,6 +38,7 @@ import numpy as np import xarray as xr +from satpy._instruments import OSCAR from satpy.readers.core.file_handlers import BaseFileHandler from satpy.readers.core.utils import np2str from satpy.utils import angle2xyz, get_legacy_chunk_size, lonlat2xyz, xyz2angle, xyz2lonlat @@ -136,7 +137,7 @@ def __init__(self, filename, filename_info, filetype_info): self.mda = {} short_name = np2str(self.h5f.attrs["Platform_Short_Name"]) self.mda["platform_name"] = short_names.get(short_name, short_name) - self.mda["instruments"] = {"VIIRS"} + self.mda["instruments"] = {OSCAR.VIIRS.value} def __del__(self): """Close file handlers when we are done.""" diff --git a/satpy/readers/viirs_edr.py b/satpy/readers/viirs_edr.py index c5956aaf76..a426ea2ea3 100644 --- a/satpy/readers/viirs_edr.py +++ b/satpy/readers/viirs_edr.py @@ -82,6 +82,7 @@ import xarray as xr from satpy import DataID +from satpy._instruments import OSCAR from satpy.readers.core.file_handlers import BaseFileHandler from satpy.utils import get_chunk_size_limit @@ -121,7 +122,7 @@ def __init__(self, filename, filename_info, filetype_info, **kwargs): self.nc["Longitude"].attrs.update({"standard_name": "longitude"}) self.algorithm_version = filename_info["platform_shortname"] - self.sensor_name = "VIIRS" + self.sensor_name = OSCAR.VIIRS def rows_per_scans(self, data_arr: xr.DataArray) -> int: """Get number of array rows per instrument scan based on data resolution.""" diff --git a/satpy/readers/viirs_edr_active_fires.py b/satpy/readers/viirs_edr_active_fires.py index 5465651fb7..3a4d6ac806 100644 --- a/satpy/readers/viirs_edr_active_fires.py +++ b/satpy/readers/viirs_edr_active_fires.py @@ -24,6 +24,7 @@ import dask.dataframe as dd import xarray as xr +from satpy._instruments import OSCAR from satpy.dataset.dataid import DataID from satpy.readers.core.file_handlers import BaseFileHandler from satpy.readers.core.netcdf import NetCDF4FileHandler @@ -122,7 +123,11 @@ def __init__(self, filename, filename_info, filetype_info): def get_dataset(self, dsid, dsinfo): """Get requested data as DataArray.""" ds = self[dsid["name"]].to_dask_array(lengths=True) - data = xr.DataArray(ds, dims=("y",), attrs={"platform_name": self.platform_name, "instruments": {"VIIRS"}}) + data = xr.DataArray( + ds, + dims=("y",), + attrs={"platform_name": self.platform_name, "instruments": {OSCAR.VIIRS.value}} + ) for key in ("units", "standard_name", "flag_meanings", "flag_values", "_FillValue"): # we only want to add information that isn't present already if key in dsinfo and key not in data.attrs: diff --git a/satpy/readers/viirs_edr_flood.py b/satpy/readers/viirs_edr_flood.py index 6f8c719022..5d75642a38 100644 --- a/satpy/readers/viirs_edr_flood.py +++ b/satpy/readers/viirs_edr_flood.py @@ -58,7 +58,7 @@ def get_metadata(self, data, ds_info): metadata.update(data.attrs) metadata.update(ds_info) metadata.update({ - "instruments": {self.sensor_name}, + "instruments": {str(self.sensor_name)}, "platform_name": self.platform_name, "start_time": self.start_time, "end_time": self.end_time, diff --git a/satpy/readers/viirs_l1b.py b/satpy/readers/viirs_l1b.py index 388e247c0d..fe709e22cc 100644 --- a/satpy/readers/viirs_l1b.py +++ b/satpy/readers/viirs_l1b.py @@ -191,7 +191,7 @@ def get_metadata(self, dataset_id, ds_info): "units": ds_info.get("units", file_units), "file_units": file_units, "platform_name": self.platform_name, - "instruments": {self.sensor_name}, + "instruments": {str(self.sensor_name)}, "day_night": self["/attr/DayNightFlag"], "orbital_parameters": orb_param, "start_orbit": self.start_orbit_number, diff --git a/satpy/readers/viirs_l2.py b/satpy/readers/viirs_l2.py index bbfd880f13..c201b2738c 100644 --- a/satpy/readers/viirs_l2.py +++ b/satpy/readers/viirs_l2.py @@ -106,7 +106,7 @@ def get_metadata(self, dataset_id, ds_info): { "file_units": file_units, "platform_name": self.platform_name, - "instruments": {self.sensor_name}, + "instruments": {str(self.sensor_name)}, "start_orbit": self.start_orbit_number, "end_orbit": self.end_orbit_number, } diff --git a/satpy/tests/reader_tests/test_aapp_mhs_amsub_l1c.py b/satpy/tests/reader_tests/test_aapp_mhs_amsub_l1c.py index a7cfa17ddb..4203f8a329 100644 --- a/satpy/tests/reader_tests/test_aapp_mhs_amsub_l1c.py +++ b/satpy/tests/reader_tests/test_aapp_mhs_amsub_l1c.py @@ -26,6 +26,7 @@ import numpy as np import pytest +from satpy._instruments import OSCAR from satpy.readers.aapp_mhs_amsub_l1c import _HEADERTYPE, _SCANTYPE, HEADER_LENGTH, MHS_AMSUB_AAPPL1CFile from satpy.tests.utils import make_dataid @@ -378,7 +379,7 @@ def test_sensor_name(self): fh_ = MHS_AMSUB_AAPPL1CFile(tmpfile, self.filename_info, self.filetype_info) - assert fh_.sensor == "mhs" + assert fh_.sensor == OSCAR.MHS self._header["instrument"][0] = 11 with tempfile.TemporaryFile() as tmpfile: @@ -388,7 +389,7 @@ def test_sensor_name(self): fh_ = MHS_AMSUB_AAPPL1CFile(tmpfile, self.filename_info, self.filetype_info) - assert fh_.sensor == "amsub" + assert fh_.sensor == OSCAR.AMSU_B self._header["instrument"][0] = 10 From da20004cb031d9a73a69633633b73f0de4243c8c Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Mon, 18 May 2026 15:36:12 +0000 Subject: [PATCH 37/72] Revert MIMIC changes --- satpy/readers/mimic_TPW2_nc.py | 2 +- satpy/tests/reader_tests/test_mimic_TPW2_lowres.py | 6 +++--- satpy/tests/reader_tests/test_mimic_TPW2_nc.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/satpy/readers/mimic_TPW2_nc.py b/satpy/readers/mimic_TPW2_nc.py index b7429a66f9..053b6645ac 100644 --- a/satpy/readers/mimic_TPW2_nc.py +++ b/satpy/readers/mimic_TPW2_nc.py @@ -149,7 +149,7 @@ def get_metadata(self, data, info): metadata.update(info) metadata.update({ "platform_shortname": "aggregated microwave", - "instruments": {"MIMIC"}, + "instruments": {"mimic"}, "start_time": self.start_time, "end_time": self.end_time, }) diff --git a/satpy/tests/reader_tests/test_mimic_TPW2_lowres.py b/satpy/tests/reader_tests/test_mimic_TPW2_lowres.py index 914c101488..69fc65088a 100644 --- a/satpy/tests/reader_tests/test_mimic_TPW2_lowres.py +++ b/satpy/tests/reader_tests/test_mimic_TPW2_lowres.py @@ -120,7 +120,7 @@ def test_load_mimic_float(self, mimic_file): assert len(ds) == len(float_variables) for d in ds.values(): assert d.attrs["platform_shortname"] == "aggregated microwave" - assert d.attrs["instruments"] == {"MIMIC"} + assert d.attrs["instruments"] == {"mimic"} assert d.attrs["units"] == "mm" assert "area" in d.attrs assert d.attrs["area"] is not None @@ -135,7 +135,7 @@ def test_load_mimic_timedelta(self, mimic_file): assert len(ds) == len(date_variables) for d in ds.values(): assert d.attrs["platform_shortname"] == "aggregated microwave" - assert d.attrs["instruments"] == {"MIMIC"} + assert d.attrs["instruments"] == {"mimic"} assert d.attrs["units"] == "minutes" assert "area" in d.attrs assert d.attrs["area"] is not None @@ -151,7 +151,7 @@ def test_load_mimic_ubyte(self, mimic_file): assert len(ds) == len(ubyte_variables) for d in ds.values(): assert d.attrs["platform_shortname"] == "aggregated microwave" - assert d.attrs["instruments"] == {"MIMIC"} + assert d.attrs["instruments"] == {"mimic"} assert "source_key" in d.attrs assert "area" in d.attrs assert d.attrs["area"] is not None diff --git a/satpy/tests/reader_tests/test_mimic_TPW2_nc.py b/satpy/tests/reader_tests/test_mimic_TPW2_nc.py index 3cd1523192..7b89abe6fb 100644 --- a/satpy/tests/reader_tests/test_mimic_TPW2_nc.py +++ b/satpy/tests/reader_tests/test_mimic_TPW2_nc.py @@ -97,7 +97,7 @@ def test_load_mimic(self, mimic_file): assert len(ds) == 1 for d in ds.values(): assert d.attrs["platform_shortname"] == "aggregated microwave" - assert d.attrs["instruments"] == {"MIMIC"} + assert d.attrs["instruments"] == {"mimic"} assert "area" in d.attrs assert "units" in d.attrs assert d.attrs["area"] is not None From 4f0ff261b08bebcfe7ebd640fff503547ab3e0e8 Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Mon, 18 May 2026 15:57:41 +0000 Subject: [PATCH 38/72] Adapt instrument normalization to WMO names --- satpy/_instruments.py | 20 ++++++++++++++++++-- satpy/tests/test_instruments.py | 8 ++++---- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/satpy/_instruments.py b/satpy/_instruments.py index 44cd1d590a..793d09ab64 100644 --- a/satpy/_instruments.py +++ b/satpy/_instruments.py @@ -55,7 +55,15 @@ def get_instruments_from_attrs(attrs: dict[str,Any]) -> set[str]: def normalize_instrument_name(instrument: str) -> str: """Normalize instrument name for internal usage.""" - return instrument.replace("-", "").replace(" ", "_").replace("/", "-").lower() + sep_map = { + "-": "", + "(": "", + ")": "", + " ": "_", + "/": "-" + } + sep_trans = str.maketrans(sep_map) + return instrument.translate(sep_trans).lower() def get_one_instrument_from_attrs(attrs: dict[str,Any]) -> str: @@ -75,8 +83,16 @@ def get_pyspectral_instrument_name(instrument: str) -> str: def serialize_instruments(instruments: set[str]) -> str: """Serialize a set of instruments.""" + sep_map = { + "-": "", + "(": "", + ")": "", + " ": "", + "/": "" + } + sep_trans = str.maketrans(sep_map) return "-".join( - instr.replace("-", "").replace(" ", "").replace("/", "").lower() + instr.translate(sep_trans).lower() for instr in sorted(instruments) ) diff --git a/satpy/tests/test_instruments.py b/satpy/tests/test_instruments.py index 473aad6a9d..22e4cf28d2 100644 --- a/satpy/tests/test_instruments.py +++ b/satpy/tests/test_instruments.py @@ -47,14 +47,14 @@ def test_get_instruments_from_attrs_with_warning(attrs, expected): def test_normalize_instrument_name(): """Test instrument name normalization.""" - instr = "My Instrument-123/1" - expected = "my_instrument123-1" + instr = "My Instrument-123/1 (My Platform)" + expected = "my_instrument123-1_my_platform" assert instru.normalize_instrument_name(instr) == expected def test_serialize_instruments(): """Test instrument set serialization.""" - instruments = {"My Instrument-123/1", "ABI"} - expected = "abi-myinstrument1231" + instruments = {"My Instrument-123/1 (My Platform)", "ABI"} + expected = "abi-myinstrument1231myplatform" assert instru.serialize_instruments(instruments) == expected def test_set_instruments_attr(): From c859cd8e03eb48187ed5bd3983725823c2870f61 Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Tue, 19 May 2026 09:01:38 +0000 Subject: [PATCH 39/72] Use lists for instruments in enhancements --- satpy/decision_tree.py | 19 ++++++++++++--- satpy/etc/enhancements/abi.yaml | 24 +++++++++---------- satpy/etc/enhancements/ahi.yaml | 4 ++-- satpy/etc/enhancements/amsr2.yaml | 24 +++++++++---------- .../tests/enhancement_tests/test_enhancer.py | 4 ++-- 5 files changed, 44 insertions(+), 31 deletions(-) diff --git a/satpy/decision_tree.py b/satpy/decision_tree.py index c1f5e25162..c9adc26ac4 100644 --- a/satpy/decision_tree.py +++ b/satpy/decision_tree.py @@ -163,8 +163,8 @@ def _build_tree(self, conf): for match_level, match_key in enumerate(self._match_keys): # or None is necessary if they have empty strings this_attr_val = sect_attrs.get(match_key, self.any_key) or None - if match_key in self._multival_keys and isinstance(this_attr_val, list): - this_attr_val = tuple(sorted(this_attr_val)) + if match_key in self._multival_keys: + this_attr_val = self._normalize_multival_attr(this_attr_val) is_last_key = match_key == self._match_keys[-1] level_needs_init = this_attr_val not in curr_level if is_last_key: @@ -176,10 +176,23 @@ def _build_tree(self, conf): curr_level[this_attr_val] = _DecisionDict(self._match_keys[match_level + 1], match_level + 1) curr_level = curr_level[this_attr_val] + def _normalize_multival_attr(self, attr): + """Convert multival attributes from list/str to sorted tuple.""" + if isinstance(attr, list): + return tuple(sorted(attr)) + elif isinstance(attr, str): + return tuple([attr]) + return attr + @staticmethod def _convert_query_val_to_hashable(query_val): _sorted_query_val = sorted(query_val) - query_vals = [tuple(_sorted_query_val)] + _sorted_query_val + # Full match: All elements in the tuple + query_vals = [tuple(_sorted_query_val)] + # Partial match: One element of the tuple, as tuple + query_vals += [tuple([v]) for v in _sorted_query_val] + # Partial match: One element of the tuple, as string + query_vals += _sorted_query_val query_vals += query_val return query_vals diff --git a/satpy/etc/enhancements/abi.yaml b/satpy/etc/enhancements/abi.yaml index 7952c30a83..e72e7a7f63 100644 --- a/satpy/etc/enhancements/abi.yaml +++ b/satpy/etc/enhancements/abi.yaml @@ -1,7 +1,7 @@ enhancements: cimss_true_color: standard_name: cimss_true_color - instruments: abi + instruments: [abi] operations: - name: linear_stretch method: !!python/name:satpy.enhancements.contrast.stretch @@ -35,7 +35,7 @@ enhancements: true_color_with_night_fires: standard_name: true_color_with_night_fires - instruments: abi + instruments: [abi] operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch @@ -77,7 +77,7 @@ enhancements: ash_abi: ## RGB Ash recipe source: http://rammb.cira.colostate.edu/training/visit/quick_guides/GOES_Ash_RGB.pdf standard_name: ash - instruments: abi + instruments: [abi] operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch @@ -89,7 +89,7 @@ enhancements: dust_abi: ## RGB Dust recipe source: http://rammb.cira.colostate.edu/training/visit/quick_guides/Dust_RGB_Quick_Guide.pdf standard_name: dust - instruments: abi + instruments: [abi] operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch @@ -105,7 +105,7 @@ enhancements: convection_abi: ## RGB Convection recipe source: http://rammb.cira.colostate.edu/training/visit/quick_guides/QuickGuide_GOESR_DayConvectionRGB_final.pdf standard_name: convection - instruments: abi + instruments: [abi] operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch @@ -142,7 +142,7 @@ enhancements: night_microphysics_abi: ## RGB Nighttime Microphysics recipe source: http://rammb.cira.colostate.edu/training/visit/quick_guides/QuickGuide_GOESR_NtMicroRGB_final.pdf standard_name: night_microphysics - instruments: abi + instruments: [abi] operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch @@ -154,7 +154,7 @@ enhancements: night_microphysics_tropical_abi: ## RGB Nighttime Microphysics recipe source: http://rammb.cira.colostate.edu/training/visit/quick_guides/QuickGuide_GOESR_NtMicroRGB_final.pdf standard_name: night_microphysics_tropical - instruments: abi + instruments: [abi] operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch @@ -170,7 +170,7 @@ enhancements: land_cloud_fire: ## RGB Day Land Cloud Fire recipe source: http://rammb.cira.colostate.edu/training/visit/quick_guides/QuickGuide_GOESR_DayLandCloudFireRGB_final.pdf standard_name: land_cloud_fire - instruments: abi + instruments: [abi] operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch @@ -179,7 +179,7 @@ enhancements: land_cloud: ## RGB Day Land Cloud Fire recipe source: http://rammb.cira.colostate.edu/training/visit/quick_guides/QuickGuide_GOESR_daylandcloudRGB_final.pdf standard_name: land_cloud - instruments: abi + instruments: [abi] operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch @@ -191,7 +191,7 @@ enhancements: # IR with white clouds highlighted_brightness_temperature: standard_name: highlighted_toa_brightness_temperature - instruments: abi + instruments: [abi] operations: - name: btemp_threshold method: !!python/name:satpy.enhancements.contrast.btemp_threshold @@ -308,7 +308,7 @@ enhancements: ## RGB Recipe: https://rammb2.cira.colostate.edu/wp-content/uploads/2024/11/GOES-BlowingSnowRGB1_QuickGuide_24April2024.pdf ## Modified to match recommendations from RGB Workshop 2025 standard_name: day_blowing_snow - instruments: abi + instruments: [abi] operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch @@ -323,7 +323,7 @@ enhancements: day_cloud_type: # Recipe PDF: http://cimss.ssec.wisc.edu/goes/OCLOFactSheetPDFs/ABIQuickGuide_Day_Cloud_Type_RGB.pdf standard_name: day_cloud_type - instruments: abi + instruments: [abi] operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch diff --git a/satpy/etc/enhancements/ahi.yaml b/satpy/etc/enhancements/ahi.yaml index ce97bca324..3f73b9c1a4 100644 --- a/satpy/etc/enhancements/ahi.yaml +++ b/satpy/etc/enhancements/ahi.yaml @@ -12,7 +12,7 @@ enhancements: day_severe_storms: standard_name: day_severe_storms - instruments: ahi + instruments: [ahi] operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch @@ -27,7 +27,7 @@ enhancements: night_microphysics_tropical: standard_name: night_microphysics_tropical - instruments: ahi + instruments: [ahi] operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch diff --git a/satpy/etc/enhancements/amsr2.yaml b/satpy/etc/enhancements/amsr2.yaml index 4a0ac05412..788b52cc2c 100644 --- a/satpy/etc/enhancements/amsr2.yaml +++ b/satpy/etc/enhancements/amsr2.yaml @@ -3,28 +3,28 @@ enhancements: # https://www.ospo.noaa.gov/Products/atmosphere/gpds/maps.html?GPRR#gpdsMaps gaasp_clw: name: CLW - instruments: amsr2 + instruments: [amsr2] operations: - name: linear_stretch method: !!python/name:satpy.enhancements.contrast.stretch kwargs: {stretch: 'crude', min_stretch: 0.0, max_stretch: 0.5} gaasp_sst: name: SST - instruments: amsr2 + instruments: [amsr2] operations: - name: linear_stretch method: !!python/name:satpy.enhancements.contrast.stretch kwargs: {stretch: 'crude', min_stretch: -5.0, max_stretch: 35} gaasp_tpw: name: TPW - instruments: amsr2 + instruments: [amsr2] operations: - name: linear_stretch method: !!python/name:satpy.enhancements.contrast.stretch kwargs: {stretch: 'crude', min_stretch: 0.0, max_stretch: 75.0} gaasp_wspd: name: WSPD - instruments: amsr2 + instruments: [amsr2] operations: - name: linear_stretch method: !!python/name:satpy.enhancements.contrast.stretch @@ -32,56 +32,56 @@ enhancements: # Snow_Cover unscaled (category product) gaasp_snow_depth: name: Snow_Depth - instruments: amsr2 + instruments: [amsr2] operations: - name: linear_stretch method: !!python/name:satpy.enhancements.contrast.stretch kwargs: {stretch: 'crude', min_stretch: 0.0, max_stretch: 150.0} gaasp_swe: name: SWE - instruments: amsr2 + instruments: [amsr2] operations: - name: linear_stretch method: !!python/name:satpy.enhancements.contrast.stretch kwargs: {stretch: 'crude', min_stretch: 0.0, max_stretch: 16.0} gaasp_soil_moisture: name: Soil_Moisture - instruments: amsr2 + instruments: [amsr2] operations: - name: linear_stretch method: !!python/name:satpy.enhancements.contrast.stretch kwargs: {stretch: 'crude', min_stretch: 0.0, max_stretch: 100.0} gaasp_ice_concentration_nh: name: NASA_Team_2_Ice_Concentration_NH - instruments: amsr2 + instruments: [amsr2] operations: - name: linear_stretch method: !!python/name:satpy.enhancements.contrast.stretch kwargs: {stretch: 'crude', min_stretch: 0.0, max_stretch: 100.0} gaasp_ice_concentration_sh: name: NASA_Team_2_Ice_Concentration_SH - instruments: amsr2 + instruments: [amsr2] operations: - name: linear_stretch method: !!python/name:satpy.enhancements.contrast.stretch kwargs: {stretch: 'crude', min_stretch: 0.0, max_stretch: 100.0} # gaasp_latency_nh: # name: Latency_NH -# instruments: amsr2 +# instruments: [amsr2] # operations: # - name: linear_stretch # method: !!python/name:satpy.enhancements.contrast.stretch # kwargs: {stretch: 'crude', min_stretch: 0.0, max_stretch: 100.0} # gaasp_latency_sh: # name: Latency_SH -# instruments: amsr2 +# instruments: [amsr2] # operations: # - name: linear_stretch # method: !!python/name:satpy.enhancements.contrast.stretch # kwargs: {stretch: 'crude', min_stretch: 0.0, max_stretch: 100.0} gaasp_rain_rate: name: Rain_Rate - instruments: amsr2 + instruments: [amsr2] operations: - name: linear_stretch method: !!python/name:satpy.enhancements.contrast.stretch diff --git a/satpy/tests/enhancement_tests/test_enhancer.py b/satpy/tests/enhancement_tests/test_enhancer.py index bcb1504058..06e03da02a 100644 --- a/satpy/tests/enhancement_tests/test_enhancer.py +++ b/satpy/tests/enhancement_tests/test_enhancer.py @@ -144,7 +144,7 @@ class TestComplexSensorEnhancerConfigs(_BaseCustomEnhancementConfigTests): enhancements: test1_sensor1_specific: name: test1 - instruments: test_sensor1 + instruments: [test_sensor1] operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch @@ -160,7 +160,7 @@ class TestComplexSensorEnhancerConfigs(_BaseCustomEnhancementConfigTests): kwargs: {stretch: crude, min_stretch: 0, max_stretch: 100} test1_sensor2_specific: name: test1 - instruments: test_sensor2 + instruments: [test_sensor2] operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch From fd259b3b7c79eb762b66920a916f1fd7684e7f2f Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Tue, 19 May 2026 09:04:05 +0000 Subject: [PATCH 40/72] Normalize instrument names from dataset attributes --- satpy/enhancements/enhancer.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/satpy/enhancements/enhancer.py b/satpy/enhancements/enhancer.py index ac6f1cde13..d2dbcc40c5 100644 --- a/satpy/enhancements/enhancer.py +++ b/satpy/enhancements/enhancer.py @@ -228,7 +228,18 @@ def get_enhanced_image(dataset, enhance=None, overlay=None, decorate=None, sensors = instru.get_instruments_from_attrs(dataset.attrs) if sensors: enhancer.add_sensor_enhancements(sensors) - enhancer.apply(img, **dataset.attrs) + + # As long as enhancement YAMLs don't contain WMO instrument + # names yet, normalize instrument names from dataset + # attributes. This can be removed as soon as enhancement + # YAMLs have been updated. + attrs = dataset.attrs.copy() + normalized = { + instru.normalize_instrument_name(sensor) + for sensor in sensors + } + instru.set_instruments_attr(attrs, normalized) + enhancer.apply(img, **attrs) if overlay is not None: from satpy.enhancements.overlays import add_overlay From 4cc22ee5d1293c321c898e44829e6bfc148fcc74 Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Tue, 19 May 2026 10:01:27 +0000 Subject: [PATCH 41/72] Harmonize enum to string conversion --- satpy/readers/core/hdfeos.py | 2 +- satpy/readers/core/landsat.py | 3 ++- satpy/readers/core/vii_nc.py | 2 +- satpy/readers/core/viirs_atms_sdr.py | 2 +- satpy/readers/electrol_hrit.py | 2 +- satpy/readers/gms/gms5_vissr_l1b.py | 2 +- satpy/readers/oceancolorcci_l3_nc.py | 2 +- satpy/readers/seadas_l2.py | 7 ++++--- satpy/readers/seviri_l1b_hrit.py | 2 +- satpy/readers/seviri_l1b_native.py | 2 +- satpy/readers/seviri_l1b_nc.py | 2 +- satpy/readers/sgli_l1b.py | 2 +- satpy/readers/viirs_compact.py | 2 +- satpy/readers/viirs_edr_active_fires.py | 2 +- 14 files changed, 18 insertions(+), 16 deletions(-) diff --git a/satpy/readers/core/hdfeos.py b/satpy/readers/core/hdfeos.py index 03389546bb..ab0ba0a864 100644 --- a/satpy/readers/core/hdfeos.py +++ b/satpy/readers/core/hdfeos.py @@ -263,7 +263,7 @@ def _add_satpy_metadata(self, data_id: DataID, data_arr: xr.DataArray): """Add metadata that is specific to Satpy.""" new_attrs = { "platform_name": self.metadata_platform_name, - "instruments": {OSCAR.MODIS.value}, + "instruments": {str(OSCAR.MODIS)}, } res = data_id["resolution"] diff --git a/satpy/readers/core/landsat.py b/satpy/readers/core/landsat.py index 4af59e3a1e..fe739bd12c 100644 --- a/satpy/readers/core/landsat.py +++ b/satpy/readers/core/landsat.py @@ -43,6 +43,7 @@ import rioxarray # noqa: F401 # need by xarray with the engine rasterio import xarray as xr +import satpy._instruments as instru from satpy._instruments import OSCAR from satpy.readers.core.file_handlers import BaseFileHandler from satpy.readers.core.remote import open_file_or_filename @@ -213,7 +214,7 @@ def _collect_attrs(self, data, name, info): attrs["perc_cloud_cover"] = self._mda.cloud_cover # Add platform / sensor attributes attrs["platform_name"] = self.platform_name - attrs["instruments"] = self.sensor + attrs["instruments"] = instru.enum_to_str(self.sensor) # Apply attrs from YAML if "standard_name" in info: attrs["standard_name"] = info["standard_name"] diff --git a/satpy/readers/core/vii_nc.py b/satpy/readers/core/vii_nc.py index 8502fa802c..a1b2acb546 100644 --- a/satpy/readers/core/vii_nc.py +++ b/satpy/readers/core/vii_nc.py @@ -195,7 +195,7 @@ def _get_global_attributes(self): "end_time": self.end_time, "spacecraft_name": self.spacecraft_name, "ssp_lon": self.ssp_lon, - "instruments": {str(self.sensor)}, + "instruments": {self.sensor}, "filename_start_time": self.filename_info["sensing_start_time"], "filename_end_time": self.filename_info["sensing_end_time"], "platform_name": self.spacecraft_name, diff --git a/satpy/readers/core/viirs_atms_sdr.py b/satpy/readers/core/viirs_atms_sdr.py index bf07e4a3a8..b1627db7ca 100644 --- a/satpy/readers/core/viirs_atms_sdr.py +++ b/satpy/readers/core/viirs_atms_sdr.py @@ -276,7 +276,7 @@ def _update_data_attributes(self, data, dataset_id, ds_info): i.update(ds_info) i.update({ "platform_name": self.platform_name, - "instruments": {str(self.sensor_name)}, + "instruments": {self.sensor_name}, "start_orbit": self.start_orbit_number, "end_orbit": self.end_orbit_number, "units": output_units, diff --git a/satpy/readers/electrol_hrit.py b/satpy/readers/electrol_hrit.py index 8d8a7782ae..a9a793d3b7 100644 --- a/satpy/readers/electrol_hrit.py +++ b/satpy/readers/electrol_hrit.py @@ -287,7 +287,7 @@ def get_dataset(self, key, info): res.attrs["standard_name"] = info["standard_name"] res.attrs["wavelength"] = info["wavelength"] res.attrs["platform_name"] = self.platform_name - res.attrs["instruments"] = {OSCAR.MSU_GS.value} + res.attrs["instruments"] = {str(OSCAR.MSU_GS)} res.attrs["orbital_parameters"] = { "satellite_nominal_longitude": self.mda["orbital_parameters"]["satellite_nominal_longitude"], "satellite_nominal_latitude": 0., diff --git a/satpy/readers/gms/gms5_vissr_l1b.py b/satpy/readers/gms/gms5_vissr_l1b.py index 91418aaaab..ca1d14a804 100644 --- a/satpy/readers/gms/gms5_vissr_l1b.py +++ b/satpy/readers/gms/gms5_vissr_l1b.py @@ -287,7 +287,7 @@ def _get_nominal_shape(self): def _get_mda(self): return { "platform": self._mode_block["satellite_name"].decode().strip().upper(), - "instruments": {OSCAR.VISSR_HIMAWARI_5.value}, + "instruments": {str(OSCAR.VISSR_HIMAWARI_5)}, "time_parameters": self._get_time_parameters(), "orbital_parameters": self._get_orbital_parameters(), } diff --git a/satpy/readers/oceancolorcci_l3_nc.py b/satpy/readers/oceancolorcci_l3_nc.py index b17d2c4ca2..3a2e659ff0 100644 --- a/satpy/readers/oceancolorcci_l3_nc.py +++ b/satpy/readers/oceancolorcci_l3_nc.py @@ -75,7 +75,7 @@ def _update_attrs(self, dataset, dataset_info): """Update dataset attributes.""" dataset.attrs.update(self[dataset_info["nc_key"]].attrs) dataset.attrs.update(dataset_info) - dataset.attrs["instruments"] = {OSCAR.SEAWIFS.value, OSCAR.MERIS.value, OSCAR.MODIS.value, OSCAR.VIIRS.value} + dataset.attrs["instruments"] = {str(OSCAR.SEAWIFS), str(OSCAR.MERIS), str(OSCAR.MODIS), str(OSCAR.VIIRS)} dataset.attrs["composite_period"] = self.composite_period # remove attributes from original file which don't apply anymore dataset.attrs.pop("nc_key") diff --git a/satpy/readers/seadas_l2.py b/satpy/readers/seadas_l2.py index 139f55fb2a..4d2887a9a2 100644 --- a/satpy/readers/seadas_l2.py +++ b/satpy/readers/seadas_l2.py @@ -31,6 +31,7 @@ import datetime as dt +import satpy._instruments as instru from satpy._instruments import OSCAR from satpy.readers.core.hdf4 import HDF4FileHandler from satpy.readers.core.netcdf import NetCDF4FileHandler @@ -45,7 +46,7 @@ def __init__(self, filename, filename_info, filetype_info, apply_quality_flags=F self.apply_quality_flags = apply_quality_flags and self.l2_flags_var_name in self def _add_satpy_metadata(self, data): - data.attrs["instruments"] = self.sensor_names + data.attrs["instruments"] = instru.enum_to_str(self.sensor_names) data.attrs["platform_name"] = self._platform_name() data.attrs["rows_per_scan"] = self._rows_per_scan() return data @@ -84,9 +85,9 @@ def sensor_names(self): # Example: MODISA or VIIRSN or VIIRSJ1 sensor_name = self[self.sensor_attr_name] if sensor_name.startswith("MODIS"): - return {OSCAR.MODIS.value} + return {OSCAR.MODIS} if sensor_name.startswith("VIIRS"): - return {OSCAR.VIIRS.value} + return {OSCAR.VIIRS} # Example: OCI return {sensor_name} diff --git a/satpy/readers/seviri_l1b_hrit.py b/satpy/readers/seviri_l1b_hrit.py index 2b40c9ea62..9c292aca63 100644 --- a/satpy/readers/seviri_l1b_hrit.py +++ b/satpy/readers/seviri_l1b_hrit.py @@ -776,7 +776,7 @@ def _update_attrs(self, res, info): res.attrs["wavelength"] = info["wavelength"] res.attrs["standard_name"] = info["standard_name"] res.attrs["platform_name"] = self.platform_name - res.attrs["instruments"] = {OSCAR.SEVIRI.value} + res.attrs["instruments"] = {str(OSCAR.SEVIRI)} res.attrs["nominal_start_time"] = self.nominal_start_time res.attrs["nominal_end_time"] = self.nominal_end_time res.attrs["time_parameters"] = { diff --git a/satpy/readers/seviri_l1b_native.py b/satpy/readers/seviri_l1b_native.py index 8c4281ade3..4c278ddbed 100644 --- a/satpy/readers/seviri_l1b_native.py +++ b/satpy/readers/seviri_l1b_native.py @@ -708,7 +708,7 @@ def _update_attrs(self, dataset, dataset_info): dataset.attrs["wavelength"] = dataset_info["wavelength"] dataset.attrs["standard_name"] = dataset_info["standard_name"] dataset.attrs["platform_name"] = self.mda["platform_name"] - dataset.attrs["instruments"] = {OSCAR.SEVIRI.value} + dataset.attrs["instruments"] = {str(OSCAR.SEVIRI)} dataset.attrs["georef_offset_corrected"] = self.mda[ "offset_corrected"] dataset.attrs["time_parameters"] = { diff --git a/satpy/readers/seviri_l1b_nc.py b/satpy/readers/seviri_l1b_nc.py index 56a0fd6870..be57eec659 100644 --- a/satpy/readers/seviri_l1b_nc.py +++ b/satpy/readers/seviri_l1b_nc.py @@ -234,7 +234,7 @@ def _update_attrs(self, dataset, dataset_info): dataset.attrs.update(self.nc[dataset_info["nc_key"]].attrs) dataset.attrs.update(dataset_info) dataset.attrs["platform_name"] = "Meteosat-" + SATNUM[self.platform_id] - dataset.attrs["instruments"] = {OSCAR.SEVIRI.value} + dataset.attrs["instruments"] = {str(OSCAR.SEVIRI)} dataset.attrs["orbital_parameters"] = { "projection_longitude": self.mda["projection_parameters"]["ssp_longitude"], "projection_latitude": 0., diff --git a/satpy/readers/sgli_l1b.py b/satpy/readers/sgli_l1b.py index dd4cd0355d..4dfcc69be3 100644 --- a/satpy/readers/sgli_l1b.py +++ b/satpy/readers/sgli_l1b.py @@ -91,7 +91,7 @@ def get_dataset(self, key, info): dataset = self.prepare_dataset(key, dataset) dataset.attrs["platform_name"] = "GCOM-C1" - dataset.attrs["instruments"] = {OSCAR.SGLI.value} + dataset.attrs["instruments"] = {str(OSCAR.SGLI)} dataset.attrs["units"] = info["units"] dataset.attrs["standard_name"] = info["standard_name"] return dataset diff --git a/satpy/readers/viirs_compact.py b/satpy/readers/viirs_compact.py index d3c3da4ea9..7580f90440 100644 --- a/satpy/readers/viirs_compact.py +++ b/satpy/readers/viirs_compact.py @@ -137,7 +137,7 @@ def __init__(self, filename, filename_info, filetype_info): self.mda = {} short_name = np2str(self.h5f.attrs["Platform_Short_Name"]) self.mda["platform_name"] = short_names.get(short_name, short_name) - self.mda["instruments"] = {OSCAR.VIIRS.value} + self.mda["instruments"] = {str(OSCAR.VIIRS)} def __del__(self): """Close file handlers when we are done.""" diff --git a/satpy/readers/viirs_edr_active_fires.py b/satpy/readers/viirs_edr_active_fires.py index 3a4d6ac806..d99e4abbb0 100644 --- a/satpy/readers/viirs_edr_active_fires.py +++ b/satpy/readers/viirs_edr_active_fires.py @@ -126,7 +126,7 @@ def get_dataset(self, dsid, dsinfo): data = xr.DataArray( ds, dims=("y",), - attrs={"platform_name": self.platform_name, "instruments": {OSCAR.VIIRS.value}} + attrs={"platform_name": self.platform_name, "instruments": {str(OSCAR.VIIRS)}} ) for key in ("units", "standard_name", "flag_meanings", "flag_values", "_FillValue"): # we only want to add information that isn't present already From e5812715d5d3e1d4bfe24772f9e686af6aee5457 Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Tue, 19 May 2026 10:41:57 +0000 Subject: [PATCH 42/72] Fix more string conversions --- satpy/readers/avhrr_l1b_gaclac.py | 2 +- satpy/readers/clavrx.py | 2 +- satpy/readers/eps_l1b.py | 2 +- satpy/readers/eum_l2_bufr.py | 2 +- satpy/readers/geocat.py | 3 ++- satpy/readers/ghrsst_l2.py | 4 +--- satpy/readers/goes_imager_hrit.py | 2 +- satpy/readers/iasi_l2.py | 2 +- satpy/readers/iasi_l2_so2_bufr.py | 2 +- satpy/readers/ici_l1b_nc.py | 2 +- satpy/readers/insat3d_img_l1b_h5.py | 2 +- satpy/readers/mwr_l1b.py | 2 +- satpy/readers/mwr_l1c.py | 2 +- satpy/readers/nwcsaf_nc.py | 5 +++-- satpy/readers/olci_nc.py | 8 ++++---- satpy/readers/omps_edr.py | 2 +- satpy/readers/satpy_cf_nc.py | 7 +++---- satpy/readers/slstr_l1b.py | 2 +- satpy/readers/viirs_edr.py | 2 +- satpy/readers/viirs_l1b.py | 2 +- satpy/readers/viirs_l2.py | 2 +- satpy/tests/reader_tests/test_aapp_mhs_amsub_l1c.py | 5 ++--- 22 files changed, 31 insertions(+), 33 deletions(-) diff --git a/satpy/readers/avhrr_l1b_gaclac.py b/satpy/readers/avhrr_l1b_gaclac.py index d88c53c962..dc89cc89ab 100644 --- a/satpy/readers/avhrr_l1b_gaclac.py +++ b/satpy/readers/avhrr_l1b_gaclac.py @@ -319,7 +319,7 @@ def _update_attrs(self, res): res.attrs[attr] = self.reader.meta_data[attr] res.attrs["platform_name"] = self.reader.spacecraft_name res.attrs["orbit_number"] = self.filename_info.get("orbit_number", None) - res.attrs["instruments"] = {self.sensor} + res.attrs["instruments"] = {str(self.sensor)} try: res.attrs["orbital_parameters"] = {"tle": self.reader.get_tle_lines()} except (IndexError, RuntimeError): diff --git a/satpy/readers/clavrx.py b/satpy/readers/clavrx.py index 7af89328f4..aff7a4ec61 100644 --- a/satpy/readers/clavrx.py +++ b/satpy/readers/clavrx.py @@ -304,7 +304,7 @@ def get_metadata(sensor: str, platform: str, attrs: dict, ds_info: dict) -> dict attr_info["units"] = CF_UNITS[u] if u.lower() == "none": attr_info["units"] = "1" - attr_info["instruments"] = {sensor} + attr_info["instruments"] = {str(sensor)} attr_info["platform_name"] = platform rps = _get_rows_per_scan(sensor) if rps: diff --git a/satpy/readers/eps_l1b.py b/satpy/readers/eps_l1b.py index 080e2e4738..1b26021625 100644 --- a/satpy/readers/eps_l1b.py +++ b/satpy/readers/eps_l1b.py @@ -290,7 +290,7 @@ def get_dataset(self, key, info): return dataset.attrs["platform_name"] = self.platform_name - dataset.attrs["instruments"] = {self.sensor_name} + dataset.attrs["instruments"] = {str(self.sensor_name)} if "calibration" in key: dataset.attrs["units"] = self.units[key["calibration"]] dataset.attrs.update(info) diff --git a/satpy/readers/eum_l2_bufr.py b/satpy/readers/eum_l2_bufr.py index b1160a00f5..9c0bb5a916 100644 --- a/satpy/readers/eum_l2_bufr.py +++ b/satpy/readers/eum_l2_bufr.py @@ -314,7 +314,7 @@ def _construct_area_def(self, dataset_id): def _add_attributes(self, xarr, dataset_info): """Add dataset attributes to xarray.""" - xarr.attrs["instruments"] = {WMO_INSTRUMENT_NAMES.get(self.sensor_name, self.sensor_name)} + xarr.attrs["instruments"] = {str(WMO_INSTRUMENT_NAMES.get(self.sensor_name, self.sensor_name))} xarr.attrs["platform_name"] = self.platform_name xarr.attrs["ssp_lon"] = self.ssp_lon if ("resolution" not in dataset_info) or (dataset_info["resolution"] is None): diff --git a/satpy/readers/geocat.py b/satpy/readers/geocat.py index d0da9d90e8..d4f072a09b 100644 --- a/satpy/readers/geocat.py +++ b/satpy/readers/geocat.py @@ -37,6 +37,7 @@ from pyproj import Proj from pyresample import geometry +import satpy._instruments as instru from satpy._instruments import OSCAR from satpy.readers.core.netcdf import NetCDF4FileHandler @@ -293,7 +294,7 @@ def get_metadata(self, dataset_id, ds_info): # CF compliance info["units"] = CF_UNITS[u] - info["instruments"] = self.sensor_names + info["instruments"] = instru.enum_to_str(self.sensor_names) info["platform_name"] = self.get_platform(self["/attr/Platform_Name"]) info["resolution"] = dataset_id["resolution"] if var_name == "pixel_longitude": diff --git a/satpy/readers/ghrsst_l2.py b/satpy/readers/ghrsst_l2.py index e543b37310..47b82321b1 100644 --- a/satpy/readers/ghrsst_l2.py +++ b/satpy/readers/ghrsst_l2.py @@ -77,9 +77,7 @@ def _is_sst_file(name): def get_dataset(self, key, info): """Get any available dataset.""" stdname = info.get("standard_name") - data_array = self.nc[stdname].squeeze() - data_array.attrs["instruments"] = {self.sensor} - return data_array + return self.nc[stdname].squeeze() @property def start_time(self): diff --git a/satpy/readers/goes_imager_hrit.py b/satpy/readers/goes_imager_hrit.py index 7b07507cf4..cd7ecfd878 100644 --- a/satpy/readers/goes_imager_hrit.py +++ b/satpy/readers/goes_imager_hrit.py @@ -373,7 +373,7 @@ def get_dataset(self, key, info): new_attrs.update(res.attrs) res.attrs = new_attrs res.attrs["platform_name"] = self.platform_name - res.attrs["instruments"] = {self.instrument} + res.attrs["instruments"] = {str(self.instrument)} res.attrs["orbital_parameters"] = {"projection_longitude": self.mda["projection_parameters"]["SSP_longitude"], "projection_latitude": 0.0, "projection_altitude": ALTITUDE} diff --git a/satpy/readers/iasi_l2.py b/satpy/readers/iasi_l2.py index 85dfa5bd14..684e06ae7c 100644 --- a/satpy/readers/iasi_l2.py +++ b/satpy/readers/iasi_l2.py @@ -133,7 +133,7 @@ def get_dataset(self, key, info): else: m_data = read_geo(fid, key) m_data.attrs.update(info) - m_data.attrs["instruments"] = {self.sensor} + m_data.attrs["instruments"] = {str(self.sensor)} m_data.attrs["platform_name"] = self.platform_name return m_data diff --git a/satpy/readers/iasi_l2_so2_bufr.py b/satpy/readers/iasi_l2_so2_bufr.py index ba31d12bd5..7a71d61e05 100644 --- a/satpy/readers/iasi_l2_so2_bufr.py +++ b/satpy/readers/iasi_l2_so2_bufr.py @@ -232,7 +232,7 @@ def get_dataset(self, dataset_id, dataset_info): arr[arr == dataset_info["fill_value"]] = np.nan xarr = xr.DataArray(arr, dims=["y", "x"], name=dataset_info["name"]) - xarr.attrs["instruments"] = {OSCAR.IASI} + xarr.attrs["instruments"] = {str(OSCAR.IASI)} xarr.attrs["platform_name"] = self.platform_name xarr.attrs.update(dataset_info) diff --git a/satpy/readers/ici_l1b_nc.py b/satpy/readers/ici_l1b_nc.py index 3713351468..76fa938d4e 100644 --- a/satpy/readers/ici_l1b_nc.py +++ b/satpy/readers/ici_l1b_nc.py @@ -435,7 +435,7 @@ def _get_global_attributes(self): "end_time": self.end_time, "spacecraft_name": self.platform_name, "ssp_lon": self.ssp_lon, - "instruments": {str(self.sensor)}, + "instruments": {self.sensor}, "filename_start_time": self.filename_info["sensing_start_time"], "filename_end_time": self.filename_info["sensing_end_time"], "platform_name": self.platform_name, diff --git a/satpy/readers/insat3d_img_l1b_h5.py b/satpy/readers/insat3d_img_l1b_h5.py index 5f4f9d9179..caedee03e6 100644 --- a/satpy/readers/insat3d_img_l1b_h5.py +++ b/satpy/readers/insat3d_img_l1b_h5.py @@ -160,7 +160,7 @@ def get_dataset(self, ds_id, ds_info): satellite_nominal_altitude=float(ds.attrs["Nominal_Altitude(km)"]), satellite_actual_altitude=float(ds.attrs["Observed_Altitude(km)"])) darr.attrs["platform_name"] = "insat-3d" - darr.attrs["instruments"] = {OSCAR.IMAGER_INSAT} + darr.attrs["instruments"] = {str(OSCAR.IMAGER_INSAT)} darr = darr.squeeze() return darr diff --git a/satpy/readers/mwr_l1b.py b/satpy/readers/mwr_l1b.py index 3d1b508549..19e3a2995c 100644 --- a/satpy/readers/mwr_l1b.py +++ b/satpy/readers/mwr_l1b.py @@ -192,7 +192,7 @@ def get_dataset(self, dataset_id, dataset_info): "sub_satellite_longitude_end": self.sub_satellite_longitude_end} data_array.attrs["platform_name"] = self.platform_name - data_array.attrs["instruments"] = {self.sensor} + data_array.attrs["instruments"] = {str(self.sensor)} data_array.attrs["orbit_number"] = self.orbit_start return data_array diff --git a/satpy/readers/mwr_l1c.py b/satpy/readers/mwr_l1c.py index 11087844bd..6b771d92bd 100644 --- a/satpy/readers/mwr_l1c.py +++ b/satpy/readers/mwr_l1c.py @@ -92,7 +92,7 @@ def get_dataset(self, dataset_id, dataset_info): data_array.attrs.update(dataset_info) data_array.attrs["platform_name"] = self.platform_name - data_array.attrs["instruments"] = {self.sensor} + data_array.attrs["instruments"] = {str(self.sensor)} return data_array def _get_navigation_data(self, dataset_id, dataset_info): diff --git a/satpy/readers/nwcsaf_nc.py b/satpy/readers/nwcsaf_nc.py index c7913cc5da..6007268814 100644 --- a/satpy/readers/nwcsaf_nc.py +++ b/satpy/readers/nwcsaf_nc.py @@ -35,6 +35,7 @@ from pyproj import CRS from pyresample.geometry import AreaDefinition +import satpy._instruments as instru from satpy._instruments import OSCAR from satpy.readers.core.file_handlers import BaseFileHandler from satpy.readers.core.utils import unzip_file @@ -136,7 +137,7 @@ def set_platform_and_sensor(self, **kwargs): self.platform_name = kwargs["platform_name"] self.pps = True - self.sensor = set([SENSOR.get(self.platform_name, "SEVIRI")]) + self.sensor = {SENSOR.get(self.platform_name, "SEVIRI")} def remove_timedim(self, var): """Remove time dimension from dataset.""" @@ -229,7 +230,7 @@ def scale_dataset(self, variable, info): variable.attrs.pop("scale_factor", None) variable.attrs.update({"platform_name": self.platform_name, - "instruments": str(self.sensor)}) + "instruments": instru.enum_to_str(self.sensor)}) if not variable.attrs.get("standard_name", "").endswith("status_flag"): # TODO: do we really need to add units to everything ? diff --git a/satpy/readers/olci_nc.py b/satpy/readers/olci_nc.py index 1886eca645..ae7c974054 100644 --- a/satpy/readers/olci_nc.py +++ b/satpy/readers/olci_nc.py @@ -234,7 +234,7 @@ def get_dataset(self, key, info): dataset.attrs["units"] = "%" dataset.attrs["platform_name"] = self.platform_name - dataset.attrs["instruments"] = {self.sensor} + dataset.attrs["instruments"] = {str(self.sensor)} dataset.attrs.update(key.to_dict()) return dataset @@ -266,7 +266,7 @@ def get_dataset(self, key, info): dataset = self.getbitmask(dataset, self.mask_items) dataset.attrs["platform_name"] = self.platform_name - dataset.attrs["instruments"] = {self.sensor} + dataset.attrs["instruments"] = {str(self.sensor)} dataset.attrs.update(key.to_dict()) if self.unlog: dataset = self.delog(dataset) @@ -373,7 +373,7 @@ def get_dataset(self, key, info): values = self.nc[self.datasets[key["name"]]] values.attrs["platform_name"] = self.platform_name - values.attrs["instruments"] = {self.sensor} + values.attrs["instruments"] = {str(self.sensor)} values.attrs.update(key.to_dict()) return values @@ -443,7 +443,7 @@ def get_dataset(self, key, info): values = self.nc[key["name"]] values.attrs["platform_name"] = self.platform_name - values.attrs["instruments"] = {self.sensor} + values.attrs["instruments"] = {str(self.sensor)} values.attrs.update(key.to_dict()) return values diff --git a/satpy/readers/omps_edr.py b/satpy/readers/omps_edr.py index 03dc72af50..ce6e610d66 100644 --- a/satpy/readers/omps_edr.py +++ b/satpy/readers/omps_edr.py @@ -113,7 +113,7 @@ def get_metadata(self, dataset_id, ds_info): info.update( { "platform_name": self.platform_name, - "instruments": {str(self.sensor_name)}, + "instruments": {self.sensor_name}, "orbital_parameters": { "start_orbit": self.start_orbit_number, "end_orbit": self.end_orbit_number, diff --git a/satpy/readers/satpy_cf_nc.py b/satpy/readers/satpy_cf_nc.py index 028c63b803..a65f9b337e 100644 --- a/satpy/readers/satpy_cf_nc.py +++ b/satpy/readers/satpy_cf_nc.py @@ -182,6 +182,7 @@ import xarray as xr from pyresample import AreaDefinition +import satpy._instruments as instru import satpy.cf.decoding from satpy.dataset.dataid import WavelengthRange from satpy.readers.core.file_handlers import BaseFileHandler @@ -215,10 +216,8 @@ def sensor_names(self): """Get sensor set.""" sensors = set() for _, ds_info in self.available_datasets(): - try: - sensors.update(ds_info["instruments"]) - except KeyError: - continue + instruments = instru.get_instruments_from_attrs(ds_info) + sensors.update(instruments) return sensors def available_datasets(self, configured_datasets=None): diff --git a/satpy/readers/slstr_l1b.py b/satpy/readers/slstr_l1b.py index 63e13bd1fa..0e7af319c0 100644 --- a/satpy/readers/slstr_l1b.py +++ b/satpy/readers/slstr_l1b.py @@ -342,7 +342,7 @@ def get_dataset(self, key, info): dims=["y", "x"], attrs=variable.attrs) variable.attrs["platform_name"] = self.platform_name - variable.attrs["instruments"] = {self.sensor} + variable.attrs["instruments"] = {str(self.sensor)} if "units" not in variable.attrs: variable.attrs["units"] = "degrees" diff --git a/satpy/readers/viirs_edr.py b/satpy/readers/viirs_edr.py index a426ea2ea3..9aeab76e07 100644 --- a/satpy/readers/viirs_edr.py +++ b/satpy/readers/viirs_edr.py @@ -178,7 +178,7 @@ def _sanitize_metadata(self, data_arr: xr.DataArray, info: dict) -> xr.DataArray data_arr.attrs["standard_name"] = info["standard_name"] self._decode_flag_meanings(data_arr) data_arr.attrs["platform_name"] = self.platform_name - data_arr.attrs["instruments"] = {self.sensor_name} + data_arr.attrs["instruments"] = {str(self.sensor_name)} data_arr.attrs["rows_per_scan"] = self.rows_per_scans(data_arr) return data_arr diff --git a/satpy/readers/viirs_l1b.py b/satpy/readers/viirs_l1b.py index fe709e22cc..388e247c0d 100644 --- a/satpy/readers/viirs_l1b.py +++ b/satpy/readers/viirs_l1b.py @@ -191,7 +191,7 @@ def get_metadata(self, dataset_id, ds_info): "units": ds_info.get("units", file_units), "file_units": file_units, "platform_name": self.platform_name, - "instruments": {str(self.sensor_name)}, + "instruments": {self.sensor_name}, "day_night": self["/attr/DayNightFlag"], "orbital_parameters": orb_param, "start_orbit": self.start_orbit_number, diff --git a/satpy/readers/viirs_l2.py b/satpy/readers/viirs_l2.py index c201b2738c..bbfd880f13 100644 --- a/satpy/readers/viirs_l2.py +++ b/satpy/readers/viirs_l2.py @@ -106,7 +106,7 @@ def get_metadata(self, dataset_id, ds_info): { "file_units": file_units, "platform_name": self.platform_name, - "instruments": {str(self.sensor_name)}, + "instruments": {self.sensor_name}, "start_orbit": self.start_orbit_number, "end_orbit": self.end_orbit_number, } diff --git a/satpy/tests/reader_tests/test_aapp_mhs_amsub_l1c.py b/satpy/tests/reader_tests/test_aapp_mhs_amsub_l1c.py index 4203f8a329..a5c5499815 100644 --- a/satpy/tests/reader_tests/test_aapp_mhs_amsub_l1c.py +++ b/satpy/tests/reader_tests/test_aapp_mhs_amsub_l1c.py @@ -26,7 +26,6 @@ import numpy as np import pytest -from satpy._instruments import OSCAR from satpy.readers.aapp_mhs_amsub_l1c import _HEADERTYPE, _SCANTYPE, HEADER_LENGTH, MHS_AMSUB_AAPPL1CFile from satpy.tests.utils import make_dataid @@ -379,7 +378,7 @@ def test_sensor_name(self): fh_ = MHS_AMSUB_AAPPL1CFile(tmpfile, self.filename_info, self.filetype_info) - assert fh_.sensor == OSCAR.MHS + assert fh_.sensor == "MHS" self._header["instrument"][0] = 11 with tempfile.TemporaryFile() as tmpfile: @@ -389,7 +388,7 @@ def test_sensor_name(self): fh_ = MHS_AMSUB_AAPPL1CFile(tmpfile, self.filename_info, self.filetype_info) - assert fh_.sensor == OSCAR.AMSU_B + assert fh_.sensor == "AMSU-B" self._header["instrument"][0] = 10 From 85631d9824267599a20f5f366f87600d5e5bde6b Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Wed, 20 May 2026 13:47:29 +0000 Subject: [PATCH 43/72] Restore normalized instrument names in YAMLs --- satpy/_instruments.py | 6 + satpy/etc/readers/ahi_hrit.yaml | 34 +- satpy/etc/readers/fci_l1c_nc.yaml | 418 +++++++++--------- satpy/etc/readers/grib.yaml | 2 +- satpy/etc/readers/iasi_l2.yaml | 2 +- satpy/etc/readers/mtsat2-imager_hrit.yaml | 12 +- satpy/etc/readers/mviri_l1b_fiduceo_nc.yaml | 2 +- satpy/etc/readers/viirs_compact.yaml | 56 +-- satpy/readers/fci_l1c_nc.py | 5 + satpy/readers/hrit_jma.py | 8 +- satpy/tests/reader_tests/test_ahi_hrit.py | 8 +- satpy/tests/reader_tests/test_fci_l1c_nc.py | 8 +- satpy/tests/reader_tests/test_iasi_l2.py | 2 +- .../tests/reader_tests/test_viirs_compact.py | 1 + 14 files changed, 290 insertions(+), 274 deletions(-) diff --git a/satpy/_instruments.py b/satpy/_instruments.py index 793d09ab64..799de8f3d5 100644 --- a/satpy/_instruments.py +++ b/satpy/_instruments.py @@ -169,3 +169,9 @@ class OSCAR(StrEnum): def enum_to_str(instruments: set[StrEnum]) -> set[str]: """Convert OSCAR enums to string.""" return {str(i) for i in instruments} + + +NORMALIZED_TO_WMO: dict[str, str] = { + normalize_instrument_name(instrument): str(instrument) + for instrument in OSCAR +} diff --git a/satpy/etc/readers/ahi_hrit.yaml b/satpy/etc/readers/ahi_hrit.yaml index 239ebd0bc1..38d587df47 100644 --- a/satpy/etc/readers/ahi_hrit.yaml +++ b/satpy/etc/readers/ahi_hrit.yaml @@ -9,7 +9,7 @@ reader: description: Reader for the JMA Himawari AHI Level 1 data in HRIT format status: Nominal supports_fsspec: false - instruments: [AHI] + instruments: [ahi] reader: !!python/name:satpy.readers.core.yaml_reader.GEOSegmentYAMLReader # file pattern keys to sort files by with 'satpy.utils.group_files' group_keys: ['start_time', 'area'] @@ -228,7 +228,7 @@ file_types: datasets: B01: name: B01 - instruments: [AHI] + instruments: [ahi] wavelength: [0.45,0.47,0.49] resolution: 1000 calibration: @@ -242,7 +242,7 @@ datasets: B02: name: B02 - instruments: [AHI] + instruments: [ahi] wavelength: [0.49,0.51,0.53] resolution: 1000 calibration: @@ -256,7 +256,7 @@ datasets: B03: name: B03 - instruments: [AHI] + instruments: [ahi] wavelength: [0.62,0.64,0.66] resolution: 1000 calibration: @@ -270,7 +270,7 @@ datasets: B04: name: B04 - instruments: [AHI] + instruments: [ahi] wavelength: [0.85, 0.86, 0.87] resolution: 4000 calibration: @@ -284,7 +284,7 @@ datasets: B05: name: B05 - instruments: [AHI] + instruments: [ahi] wavelength: [1.5, 1.6, 1.7] resolution: 4000 calibration: @@ -298,7 +298,7 @@ datasets: B06: name: B06 - instruments: [AHI] + instruments: [ahi] wavelength: [2.2, 2.3, 2.4] resolution: 4000 calibration: @@ -315,7 +315,7 @@ datasets: resolution: 4000: {file_type: [hrit_b07_ir4_seg, hrit_b07_ir4_fd]} 2000: {file_type: [hrit_b07_seg, hrit_b07_fd]} - instruments: [AHI] + instruments: [ahi] wavelength: [3.7, 3.9, 4.1] calibration: brightness_temperature: @@ -327,7 +327,7 @@ datasets: B08: name: B08 - instruments: [AHI] + instruments: [ahi] wavelength: [6.0, 6.2, 6.4] resolution: 4000 calibration: @@ -341,7 +341,7 @@ datasets: B09: name: B09 - instruments: [AHI] + instruments: [ahi] wavelength: [6.7, 6.9, 7.1] resolution: 4000 calibration: @@ -355,7 +355,7 @@ datasets: B10: name: B10 - instruments: [AHI] + instruments: [ahi] wavelength: [7.1, 7.3, 7.5] resolution: 4000 calibration: @@ -369,7 +369,7 @@ datasets: B11: name: B11 - instruments: [AHI] + instruments: [ahi] wavelength: [8.4, 8.6, 8.8] resolution: 4000 calibration: @@ -383,7 +383,7 @@ datasets: B12: name: B12 - instruments: [AHI] + instruments: [ahi] wavelength: [9.4, 9.6, 9.8] resolution: 4000 calibration: @@ -397,7 +397,7 @@ datasets: B13: name: B13 - instruments: [AHI] + instruments: [ahi] wavelength: [10.2, 10.4, 10.6] resolution: 4000 calibration: @@ -411,7 +411,7 @@ datasets: B14: name: B14 - instruments: [AHI] + instruments: [ahi] wavelength: [11.0, 11.2, 11.4] resolution: 4000 calibration: @@ -425,7 +425,7 @@ datasets: B15: name: B15 - instruments: [AHI] + instruments: [ahi] wavelength: [12.2, 12.4, 12.6] resolution: 4000 calibration: @@ -439,7 +439,7 @@ datasets: B16: name: B16 - instruments: [AHI] + instruments: [ahi] wavelength: [13.1, 13.3, 13.5] resolution: 4000 calibration: diff --git a/satpy/etc/readers/fci_l1c_nc.yaml b/satpy/etc/readers/fci_l1c_nc.yaml index c3f447d599..9498a6abfb 100644 --- a/satpy/etc/readers/fci_l1c_nc.yaml +++ b/satpy/etc/readers/fci_l1c_nc.yaml @@ -9,7 +9,7 @@ reader: status: Beta for full-disc and RSS FDHSI, HRFI, African dissemination format, special scans, IDPF-I and IQT-I processing facilities. supports_fsspec: true reader: !!python/name:satpy.readers.core.yaml_reader.GEOVariableSegmentYAMLReader - instruments: [FCI] + instruments: [fci] # Source: MTG FCI L1 Product User Guide [FCIL1PUG] # https://www.eumetsat.int/media/45923 @@ -254,7 +254,7 @@ file_types: datasets: vis_04: name: vis_04 - instruments: [FCI] + instruments: [fci] wavelength: [0.384, 0.444, 0.504] resolution: 1000: { file_type: fci_l1c_fdhsi } @@ -272,7 +272,7 @@ datasets: vis_05: name: vis_05 - instruments: [FCI] + instruments: [fci] wavelength: [0.470, 0.510, 0.550] resolution: 1000: { file_type: fci_l1c_fdhsi } @@ -290,7 +290,7 @@ datasets: vis_06: name: vis_06 - instruments: [FCI] + instruments: [fci] wavelength: [0.590, 0.640, 0.690] resolution: 500: { file_type: fci_l1c_hrfi } @@ -309,7 +309,7 @@ datasets: vis_08: name: vis_08 - instruments: [FCI] + instruments: [fci] wavelength: [0.815, 0.865, 0.915] resolution: 1000: { file_type: fci_l1c_fdhsi } @@ -327,7 +327,7 @@ datasets: vis_09: name: vis_09 - instruments: [FCI] + instruments: [fci] wavelength: [0.894, 0.914, 0.934] resolution: 1000: { file_type: fci_l1c_fdhsi } @@ -345,7 +345,7 @@ datasets: nir_13: name: nir_13 - instruments: [FCI] + instruments: [fci] wavelength: [1.350, 1.380, 1.410] resolution: 1000: { file_type: fci_l1c_fdhsi } @@ -363,7 +363,7 @@ datasets: nir_16: name: nir_16 - instruments: [FCI] + instruments: [fci] wavelength: [1.560, 1.610, 1.660] resolution: 1000: { file_type: fci_l1c_fdhsi } @@ -381,7 +381,7 @@ datasets: nir_22: name: nir_22 - instruments: [FCI] + instruments: [fci] wavelength: [2.200, 2.250, 2.300] resolution: 500: { file_type: fci_l1c_hrfi } @@ -400,7 +400,7 @@ datasets: ir_38: name: ir_38 - instruments: [FCI] + instruments: [fci] wavelength: [3.400, 3.800, 4.200] resolution: 1000: { file_type: fci_l1c_hrfi } @@ -419,7 +419,7 @@ datasets: wv_63: name: wv_63 - instruments: [FCI] + instruments: [fci] wavelength: [5.300, 6.300, 7.300] resolution: 2000: { file_type: fci_l1c_fdhsi } @@ -437,7 +437,7 @@ datasets: wv_73: name: wv_73 - instruments: [FCI] + instruments: [fci] wavelength: [6.850, 7.350, 7.850] resolution: 2000: { file_type: fci_l1c_fdhsi } @@ -455,7 +455,7 @@ datasets: ir_87: name: ir_87 - instruments: [FCI] + instruments: [fci] wavelength: [8.300, 8.700, 9.100] resolution: 2000: { file_type: fci_l1c_fdhsi } @@ -473,7 +473,7 @@ datasets: ir_97: name: ir_97 - instruments: [FCI] + instruments: [fci] wavelength: [9.360, 9.660, 9.960] resolution: 2000: { file_type: fci_l1c_fdhsi } @@ -491,7 +491,7 @@ datasets: ir_105: name: ir_105 - instruments: [FCI] + instruments: [fci] wavelength: [9.800, 10.500, 11.200] resolution: 1000: { file_type: fci_l1c_hrfi } @@ -510,7 +510,7 @@ datasets: ir_123: name: ir_123 - instruments: [FCI] + instruments: [fci] wavelength: [11.800, 12.300, 12.800] resolution: 2000: { file_type: fci_l1c_fdhsi } @@ -528,7 +528,7 @@ datasets: ir_133: name: ir_133 - instruments: [FCI] + instruments: [fci] wavelength: [12.700, 13.300, 13.900] resolution: 2000: { file_type: fci_l1c_fdhsi } @@ -546,21 +546,21 @@ datasets: vis_04_pixel_quality: name: vis_04_pixel_quality - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_04 } vis_05_pixel_quality: name: vis_05_pixel_quality - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_05 } vis_06_pixel_quality: name: vis_06_pixel_quality - instruments: [FCI] + instruments: [fci] resolution: 500: { file_type: fci_l1c_hrfi } 1000: { file_type: [fci_l1c_fdhsi, fci_l1c_af_vis_06_1km] } @@ -568,35 +568,35 @@ datasets: vis_08_pixel_quality: name: vis_08_pixel_quality - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_08 } vis_09_pixel_quality: name: vis_09_pixel_quality - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_09 } nir_13_pixel_quality: name: nir_13_pixel_quality - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_nir_13 } nir_16_pixel_quality: name: nir_16_pixel_quality - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_nir_16 } nir_22_pixel_quality: name: nir_22_pixel_quality - instruments: [FCI] + instruments: [fci] resolution: 500: { file_type: fci_l1c_hrfi } 1000: { file_type: fci_l1c_fdhsi } @@ -604,7 +604,7 @@ datasets: ir_38_pixel_quality: name: ir_38_pixel_quality - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_hrfi } 2000: { file_type: fci_l1c_fdhsi } @@ -612,35 +612,35 @@ datasets: wv_63_pixel_quality: name: wv_63_pixel_quality - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_wv_63 } wv_73_pixel_quality: name: wv_73_pixel_quality - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_wv_73 } ir_87_pixel_quality: name: ir_87_pixel_quality - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_87 } ir_97_pixel_quality: name: ir_97_pixel_quality - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_97 } ir_105_pixel_quality: name: ir_105_pixel_quality - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_hrfi } 2000: { file_type: fci_l1c_fdhsi } @@ -648,35 +648,35 @@ datasets: ir_123_pixel_quality: name: ir_123_pixel_quality - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_123 } ir_133_pixel_quality: name: ir_133_pixel_quality - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_133 } vis_04_index_map: name: vis_04_index_map - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_04 } vis_05_index_map: name: vis_05_index_map - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_05 } vis_06_index_map: name: vis_06_index_map - instruments: [FCI] + instruments: [fci] resolution: 500: { file_type: fci_l1c_hrfi } 1000: { file_type: [fci_l1c_fdhsi, fci_l1c_af_vis_06_1km] } @@ -684,35 +684,35 @@ datasets: vis_08_index_map: name: vis_08_index_map - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_08 } vis_09_index_map: name: vis_09_index_map - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_09 } nir_13_index_map: name: nir_13_index_map - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_nir_13 } nir_16_index_map: name: nir_16_index_map - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_nir_16 } nir_22_index_map: name: nir_22_index_map - instruments: [FCI] + instruments: [fci] resolution: 500: { file_type: fci_l1c_hrfi } 1000: { file_type: fci_l1c_fdhsi } @@ -720,7 +720,7 @@ datasets: ir_38_index_map: name: ir_38_index_map - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_hrfi } 2000: { file_type: fci_l1c_fdhsi } @@ -728,35 +728,35 @@ datasets: wv_63_index_map: name: wv_63_index_map - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_wv_63 } wv_73_index_map: name: wv_73_index_map - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_wv_73 } ir_87_index_map: name: ir_87_index_map - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_87 } ir_97_index_map: name: ir_97_index_map - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_97 } ir_105_index_map: name: ir_105_index_map - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_hrfi } 2000: { file_type: fci_l1c_fdhsi } @@ -764,14 +764,14 @@ datasets: ir_123_index_map: name: ir_123_index_map - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_123 } ir_133_index_map: name: ir_133_index_map - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_133 } @@ -779,7 +779,7 @@ datasets: vis_04_time: name: vis_04_time units: s - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_04 } @@ -787,7 +787,7 @@ datasets: vis_05_time: name: vis_05_time units: s - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_05 } @@ -795,7 +795,7 @@ datasets: vis_06_time: name: vis_06_time units: s - instruments: [FCI] + instruments: [fci] resolution: 500: { file_type: fci_l1c_hrfi } 1000: { file_type: [fci_l1c_fdhsi, fci_l1c_af_vis_06_1km] } @@ -804,7 +804,7 @@ datasets: vis_08_time: name: vis_08_time units: s - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_08 } @@ -812,7 +812,7 @@ datasets: vis_09_time: name: vis_09_time units: s - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_09 } @@ -820,7 +820,7 @@ datasets: nir_13_time: name: nir_13_time units: s - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_nir_13 } @@ -828,7 +828,7 @@ datasets: nir_16_time: name: nir_16_time units: s - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_nir_16 } @@ -836,7 +836,7 @@ datasets: nir_22_time: name: nir_22_time units: s - instruments: [FCI] + instruments: [fci] resolution: 500: { file_type: fci_l1c_hrfi } 1000: { file_type: fci_l1c_fdhsi } @@ -845,7 +845,7 @@ datasets: ir_38_time: name: ir_38_time units: s - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_hrfi } 2000: { file_type: fci_l1c_fdhsi } @@ -854,7 +854,7 @@ datasets: wv_63_time: name: wv_63_time units: s - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_wv_63 } @@ -862,7 +862,7 @@ datasets: wv_73_time: name: wv_73_time units: s - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_wv_73 } @@ -870,7 +870,7 @@ datasets: ir_87_time: name: ir_87_time units: s - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_87 } @@ -878,7 +878,7 @@ datasets: ir_97_time: name: ir_97_time units: s - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_97 } @@ -886,7 +886,7 @@ datasets: ir_105_time: name: ir_105_time units: s - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_hrfi } 2000: { file_type: fci_l1c_fdhsi } @@ -895,7 +895,7 @@ datasets: ir_123_time: name: ir_123_time units: s - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_123 } @@ -903,28 +903,28 @@ datasets: ir_133_time: name: ir_133_time units: s - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_133 } vis_04_swath_direction: name: vis_04_swath_direction - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_04 } vis_05_swath_direction: name: vis_05_swath_direction - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_05 } vis_06_swath_direction: name: vis_06_swath_direction - instruments: [FCI] + instruments: [fci] resolution: 500: { file_type: fci_l1c_hrfi } 1000: { file_type: [fci_l1c_fdhsi, fci_l1c_af_vis_06_1km] } @@ -932,35 +932,35 @@ datasets: vis_08_swath_direction: name: vis_08_swath_direction - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_08 } vis_09_swath_direction: name: vis_09_swath_direction - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_09 } nir_13_swath_direction: name: nir_13_swath_direction - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_nir_13 } nir_16_swath_direction: name: nir_16_swath_direction - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_nir_16 } nir_22_swath_direction: name: nir_22_swath_direction - instruments: [FCI] + instruments: [fci] resolution: 500: { file_type: fci_l1c_hrfi } 1000: { file_type: fci_l1c_fdhsi } @@ -968,7 +968,7 @@ datasets: ir_38_swath_direction: name: ir_38_swath_direction - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_hrfi } 2000: { file_type: fci_l1c_fdhsi } @@ -976,35 +976,35 @@ datasets: wv_63_swath_direction: name: wv_63_swath_direction - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_wv_63 } wv_73_swath_direction: name: wv_73_swath_direction - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_wv_73 } ir_87_swath_direction: name: ir_87_swath_direction - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_87 } ir_97_swath_direction: name: ir_97_swath_direction - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_97 } ir_105_swath_direction: name: ir_105_swath_direction - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_hrfi } 2000: { file_type: fci_l1c_fdhsi } @@ -1012,35 +1012,35 @@ datasets: ir_123_swath_direction: name: ir_123_swath_direction - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_123 } ir_133_swath_direction: name: ir_133_swath_direction - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_133 } vis_04_swath_number: name: vis_04_swath_number - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_04 } vis_05_swath_number: name: vis_05_swath_number - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_05 } vis_06_swath_number: name: vis_06_swath_number - instruments: [FCI] + instruments: [fci] resolution: 500: { file_type: fci_l1c_hrfi } 1000: { file_type: [fci_l1c_fdhsi, fci_l1c_af_vis_06_1km] } @@ -1048,35 +1048,35 @@ datasets: vis_08_swath_number: name: vis_08_swath_number - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_08 } vis_09_swath_number: name: vis_09_swath_number - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_09 } nir_13_swath_number: name: nir_13_swath_number - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_nir_13 } nir_16_swath_number: name: nir_16_swath_number - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_nir_16 } nir_22_swath_number: name: nir_22_swath_number - instruments: [FCI] + instruments: [fci] resolution: 500: { file_type: fci_l1c_hrfi } 1000: { file_type: fci_l1c_fdhsi } @@ -1084,7 +1084,7 @@ datasets: ir_38_swath_number: name: ir_38_swath_number - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_hrfi } 2000: { file_type: fci_l1c_fdhsi } @@ -1092,35 +1092,35 @@ datasets: wv_63_swath_number: name: wv_63_swath_number - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_wv_63 } wv_73_swath_number: name: wv_73_swath_number - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_wv_73 } ir_87_swath_number: name: ir_87_swath_number - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_87 } ir_97_swath_number: name: ir_97_swath_number - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_97 } ir_105_swath_number: name: ir_105_swath_number - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_hrfi } 2000: { file_type: fci_l1c_fdhsi } @@ -1128,14 +1128,14 @@ datasets: ir_123_swath_number: name: ir_123_swath_number - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_123 } ir_133_swath_number: name: ir_133_swath_number - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_133 } @@ -1143,7 +1143,7 @@ datasets: vis_04_subsatellite_latitude: name: vis_04_subsatellite_latitude units: deg - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_04 } @@ -1151,7 +1151,7 @@ datasets: vis_05_subsatellite_latitude: name: vis_05_subsatellite_latitude units: deg - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_05 } @@ -1159,7 +1159,7 @@ datasets: vis_06_subsatellite_latitude: name: vis_06_subsatellite_latitude units: deg - instruments: [FCI] + instruments: [fci] resolution: 500: { file_type: fci_l1c_hrfi } 1000: { file_type: [fci_l1c_fdhsi, fci_l1c_af_vis_06_1km] } @@ -1168,7 +1168,7 @@ datasets: vis_08_subsatellite_latitude: name: vis_08_subsatellite_latitude units: deg - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_08 } @@ -1176,7 +1176,7 @@ datasets: vis_09_subsatellite_latitude: name: vis_09_subsatellite_latitude units: deg - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_09 } @@ -1184,7 +1184,7 @@ datasets: nir_13_subsatellite_latitude: name: nir_13_subsatellite_latitude units: deg - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_nir_13 } @@ -1192,7 +1192,7 @@ datasets: nir_16_subsatellite_latitude: name: nir_16_subsatellite_latitude units: deg - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_nir_16 } @@ -1200,7 +1200,7 @@ datasets: nir_22_subsatellite_latitude: name: nir_22_subsatellite_latitude units: deg - instruments: [FCI] + instruments: [fci] resolution: 500: { file_type: fci_l1c_hrfi } 1000: { file_type: fci_l1c_fdhsi } @@ -1209,7 +1209,7 @@ datasets: ir_38_subsatellite_latitude: name: ir_38_subsatellite_latitude units: deg - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_hrfi } 2000: { file_type: fci_l1c_fdhsi } @@ -1218,7 +1218,7 @@ datasets: wv_63_subsatellite_latitude: name: wv_63_subsatellite_latitude units: deg - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_wv_63 } @@ -1226,7 +1226,7 @@ datasets: wv_73_subsatellite_latitude: name: wv_73_subsatellite_latitude units: deg - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_wv_73 } @@ -1234,7 +1234,7 @@ datasets: ir_87_subsatellite_latitude: name: ir_87_subsatellite_latitude units: deg - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_87 } @@ -1242,7 +1242,7 @@ datasets: ir_97_subsatellite_latitude: name: ir_97_subsatellite_latitude units: deg - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_97 } @@ -1250,7 +1250,7 @@ datasets: ir_105_subsatellite_latitude: name: ir_105_subsatellite_latitude units: deg - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_hrfi } 2000: { file_type: fci_l1c_fdhsi } @@ -1259,7 +1259,7 @@ datasets: ir_123_subsatellite_latitude: name: ir_123_subsatellite_latitude units: deg - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_123 } @@ -1267,7 +1267,7 @@ datasets: ir_133_subsatellite_latitude: name: ir_133_subsatellite_latitude units: deg - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_133 } @@ -1275,7 +1275,7 @@ datasets: vis_04_subsatellite_longitude: name: vis_04_subsatellite_longitude units: deg - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_04 } @@ -1283,7 +1283,7 @@ datasets: vis_05_subsatellite_longitude: name: vis_05_subsatellite_longitude units: deg - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_05 } @@ -1291,7 +1291,7 @@ datasets: vis_06_subsatellite_longitude: name: vis_06_subsatellite_longitude units: deg - instruments: [FCI] + instruments: [fci] resolution: 500: { file_type: fci_l1c_hrfi } 1000: { file_type: [fci_l1c_fdhsi, fci_l1c_af_vis_06_1km] } @@ -1300,7 +1300,7 @@ datasets: vis_08_subsatellite_longitude: name: vis_08_subsatellite_longitude units: deg - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_08 } @@ -1308,7 +1308,7 @@ datasets: vis_09_subsatellite_longitude: name: vis_09_subsatellite_longitude units: deg - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_09 } @@ -1316,7 +1316,7 @@ datasets: nir_13_subsatellite_longitude: name: nir_13_subsatellite_longitude units: deg - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_nir_13 } @@ -1324,7 +1324,7 @@ datasets: nir_16_subsatellite_longitude: name: nir_16_subsatellite_longitude units: deg - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_nir_16 } @@ -1332,7 +1332,7 @@ datasets: nir_22_subsatellite_longitude: name: nir_22_subsatellite_longitude units: deg - instruments: [FCI] + instruments: [fci] resolution: 500: { file_type: fci_l1c_hrfi } 1000: { file_type: fci_l1c_fdhsi } @@ -1341,7 +1341,7 @@ datasets: ir_38_subsatellite_longitude: name: ir_38_subsatellite_longitude units: deg - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_hrfi } 2000: { file_type: fci_l1c_fdhsi } @@ -1350,7 +1350,7 @@ datasets: wv_63_subsatellite_longitude: name: wv_63_subsatellite_longitude units: deg - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_wv_63 } @@ -1358,7 +1358,7 @@ datasets: wv_73_subsatellite_longitude: name: wv_73_subsatellite_longitude units: deg - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_wv_73 } @@ -1366,7 +1366,7 @@ datasets: ir_87_subsatellite_longitude: name: ir_87_subsatellite_longitude units: deg - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_87 } @@ -1374,7 +1374,7 @@ datasets: ir_97_subsatellite_longitude: name: ir_97_subsatellite_longitude units: deg - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_97 } @@ -1382,7 +1382,7 @@ datasets: ir_105_subsatellite_longitude: name: ir_105_subsatellite_longitude units: deg - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_hrfi } 2000: { file_type: fci_l1c_fdhsi } @@ -1391,7 +1391,7 @@ datasets: ir_123_subsatellite_longitude: name: ir_123_subsatellite_longitude units: deg - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_123 } @@ -1399,7 +1399,7 @@ datasets: ir_133_subsatellite_longitude: name: ir_133_subsatellite_longitude units: deg - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_133 } @@ -1407,7 +1407,7 @@ datasets: vis_04_subsolar_latitude: name: vis_04_subsolar_latitude units: deg - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_04 } @@ -1415,7 +1415,7 @@ datasets: vis_05_subsolar_latitude: name: vis_05_subsolar_latitude units: deg - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_05 } @@ -1423,7 +1423,7 @@ datasets: vis_06_subsolar_latitude: name: vis_06_subsolar_latitude units: deg - instruments: [FCI] + instruments: [fci] resolution: 500: { file_type: fci_l1c_hrfi } 1000: { file_type: [fci_l1c_fdhsi, fci_l1c_af_vis_06_1km] } @@ -1432,7 +1432,7 @@ datasets: vis_08_subsolar_latitude: name: vis_08_subsolar_latitude units: deg - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_08 } @@ -1440,7 +1440,7 @@ datasets: vis_09_subsolar_latitude: name: vis_09_subsolar_latitude units: deg - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_09 } @@ -1448,7 +1448,7 @@ datasets: nir_13_subsolar_latitude: name: nir_13_subsolar_latitude units: deg - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_nir_13 } @@ -1456,7 +1456,7 @@ datasets: nir_16_subsolar_latitude: name: nir_16_subsolar_latitude units: deg - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_nir_16 } @@ -1464,7 +1464,7 @@ datasets: nir_22_subsolar_latitude: name: nir_22_subsolar_latitude units: deg - instruments: [FCI] + instruments: [fci] resolution: 500: { file_type: fci_l1c_hrfi } 1000: { file_type: fci_l1c_fdhsi } @@ -1473,7 +1473,7 @@ datasets: ir_38_subsolar_latitude: name: ir_38_subsolar_latitude units: deg - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_hrfi } 2000: { file_type: fci_l1c_fdhsi } @@ -1482,7 +1482,7 @@ datasets: wv_63_subsolar_latitude: name: wv_63_subsolar_latitude units: deg - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_wv_63 } @@ -1490,7 +1490,7 @@ datasets: wv_73_subsolar_latitude: name: wv_73_subsolar_latitude units: deg - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_wv_73 } @@ -1498,7 +1498,7 @@ datasets: ir_87_subsolar_latitude: name: ir_87_subsolar_latitude units: deg - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_87 } @@ -1506,7 +1506,7 @@ datasets: ir_97_subsolar_latitude: name: ir_97_subsolar_latitude units: deg - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_97 } @@ -1514,7 +1514,7 @@ datasets: ir_105_subsolar_latitude: name: ir_105_subsolar_latitude units: deg - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_hrfi } 2000: { file_type: fci_l1c_fdhsi } @@ -1523,7 +1523,7 @@ datasets: ir_123_subsolar_latitude: name: ir_123_subsolar_latitude units: deg - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_123 } @@ -1531,7 +1531,7 @@ datasets: ir_133_subsolar_latitude: name: ir_133_subsolar_latitude units: deg - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_133 } @@ -1539,7 +1539,7 @@ datasets: vis_04_subsolar_longitude: name: vis_04_subsolar_longitude units: deg - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_04 } @@ -1547,7 +1547,7 @@ datasets: vis_05_subsolar_longitude: name: vis_05_subsolar_longitude units: deg - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_05 } @@ -1555,7 +1555,7 @@ datasets: vis_06_subsolar_longitude: name: vis_06_subsolar_longitude units: deg - instruments: [FCI] + instruments: [fci] resolution: 500: { file_type: fci_l1c_hrfi } 1000: { file_type: [fci_l1c_fdhsi, fci_l1c_af_vis_06_1km] } @@ -1564,7 +1564,7 @@ datasets: vis_08_subsolar_longitude: name: vis_08_subsolar_longitude units: deg - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_08 } @@ -1572,7 +1572,7 @@ datasets: vis_09_subsolar_longitude: name: vis_09_subsolar_longitude units: deg - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_09 } @@ -1580,7 +1580,7 @@ datasets: nir_13_subsolar_longitude: name: nir_13_subsolar_longitude units: deg - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_nir_13 } @@ -1588,7 +1588,7 @@ datasets: nir_16_subsolar_longitude: name: nir_16_subsolar_longitude units: deg - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_nir_16 } @@ -1596,7 +1596,7 @@ datasets: nir_22_subsolar_longitude: name: nir_22_subsolar_longitude units: deg - instruments: [FCI] + instruments: [fci] resolution: 500: { file_type: fci_l1c_hrfi } 1000: { file_type: fci_l1c_fdhsi } @@ -1605,7 +1605,7 @@ datasets: ir_38_subsolar_longitude: name: ir_38_subsolar_longitude units: deg - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_hrfi } 2000: { file_type: fci_l1c_fdhsi } @@ -1614,7 +1614,7 @@ datasets: wv_63_subsolar_longitude: name: wv_63_subsolar_longitude units: deg - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_wv_63 } @@ -1622,7 +1622,7 @@ datasets: wv_73_subsolar_longitude: name: wv_73_subsolar_longitude units: deg - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_wv_73 } @@ -1630,7 +1630,7 @@ datasets: ir_87_subsolar_longitude: name: ir_87_subsolar_longitude units: deg - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_87 } @@ -1638,7 +1638,7 @@ datasets: ir_97_subsolar_longitude: name: ir_97_subsolar_longitude units: deg - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_97 } @@ -1646,7 +1646,7 @@ datasets: ir_105_subsolar_longitude: name: ir_105_subsolar_longitude units: deg - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_hrfi } 2000: { file_type: fci_l1c_fdhsi } @@ -1655,7 +1655,7 @@ datasets: ir_123_subsolar_longitude: name: ir_123_subsolar_longitude units: deg - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_123 } @@ -1663,7 +1663,7 @@ datasets: ir_133_subsolar_longitude: name: ir_133_subsolar_longitude units: deg - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_133 } @@ -1671,7 +1671,7 @@ datasets: vis_04_platform_altitude: name: vis_04_platform_altitude units: m - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_04 } @@ -1679,7 +1679,7 @@ datasets: vis_05_platform_altitude: name: vis_05_platform_altitude units: m - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_05 } @@ -1687,7 +1687,7 @@ datasets: vis_06_platform_altitude: name: vis_06_platform_altitude units: m - instruments: [FCI] + instruments: [fci] resolution: 500: { file_type: fci_l1c_hrfi } 1000: { file_type: [fci_l1c_fdhsi, fci_l1c_af_vis_06_1km] } @@ -1696,7 +1696,7 @@ datasets: vis_08_platform_altitude: name: vis_08_platform_altitude units: m - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_08 } @@ -1704,7 +1704,7 @@ datasets: vis_09_platform_altitude: name: vis_09_platform_altitude units: m - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_09 } @@ -1712,7 +1712,7 @@ datasets: nir_13_platform_altitude: name: nir_13_platform_altitude units: m - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_nir_13 } @@ -1720,7 +1720,7 @@ datasets: nir_16_platform_altitude: name: nir_16_platform_altitude units: m - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_nir_16 } @@ -1728,7 +1728,7 @@ datasets: nir_22_platform_altitude: name: nir_22_platform_altitude units: m - instruments: [FCI] + instruments: [fci] resolution: 500: { file_type: fci_l1c_hrfi } 1000: { file_type: fci_l1c_fdhsi } @@ -1737,7 +1737,7 @@ datasets: ir_38_platform_altitude: name: ir_38_platform_altitude units: m - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_hrfi } 2000: { file_type: fci_l1c_fdhsi } @@ -1746,7 +1746,7 @@ datasets: wv_63_platform_altitude: name: wv_63_platform_altitude units: m - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_wv_63 } @@ -1754,7 +1754,7 @@ datasets: wv_73_platform_altitude: name: wv_73_platform_altitude units: m - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_wv_73 } @@ -1762,7 +1762,7 @@ datasets: ir_87_platform_altitude: name: ir_87_platform_altitude units: m - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_87 } @@ -1770,7 +1770,7 @@ datasets: ir_97_platform_altitude: name: ir_97_platform_altitude units: m - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_97 } @@ -1778,7 +1778,7 @@ datasets: ir_105_platform_altitude: name: ir_105_platform_altitude units: m - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_hrfi } 2000: { file_type: fci_l1c_fdhsi } @@ -1787,7 +1787,7 @@ datasets: ir_123_platform_altitude: name: ir_123_platform_altitude units: m - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_123 } @@ -1795,7 +1795,7 @@ datasets: ir_133_platform_altitude: name: ir_133_platform_altitude units: m - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_133 } @@ -1803,7 +1803,7 @@ datasets: vis_04_earth_sun_distance: name: vis_04_earth_sun_distance units: km - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: [fci_l1c_af_vis_04, fci_l1c_fdhsi] } 3000: { file_type: fci_l1c_af_vis_04 } @@ -1811,7 +1811,7 @@ datasets: vis_05_earth_sun_distance: name: vis_05_earth_sun_distance units: km - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_05 } @@ -1819,7 +1819,7 @@ datasets: vis_06_earth_sun_distance: name: vis_06_earth_sun_distance units: km - instruments: [FCI] + instruments: [fci] resolution: 500: { file_type: fci_l1c_hrfi } 1000: { file_type: [fci_l1c_fdhsi, fci_l1c_af_vis_06_1km] } @@ -1828,7 +1828,7 @@ datasets: vis_08_earth_sun_distance: name: vis_08_earth_sun_distance units: km - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_08 } @@ -1836,7 +1836,7 @@ datasets: vis_09_earth_sun_distance: name: vis_09_earth_sun_distance units: km - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_09 } @@ -1844,7 +1844,7 @@ datasets: nir_13_earth_sun_distance: name: nir_13_earth_sun_distance units: km - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_nir_13 } @@ -1852,7 +1852,7 @@ datasets: nir_16_earth_sun_distance: name: nir_16_earth_sun_distance units: km - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_nir_16 } @@ -1860,7 +1860,7 @@ datasets: nir_22_earth_sun_distance: name: nir_22_earth_sun_distance units: km - instruments: [FCI] + instruments: [fci] resolution: 500: { file_type: fci_l1c_hrfi } 1000: { file_type: fci_l1c_fdhsi } @@ -1869,7 +1869,7 @@ datasets: ir_38_earth_sun_distance: name: ir_38_earth_sun_distance units: km - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_hrfi } 2000: { file_type: fci_l1c_fdhsi } @@ -1878,7 +1878,7 @@ datasets: wv_63_earth_sun_distance: name: wv_63_earth_sun_distance units: km - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_wv_63 } @@ -1886,7 +1886,7 @@ datasets: wv_73_earth_sun_distance: name: wv_73_earth_sun_distance units: km - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_wv_73 } @@ -1894,7 +1894,7 @@ datasets: ir_87_earth_sun_distance: name: ir_87_earth_sun_distance units: km - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_87 } @@ -1902,7 +1902,7 @@ datasets: ir_97_earth_sun_distance: name: ir_97_earth_sun_distance units: km - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_97 } @@ -1910,7 +1910,7 @@ datasets: ir_105_earth_sun_distance: name: ir_105_earth_sun_distance units: km - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_hrfi } 2000: { file_type: fci_l1c_fdhsi } @@ -1919,7 +1919,7 @@ datasets: ir_123_earth_sun_distance: name: ir_123_earth_sun_distance units: km - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_123 } @@ -1927,7 +1927,7 @@ datasets: ir_133_earth_sun_distance: name: ir_133_earth_sun_distance units: km - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_133 } @@ -1935,7 +1935,7 @@ datasets: vis_04_sun_satellite_distance: name: vis_04_sun_satellite_distance units: km - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_04 } @@ -1943,7 +1943,7 @@ datasets: vis_05_sun_satellite_distance: name: vis_05_sun_satellite_distance units: km - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_05 } @@ -1951,7 +1951,7 @@ datasets: vis_06_sun_satellite_distance: name: vis_06_sun_satellite_distance units: km - instruments: [FCI] + instruments: [fci] resolution: 500: { file_type: fci_l1c_hrfi } 1000: { file_type: [fci_l1c_fdhsi, fci_l1c_af_vis_06_1km] } @@ -1960,7 +1960,7 @@ datasets: vis_08_sun_satellite_distance: name: vis_08_sun_satellite_distance units: km - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_08 } @@ -1968,7 +1968,7 @@ datasets: vis_09_sun_satellite_distance: name: vis_09_sun_satellite_distance units: km - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_vis_09 } @@ -1976,7 +1976,7 @@ datasets: nir_13_sun_satellite_distance: name: nir_13_sun_satellite_distance units: km - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_nir_13 } @@ -1984,7 +1984,7 @@ datasets: nir_16_sun_satellite_distance: name: nir_16_sun_satellite_distance units: km - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_nir_16 } @@ -1992,7 +1992,7 @@ datasets: nir_22_sun_satellite_distance: name: nir_22_sun_satellite_distance units: km - instruments: [FCI] + instruments: [fci] resolution: 500: { file_type: fci_l1c_hrfi } 1000: { file_type: fci_l1c_fdhsi } @@ -2001,7 +2001,7 @@ datasets: ir_38_sun_satellite_distance: name: ir_38_sun_satellite_distance units: km - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_hrfi } 2000: { file_type: fci_l1c_fdhsi } @@ -2010,7 +2010,7 @@ datasets: wv_63_sun_satellite_distance: name: wv_63_sun_satellite_distance units: km - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_wv_63 } @@ -2018,7 +2018,7 @@ datasets: wv_73_sun_satellite_distance: name: wv_73_sun_satellite_distance units: km - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_wv_73 } @@ -2026,7 +2026,7 @@ datasets: ir_87_sun_satellite_distance: name: ir_87_sun_satellite_distance units: km - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_87 } @@ -2034,7 +2034,7 @@ datasets: ir_97_sun_satellite_distance: name: ir_97_sun_satellite_distance units: km - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_97 } @@ -2042,7 +2042,7 @@ datasets: ir_105_sun_satellite_distance: name: ir_105_sun_satellite_distance units: km - instruments: [FCI] + instruments: [fci] resolution: 1000: { file_type: fci_l1c_hrfi } 2000: { file_type: fci_l1c_fdhsi } @@ -2051,7 +2051,7 @@ datasets: ir_123_sun_satellite_distance: name: ir_123_sun_satellite_distance units: km - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_123 } @@ -2059,7 +2059,7 @@ datasets: ir_133_sun_satellite_distance: name: ir_133_sun_satellite_distance units: km - instruments: [FCI] + instruments: [fci] resolution: 2000: { file_type: fci_l1c_fdhsi } 3000: { file_type: fci_l1c_af_ir_133 } diff --git a/satpy/etc/readers/grib.yaml b/satpy/etc/readers/grib.yaml index 29c78d86da..1009ed291f 100644 --- a/satpy/etc/readers/grib.yaml +++ b/satpy/etc/readers/grib.yaml @@ -6,7 +6,7 @@ reader: status: Beta supports_fsspec: false reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader - instruments: [unknown] + sensors: [unknown] data_identification_keys: name: required: true diff --git a/satpy/etc/readers/iasi_l2.yaml b/satpy/etc/readers/iasi_l2.yaml index d6bc5b7aff..927e880ed0 100644 --- a/satpy/etc/readers/iasi_l2.yaml +++ b/satpy/etc/readers/iasi_l2.yaml @@ -6,7 +6,7 @@ reader: status: Alpha supports_fsspec: false reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader - instruments: [IASI] + sensors: [iasi] default_datasets: datasets: diff --git a/satpy/etc/readers/mtsat2-imager_hrit.yaml b/satpy/etc/readers/mtsat2-imager_hrit.yaml index 4d90df916b..11b3d74a3d 100644 --- a/satpy/etc/readers/mtsat2-imager_hrit.yaml +++ b/satpy/etc/readers/mtsat2-imager_hrit.yaml @@ -13,7 +13,7 @@ reader: status: Beta supports_fsspec: false - instruments: [IMAGER (MTSAT-2)] + instruments: [imager_mtsat-2] reader: !!python/name:satpy.readers.core.yaml_reader.GEOSegmentYAMLReader file_types: @@ -86,7 +86,7 @@ file_types: datasets: VIS: name: VIS - instruments: [IMAGER (MTSAT-2)] + instruments: [imager_mtsat-2] wavelength: [0.55, 0.675, 0.80] resolution: 1000 calibration: @@ -100,7 +100,7 @@ datasets: IR1: name: IR1 - instruments: [IMAGER (MTSAT-2)] + instruments: [imager_mtsat-2] wavelength: [10.3, 10.8, 11.3] resolution: 4000 calibration: @@ -114,7 +114,7 @@ datasets: IR2: name: IR2 - instruments: [IMAGER (MTSAT-2)] + instruments: [imager_mtsat-2] wavelength: [11.5, 12.0, 12.5] resolution: 4000 calibration: @@ -128,7 +128,7 @@ datasets: IR3: name: IR3 - instruments: [IMAGER (MTSAT-2)] + instruments: [imager_mtsat-2] wavelength: [6.5, 6.75, 7.0] resolution: 4000 calibration: @@ -142,7 +142,7 @@ datasets: IR4: name: IR4 - instruments: [IMAGER (MTSAT-2)] + instruments: [imager_mtsat-2] wavelength: [3.5, 3.75, 4.0] resolution: 4000 calibration: diff --git a/satpy/etc/readers/mviri_l1b_fiduceo_nc.yaml b/satpy/etc/readers/mviri_l1b_fiduceo_nc.yaml index 9f371684b9..4a895da10f 100644 --- a/satpy/etc/readers/mviri_l1b_fiduceo_nc.yaml +++ b/satpy/etc/readers/mviri_l1b_fiduceo_nc.yaml @@ -12,7 +12,7 @@ reader: For documentation see: http://doi.org/10.15770/EUM_SEC_CLM_0009 . status: Beta supports_fsspec: false - instruments: [MVIRI] + instruments: [mviri] reader: !!python/name:satpy.readers.core.yaml_reader.GEOFlippableFileYAMLReader file_types: diff --git a/satpy/etc/readers/viirs_compact.yaml b/satpy/etc/readers/viirs_compact.yaml index 23a4d903e8..882c66ac3f 100644 --- a/satpy/etc/readers/viirs_compact.yaml +++ b/satpy/etc/readers/viirs_compact.yaml @@ -6,7 +6,7 @@ reader: status: Nominal supports_fsspec: false reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader - instruments: [VIIRS] + instruments: [viirs] default_datasets: datasets: @@ -40,7 +40,7 @@ datasets: M01: name: M01 - instruments: [VIIRS] + instruments: [viirs] wavelength: [0.402,0.412,0.422] resolution: 742 calibration: @@ -55,7 +55,7 @@ datasets: M02: name: M02 - instruments: [VIIRS] + instruments: [viirs] wavelength: [0.436,0.445,0.454] resolution: 742 calibration: @@ -70,7 +70,7 @@ datasets: M03: name: M03 - instruments: [VIIRS] + instruments: [viirs] wavelength: [0.478,0.488,0.498] resolution: 742 calibration: @@ -85,7 +85,7 @@ datasets: M04: name: M04 - instruments: [VIIRS] + instruments: [viirs] wavelength: [0.545,0.555,0.565] resolution: 742 calibration: @@ -100,7 +100,7 @@ datasets: M05: name: M05 - instruments: [VIIRS] + instruments: [viirs] wavelength: [0.662,0.672,0.682] resolution: 742 calibration: @@ -115,7 +115,7 @@ datasets: M06: name: M06 - instruments: [VIIRS] + instruments: [viirs] wavelength: [0.739,0.746,0.754] resolution: 742 calibration: @@ -130,7 +130,7 @@ datasets: M07: name: M07 - instruments: [VIIRS] + instruments: [viirs] wavelength: [0.846,0.865,0.885] resolution: 742 calibration: @@ -145,7 +145,7 @@ datasets: M08: name: M08 - instruments: [VIIRS] + instruments: [viirs] wavelength: [1.230,1.240,1.250] resolution: 742 calibration: @@ -160,7 +160,7 @@ datasets: M09: name: M09 - instruments: [VIIRS] + instruments: [viirs] resolution: 742 wavelength: [1.371,1.378,1.386] calibration: @@ -175,7 +175,7 @@ datasets: M10: name: M10 - instruments: [VIIRS] + instruments: [viirs] wavelength: [1.580,1.610,1.640] resolution: 742 calibration: @@ -190,7 +190,7 @@ datasets: M11: name: M11 - instruments: [VIIRS] + instruments: [viirs] resolution: 742 wavelength: [2.225,2.250,2.275] calibration: @@ -205,7 +205,7 @@ datasets: M12: name: M12 - instruments: [VIIRS] + instruments: [viirs] wavelength: [3.610,3.700,3.790] resolution: 742 calibration: @@ -220,7 +220,7 @@ datasets: M13: name: M13 - instruments: [VIIRS] + instruments: [viirs] wavelength: [3.973,4.050,4.128] resolution: 742 calibration: @@ -235,7 +235,7 @@ datasets: M14: name: M14 - instruments: [VIIRS] + instruments: [viirs] resolution: 742 wavelength: [8.400,8.550,8.700] calibration: @@ -250,7 +250,7 @@ datasets: M15: name: M15 - instruments: [VIIRS] + instruments: [viirs] resolution: 742 wavelength: [10.263,10.763,11.263] calibration: @@ -265,7 +265,7 @@ datasets: M16: name: M16 - instruments: [VIIRS] + instruments: [viirs] wavelength: [11.538,12.013,12.489] resolution: 742 calibration: @@ -280,7 +280,7 @@ datasets: DNB: name: DNB - instruments: [VIIRS] + instruments: [viirs] wavelength: [0.500,0.700,0.900] resolution: 743 calibration: @@ -292,7 +292,7 @@ datasets: satellite_azimuth_angle: name: satellite_azimuth_angle - instruments: [VIIRS] + instruments: [viirs] resolution: 742 file_type: compact_m units: degree @@ -301,7 +301,7 @@ datasets: solar_azimuth_angle: name: solar_azimuth_angle - instruments: [VIIRS] + instruments: [viirs] resolution: 742 file_type: compact_m units: degree @@ -310,7 +310,7 @@ datasets: satellite_zenith_angle: name: satellite_zenith_angle - instruments: [VIIRS] + instruments: [viirs] resolution: 742 file_type: compact_m units: degree @@ -319,7 +319,7 @@ datasets: solar_zenith_angle: name: solar_zenith_angle - instruments: [VIIRS] + instruments: [viirs] resolution: 742 file_type: compact_m units: degree @@ -328,7 +328,7 @@ datasets: satellite_azimuth_angle_dnb: name: dnb_satellite_azimuth_angle - instruments: [VIIRS] + instruments: [viirs] resolution: 743 file_type: compact_dnb units: degree @@ -337,7 +337,7 @@ datasets: solar_azimuth_angle_dnb: name: dnb_solar_azimuth_angle - instruments: [VIIRS] + instruments: [viirs] resolution: 743 file_type: compact_dnb units: degree @@ -346,7 +346,7 @@ datasets: satellite_zenith_angle_dnb: name: dnb_satellite_zenith_angle - instruments: [VIIRS] + instruments: [viirs] resolution: 743 file_type: compact_dnb units: degree @@ -355,7 +355,7 @@ datasets: solar_zenith_angle_dnb: name: dnb_solar_zenith_angle - instruments: [VIIRS] + instruments: [viirs] resolution: 743 file_type: compact_dnb units: degree @@ -364,7 +364,7 @@ datasets: lunar_zenith_angle_dnb: name: dnb_lunar_zenith_angle - instruments: [VIIRS] + instruments: [viirs] resolution: 743 file_type: compact_dnb units: degree @@ -373,7 +373,7 @@ datasets: lunar_azimuth_angle_dnb: name: dnb_lunar_azimuth_angle - instruments: [VIIRS] + instruments: [viirs] resolution: 743 file_type: compact_dnb units: degree diff --git a/satpy/readers/fci_l1c_nc.py b/satpy/readers/fci_l1c_nc.py index d8a1d6c743..cc0bdf371c 100644 --- a/satpy/readers/fci_l1c_nc.py +++ b/satpy/readers/fci_l1c_nc.py @@ -190,6 +190,7 @@ from pyresample import geometry import satpy +import satpy._instruments as instru from satpy.readers.core._geos_area import get_geos_area_naming from satpy.readers.core.eum import get_service_mode from satpy.readers.core.fci import platform_name_translate @@ -484,6 +485,10 @@ def _set_and_cleanup_attributes(self, resattrs, attrs=None, key=None, info=None) """ if info is not None: resattrs.update(info) + resattrs["instruments"] = { + instru.NORMALIZED_TO_WMO[instrument] + for instrument in resattrs["instruments"] + } if None not in (attrs, key): resattrs = self._set_calibrated_data_attributes(resattrs, attrs, key) diff --git a/satpy/readers/hrit_jma.py b/satpy/readers/hrit_jma.py index 971946bc90..846ce81dc5 100644 --- a/satpy/readers/hrit_jma.py +++ b/satpy/readers/hrit_jma.py @@ -385,9 +385,13 @@ def get_dataset(self, key, info): res.coords["acq_time"] = ("y", self.acq_time) res.coords["acq_time"].attrs["long_name"] = "Scanline acquisition time" - # Update attributes + # Update attributes. YAML info contains normalized instrument name, + # convert to WMO name. res.attrs.update(info) - res.attrs["instruments"] = set(res.attrs["instruments"]) + res.attrs["instruments"] = { + instru.NORMALIZED_TO_WMO[instrument] + for instrument in res.attrs["instruments"] + } res.attrs["platform_name"] = self.platform res.attrs["orbital_parameters"] = { "projection_longitude": float(self.mda["projection_parameters"]["SSP_longitude"]), diff --git a/satpy/tests/reader_tests/test_ahi_hrit.py b/satpy/tests/reader_tests/test_ahi_hrit.py index 929245a237..d9fc6a014f 100644 --- a/satpy/tests/reader_tests/test_ahi_hrit.py +++ b/satpy/tests/reader_tests/test_ahi_hrit.py @@ -396,7 +396,7 @@ def test_calibrate(tmp_path, calibration): key, { "units": units, - "instruments": ["AHI"], + "instruments": ["ahi"], }, ) @@ -430,7 +430,7 @@ def test_mask_space(tmp_path): reader = HRITJMAFileHandler(hrit_path, {"start_time": dt.datetime.now()}, {}) key = make_dataid(name="VIS", calibration="counts") - res = reader.get_dataset(key, {"units": "1", "instruments": ["AHI"]}) + res = reader.get_dataset(key, {"units": "1", "instruments": ["ahi"]}) res_np = res.data.compute() assert res_np.dtype == res.dtype assert res_np.dtype == np.float32 @@ -462,7 +462,7 @@ def test_get_dataset(tmp_path): key = make_dataid(name="VIS", calibration="reflectance") with mock.patch.object(reader, "_mask_space", wraps=reader._mask_space) as mask_space, \ mock.patch.object(reader, "calibrate", wraps=reader.calibrate) as calibrate: - res = reader.get_dataset(key, {"units": "%", "instruments": ["AHI"]}) + res = reader.get_dataset(key, {"units": "%", "instruments": ["ahi"]}) mask_space.assert_called() calibrate.assert_called() @@ -485,7 +485,7 @@ def test_sensor_mismatch(tmp_path, caplog): reader = HRITJMAFileHandler(hrit_path, {"start_time": dt.datetime.now()}, {}) key = make_dataid(name="VIS", calibration="reflectance") - reader.get_dataset(key, {"units": "%", "instruments": ["JAMI"]}) + reader.get_dataset(key, {"units": "%", "instruments": ["jami"]}) assert "Sensor-Platform mismatch" in caplog.text hrit_path.unlink() diff --git a/satpy/tests/reader_tests/test_fci_l1c_nc.py b/satpy/tests/reader_tests/test_fci_l1c_nc.py index 428389c9eb..4cbeb3d6b5 100644 --- a/satpy/tests/reader_tests/test_fci_l1c_nc.py +++ b/satpy/tests/reader_tests/test_fci_l1c_nc.py @@ -1120,7 +1120,7 @@ def test_load_aux_data(self, reader_configs, fh_param): assert "orbital_parameters" in res[aux].attrs.keys() assert "file_type" in res[aux].attrs.keys() assert "name" in res[aux].attrs.keys() - assert "instruments" in res[aux].attrs.keys() + assert res[aux].attrs["instruments"] == {"FCI"} assert "resolution" in res[aux].attrs.keys() @pytest.mark.parametrize("fh_param", [(lazy_fixture("FakeFCIFileHandlerFDHSI_fixture")), @@ -1314,9 +1314,9 @@ def test_load_composite(self): # in the tests.compositor_tests package from satpy.composites.config_loader import load_compositor_configs_for_sensors - comps, mods = load_compositor_configs_for_sensors(["FCI"]) - assert len(comps["FCI"]) > 0 - assert len(mods["FCI"]) > 0 + comps, mods = load_compositor_configs_for_sensors(["fci"]) + assert len(comps["fci"]) > 0 + assert len(mods["fci"]) > 0 class TestFCIL1cNCReaderBadData: diff --git a/satpy/tests/reader_tests/test_iasi_l2.py b/satpy/tests/reader_tests/test_iasi_l2.py index fe0fb28ccd..6d2d524523 100644 --- a/satpy/tests/reader_tests/test_iasi_l2.py +++ b/satpy/tests/reader_tests/test_iasi_l2.py @@ -157,7 +157,7 @@ def test_scene(test_data): assert scn.start_time is not None assert scn.end_time is not None assert scn.sensor_names - assert "IASI" in scn.sensor_names + assert "iasi" in scn.sensor_names def test_scene_load_available_datasets(test_data): diff --git a/satpy/tests/reader_tests/test_viirs_compact.py b/satpy/tests/reader_tests/test_viirs_compact.py index 1e282297b1..09f62427d4 100644 --- a/satpy/tests/reader_tests/test_viirs_compact.py +++ b/satpy/tests/reader_tests/test_viirs_compact.py @@ -2468,6 +2468,7 @@ def test_get_dataset(self): assert ds.dtype == np.float32 assert ds.compute().shape == (752, 4064) assert ds.attrs["rows_per_scan"] == 16 + assert ds.attrs["instruments"] == {"VIIRS"} def test_distributed(self): """Check that distributed computations work.""" From 6fb27d506294626abb61514b0ce75e5cb51bf804 Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Wed, 20 May 2026 14:19:34 +0000 Subject: [PATCH 44/72] Normalize instruments internally --- satpy/_instruments.py | 4 +++- satpy/enhancements/enhancer.py | 11 +++++++++-- satpy/scene.py | 4 +++- satpy/tests/test_instruments.py | 11 ++++++----- 4 files changed, 21 insertions(+), 9 deletions(-) diff --git a/satpy/_instruments.py b/satpy/_instruments.py index 799de8f3d5..98a245f115 100644 --- a/satpy/_instruments.py +++ b/satpy/_instruments.py @@ -24,7 +24,7 @@ logger = logging.getLogger(__name__) -def get_instruments_from_attrs(attrs: dict[str,Any]) -> set[str]: +def get_instruments_from_attrs(attrs: dict[str,Any], normalize: bool=False) -> set[str]: """Get instrument names from dataset attributes. String type attributes are converted to set. This can be @@ -49,6 +49,8 @@ def get_instruments_from_attrs(attrs: dict[str,Any]) -> set[str]: stacklevel=2 ) instruments = set([instruments]) + if normalize: + return {normalize_instrument_name(instrument) for instrument in instruments} return instruments diff --git a/satpy/enhancements/enhancer.py b/satpy/enhancements/enhancer.py index a26085c289..edcf8c086c 100644 --- a/satpy/enhancements/enhancer.py +++ b/satpy/enhancements/enhancer.py @@ -210,10 +210,10 @@ def get_enhanced_image(dataset, enhance=None, overlay=None, decorate=None, if enhancer is None or enhancer.enhancement_tree is None: LOG.debug("No enhancement being applied to dataset") else: - sensors = instru.get_instruments_from_attrs(dataset.attrs) + sensors = instru.get_instruments_from_attrs(dataset.attrs, normalize=True) if sensors: enhancer.add_sensor_enhancements(sensors) - enhancer.apply(img, **dataset.attrs) + enhancer.apply(img, **_get_normalized_attrs(dataset, sensors)) if overlay is not None: from satpy.enhancements.overlays import add_overlay @@ -226,3 +226,10 @@ def get_enhanced_image(dataset, enhance=None, overlay=None, decorate=None, img = add_decorate(img, fill_value=fill_value, **decorate) return img + + +def _get_normalized_attrs(dataset, normalized_instruments): + """Get normalized dataset attributes matching the YAML definition.""" + attrs = dataset.attrs.copy() + instru.set_instruments_attr(attrs, normalized_instruments) + return attrs diff --git a/satpy/scene.py b/satpy/scene.py index f43757a4a4..67cb81c010 100644 --- a/satpy/scene.py +++ b/satpy/scene.py @@ -201,7 +201,9 @@ def sensor_names(self) -> set[str]: def _contained_sensor_names(self) -> set[str]: sensor_names = set() for data_arr in self.values(): - sensor_names.update(instru.get_instruments_from_attrs(data_arr.attrs)) + sensor_names.update( + instru.get_instruments_from_attrs(data_arr.attrs, normalize=True) + ) return sensor_names @property diff --git a/satpy/tests/test_instruments.py b/satpy/tests/test_instruments.py index 22e4cf28d2..34bb74142f 100644 --- a/satpy/tests/test_instruments.py +++ b/satpy/tests/test_instruments.py @@ -22,15 +22,16 @@ @pytest.mark.parametrize( - ("attrs", "expected"), + ("attrs", "normalize", "expected"), [ - ({"instruments": {"myinstr"}}, {"myinstr"}), - ({}, set()), + ({"instruments": {"AVHRR/3"}}, False, {"AVHRR/3"}), + ({"instruments": {"AVHRR/3"}}, True, {"avhrr-3"}), + ({}, False, set()), ] ) -def test_get_instruments_from_attrs(attrs, expected): +def test_get_instruments_from_attrs(attrs, normalize, expected): """Test getting instruments from dataset attributes.""" - assert instru.get_instruments_from_attrs(attrs) == expected + assert instru.get_instruments_from_attrs(attrs, normalize) == expected @pytest.mark.parametrize( ("attrs", "expected"), From 7a866b270f74e519e9b17f269f804cbca6a5ca6b Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Wed, 20 May 2026 16:31:32 +0000 Subject: [PATCH 45/72] Revert "Normalize instruments internally" This reverts commit 6fb27d506294626abb61514b0ce75e5cb51bf804. --- satpy/_instruments.py | 4 +--- satpy/enhancements/enhancer.py | 11 ++--------- satpy/scene.py | 4 +--- satpy/tests/test_instruments.py | 11 +++++------ 4 files changed, 9 insertions(+), 21 deletions(-) diff --git a/satpy/_instruments.py b/satpy/_instruments.py index 98a245f115..799de8f3d5 100644 --- a/satpy/_instruments.py +++ b/satpy/_instruments.py @@ -24,7 +24,7 @@ logger = logging.getLogger(__name__) -def get_instruments_from_attrs(attrs: dict[str,Any], normalize: bool=False) -> set[str]: +def get_instruments_from_attrs(attrs: dict[str,Any]) -> set[str]: """Get instrument names from dataset attributes. String type attributes are converted to set. This can be @@ -49,8 +49,6 @@ def get_instruments_from_attrs(attrs: dict[str,Any], normalize: bool=False) -> s stacklevel=2 ) instruments = set([instruments]) - if normalize: - return {normalize_instrument_name(instrument) for instrument in instruments} return instruments diff --git a/satpy/enhancements/enhancer.py b/satpy/enhancements/enhancer.py index edcf8c086c..a26085c289 100644 --- a/satpy/enhancements/enhancer.py +++ b/satpy/enhancements/enhancer.py @@ -210,10 +210,10 @@ def get_enhanced_image(dataset, enhance=None, overlay=None, decorate=None, if enhancer is None or enhancer.enhancement_tree is None: LOG.debug("No enhancement being applied to dataset") else: - sensors = instru.get_instruments_from_attrs(dataset.attrs, normalize=True) + sensors = instru.get_instruments_from_attrs(dataset.attrs) if sensors: enhancer.add_sensor_enhancements(sensors) - enhancer.apply(img, **_get_normalized_attrs(dataset, sensors)) + enhancer.apply(img, **dataset.attrs) if overlay is not None: from satpy.enhancements.overlays import add_overlay @@ -226,10 +226,3 @@ def get_enhanced_image(dataset, enhance=None, overlay=None, decorate=None, img = add_decorate(img, fill_value=fill_value, **decorate) return img - - -def _get_normalized_attrs(dataset, normalized_instruments): - """Get normalized dataset attributes matching the YAML definition.""" - attrs = dataset.attrs.copy() - instru.set_instruments_attr(attrs, normalized_instruments) - return attrs diff --git a/satpy/scene.py b/satpy/scene.py index 67cb81c010..f43757a4a4 100644 --- a/satpy/scene.py +++ b/satpy/scene.py @@ -201,9 +201,7 @@ def sensor_names(self) -> set[str]: def _contained_sensor_names(self) -> set[str]: sensor_names = set() for data_arr in self.values(): - sensor_names.update( - instru.get_instruments_from_attrs(data_arr.attrs, normalize=True) - ) + sensor_names.update(instru.get_instruments_from_attrs(data_arr.attrs)) return sensor_names @property diff --git a/satpy/tests/test_instruments.py b/satpy/tests/test_instruments.py index 34bb74142f..22e4cf28d2 100644 --- a/satpy/tests/test_instruments.py +++ b/satpy/tests/test_instruments.py @@ -22,16 +22,15 @@ @pytest.mark.parametrize( - ("attrs", "normalize", "expected"), + ("attrs", "expected"), [ - ({"instruments": {"AVHRR/3"}}, False, {"AVHRR/3"}), - ({"instruments": {"AVHRR/3"}}, True, {"avhrr-3"}), - ({}, False, set()), + ({"instruments": {"myinstr"}}, {"myinstr"}), + ({}, set()), ] ) -def test_get_instruments_from_attrs(attrs, normalize, expected): +def test_get_instruments_from_attrs(attrs, expected): """Test getting instruments from dataset attributes.""" - assert instru.get_instruments_from_attrs(attrs, normalize) == expected + assert instru.get_instruments_from_attrs(attrs) == expected @pytest.mark.parametrize( ("attrs", "expected"), From 6fa9743dc3c8c92d92b23fb10895b41cbd8c067a Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Wed, 20 May 2026 16:34:39 +0000 Subject: [PATCH 46/72] Use better name for instruments module --- satpy/composites/aux_data.py | 4 ++-- satpy/composites/config_loader.py | 4 ++-- satpy/composites/core.py | 6 +++--- satpy/composites/fill.py | 6 +++--- satpy/composites/glm.py | 4 ++-- satpy/dependency_tree.py | 4 ++-- satpy/modifiers/_crefl_utils.py | 8 ++++---- satpy/modifiers/atmosphere.py | 10 +++++----- satpy/modifiers/spectral.py | 6 +++--- satpy/scene.py | 4 ++-- satpy/writers/core/base.py | 8 ++++---- satpy/writers/mitiff.py | 4 ++-- 12 files changed, 34 insertions(+), 34 deletions(-) diff --git a/satpy/composites/aux_data.py b/satpy/composites/aux_data.py index 6b1724241f..10c0eeab50 100644 --- a/satpy/composites/aux_data.py +++ b/satpy/composites/aux_data.py @@ -22,7 +22,7 @@ import os import satpy -import satpy._instruments as instru +import satpy._instruments as inst_utils from satpy.aux_download import DataDownloadMixin from .core import GenericCompositor @@ -157,7 +157,7 @@ def __call__(self, *args, **kwargs): if self.area is None: raise AttributeError("Area definition needs to be configured") img.attrs["area"] = self.area - instru.set_instruments_attr(img.attrs, set()) + inst_utils.set_instruments_attr(img.attrs, set()) img.attrs["mode"] = "".join(img.bands.data) img.attrs.pop("modifiers", None) img.attrs.pop("calibration", None) diff --git a/satpy/composites/config_loader.py b/satpy/composites/config_loader.py index e8a1ca192d..37004a5865 100644 --- a/satpy/composites/config_loader.py +++ b/satpy/composites/config_loader.py @@ -28,7 +28,7 @@ from yaml import UnsafeLoader import satpy -import satpy._instruments as instru +import satpy._instruments as inst_utils from satpy import DataID, DataQuery from satpy._config import config_search_paths, get_entry_points_config_dirs, glob_config from satpy.dataset.dataid import minimal_default_keys_config @@ -269,7 +269,7 @@ def load_compositor_configs_for_sensor(sensor_name: str) -> tuple[dict[str, dict DataID key -> key properties """ - config_filename = instru.normalize_instrument_name(sensor_name) + ".yaml" + config_filename = inst_utils.normalize_instrument_name(sensor_name) + ".yaml" logger.debug("Looking for composites config file %s", config_filename) paths = get_entry_points_config_dirs("satpy.composites") composite_configs = config_search_paths( diff --git a/satpy/composites/core.py b/satpy/composites/core.py index c44c289cd9..e3b4b733e0 100644 --- a/satpy/composites/core.py +++ b/satpy/composites/core.py @@ -26,7 +26,7 @@ import numpy as np import xarray as xr -import satpy._instruments as instru +import satpy._instruments as inst_utils from satpy.dataset import DataID, combine_metadata from satpy.dataset.dataid import minimal_default_keys_config from satpy.utils import unify_chunks @@ -437,7 +437,7 @@ def _concat_datasets(self, projectables, mode): def _get_sensors(self, projectables) -> set[str]: sensors = set() for projectable in projectables: - sensors.update(instru.get_instruments_from_attrs(projectable.attrs)) + sensors.update(inst_utils.get_instruments_from_attrs(projectable.attrs)) return sensors def __call__( @@ -504,7 +504,7 @@ def _get_updated_attrs(self, datasets, attrs, mode): new_attrs.update(self.attrs) if resolution is not None: new_attrs["resolution"] = resolution - instru.set_instruments_attr(new_attrs, self._get_sensors(datasets)) + inst_utils.set_instruments_attr(new_attrs, self._get_sensors(datasets)) new_attrs["mode"] = mode return new_attrs diff --git a/satpy/composites/fill.py b/satpy/composites/fill.py index b10c80ab43..81e02750a1 100644 --- a/satpy/composites/fill.py +++ b/satpy/composites/fill.py @@ -25,7 +25,7 @@ import numpy as np import xarray as xr -import satpy._instruments as instru +import satpy._instruments as inst_utils from satpy.dataset import combine_metadata from .core import ( @@ -407,10 +407,10 @@ def _combine_metadata_with_mode_and_sensor(self, # 'mode' is no longer valid after we've remove the 'A' # let the base class __call__ determine mode attrs.pop("mode", None) - if not instru.get_instruments_from_attrs(attrs): + if not inst_utils.get_instruments_from_attrs(attrs): # sensor can be a set instruments = self._get_sensors([foreground, background]) - instru.set_instruments_attr(attrs, instruments) + inst_utils.set_instruments_attr(attrs, instruments) return attrs @staticmethod diff --git a/satpy/composites/glm.py b/satpy/composites/glm.py index 6c5ff5f26f..c7591e9de3 100644 --- a/satpy/composites/glm.py +++ b/satpy/composites/glm.py @@ -21,7 +21,7 @@ import xarray as xr -import satpy._instruments as instru +import satpy._instruments as inst_utils from satpy.enhancements.enhancer import get_enhanced_image from .core import GenericCompositor @@ -97,7 +97,7 @@ def _update_attrs(self, new_data, background_layer, highlight_layer): new_data.attrs = background_layer.attrs.copy() new_data.attrs["units"] = 1 new_sensors = self._get_sensors((highlight_layer, background_layer)) - instru.set_instruments_attr(new_data.attrs, new_sensors) + inst_utils.set_instruments_attr(new_data.attrs, new_sensors) def __call__(self, projectables, optional_datasets=None, **attrs): """Create RGBA image with highlighted pixels.""" diff --git a/satpy/dependency_tree.py b/satpy/dependency_tree.py index c250987802..19348633a8 100644 --- a/satpy/dependency_tree.py +++ b/satpy/dependency_tree.py @@ -23,7 +23,7 @@ import numpy as np -import satpy._instruments as instru +import satpy._instruments as inst_utils from satpy import DataID, DatasetDict from satpy.dataset import ModifierTuple, create_filtered_query from satpy.dataset.data_dict import TooManyResults, get_key @@ -505,7 +505,7 @@ def get_compositor(self, key): def get_modifier(self, comp_id): """Get a modifer.""" # create a DataID for the compositor we are generating - instr_key = instru.get_instruments_key() + instr_key = inst_utils.get_instruments_key() modifier = comp_id["modifiers"][-1] for sensor_name in sorted(self.modifiers): modifiers = self.modifiers[sensor_name] diff --git a/satpy/modifiers/_crefl_utils.py b/satpy/modifiers/_crefl_utils.py index 5a9c3796a0..ecda921933 100644 --- a/satpy/modifiers/_crefl_utils.py +++ b/satpy/modifiers/_crefl_utils.py @@ -69,7 +69,7 @@ import numpy as np import xarray as xr -import satpy._instruments as instru +import satpy._instruments as inst_utils from satpy.dataset.dataid import WavelengthRange LOG = logging.getLogger(__name__) @@ -283,7 +283,7 @@ def run_crefl(refl, :param avg_elevation: average elevation (usually pre-calculated and stored in CMGDEM.hdf) """ - sensor = instru.get_one_instrument_from_attrs(refl.attrs) + sensor = inst_utils.get_one_instrument_from_attrs(refl.attrs) runner_cls = _runner_class_for_sensor(sensor) runner = runner_cls(refl) corr_refl = runner(sensor_azimuth, sensor_zenith, solar_azimuth, solar_zenith, avg_elevation) @@ -350,7 +350,7 @@ def _run_crefl(self, mus, muv, phi, solar_zenith, sensor_zenith, height, coeffs) class _VIIRSMODISCREFLRunner(_CREFLRunner): def _run_crefl(self, mus, muv, phi, solar_zenith, sensor_zenith, height, coeffs): - instrument = instru.get_one_instrument_from_attrs(self._refl.attrs) + instrument = inst_utils.get_one_instrument_from_attrs(self._refl.attrs) return da.map_blocks(_run_crefl, self._refl.data, mus.data, muv.data, phi.data, height, instrument, *coeffs, meta=np.ndarray((), dtype=self._refl.dtype), @@ -387,7 +387,7 @@ def _run_crefl(self, mus, muv, phi, solar_zenith, sensor_zenith, height, coeffs) def _runner_class_for_sensor(sensor_name: str) -> Type[_CREFLRunner]: try: - return _SENSOR_TO_RUNNER[instru.normalize_instrument_name(sensor_name)] + return _SENSOR_TO_RUNNER[inst_utils.normalize_instrument_name(sensor_name)] except KeyError: raise NotImplementedError(f"Don't know how to apply CREFL to data from sensor {sensor_name}.") diff --git a/satpy/modifiers/atmosphere.py b/satpy/modifiers/atmosphere.py index de68dbd976..0a93418c70 100644 --- a/satpy/modifiers/atmosphere.py +++ b/satpy/modifiers/atmosphere.py @@ -23,7 +23,7 @@ import numpy as np import xarray as xr -import satpy._instruments as instru +import satpy._instruments as inst_utils from satpy.modifiers import ModifierBase from satpy.modifiers._crefl import ReflectanceCorrector # noqa from satpy.modifiers.angles import compute_relative_azimuth, get_angles, get_satellite_zenith_angle @@ -105,8 +105,8 @@ def __call__(self, projectables, optional_datasets=None, **info): logger.info("Removing Rayleigh scattering with atmosphere '%s' and " "aerosol type '%s' for '%s'", atmosphere, aerosol_type, vis.attrs["name"]) - sensor = instru.get_pyspectral_instrument_name( - instru.get_one_instrument_from_attrs(vis.attrs) + sensor = inst_utils.get_pyspectral_instrument_name( + inst_utils.get_one_instrument_from_attrs(vis.attrs) ) corrector = Rayleigh(vis.attrs["platform_name"], sensor, atmosphere=atmosphere, @@ -162,8 +162,8 @@ def __call__(self, projectables, optional_datasets=None, **info): satz = satz.data # get dask array underneath logger.info("Correction for limb cooling") - sensor = instru.get_pyspectral_instrument_name( - instru.get_one_instrument_from_attrs(band.attrs) + sensor = inst_utils.get_pyspectral_instrument_name( + inst_utils.get_one_instrument_from_attrs(band.attrs) ) corrector = AtmosphericalCorrection(band.attrs["platform_name"], sensor) diff --git a/satpy/modifiers/spectral.py b/satpy/modifiers/spectral.py index 3a37aa8579..7c990417e8 100644 --- a/satpy/modifiers/spectral.py +++ b/satpy/modifiers/spectral.py @@ -21,7 +21,7 @@ import xarray as xr -import satpy._instruments as instru +import satpy._instruments as inst_utils from satpy.modifiers import ModifierBase try: @@ -132,8 +132,8 @@ def _init_reflectance_calculator(self, metadata): if not Calculator: logger.info("Couldn't load pyspectral") raise ImportError("No module named pyspectral.near_infrared_reflectance") - sensor = instru.get_pyspectral_instrument_name( - instru.get_one_instrument_from_attrs(metadata) + sensor = inst_utils.get_pyspectral_instrument_name( + inst_utils.get_one_instrument_from_attrs(metadata) ) reflectance_3x_calculator = Calculator(metadata["platform_name"], sensor, metadata["name"], sunz_threshold=self.sun_zenith_threshold, diff --git a/satpy/scene.py b/satpy/scene.py index f43757a4a4..70cfa32c94 100644 --- a/satpy/scene.py +++ b/satpy/scene.py @@ -29,7 +29,7 @@ from pyresample.geometry import AreaDefinition, BaseDefinition, CoordinateDefinition, SwathDefinition from xarray import DataArray -import satpy._instruments as instru +import satpy._instruments as inst_utils from satpy.area import get_area_def from satpy.composites.config_loader import load_compositor_configs_for_sensors from satpy.composites.core import IncompatibleAreas @@ -201,7 +201,7 @@ def sensor_names(self) -> set[str]: def _contained_sensor_names(self) -> set[str]: sensor_names = set() for data_arr in self.values(): - sensor_names.update(instru.get_instruments_from_attrs(data_arr.attrs)) + sensor_names.update(inst_utils.get_instruments_from_attrs(data_arr.attrs)) return sensor_names @property diff --git a/satpy/writers/core/base.py b/satpy/writers/core/base.py index 2582414475..65024dc557 100644 --- a/satpy/writers/core/base.py +++ b/satpy/writers/core/base.py @@ -22,7 +22,7 @@ import typing import warnings -import satpy._instruments as instru +import satpy._instruments as inst_utils from satpy.aux_download import DataDownloadMixin from satpy.plugin_base import Plugin from satpy.writers.core.compute import compute_writer_results, split_results @@ -139,9 +139,9 @@ def create_filename_parser(self, base_dir): @staticmethod def _prepare_metadata_for_filename_formatting(attrs): with contextlib.suppress(KeyError): - instruments = instru.get_instruments_from_attrs(attrs) - serialized = instru.serialize_instruments(instruments) - instru.set_instruments_attr(attrs, serialized) + instruments = inst_utils.get_instruments_from_attrs(attrs) + serialized = inst_utils.serialize_instruments(instruments) + inst_utils.set_instruments_attr(attrs, serialized) def get_filename(self, **kwargs): """Create a filename where output data will be saved. diff --git a/satpy/writers/mitiff.py b/satpy/writers/mitiff.py index 11803ee8e8..25d9656ed3 100644 --- a/satpy/writers/mitiff.py +++ b/satpy/writers/mitiff.py @@ -26,7 +26,7 @@ import numpy as np from PIL import Image, ImagePalette -import satpy._instruments as instru +import satpy._instruments as inst_utils from satpy.dataset import DataID, DataQuery from satpy.enhancements.enhancer import get_enhanced_image from satpy.writers.core.image import ImageWriter @@ -56,7 +56,7 @@ def _adjust_kwargs(dataset, kwargs): if "sensor" not in kwargs: # MITIFFs needing to handle sensor can only have one sensor # Assume the first value of set as the sensor. - kwargs["sensor"] = instru.get_one_instrument_from_attrs(dataset.attrs) + kwargs["sensor"] = inst_utils.get_one_instrument_from_attrs(dataset.attrs) class MITIFFWriter(ImageWriter): From 677a3886562961760cfc8739739762812f3caba4 Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Wed, 20 May 2026 17:14:31 +0000 Subject: [PATCH 47/72] Choose better method names --- satpy/_instruments.py | 31 ++++++++++------- satpy/composites/config_loader.py | 2 +- satpy/enhancements/enhancer.py | 6 ++-- satpy/modifiers/_crefl_utils.py | 2 +- satpy/tests/test_instruments.py | 55 +++++++++++++++++++------------ satpy/writers/core/base.py | 8 ++--- 6 files changed, 62 insertions(+), 42 deletions(-) diff --git a/satpy/_instruments.py b/satpy/_instruments.py index 3c846c2869..b9f0a72b52 100644 --- a/satpy/_instruments.py +++ b/satpy/_instruments.py @@ -23,7 +23,7 @@ logger = logging.getLogger(__name__) -def get_instruments_from_attrs(attrs: dict[str,Any]) -> set[str]: +def get_instruments_from_attrs(attrs: dict[str,Any], to_internal: bool=False) -> set[str]: """Get instrument names from dataset attributes. String type attributes are converted to set. This can be @@ -48,13 +48,25 @@ def get_instruments_from_attrs(attrs: dict[str,Any]) -> set[str]: stacklevel=2 ) instruments = set([instruments]) + if to_internal: + return { + wmo_to_internal(inst) for inst in instruments + } return instruments -def normalize_instrument_name(instrument: str) -> str: - """Normalize instrument name for internal usage.""" - return instrument.replace("-", "").replace(" ", "_").replace("/", "-").lower() +def wmo_to_internal(instrument: str) -> str: + """Convert WMO to internal instrument name.""" + sep_map = { + "-": "-", + "(": "", + ")": "", + " ": "_", + "/": "-" + } + sep_trans = str.maketrans(sep_map) + return instrument.translate(sep_trans).lower() def get_one_instrument_from_attrs(attrs: dict[str,Any]) -> str: @@ -69,15 +81,12 @@ def get_one_instrument_from_attrs(attrs: dict[str,Any]) -> str: def get_pyspectral_instrument_name(instrument: str) -> str: """Get instrument name expected by pyspectral.""" - return normalize_instrument_name(instrument) + return wmo_to_internal(instrument) -def serialize_instruments(instruments: set[str]) -> str: - """Serialize a set of instruments.""" - return "-".join( - instr.replace("-", "").replace(" ", "").replace("/", "").lower() - for instr in sorted(instruments) - ) +def join_instrument_names(instruments: set[str]) -> str: + """Join a set of instrument names.""" + return "-".join(sorted(instruments)) def set_instruments_attr(attrs: dict[str,Any], instruments: set[str]|str) -> None: diff --git a/satpy/composites/config_loader.py b/satpy/composites/config_loader.py index 37004a5865..a33b1bfd06 100644 --- a/satpy/composites/config_loader.py +++ b/satpy/composites/config_loader.py @@ -269,7 +269,7 @@ def load_compositor_configs_for_sensor(sensor_name: str) -> tuple[dict[str, dict DataID key -> key properties """ - config_filename = inst_utils.normalize_instrument_name(sensor_name) + ".yaml" + config_filename = inst_utils.wmo_to_internal(sensor_name) + ".yaml" logger.debug("Looking for composites config file %s", config_filename) paths = get_entry_points_config_dirs("satpy.composites") composite_configs = config_search_paths( diff --git a/satpy/enhancements/enhancer.py b/satpy/enhancements/enhancer.py index a26085c289..8c0f0a7719 100644 --- a/satpy/enhancements/enhancer.py +++ b/satpy/enhancements/enhancer.py @@ -22,7 +22,7 @@ import yaml from yaml import UnsafeLoader -import satpy._instruments as instru +import satpy._instruments as inst_utils from satpy._config import config_search_paths, get_entry_points_config_dirs from satpy.decision_tree import DecisionTree from satpy.utils import ( @@ -130,7 +130,7 @@ def get_sensor_enhancement_config(self, sensors: set[str]): """Get the sensor-specific config.""" paths = get_entry_points_config_dirs("satpy.enhancements") for sensor_name in sensors: - basename = instru.normalize_instrument_name(sensor_name) + ".yaml" + basename = inst_utils.wmo_to_internal(sensor_name) + ".yaml" config_fn = os.path.join("enhancements", basename) config_files = config_search_paths(config_fn, search_dirs=paths) # Note: Enhancement configuration files can't overwrite individual @@ -210,7 +210,7 @@ def get_enhanced_image(dataset, enhance=None, overlay=None, decorate=None, if enhancer is None or enhancer.enhancement_tree is None: LOG.debug("No enhancement being applied to dataset") else: - sensors = instru.get_instruments_from_attrs(dataset.attrs) + sensors = inst_utils.get_instruments_from_attrs(dataset.attrs) if sensors: enhancer.add_sensor_enhancements(sensors) enhancer.apply(img, **dataset.attrs) diff --git a/satpy/modifiers/_crefl_utils.py b/satpy/modifiers/_crefl_utils.py index ecda921933..69322d6a11 100644 --- a/satpy/modifiers/_crefl_utils.py +++ b/satpy/modifiers/_crefl_utils.py @@ -387,7 +387,7 @@ def _run_crefl(self, mus, muv, phi, solar_zenith, sensor_zenith, height, coeffs) def _runner_class_for_sensor(sensor_name: str) -> Type[_CREFLRunner]: try: - return _SENSOR_TO_RUNNER[inst_utils.normalize_instrument_name(sensor_name)] + return _SENSOR_TO_RUNNER[inst_utils.wmo_to_internal(sensor_name)] except KeyError: raise NotImplementedError(f"Don't know how to apply CREFL to data from sensor {sensor_name}.") diff --git a/satpy/tests/test_instruments.py b/satpy/tests/test_instruments.py index 473aad6a9d..53975948af 100644 --- a/satpy/tests/test_instruments.py +++ b/satpy/tests/test_instruments.py @@ -18,19 +18,20 @@ import pytest import satpy -import satpy._instruments as instru +import satpy._instruments as inst_utils @pytest.mark.parametrize( - ("attrs", "expected"), + ("attrs", "to_internal", "expected"), [ - ({"instruments": {"myinstr"}}, {"myinstr"}), - ({}, set()), + ({"instruments": {"AVHRR/3"}}, False, {"AVHRR/3"}), + ({"instruments": {"AVHRR/3"}}, True, {"avhrr-3"}), + ({}, False, set()), ] ) -def test_get_instruments_from_attrs(attrs, expected): +def test_get_instruments_from_attrs(attrs, to_internal, expected): """Test getting instruments from dataset attributes.""" - assert instru.get_instruments_from_attrs(attrs) == expected + assert inst_utils.get_instruments_from_attrs(attrs, to_internal) == expected @pytest.mark.parametrize( ("attrs", "expected"), @@ -43,39 +44,51 @@ def test_get_instruments_from_attrs(attrs, expected): def test_get_instruments_from_attrs_with_warning(attrs, expected): """Test deprecation warnings when getting instruments.""" with pytest.warns(DeprecationWarning, match="v1.1"): - assert instru.get_instruments_from_attrs(attrs) == expected + assert inst_utils.get_instruments_from_attrs(attrs) == expected + + +@pytest.mark.parametrize( + ("instrument", "expected"), + [ + ("AVHRR/3", "avhrr-3"), + ("IMAGER (GOES 8-11)", "imager_goes_8-11"), + ("MERSI-1", "mersi-1"), + ("MSU-GS/A", "msu-gs-a"), + ] +) +def test_wmo_to_internal(instrument, expected): + """Test conversion to internal instrument name.""" + assert inst_utils.wmo_to_internal(instrument) == expected + -def test_normalize_instrument_name(): - """Test instrument name normalization.""" - instr = "My Instrument-123/1" - expected = "my_instrument123-1" - assert instru.normalize_instrument_name(instr) == expected +def test_join_instruments(): + """Test joining a set of instruments.""" + instruments = {"mersi-1", "abi"} + expected = "abi-mersi-1" + assert inst_utils.join_instrument_names(instruments) == expected -def test_serialize_instruments(): - """Test instrument set serialization.""" - instruments = {"My Instrument-123/1", "ABI"} - expected = "abi-myinstrument1231" - assert instru.serialize_instruments(instruments) == expected def test_set_instruments_attr(): """Test setting instruments attribute.""" attrs = {"instruments": {"myinstrument"}} new_instruments = {"i1", "i2"} with satpy.config.set(instruments_key="instruments"): - instru.set_instruments_attr(attrs, new_instruments) + inst_utils.set_instruments_attr(attrs, new_instruments) assert attrs["instruments"] == new_instruments + def test_get_one_instrument_from_attrs(): """Test getting a single instrument from dataset attributes.""" attrs = {"instruments": {"i1"}} with satpy.config.set(instruments_key="instruments"): - assert instru.get_one_instrument_from_attrs(attrs) == "i1" + assert inst_utils.get_one_instrument_from_attrs(attrs) == "i1" + def test_get_one_instrument_from_attrs_with_warning(caplog): """Test warnings when getting a single instrument.""" attrs = {"instruments": {"i1", "i2"}} with satpy.config.set(instruments_key="instruments"): - instru.get_one_instrument_from_attrs(attrs) + inst_utils.get_one_instrument_from_attrs(attrs) assert "More than one" in caplog.text with pytest.raises(KeyError): - instru.get_one_instrument_from_attrs({}) + inst_utils.get_one_instrument_from_attrs({}) diff --git a/satpy/writers/core/base.py b/satpy/writers/core/base.py index 65024dc557..11b81708ed 100644 --- a/satpy/writers/core/base.py +++ b/satpy/writers/core/base.py @@ -16,7 +16,6 @@ """Shared objects and base classes for writers.""" from __future__ import annotations -import contextlib import logging import os import typing @@ -138,10 +137,9 @@ def create_filename_parser(self, base_dir): @staticmethod def _prepare_metadata_for_filename_formatting(attrs): - with contextlib.suppress(KeyError): - instruments = inst_utils.get_instruments_from_attrs(attrs) - serialized = inst_utils.serialize_instruments(instruments) - inst_utils.set_instruments_attr(attrs, serialized) + instruments = inst_utils.get_instruments_from_attrs(attrs, to_internal=True) + joined = inst_utils.join_instrument_names(instruments) + inst_utils.set_instruments_attr(attrs, joined) def get_filename(self, **kwargs): """Create a filename where output data will be saved. From 0f9c99c838ce854684fe8f6af13e8864f59931b0 Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Wed, 20 May 2026 17:15:50 +0000 Subject: [PATCH 48/72] Add internal to WMO conversion method --- .pre-commit-config.yaml | 2 +- satpy/_instruments.py | 73 +++++++++++++++++++++++++++++++++ satpy/tests/test_instruments.py | 13 ++++++ 3 files changed, 87 insertions(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 38aeed0b47..fbb852bb20 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -27,7 +27,7 @@ repos: - types-setuptools - types-PyYAML - types-requests - args: ["--python-version", "3.10", "--ignore-missing-imports"] + args: ["--python-version", "3.11", "--ignore-missing-imports"] - repo: https://github.com/pycqa/isort rev: 9.0.0a3 hooks: diff --git a/satpy/_instruments.py b/satpy/_instruments.py index b9f0a72b52..8696e53a33 100644 --- a/satpy/_instruments.py +++ b/satpy/_instruments.py @@ -17,6 +17,7 @@ import logging import warnings +from enum import StrEnum from typing import Any import satpy @@ -98,3 +99,75 @@ def set_instruments_attr(attrs: dict[str,Any], instruments: set[str]|str) -> Non def get_instruments_key(): """Get key for instruments in dataset attributes.""" return satpy.config.get("instruments_key") + + +class OSCAR(StrEnum): + """WMO OSCAR instrument names.""" + ABI = "ABI" + AHI = "AHI" + AMSR_2 = "AMSR2" + AMSU_A = "AMSU-A" + AMSU_B = "AMSU-B" + ATMS = "ATMS" + AVHRR = "AVHRR" + AVHRR_2 = "AVHRR/2" + AVHRR_3 = "AVHRR/3" + CRIS = "CrIS" + EPIC = "EPIC" + ETM_PLUS = "ETM+" + FCI = "FCI" + GLM = "GLM" + GMI = "GMI" + IASI = "IASI" + IASI_NG = "IASI-NG" + IMAGER_GOES_12_15 = "IMAGER (GOES 12-15)" + IMAGER_GOES_8_11 = "IMAGER (GOES 8-11)" + IMAGER_INSAT = "IMAGER (INSAT)" + IMAGER_MTSAT_2 = "IMAGER (MTSAT-2)" + JAMI = "JAMI" + LI = "LI" + MERIS = "MERIS" + MERSI_1 = "MERSI-1" + MERSI_2 = "MERSI-2" + MERSI_3 = "MERSI-3" + MERSI_LL = "MERSI-LL" + MERSI_RM = "MERSI-RM" + METIMAGE = "METimage" + MHS = "MHS" + MODIS = "MODIS" + MSS = "MSS" + MSU_GS = "MSU-GS" + MSU_GS_A = "MSU-GS/A" + MVIRI = "MVIRI" + # OSCAR lists "MWR (Sterna)", "MWR (AWS)" etc. + # But to avoid enhancement/composite duplication + # we just use "MWR". + MWR = "MWR" + OCI = "OCI" + OLCI = "OLCI" + OLI = "OLI" + SEAWIFS = "SeaWiFS" + SEVIRI = "SEVIRI" + SGLI = "SGLI" + SLSTR = "SLSTR" + SSMIS = "SSMIS" + TIRS = "TIRS" + TM = "TM" + VIIRS = "VIIRS" + VISSR = "VISSR" + VISSR_HIMAWARI_5 = "VISSR (Himawari-5)" + + +def enum_to_str(instruments: set[StrEnum]) -> set[str]: + """Convert OSCAR enums to string.""" + return {str(i) for i in instruments} + + +_INTERNAL_TO_WMO = { + wmo_to_internal(inst): str(inst) + for inst in OSCAR +} + +def internal_to_wmo(instrument: str) -> str: + """Convert internal to WMO instrument name.""" + return _INTERNAL_TO_WMO.get(instrument, instrument) diff --git a/satpy/tests/test_instruments.py b/satpy/tests/test_instruments.py index 53975948af..abed85dbf2 100644 --- a/satpy/tests/test_instruments.py +++ b/satpy/tests/test_instruments.py @@ -92,3 +92,16 @@ def test_get_one_instrument_from_attrs_with_warning(caplog): assert "More than one" in caplog.text with pytest.raises(KeyError): inst_utils.get_one_instrument_from_attrs({}) + + + +@pytest.mark.parametrize( + ("instrument", "expected"), + [ + ("abi", "ABI"), + ("ABI", "ABI"), + ] +) +def test_internal_to_wmo(instrument, expected): + """Test conversion to WMO instrument name.""" + assert inst_utils.internal_to_wmo(instrument) == expected From 5ecfa7f693b1c4035af689664281238ff34c22d3 Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Wed, 20 May 2026 17:36:56 +0000 Subject: [PATCH 49/72] Make scene.sensor_names return WMO names --- satpy/scene.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/satpy/scene.py b/satpy/scene.py index 70cfa32c94..e26f2e363f 100644 --- a/satpy/scene.py +++ b/satpy/scene.py @@ -183,7 +183,7 @@ def _create_reader_instances(self, @property def sensor_names(self) -> set[str]: - """Return sensor names for the data currently contained in this Scene. + """Return WMO sensor names for the data currently contained in this Scene. Sensor information is collected from data contained in the Scene whether loaded from a reader or generated as a composite with @@ -194,8 +194,7 @@ def sensor_names(self) -> set[str]: """ contained_sensor_names = self._contained_sensor_names() - reader_sensor_names = set([sensor for reader_instance in self._readers.values() - for sensor in reader_instance.sensor_names]) + reader_sensor_names = self._reader_sensor_names() return contained_sensor_names | reader_sensor_names def _contained_sensor_names(self) -> set[str]: @@ -204,6 +203,20 @@ def _contained_sensor_names(self) -> set[str]: sensor_names.update(inst_utils.get_instruments_from_attrs(data_arr.attrs)) return sensor_names + def _reader_sensor_names(self) -> set[str]: + """Get WMO instrument names from readers.""" + instruments = set( + [ + instrument + for reader_instance in self._readers.values() + for instrument in reader_instance.sensor_names + ] + ) + return { + inst_utils.internal_to_wmo(inst) + for inst in instruments + } + @property def start_time(self): """Return the start time of the contained data. From 020a1d9ba98a6d2089f4284af355750db436c2ae Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Wed, 20 May 2026 17:52:31 +0000 Subject: [PATCH 50/72] Convert WMO to internal in enhancer --- satpy/enhancements/enhancer.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/satpy/enhancements/enhancer.py b/satpy/enhancements/enhancer.py index 8c0f0a7719..fb2299b660 100644 --- a/satpy/enhancements/enhancer.py +++ b/satpy/enhancements/enhancer.py @@ -210,10 +210,11 @@ def get_enhanced_image(dataset, enhance=None, overlay=None, decorate=None, if enhancer is None or enhancer.enhancement_tree is None: LOG.debug("No enhancement being applied to dataset") else: - sensors = inst_utils.get_instruments_from_attrs(dataset.attrs) + sensors = inst_utils.get_instruments_from_attrs(dataset.attrs, to_internal=True) if sensors: enhancer.add_sensor_enhancements(sensors) - enhancer.apply(img, **dataset.attrs) + dataset_attrs = _get_dataset_attrs_for_enh(dataset, sensors) + enhancer.apply(img, **dataset_attrs) if overlay is not None: from satpy.enhancements.overlays import add_overlay @@ -226,3 +227,14 @@ def get_enhanced_image(dataset, enhance=None, overlay=None, decorate=None, img = add_decorate(img, fill_value=fill_value, **decorate) return img + + +def _get_dataset_attrs_for_enh(dataset, instruments_int): + """Get dataset attributes for applying enhancement. + + In particular, use instrument names in internal format so that + they match the enhancement definition in the YAML. + """ + attrs = dataset.attrs.copy() + inst_utils.set_instruments_attr(attrs, instruments_int) + return attrs From 18744ea539e220ceb71b62fd7cec1f3ab7cd0c1b Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Wed, 20 May 2026 17:59:33 +0000 Subject: [PATCH 51/72] Fix sensor_names expectations in tests --- satpy/tests/reader_tests/test_iasi_l2.py | 2 +- satpy/tests/reader_tests/test_iasi_l2_so2_bufr.py | 2 +- satpy/tests/reader_tests/test_seviri_l1b_native.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/satpy/tests/reader_tests/test_iasi_l2.py b/satpy/tests/reader_tests/test_iasi_l2.py index 3f0e618119..6c92077a50 100644 --- a/satpy/tests/reader_tests/test_iasi_l2.py +++ b/satpy/tests/reader_tests/test_iasi_l2.py @@ -157,7 +157,7 @@ def test_scene(test_data): assert scn.start_time is not None assert scn.end_time is not None assert scn.sensor_names - assert "iasi" in scn.sensor_names + assert "IASI" in scn.sensor_names def test_scene_load_available_datasets(test_data): diff --git a/satpy/tests/reader_tests/test_iasi_l2_so2_bufr.py b/satpy/tests/reader_tests/test_iasi_l2_so2_bufr.py index b75ac67dee..e9f4c0180a 100644 --- a/satpy/tests/reader_tests/test_iasi_l2_so2_bufr.py +++ b/satpy/tests/reader_tests/test_iasi_l2_so2_bufr.py @@ -366,7 +366,7 @@ def test_scene(self): assert scn.start_time is not None assert scn.end_time is not None assert scn.sensor_names - assert "iasi" in scn.sensor_names + assert "IASI" in scn.sensor_names @unittest.skipIf(sys.platform.startswith("win"), "'eccodes' not supported on Windows") def test_scene_load_available_datasets(self): diff --git a/satpy/tests/reader_tests/test_seviri_l1b_native.py b/satpy/tests/reader_tests/test_seviri_l1b_native.py index c2a5ffee9e..aa8f83ed5b 100644 --- a/satpy/tests/reader_tests/test_seviri_l1b_native.py +++ b/satpy/tests/reader_tests/test_seviri_l1b_native.py @@ -1301,7 +1301,7 @@ def test_read_physical_seviri_nat_file(full_path): """ scene = scene_from_physical_seviri_nat_file(full_path) - assert scene.sensor_names == {"seviri"} + assert scene.sensor_names == {"SEVIRI"} assert len(scene.available_dataset_ids()) == 36 assert set(scene.available_dataset_names()) == set(CHANNEL_INDEX_LIST) From fc63d83ea1586d1bc9ec2ecfc165cdbd2ffa782f Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Thu, 21 May 2026 09:21:58 +0000 Subject: [PATCH 52/72] Fix conversion to WMO name --- satpy/readers/fci_l1c_nc.py | 17 +++++++++++------ satpy/readers/hrit_jma.py | 15 ++++++++++----- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/satpy/readers/fci_l1c_nc.py b/satpy/readers/fci_l1c_nc.py index 64caa97e78..3e94bf3d3a 100644 --- a/satpy/readers/fci_l1c_nc.py +++ b/satpy/readers/fci_l1c_nc.py @@ -483,12 +483,7 @@ def _set_and_cleanup_attributes(self, resattrs, attrs=None, key=None, info=None) Ensures attributes from uncalibrated data (e.g. `_FillValue` from counts) are not propagated to the calibrated data. """ - if info is not None: - resattrs.update(info) - resattrs["instruments"] = { - inst_utils.NORMALIZED_TO_WMO[instrument] - for instrument in resattrs["instruments"] - } + self._update_attrs_with_reader_info(resattrs, info) if None not in (attrs, key): resattrs = self._set_calibrated_data_attributes(resattrs, attrs, key) @@ -506,6 +501,16 @@ def _set_and_cleanup_attributes(self, resattrs, attrs=None, key=None, info=None) resattrs.update(self.orbital_param) return resattrs + def _update_attrs_with_reader_info(self, attrs, info): + if info is not None: + attrs.update(info) + # Instrument names in reader definition are internal format, + # convert to WMO names. + attrs["instruments"] = { + inst_utils.internal_to_wmo(instrument) + for instrument in attrs["instruments"] + } + def get_iqt_parameters_lon_lat_alt(self): """Compute the orbital parameters for IQT data. diff --git a/satpy/readers/hrit_jma.py b/satpy/readers/hrit_jma.py index a4ae3adaa2..9fdaf0ca00 100644 --- a/satpy/readers/hrit_jma.py +++ b/satpy/readers/hrit_jma.py @@ -387,11 +387,7 @@ def get_dataset(self, key, info): # Update attributes. YAML info contains normalized instrument name, # convert to WMO name. - res.attrs.update(info) - res.attrs["instruments"] = { - inst_utils.NORMALIZED_TO_WMO[instrument] - for instrument in res.attrs["instruments"] - } + self._update_attrs_with_reader_info(res.attrs, info) res.attrs["platform_name"] = self.platform res.attrs["orbital_parameters"] = { "projection_longitude": float(self.mda["projection_parameters"]["SSP_longitude"]), @@ -400,6 +396,15 @@ def get_dataset(self, key, info): return res + def _update_attrs_with_reader_info(self, attrs, info): + attrs.update(info) + # Instrument names in reader definition are internal format, + # convert to WMO names. + attrs["instruments"] = { + inst_utils.internal_to_wmo(instrument) + for instrument in attrs["instruments"] + } + def _mask_space(self, data): """Mask space pixels.""" geomask = get_geostationary_mask(area=self.area) From e824e7e5c9d5cec2a6d90eb6e951e441d3a8378f Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Thu, 21 May 2026 10:08:35 +0000 Subject: [PATCH 53/72] Rename sensors -> instruments in reader yaml files --- satpy/etc/readers/abi_l1b.yaml | 2 +- satpy/etc/readers/abi_l1b_scmi.yaml | 2 +- satpy/etc/readers/abi_l2_nc.yaml | 2 +- satpy/etc/readers/acspo.yaml | 2 +- satpy/etc/readers/agri_fy4a_l1.yaml | 2 +- satpy/etc/readers/agri_fy4b_l1.yaml | 2 +- satpy/etc/readers/ahi_hsd.yaml | 2 +- satpy/etc/readers/ahi_l1b_gridded_bin.yaml | 2 +- satpy/etc/readers/ahi_l2_nc.yaml | 2 +- satpy/etc/readers/ami_l1b.yaml | 2 +- satpy/etc/readers/amsr2_l1b.yaml | 2 +- satpy/etc/readers/amsr2_l2.yaml | 2 +- satpy/etc/readers/amsr2_l2_gaasp.yaml | 2 +- satpy/etc/readers/amsub_l1c_aapp.yaml | 2 +- satpy/etc/readers/ascat_l2_soilmoisture_bufr.yaml | 2 +- satpy/etc/readers/atms_l1b_nc.yaml | 2 +- satpy/etc/readers/atms_sdr_hdf5.yaml | 2 +- satpy/etc/readers/avhrr_l0_hrpt.yaml | 2 +- satpy/etc/readers/avhrr_l1b_aapp.yaml | 2 +- satpy/etc/readers/avhrr_l1b_eps.yaml | 2 +- satpy/etc/readers/avhrr_l1b_gaclac.yaml | 2 +- satpy/etc/readers/avhrr_l1c_eum_gac_fdr_nc.yaml | 2 +- satpy/etc/readers/aws1_mwr_l1b_nc.yaml | 2 +- satpy/etc/readers/aws1_mwr_l1c_nc.yaml | 2 +- satpy/etc/readers/caliop_l2_cloud.yaml | 2 +- satpy/etc/readers/camel_l3_nc.yaml | 2 +- satpy/etc/readers/clavrx.yaml | 2 +- satpy/etc/readers/cmsaf-claas2_l2_nc.yaml | 2 +- satpy/etc/readers/electrol_hrit.yaml | 2 +- satpy/etc/readers/epic_l1b_h5.yaml | 2 +- satpy/etc/readers/eps_sterna_mwr_l1b_nc.yaml | 2 +- satpy/etc/readers/etm_l1_tif.yaml | 2 +- satpy/etc/readers/etm_l2_tif.yaml | 2 +- satpy/etc/readers/fci_l2_bufr.yaml | 2 +- satpy/etc/readers/fci_l2_grib.yaml | 2 +- satpy/etc/readers/fci_l2_nc.yaml | 2 +- satpy/etc/readers/fy3a_mersi1_l1b.yaml | 2 +- satpy/etc/readers/fy3b_mersi1_l1b.yaml | 2 +- satpy/etc/readers/fy3c_mersi1_l1b.yaml | 2 +- satpy/etc/readers/generic_image.yaml | 2 +- satpy/etc/readers/geocat.yaml | 2 +- satpy/etc/readers/gerb_l2_hr_h5.yaml | 2 +- satpy/etc/readers/ghi_l1.yaml | 2 +- satpy/etc/readers/ghrsst_l2.yaml | 2 +- satpy/etc/readers/gld360_ualf2.yaml | 4 ++-- satpy/etc/readers/glm_l2.yaml | 2 +- satpy/etc/readers/gms5-vissr_l1b.yaml | 2 +- satpy/etc/readers/goci2_l2_nc.yaml | 2 +- satpy/etc/readers/goes-imager_hrit.yaml | 2 +- satpy/etc/readers/goes-imager_nc.yaml | 2 +- satpy/etc/readers/gpm_imerg.yaml | 2 +- satpy/etc/readers/grib.yaml | 2 +- satpy/etc/readers/hsaf_grib.yaml | 2 +- satpy/etc/readers/hsaf_h5.yaml | 2 +- satpy/etc/readers/hsaf_nc.yaml | 2 +- satpy/etc/readers/hy2_scat_l2b_h5.yaml | 2 +- satpy/etc/readers/iasi_l2.yaml | 2 +- satpy/etc/readers/iasi_l2_cdr_nc.yaml | 2 +- satpy/etc/readers/iasi_l2_so2_bufr.yaml | 2 +- satpy/etc/readers/iasi_ng_l2_nc.yaml | 2 +- satpy/etc/readers/ici_l1b_nc.yaml | 2 +- satpy/etc/readers/insat3d_img_l1b_h5.yaml | 2 +- satpy/etc/readers/jami_hrit.yaml | 2 +- satpy/etc/readers/li_l2_nc.yaml | 2 +- satpy/etc/readers/maia.yaml | 2 +- satpy/etc/readers/mcd12q1.yaml | 2 +- satpy/etc/readers/meris_nc_sen3.yaml | 2 +- satpy/etc/readers/mersi2_l1b.yaml | 2 +- satpy/etc/readers/mersi3_l1b.yaml | 2 +- satpy/etc/readers/mersi_ll_l1b.yaml | 2 +- satpy/etc/readers/mersi_rm_l1b.yaml | 2 +- satpy/etc/readers/mhs_l1c_aapp.yaml | 2 +- satpy/etc/readers/mimicTPW2_comp.yaml | 2 +- satpy/etc/readers/mirs.yaml | 2 +- satpy/etc/readers/modis_l1b.yaml | 2 +- satpy/etc/readers/modis_l2.yaml | 2 +- satpy/etc/readers/modis_l3.yaml | 2 +- satpy/etc/readers/msi_l1c_earthcare.yaml | 2 +- satpy/etc/readers/msi_safe.yaml | 2 +- satpy/etc/readers/msi_safe_l2a.yaml | 2 +- satpy/etc/readers/mss_l1_tif.yaml | 2 +- satpy/etc/readers/msu_gsa_l1b.yaml | 2 +- satpy/etc/readers/multiple_sensors_isccpng_l1g_nc.yaml | 2 +- satpy/etc/readers/mwi_l1b_nc.yaml | 2 +- satpy/etc/readers/mws_l1b_nc.yaml | 2 +- satpy/etc/readers/nucaps.yaml | 2 +- satpy/etc/readers/nwcsaf-geo.yaml | 2 +- satpy/etc/readers/nwcsaf-msg2013-hdf5.yaml | 2 +- satpy/etc/readers/nwcsaf-pps_nc.yaml | 2 +- satpy/etc/readers/oceancolorcci_l3_nc.yaml | 2 +- satpy/etc/readers/oci_l2_bgc.yaml | 2 +- satpy/etc/readers/olci_l1b.yaml | 2 +- satpy/etc/readers/olci_l2.yaml | 2 +- satpy/etc/readers/oli_tirs_l1_tif.yaml | 2 +- satpy/etc/readers/oli_tirs_l2_tif.yaml | 2 +- satpy/etc/readers/omps_edr.yaml | 2 +- satpy/etc/readers/osisaf_nc.yaml | 2 +- satpy/etc/readers/pace_oci_l1b_nc.yaml | 2 +- satpy/etc/readers/safe_sar_l2_ocn.yaml | 2 +- satpy/etc/readers/sar-c_safe.yaml | 2 +- satpy/etc/readers/satpy_cf_nc.yaml | 2 +- satpy/etc/readers/scatsat1_l2b.yaml | 2 +- satpy/etc/readers/seadas_l2.yaml | 2 +- satpy/etc/readers/seviri_l1b_hrit.yaml | 2 +- satpy/etc/readers/seviri_l1b_icare.yaml | 2 +- satpy/etc/readers/seviri_l1b_native.yaml | 2 +- satpy/etc/readers/seviri_l1b_nc.yaml | 2 +- satpy/etc/readers/seviri_l2_bufr.yaml | 2 +- satpy/etc/readers/seviri_l2_grib.yaml | 2 +- satpy/etc/readers/sgli_l1b.yaml | 2 +- satpy/etc/readers/slstr_l1b.yaml | 2 +- satpy/etc/readers/smos_l2_wind.yaml | 2 +- satpy/etc/readers/tm_l1_tif.yaml | 2 +- satpy/etc/readers/tm_l2_tif.yaml | 2 +- satpy/etc/readers/tropomi_l2.yaml | 2 +- satpy/etc/readers/vii_l1b_nc.yaml | 2 +- satpy/etc/readers/vii_l2_nc.yaml | 2 +- satpy/etc/readers/viirs_edr.yaml | 2 +- satpy/etc/readers/viirs_edr_active_fires.yaml | 2 +- satpy/etc/readers/viirs_edr_flood.yaml | 2 +- satpy/etc/readers/viirs_l1b.yaml | 2 +- satpy/etc/readers/viirs_l2.yaml | 2 +- satpy/etc/readers/viirs_sdr.yaml | 2 +- satpy/etc/readers/viirs_vgac_l1c_nc.yaml | 2 +- satpy/etc/readers/virr_l1b.yaml | 2 +- 125 files changed, 126 insertions(+), 126 deletions(-) diff --git a/satpy/etc/readers/abi_l1b.yaml b/satpy/etc/readers/abi_l1b.yaml index 3f743b0e5d..0c7f55cf6c 100644 --- a/satpy/etc/readers/abi_l1b.yaml +++ b/satpy/etc/readers/abi_l1b.yaml @@ -14,7 +14,7 @@ reader: `here `_. status: Nominal supports_fsspec: true - sensors: [abi] + instruments: [abi] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader # file pattern keys to sort files by with 'satpy.utils.group_files' group_keys: ['start_time', 'platform_shortname', 'scene_abbr'] diff --git a/satpy/etc/readers/abi_l1b_scmi.yaml b/satpy/etc/readers/abi_l1b_scmi.yaml index bad924223e..25b20e418c 100644 --- a/satpy/etc/readers/abi_l1b_scmi.yaml +++ b/satpy/etc/readers/abi_l1b_scmi.yaml @@ -5,7 +5,7 @@ reader: description: SCMI NetCDF4 Reader for ABI data status: Beta supports_fsspec: false - sensors: [] + instruments: [] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader # Typical filenames from Unidata THREDDS server: diff --git a/satpy/etc/readers/abi_l2_nc.yaml b/satpy/etc/readers/abi_l2_nc.yaml index ff4dec80d7..588a465073 100644 --- a/satpy/etc/readers/abi_l2_nc.yaml +++ b/satpy/etc/readers/abi_l2_nc.yaml @@ -9,7 +9,7 @@ reader: `here `_. status: Beta supports_fsspec: true - sensors: ['abi'] + instruments: ['abi'] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader # file pattern keys to sort files by with 'satpy.utils.group_files' group_keys: ['start_time', 'platform_shortname', 'scene_abbr'] diff --git a/satpy/etc/readers/acspo.yaml b/satpy/etc/readers/acspo.yaml index 62a90b4e03..27ce71830f 100644 --- a/satpy/etc/readers/acspo.yaml +++ b/satpy/etc/readers/acspo.yaml @@ -6,7 +6,7 @@ reader: status: Nominal supports_fsspec: false reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader - sensors: [viirs,modis,avhrr] + instruments: [viirs,modis,avhrr] default_datasets: file_types: diff --git a/satpy/etc/readers/agri_fy4a_l1.yaml b/satpy/etc/readers/agri_fy4a_l1.yaml index 81452f999a..f4999eebab 100644 --- a/satpy/etc/readers/agri_fy4a_l1.yaml +++ b/satpy/etc/readers/agri_fy4a_l1.yaml @@ -9,7 +9,7 @@ reader: description: FY-4A AGRI instrument HDF5 reader status: Beta supports_fsspec: false - sensors: [agri] + instruments: [agri] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader file_types: diff --git a/satpy/etc/readers/agri_fy4b_l1.yaml b/satpy/etc/readers/agri_fy4b_l1.yaml index 7c54b8ef29..4be4f88977 100644 --- a/satpy/etc/readers/agri_fy4b_l1.yaml +++ b/satpy/etc/readers/agri_fy4b_l1.yaml @@ -9,7 +9,7 @@ reader: description: FY-4B AGRI instrument HDF5 reader status: Nominal supports_fsspec: true - sensors: [agri] + instruments: [agri] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader file_types: diff --git a/satpy/etc/readers/ahi_hsd.yaml b/satpy/etc/readers/ahi_hsd.yaml index b74fc6cd33..584044d214 100644 --- a/satpy/etc/readers/ahi_hsd.yaml +++ b/satpy/etc/readers/ahi_hsd.yaml @@ -9,7 +9,7 @@ reader: status: Nominal supports_fsspec: false reader: !!python/name:satpy.readers.core.yaml_reader.GEOSegmentYAMLReader - sensors: [ahi] + instruments: [ahi] # file pattern keys to sort files by with 'satpy.utils.group_files' group_keys: ['start_time', 'platform_shortname', 'area'] diff --git a/satpy/etc/readers/ahi_l1b_gridded_bin.yaml b/satpy/etc/readers/ahi_l1b_gridded_bin.yaml index 296cf5fb4a..763f0a12c9 100644 --- a/satpy/etc/readers/ahi_l1b_gridded_bin.yaml +++ b/satpy/etc/readers/ahi_l1b_gridded_bin.yaml @@ -10,7 +10,7 @@ reader: status: Nominal supports_fsspec: false reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader - sensors: [ahi] + instruments: [ahi] # file pattern keys to sort files by with 'satpy.utils.group_files' group_keys: ['start_time'] diff --git a/satpy/etc/readers/ahi_l2_nc.yaml b/satpy/etc/readers/ahi_l2_nc.yaml index 273d19aefa..6d9a69f514 100644 --- a/satpy/etc/readers/ahi_l2_nc.yaml +++ b/satpy/etc/readers/ahi_l2_nc.yaml @@ -4,7 +4,7 @@ reader: long_name: Himawari-8/9 AHI Level 2 products in netCDF4 format from NOAA enterprise status: Beta supports_fsspec: true - sensors: ['ahi'] + instruments: ['ahi'] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader diff --git a/satpy/etc/readers/ami_l1b.yaml b/satpy/etc/readers/ami_l1b.yaml index e5ca8866d9..394bf0d6fa 100644 --- a/satpy/etc/readers/ami_l1b.yaml +++ b/satpy/etc/readers/ami_l1b.yaml @@ -6,7 +6,7 @@ reader: GEO-KOMPSAT-2 AMI Level 1b data reader in the NetCDF4 format. The file format and instrument are described on KMA's website `here `_. - sensors: [ami] + instruments: [ami] status: Beta supports_fsspec: true reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader diff --git a/satpy/etc/readers/amsr2_l1b.yaml b/satpy/etc/readers/amsr2_l1b.yaml index 5d6fd2e616..84fec405b6 100644 --- a/satpy/etc/readers/amsr2_l1b.yaml +++ b/satpy/etc/readers/amsr2_l1b.yaml @@ -7,7 +7,7 @@ reader: supports_fsspec: false # could this be a python hook ? reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader - sensors: [amsr2] + instruments: [amsr2] datasets: btemp_10.7v: diff --git a/satpy/etc/readers/amsr2_l2.yaml b/satpy/etc/readers/amsr2_l2.yaml index 611a674ada..6b1d420004 100644 --- a/satpy/etc/readers/amsr2_l2.yaml +++ b/satpy/etc/readers/amsr2_l2.yaml @@ -9,7 +9,7 @@ reader: status: Beta supports_fsspec: false reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader - sensors: [amsr2] + instruments: [amsr2] file_types: amsr2_l2_ssw: diff --git a/satpy/etc/readers/amsr2_l2_gaasp.yaml b/satpy/etc/readers/amsr2_l2_gaasp.yaml index 5cc4874007..9f65056e5f 100644 --- a/satpy/etc/readers/amsr2_l2_gaasp.yaml +++ b/satpy/etc/readers/amsr2_l2_gaasp.yaml @@ -9,7 +9,7 @@ reader: status: Beta supports_fsspec: false reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader - sensors: [amsr2] + instruments: [amsr2] file_types: amsr2_mbt: diff --git a/satpy/etc/readers/amsub_l1c_aapp.yaml b/satpy/etc/readers/amsub_l1c_aapp.yaml index e13dbf5bb8..a1e50f1435 100644 --- a/satpy/etc/readers/amsub_l1c_aapp.yaml +++ b/satpy/etc/readers/amsub_l1c_aapp.yaml @@ -6,7 +6,7 @@ reader: status: Beta supports_fsspec: false reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader - sensors: [amsub,] + instruments: [amsub,] data_identification_keys: name: diff --git a/satpy/etc/readers/ascat_l2_soilmoisture_bufr.yaml b/satpy/etc/readers/ascat_l2_soilmoisture_bufr.yaml index f6ebea19f3..273fa6cee7 100644 --- a/satpy/etc/readers/ascat_l2_soilmoisture_bufr.yaml +++ b/satpy/etc/readers/ascat_l2_soilmoisture_bufr.yaml @@ -6,7 +6,7 @@ reader: Reader for ASCAT L2 SOIL MOISUTRE FILES status: Defunct supports_fsspec: false - sensors: [scatterometer] + instruments: [scatterometer] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader default_datasets: diff --git a/satpy/etc/readers/atms_l1b_nc.yaml b/satpy/etc/readers/atms_l1b_nc.yaml index 4bec4113d5..057d03f84d 100644 --- a/satpy/etc/readers/atms_l1b_nc.yaml +++ b/satpy/etc/readers/atms_l1b_nc.yaml @@ -5,7 +5,7 @@ reader: description: > Reader for the S-NPP and JPSS-1 Advanced Technology Microwave Sounder Level 1B files in NetCDF4. status: Beta - sensors: [atms] + instruments: [atms] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader supports_fsspec: false diff --git a/satpy/etc/readers/atms_sdr_hdf5.yaml b/satpy/etc/readers/atms_sdr_hdf5.yaml index b3ce69bfe9..b92669a209 100644 --- a/satpy/etc/readers/atms_sdr_hdf5.yaml +++ b/satpy/etc/readers/atms_sdr_hdf5.yaml @@ -7,7 +7,7 @@ reader: (474-00001-03_JPSS-CDFCB-X-Vol-III_0124C.pdf) https://www.nesdis.noaa.gov/about/documents-reports/jpss-technical-documents/jpss-science-documents status: Beta - sensors: [atms] + instruments: [atms] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader supports_fsspec: false diff --git a/satpy/etc/readers/avhrr_l0_hrpt.yaml b/satpy/etc/readers/avhrr_l0_hrpt.yaml index 4f22528a5d..fe0741b240 100644 --- a/satpy/etc/readers/avhrr_l0_hrpt.yaml +++ b/satpy/etc/readers/avhrr_l0_hrpt.yaml @@ -6,7 +6,7 @@ reader: status: Alpha supports_fsspec: false reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader - sensors: [avhrr-3, avhrr-2] + instruments: [avhrr-3, avhrr-2] datasets: "1": diff --git a/satpy/etc/readers/avhrr_l1b_aapp.yaml b/satpy/etc/readers/avhrr_l1b_aapp.yaml index 998170cb7c..1586a50091 100644 --- a/satpy/etc/readers/avhrr_l1b_aapp.yaml +++ b/satpy/etc/readers/avhrr_l1b_aapp.yaml @@ -6,7 +6,7 @@ reader: status: Nominal supports_fsspec: false reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader - sensors: [avhrr-3,] + instruments: [avhrr-3,] datasets: '1': diff --git a/satpy/etc/readers/avhrr_l1b_eps.yaml b/satpy/etc/readers/avhrr_l1b_eps.yaml index 5715f113cf..f63df7f311 100644 --- a/satpy/etc/readers/avhrr_l1b_eps.yaml +++ b/satpy/etc/readers/avhrr_l1b_eps.yaml @@ -6,7 +6,7 @@ reader: status: Nominal supports_fsspec: false reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader - sensors: [avhrr-3] + instruments: [avhrr-3] datasets: '1': diff --git a/satpy/etc/readers/avhrr_l1b_gaclac.yaml b/satpy/etc/readers/avhrr_l1b_gaclac.yaml index 1226d74d87..85e24a6a07 100644 --- a/satpy/etc/readers/avhrr_l1b_gaclac.yaml +++ b/satpy/etc/readers/avhrr_l1b_gaclac.yaml @@ -6,7 +6,7 @@ reader: status: Nominal supports_fsspec: false reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader - sensors: [avhrr-3, avhrr-2, avhrr-1] + instruments: [avhrr-3, avhrr-2, avhrr-1] datasets: '1': diff --git a/satpy/etc/readers/avhrr_l1c_eum_gac_fdr_nc.yaml b/satpy/etc/readers/avhrr_l1c_eum_gac_fdr_nc.yaml index 7dcc2535b0..743b3e8ae5 100644 --- a/satpy/etc/readers/avhrr_l1c_eum_gac_fdr_nc.yaml +++ b/satpy/etc/readers/avhrr_l1c_eum_gac_fdr_nc.yaml @@ -5,7 +5,7 @@ reader: description: NetCDF4 reader for EUMETCSAT GAC FDR AVHRR L1c status: Defunct supports_fsspec: false - sensors: [avhrr-3, avhrr-2, avhrr-1] + instruments: [avhrr-3, avhrr-2, avhrr-1] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader diff --git a/satpy/etc/readers/aws1_mwr_l1b_nc.yaml b/satpy/etc/readers/aws1_mwr_l1b_nc.yaml index a3102b5ecd..c15ad55616 100644 --- a/satpy/etc/readers/aws1_mwr_l1b_nc.yaml +++ b/satpy/etc/readers/aws1_mwr_l1b_nc.yaml @@ -4,7 +4,7 @@ reader: long_name: AWS1 MWR L1B Radiance (NetCDF4) description: Reader for the ESA AWS (Arctic Weather Satellite) Microwave Radiometer (MWR) level-1b files in netCDF4. reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader - sensors: [mwr,] + instruments: [mwr,] status: Beta supports_fsspec: false diff --git a/satpy/etc/readers/aws1_mwr_l1c_nc.yaml b/satpy/etc/readers/aws1_mwr_l1c_nc.yaml index 83d5c7a69e..5ec0acde08 100644 --- a/satpy/etc/readers/aws1_mwr_l1c_nc.yaml +++ b/satpy/etc/readers/aws1_mwr_l1c_nc.yaml @@ -4,7 +4,7 @@ reader: long_name: AWS1 MWR L1C Radiance (NetCDF4) description: Reader for the ESA AWS (Arctic Weather Satellite) MWR level-1c files in netCDF4. reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader - sensors: [mwr,] + instruments: [mwr,] status: Beta supports_fsspec: false diff --git a/satpy/etc/readers/caliop_l2_cloud.yaml b/satpy/etc/readers/caliop_l2_cloud.yaml index c5c4ee1146..015050a705 100644 --- a/satpy/etc/readers/caliop_l2_cloud.yaml +++ b/satpy/etc/readers/caliop_l2_cloud.yaml @@ -7,7 +7,7 @@ reader: supports_fsspec: false reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader default_datasets: [] - sensors: [caliop] + instruments: [caliop] datasets: elevation: diff --git a/satpy/etc/readers/camel_l3_nc.yaml b/satpy/etc/readers/camel_l3_nc.yaml index 25b6e8b993..3f7e45b16c 100644 --- a/satpy/etc/readers/camel_l3_nc.yaml +++ b/satpy/etc/readers/camel_l3_nc.yaml @@ -8,7 +8,7 @@ reader: `here `_. status: Nominal supports_fsspec: false - sensors: [combined] + instruments: [combined] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader file_types: diff --git a/satpy/etc/readers/clavrx.yaml b/satpy/etc/readers/clavrx.yaml index 175166ced8..e3d3ad991f 100644 --- a/satpy/etc/readers/clavrx.yaml +++ b/satpy/etc/readers/clavrx.yaml @@ -6,7 +6,7 @@ reader: status: Nominal supports_fsspec: false reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader - sensors: [viirs, modis, avhrr, ahi, abi] + instruments: [viirs, modis, avhrr, ahi, abi] file_types: clavrx_hdf4: diff --git a/satpy/etc/readers/cmsaf-claas2_l2_nc.yaml b/satpy/etc/readers/cmsaf-claas2_l2_nc.yaml index 104349855b..e98e807c72 100644 --- a/satpy/etc/readers/cmsaf-claas2_l2_nc.yaml +++ b/satpy/etc/readers/cmsaf-claas2_l2_nc.yaml @@ -10,7 +10,7 @@ reader: CMSAF and its products can be found at https://www.cmsaf.eu/. status: Beta supports_fsspec: false - sensors: [seviri] + instruments: [seviri] doi: doi:10.5676/EUM_SAF_CM/CLAAS/V002. reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader diff --git a/satpy/etc/readers/electrol_hrit.yaml b/satpy/etc/readers/electrol_hrit.yaml index 436d952bfd..148de8339f 100644 --- a/satpy/etc/readers/electrol_hrit.yaml +++ b/satpy/etc/readers/electrol_hrit.yaml @@ -5,7 +5,7 @@ reader: description: Reader for Electro-L N2 MSU-GS HRIT data status: Nominal supports_fsspec: false - sensors: [msu-gs] + instruments: [msu-gs] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader file_types: diff --git a/satpy/etc/readers/epic_l1b_h5.yaml b/satpy/etc/readers/epic_l1b_h5.yaml index ea81cd0a32..33cc49467a 100644 --- a/satpy/etc/readers/epic_l1b_h5.yaml +++ b/satpy/etc/readers/epic_l1b_h5.yaml @@ -7,7 +7,7 @@ reader: For documentation see: https://cmr.earthdata.nasa.gov/search/concepts/C1667168435-LARC_ASDC.html. status: Beta supports_fsspec: false - sensors: [epic] + instruments: [epic] reader: !!python/name:satpy.readers.core.yaml_reader.GEOFlippableFileYAMLReader file_types: diff --git a/satpy/etc/readers/eps_sterna_mwr_l1b_nc.yaml b/satpy/etc/readers/eps_sterna_mwr_l1b_nc.yaml index b8271e4068..c02ef8abfb 100644 --- a/satpy/etc/readers/eps_sterna_mwr_l1b_nc.yaml +++ b/satpy/etc/readers/eps_sterna_mwr_l1b_nc.yaml @@ -4,7 +4,7 @@ reader: long_name: EPS-Sterna MWR L1B Radiance (NetCDF4) description: Reader for the EUMETSAT EPS-Sterna radiometer level-1b files in netCDF4. reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader - sensors: [mwr,] + instruments: [mwr,] status: Beta supports_fsspec: false diff --git a/satpy/etc/readers/etm_l1_tif.yaml b/satpy/etc/readers/etm_l1_tif.yaml index f18e9b6a1d..ce7d10445e 100644 --- a/satpy/etc/readers/etm_l1_tif.yaml +++ b/satpy/etc/readers/etm_l1_tif.yaml @@ -5,7 +5,7 @@ reader: description: GeoTIFF reader for Landsat-7 ETM+ L1 data. status: Beta supports_fsspec: true - sensors: etm+ + instruments: etm+ default_channels: [] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader data_identification_keys: diff --git a/satpy/etc/readers/etm_l2_tif.yaml b/satpy/etc/readers/etm_l2_tif.yaml index d9a4f6d80b..f792de6206 100644 --- a/satpy/etc/readers/etm_l2_tif.yaml +++ b/satpy/etc/readers/etm_l2_tif.yaml @@ -5,7 +5,7 @@ reader: description: GeoTIFF reader for Landsat-7 ETM+ L2 data. status: Beta supports_fsspec: true - sensors: etm+ + instruments: etm+ default_channels: [] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader data_identification_keys: diff --git a/satpy/etc/readers/fci_l2_bufr.yaml b/satpy/etc/readers/fci_l2_bufr.yaml index 1754093fcd..80b0c1ccf6 100644 --- a/satpy/etc/readers/fci_l2_bufr.yaml +++ b/satpy/etc/readers/fci_l2_bufr.yaml @@ -5,7 +5,7 @@ reader: description: FCI L2 BUFR Product Reader status: Alpha supports_fsspec: false - sensors: [fci] + instruments: [fci] reader: !!python/name:satpy.readers.core.yaml_reader.GEOFlippableFileYAMLReader file_types: diff --git a/satpy/etc/readers/fci_l2_grib.yaml b/satpy/etc/readers/fci_l2_grib.yaml index 23fad1b74f..244784c1af 100644 --- a/satpy/etc/readers/fci_l2_grib.yaml +++ b/satpy/etc/readers/fci_l2_grib.yaml @@ -5,7 +5,7 @@ reader: description: Reader for EUMETSAT MTG FCI L2 files in GRIB2 format. status: Nominal supports_fsspec: false - sensors: [fci] + instruments: [fci] reader: !!python/name:satpy.readers.core.yaml_reader.GEOFlippableFileYAMLReader file_types: diff --git a/satpy/etc/readers/fci_l2_nc.yaml b/satpy/etc/readers/fci_l2_nc.yaml index 04962740b7..4be1056784 100644 --- a/satpy/etc/readers/fci_l2_nc.yaml +++ b/satpy/etc/readers/fci_l2_nc.yaml @@ -5,7 +5,7 @@ reader: description: Reader for EUMETSAT MTG FCI L2 files in NetCDF4 format. status: Alpha supports_fsspec: false - sensors: [fci] + instruments: [fci] reader: !!python/name:satpy.readers.core.yaml_reader.GEOFlippableFileYAMLReader file_types: diff --git a/satpy/etc/readers/fy3a_mersi1_l1b.yaml b/satpy/etc/readers/fy3a_mersi1_l1b.yaml index 738c9e6bb4..47fe8af355 100644 --- a/satpy/etc/readers/fy3a_mersi1_l1b.yaml +++ b/satpy/etc/readers/fy3a_mersi1_l1b.yaml @@ -5,7 +5,7 @@ reader: description: FY-3A Medium Resolution Spectral Imager 1 (MERSI-1) L1B Reader status: Beta supports_fsspec: false - sensors: [mersi-1] + instruments: [mersi-1] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader file_types: diff --git a/satpy/etc/readers/fy3b_mersi1_l1b.yaml b/satpy/etc/readers/fy3b_mersi1_l1b.yaml index 8ed0885e3f..3498d61436 100644 --- a/satpy/etc/readers/fy3b_mersi1_l1b.yaml +++ b/satpy/etc/readers/fy3b_mersi1_l1b.yaml @@ -5,7 +5,7 @@ reader: description: FY-3B Medium Resolution Spectral Imager 1 (MERSI-1) L1B Reader status: Beta supports_fsspec: false - sensors: [mersi-1] + instruments: [mersi-1] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader file_types: diff --git a/satpy/etc/readers/fy3c_mersi1_l1b.yaml b/satpy/etc/readers/fy3c_mersi1_l1b.yaml index f2a4740969..d78b3d6e49 100644 --- a/satpy/etc/readers/fy3c_mersi1_l1b.yaml +++ b/satpy/etc/readers/fy3c_mersi1_l1b.yaml @@ -5,7 +5,7 @@ reader: description: FY-3B Medium Resolution Spectral Imager 1 (MERSI-1) L1B Reader status: Beta supports_fsspec: false - sensors: [mersi-1] + instruments: [mersi-1] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader file_types: diff --git a/satpy/etc/readers/generic_image.yaml b/satpy/etc/readers/generic_image.yaml index 62a710ab8e..eacbe36af6 100644 --- a/satpy/etc/readers/generic_image.yaml +++ b/satpy/etc/readers/generic_image.yaml @@ -6,7 +6,7 @@ reader: status: Nominal supports_fsspec: true reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader - sensors: [images] + instruments: [images] datasets: image: diff --git a/satpy/etc/readers/geocat.yaml b/satpy/etc/readers/geocat.yaml index 1866b81ad1..01661bd344 100644 --- a/satpy/etc/readers/geocat.yaml +++ b/satpy/etc/readers/geocat.yaml @@ -6,7 +6,7 @@ reader: status: Nominal supports_fsspec: false reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader - sensors: [abi, ahi, goes_imager] + instruments: [abi, ahi, goes_imager] file_types: level2: diff --git a/satpy/etc/readers/gerb_l2_hr_h5.yaml b/satpy/etc/readers/gerb_l2_hr_h5.yaml index 7438845da4..2dd5674dc3 100644 --- a/satpy/etc/readers/gerb_l2_hr_h5.yaml +++ b/satpy/etc/readers/gerb_l2_hr_h5.yaml @@ -6,7 +6,7 @@ reader: status: Beta supports_fsspec: false reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader - sensors: [gerb] + instruments: [gerb] file_types: gerb_l2_hr_h5: diff --git a/satpy/etc/readers/ghi_l1.yaml b/satpy/etc/readers/ghi_l1.yaml index e5bbe4592c..ae816738d5 100644 --- a/satpy/etc/readers/ghi_l1.yaml +++ b/satpy/etc/readers/ghi_l1.yaml @@ -9,7 +9,7 @@ reader: description: FY-4A GHI instrument HDF5 reader status: Nominal supports_fsspec: false - sensors: [ghi] + instruments: [ghi] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader file_types: diff --git a/satpy/etc/readers/ghrsst_l2.yaml b/satpy/etc/readers/ghrsst_l2.yaml index 2a6ec00e38..7127397b12 100644 --- a/satpy/etc/readers/ghrsst_l2.yaml +++ b/satpy/etc/readers/ghrsst_l2.yaml @@ -5,7 +5,7 @@ reader: description: NC Reader for GHRSST Level 2 data status: Beta supports_fsspec: false - sensors: ['slstr', 'avhrr/3', 'viirs'] + instruments: ['slstr', 'avhrr/3', 'viirs'] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader file_types: diff --git a/satpy/etc/readers/gld360_ualf2.yaml b/satpy/etc/readers/gld360_ualf2.yaml index 758b8768a7..069aceff4a 100644 --- a/satpy/etc/readers/gld360_ualf2.yaml +++ b/satpy/etc/readers/gld360_ualf2.yaml @@ -4,7 +4,7 @@ reader: long_name: Vaisala GLD360 UALF2 description: Vaisala GLD360 reader for Universal ASCII Lightning Format 2. reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader - sensors: [gld360] + instruments: [gld360] file_types: gld360: @@ -80,7 +80,7 @@ datasets: file_type: gld360 coordinates: [ longitude, latitude ] - number_of_sensors: + number_of_instruments: name: number_of_sensors sensor: gld360 file_type: gld360 diff --git a/satpy/etc/readers/glm_l2.yaml b/satpy/etc/readers/glm_l2.yaml index a37565f44c..8a285ca976 100644 --- a/satpy/etc/readers/glm_l2.yaml +++ b/satpy/etc/readers/glm_l2.yaml @@ -8,7 +8,7 @@ reader: supported. status: Beta supports_fsspec: false - sensors: [glm] + instruments: [glm] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader # file pattern keys to sort files by with 'satpy.utils.group_files' group_keys: ['start_time', 'platform_shortname', 'scene_abbr'] diff --git a/satpy/etc/readers/gms5-vissr_l1b.yaml b/satpy/etc/readers/gms5-vissr_l1b.yaml index b23652c99f..94621065f7 100644 --- a/satpy/etc/readers/gms5-vissr_l1b.yaml +++ b/satpy/etc/readers/gms5-vissr_l1b.yaml @@ -10,7 +10,7 @@ reader: status: Alpha supports_fsspec: true - sensors: [gms5-vissr] + instruments: [gms5-vissr] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader file_types: diff --git a/satpy/etc/readers/goci2_l2_nc.yaml b/satpy/etc/readers/goci2_l2_nc.yaml index 29fc0eb66c..e8e029cf94 100644 --- a/satpy/etc/readers/goci2_l2_nc.yaml +++ b/satpy/etc/readers/goci2_l2_nc.yaml @@ -4,7 +4,7 @@ reader: long_name: GK-2B GOCI-II Level 2 products in netCDF4 format from NOSC status: Beta supports_fsspec: true - sensors: ['goci2'] + instruments: ['goci2'] reader: !!python/name:satpy.readers.core.yaml_reader.GEOSegmentYAMLReader # file pattern keys to sort files by with 'satpy.utils.group_files' group_keys: ['start_time', 'platform_shortname', "slot"] diff --git a/satpy/etc/readers/goes-imager_hrit.yaml b/satpy/etc/readers/goes-imager_hrit.yaml index 83b2e5ef5c..6b86b9aee0 100644 --- a/satpy/etc/readers/goes-imager_hrit.yaml +++ b/satpy/etc/readers/goes-imager_hrit.yaml @@ -5,7 +5,7 @@ reader: description: Reader for GOES Imager Level 1 data in HRIT format status: Nominal supports_fsspec: false - sensors: [goes_imager] + instruments: [goes_imager] reader: !!python/name:satpy.readers.core.yaml_reader.GEOSegmentYAMLReader # eg. diff --git a/satpy/etc/readers/goes-imager_nc.yaml b/satpy/etc/readers/goes-imager_nc.yaml index 2042362d1d..efceef40c4 100644 --- a/satpy/etc/readers/goes-imager_nc.yaml +++ b/satpy/etc/readers/goes-imager_nc.yaml @@ -11,7 +11,7 @@ reader: status: Beta supports_fsspec: false - sensors: [goes_imager] + instruments: [goes_imager] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader diff --git a/satpy/etc/readers/gpm_imerg.yaml b/satpy/etc/readers/gpm_imerg.yaml index 68f0555eb6..a7e491675b 100644 --- a/satpy/etc/readers/gpm_imerg.yaml +++ b/satpy/etc/readers/gpm_imerg.yaml @@ -5,7 +5,7 @@ reader: description: HDF5 reader for the GPM/IMERG data status: Nominal supports_fsspec: false - sensors: [multiple] + instruments: [multiple] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader file_types: diff --git a/satpy/etc/readers/grib.yaml b/satpy/etc/readers/grib.yaml index 1009ed291f..29c78d86da 100644 --- a/satpy/etc/readers/grib.yaml +++ b/satpy/etc/readers/grib.yaml @@ -6,7 +6,7 @@ reader: status: Beta supports_fsspec: false reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader - sensors: [unknown] + instruments: [unknown] data_identification_keys: name: required: true diff --git a/satpy/etc/readers/hsaf_grib.yaml b/satpy/etc/readers/hsaf_grib.yaml index a15237e1c4..8a4f13b32d 100644 --- a/satpy/etc/readers/hsaf_grib.yaml +++ b/satpy/etc/readers/hsaf_grib.yaml @@ -6,7 +6,7 @@ reader: status: Beta, only h03, h03b, h05 and h05b currently supported supports_fsspec: false reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader - sensors: [hsaf] + instruments: [hsaf] file_types: hsafgrib: diff --git a/satpy/etc/readers/hsaf_h5.yaml b/satpy/etc/readers/hsaf_h5.yaml index b000fa5ad6..b7f6a9e99a 100644 --- a/satpy/etc/readers/hsaf_h5.yaml +++ b/satpy/etc/readers/hsaf_h5.yaml @@ -6,7 +6,7 @@ reader: status: Beta, only h10 currently supported supports_fsspec: false reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader - sensors: [hsaf] + instruments: [hsaf] file_types: hsafh5: diff --git a/satpy/etc/readers/hsaf_nc.yaml b/satpy/etc/readers/hsaf_nc.yaml index 75b0225b35..42b9af75ff 100644 --- a/satpy/etc/readers/hsaf_nc.yaml +++ b/satpy/etc/readers/hsaf_nc.yaml @@ -7,7 +7,7 @@ reader: containing rain rate (rr and acc_rr) and quality index (qind). status: Alpha supports_fsspec: false - sensors: [hsaf] + instruments: [hsaf] reader: !!python/name:satpy.readers.core.yaml_reader.GEOFlippableFileYAMLReader file_types: diff --git a/satpy/etc/readers/hy2_scat_l2b_h5.yaml b/satpy/etc/readers/hy2_scat_l2b_h5.yaml index 32e13628d0..1b709e6fce 100644 --- a/satpy/etc/readers/hy2_scat_l2b_h5.yaml +++ b/satpy/etc/readers/hy2_scat_l2b_h5.yaml @@ -6,7 +6,7 @@ reader: status: Beta supports_fsspec: false reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader - sensors: [scatterometer] + instruments: [scatterometer] default_datasets: file_types: diff --git a/satpy/etc/readers/iasi_l2.yaml b/satpy/etc/readers/iasi_l2.yaml index 927e880ed0..9854900360 100644 --- a/satpy/etc/readers/iasi_l2.yaml +++ b/satpy/etc/readers/iasi_l2.yaml @@ -6,7 +6,7 @@ reader: status: Alpha supports_fsspec: false reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader - sensors: [iasi] + instruments: [iasi] default_datasets: datasets: diff --git a/satpy/etc/readers/iasi_l2_cdr_nc.yaml b/satpy/etc/readers/iasi_l2_cdr_nc.yaml index c1ac1cca30..7f5eadf681 100644 --- a/satpy/etc/readers/iasi_l2_cdr_nc.yaml +++ b/satpy/etc/readers/iasi_l2_cdr_nc.yaml @@ -10,7 +10,7 @@ reader: status: Alpha supports_fsspec: True reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader - sensors: [iasi] + instruments: [iasi] default_datasets: file_types: diff --git a/satpy/etc/readers/iasi_l2_so2_bufr.yaml b/satpy/etc/readers/iasi_l2_so2_bufr.yaml index 909e6e1ff3..4f2c87a67a 100644 --- a/satpy/etc/readers/iasi_l2_so2_bufr.yaml +++ b/satpy/etc/readers/iasi_l2_so2_bufr.yaml @@ -6,7 +6,7 @@ reader: Reader for IASI L2 files status: Beta supports_fsspec: false - sensors: [iasi] + instruments: [iasi] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader default_datasets: diff --git a/satpy/etc/readers/iasi_ng_l2_nc.yaml b/satpy/etc/readers/iasi_ng_l2_nc.yaml index d7375e4a88..0e96380c8f 100644 --- a/satpy/etc/readers/iasi_ng_l2_nc.yaml +++ b/satpy/etc/readers/iasi_ng_l2_nc.yaml @@ -3,7 +3,7 @@ reader: short_name: IASI-NG L2 NC Reader long_name: IASI-NG Level-2 NetCDF Reader description: Reader for IASI-NG Level-2 NetCDF files - sensors: [iasi] + instruments: [iasi] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader status: Alpha supports_fsspec: false diff --git a/satpy/etc/readers/ici_l1b_nc.yaml b/satpy/etc/readers/ici_l1b_nc.yaml index 809a225c7c..67ffc865fb 100644 --- a/satpy/etc/readers/ici_l1b_nc.yaml +++ b/satpy/etc/readers/ici_l1b_nc.yaml @@ -5,7 +5,7 @@ reader: description: > Reader for EUMETSAT EPS-SG Ice Cloud Imager Level 1B Radiance files in NetCDF4. status: Beta - sensors: [ici] + instruments: [ici] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader data_identification_keys: diff --git a/satpy/etc/readers/insat3d_img_l1b_h5.yaml b/satpy/etc/readers/insat3d_img_l1b_h5.yaml index ac7d90d685..26d8f546c7 100644 --- a/satpy/etc/readers/insat3d_img_l1b_h5.yaml +++ b/satpy/etc/readers/insat3d_img_l1b_h5.yaml @@ -7,7 +7,7 @@ reader: For documentation see: https://mosdac.gov.in/insat-3d-references . status: Beta, navigation still off supports_fsspec: false - sensors: [insat3d_img] + instruments: [insat3d_img] reader: !!python/name:satpy.readers.core.yaml_reader.GEOFlippableFileYAMLReader file_types: diff --git a/satpy/etc/readers/jami_hrit.yaml b/satpy/etc/readers/jami_hrit.yaml index da011dec31..acf4d89e4d 100644 --- a/satpy/etc/readers/jami_hrit.yaml +++ b/satpy/etc/readers/jami_hrit.yaml @@ -13,7 +13,7 @@ reader: status: Beta supports_fsspec: false - sensors: [jami] + instruments: [jami] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader file_types: diff --git a/satpy/etc/readers/li_l2_nc.yaml b/satpy/etc/readers/li_l2_nc.yaml index ad1ac5c75e..1deec588f5 100644 --- a/satpy/etc/readers/li_l2_nc.yaml +++ b/satpy/etc/readers/li_l2_nc.yaml @@ -3,7 +3,7 @@ reader: short_name: LI L2 NC Reader long_name: LI Level-2 NetCDF Reader description: Reader for MTG Lightning Imager (LI) Level-2 NetCDF files - sensors: [li] + instruments: [li] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader status: Beta supports_fsspec: false diff --git a/satpy/etc/readers/maia.yaml b/satpy/etc/readers/maia.yaml index caa0acec7b..c0e2498ab1 100644 --- a/satpy/etc/readers/maia.yaml +++ b/satpy/etc/readers/maia.yaml @@ -6,7 +6,7 @@ reader: status: Nominal supports_fsspec: false reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader - sensors: [viirs, avhrr] + instruments: [viirs, avhrr] file_types: maia: diff --git a/satpy/etc/readers/mcd12q1.yaml b/satpy/etc/readers/mcd12q1.yaml index c0c439449f..a8314a4af3 100644 --- a/satpy/etc/readers/mcd12q1.yaml +++ b/satpy/etc/readers/mcd12q1.yaml @@ -6,7 +6,7 @@ reader: status: Beta supports_fsspec: false reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader - sensors: [modis] + instruments: [modis] file_types: modis_mcd12q1_hdf_eos: diff --git a/satpy/etc/readers/meris_nc_sen3.yaml b/satpy/etc/readers/meris_nc_sen3.yaml index 32cd998ef3..c65b1ae9f3 100644 --- a/satpy/etc/readers/meris_nc_sen3.yaml +++ b/satpy/etc/readers/meris_nc_sen3.yaml @@ -5,7 +5,7 @@ reader: description: NC Reader for MERIS data (Sentinel 3 like format) status: Beta supports_fsspec: false - sensors: [meris] + instruments: [meris] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader file_types: diff --git a/satpy/etc/readers/mersi2_l1b.yaml b/satpy/etc/readers/mersi2_l1b.yaml index 59ef4e9adb..6d58ad89c1 100644 --- a/satpy/etc/readers/mersi2_l1b.yaml +++ b/satpy/etc/readers/mersi2_l1b.yaml @@ -5,7 +5,7 @@ reader: description: FY-3D Medium Resolution Spectral Imager 2 (MERSI-2) L1B Reader status: Beta supports_fsspec: false - sensors: [mersi-2] + instruments: [mersi-2] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader file_types: diff --git a/satpy/etc/readers/mersi3_l1b.yaml b/satpy/etc/readers/mersi3_l1b.yaml index 93dffffd9b..41f9df7f53 100644 --- a/satpy/etc/readers/mersi3_l1b.yaml +++ b/satpy/etc/readers/mersi3_l1b.yaml @@ -5,7 +5,7 @@ reader: description: FY-3F Medium Resolution Spectral Imager 3 (MERSI-3) L1B Reader status: Beta supports_fsspec: false - sensors: [mersi-3] + instruments: [mersi-3] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader file_types: diff --git a/satpy/etc/readers/mersi_ll_l1b.yaml b/satpy/etc/readers/mersi_ll_l1b.yaml index ad035b4fcc..b2bc3dcf9e 100644 --- a/satpy/etc/readers/mersi_ll_l1b.yaml +++ b/satpy/etc/readers/mersi_ll_l1b.yaml @@ -5,7 +5,7 @@ reader: description: FY-3E Medium Resolution Spectral Imager - Low Light (MERSI-LL) L1B Reader status: Nominal supports_fsspec: true - sensors: [mersi-ll] + instruments: [mersi-ll] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader file_types: diff --git a/satpy/etc/readers/mersi_rm_l1b.yaml b/satpy/etc/readers/mersi_rm_l1b.yaml index b689ff2ceb..e3612cafa3 100644 --- a/satpy/etc/readers/mersi_rm_l1b.yaml +++ b/satpy/etc/readers/mersi_rm_l1b.yaml @@ -5,7 +5,7 @@ reader: description: FY-3G Medium Resolution Spectral Imager - Rainfall Measurement (MERSI-RM) L1B Reader status: Beta supports_fsspec: false - sensors: [mersi-rm] + instruments: [mersi-rm] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader file_types: diff --git a/satpy/etc/readers/mhs_l1c_aapp.yaml b/satpy/etc/readers/mhs_l1c_aapp.yaml index 38361048d9..a3aa92e1ab 100644 --- a/satpy/etc/readers/mhs_l1c_aapp.yaml +++ b/satpy/etc/readers/mhs_l1c_aapp.yaml @@ -6,7 +6,7 @@ reader: status: Nominal supports_fsspec: false reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader - sensors: [mhs,] + instruments: [mhs,] data_identification_keys: name: diff --git a/satpy/etc/readers/mimicTPW2_comp.yaml b/satpy/etc/readers/mimicTPW2_comp.yaml index 3e15c74081..57cba85b08 100644 --- a/satpy/etc/readers/mimicTPW2_comp.yaml +++ b/satpy/etc/readers/mimicTPW2_comp.yaml @@ -5,7 +5,7 @@ reader: description: NetCDF4 reader for the MIMIC TPW Version 2.0 product status: Beta supports_fsspec: false - sensors: [mimic] + instruments: [mimic] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader file_types: diff --git a/satpy/etc/readers/mirs.yaml b/satpy/etc/readers/mirs.yaml index 5e0cf80f9b..12332e21fa 100644 --- a/satpy/etc/readers/mirs.yaml +++ b/satpy/etc/readers/mirs.yaml @@ -6,7 +6,7 @@ reader: status: Beta supports_fsspec: false reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader - sensors: [amsu, amsu-mhs, atms, ssmis, gmi] + instruments: [amsu, amsu-mhs, atms, ssmis, gmi] data_files: - url: "https://zenodo.org/record/10357932/files/limbcoef_atmsland_noaa20.txt" known_hash: "08deca15afe8638effac9e6ccb442c2c386f5444926129d30a250d5840264c1d" diff --git a/satpy/etc/readers/modis_l1b.yaml b/satpy/etc/readers/modis_l1b.yaml index 864ed0bedb..3f4f4eea6f 100644 --- a/satpy/etc/readers/modis_l1b.yaml +++ b/satpy/etc/readers/modis_l1b.yaml @@ -6,7 +6,7 @@ reader: status: Nominal supports_fsspec: false reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader - sensors: [modis] + instruments: [modis] default_datasets: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36] datasets: diff --git a/satpy/etc/readers/modis_l2.yaml b/satpy/etc/readers/modis_l2.yaml index 354fb796fe..4171bd46e7 100644 --- a/satpy/etc/readers/modis_l2.yaml +++ b/satpy/etc/readers/modis_l2.yaml @@ -6,7 +6,7 @@ reader: status: Beta supports_fsspec: false reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader - sensors: [modis] + instruments: [modis] file_types: mod05_hdf: diff --git a/satpy/etc/readers/modis_l3.yaml b/satpy/etc/readers/modis_l3.yaml index 7699e26f61..c67a7af4e9 100644 --- a/satpy/etc/readers/modis_l3.yaml +++ b/satpy/etc/readers/modis_l3.yaml @@ -6,7 +6,7 @@ reader: status: Beta supports_fsspec: false reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader - sensors: [modis] + instruments: [modis] file_types: modis_l3_cmg_hdf: diff --git a/satpy/etc/readers/msi_l1c_earthcare.yaml b/satpy/etc/readers/msi_l1c_earthcare.yaml index 833241417e..d8c0414037 100644 --- a/satpy/etc/readers/msi_l1c_earthcare.yaml +++ b/satpy/etc/readers/msi_l1c_earthcare.yaml @@ -5,7 +5,7 @@ reader: description: Multispectral Imager for EarthCARE Level 1C (regridded) Reader status: Nominal supports_fsspec: true - sensors: [ec_msi] + instruments: [ec_msi] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader diff --git a/satpy/etc/readers/msi_safe.yaml b/satpy/etc/readers/msi_safe.yaml index 14c5ece2d3..6bdd292fc0 100644 --- a/satpy/etc/readers/msi_safe.yaml +++ b/satpy/etc/readers/msi_safe.yaml @@ -5,7 +5,7 @@ reader: description: SAFE Reader for MSI L1C data (Sentinel-2) status: Nominal supports_fsspec: false - sensors: [sen2_msi] + instruments: [sen2_msi] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader file_types: diff --git a/satpy/etc/readers/msi_safe_l2a.yaml b/satpy/etc/readers/msi_safe_l2a.yaml index 964207eb7d..d9e707ee04 100644 --- a/satpy/etc/readers/msi_safe_l2a.yaml +++ b/satpy/etc/readers/msi_safe_l2a.yaml @@ -5,7 +5,7 @@ reader: description: SAFE Reader for MSI L2A data (Sentinel-2) status: Nominal supports_fsspec: false - sensors: [msi] + instruments: [msi] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader data_identification_keys: name: diff --git a/satpy/etc/readers/mss_l1_tif.yaml b/satpy/etc/readers/mss_l1_tif.yaml index b085bd3552..759b2e1d65 100644 --- a/satpy/etc/readers/mss_l1_tif.yaml +++ b/satpy/etc/readers/mss_l1_tif.yaml @@ -5,7 +5,7 @@ reader: description: GeoTIFF reader for Landsat-1/2/3/4/5 MSS L1 data. status: Beta supports_fsspec: true - sensors: mss + instruments: mss default_channels: [] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader data_identification_keys: diff --git a/satpy/etc/readers/msu_gsa_l1b.yaml b/satpy/etc/readers/msu_gsa_l1b.yaml index d650ac6e89..0a27b90fbc 100644 --- a/satpy/etc/readers/msu_gsa_l1b.yaml +++ b/satpy/etc/readers/msu_gsa_l1b.yaml @@ -5,7 +5,7 @@ reader: description: H5 reader for MSG-GS/A data status: Beta supports_fsspec: false - sensors: [msu_gsa] + instruments: [msu_gsa] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader diff --git a/satpy/etc/readers/multiple_sensors_isccpng_l1g_nc.yaml b/satpy/etc/readers/multiple_sensors_isccpng_l1g_nc.yaml index a0f3537de8..b5e2b6cb8e 100644 --- a/satpy/etc/readers/multiple_sensors_isccpng_l1g_nc.yaml +++ b/satpy/etc/readers/multiple_sensors_isccpng_l1g_nc.yaml @@ -3,7 +3,7 @@ reader: short_name: ISCCP NG NetCDF4 long_name: ISCCP NG Level 1g NetCDF4 description: https://cimss.ssec.wisc.edu/isccp-ng/ - sensors: [seviri, abi, ahi] + instruments: [seviri, abi, ahi] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader group_keys: ["start_time", "satid"] diff --git a/satpy/etc/readers/mwi_l1b_nc.yaml b/satpy/etc/readers/mwi_l1b_nc.yaml index f52247bc00..74ce30fd02 100644 --- a/satpy/etc/readers/mwi_l1b_nc.yaml +++ b/satpy/etc/readers/mwi_l1b_nc.yaml @@ -5,7 +5,7 @@ reader: description: > Reader for EUMETSAT EPS-SG Micro-Wave Imager Level 1B Radiance files in NetCDF4. status: Beta - sensors: [mwi] + instruments: [mwi] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader data_identification_keys: diff --git a/satpy/etc/readers/mws_l1b_nc.yaml b/satpy/etc/readers/mws_l1b_nc.yaml index c79baece8f..500d632c8a 100644 --- a/satpy/etc/readers/mws_l1b_nc.yaml +++ b/satpy/etc/readers/mws_l1b_nc.yaml @@ -4,7 +4,7 @@ reader: long_name: EPS-SG MWS L1B Radiance (NetCDF4) description: Reader for the EPS-SG l1b MWS (Microwave Sounder) level-1 files in netCDF4. reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader - sensors: [mws,] + instruments: [mws,] status: Beta data_identification_keys: diff --git a/satpy/etc/readers/nucaps.yaml b/satpy/etc/readers/nucaps.yaml index a948357a04..33dfa1b017 100644 --- a/satpy/etc/readers/nucaps.yaml +++ b/satpy/etc/readers/nucaps.yaml @@ -6,7 +6,7 @@ reader: status: Nominal supports_fsspec: false reader: !!python/name:satpy.readers.nucaps.NUCAPSReader - sensors: [cris, atms, viirs] + instruments: [cris, atms, viirs] data_identification_keys: name: required: true diff --git a/satpy/etc/readers/nwcsaf-geo.yaml b/satpy/etc/readers/nwcsaf-geo.yaml index 3a7f13bff3..fc170c01eb 100644 --- a/satpy/etc/readers/nwcsaf-geo.yaml +++ b/satpy/etc/readers/nwcsaf-geo.yaml @@ -5,7 +5,7 @@ reader: description: NetCDF4 reader for the NWCSAF GEO 2016/2018 format status: Alpha supports_fsspec: false - sensors: [seviri, abi, ahi] + instruments: [seviri, abi, ahi] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader file_types: diff --git a/satpy/etc/readers/nwcsaf-msg2013-hdf5.yaml b/satpy/etc/readers/nwcsaf-msg2013-hdf5.yaml index e5fe95f258..d5c5711ae0 100644 --- a/satpy/etc/readers/nwcsaf-msg2013-hdf5.yaml +++ b/satpy/etc/readers/nwcsaf-msg2013-hdf5.yaml @@ -5,7 +5,7 @@ reader: description: HDF5 reader for the NWCSAF/Geo Seviri 2013 format status: Defunct supports_fsspec: false - sensors: [seviri] + instruments: [seviri] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader file_types: diff --git a/satpy/etc/readers/nwcsaf-pps_nc.yaml b/satpy/etc/readers/nwcsaf-pps_nc.yaml index 45c2ecd0bd..1049113734 100644 --- a/satpy/etc/readers/nwcsaf-pps_nc.yaml +++ b/satpy/etc/readers/nwcsaf-pps_nc.yaml @@ -5,7 +5,7 @@ reader: description: NetCDF4 reader for the NWCSAF/PPS 2014 format status: Alpha, only standard swath based ouput supported (remapped netCDF and CPP products not supported yet) supports_fsspec: false - sensors: ['avhrr-3', 'viirs', 'modis'] + instruments: ['avhrr-3', 'viirs', 'modis'] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader file_types: diff --git a/satpy/etc/readers/oceancolorcci_l3_nc.yaml b/satpy/etc/readers/oceancolorcci_l3_nc.yaml index 67b703f99a..65a704c3e9 100644 --- a/satpy/etc/readers/oceancolorcci_l3_nc.yaml +++ b/satpy/etc/readers/oceancolorcci_l3_nc.yaml @@ -5,7 +5,7 @@ reader: description: NetCDF Reader for ESA Oceancolor CCI data status: Nominal supports_fsspec: false - sensors: [merged] + instruments: [merged] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader file_types: diff --git a/satpy/etc/readers/oci_l2_bgc.yaml b/satpy/etc/readers/oci_l2_bgc.yaml index 4d8c17c38a..2efc12cd37 100644 --- a/satpy/etc/readers/oci_l2_bgc.yaml +++ b/satpy/etc/readers/oci_l2_bgc.yaml @@ -6,7 +6,7 @@ reader: status: Beta supports_fsspec: false reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader - sensors: [oci] + instruments: [oci] file_types: bgc_nc: diff --git a/satpy/etc/readers/olci_l1b.yaml b/satpy/etc/readers/olci_l1b.yaml index d36279a262..fcf4ea2ee7 100644 --- a/satpy/etc/readers/olci_l1b.yaml +++ b/satpy/etc/readers/olci_l1b.yaml @@ -5,7 +5,7 @@ reader: description: NC Reader for OLCI data status: Nominal supports_fsspec: true - sensors: [olci] + instruments: [olci] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader file_types: diff --git a/satpy/etc/readers/olci_l2.yaml b/satpy/etc/readers/olci_l2.yaml index 95cb024e72..ac2a5ed548 100644 --- a/satpy/etc/readers/olci_l2.yaml +++ b/satpy/etc/readers/olci_l2.yaml @@ -5,7 +5,7 @@ reader: description: NC Reader for OLCI data status: Nominal supports_fsspec: true - sensors: [olci] + instruments: [olci] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader file_types: diff --git a/satpy/etc/readers/oli_tirs_l1_tif.yaml b/satpy/etc/readers/oli_tirs_l1_tif.yaml index db758e7057..e489e0d738 100644 --- a/satpy/etc/readers/oli_tirs_l1_tif.yaml +++ b/satpy/etc/readers/oli_tirs_l1_tif.yaml @@ -5,7 +5,7 @@ reader: description: GeoTIFF reader for Landsat-8/9 OLI/TIRS L1 data. status: Beta supports_fsspec: true - sensors: oli_tirs + instruments: oli_tirs reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader data_identification_keys: name: diff --git a/satpy/etc/readers/oli_tirs_l2_tif.yaml b/satpy/etc/readers/oli_tirs_l2_tif.yaml index a0406ea749..79c78582e9 100644 --- a/satpy/etc/readers/oli_tirs_l2_tif.yaml +++ b/satpy/etc/readers/oli_tirs_l2_tif.yaml @@ -5,7 +5,7 @@ reader: description: GeoTIFF reader for Landsat-8/9 OLI/TIRS L2 data. status: Beta supports_fsspec: true - sensors: oli_tirs + instruments: oli_tirs default_channels: [] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader data_identification_keys: diff --git a/satpy/etc/readers/omps_edr.yaml b/satpy/etc/readers/omps_edr.yaml index 7f1c2c0ab8..c23a4b057d 100644 --- a/satpy/etc/readers/omps_edr.yaml +++ b/satpy/etc/readers/omps_edr.yaml @@ -6,7 +6,7 @@ reader: status: Beta supports_fsspec: true reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader - sensors: [omps] + instruments: [omps] file_types: omps_total_ozone: diff --git a/satpy/etc/readers/osisaf_nc.yaml b/satpy/etc/readers/osisaf_nc.yaml index bdcfd9687a..fb8d501131 100644 --- a/satpy/etc/readers/osisaf_nc.yaml +++ b/satpy/etc/readers/osisaf_nc.yaml @@ -10,7 +10,7 @@ reader: status: Beta supports_fsspec: true - sensors: [osisaf] + instruments: [osisaf] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader file_types: diff --git a/satpy/etc/readers/pace_oci_l1b_nc.yaml b/satpy/etc/readers/pace_oci_l1b_nc.yaml index 5ae6dbceb5..187be5b4ef 100644 --- a/satpy/etc/readers/pace_oci_l1b_nc.yaml +++ b/satpy/etc/readers/pace_oci_l1b_nc.yaml @@ -5,7 +5,7 @@ reader: description: NC Reader for OCI data status: Nominal supports_fsspec: true - sensors: [oci] + instruments: [oci] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader file_types: diff --git a/satpy/etc/readers/safe_sar_l2_ocn.yaml b/satpy/etc/readers/safe_sar_l2_ocn.yaml index a7117c36c2..81e2b4f516 100644 --- a/satpy/etc/readers/safe_sar_l2_ocn.yaml +++ b/satpy/etc/readers/safe_sar_l2_ocn.yaml @@ -5,7 +5,7 @@ reader: description: SAFE Reader for SAR L2 OCN data status: Defunct supports_fsspec: false - sensors: [sar-c] + instruments: [sar-c] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader file_types: diff --git a/satpy/etc/readers/sar-c_safe.yaml b/satpy/etc/readers/sar-c_safe.yaml index d55f8ca9b6..6939edb6a9 100644 --- a/satpy/etc/readers/sar-c_safe.yaml +++ b/satpy/etc/readers/sar-c_safe.yaml @@ -5,7 +5,7 @@ reader: description: SAFE Reader for SAR-C data status: Nominal supports_fsspec: true - sensors: [sar-c] + instruments: [sar-c] reader: !!python/name:satpy.readers.sar_c_safe.SAFESARReader data_identification_keys: name: diff --git a/satpy/etc/readers/satpy_cf_nc.yaml b/satpy/etc/readers/satpy_cf_nc.yaml index e9239352d0..c365156a45 100644 --- a/satpy/etc/readers/satpy_cf_nc.yaml +++ b/satpy/etc/readers/satpy_cf_nc.yaml @@ -6,7 +6,7 @@ reader: status: Nominal supports_fsspec: false reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader - sensors: [many] + instruments: [many] #datasets: diff --git a/satpy/etc/readers/scatsat1_l2b.yaml b/satpy/etc/readers/scatsat1_l2b.yaml index 71ef94ee3d..10058a61e3 100644 --- a/satpy/etc/readers/scatsat1_l2b.yaml +++ b/satpy/etc/readers/scatsat1_l2b.yaml @@ -6,7 +6,7 @@ reader: reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader status: defunct supports_fsspec: false - sensors: [scatterometer] + instruments: [scatterometer] default_datasets: datasets: diff --git a/satpy/etc/readers/seadas_l2.yaml b/satpy/etc/readers/seadas_l2.yaml index 39e5649b20..ba2b4cd4db 100644 --- a/satpy/etc/readers/seadas_l2.yaml +++ b/satpy/etc/readers/seadas_l2.yaml @@ -6,7 +6,7 @@ reader: status: Beta supports_fsspec: false reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader - sensors: [modis, viirs] + instruments: [modis, viirs] file_types: chlora_seadas: diff --git a/satpy/etc/readers/seviri_l1b_hrit.yaml b/satpy/etc/readers/seviri_l1b_hrit.yaml index d83135092e..8f1ec1be40 100644 --- a/satpy/etc/readers/seviri_l1b_hrit.yaml +++ b/satpy/etc/readers/seviri_l1b_hrit.yaml @@ -11,7 +11,7 @@ reader: HRIT reader for EUMETSAT MSG (Meteosat 8 to 11) SEVIRI Level 1b files. status: Nominal supports_fsspec: true - sensors: [seviri] + instruments: [seviri] reader: !!python/name:satpy.readers.core.yaml_reader.GEOSegmentYAMLReader file_types: diff --git a/satpy/etc/readers/seviri_l1b_icare.yaml b/satpy/etc/readers/seviri_l1b_icare.yaml index 68f0b31ce0..f0dd1dbd70 100644 --- a/satpy/etc/readers/seviri_l1b_icare.yaml +++ b/satpy/etc/readers/seviri_l1b_icare.yaml @@ -11,7 +11,7 @@ reader: A reader for L1b SEVIRI data that has been retrieved from the ICARE service as HDF. status: Defunct supports_fsspec: false - sensors: [seviri] + instruments: [seviri] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader file_types: diff --git a/satpy/etc/readers/seviri_l1b_native.yaml b/satpy/etc/readers/seviri_l1b_native.yaml index 9421e53011..d66f626fe9 100644 --- a/satpy/etc/readers/seviri_l1b_native.yaml +++ b/satpy/etc/readers/seviri_l1b_native.yaml @@ -6,7 +6,7 @@ reader: Reader for EUMETSAT MSG SEVIRI Level 1b native format files. status: Nominal supports_fsspec: true - sensors: [seviri] + instruments: [seviri] reader: !!python/name:satpy.readers.core.yaml_reader.GEOFlippableFileYAMLReader # file pattern keys to sort files by with 'satpy.utils.group_files' group_keys: ['end_time', 'satid'] diff --git a/satpy/etc/readers/seviri_l1b_nc.yaml b/satpy/etc/readers/seviri_l1b_nc.yaml index b5facc9e07..81aa3e11da 100644 --- a/satpy/etc/readers/seviri_l1b_nc.yaml +++ b/satpy/etc/readers/seviri_l1b_nc.yaml @@ -6,7 +6,7 @@ reader: NetCDF4 reader for EUMETSAT MSG SEVIRI Level 1b files. status: Beta, HRV channel not supported supports_fsspec: true - sensors: [seviri] + instruments: [seviri] reader: !!python/name:satpy.readers.core.yaml_reader.GEOFlippableFileYAMLReader group_keys: ["start_time", "satid"] diff --git a/satpy/etc/readers/seviri_l2_bufr.yaml b/satpy/etc/readers/seviri_l2_bufr.yaml index a25ccc360e..c20f937bc4 100644 --- a/satpy/etc/readers/seviri_l2_bufr.yaml +++ b/satpy/etc/readers/seviri_l2_bufr.yaml @@ -5,7 +5,7 @@ reader: description: SEVIRI L2 BUFR Product Reader status: Alpha supports_fsspec: false - sensors: [seviri] + instruments: [seviri] reader: !!python/name:satpy.readers.core.yaml_reader.GEOFlippableFileYAMLReader file_types: diff --git a/satpy/etc/readers/seviri_l2_grib.yaml b/satpy/etc/readers/seviri_l2_grib.yaml index b3c57f49b6..52b1d3bf5b 100644 --- a/satpy/etc/readers/seviri_l2_grib.yaml +++ b/satpy/etc/readers/seviri_l2_grib.yaml @@ -5,7 +5,7 @@ reader: description: Reader for EUMETSAT MSG SEVIRI L2 files in GRIB format. status: Nominal supports_fsspec: false - sensors: [seviri] + instruments: [seviri] reader: !!python/name:satpy.readers.core.yaml_reader.GEOFlippableFileYAMLReader diff --git a/satpy/etc/readers/sgli_l1b.yaml b/satpy/etc/readers/sgli_l1b.yaml index 67863624d6..0143cb102d 100644 --- a/satpy/etc/readers/sgli_l1b.yaml +++ b/satpy/etc/readers/sgli_l1b.yaml @@ -6,7 +6,7 @@ reader: status: Beta supports_fsspec: false reference: https://gportal.jaxa.jp/gpr/assets/mng_upload/GCOM-C/SGLI_Level1_Product_Format_Description_en.pdf - sensors: [sgli] + instruments: [sgli] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader data_identification_keys: diff --git a/satpy/etc/readers/slstr_l1b.yaml b/satpy/etc/readers/slstr_l1b.yaml index 40dde70853..d9c80f31e3 100644 --- a/satpy/etc/readers/slstr_l1b.yaml +++ b/satpy/etc/readers/slstr_l1b.yaml @@ -5,7 +5,7 @@ reader: description: NC Reader for SLSTR data status: Alpha supports_fsspec: false - sensors: [slstr] + instruments: [slstr] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader data_identification_keys: diff --git a/satpy/etc/readers/smos_l2_wind.yaml b/satpy/etc/readers/smos_l2_wind.yaml index ad8b65078e..6b02fdcb07 100644 --- a/satpy/etc/readers/smos_l2_wind.yaml +++ b/satpy/etc/readers/smos_l2_wind.yaml @@ -6,7 +6,7 @@ reader: status: Beta supports_fsspec: false reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader - sensors: [smos] + instruments: [smos] file_types: smos_l2_wind: diff --git a/satpy/etc/readers/tm_l1_tif.yaml b/satpy/etc/readers/tm_l1_tif.yaml index 5cf561d6e9..12adf39f36 100644 --- a/satpy/etc/readers/tm_l1_tif.yaml +++ b/satpy/etc/readers/tm_l1_tif.yaml @@ -5,7 +5,7 @@ reader: description: GeoTIFF reader for Landsat-4/5 TM L1 data. status: Beta supports_fsspec: true - sensors: tm + instruments: tm default_channels: [] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader data_identification_keys: diff --git a/satpy/etc/readers/tm_l2_tif.yaml b/satpy/etc/readers/tm_l2_tif.yaml index 45cc10926c..bfa0ee1e5f 100644 --- a/satpy/etc/readers/tm_l2_tif.yaml +++ b/satpy/etc/readers/tm_l2_tif.yaml @@ -5,7 +5,7 @@ reader: description: GeoTIFF reader for Landsat-4/5 TM L2 data. status: Beta supports_fsspec: true - sensors: tm + instruments: tm default_channels: [] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader data_identification_keys: diff --git a/satpy/etc/readers/tropomi_l2.yaml b/satpy/etc/readers/tropomi_l2.yaml index c407de1ceb..069115d495 100644 --- a/satpy/etc/readers/tropomi_l2.yaml +++ b/satpy/etc/readers/tropomi_l2.yaml @@ -6,7 +6,7 @@ reader: status: Beta supports_fsspec: false reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader - sensors: [tropomi] + instruments: [tropomi] file_types: tropomi_l2: diff --git a/satpy/etc/readers/vii_l1b_nc.yaml b/satpy/etc/readers/vii_l1b_nc.yaml index 67a98ef46b..c987b3101a 100644 --- a/satpy/etc/readers/vii_l1b_nc.yaml +++ b/satpy/etc/readers/vii_l1b_nc.yaml @@ -6,7 +6,7 @@ reader: Reader for EUMETSAT EPS-SG Visual Infrared Imager Level 1B Radiance files in NetCDF4 format per FS V4A. status: Beta supports_fsspec: false - sensors: [vii] + instruments: [vii] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader file_types: diff --git a/satpy/etc/readers/vii_l2_nc.yaml b/satpy/etc/readers/vii_l2_nc.yaml index 037da505b9..6fff8c14bd 100644 --- a/satpy/etc/readers/vii_l2_nc.yaml +++ b/satpy/etc/readers/vii_l2_nc.yaml @@ -6,7 +6,7 @@ Reader for EUMETSAT EPSG-SG Visual Infrared Imager Level 2 files in NetCDF4 format. status: Beta supports_fsspec: false - sensors: [vii] + instruments: [vii] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader file_types: diff --git a/satpy/etc/readers/viirs_edr.yaml b/satpy/etc/readers/viirs_edr.yaml index 3929a62448..6e431f21c7 100644 --- a/satpy/etc/readers/viirs_edr.yaml +++ b/satpy/etc/readers/viirs_edr.yaml @@ -6,7 +6,7 @@ reader: status: Beta supports_fsspec: false reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader - sensors: [viirs] + instruments: [viirs] group_keys: ['platform_shortname'] default_datasets: diff --git a/satpy/etc/readers/viirs_edr_active_fires.yaml b/satpy/etc/readers/viirs_edr_active_fires.yaml index a36b63ddae..a2e0dfe534 100644 --- a/satpy/etc/readers/viirs_edr_active_fires.yaml +++ b/satpy/etc/readers/viirs_edr_active_fires.yaml @@ -6,7 +6,7 @@ reader: status: Beta supports_fsspec: false reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader - sensors: [viirs] + instruments: [viirs] file_types: fires_netcdf_img: diff --git a/satpy/etc/readers/viirs_edr_flood.yaml b/satpy/etc/readers/viirs_edr_flood.yaml index a1eccacdd6..545fb81d97 100644 --- a/satpy/etc/readers/viirs_edr_flood.yaml +++ b/satpy/etc/readers/viirs_edr_flood.yaml @@ -6,7 +6,7 @@ reader: status: Defunct supports_fsspec: false reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader - sensors: [viirs] + instruments: [viirs] file_types: viirs_edr: diff --git a/satpy/etc/readers/viirs_l1b.yaml b/satpy/etc/readers/viirs_l1b.yaml index 4bd0d9d1ed..3cbd0ccdbf 100644 --- a/satpy/etc/readers/viirs_l1b.yaml +++ b/satpy/etc/readers/viirs_l1b.yaml @@ -6,7 +6,7 @@ reader: status: Nominal supports_fsspec: false reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader - sensors: [viirs] + instruments: [viirs] default_datasets: navigations: diff --git a/satpy/etc/readers/viirs_l2.yaml b/satpy/etc/readers/viirs_l2.yaml index 81cb1b8bea..c4bdf1eb83 100644 --- a/satpy/etc/readers/viirs_l2.yaml +++ b/satpy/etc/readers/viirs_l2.yaml @@ -6,7 +6,7 @@ reader: status: Alpha supports_fsspec: false reader: !!python/name:satpy.readers.core.yaml_reader.GEOFlippableFileYAMLReader - sensors: [viirs] + instruments: [viirs] file_types: cldprop_l2_viirs: diff --git a/satpy/etc/readers/viirs_sdr.yaml b/satpy/etc/readers/viirs_sdr.yaml index 70f2c5f34a..294181db07 100644 --- a/satpy/etc/readers/viirs_sdr.yaml +++ b/satpy/etc/readers/viirs_sdr.yaml @@ -6,7 +6,7 @@ reader: status: Nominal supports_fsspec: false reader: !!python/name:satpy.readers.viirs_sdr.VIIRSSDRReader - sensors: [viirs] + instruments: [viirs] # file pattern keys to sort files by with 'satpy.utils.group_files' # by default, don't use start_time group files (only orbit and platform) group_keys: ['orbit', 'platform_shortname'] diff --git a/satpy/etc/readers/viirs_vgac_l1c_nc.yaml b/satpy/etc/readers/viirs_vgac_l1c_nc.yaml index 08c7770a8d..c91ad2d261 100644 --- a/satpy/etc/readers/viirs_vgac_l1c_nc.yaml +++ b/satpy/etc/readers/viirs_vgac_l1c_nc.yaml @@ -5,7 +5,7 @@ reader: description: https://ams.confex.com/ams/JOINTSATMET/mediafile/Handout/Paper360556/VGAC%20version%201%20-%20poster.pdf NetCDF4 reader for VGAC from VIIRS. - sensors: [viirs] + instruments: [viirs] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader file_types: diff --git a/satpy/etc/readers/virr_l1b.yaml b/satpy/etc/readers/virr_l1b.yaml index e9feb2ca52..f1b397b6ee 100644 --- a/satpy/etc/readers/virr_l1b.yaml +++ b/satpy/etc/readers/virr_l1b.yaml @@ -5,7 +5,7 @@ reader: description: reader for VIRR data status: Beta supports_fsspec: false - sensors: [virr] + instruments: [virr] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader file_types: From 1f0d9a03edee1037b3d73fd81dbe774d2bee096d Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Thu, 21 May 2026 10:13:42 +0000 Subject: [PATCH 54/72] Remove extra quotes --- satpy/etc/readers/abi_l2_nc.yaml | 2 +- satpy/etc/readers/ahi_l2_nc.yaml | 2 +- satpy/etc/readers/ghrsst_l2.yaml | 2 +- satpy/etc/readers/goci2_l2_nc.yaml | 2 +- satpy/etc/readers/nwcsaf-pps_nc.yaml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/satpy/etc/readers/abi_l2_nc.yaml b/satpy/etc/readers/abi_l2_nc.yaml index 588a465073..33bf9c5cf5 100644 --- a/satpy/etc/readers/abi_l2_nc.yaml +++ b/satpy/etc/readers/abi_l2_nc.yaml @@ -9,7 +9,7 @@ reader: `here `_. status: Beta supports_fsspec: true - instruments: ['abi'] + instruments: [abi] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader # file pattern keys to sort files by with 'satpy.utils.group_files' group_keys: ['start_time', 'platform_shortname', 'scene_abbr'] diff --git a/satpy/etc/readers/ahi_l2_nc.yaml b/satpy/etc/readers/ahi_l2_nc.yaml index 6d9a69f514..09f1985de8 100644 --- a/satpy/etc/readers/ahi_l2_nc.yaml +++ b/satpy/etc/readers/ahi_l2_nc.yaml @@ -4,7 +4,7 @@ reader: long_name: Himawari-8/9 AHI Level 2 products in netCDF4 format from NOAA enterprise status: Beta supports_fsspec: true - instruments: ['ahi'] + instruments: [ahi] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader diff --git a/satpy/etc/readers/ghrsst_l2.yaml b/satpy/etc/readers/ghrsst_l2.yaml index 7127397b12..c64043e7ff 100644 --- a/satpy/etc/readers/ghrsst_l2.yaml +++ b/satpy/etc/readers/ghrsst_l2.yaml @@ -5,7 +5,7 @@ reader: description: NC Reader for GHRSST Level 2 data status: Beta supports_fsspec: false - instruments: ['slstr', 'avhrr/3', 'viirs'] + instruments: [slstr, avhrr/3, viirs] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader file_types: diff --git a/satpy/etc/readers/goci2_l2_nc.yaml b/satpy/etc/readers/goci2_l2_nc.yaml index e8e029cf94..fa2ee804c4 100644 --- a/satpy/etc/readers/goci2_l2_nc.yaml +++ b/satpy/etc/readers/goci2_l2_nc.yaml @@ -4,7 +4,7 @@ reader: long_name: GK-2B GOCI-II Level 2 products in netCDF4 format from NOSC status: Beta supports_fsspec: true - instruments: ['goci2'] + instruments: [goci2] reader: !!python/name:satpy.readers.core.yaml_reader.GEOSegmentYAMLReader # file pattern keys to sort files by with 'satpy.utils.group_files' group_keys: ['start_time', 'platform_shortname', "slot"] diff --git a/satpy/etc/readers/nwcsaf-pps_nc.yaml b/satpy/etc/readers/nwcsaf-pps_nc.yaml index 1049113734..c8009b28d2 100644 --- a/satpy/etc/readers/nwcsaf-pps_nc.yaml +++ b/satpy/etc/readers/nwcsaf-pps_nc.yaml @@ -5,7 +5,7 @@ reader: description: NetCDF4 reader for the NWCSAF/PPS 2014 format status: Alpha, only standard swath based ouput supported (remapped netCDF and CPP products not supported yet) supports_fsspec: false - instruments: ['avhrr-3', 'viirs', 'modis'] + instruments: [avhrr-3, viirs, modis] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader file_types: From f10216b6f6bb5e1f4dd06c9d80798e30be04459a Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Thu, 21 May 2026 10:14:48 +0000 Subject: [PATCH 55/72] Remove trailing comma --- satpy/etc/readers/amsub_l1c_aapp.yaml | 2 +- satpy/etc/readers/avhrr_l1b_aapp.yaml | 2 +- satpy/etc/readers/aws1_mwr_l1b_nc.yaml | 2 +- satpy/etc/readers/aws1_mwr_l1c_nc.yaml | 2 +- satpy/etc/readers/eps_sterna_mwr_l1b_nc.yaml | 2 +- satpy/etc/readers/mhs_l1c_aapp.yaml | 2 +- satpy/etc/readers/mws_l1b_nc.yaml | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/satpy/etc/readers/amsub_l1c_aapp.yaml b/satpy/etc/readers/amsub_l1c_aapp.yaml index a1e50f1435..e2e468c4fb 100644 --- a/satpy/etc/readers/amsub_l1c_aapp.yaml +++ b/satpy/etc/readers/amsub_l1c_aapp.yaml @@ -6,7 +6,7 @@ reader: status: Beta supports_fsspec: false reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader - instruments: [amsub,] + instruments: [amsub] data_identification_keys: name: diff --git a/satpy/etc/readers/avhrr_l1b_aapp.yaml b/satpy/etc/readers/avhrr_l1b_aapp.yaml index 1586a50091..d079266942 100644 --- a/satpy/etc/readers/avhrr_l1b_aapp.yaml +++ b/satpy/etc/readers/avhrr_l1b_aapp.yaml @@ -6,7 +6,7 @@ reader: status: Nominal supports_fsspec: false reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader - instruments: [avhrr-3,] + instruments: [avhrr-3] datasets: '1': diff --git a/satpy/etc/readers/aws1_mwr_l1b_nc.yaml b/satpy/etc/readers/aws1_mwr_l1b_nc.yaml index c15ad55616..c5894b119c 100644 --- a/satpy/etc/readers/aws1_mwr_l1b_nc.yaml +++ b/satpy/etc/readers/aws1_mwr_l1b_nc.yaml @@ -4,7 +4,7 @@ reader: long_name: AWS1 MWR L1B Radiance (NetCDF4) description: Reader for the ESA AWS (Arctic Weather Satellite) Microwave Radiometer (MWR) level-1b files in netCDF4. reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader - instruments: [mwr,] + instruments: [mwr] status: Beta supports_fsspec: false diff --git a/satpy/etc/readers/aws1_mwr_l1c_nc.yaml b/satpy/etc/readers/aws1_mwr_l1c_nc.yaml index 5ec0acde08..3afc100172 100644 --- a/satpy/etc/readers/aws1_mwr_l1c_nc.yaml +++ b/satpy/etc/readers/aws1_mwr_l1c_nc.yaml @@ -4,7 +4,7 @@ reader: long_name: AWS1 MWR L1C Radiance (NetCDF4) description: Reader for the ESA AWS (Arctic Weather Satellite) MWR level-1c files in netCDF4. reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader - instruments: [mwr,] + instruments: [mwr] status: Beta supports_fsspec: false diff --git a/satpy/etc/readers/eps_sterna_mwr_l1b_nc.yaml b/satpy/etc/readers/eps_sterna_mwr_l1b_nc.yaml index c02ef8abfb..376748d180 100644 --- a/satpy/etc/readers/eps_sterna_mwr_l1b_nc.yaml +++ b/satpy/etc/readers/eps_sterna_mwr_l1b_nc.yaml @@ -4,7 +4,7 @@ reader: long_name: EPS-Sterna MWR L1B Radiance (NetCDF4) description: Reader for the EUMETSAT EPS-Sterna radiometer level-1b files in netCDF4. reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader - instruments: [mwr,] + instruments: [mwr] status: Beta supports_fsspec: false diff --git a/satpy/etc/readers/mhs_l1c_aapp.yaml b/satpy/etc/readers/mhs_l1c_aapp.yaml index a3aa92e1ab..229e149f7c 100644 --- a/satpy/etc/readers/mhs_l1c_aapp.yaml +++ b/satpy/etc/readers/mhs_l1c_aapp.yaml @@ -6,7 +6,7 @@ reader: status: Nominal supports_fsspec: false reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader - instruments: [mhs,] + instruments: [mhs] data_identification_keys: name: diff --git a/satpy/etc/readers/mws_l1b_nc.yaml b/satpy/etc/readers/mws_l1b_nc.yaml index 500d632c8a..559e4f4c49 100644 --- a/satpy/etc/readers/mws_l1b_nc.yaml +++ b/satpy/etc/readers/mws_l1b_nc.yaml @@ -4,7 +4,7 @@ reader: long_name: EPS-SG MWS L1B Radiance (NetCDF4) description: Reader for the EPS-SG l1b MWS (Microwave Sounder) level-1 files in netCDF4. reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader - instruments: [mws,] + instruments: [mws] status: Beta data_identification_keys: From 51c8abf6ab30c5163bd61c3a749e69dca2366ce3 Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Fri, 22 May 2026 07:56:55 +0000 Subject: [PATCH 56/72] Improve docstring --- satpy/decision_tree.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/satpy/decision_tree.py b/satpy/decision_tree.py index c9adc26ac4..18e5572a25 100644 --- a/satpy/decision_tree.py +++ b/satpy/decision_tree.py @@ -186,12 +186,21 @@ def _normalize_multival_attr(self, attr): @staticmethod def _convert_query_val_to_hashable(query_val): + """Prepare multival query for matching. + + First priority is exact matches with the sorted values. + If not found, search each of the values individually in + alphabetical order - both as tuple with a single element + and string. + """ _sorted_query_val = sorted(query_val) - # Full match: All elements in the tuple + # Exact match query_vals = [tuple(_sorted_query_val)] - # Partial match: One element of the tuple, as tuple + # Each of the values individually, in alphabetical order, + # as tuple with a single element. query_vals += [tuple([v]) for v in _sorted_query_val] - # Partial match: One element of the tuple, as string + # Each of the values individually, in alphabetical order, + # as string. query_vals += _sorted_query_val query_vals += query_val return query_vals From 8a54b769a0394ea0d36f1eb7fd4d201c1eac16a3 Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper <1991007+sfinkens@users.noreply.github.com> Date: Fri, 22 May 2026 09:58:36 +0200 Subject: [PATCH 57/72] Simplify tuple creation Co-authored-by: David Hoese --- satpy/decision_tree.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/satpy/decision_tree.py b/satpy/decision_tree.py index c9adc26ac4..2a89fe676e 100644 --- a/satpy/decision_tree.py +++ b/satpy/decision_tree.py @@ -181,7 +181,7 @@ def _normalize_multival_attr(self, attr): if isinstance(attr, list): return tuple(sorted(attr)) elif isinstance(attr, str): - return tuple([attr]) + return (attr,) return attr @staticmethod From 830678e5aa4a6ed15eb8850394bcc9420cf05068 Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Fri, 22 May 2026 08:05:22 +0000 Subject: [PATCH 58/72] Add v1.0 scissors --- satpy/enhancements/enhancer.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/satpy/enhancements/enhancer.py b/satpy/enhancements/enhancer.py index e0c1d9b4f7..fb29c04a16 100644 --- a/satpy/enhancements/enhancer.py +++ b/satpy/enhancements/enhancer.py @@ -58,7 +58,9 @@ def add_config_to_tree(self, *decision_dict: str | Path | dict) -> None: for config_file in decision_dict: config_dict = self._get_config_dict_from_user(config_file) recursive_dict_update(conf, config_dict) + # 8< v1.0 self._ensure_compat(conf) + # >8 v1.0 self._build_tree(conf) def _get_config_dict_from_user(self, config_file: str | Path | dict) -> dict: @@ -89,6 +91,7 @@ def _get_yaml_enhancement_dict(self, config_file: str | Path) -> dict: LOG.debug(f"Adding enhancement configuration from file: {config_file}") return enhancement_section + # 8< v1.0 def _ensure_compat(self, config_dict: dict) -> None: for enh_name, enh_config in config_dict.items(): if "sensor" in enh_config: @@ -101,6 +104,7 @@ def _ensure_compat(self, config_dict: dict) -> None: stacklevel=3 ) inst_utils.set_instruments_attr(config_dict[enh_name], enh_config["sensor"]) + # >8 v1.0 def find_match(self, **query_dict): """Find a match.""" From 30c9926e245e8f26e6fb9ec32837467a92b238ce Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Fri, 22 May 2026 08:18:45 +0000 Subject: [PATCH 59/72] Wrap deprecation warning in v1.0 scissors --- satpy/readers/core/yaml_reader.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/satpy/readers/core/yaml_reader.py b/satpy/readers/core/yaml_reader.py index cb6904be4c..9adbcf9b93 100644 --- a/satpy/readers/core/yaml_reader.py +++ b/satpy/readers/core/yaml_reader.py @@ -148,6 +148,7 @@ def __init__(self, config_dict): filetype_info["file_patterns"] = file_patterns self.file_patterns.extend(file_patterns) + # 8< v1.0 if "sensors" in self.info: warnings.warn( "Renaming the 'sensors' reader attribute to 'instruments'. " @@ -158,6 +159,7 @@ def __init__(self, config_dict): stacklevel=3 ) self.info["instruments"] = self.info["sensors"] + # >8 v1.0 if "instruments" in self.info and not isinstance(self.info["instruments"], (list, tuple)): self.info["instruments"] = [self.info["instruments"]] self.datasets = self.config.get("datasets", {}) From 8afd9f254fde1eda3b180b7ba744df2f326462cc Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Fri, 22 May 2026 08:28:07 +0000 Subject: [PATCH 60/72] Wrap deprecation warning with v1.0 scissors --- satpy/_instruments.py | 10 +++++++--- satpy/tests/test_instruments.py | 3 +++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/satpy/_instruments.py b/satpy/_instruments.py index 8696e53a33..a0b6aa3b8b 100644 --- a/satpy/_instruments.py +++ b/satpy/_instruments.py @@ -31,15 +31,18 @@ def get_instruments_from_attrs(attrs: dict[str,Any], to_internal: bool=False) -> removed once all file handlers provide instruments as a set. """ - legacy = attrs.get("sensor", set()) - instruments = attrs.get("instruments", legacy) - if legacy: + instruments = attrs.get("instruments", set()) + # 8< v1.0 + sensor = attrs.get("sensor", set()) + if sensor: warnings.warn( "Satpy will ignore the 'sensor' attribute as of v1.1. " "Use the 'instruments' attribute instead.", DeprecationWarning, stacklevel=2 ) + if not instruments: + instruments = sensor if isinstance(instruments, str): warnings.warn( "Converting 'instruments' attribute from string to set. " @@ -49,6 +52,7 @@ def get_instruments_from_attrs(attrs: dict[str,Any], to_internal: bool=False) -> stacklevel=2 ) instruments = set([instruments]) + # >8 v1.0 if to_internal: return { wmo_to_internal(inst) for inst in instruments diff --git a/satpy/tests/test_instruments.py b/satpy/tests/test_instruments.py index abed85dbf2..91008c0023 100644 --- a/satpy/tests/test_instruments.py +++ b/satpy/tests/test_instruments.py @@ -33,6 +33,8 @@ def test_get_instruments_from_attrs(attrs, to_internal, expected): """Test getting instruments from dataset attributes.""" assert inst_utils.get_instruments_from_attrs(attrs, to_internal) == expected + +# 8< v1.0 @pytest.mark.parametrize( ("attrs", "expected"), [ @@ -45,6 +47,7 @@ def test_get_instruments_from_attrs_with_warning(attrs, expected): """Test deprecation warnings when getting instruments.""" with pytest.warns(DeprecationWarning, match="v1.1"): assert inst_utils.get_instruments_from_attrs(attrs) == expected +# >8 v1.0 @pytest.mark.parametrize( From bd2a3555c9c51e18a610b4ea6dca28a49d405e19 Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Fri, 22 May 2026 08:30:29 +0000 Subject: [PATCH 61/72] Change scissor version to 1.1 --- satpy/_instruments.py | 4 ++-- satpy/tests/test_instruments.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/satpy/_instruments.py b/satpy/_instruments.py index a0b6aa3b8b..5526510d07 100644 --- a/satpy/_instruments.py +++ b/satpy/_instruments.py @@ -32,7 +32,7 @@ def get_instruments_from_attrs(attrs: dict[str,Any], to_internal: bool=False) -> set. """ instruments = attrs.get("instruments", set()) - # 8< v1.0 + # 8< v1.1 sensor = attrs.get("sensor", set()) if sensor: warnings.warn( @@ -52,7 +52,7 @@ def get_instruments_from_attrs(attrs: dict[str,Any], to_internal: bool=False) -> stacklevel=2 ) instruments = set([instruments]) - # >8 v1.0 + # >8 v1.1 if to_internal: return { wmo_to_internal(inst) for inst in instruments diff --git a/satpy/tests/test_instruments.py b/satpy/tests/test_instruments.py index 91008c0023..5a724590f4 100644 --- a/satpy/tests/test_instruments.py +++ b/satpy/tests/test_instruments.py @@ -34,7 +34,7 @@ def test_get_instruments_from_attrs(attrs, to_internal, expected): assert inst_utils.get_instruments_from_attrs(attrs, to_internal) == expected -# 8< v1.0 +# 8< v1.1 @pytest.mark.parametrize( ("attrs", "expected"), [ @@ -47,7 +47,7 @@ def test_get_instruments_from_attrs_with_warning(attrs, expected): """Test deprecation warnings when getting instruments.""" with pytest.warns(DeprecationWarning, match="v1.1"): assert inst_utils.get_instruments_from_attrs(attrs) == expected -# >8 v1.0 +# >8 v1.1 @pytest.mark.parametrize( From 47bfb3d611996e4329ff36d96bdafbe4695762c9 Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Fri, 22 May 2026 08:32:25 +0000 Subject: [PATCH 62/72] Change scissor version to 1.1 --- satpy/enhancements/enhancer.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/satpy/enhancements/enhancer.py b/satpy/enhancements/enhancer.py index fb29c04a16..bab850157f 100644 --- a/satpy/enhancements/enhancer.py +++ b/satpy/enhancements/enhancer.py @@ -58,9 +58,9 @@ def add_config_to_tree(self, *decision_dict: str | Path | dict) -> None: for config_file in decision_dict: config_dict = self._get_config_dict_from_user(config_file) recursive_dict_update(conf, config_dict) - # 8< v1.0 + # 8< v1.1 self._ensure_compat(conf) - # >8 v1.0 + # >8 v1.1 self._build_tree(conf) def _get_config_dict_from_user(self, config_file: str | Path | dict) -> dict: @@ -91,7 +91,7 @@ def _get_yaml_enhancement_dict(self, config_file: str | Path) -> dict: LOG.debug(f"Adding enhancement configuration from file: {config_file}") return enhancement_section - # 8< v1.0 + # 8< v1.1 def _ensure_compat(self, config_dict: dict) -> None: for enh_name, enh_config in config_dict.items(): if "sensor" in enh_config: @@ -104,7 +104,7 @@ def _ensure_compat(self, config_dict: dict) -> None: stacklevel=3 ) inst_utils.set_instruments_attr(config_dict[enh_name], enh_config["sensor"]) - # >8 v1.0 + # >8 v1.1 def find_match(self, **query_dict): """Find a match.""" From 289116dd87829cbcf0539b287e0e46ebd8185246 Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Fri, 22 May 2026 08:34:29 +0000 Subject: [PATCH 63/72] Change scissor version to 1.1 --- satpy/readers/core/yaml_reader.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/satpy/readers/core/yaml_reader.py b/satpy/readers/core/yaml_reader.py index 9adbcf9b93..7f3fa8a3ac 100644 --- a/satpy/readers/core/yaml_reader.py +++ b/satpy/readers/core/yaml_reader.py @@ -148,7 +148,7 @@ def __init__(self, config_dict): filetype_info["file_patterns"] = file_patterns self.file_patterns.extend(file_patterns) - # 8< v1.0 + # 8< v1.1 if "sensors" in self.info: warnings.warn( "Renaming the 'sensors' reader attribute to 'instruments'. " @@ -159,7 +159,7 @@ def __init__(self, config_dict): stacklevel=3 ) self.info["instruments"] = self.info["sensors"] - # >8 v1.0 + # >8 v1.1 if "instruments" in self.info and not isinstance(self.info["instruments"], (list, tuple)): self.info["instruments"] = [self.info["instruments"]] self.datasets = self.config.get("datasets", {}) From e0e76de83333f5b009878fed4cef00077f5ed19c Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Fri, 22 May 2026 09:55:03 +0000 Subject: [PATCH 64/72] Add config switch for legacy sensor attribute --- doc/source/reading.rst | 8 +++--- satpy/_config.py | 3 ++- satpy/scene.py | 21 ++++++++++++++++ satpy/tests/scene_tests/test_data_access.py | 27 +++++++++++++++++++++ 4 files changed, 55 insertions(+), 4 deletions(-) diff --git a/doc/source/reading.rst b/doc/source/reading.rst index 288bb3d6d4..de375ec6dc 100644 --- a/doc/source/reading.rst +++ b/doc/source/reading.rst @@ -222,9 +222,11 @@ time etc. The following attributes are standardized across all readers: :class:`~pyresample.geometry.SwathDefinition` if data is geolocated. Areas are used for gridded projected data and Swaths when data must be described by individual longitude/latitude coordinates. See the Coordinates section below. -* ``sensor``: The name of the sensor that recorded the data. For full support through Satpy this - should be all lowercase. If the dataset is the result of observations from multiple sensors a - ``set`` object can be used to specify more than one sensor name. +* ``instruments`` (previously ``sensor`` in Satpy < v1.0): Names of instruments that recorded the + data, stored in a ``set`` object. Starting with Satpy v1.0 the instrument names follow WMO OSCAR + naming conventions. To facilitate the transision, you can get the ``sensor`` attribute back by + setting ``satpy.config.set(legacy_sensor_attribute=True)``. This config switch will be removed + in Satpy v1.1. * ``reader``: The name of the Satpy reader that produced the dataset. * ``orbital_parameters``: Dictionary of orbital parameters describing the satellite's position. See the :ref:`orbital_parameters` section below for more information. diff --git a/satpy/_config.py b/satpy/_config.py index 0b2e6a57c0..b55a89bd18 100644 --- a/satpy/_config.py +++ b/satpy/_config.py @@ -54,7 +54,8 @@ "readers": { "clip_negative_radiances": False, }, - "instruments_key": "sensor" + "instruments_key": "sensor", + "legacy_sensor_attribute": False } # Satpy main configuration object diff --git a/satpy/scene.py b/satpy/scene.py index e26f2e363f..5fb0d02fcd 100644 --- a/satpy/scene.py +++ b/satpy/scene.py @@ -29,6 +29,7 @@ from pyresample.geometry import AreaDefinition, BaseDefinition, CoordinateDefinition, SwathDefinition from xarray import DataArray +import satpy import satpy._instruments as inst_utils from satpy.area import get_area_def from satpy.composites.config_loader import load_compositor_configs_for_sensors @@ -841,8 +842,28 @@ def __getitem__(self, key): """Get a dataset or create a new 'slice' of the Scene.""" if isinstance(key, tuple): return self.slice(key) + # 8< v1.1 + data_array = self._datasets[key] + if self._should_add_legacy_sensor_attribute(data_array): + self._set_legacy_sensor_attribute(data_array) + return data_array + # >8 v1.1 return self._datasets[key] + # 8< v1.1 + def _should_add_legacy_sensor_attribute(self, data_array: xr.DataArray) -> bool: + instruments = inst_utils.get_instruments_from_attrs(data_array.attrs) + return bool(instruments) and satpy.config.get("legacy_sensor_attribute") + + def _set_legacy_sensor_attribute(self, data_array: xr.DataArray) -> None: + instruments = inst_utils.get_instruments_from_attrs(data_array.attrs) + if len(instruments) == 1: + # In satpy < v1.0 single sensors are provided as string + data_array.attrs["sensor"] = inst_utils.wmo_to_internal(list(instruments)[0]) + else: + data_array.attrs["sensor"] = {inst_utils.wmo_to_internal(inst) for inst in instruments} + # >8 v1.1 + def __setitem__(self, key, value): """Add the item to the scene.""" self._datasets[key] = value diff --git a/satpy/tests/scene_tests/test_data_access.py b/satpy/tests/scene_tests/test_data_access.py index 60ada6c78b..6e4cdb8baf 100644 --- a/satpy/tests/scene_tests/test_data_access.py +++ b/satpy/tests/scene_tests/test_data_access.py @@ -21,6 +21,7 @@ import xarray as xr from dask import array as da +import satpy from satpy import Scene from satpy.dataset.dataid import default_id_keys_config from satpy.tests.utils import FAKE_FILEHANDLER_END, FAKE_FILEHANDLER_START, make_cid, make_dataid @@ -393,3 +394,29 @@ def test_chunk_pass_through(self): scene.load(["ds1"]) scene = scene.chunk(chunks=2) assert scene["ds1"].data.chunksize == (2, 2) + + +class TestLegacySensorAttribute: + """Tests for legacy sensor attribute.""" + + @pytest.mark.parametrize( + ("instruments", "expected"), + [ + ({"inst1"}, "inst1"), + ({"inst1", "inst2"}, {"inst1", "inst2"}) + ] + ) + def test_getting_dataset_with_sensor_attribute(self, instruments, expected): + """Test getting dataset with sensor attribute.""" + scene = Scene() + scene["ds"] = xr.DataArray(attrs={"instruments": instruments}) + assert "sensor" not in scene["ds"].attrs + with satpy.config.set(legacy_sensor_attribute=True): + assert scene["ds"].attrs["sensor"] == expected + + def test_no_instruments_no_sensor(self): + """Test setting sensor only if instruments are present.""" + scene = Scene() + scene["ds"] = xr.DataArray() + with satpy.config.set(legacy_sensor_attribute=True): + assert "sensor" not in scene["ds"].attrs From f6a5fee34eb59b47b719cb36afb7acc9ce6689d3 Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Fri, 22 May 2026 10:15:58 +0000 Subject: [PATCH 65/72] Update documentation --- doc/source/reading.rst | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/doc/source/reading.rst b/doc/source/reading.rst index de375ec6dc..a7061a395f 100644 --- a/doc/source/reading.rst +++ b/doc/source/reading.rst @@ -222,11 +222,27 @@ time etc. The following attributes are standardized across all readers: :class:`~pyresample.geometry.SwathDefinition` if data is geolocated. Areas are used for gridded projected data and Swaths when data must be described by individual longitude/latitude coordinates. See the Coordinates section below. -* ``instruments`` (previously ``sensor`` in Satpy < v1.0): Names of instruments that recorded the - data, stored in a ``set`` object. Starting with Satpy v1.0 the instrument names follow WMO OSCAR - naming conventions. To facilitate the transision, you can get the ``sensor`` attribute back by - setting ``satpy.config.set(legacy_sensor_attribute=True)``. This config switch will be removed - in Satpy v1.1. +* ``sensor``: The name of the sensor that recorded the data. For full support through Satpy this + should be all lowercase. If the dataset is the result of observations from multiple sensors a + ``set`` object can be used to specify more than one sensor name. + + .. versionremoved:: 1.0 + + The ``sensor`` attribute has been replaced by ``instruments`` in Satpy v1.0. During + a transition phase the attribute can be restored by setting + + .. code-block:: python + + import satpy + satpy.config.set(legacy_sensor_attribute=True) + + This option will be removed in Satpy v1.1. + +* ``instruments``: Names of instruments that recorded the data, stored in a ``set`` object. + Instrument names follow the WMO OSCAR naming conventions. + + .. versionadded:: 1.0 + * ``reader``: The name of the Satpy reader that produced the dataset. * ``orbital_parameters``: Dictionary of orbital parameters describing the satellite's position. See the :ref:`orbital_parameters` section below for more information. From 02c455a1abc7ea94d0b27eabbd8915f82a3a312d Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Fri, 22 May 2026 12:26:59 +0000 Subject: [PATCH 66/72] Change scissors again --- satpy/_instruments.py | 8 ++++---- satpy/tests/test_instruments.py | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/satpy/_instruments.py b/satpy/_instruments.py index 5526510d07..2c9bf22b96 100644 --- a/satpy/_instruments.py +++ b/satpy/_instruments.py @@ -32,11 +32,11 @@ def get_instruments_from_attrs(attrs: dict[str,Any], to_internal: bool=False) -> set. """ instruments = attrs.get("instruments", set()) - # 8< v1.1 + # 8< v1.0 sensor = attrs.get("sensor", set()) if sensor: warnings.warn( - "Satpy will ignore the 'sensor' attribute as of v1.1. " + "Satpy will ignore the 'sensor' attribute as of v1.0. " "Use the 'instruments' attribute instead.", DeprecationWarning, stacklevel=2 @@ -46,13 +46,13 @@ def get_instruments_from_attrs(attrs: dict[str,Any], to_internal: bool=False) -> if isinstance(instruments, str): warnings.warn( "Converting 'instruments' attribute from string to set. " - "This will result in an error in v1.1, when Satpy will require " + "This will result in an error in v1.0, when Satpy will require " "set type instruments attributes.", DeprecationWarning, stacklevel=2 ) instruments = set([instruments]) - # >8 v1.1 + # >8 v1.0 if to_internal: return { wmo_to_internal(inst) for inst in instruments diff --git a/satpy/tests/test_instruments.py b/satpy/tests/test_instruments.py index 5a724590f4..1229a822c8 100644 --- a/satpy/tests/test_instruments.py +++ b/satpy/tests/test_instruments.py @@ -34,7 +34,7 @@ def test_get_instruments_from_attrs(attrs, to_internal, expected): assert inst_utils.get_instruments_from_attrs(attrs, to_internal) == expected -# 8< v1.1 +# 8< v1.0 @pytest.mark.parametrize( ("attrs", "expected"), [ @@ -45,9 +45,9 @@ def test_get_instruments_from_attrs(attrs, to_internal, expected): ) def test_get_instruments_from_attrs_with_warning(attrs, expected): """Test deprecation warnings when getting instruments.""" - with pytest.warns(DeprecationWarning, match="v1.1"): + with pytest.warns(DeprecationWarning, match="v1.0"): assert inst_utils.get_instruments_from_attrs(attrs) == expected -# >8 v1.1 +# >8 v1.0 @pytest.mark.parametrize( From 5f836eae574177eec0ce90e56bbc963579e4932a Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Fri, 22 May 2026 12:28:21 +0000 Subject: [PATCH 67/72] Change scissors again --- satpy/enhancements/enhancer.py | 10 +++++----- satpy/tests/test_instruments.py | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/satpy/enhancements/enhancer.py b/satpy/enhancements/enhancer.py index bab850157f..0b3c09515b 100644 --- a/satpy/enhancements/enhancer.py +++ b/satpy/enhancements/enhancer.py @@ -58,9 +58,9 @@ def add_config_to_tree(self, *decision_dict: str | Path | dict) -> None: for config_file in decision_dict: config_dict = self._get_config_dict_from_user(config_file) recursive_dict_update(conf, config_dict) - # 8< v1.1 + # 8< v1.0 self._ensure_compat(conf) - # >8 v1.1 + # >8 v1.0 self._build_tree(conf) def _get_config_dict_from_user(self, config_file: str | Path | dict) -> dict: @@ -91,20 +91,20 @@ def _get_yaml_enhancement_dict(self, config_file: str | Path) -> dict: LOG.debug(f"Adding enhancement configuration from file: {config_file}") return enhancement_section - # 8< v1.1 + # 8< v1.0 def _ensure_compat(self, config_dict: dict) -> None: for enh_name, enh_config in config_dict.items(): if "sensor" in enh_config: warnings.warn( "Renaming the 'sensor' enhancement attribute to 'instruments'. " - "This will raise an exception in Satpy v1.1 when the 'sensor' " + "This will raise an exception in Satpy v1.0 when the 'sensor' " "attribute will be removed. To silence this warning, rename " "'sensor' to 'instruments' in your enhancement YAML file.", DeprecationWarning, stacklevel=3 ) inst_utils.set_instruments_attr(config_dict[enh_name], enh_config["sensor"]) - # >8 v1.1 + # >8 v1.0 def find_match(self, **query_dict): """Find a match.""" diff --git a/satpy/tests/test_instruments.py b/satpy/tests/test_instruments.py index abed85dbf2..a4d28f1141 100644 --- a/satpy/tests/test_instruments.py +++ b/satpy/tests/test_instruments.py @@ -43,7 +43,7 @@ def test_get_instruments_from_attrs(attrs, to_internal, expected): ) def test_get_instruments_from_attrs_with_warning(attrs, expected): """Test deprecation warnings when getting instruments.""" - with pytest.warns(DeprecationWarning, match="v1.1"): + with pytest.warns(DeprecationWarning, match="v1.0"): assert inst_utils.get_instruments_from_attrs(attrs) == expected From 8ceabf11180363ec284f79b3b44cd4e8e57dc828 Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Fri, 22 May 2026 12:29:26 +0000 Subject: [PATCH 68/72] Change scissors again --- satpy/readers/core/yaml_reader.py | 6 +++--- satpy/tests/test_instruments.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/satpy/readers/core/yaml_reader.py b/satpy/readers/core/yaml_reader.py index 7f3fa8a3ac..d1ee0042f8 100644 --- a/satpy/readers/core/yaml_reader.py +++ b/satpy/readers/core/yaml_reader.py @@ -148,18 +148,18 @@ def __init__(self, config_dict): filetype_info["file_patterns"] = file_patterns self.file_patterns.extend(file_patterns) - # 8< v1.1 + # 8< v1.0 if "sensors" in self.info: warnings.warn( "Renaming the 'sensors' reader attribute to 'instruments'. " - "This will raise an exception in Satpy v1.1 when the 'sensors' " + "This will raise an exception in Satpy v1.0 when the 'sensors' " "attribute will be removed. To silence this warning, rename " "'sensors' to 'instruments' in your reader YAML file.", DeprecationWarning, stacklevel=3 ) self.info["instruments"] = self.info["sensors"] - # >8 v1.1 + # >8 v1.0 if "instruments" in self.info and not isinstance(self.info["instruments"], (list, tuple)): self.info["instruments"] = [self.info["instruments"]] self.datasets = self.config.get("datasets", {}) diff --git a/satpy/tests/test_instruments.py b/satpy/tests/test_instruments.py index abed85dbf2..a4d28f1141 100644 --- a/satpy/tests/test_instruments.py +++ b/satpy/tests/test_instruments.py @@ -43,7 +43,7 @@ def test_get_instruments_from_attrs(attrs, to_internal, expected): ) def test_get_instruments_from_attrs_with_warning(attrs, expected): """Test deprecation warnings when getting instruments.""" - with pytest.warns(DeprecationWarning, match="v1.1"): + with pytest.warns(DeprecationWarning, match="v1.0"): assert inst_utils.get_instruments_from_attrs(attrs) == expected From c147e098d84d5f371e1d8fcbb462791c957174de Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Fri, 22 May 2026 12:30:14 +0000 Subject: [PATCH 69/72] Change scissors again --- satpy/scene.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/satpy/scene.py b/satpy/scene.py index 5fb0d02fcd..3fa5cd098e 100644 --- a/satpy/scene.py +++ b/satpy/scene.py @@ -842,15 +842,15 @@ def __getitem__(self, key): """Get a dataset or create a new 'slice' of the Scene.""" if isinstance(key, tuple): return self.slice(key) - # 8< v1.1 + # 8< v1.0 data_array = self._datasets[key] if self._should_add_legacy_sensor_attribute(data_array): self._set_legacy_sensor_attribute(data_array) return data_array - # >8 v1.1 + # >8 v1.0 return self._datasets[key] - # 8< v1.1 + # 8< v1.0 def _should_add_legacy_sensor_attribute(self, data_array: xr.DataArray) -> bool: instruments = inst_utils.get_instruments_from_attrs(data_array.attrs) return bool(instruments) and satpy.config.get("legacy_sensor_attribute") @@ -862,7 +862,7 @@ def _set_legacy_sensor_attribute(self, data_array: xr.DataArray) -> None: data_array.attrs["sensor"] = inst_utils.wmo_to_internal(list(instruments)[0]) else: data_array.attrs["sensor"] = {inst_utils.wmo_to_internal(inst) for inst in instruments} - # >8 v1.1 + # >8 v1.0 def __setitem__(self, key, value): """Add the item to the scene.""" From e9e3e9f3b2c7a19fdc30b8f122885572ec53f6f4 Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Fri, 22 May 2026 12:40:02 +0000 Subject: [PATCH 70/72] Remove config switch --- doc/source/reading.rst | 14 +++----------- satpy/_config.py | 1 - satpy/scene.py | 13 ++++--------- satpy/tests/scene_tests/test_data_access.py | 16 ++++------------ 4 files changed, 11 insertions(+), 33 deletions(-) diff --git a/doc/source/reading.rst b/doc/source/reading.rst index a7061a395f..ac2e6718b2 100644 --- a/doc/source/reading.rst +++ b/doc/source/reading.rst @@ -226,22 +226,14 @@ time etc. The following attributes are standardized across all readers: should be all lowercase. If the dataset is the result of observations from multiple sensors a ``set`` object can be used to specify more than one sensor name. - .. versionremoved:: 1.0 + .. deprecated:: 0.61.0 - The ``sensor`` attribute has been replaced by ``instruments`` in Satpy v1.0. During - a transition phase the attribute can be restored by setting - - .. code-block:: python - - import satpy - satpy.config.set(legacy_sensor_attribute=True) - - This option will be removed in Satpy v1.1. + The ``sensor`` attribute will be replaced by ``instruments`` in Satpy v1.0. * ``instruments``: Names of instruments that recorded the data, stored in a ``set`` object. Instrument names follow the WMO OSCAR naming conventions. - .. versionadded:: 1.0 + .. versionadded:: 0.61.0 * ``reader``: The name of the Satpy reader that produced the dataset. * ``orbital_parameters``: Dictionary of orbital parameters describing the satellite's position. diff --git a/satpy/_config.py b/satpy/_config.py index b55a89bd18..8ed4dbf93b 100644 --- a/satpy/_config.py +++ b/satpy/_config.py @@ -55,7 +55,6 @@ "clip_negative_radiances": False, }, "instruments_key": "sensor", - "legacy_sensor_attribute": False } # Satpy main configuration object diff --git a/satpy/scene.py b/satpy/scene.py index 3fa5cd098e..ac0e808ed4 100644 --- a/satpy/scene.py +++ b/satpy/scene.py @@ -29,7 +29,6 @@ from pyresample.geometry import AreaDefinition, BaseDefinition, CoordinateDefinition, SwathDefinition from xarray import DataArray -import satpy import satpy._instruments as inst_utils from satpy.area import get_area_def from satpy.composites.config_loader import load_compositor_configs_for_sensors @@ -844,19 +843,15 @@ def __getitem__(self, key): return self.slice(key) # 8< v1.0 data_array = self._datasets[key] - if self._should_add_legacy_sensor_attribute(data_array): - self._set_legacy_sensor_attribute(data_array) + instruments = inst_utils.get_instruments_from_attrs(data_array.attrs) + if instruments: + self._set_legacy_sensor_attribute(data_array, instruments) return data_array # >8 v1.0 return self._datasets[key] # 8< v1.0 - def _should_add_legacy_sensor_attribute(self, data_array: xr.DataArray) -> bool: - instruments = inst_utils.get_instruments_from_attrs(data_array.attrs) - return bool(instruments) and satpy.config.get("legacy_sensor_attribute") - - def _set_legacy_sensor_attribute(self, data_array: xr.DataArray) -> None: - instruments = inst_utils.get_instruments_from_attrs(data_array.attrs) + def _set_legacy_sensor_attribute(self, data_array: xr.DataArray, instruments: set[str]) -> None: if len(instruments) == 1: # In satpy < v1.0 single sensors are provided as string data_array.attrs["sensor"] = inst_utils.wmo_to_internal(list(instruments)[0]) diff --git a/satpy/tests/scene_tests/test_data_access.py b/satpy/tests/scene_tests/test_data_access.py index 6e4cdb8baf..3a620c3455 100644 --- a/satpy/tests/scene_tests/test_data_access.py +++ b/satpy/tests/scene_tests/test_data_access.py @@ -21,7 +21,6 @@ import xarray as xr from dask import array as da -import satpy from satpy import Scene from satpy.dataset.dataid import default_id_keys_config from satpy.tests.utils import FAKE_FILEHANDLER_END, FAKE_FILEHANDLER_START, make_cid, make_dataid @@ -409,14 +408,7 @@ class TestLegacySensorAttribute: def test_getting_dataset_with_sensor_attribute(self, instruments, expected): """Test getting dataset with sensor attribute.""" scene = Scene() - scene["ds"] = xr.DataArray(attrs={"instruments": instruments}) - assert "sensor" not in scene["ds"].attrs - with satpy.config.set(legacy_sensor_attribute=True): - assert scene["ds"].attrs["sensor"] == expected - - def test_no_instruments_no_sensor(self): - """Test setting sensor only if instruments are present.""" - scene = Scene() - scene["ds"] = xr.DataArray() - with satpy.config.set(legacy_sensor_attribute=True): - assert "sensor" not in scene["ds"].attrs + scene["ds1"] = xr.DataArray(attrs={"instruments": instruments}) + scene["ds2"] = xr.DataArray() + assert scene["ds1"].attrs["sensor"] == expected + assert "sensor" not in scene["ds2"].attrs From 831938d4ca409468722985f0d029fe1fa77c21bd Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Fri, 22 May 2026 13:22:26 +0000 Subject: [PATCH 71/72] Fix exceptions in sensor compatibility --- satpy/scene.py | 24 ++++++++++++---- satpy/tests/scene_tests/test_data_access.py | 32 +++++++++++++++------ 2 files changed, 41 insertions(+), 15 deletions(-) diff --git a/satpy/scene.py b/satpy/scene.py index ac0e808ed4..796f72fd49 100644 --- a/satpy/scene.py +++ b/satpy/scene.py @@ -843,20 +843,32 @@ def __getitem__(self, key): return self.slice(key) # 8< v1.0 data_array = self._datasets[key] - instruments = inst_utils.get_instruments_from_attrs(data_array.attrs) - if instruments: - self._set_legacy_sensor_attribute(data_array, instruments) + if self._should_add_sensor_attribute(data_array): + self._set_sensor_attribute(data_array) return data_array # >8 v1.0 return self._datasets[key] # 8< v1.0 - def _set_legacy_sensor_attribute(self, data_array: xr.DataArray, instruments: set[str]) -> None: - if len(instruments) == 1: - # In satpy < v1.0 single sensors are provided as string + def _should_add_sensor_attribute(self, data_array: xr.DataArray) -> bool: + return "instruments" in data_array.attrs and "sensor" not in data_array.attrs + + def _set_sensor_attribute(self, data_array: xr.DataArray) -> None: + instruments = data_array.attrs["instruments"] + if self._should_convert_to_string(instruments): data_array.attrs["sensor"] = inst_utils.wmo_to_internal(list(instruments)[0]) else: data_array.attrs["sensor"] = {inst_utils.wmo_to_internal(inst) for inst in instruments} + + def _should_convert_to_string(self, instruments): + # In satpy < v1.0 single sensors are provided as string by most readers + has_one_element = len(instruments) == 1 + readers_providing_set = [ + "seadas_l2", + "oci_l2_bgc" + ] + is_exception = any([reader in self._readers for reader in readers_providing_set]) + return has_one_element and not is_exception # >8 v1.0 def __setitem__(self, key, value): diff --git a/satpy/tests/scene_tests/test_data_access.py b/satpy/tests/scene_tests/test_data_access.py index 3a620c3455..f2463abf54 100644 --- a/satpy/tests/scene_tests/test_data_access.py +++ b/satpy/tests/scene_tests/test_data_access.py @@ -395,20 +395,34 @@ def test_chunk_pass_through(self): assert scene["ds1"].data.chunksize == (2, 2) +# 8< v1.0 class TestLegacySensorAttribute: """Tests for legacy sensor attribute.""" @pytest.mark.parametrize( - ("instruments", "expected"), + ("reader", "expected"), [ - ({"inst1"}, "inst1"), - ({"inst1", "inst2"}, {"inst1", "inst2"}) + ("most-readers", "inst1"), + ("seadas_l2", {"inst1"}), + ("oci_l2_bgc", {"inst1"}), ] ) - def test_getting_dataset_with_sensor_attribute(self, instruments, expected): - """Test getting dataset with sensor attribute.""" + def test_set_single_sensor(self, reader, expected): + """Test setting sensor attribute with a single sensor.""" scene = Scene() - scene["ds1"] = xr.DataArray(attrs={"instruments": instruments}) - scene["ds2"] = xr.DataArray() - assert scene["ds1"].attrs["sensor"] == expected - assert "sensor" not in scene["ds2"].attrs + scene._readers[reader] = "dummy" + scene["ds"] = xr.DataArray(attrs={"instruments": {"inst1"}}) + assert scene["ds"].attrs["sensor"] == expected + + def test_set_multi_sensor(self): + """Test setting sensor attribute with multiple sensors.""" + scene = Scene() + scene["ds"] = xr.DataArray(attrs={"instruments": {"inst1", "inst2"}}) + assert scene["ds"].attrs["sensor"] == {"inst1", "inst2"} + + def test_set_no_sensor(self): + """Test sensor attribute is set only if instruments present.""" + scene = Scene() + scene["ds"] = xr.DataArray() + assert "sensor" not in scene["ds"].attrs +# >8 v1.0 From da4b46018de59d2b150260b505276ee4d4ef5a29 Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Fri, 22 May 2026 14:02:46 +0000 Subject: [PATCH 72/72] Update documentation --- doc/source/dev_guide/custom_reader.rst | 2 +- doc/source/dev_guide/xarray_migration.rst | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/source/dev_guide/custom_reader.rst b/doc/source/dev_guide/custom_reader.rst index 0475e07144..0098e14f13 100644 --- a/doc/source/dev_guide/custom_reader.rst +++ b/doc/source/dev_guide/custom_reader.rst @@ -494,7 +494,7 @@ needs to implement a few methods: successful, containing the data and :ref:`metadata ` of the loaded dataset, or return None if the loading was unsuccessful. If the reader is reading satellite data the returned xarray.DataArrays should also have the - attributes ``platform_name`` and ``sensor`` with names according to https://space.oscar.wmo.int/. + attributes ``platform_name`` and ``instruments`` with names according to https://space.oscar.wmo.int/. The DataArray should at least have a ``y`` dimension. For data covering a 2D region on the Earth, their should be at least a ``y`` and ``x`` diff --git a/doc/source/dev_guide/xarray_migration.rst b/doc/source/dev_guide/xarray_migration.rst index 1a3b02f61e..6bd2d1d248 100644 --- a/doc/source/dev_guide/xarray_migration.rst +++ b/doc/source/dev_guide/xarray_migration.rst @@ -35,7 +35,7 @@ To create such an array, you can do for example my_dataarray = xr.DataArray(my_data, dims=['y', 'x'], coords={'x': np.arange(...)}, - attrs={'sensor': 'olci'}) + attrs={'instruments': {'OLCI'}) where ``my_data`` can be a regular numpy array, a numpy memmap, or, if you want to keep things lazy, a dask array (more on dask later). Satpy uses dask @@ -105,7 +105,7 @@ Some metadata that should always be present in our dataarrays: - ``area`` the area of the dataset. This should be handled in the reader. - ``start_time``, ``end_time`` -- ``sensor`` +- ``instruments`` Operations on DataArrays ************************