API Reference

nrt.validate.utils module

nrt.validate.utils.combine_transforms(*transforms)

Utility function to combine multiple transforms

nrt.validate.utils.get_chips(ds, geom, size, compositor=<nrt.validate.composites.SimpleComposite object>, res=None, scale=4, outline_color='magenta')

Prepare a list of chips (ipywidget.Images) croped from a xarray Dataset

Parameters:
  • ds (xarray.Dataset) – A mulivariate and multidimentional data cube containing the data to create the chips

  • geom (dict) – A geojson geometry to define the cropping location and overlay on each chip. Must spatially intersect with ds and be in the same CRS

  • size (float) – Size of the bounding box used for cropping (created around the centroid of geom). In CRS unit.

  • compositor (callable) – Callable to transform a temporal slice of the provided Dataset into a 3D numpy array. See the nrt.validate.composites module for examples

  • res (float) – An optional value defining the size of the overlayed square geometry in case geom is a Point. If None and geom is a point, the Dataset resolution is used.

  • scale (int) – Scaling factor to increase image size

  • outline_color (str) – Color of the (expanded) geometry outline. See the Matplotlib Named Colors Gallery

Examples

>>> import xarray as xr
>>> from nrt.validate import utils
>>> from nrt.validate.composites import TasseledCapComposite
>>> import ipywidgets as ipw
>>> #TODO: Change the line below with cube from nrt.data package
>>> cube = xr.open_dataset('/home/loic/Downloads/czechia_nrt_test.nc')
>>> geom_point = {'type': 'Point', 'coordinates': [4813210, 2935950]}
>>> geom_poly = {"type": "Polygon", "coordinates": [[[4813283, 2935951],
...                                                  [4813250, 2935998],
...                                                  [4813193, 2936019],
...                                                  [4813159, 2936013],
...                                                  [4813134, 2935956],
...                                                  [4813146, 2935899],
...                                                  [4813204, 2935877],
...                                                  [4813232, 2935869],
...                                                  [4813279, 2935927],
...                                                  [4813277, 2935967],
...                                                  [4813283, 2935951]]]}
>>> box_layout = ipw.Layout(display='flex',
...                         flex_flow='row wrap',
...                         align_items='stretch',
...                         width='100%',
...                         height='800px',
...                         overflow='auto')
>>> chips_point = utils.get_chips(ds=cube, geom=geom_point, size=300,
...                               compositor=TasseledCapComposite(), res=None)
>>> box = ipw.Box(children=chips_point, layout=box_layout)
>>> box
>>> chips_poly = utils.get_chips(ds=cube, geom=geom_poly, size=200, res=None)
>>> box = ipw.Box(children=chips_poly, layout=box_layout)
>>> box
Returns:

List of ipywidgets.Image with geometry overlay

Return type:

list

nrt.validate.utils.get_ts(ds, geom, vi_calculator=NDVI(blue='B02', green='B03', red='B04', nir='B8A', re1='B05', re2='B06', re3='B07', swir1='B11', swir2='B12'))

Extract a time-series and compute desired index for a geometry overlayed on a Dataset

Parameters:
  • ds (xarray.Dataset) – The dataset from which to compute and extract the time-series

  • geom (dict) – A geojson geometry (Point or Polygon). If point, the nearest pixel is extracted; if Polygon, spatial average excluding nan s is computed for each time-step.

  • vi_calculator (callable) – A callable to process a DataArray containing the desired index from the dataset. See the nrt.validate.indices module for examples and already implemented simple transforms

Examples

>>> import xarray as xr
>>> from nrt.validate import utils
>>> from nrt.validate.indices import NDVI, CR_SWIR
>>> from nrt.validate.xr_transforms import S2CloudMasking
>>> import ipywidgets as ipw
>>> from bqplot import DateScale, LinearScale, Axis, Scatter, Figure
>>> cube = xr.open_dataset('/home/loic/Downloads/czechia_nrt_test.nc')
>>> geom_point = {'type': 'Point', 'coordinates': [4813210, 2935950]}
>>> geom_poly = {"type": "Polygon", "coordinates": [[[4813283, 2935951],
...                                                  [4813250, 2935998],
...                                                  [4813193, 2936019],
...                                                  [4813159, 2936013],
...                                                  [4813134, 2935956],
...                                                  [4813146, 2935899],
...                                                  [4813204, 2935877],
...                                                  [4813232, 2935869],
...                                                  [4813279, 2935927],
...                                                  [4813277, 2935967],
...                                                  [4813283, 2935951]]]}
>>> dates, ts_point = utils.get_ts(ds=cube, geom=geom_point,
...                         vi_calculator=utils.combine_transforms(S2CloudMasking(), NDVI()))
>>> _, ts_poly = utils.get_ts(ds=cube, geom=geom_poly,
...                         vi_calculator=utils.combine_transforms(S2CloudMasking(), CR_SWIR()))
>>> # Visualize using bqplot
>>> x_scale = DateScale()
>>> y_scale = LinearScale()
>>> x_ax = Axis(label='Date', scale=x_scale, tick_format='%m-%Y', tick_rotate=45)
>>> y_ax = Axis(label='Value', scale=y_scale, orientation='vertical')
>>> point_values = Scatter(x=dates, y=ts_point, scales={'x': x_scale, 'y': y_scale}, colors='green')
>>> polygon_values = Scatter(x=dates, y=ts_poly, scales={'x': x_scale, 'y': y_scale}, colors='blue')
>>> fig = Figure(marks=[point_values, polygon_values], axes=[x_ax, y_ax])
>>> fig
Returns:

Tuple of two elements; Array of dates and array of VI values

Return type:

tuple

nrt.validate.utils.np2ipw(arr, geom=None, transform=None, res=20, scale=4, outline_color='magenta')

Convert a 3 bands numpy array to an ipywidgets Image

Parameters:
  • arr (np.ndarray) – Array of rescaled values between 0 and 1, 3 bands in RGB order. Bands are the last dimension

  • geom (dict) – A geojson geometry to draw on top of the image. Must be in the same Coordinate Reference System as the array

  • transform (affine.Affine) – The affine transform of the image/array

  • res (float) – The image resolution in the unit of CRS. It is used to outline the center pixel in case geom is a Point and does not necessarily need to match the actual image resolution (e.g. if a value of 3 times the actual resolution is provided, the 9 central pixels will be outlined).

  • scale (int) – Scaling factor to increase image size

  • outline_color (str) –

    Color of the (expanded) geometry outline. See the Matplotlib Named Colors Gallery

Returns:

The image with geometry overlay, in ipywidgets Image format

Return type:

ipywidgets.Image

nrt.validate.composites module

class nrt.validate.composites.BaseComposite

Bases: ABC

Abstract base class for all color compositors

static stretch(arr, blim=[20, 2000], glim=[50, 2000], rlim=[20, 2000])

Apply color stretching and [0,1] clipping to a 3 bands image.

Parameters:
  • arr (np.ndarray) – 3D array; bands as last dimension in RGB order.

  • blim (list) – min and max values between which to stretch the individual bands.

  • glim (list) – min and max values between which to stretch the individual bands.

  • rlim (list) – min and max values between which to stretch the individual bands.

Returns:

Stretched and clipped array.

Return type:

np.ndarray

class nrt.validate.composites.S2CIR(b='B03', g='B04', r='B08', blim=[250, 1300], glim=[150, 1700], rlim=[1500, 4000])

Bases: SimpleComposite

class nrt.validate.composites.S2SWIR(b='B04', g='B8A', r='B11', blim=[150, 1200], glim=[1800, 3500], rlim=[800, 3000])

Bases: SimpleComposite

class nrt.validate.composites.S2SWIRx2(b='B12', g='B11', r='B8A', blim=[250, 1800], glim=[600, 3000], rlim=[1200, 4500])

Bases: SimpleComposite

class nrt.validate.composites.S2TasseledCapComposite(blue='B02', green='B03', red='B04', nir='B8A', swir1='B11', swir2='B12', rlim=[300, 1500], glim=[1200, 4500], blim=[600, 2900])

Bases: BaseComposite

brightness(ds)
greenness(ds)
wetness(ds)
class nrt.validate.composites.SimpleComposite(b='B02', g='B03', r='B04', blim=[20, 2000], glim=[50, 2000], rlim=[20, 2000])

Bases: BaseComposite

nrt.validate.estimators module

Statistical estimators for map accuracy assessment.

class nrt.validate.estimators.BaseEstimator

Bases: ABC

Abstract strategy for accuracy estimation.

abstractmethod estimate_mean(mask: ndarray, weights: ndarray | None = None) Tuple[float, float]

Returns (Estimate, Standard Error) for a population mean/proportion.

abstractmethod estimate_ratio(numerator_mask: ndarray, denominator_mask: ndarray, weights: ndarray | None = None) Tuple[float, float]

Returns (Estimate, Standard Error) for a ratio Y/X.

f1_score(y_true: ndarray, y_pred: ndarray, label: Any, se_method: str = None, n_boot: int = 500) Tuple[float, float]

Computes the F1 Score for a specific class with optional SE estimation.

Parameters:
  • y_true – 1D array of reference labels.

  • y_pred – 1D array of map labels.

  • label – The specific class ID to evaluate.

  • se_method – Method for Standard Error calculation. Options: [None, ‘simple_bootstrap’, ‘bootstrap’]. Default is None (returns 0.0 for SE).

  • n_boot – Number of bootstrap iterations if se_method is ‘simple_bootstrap’.

Returns:

(F1 Estimate, Standard Error)

overall_accuracy(y_true: ndarray, y_pred: ndarray) Tuple[float, float]

Computes Overall Accuracy (OA).

Parameters:
  • y_true – 1D array of reference labels.

  • y_pred – 1D array of map labels (must be aligned with y_true and strata).

Returns:

(Estimate, Standard Error)

producer_accuracy(y_true: ndarray, y_pred: ndarray, label: Any) Tuple[float, float]

Computes Producer’s Accuracy (Recall) for a specific class.

Formula: P(Map = label | Reference = label)

Parameters:
  • y_true – 1D array of reference labels.

  • y_pred – 1D array of map labels.

  • label – The specific class ID to evaluate.

Returns:

(Estimate, Standard Error)

user_accuracy(y_true: ndarray, y_pred: ndarray, label: Any) Tuple[float, float]

Computes User’s Accuracy (Precision) for a specific class.

Formula: P(Reference = label | Map = label)

Parameters:
  • y_true – 1D array of reference labels.

  • y_pred – 1D array of map labels.

  • label – The specific class ID to evaluate.

Returns:

(Estimate, Standard Error)

class nrt.validate.estimators.SimpleRandomEstimator

Bases: BaseEstimator

Estimator for Simple Random Sampling (SRS).

estimate_mean(mask: ndarray, weights: ndarray | None = None) Tuple[float, float]

Returns (Estimate, Standard Error) for a population mean/proportion.

estimate_ratio(numerator_mask: ndarray, denominator_mask: ndarray, weights: ndarray | None = None) Tuple[float, float]

Returns (Estimate, Standard Error) for a ratio Y/X.

class nrt.validate.estimators.StratifiedEstimator(strata_labels: ndarray | list, stratum_pop_sizes: Dict[Any, int])

Bases: BaseEstimator

Accuracy and area estimators for Stratified Random Sampling (StrRS).

This class implements the methodology described in Stehman (2014). It is a generalized estimator that should be used whenever the sampling design is stratified (i.e., sample sizes $n_h$ are fixed in advance per stratum), regardless of how the strata are defined.

Use Cases:
  1. Strata ≠ Map Classes: The primary use case described in Stehman (2014). For example, when validating a change map using strata defined by “buffer zones” or “likely change” areas that do not map 1:1 to the final map classes.

  2. Strata = Map Classes: The standard case often associated with Olofsson et al. (2013/2014). When strata exactly match the map classes, the formulas in this class mathematically simplify to the standard “confusion matrix” estimators.

Reference:

Stehman, S. V. (2014). Estimating area and map accuracy for stratified random sampling when the strata are different from the map classes. International Journal of Remote Sensing, 35(13), 4923-4939.

strata_labels

Stratum ID for each sample unit.

Type:

np.ndarray

meta

Stratum metadata (N_h, n_h, weights).

Type:

pd.DataFrame

Examples

