Skip to content

Add NDSI, NDBI, BAI, MSAVI2, and OSAVI spectral indices #1037

@brendancol

Description

@brendancol

Author of Proposal:

Reason or problem

The multispectral module has 13 spectral indices but is missing several that come up often in practice. Snow mapping needs NDSI. Urban land use work needs NDBI. BAI gives a different angle on burn scars than NBR -- it measures spectral distance to a charcoal point instead of a band ratio. MSAVI2 and OSAVI handle soil brightness better than SAVI because they don't require guessing an L parameter.

Proposal

Add five spectral indices to xrspatial/multispectral.py:

  1. NDSI (Normalized Difference Snow Index) -- (green - SWIR1) / (green + SWIR1). Separates snow/ice from clouds. Hall et al. 1995.
  2. NDBI (Normalized Difference Built-up Index) -- (SWIR1 - NIR) / (SWIR1 + NIR). Picks out built-up areas. Zha et al. 2003.
  3. BAI (Burn Area Index) -- 1 / ((0.1 - red)^2 + (0.06 - NIR)^2). Spectral distance to a charcoal reflectance point. Chuvieco et al. 2002.
  4. MSAVI2 (Modified Soil Adjusted Vegetation Index) -- (2*NIR + 1 - sqrt((2*NIR + 1)^2 - 8*(NIR - red))) / 2. Self-adjusting soil line, no L parameter. Qi et al. 1994.
  5. OSAVI (Optimized Soil Adjusted Vegetation Index) -- (NIR - red) / (NIR + red + 0.16). SAVI with fixed L=0.16, tuned for sparse canopy. Rondeaux et al. 1996.

Design:

  • NDSI and NDBI reuse the existing _normalized_ratio kernels (same path as NDVI, NDWI, NBR, etc.)
  • BAI and MSAVI2 need dedicated CPU/GPU kernels -- their formulas don't fit the normalized ratio pattern
  • OSAVI is structurally identical to SAVI but with fixed L=0.16 and without the (1 + L) multiplier
  • All five follow the 4-backend pattern (numpy, cupy, dask+numpy, dask+cupy)
  • All five use @supports_dataset_bands for Dataset band mapping

Usage:

from xrspatial.multispectral import ndsi, ndbi, bai, msavi2, osavi

# Standalone DataArrays
snow = ndsi(green_agg=green, swir1_agg=swir1)
urban = ndbi(swir1_agg=swir1, nir_agg=nir)
burn = bai(red_agg=red, nir_agg=nir)
veg = msavi2(nir_agg=nir, red_agg=red)
veg2 = osavi(nir_agg=nir, red_agg=red)

# Dataset with band mapping
snow = ndsi(ds, green='B3', swir1='B11')

Stakeholders and impacts

Anyone doing snow mapping, urban classification, burn scar analysis, or vegetation work where soil background is a problem. No breaking changes.

Drawbacks

None worth mentioning. Each index is about 20 lines of band arithmetic following the existing pattern.

Alternatives

Users can do this manually with xarray arithmetic, but they lose backend dispatch, GPU acceleration, and Dataset band mapping.

Unresolved questions

None. The formulas and band requirements are long-established.

Additional notes

NDSI and NDBI reuse existing normalized ratio kernels. BAI and MSAVI2 need new kernels. OSAVI reuses the SAVI structure with fixed parameters.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions