diff --git a/satpy/_config.py b/satpy/_config.py index 47012742d6..939396b93e 100644 --- a/satpy/_config.py +++ b/satpy/_config.py @@ -54,6 +54,9 @@ "readers": { "clip_negative_radiances": False, }, + # 8< v1.0 + "legacy_sensor_attribute": True + # >8 v1.0 } # Satpy main configuration object diff --git a/satpy/composites/aux_data.py b/satpy/composites/aux_data.py index 3179f826a5..2194e2238b 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["_satpy_sensor"] = None 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 1b124eb88a..70c12642fa 100644 --- a/satpy/composites/core.py +++ b/satpy/composites/core.py @@ -436,7 +436,7 @@ def _concat_datasets(self, projectables, mode): def _get_sensors(self, projectables): sensor = set() for projectable in projectables: - current_sensor = projectable.attrs.get("sensor", None) + current_sensor = projectable.attrs.get("_satpy_sensor", None) if current_sensor: if isinstance(current_sensor, (str, bytes)): sensor.add(current_sensor) @@ -512,7 +512,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["_satpy_sensor"] = 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..123e57e7a9 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("_satpy_sensor") is None: # sensor can be a set - attrs["sensor"] = self._get_sensors([foreground, background]) + attrs["_satpy_sensor"] = self._get_sensors([foreground, background]) return attrs @staticmethod diff --git a/satpy/composites/glm.py b/satpy/composites/glm.py index 866e952698..527c5acc40 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, + "_satpy_sensor": new_sensors, }) def __call__(self, projectables, optional_datasets=None, **attrs): diff --git a/satpy/dependency_tree.py b/satpy/dependency_tree.py index 97777a71e9..38d2773967 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["_satpy_sensor"] = 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 b196c63e28..8bf63377be 100644 --- a/satpy/enhancements/enhancer.py +++ b/satpy/enhancements/enhancer.py @@ -38,12 +38,12 @@ def __init__(self, *decision_dicts, **kwargs): ("name", "reader", "platform_name", - "sensor", + "_satpy_sensor", "standard_name", "units", )) self.prefix = kwargs.pop("config_section", "enhancements") - multival_keys = kwargs.pop("multival_keys", ["sensor"]) + multival_keys = kwargs.pop("multival_keys", ["_satpy_sensor"]) super(EnhancementDecisionTree, self).__init__( decision_dicts, match_keys, multival_keys) @@ -209,8 +209,8 @@ 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"]) + if dataset.attrs.get("_satpy_sensor", None): + enhancer.add_sensor_enhancements(dataset.attrs["_satpy_sensor"]) enhancer.apply(img, **dataset.attrs) diff --git a/satpy/modifiers/_crefl_utils.py b/satpy/modifiers/_crefl_utils.py index b8a1d52a4b..84a013876a 100644 --- a/satpy/modifiers/_crefl_utils.py +++ b/satpy/modifiers/_crefl_utils.py @@ -282,7 +282,7 @@ 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"]) + runner_cls = _runner_class_for_sensor(refl.attrs["_satpy_sensor"]) runner = runner_cls(refl) corr_refl = runner(sensor_azimuth, sensor_zenith, solar_azimuth, solar_zenith, avg_elevation) return corr_refl @@ -349,7 +349,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): return da.map_blocks(_run_crefl, self._refl.data, mus.data, muv.data, phi.data, - height, self._refl.attrs.get("sensor"), *coeffs, + height, self._refl.attrs.get("_satpy_sensor"), *coeffs, meta=np.ndarray((), dtype=self._refl.dtype), chunks=self._refl.chunks, dtype=self._refl.dtype, ) diff --git a/satpy/modifiers/atmosphere.py b/satpy/modifiers/atmosphere.py index c7144c27ca..284f9968b6 100644 --- a/satpy/modifiers/atmosphere.py +++ b/satpy/modifiers/atmosphere.py @@ -104,7 +104,7 @@ 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"], + corrector = Rayleigh(vis.attrs["platform_name"], vis.attrs["_satpy_sensor"], atmosphere=atmosphere, aerosol_type=aerosol_type) @@ -159,7 +159,7 @@ def __call__(self, projectables, optional_datasets=None, **info): logger.info("Correction for limb cooling") corrector = AtmosphericalCorrection(band.attrs["platform_name"], - band.attrs["sensor"]) + band.attrs["_satpy_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..effeee2db6 100644 --- a/satpy/modifiers/spectral.py +++ b/satpy/modifiers/spectral.py @@ -132,7 +132,7 @@ def _init_reflectance_calculator(self, metadata): 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"], + reflectance_3x_calculator = Calculator(metadata["platform_name"], metadata["_satpy_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..4c5b3c62cb 100644 --- a/satpy/readers/abi_l1b.py +++ b/satpy/readers/abi_l1b.py @@ -73,7 +73,9 @@ def get_dataset(self, key, info): def _adjust_attrs(self, data, key): data.attrs.update({"platform_name": self.platform_name, - "sensor": self.sensor}) + "_satpy_sensor": self.sensor, + "instruments": {self.instrument} + }) # 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..0481fc1118 100644 --- a/satpy/readers/core/abi.py +++ b/satpy/readers/core/abi.py @@ -30,7 +30,7 @@ from satpy._compat import cached_property 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 +from satpy.utils import get_dask_chunk_size_in_bytes, oscar_to_satpy logger = logging.getLogger(__name__) @@ -108,10 +108,15 @@ def _rename_dims(nc): nc = nc.rename({"lon": "x", "lat": "y"}) return nc + @property + def instrument(self): + """Get OSCAR instrument name for current file handler.""" + return "ABI" + @property def sensor(self): - """Get sensor name for current file handler.""" - return "abi" + """Get satpy internal sensor name for current file handler.""" + return oscar_to_satpy(self.instrument) def __getitem__(self, item): """Wrap `self.nc[item]` for better floating point precision. diff --git a/satpy/scene.py b/satpy/scene.py index 8693cc77b4..cbdc855821 100644 --- a/satpy/scene.py +++ b/satpy/scene.py @@ -18,6 +18,7 @@ """Scene object to hold satellite data.""" from __future__ import annotations +import contextlib import logging import os import warnings @@ -29,6 +30,7 @@ from pyresample.geometry import AreaDefinition, BaseDefinition, CoordinateDefinition, SwathDefinition from xarray import DataArray +import satpy from satpy.area import get_area_def from satpy.composites.config_loader import load_compositor_configs_for_sensors from satpy.composites.core import IncompatibleAreas @@ -197,12 +199,12 @@ 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: + if "_satpy_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"]) + if isinstance(data_arr.attrs["_satpy_sensor"], str): + sensor_names.add(data_arr.attrs["_satpy_sensor"]) + elif isinstance(data_arr.attrs["_satpy_sensor"], set): + sensor_names.update(data_arr.attrs["_satpy_sensor"]) return sensor_names @property @@ -828,8 +830,15 @@ def get(self, key, default=None): def __getitem__(self, key): """Get a dataset or create a new 'slice' of the Scene.""" if isinstance(key, tuple): - return self.slice(key) - return self._datasets[key] + res = self.slice(key) + else: + res = self._datasets[key] + # 8< v1.0 + if satpy.config.get("legacy_sensor_attribute"): + with contextlib.suppress(KeyError): + res.attrs["sensor"] = res.attrs["_satpy_sensor"] + # >8 v1.0 + return res def __setitem__(self, key, value): """Add the item to the scene.""" diff --git a/satpy/tests/conftest.py b/satpy/tests/conftest.py index 85ff3f957b..6d5da1e981 100644 --- a/satpy/tests/conftest.py +++ b/satpy/tests/conftest.py @@ -63,3 +63,11 @@ def _forbid_pyspectral_downloads(): with forbid_pyspectral_downloads(): yield + +# 8< v1.0 +@pytest.fixture(scope="session", autouse=True) +def legacy_sensor_attribute(): + """Request legacy sensor attribute.""" + import satpy + satpy.config.set(legacy_sensor_attribute=True) +# >8 v1.0 diff --git a/satpy/tests/reader_tests/test_abi_l1b.py b/satpy/tests/reader_tests/test_abi_l1b.py index bcb6d35041..ba70d520b3 100644 --- a/satpy/tests/reader_tests/test_abi_l1b.py +++ b/satpy/tests/reader_tests/test_abi_l1b.py @@ -371,7 +371,8 @@ def test_get_dataset(self, c01_data_arr): "scan_mode": "M4", "scene_abbr": "C", "scene_id": None, - "sensor": "abi", + "_satpy_sensor": "abi", + "instruments": {"ABI"}, "timeline_ID": None, "suffix": "suffix", "units": "W m-2 um-1 sr-1", diff --git a/satpy/utils.py b/satpy/utils.py index bdf9d77e4c..f56fd8f000 100644 --- a/satpy/utils.py +++ b/satpy/utils.py @@ -940,3 +940,8 @@ def flatten_dict(d, parent_key="", sep="_"): else: items.append((new_key, v)) return dict(items) + + +def oscar_to_satpy(instrument: str) -> str: + """Translate OSCAR instrument name to Satpy naming convention.""" + return instrument.lower().replace("-", "").replace(" ", "_").replace("/", "-")