Source code for swectral.vegeind.sr
# -*- coding: utf-8 -*-
"""
Swectral - Vegetation Indices - SR (simple ratio, simple ratio of R800 / R675)
Copyright (c) 2025 Siwei Luo. MIT License.
"""
import numpy as np
import pandas as pd
from typing import Annotated, Any
from ..specio import simple_type_validator, arraylike_validator
# # For local test
# from specpipe.specio import simple_type_validator, arraylike_validator
# %% Vegetation index function
[docs]
@simple_type_validator
def sr(
spec_array: Annotated[Any, arraylike_validator(ndim=2)],
wavelength: Annotated[Any, arraylike_validator(ndim=1)],
axis: int = 0,
) -> pd.DataFrame:
"""
Compute SR (simple ratio, simple ratio of R800 / R675) of 2D array-like of 1D spectra data series.
The calculation is based on::
Jordan, C. F. (1969).
Derivation of Leaf-Area Index from Quality of Light on the Forest Floor.
Ecology, 50(4), 663–666.
https://doi.org/10.2307/1936256
Parameters
----------
spec_array : 2D array-like, shape (n_samples, n_features)
2D array-like of 1D spectral data series.
wavelength : 1D array-like
Wavelengths for the spectra data.
axis : int, optional
Axis of spectral data series index, must be 0 or 1. If 0, each row represents a sample spectrum.
Returns
-------
pandas.DataFrame
Dataframe containing spectral indices of the samples.
See Also
--------
vegeind_summary
"""
# Validate input spectral data array
spec_array = np.array(spec_array).astype('float32')
if np.isnan(spec_array).any():
raise ValueError('spec_array contains NaN values.')
# Validate axis
if axis == 1:
spec_array = spec_array.T
elif axis != 0:
raise ValueError(f'axis must be 0 or 1, got: {axis}.')
wavelength = np.array(wavelength).astype('float32')
# Validate data and wavelengths
if len(wavelength) != spec_array.shape[1]:
raise ValueError(
f'Length of given wavelength does not match the band number of spec_array, \
got wavelength length: {len(wavelength)}, \
got band number of given spec_array: {spec_array.shape[1]}'
)
if (min(wavelength) > 675) | (max(wavelength) < 800):
raise ValueError(
'Index requires wavelength range (675, 800) '
+ f'exceeds given wavelength range ({min(wavelength)}, {max(wavelength)}).'
)
# Spectra indices of the wavelength
r800_ind = (np.abs(wavelength - 800)).argmin()
r675_ind = (np.abs(wavelength - 675)).argmin()
r800 = spec_array[:, r800_ind]
r675 = spec_array[:, r675_ind]
r800[r800 == 0] = 1e-15
r675[r675 == 0] = 1e-15
# Compute vegetation indices
sr_values = r800 / r675
sr_values = np.array([sr_values])
if axis == 0:
df_vi = pd.DataFrame(sr_values.T, columns=['SR'])
else:
df_vi = pd.DataFrame(sr_values, index=['SR'])
return df_vi
# %% Local test
# from specpipe.vegeind.demo_data import create_vegeind_demo_data
# specdata = create_vegeind_demo_data()
# vidata = sr(specdata, wavelength=specdata.columns)