Skip to content
Open
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions doc/source/enhancements.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -95,7 +97,7 @@ implementation depends on the following keys:
1. ``name``
2. ``reader``
3. ``platform_name``
4. ``sensor``
4. ``instruments`` (previously ``sensor`` in Satpy <1.0)
5. ``standard_name``
6. ``units``

Expand Down
19 changes: 16 additions & 3 deletions satpy/decision_tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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])
Comment thread
sfinkens marked this conversation as resolved.
Outdated
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
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't remember how this worked in the old code so I don't understand how this works now, but if you say it works then 👍

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've updated the docstring to make it more clear.

query_vals += query_val
return query_vals

Expand Down
32 changes: 29 additions & 3 deletions satpy/enhancements/enhancer.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from __future__ import annotations

import os
import warnings
from pathlib import Path

import yaml
Expand All @@ -42,12 +43,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)

Expand All @@ -57,6 +58,7 @@ 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)
self._ensure_compat(conf)
self._build_tree(conf)

def _get_config_dict_from_user(self, config_file: str | Path | dict) -> dict:
Expand Down Expand Up @@ -87,6 +89,19 @@ 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' enhancement attribute to 'instruments'. "
"This will raise an exception in Satpy v1.1 when the 'sensor' "
"attribute will be removed. To silence this warning, rename "
"'sensor' to 'instruments' in your enhancement YAML file.",
DeprecationWarning,
stacklevel=3
)
instru.set_instruments_attr(config_dict[enh_name], enh_config["sensor"])
Comment thread
sfinkens marked this conversation as resolved.
Outdated

def find_match(self, **query_dict):
"""Find a match."""
try:
Expand Down Expand Up @@ -213,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)
Comment thread
sfinkens marked this conversation as resolved.
Outdated

if overlay is not None:
from satpy.enhancements.overlays import add_overlay
Expand Down
24 changes: 12 additions & 12 deletions satpy/etc/enhancements/abi.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
enhancements:
cimss_true_color:
standard_name: cimss_true_color
sensor: abi
instruments: [abi]
operations:
- name: linear_stretch
method: !!python/name:satpy.enhancements.contrast.stretch
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
instruments: [abi]
operations:
- name: stretch
method: !!python/name:satpy.enhancements.contrast.stretch
Expand All @@ -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
instruments: [abi]
operations:
- name: stretch
method: !!python/name:satpy.enhancements.contrast.stretch
Expand All @@ -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
instruments: [abi]
operations:
- name: stretch
method: !!python/name:satpy.enhancements.contrast.stretch
Expand Down Expand Up @@ -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
instruments: [abi]
operations:
- name: stretch
method: !!python/name:satpy.enhancements.contrast.stretch
Expand All @@ -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
instruments: [abi]
operations:
- name: stretch
method: !!python/name:satpy.enhancements.contrast.stretch
Expand All @@ -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
instruments: [abi]
operations:
- name: stretch
method: !!python/name:satpy.enhancements.contrast.stretch
Expand All @@ -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
instruments: [abi]
operations:
- name: stretch
method: !!python/name:satpy.enhancements.contrast.stretch
Expand All @@ -191,7 +191,7 @@ enhancements:
# IR with white clouds
highlighted_brightness_temperature:
standard_name: highlighted_toa_brightness_temperature
sensor: abi
instruments: [abi]
operations:
- name: btemp_threshold
method: !!python/name:satpy.enhancements.contrast.btemp_threshold
Expand Down Expand Up @@ -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
instruments: [abi]
operations:
- name: stretch
method: !!python/name:satpy.enhancements.contrast.stretch
Expand All @@ -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
instruments: [abi]
operations:
- name: stretch
method: !!python/name:satpy.enhancements.contrast.stretch
Expand Down
4 changes: 2 additions & 2 deletions satpy/etc/enhancements/ahi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ enhancements:

day_severe_storms:
standard_name: day_severe_storms
sensor: ahi
instruments: [ahi]
operations:
- name: stretch
method: !!python/name:satpy.enhancements.contrast.stretch
Expand All @@ -27,7 +27,7 @@ enhancements:

night_microphysics_tropical:
standard_name: night_microphysics_tropical
sensor: ahi
instruments: [ahi]
operations:
- name: stretch
method: !!python/name:satpy.enhancements.contrast.stretch
Expand Down
24 changes: 12 additions & 12 deletions satpy/etc/enhancements/amsr2.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,85 +3,85 @@ enhancements:
# https://www.ospo.noaa.gov/Products/atmosphere/gpds/maps.html?GPRR#gpdsMaps
gaasp_clw:
name: CLW
sensor: 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
sensor: 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
sensor: 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
sensor: 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}
# Snow_Cover unscaled (category product)
gaasp_snow_depth:
name: Snow_Depth
sensor: 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
sensor: 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
sensor: 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
sensor: 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
sensor: 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
# sensor: 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
# sensor: 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
sensor: amsr2
instruments: [amsr2]
operations:
- name: linear_stretch
method: !!python/name:satpy.enhancements.contrast.stretch
Expand Down
Loading
Loading