>>> import numpy as np
>>> # numerical example based on Stehman (2014) data
>>> # Strata sizes (Nh)
>>> Nh_strata = {1: 40000, 2: 30000, 3: 20000, 4: 10000}
>>> # Reconstructing vectors to exactly match the paper's CSV counts (n=10 per stratum)
>>> # Stratum 1: Map (7A, 3B). Ref (pairs with Map): 5(A,A), 1(A,C), 1(A,B), 1(B,A), 1(B,B), 1(B,C)
>>> s1 = [1]*10
>>> m1 = ["A"]*7 + ["B"]*3
>>> r1 = ["A"]*5 + ["C", "B"] + ["A", "B", "C"]
>>> # Stratum 2: Map (1A, 9B). Ref: 1(A,A), 5(B,B), 2(B,A), 2(B,B)
>>> s2 = [2]*10
>>> m2 = ["A"] + ["B"]*9
>>> r2 = ["A"] + ["B"]*5 + ["A"]*2 + ["B"]*2
>>> # Stratum 3: Map (4B, 6C). Ref: 2(B,C), 1(B,B), 1(B,A), 3(C,C), 2(C,D), 1(C,B)
>>> s3 = [3]*10
>>> m3 = ["B"]*4 + ["C"]*6
>>> r3 = ["C"]*2 + ["B", "A"] + ["C"]*3 + ["D"]*2 + ["B"]
>>> # Stratum 4: Map (10D). Ref: 7(D,D), 2(D,C), 1(D,B)
>>> s4 = [4]*10
>>> m4 = ["D"]*10
>>> r4 = ["D"]*7 + ["C"]*2 + ["B"]
>>> # Combine
>>> s = np.array(s1 + s2 + s3 + s4)
>>> m_arr = np.array(m1 + m2 + m3 + m4)
>>> r_arr = np.array(r1 + r2 + r3 + r4)
>>> est = StratifiedEstimator(s, Nh_strata)
>>> # 1. Proportion of Area of Class A (Paper: 0.35, SE: 0.082)
>>> area_A, se_area_A = est.estimate_mean(r_arr == "A")
>>> print(f"{area_A:.2f}, {se_area_A:.3f}")
0.35, 0.082
>>> # 2. Proportion of Area of Class C (Paper: 0.20, SE: 0.064)
>>> area_C, se_area_C = est.estimate_mean(r_arr == "C")
>>> print(f"{area_C:.2f}, {se_area_C:.3f}")
0.20, 0.064
>>> # 3. Overall Accuracy (Paper: 0.63, SE: 0.085)
>>> oa, se_oa = est.estimate_mean(m_arr == r_arr)
>>> print(f"{oa:.2f}, {se_oa:.3f}")
0.63, 0.085
>>> # 4. User's Accuracy of Class B (Paper: 0.574, SE: 0.125)
>>> # Num: Map is B AND Ref is B. Denom: Map is B.
>>> ua_B, se_ua_B = est.estimate_ratio((m_arr == "B") & (r_arr == "B"), (m_arr == "B"))
>>> print(f"{ua_B:.3f}, {se_ua_B:.3f}")
0.574, 0.125
>>> # 5. Producer's Accuracy of Class B (Paper: 0.794, SE: 0.114)
>>> # Note: Code result is 0.117 due to slight data reconstruction variance vs paper
>>> pa_B, se_pa_B = est.estimate_ratio((r_arr == "B") & (m_arr == "B"), (r_arr == "B"))
>>> print(f"{pa_B:.3f}, {se_pa_B:.3f}")
0.794, 0.117
estimate_mean(mask: ndarray, weights: ndarray | None = None) Tuple[float, float]

Estimates population mean (Eq. 2) and SE (Eq. 25).

estimate_ratio(numerator_mask: ndarray, denominator_mask: ndarray, weights: ndarray | None = None) Tuple[float, float]

Estimates ratio R = Y/X and SE (Eq. 28).

class nrt.validate.estimators.TwoStageClusterEstimator(psu_ids: ndarray, strata_1_ids: ndarray, global_weights: ndarray, se_method: str = 'analytical', n_boot: int = 500, strata_1_pop_sizes: Dict[Any, int] | None = None)

Bases: BaseEstimator

Estimator for Stratified Two-Stage Cluster Sampling.

METHODOLOGY NOTE: This class implements the “Ultimate Cluster” variance estimator (Särndal et al., 1992). It simplifies variance estimation by treating the Primary Sampling Unit (PSU) as the fundamental unit of analysis.

Instead of explicitly summing “Between-PSU Variance” + “Within-PSU Variance”, this method aggregates all SSUs (pixels) to calculate a weighted total for each PSU, and then calculates the variance between these PSU totals. This automatically captures the total variance (both stages) under the assumption that PSUs are sampled with replacement (or that the sampling fraction is small).

This approach is standard for large-scale remote sensing (e.g., Stehman & Selkowitz, 2010) because it is robust to complex second-stage designs (like pixel stratification) without requiring complex covariance formulas.

Examples

>>> import numpy as np
>>> # Synthetic Data verified against R 'survey' package (v4.0)
>>> # Scenario:
>>> #   - 2 Stratum (Region_A, Region_B)
>>> #   - 4 PSUs sampled per Stratum (8 PSUs total) -> No lonely PSUs!
>>> #   - 5 SSUs sampled per PSU (40 SSUs total)
>>> #   - Weights: Region_A=10, Region_B=20 (representing different sampling probs)
>>>
>>> # Data Setup
>>> strata_1 = np.array(['A']*20 + ['B']*20)  # 20 pixels in A, 20 in B
>>> psu_ids = np.concatenate([
...     [1]*5, [2]*5, [3]*5, [4]*5,   # 4 PSUs in A
...     [5]*5, [6]*5, [7]*5, [8]*5    # 4 PSUs in B
... ])
>>> weights = np.concatenate([[10]*20, [20]*20])
>>>
>>> # Target Variable (Mask):
>>> # Region A: PSUs are fairly consistent (mostly True)
>>> # Region B: PSUs are highly variable (some all True, some all False)
>>> # Constructing a pattern:
>>> # PSU 1: 5/5 True
>>> # PSU 2: 4/5 True
>>> # PSU 3: 5/5 True
>>> # PSU 4: 4/5 True (Region A is "High Accuracy")
>>> # PSU 5: 0/5 True
>>> # PSU 6: 1/5 True
>>> # PSU 7: 0/5 True
>>> # PSU 8: 5/5 True (Region B is "Noisy/Mixed")
>>> mask = np.array([
...     1,1,1,1,1,  1,1,1,1,0,  1,1,1,1,1,  1,1,1,1,0,  # A
...     0,0,0,0,0,  1,0,0,0,0,  0,0,0,0,0,  1,1,1,1,1   # B
... ], dtype=bool)
>>>
>>> # Initialize Estimator
>>> est = TwoStageClusterEstimator(psu_ids, strata_1, weights, se_method='analytical')
>>>
>>> # 1. Point Estimate (Mean)
>>> # Manual Logic:
>>> # Stratum A Est: High proportion (~0.9) * Weight 10
>>> # Stratum B Est: Low proportion (~0.3) * Weight 20
>>> # Region B dominates due to weight. Expect mean around 0.5.
>>> mean, se = est.estimate_mean(mask)
>>>
>>> # Validated against R: svyratio(~y, ~ind, design=svydesign(id=~psu, strata=~strata, weights=~w))
>>> # R Result: Mean = 0.500
>>> print(f"{mean:.3f}")
0.500
>>> # 2. Standard Error (Ultimate Cluster)
>>> # Validated against R Result: SE = 0.1598611
>>> print(f"{se:.7f}")
0.1598611
>>> # 3. Effective Sample Size
>>> # Count of PSUs with at least 1 positive sample
>>> # In A: All 4 PSUs have positives.
>>> # In B: PSU 6 has 1 positive, PSU 8 has 5 positives. (PSU 5, 7 have 0).
>>> # Total = 4 + 2 = 6
>>> est.effective_sample_size(mask)
6
effective_sample_size(mask: ndarray) int

