Source code for lsapy.criteria

"""Suitability Criteria definition."""

from __future__ import annotations

from collections.abc import Mapping
from typing import Any

import xarray as xr

import lsapy.core.formatting as fmt
from lsapy.functions import SuitabilityFunction

__all__ = ["SuitabilityCriteria"]


[docs] class SuitabilityCriteria: """ A data structure for suitability criteria. Suitability criteria are used to compute the suitability of a location from an indicator and based on a set of rules defined by a suitability function. The suitability criteria can be weighted and categorized defining how it will be aggregated with other criteria. Parameters ---------- name : str Name of the suitability criteria. indicator : xr.DataArray Indicator on which the criteria is based. func : SuitabilityFunction, optional Suitability function describing how the suitability of the criteria is computed. weight : int | float, optional Weight of the criteria used in the aggregation process if a weighted aggregation method is used. The default is 1. category : str, optional Category of the criteria. The default is None. long_name : str, optional A long name for the criteria. The default is None. If provided, it will be stored as an attribute. description : str, optional A description for the criteria. The default is None. If provided, it will be stored as an attribute. comment : str, optional Additional information about the criteria. The default is None. If provided, it will be stored as an attribute. attrs : Mapping[Any, Any], optional Arbitrary metadata to store with the criteria, in addition to the attributes `long_name`, `description`, and `comment`. The default is None. is_computed : bool, optional If the indicator data already contains the computed suitability values. Default is False. Examples -------- Here is an example using the sample soil data with the drainage class (DRC) as indicator for the criteria. >>> from lsapy.utils import open_data >>> from lsapy.functions import SuitabilityFunction >>> from xclim.indicators.atmos import growing_degree_days >>> drainage = open_data("land", variables="drainage") >>> sc = SuitabilityCriteria( ... name="drainage_class", ... long_name="Drainage Class Suitability", ... weight=3, ... category="soilTerrain", ... indicator=drainage, ... func=SuitabilityFunction(name="discrete", params={"rules": {0: 0, 1: 0.1, 2: 0.5, 3: 0.9, 4: 1}}), ... ) Here is another example using the sample climate data with the growing degree days (GDD) as indicator for the criteria computing using the `xclim` package. >>> tas = open_data("climate", variables="tas") >>> gdd = growing_degree_days(tas, thresh="10 degC", freq="YS-JUL") >>> sc = SuitabilityCriteria( ... name="growing_degree_days", ... long_name="Growing Degree Days Suitability", ... weight=1, ... category="climate", ... indicator=gdd, ... func=SuitabilityFunction(name="vetharaniam2022_eq5", params={"a": -1.41, "b": 801}), ... ) """
[docs] def __init__( self, name: str, indicator: xr.DataArray, func: SuitabilityFunction | None = None, weight: int | float | None = 1, category: str | None = None, long_name: str | None = None, description: str | None = None, comment: str | None = None, attrs: Mapping[Any, Any] | None = None, is_computed: bool = False, ) -> None: self.name = name self.indicator = indicator self.func = func self.weight = weight self.category = category self._attrs = {} if long_name: self._attrs["long_name"] = long_name if description: self._attrs["description"] = description if comment: self._attrs["comment"] = comment if attrs and isinstance(attrs, Mapping): self._attrs.update(attrs) self.is_computed = is_computed self._from_indicator = _get_indicator_description(indicator)
def __repr__(self) -> str: """Return a string representation of the suitability criteria.""" return fmt.sc_repr(self) @property def attrs(self) -> dict[Any, Any]: """ Dictionary of the suitability criteria attributes. Returns ------- dict Dictionary containing the suitability criteria attributes. """ return self._attrs @attrs.setter def attrs(self, value: Mapping[Any, Any]) -> None: """ Set the attributes of the suitability criteria. Parameters ---------- value : Mapping[Any, Any] Mapping of attributes to set for the suitability criteria. """ self._attrs = dict(value) def compute(self, **kwargs) -> xr.DataArray: """ Compute the suitability of the criteria. Returns a xarray DataArray with criteria suitability. The attributes of the DataArray describe how the suitability was computed. Parameters ---------- **kwargs : dict Additional keyword arguments to pass to the xarray apply_ufunc function. Returns ------- xr.DataArray Criteria suitability. """ if self.is_computed: out = self.indicator elif self.func is None: raise ValueError("The suitability function is not defined. Please provide a valid function.") else: out = xr.apply_ufunc(self.func, self.indicator, **kwargs) attrs: dict[str, Any] = {"weight": self.weight} if self.category: attrs["category"] = self.category attrs.update(self._attrs) attrs["history"] = ( f"func_method: {self.func if self.func is not None else 'unknown'}; " f"from_indicator: [{self._from_indicator}]" ) return out.rename(self.name).assign_attrs(attrs)
def _get_indicator_description(indicator: xr.Dataset | xr.DataArray) -> str: if indicator.attrs != {}: return f"name: {indicator.name}; " + "; ".join([f"{k}: {v}" for k, v in indicator.attrs.items()]) else: return f"name: {indicator.name}"