Source code for eoio.readers.subset.datetime_subset

"""
Future notes:

"""

from __future__ import annotations
from typing import Any, Dict
from functools import wraps

import numpy as np

from eoio.readers.subset.base_subset import BaseSubsetResolver
from processor_tools.utils.formatters import convert_datetime

__all__ = ["DatetimeSubsetResolver"]


class DatetimeSubsetError(ValueError):
    """
    Raised when datetime subset resolution fails.

    :param message:
        Description of the error.
    :returns DatetimeSubsetError:
        Exception indicating invalid datetime subset configuration.
    """


def validate_inputs(func):
    @wraps(func)
    def checker(
        self,
        data,
        variable_params,
        *args,
        **kwargs,
    ):
        # Validate data
        if not hasattr(data, "size"):
            raise DatetimeSubsetError("Data must have a 'size' attribute.")
        if data.size == 0:
            raise DatetimeSubsetError("Data cannot be empty.")

        if not isinstance(data.size, int):
            raise DatetimeSubsetError("Data size must be an integer.")

        # Validate variable_params
        if not isinstance(variable_params, dict):
            raise DatetimeSubsetError("Variable parameters must be provided as a dictionary.")

        # Validate variable_params
        valid_keys = {"min", "max", "nearest"}
        if not any(k in variable_params for k in valid_keys):
            raise DatetimeSubsetError(f"variable_params must contain one of {valid_keys}, got {variable_params.keys()}")

        # Validate tolerance if present
        if "tolerance_hours" in variable_params and not isinstance(variable_params["tolerance_hours"], (int, float)):
            raise DatetimeSubsetError("Tolerance_hours must be numeric.")

        if "tolerance_days" in variable_params and not isinstance(variable_params["tolerance_days"], (int, float)):
            raise DatetimeSubsetError("Tolerance_days must be numeric.")

        if "tolerance_minutes" in variable_params and not isinstance(
            variable_params["tolerance_minutes"], (int, float)
        ):
            raise DatetimeSubsetError("Tolerance_minutes must be numeric.")

        # check if no more than one tolerance is provided
        tolerance_keys = ["tolerance_days", "tolerance_hours", "tolerance_minutes"]
        provided_tolerances = [key for key in tolerance_keys if key in variable_params]

        if len(provided_tolerances) > 1:
            raise DatetimeSubsetError(f"Only one of {provided_tolerances} should be provided.")

        return func(self, data, variable_params, *args, **kwargs)

    return checker


# -----------------------------------------------------------------------------------
[docs] class DatetimeSubsetResolver(BaseSubsetResolver): """ Resolves datetime indices for subsetting raster data. :param data: Datetime data (e.g., xarray DataArray). Data is expected to be an xarray-like object with .values, .size, and .dims. :param datetime_params: Dictionary specifying datetime selection criteria: * {'min': '2020-01-01'} * {'max': '2020-12-31'} * {'nearest': '2020-06-01', 'tolerance': 20} * {'min': '2020-01-01', 'max': '2020-12-31'} :returns DatetimeSubsetResolver: An instance ready to compute wavelength subsets. """ @validate_inputs def __init__( self, data: Any, variable_params: Dict, ): valid_keys = {"min", "max", "nearest"} for key in valid_keys: if key in variable_params: variable_params[key] = convert_datetime(variable_params[key]) if "tolerance_days" in variable_params: variable_params["tolerance"] = np.timedelta64(variable_params["tolerance_days"], "D") if "tolerance_hours" in variable_params: variable_params["tolerance"] = np.timedelta64(variable_params["tolerance_hours"], "h") if "tolerance_minutes" in variable_params: variable_params["tolerance"] = np.timedelta64(variable_params["tolerance_minutes"], "m") data_conv = data.copy() try: data_conv.values = convert_datetime(data.values) except ValueError: # this line makes data.values not timezone aware. From my reasearch it looks like a workaround for a larger bug # with how datetimes are handled by xarray - Ashley data_conv = data_conv.assign_coords(time=convert_datetime(data.values)) super().__init__(data_conv, variable_params)
if __name__ == "__main__": pass