Returns ‘tPSU’: the count of unique PSUs containing at least one positive sample. Reference: Wickham et al. (2003).

estimate_mean(mask: ndarray, weights: ndarray | None = None, compute_se: bool = True) Tuple[float, float]

Estimates population mean/proportion. Technically implemented as a Ratio of Totals (Total Y / Total Pop) to handle variable PSU sizes correctly.

estimate_ratio(numerator_mask: ndarray, denominator_mask: ndarray, weights: ndarray | None = None, compute_se: bool = True) Tuple[float, float]

Returns (Estimate, Standard Error) for a ratio Y/X.

nrt.validate.fitting module

class nrt.validate.fitting.PartitionedHarmonicTrendModel(dates, delta_predict=np.timedelta64(5, 'D'))

Bases: object

Least square harmonic fitting of partitioned time-series

Parameters:
  • dates (np.ndarray) – Array of datetime64 dates

  • delta_predict (np.timedelta64) – Temporal interval for smooth predictions

Examples

>>> import numpy as np
>>> from nrt.validate.fitting import PartitionedHarmonicTrendModel
>>> np.random.seed(42)
>>> # Random time-series with irregularly spaced observations
>>> start_date = np.datetime64('2020-01-01')
>>> n_samples = 120
>>> days_between = np.random.randint(5, 21, size=n_samples)
>>> dates = np.cumsum(days_between)
>>> dates = start_date + np.array(dates, dtype='timedelta64[D]')
>>> y = np.random.random(size=n_samples)
>>> breakpoints = [dates[0], dates[40], dates[-1]]
>>> model = PartitionedHarmonicTrendModel(dates)
>>> dates, predictions = model.fit_predict(y, breakpoints, 2)
>>> print(dates)
>>> print(predictions)
static decimal_dates(dates)

Convert a datetime64 array to decimal years

fit_predict(y, breakpoints, order)

Fit a harmonic trend model for each segment and returns a smoothed prediction

Note that the last value of the time-series is not used for the fitting. A segment starts at a breakpoint and ends one observation before the next breakpoint

Parameters:
  • y (np.ndarray) – Array of values

  • breakpoints (list) – List of datetime64 dates corresponding to breakpoints dates. Usually include extremities of the time-series too.

  • order (int) – Harmonic order, between 0 and 5

Returns:

List of datetime64 arrays each corresponding to a segment - predicted values: List of matching arrays containing predicted values

Return type:

  • Dates

nrt.validate.indices module

class nrt.validate.indices.BaseS2Index(blue: str = 'B02', green: str = 'B03', red: str = 'B04', nir: str = 'B8A', re1: str = 'B05', re2: str = 'B06', re3: str = 'B07', swir1: str = 'B11', swir2: str = 'B12')

Bases: ABC

Abstract base class for Sentinel2 based indices

blue: str = 'B02'
green: str = 'B03'
nir: str = 'B8A'
re1: str = 'B05'
re2: str = 'B06'
re3: str = 'B07'
red: str = 'B04'
swir1: str = 'B11'
swir2: str = 'B12'
class nrt.validate.indices.CR_SWIR(blue: str = 'B02', green: str = 'B03', red: str = 'B04', nir: str = 'B8A', re1: str = 'B05', re2: str = 'B06', re3: str = 'B07', swir1: str = 'B11', swir2: str = 'B12')

Bases: BaseS2Index

CR_SWIR calculator for Sentinel 2 data organized in an xarray Dataset

This Continuum removal corresponds to the division between the observed SWIR1 value and a value interpolated between NIR and SWIR2 hence amplifying the absoption feature of the SWIR1 region. The index was first proposed by Dutrieux et al. (2021) for the development of a near real time spruce dieback detection system named FORDEAD. Note that Sentinel 2A and 2B have slightly different central wavelengths, especially for SWIR2. The mean of the two sensors is used here

class nrt.validate.indices.NCDI(blue: str = 'B02', green: str = 'B03', red: str = 'B04', nir: str = 'B8A', re1: str = 'B05', re2: str = 'B06', re3: str = 'B07', swir1: str = 'B11', swir2: str = 'B12')

Bases: BaseS2Index

Experimental Normalized SWIR1 Continuum Difference Index

A normalized variation of the CR_SWIR index. Instead of dividing the SWIR1 reflectance by the SWIR1 continuum, this index computes a normalized difference between them. It is therefore calculated as:

\[NDCI =\]

rac{{ ext{SWIR}_{1-C} - ext{SWIR}_{1-R}}}{{ ext{SWIR}_{1-C} + ext{SWIR}_{1-R}}}

Where:

  • \(SWIR_{1-C}\) is the continuum interpolated between NIR and SWIR2 for SWIR1 wavelength.

  • \(SWIR_{1-R}\) is the SWIR1 reflectance value.

class nrt.validate.indices.NDMI(blue: str = 'B02', green: str = 'B03', red: str = 'B04', nir: str = 'B8A', re1: str = 'B05', re2: str = 'B06', re3: str = 'B07', swir1: str = 'B11', swir2: str = 'B12')

Bases: BaseS2Index

NDMI calculator for Sentinel 2 data organized in an xarray Dataset

By default, the swir channel must be named 'B11' and the nir channel 'B8A' as per the BaseS2Index base class. These defaults can be modified at instantiation by passing for instance ndmi = NDMI(nir='B08')

class nrt.validate.indices.NDVI(blue: str = 'B02', green: str = 'B03', red: str = 'B04', nir: str = 'B8A', re1: str = 'B05', re2: str = 'B06', re3: str = 'B07', swir1: str = 'B11', swir2: str = 'B12')

Bases: BaseS2Index

NDVI calculator for Sentinel 2 data organized in an xarray Dataset

By default, the red channel must be named 'B03' and the nir channel 'B8A' as per the BaseS2Index base class. These defaults can be modified at instantiation by passing for instance ndvi = NDVI(red='B03_20', nir='B08_20')

nrt.validate.interface module

class nrt.validate.interface.Chips(**kwargs: Any)

Bases: HasTraits

add_or_remove_breakpoint(idx)
breakpoints

An instance of a Python list.

display()
classmethod from_cube_and_geom(ds, geom, breakpoints=[], compositor=<nrt.validate.composites.SimpleComposite object>, window_size=500, **kwargs)

Instantiate Chips from an xarray Dataset and a geometry

Geometry and cube/Dataset must share the same coordinate reference system

Parameters:
  • ds (xarray.Dataset) – The Dataset containing the data to display

  • geom (dict) – A geojson geometry (Point or Polygon) around which Dataset will be cropped and for which index time-series will be extracted

  • breakpoints (list) – Optional list of dates

  • compositor (callable) – Callable to transform a temporal slice of the provided Dataset into a 3D numpy array. See `nrt.validate.composites module for examples

  • window_size (float) – Size of the bounding box used for cropping (created around the centroid of `geom). In CRS unit.

  • **kwargs – Additional arguments passed to `nrt.validate.utils.get_chips

highlight

A container with observable traits and many elementary methods to host image chips

Examples

>>> import xarray as xr
>>> import numpy as np
>>> from nrt.validate.interface import Chips
>>> cube = xr.open_dataset('/home/loic/Downloads/czechia_nrt_test.nc')
>>> geom = {'type': 'Point', 'coordinates': [4813210, 2935950]}
>>> chips = Chips.from_cube_and_geom(ds=cube, geom=geom,
...                                  breakpoints=[np.datetime64('2018-09-28T10:00:19.024000000'),
...                                               np.datetime64('2019-02-27T09:50:31.024000000'),
...                                               np.datetime64('2021-10-29T09:50:29.024000000')])
>>> chips.display()
>>> # Add breakpoint either by clicking on a chip, or running the following method
>>> chips.add_or_remove_breakpoint(33)
class nrt.validate.interface.SegmentsLabellingInterface(**kwargs: Any)

Bases: HasTraits

create_button(idx, feature_id, color)

Create a button related to a sample

create_interactive_list(samples, color)

Create lists of samples buttons

Agrs:

samples (list): List of (idx, feature_id) tuples

current_idx

An int trait.

display()
draw_webmap(geom, res, crs)
get_fids()

Get two mutually exclusive lists of feature ids First list is the not yet interpreted Second list is the already interpreted

load_sample(idx)
on_sample_click(button)
save_to_db(button)

Save current segmentation to database

update_interface
update_lists()

Update lists of samples

update_webmap(geom, res, crs)
class nrt.validate.interface.Vits(**kwargs: Any)

Bases: HasTraits

breakpoints

An instance of a Python list.

current_vi

Handle and display the vegetation index time-series

display()
classmethod from_cube_and_geom(ds, geom, breakpoints=[], vis={'CR-SWIR': CR_SWIR(blue='B02', green='B03', red='B04', nir='B8A', re1='B05', re2='B06', re3='B07', swir1='B11', swir2='B12'), 'NDVI': NDVI(blue='B02', green='B03', red='B04', nir='B8A', re1='B05', re2='B06', re3='B07', swir1='B11', swir2='B12')}, default_vi='NDVI')

Instantiate Vits from an xarray Dataset and a geometry

Geometry and cube/Dataset must share the same coordinate reference system

Parameters:
  • ds (xarray.Dataset) – The Dataset containing the data to display

  • geom (dict) – A geojson geometry (Point or Polygon) with which the time-series will be extracted (nearest pixel in case of Point, spatial average for Polygons)

  • breakpoints (list) – Optional list of dates

  • vis (dict) – Dictionary of callables to compute vegetation indices see `nrt.validate.indices module for examples and already implemented indices

order

An int trait.

redraw_fit_lines
redraw_vlines
update_highlighted_point(idx)

Update the color of the highlighted point based on idx.

Parameters:

idx (int or None) – Index of the point to highlight or None.

nrt.validate.loaders module

Data loaders from various sources (disk, STAC, gee) to be used by the Interface

Loaders inputs are at least a feature collection with a property that can be used as a primary key and a way to retrieve xarray Dataset (a single or multiple netcdf files/zarr stores; a STAC API collection; etc). The loader are subscritable (loader[n] is used to access data of n-th element), have at least a __len__ method. Data returned by subscript are in the form of a 6 element tuple (unique_id, dates, chips, ts, geom, crs).

TODOs/questions:
  • Should WKT geometries be stored in the database? To facilitate disaster recovery

  • CRS handling, none for now. Needed? Yes, it may be needed for webmap overlay.

  • The dates array must be numpy.datetime64 with Day precision. Document that somewhere for people who wish to write their own loader

class nrt.validate.loaders.BaseLoader(fc: List[Dict[str, Any]], key: str, crs: Any, prefetch: int | None = None, cache_size: int | None = 20)

Bases: ABC

property fids

Return the list of unique feature ids

class nrt.validate.loaders.FileLoader(fc: List[Dict[str, Any]], key: str, crs: Any, datasets: Dataset | List[Dataset], vis: Dict[str, Callable[[DataArray], Any]], window_size: float, compositor: Callable[[Dataset], ndarray], xr_transform: Callable[[Dataset], Dataset] | None = None, res: float | None = None, prefetch: int | None = 5, cache_size: int | None = 20, **kwargs)

Bases: BaseLoader

A loader to prepare locally accessible data

Parameters:
  • fc (list) – A feature collection. Can be Points, Polygons or a mix of the two. Must contain a property that can be used as a unique key

  • key (str) – Name of the feature collection property to be used as unique identifier

  • crs (CRS) – A coordinate reference object, from fiona, rasterio, pyproj, etc representing projection of both fc and datasets

  • datasets (xr.Dataset or list) – (list of) xarray Datasets containing the multispectral spatio-temporal data. They must all be in the same CRS as fc. They can lazy loaded using dask (see chunks argument in open_dataset.

  • vis (dict) – Dictionary of callables to compute vegetation indices see nrt.validate.indices module for examples and already implemented indices

  • compositor (callable) – Callable to transform a temporal slice of the provided Dataset into a 3D numpy array. See nrt.validate.composites module for examples

  • window_size (float) – Size of the bounding box used for cropping (created around the centroid of geom). In CRS unit.

  • xr_transform (callable) – Callable that takes an xarray Dataset as input and returns another xarray Dataset. This operation is applied to the spatial subset of the cube used for generating chips and VI time-series. Generally used for pre-processing steps such as removal of “empty” slices, or data scaling.

  • **kwargs – Additional arguments passed to nrt.validate.utils.get_chips

Returns:

A tuple of 6 elements:
  • The unique key of the sample

  • Array of numpy.datetime64

  • List of ipywidgets.Image (the image chips)

  • Dictionary of values for multiple vegetation indices

  • The sample geometry

  • The CRS object

Return type:

tuple

Examples

>>> import xarray as xr
>>> import numpy as np
>>> from shapely.geometry import Point, mapping
>>> import rioxarray
>>> from nrt.validate.loaders import FileLoader
>>> from nrt.validate import utils
>>> from nrt.validate.indices import *
>>> from nrt.validate.composites import *
>>> from nrt.validate.xr_transforms import *
>>> cube = xr.open_dataset('/home/loic/Downloads/czechia_nrt_test.nc', chunks=-1)
>>> cube = cube.rename({'B02_20':'B02', 'B03_20': 'B03', 'B04_20': 'B04'})
>>> geom = {'type': 'Point', 'coordinates': [4813210, 2935950]}
>>> fc = [{'geometry': mapping(Point(4813210, 2935950)),
...        'properties': {'pid': 1}},
...       {'geometry': mapping(Point(4813350, 2934998)),
...        'properties': {'pid': 2}}]
>>> loader = FileLoader(fc=fc,
...                     key='pid',
...                     crs=cube.rio.crs,
...                     datasets=cube,
...                     vis={'NDVI': utils.combine_transforms(S2CloudMasking(), CR_SWIR()),
...                          'CR-SWIR': utils.combine_transforms(S2CloudMasking(), NDVI())},
...                     window_size=300,
...                     compositor=SimpleComposite(),
...                     res=None)
>>> print(len(loader[0]))
6
class nrt.validate.loaders.STACLoader(fc: List[Dict[str, Any]], key: str, crs: Any, client: Client, collection_id: str, bands: List[str], datetime: List[datetime | str], resampling: str | Dict[str, str] | None, vis: Dict[str, Callable[[DataArray], Any]], window_size: float, compositor: Callable[[Dataset], ndarray], query: Dict | None, xr_transform: Callable[[Dataset], Dataset] | None = None, res: float | None = None, prefetch: int | None = 5, cache_size: int | None = 20, **kwargs)

Bases: BaseLoader

Loader to prepare data indexed into a STAC Catalogue

Parameters:
  • fc (list) – A feature collection. Can be Points, Polygons or a mix of the two. Must contain a property that can be used as a unique key

  • key (str) – Name of the feature collection property to be used as unique identifier

  • crs (CRS) – A coordinate reference object, from fiona, rasterio, pyproj, etc representing projection of both fc and datasets

  • client (Client) – The STAC API client.

  • collection_id (str) – The STAC collection ID to query.

  • bands (list) – List of bands to load from the STAC collection.

  • datetime (list) – List of datetime objects or strings defining the time range to query.

  • resampling (str or dict, optional) – Resampling method(s) for the bands.

  • vis (dict) – Dictionary of callables to compute vegetation indices see nrt.validate.indices module for examples and already implemented indices.

  • window_size (float) – Size of the bounding box used for cropping (created around the centroid of geom). In CRS unit.

  • compositor (callable) – Callable to transform a temporal slice of the provided Dataset into a 3D numpy array. See nrt.validate.composites module for examples.

  • query (dict, optional) – Additional query parameters for the STAC API.

  • res (float, optional) – Spatial resolution for the output data.

  • prefetch (int, optional) – Number of items to prefetch and cache.

  • cache_size (int, optional) – Maximum size of the cache.

  • xr_transform (callable) – Callable that takes an xarray Dataset as input and returns another xarray Dataset. This operation is applied to the spatial subset of the cube used for generating chips and VI time-series. Generally used for pre-processing steps such as removal of “empty” slices, or data scaling.

  • kwargs (dict, optional) – Additional arguments passed to nrt.validate.utils.get_chips.

Returns:

A tuple of 6 elements:
  • The unique key of the sample

  • Array of numpy.datetime64

  • List of ipywidgets.Image (the image chips)

  • Dictionary of values for multiple vegetation indices

  • The sample geometry

  • The CRS object

Return type:

tuple

Examples

>>> import datetime
>>> from nrt.validate.loaders import STACLoader
>>> from pystac_client import Client
>>> import planetary_computer as pc
>>> from pyproj import CRS
>>> from nrt.validate import utils
>>> from nrt.validate.indices import *
>>> from nrt.validate.composites import *
>>> from nrt.validate.xr_transforms import *
>>> fc = [{'geometry': {'type': 'Point', 'coordinates': (4033880, 3217980)},
...        'properties': {'idx': 1}},
...       {'geometry': {'type': 'Point', 'coordinates': (4395490, 3038090)},
...        'properties': {'idx': 2}},
...       {'geometry': {'type': 'Point', 'coordinates': (4713260, 2931020)},
...                     'properties': {'idx': 3}}]
>>> key = 'idx'
>>> crs = CRS.from_epsg(3035)
>>> catalog = Client.open('https://planetarycomputer.microsoft.com/api/stac/v1',
...                         modifier=pc.sign_inplace)
>>> collection_id = 'sentinel-2-l2a'
>>> bands = ['B02', 'B03', 'B04', 'B08', 'B11', 'B12', 'SCL']
>>> resampling = {band: 'nearest' if band == 'SCL' else 'cubic' for band in bands}
>>> dt = [datetime.datetime(2019, 1, 1), datetime.datetime(2021,12,31)]
>>> vis = {'NDVI': utils.combine_transforms(S2CloudMasking(), CR_SWIR(nir='B08')),
...        'CR-SWIR': utils.combine_transforms(S2CloudMasking(), NDVI(red='B04', nir='B08'))}
>>> window_size = 300
>>> compositor = SimpleComposite(r='B04', g='B03', b='B02')
>>> query = {"eo:cloud_cover": {"lt": 10}}
>>> res = 10
>>> prefetch = 5
>>> loader = STACLoader(fc=fc,
...                     key=key,
...                     crs=crs,
...                     client=catalog,
...                     collection_id=collection_id,
...                     bands=bands,
...                     resampling=resampling,
...                     datetime=dt,
...                     vis=vis,
...                     window_size=window_size,
...                     compositor=compositor,
...                     query=query,
...                     res=res,
...                     prefetch=prefetch)
>>> print(loader[0])

nrt.validate.segments module

Module with data structures to handle temporal segmentation and interface with sqlite database

class nrt.validate.segments.Segment(begin: datetime64, end: datetime64, label=None)

Bases: object

Represents a temporal segment with a beginning and an end, optionally labeled.

Parameters:
  • begin (numpy.datetime64) – The beginning of the segment.

  • end (numpy.datetime64) – The end of the segment.

  • label (str) – An optional label for the segment

Examples

>>> import numpy as np
>>> import sqlite3
>>> s = Segment(np.datetime64('2020-01-01'),
...             np.datetime64('2020-01-02'),
...             'forest dieback')
>>> print(s)
Temporal segment
begin: 2020-01-01
end: 2020-01-02
label: forest dieback
>>> s.breakpoints
[numpy.datetime64('2020-01-01'), numpy.datetime64('2020-01-02')]
>>> conn = sqlite3.connect(':memory:')
>>> s.to_db(conn, 6)
>>> cur = conn.cursor()
>>> _ = cur.execute("SELECT id, feature_id, begin, end, label FROM segments WHERE id = ?", (1,))
>>> row = cur.fetchone()
>>> print(row)
(1, 6, 18262, 18263, 'forest dieback')
property breakpoints
classmethod from_db(idx, conn)

Create a Segment instance from the database using the segment ID.

Parameters:
  • idx (int) – The ID of the segment in the database.

  • conn – sqlite database connection

Returns:

An instance of the Segment class.

Return type:

Segment

to_db(conn, feature_id)

Save the Segment instance to the database.

Parameters:
  • db_path (str) – Path to the SQLite database.

  • feature_id (int) – The feature ID to associate with the segment.

widget(labels=['forest', 'dieback', 'non-forest'])

Create a widget with a label and dropdown for segment label selection.

class nrt.validate.segments.Segmentation(**kwargs: Any)

Bases: HasTraits

Container for segmentation with observable traits

Parameters:
  • breakpoints (list) – A list of numpy.datetime64 corresponding to breakpoints around temporal segments.

  • segments (list) – A list of ``Segment``s. The corresponding attribute is dynamically computed and updated from the breakpoints attribute

Examples

>>> import sqlite3
>>> import numpy as np
>>> # Generate 50 random dates between 2005 and 2008
>>> start_date = np.datetime64('2005-01-01')
>>> end_date = np.datetime64('2008-12-31')
>>> num_days = (end_date - start_date).astype(int)
>>> random_days = np.sort(np.random.randint(0, num_days, 50))
>>> random_dates = start_date + random_days
>>> random_dates = np.append(random_dates, end_date)
>>> # Open a sqlite3 connection
>>> conn = sqlite3.connect(':memory:')
>>> seg = Segmentation.from_datelist(random_dates, conn,
...                                  labels=['a', 'b'])
>>> print(seg)
Temporal segmentation with 2 breakpoints and 1 segments
>>> seg.add_breakpoint(np.datetime64('2006-11-21'))
>>> print(seg)
Temporal segmentation with 3 breakpoints and 2 segments
>>> seg.remove_breakpoint(np.datetime64('2006-11-21'))
>>> print(seg)
Temporal segmentation with 2 breakpoints and 1 segments
>>> # Write results to a sqlite database
>>> seg.add_breakpoint(np.datetime64('2006-11-21'))
>>> seg.to_db(12)
>>> cur = conn.cursor()
>>> _ = cur.execute('SELECT * FROM segments WHERE feature_id = 12')
>>> rows = cur.fetchall()
>>> print(rows)
[(1, 12, 12788, 13473, None), (2, 12, 13473, 14244, None)]
add_breakpoint(date)

Add a breakpoint date to the segmentation.

Parameters:

date (np.datetime64) – The date to add as a breakpoint.

add_or_remove_breakpoint(date)

If the date provided is already a breakpoint, remove it, otherwise add it

breakpoints: List[datetime64]

An instance of a Python list.

static compute_breakpoints(segments)

Compute breakpoints given a list of segments.

Parameters:

segments (list of Segment) – List of segments.

Returns:

Sorted list of unique breakpoints.

Return type:

list of np.datetime64

conn: Connection
display_widgets()

Display the widgets for segment management.

static exists(feature_id, conn)

Check if a feature ID exists in the segments table.

Parameters:
  • feature_id (int) – The feature ID.

  • db_path (str) – Path to the SQLite database.

Returns:

True if the feature ID exists, False otherwise.

Return type:

bool

classmethod from_datelist(dates, conn, labels)

Create a Segmentation instance from a list of dates.

Assigns a single temporal segment spanning the entire time-series

Parameters:
  • dates (list of np.datetime64) – List of dates.

  • db_path (str) – Path to the SQLite database.

Returns:

An instance of the Segmentation class.

Return type:

Segmentation

classmethod from_db(feature_id, conn, labels)

Create a Segmentation instance from the database using the feature ID.

Parameters:
  • feature_id (int) – The feature ID.

  • db_path (str) – Path to the SQLite database.

Returns:

An instance of the Segmentation class.

Return type:

Segmentation

classmethod from_db_or_datelist(feature_id, conn, dates, labels)

Create a Segmentation instance either from the database or from a list of dates.

Parameters:
  • feature_id (int) – The feature ID.

  • conn (sqlite3.Connection) – Connection to a sqlite database

  • dates (list of np.datetime64) – List of dates.

Returns:

An instance of the Segmentation class.

Return type:

Segmentation

static get_db_idx(feature_id, conn)

Retrieve the IDs of segments with a given feature ID.

Parameters:
  • feature_id (int) – The feature ID.

  • conn (sqlite3.Connection) – Database connection

Returns:

List of segment IDs.

Return type:

list of int

static get_fids_db(conn)

Get a list of unique feature_ids present in the database

labels: List[str]
remove_breakpoint(date)

Remove a breakpoint date from the segmentation.

Parameters:

date (np.datetime64) – The date to remove from breakpoints.

Raises:

ValueError – If the date is not a valid breakpoint.

segments: List[Segment]

An instance of a Python list.

to_db(feature_id)

Save the Segmentation instance to the database.

Parameters:

feature_id (int) – The feature ID.

nrt.validate.segments.disable_trait_notifications(self)

Temporatilly disable trait notifications.

nrt.validate.webmaps module

class nrt.validate.webmaps.EsriWaybackBasemap

Bases: object

A class to create and display an Esri Wayback basemap with time navigation functionality using ipyleaflet.

map

The ipyleaflet map instance.

Type:

ipyleaflet.Map

DATE_TIMEID_MAPPING

A list of tuples containing date and ID mappings.

Type:

list

Example

>>> from nrt.validate.webmaps import EsriWaybackBasemap
>>> esri_wayback_map = EsriWaybackBasemap()
>>> display(esri_wayback_map.map)
class nrt.validate.webmaps.GoogleBasemap

Bases: object

A class to create and display a Google basemap using ipyleaflet.

map

The ipyleaflet map instance.

Type:

ipyleaflet.Map

Example

>>> from nrt.validate.webmaps import GoogleBasemap
>>> google_map = GoogleBasemap()
>>> display(google_map.map)
class nrt.validate.webmaps.PlanetBasemap(frequency='quarterly', begin=datetime.datetime(2017, 1, 1, 0, 0), end=datetime.datetime(2026, 2, 20, 18, 39, 21, 584490), api_key='')

Bases: object

A class to create and display Planet basemaps with time navigation functionality using ipyleaflet.

Parameters:
  • frequency (str) – The frequency of the basemap updates. Either ‘monthly’ or ‘quarterly’.

  • begin (datetime.datetime) – The start date for the basemap time range.

  • end (datetime.datetime) – The end date for the basemap time range.

  • api_key (str) – The API key for accessing Planet basemaps.

frequency

The frequency of the basemap updates. Either ‘monthly’ or ‘quarterly’.

Type:

str

begin

The start date for the basemap time range.

Type:

datetime.datetime

end

The end date for the basemap time range.

Type:

datetime.datetime

api_key

The API key for accessing Planet basemaps.

Type:

str

map

The ipyleaflet map instance.

Type:

ipyleaflet.Map

Example

>>> import datetime
>>> from nrt.validate.webmaps import PlanetBasemap
>>> planet = PlanetBasemap(frequency='monthly', begin=datetime.datetime(2017, 1, 1), end=datetime.datetime.now(), api_key='your_api_key')
>>> display(planet.map)

nrt.validate.xr_transforms module

Callables to apply a transformation to a xarray Dataset Usually returns another xarray Dataset

class nrt.validate.xr_transforms.BaseS2Transform(blue: str = 'B02', green: str = 'B03', red: str = 'B04', nir: str = 'B8A', re1: str = 'B05', re2: str = 'B06', re3: str = 'B07', swir1: str = 'B11', swir2: str = 'B12', scl: str = 'SCL')

Bases: ABC

Abstract base class for Sentinel2 based indices

blue: str = 'B02'
green: str = 'B03'
nir: str = 'B8A'
re1: str = 'B05'
re2: str = 'B06'
re3: str = 'B07'
red: str = 'B04'
scl: str = 'SCL'
swir1: str = 'B11'
swir2: str = 'B12'
class nrt.validate.xr_transforms.S2CloudMasking(blue: str = 'B02', green: str = 'B03', red: str = 'B04', nir: str = 'B8A', re1: str = 'B05', re2: str = 'B06', re3: str = 'B07', swir1: str = 'B11', swir2: str = 'B12', scl: str = 'SCL', valid_scl_values: list = <factory>)

Bases: BaseS2Transform

Replace pixels identified as clouds, shadows, snow or dark pixels by np.nan

valid_scl_values: list
class nrt.validate.xr_transforms.S2MonthlyBest(blue: str = 'B02', green: str = 'B03', red: str = 'B04', nir: str = 'B8A', re1: str = 'B05', re2: str = 'B06', re3: str = 'B07', swir1: str = 'B11', swir2: str = 'B12', scl: str = 'SCL', valid_scl_values: list = <factory>, weight_valid: float = 1.0, weight_time: float = 0.5)

Bases: BaseS2Transform

Select the best image per month based on valid data percentage and date

The best image selection algorithm works by computing a score for each image based on the percentage of valid data and the temporal proximity to the center of the month.

Parameters:
  • ds (xr.Dataset) – xarray Dataset with time dimension

  • scl (str) – name of the SCL variable in the dataset

  • valid_scl_values (list) – list of valid SCL values used to compute the valid data score

  • weight_valid (float) – weight for the valid data score

  • weight_time (float) – weight for the temporal proximity score

Example

>>> from nrt import data
>>> from nrt.validate.xr_transforms import S2MonthlyBest
>>> # Load a small spatial-temporal subset of the dataset
>>> ds = data.germany_zarr()
>>> ds_sub = ds.isel(x=slice(100, 110), y=slice(200, 210))
>>> # Apply the transformation
>>> transform = S2MonthlyBest()
>>> ds_best = transform(ds_sub)
>>> print(ds_best)
<xarray.Dataset> Size: 261kB
Dimensions:      (time: 50, y: 10, x: 10)
Coordinates:
    spatial_ref  int32 4B 3035
  * time         (time) datetime64[ns] 400B 2018-02-22T10:40:29.028000 ... 20...
  * x            (x) float64 80B 4.134e+06 4.134e+06 ... 4.134e+06 4.134e+06
  * y            (y) float64 80B 3.111e+06 3.111e+06 ... 3.111e+06 3.111e+06
Data variables:
    B02          (time, y, x) float64 40kB 0.0501 0.0656 0.0736 ... nan nan nan
    B03          (time, y, x) float64 40kB 0.0722 0.0863 0.0909 ... nan nan nan
    B04          (time, y, x) float64 40kB 0.0577 0.0862 0.1035 ... nan nan nan
    B08          (time, y, x) float64 40kB 0.3225 0.2855 0.2602 ... nan nan nan
    B11          (time, y, x) float64 40kB 0.2112 0.2219 0.2331 ... nan nan nan
    B12          (time, y, x) float64 40kB 0.1264 0.1362 0.1457 ... nan nan nan
    SCL          (time, y, x) float32 20kB 4.0 4.0 4.0 4.0 ... 0.0 0.0 0.0 0.0
valid_scl_values: list
weight_time: float = 0.5
weight_valid: float = 1.0
class nrt.validate.xr_transforms.S2OffsetCorrection(blue: str = 'B02', green: str = 'B03', red: str = 'B04', nir: str = 'B8A', re1: str = 'B05', re2: str = 'B06', re3: str = 'B07', swir1: str = 'B11', swir2: str = 'B12', scl: str = 'SCL', offset: int = 1000)

Bases: BaseS2Transform

Apply radiometric offset correction to Sentinel-2 bands.

Starting from processing baseline 04.00, Sentinel-2 data includes a radiometric offset (1000). This transform subtracts that offset from all spectral bands while preserving the SCL (Scene Classification Layer).

Parameters:

offset (int) – The value to subtract from the bands. Default is 1000.

offset: int = 1000