Skip to content

Water Quality

Module to compute water quality parameters from atmospherically corrected images.

Overview

The gee_acolite.water_quality module provides functions to calculate various water quality parameters using atmospherically corrected remote sensing reflectance bands. Includes:

  • SPM: Suspended Particulate Matter
  • Turbidity: Water turbidity
  • Chlorophyll-a: Chlorophyll-a concentration
  • pSDB: Pseudo-Satellite Derived Bathymetry
  • Quality masks: To filter invalid pixels

Flow Diagram

flowchart TD A[Corrected Image] --> B[compute_water_mask] B --> C{Valid Mask?} C -->|Yes| D[compute_water_bands] C -->|No| E[Masked Pixel] D --> F[SPM Nechad] D --> G[Turbidity Dogliotti] D --> H[Chlorophyll-a OC3] D --> I[pSDB Stumpf] F --> J[Image with Products] G --> J H --> J I --> J style A fill:#e1f5ff style J fill:#e1ffe1 style C fill:#fff3e1

Available Products

graph LR A[Rrs Reflectance] --> B[SPM] A --> C[Turbidity] A --> D[Chlorophyll-a] A --> E[pSDB] B --> B1[spm_nechad_R665] B --> B2[spm_nechad_R865] C --> C1[turbidity_dogliotti_R665] C --> C2[turbidity_dogliotti_R865] D --> D1[chlor_a_oc3] E --> E1[pSDB_red] E --> E2[pSDB_green] style A fill:#e1f5ff style B fill:#ffe1e1 style C fill:#e1ffe1 style D fill:#ffe1ff style E fill:#ffffcc

Main Functions

compute_water_mask

compute_water_mask(image: Image, settings: dict) -> Image

Create a comprehensive water mask for quality control.

Combines multiple masking criteria to identify valid water pixels: - Water/land classification - Cirrus cloud contamination - High TOA reflectance (clouds, bright targets) - Optional cloud probability mask

Parameters:

Name Type Description Default
image Image

Atmospherically corrected image with TOA and surface reflectance bands.

required
settings dict

Processing settings including mask thresholds.

required

Returns:

Type Description
Image

Binary mask where valid water pixels = 1.

Source code in gee_acolite/water_quality.py
def compute_water_mask(image: ee.Image, settings: dict) -> ee.Image:
    """
    Create a comprehensive water mask for quality control.

    Combines multiple masking criteria to identify valid water pixels:
    - Water/land classification
    - Cirrus cloud contamination
    - High TOA reflectance (clouds, bright targets)
    - Optional cloud probability mask

    Parameters
    ----------
    image : ee.Image
        Atmospherically corrected image with TOA and surface reflectance bands.
    settings : dict
        Processing settings including mask thresholds.

    Returns
    -------
    ee.Image
        Binary mask where valid water pixels = 1.
    """
    mask = masks.non_water(image, 
                           threshold = settings.get('l2w_mask_threshold', 0.05))
    mask = mask.updateMask(masks.cirrus_mask(image, 
                                             threshold = settings.get('l2w_mask_cirrus_threshold', 0.005)))

    for band in SENTINEL2_BANDS:
        mask = mask.updateMask( masks.toa_mask(image, 
                                                f'rhot_{band}', 
                                                settings.get('l2w_mask_high_toa_threshold', 0.3)) )

    if settings.get('s2_cloud_proba', False):
        CLOUD_PROB_THRESHOLD = int(settings.get('s2_cloud_proba__cloud_threshold', 50))
        NIR_DARK_THRESHOLD = float(settings.get('s2_cloud_proba__nir_dark_threshold', 0.15))
        CLOUD_PROJ_DISTANCE = int(settings.get('s2_cloud_proba__cloud_proj_distance', 10))
        BUFFER = int(settings.get('s2_cloud_proba__buffer', 50))
        mask = mask.updateMask(masks.cld_shdw_mask(masks.add_cld_shdw_mask(image, 
                                                                           CLOUD_PROB_THRESHOLD, 
                                                                           NIR_DARK_THRESHOLD, 
                                                                           CLOUD_PROJ_DISTANCE, 
                                                                           BUFFER)))

    return mask

compute_water_bands

compute_water_bands(image: Image, settings: dict) -> Image

Compute water quality parameters and add them as bands.

Applies water mask and computes selected water quality parameters based on settings configuration.

Parameters:

Name Type Description Default
image Image

Atmospherically corrected image with surface reflectance bands.

required
settings dict

Processing settings including 'l2w_parameters' list.

required

Returns:

Type Description
Image

Input image with added water quality parameter bands.

Source code in gee_acolite/water_quality.py
def compute_water_bands(image: ee.Image, settings: dict) -> ee.Image:
    """
    Compute water quality parameters and add them as bands.

    Applies water mask and computes selected water quality parameters
    based on settings configuration.

    Parameters
    ----------
    image : ee.Image
        Atmospherically corrected image with surface reflectance bands.
    settings : dict
        Processing settings including 'l2w_parameters' list.

    Returns
    -------
    ee.Image
        Input image with added water quality parameter bands.
    """
    mask = compute_water_mask(image, settings)
    median_radius = int(settings.get('psdb_median_radius', 1))

    for product in settings['l2w_parameters']:
        fn = PRODUCTS[product]
        if product in ('pSDB_green', 'pSDB_red'):
            new_band = fn(image, median_radius=median_radius)
        else:
            new_band = fn(image)

        if isinstance(new_band, list):
            new_band = [band.updateMask(mask) for band in new_band]
        else:
            new_band = new_band.updateMask(mask)

        image = image.addBands(new_band)

    return image

Individual Products

SPM (Suspended Particulate Matter)

graph LR A[Rrs] --> B[Nechad Algorithm] B --> C[SPM mg/L] style B fill:#ffe1e1

spm_nechad2016_665

spm_nechad2016_665(image: Image) -> Image

Compute suspended particulate matter using Nechad et al. (2016) algorithm at 665nm.

Parameters:

Name Type Description Default
image Image

Image with surface reflectance band 'rhos_B4' (red band at 665nm).

required

Returns:

Type Description
Image

SPM concentration in mg/L.

References

Nechad et al. (2016). Calibration and validation of a generic multisensor algorithm for mapping of total suspended matter in turbid waters. Remote Sensing of Environment, 159, 139-152.

Turbidity

tur_nechad2016_665

tur_nechad2016_665(image: Image) -> Image

Compute turbidity using Nechad et al. (2016) algorithm at 665nm.

Parameters:

Name Type Description Default
image Image

Image with surface reflectance band 'rhos_B4' (red band at 665nm).

required

Returns:

Type Description
Image

Turbidity in FNU (Formazin Nephelometric Units).

Chlorophyll-a

chl_oc3

chl_oc3(image: Image) -> Image

Compute chlorophyll-a concentration using OC3 algorithm.

Three-band ratio algorithm using maximum of blue bands vs green. More robust than OC2 for varying water types.

Parameters:

Name Type Description Default
image Image

Image with surface reflectance bands 'rhos_B1', 'rhos_B2' (blue), and 'rhos_B3' (green).

required

Returns:

Type Description
Image

Chlorophyll-a concentration in mg/m³.

Notes

TODO: Interpolate B1 to B2 dimensions for improved accuracy.

pSDB (Pseudo-Satellite Derived Bathymetry)

pSDB_green

pSDB_green(image: Image, median_radius: int = 1) -> Image

Compute pseudo-Satellite Derived Bathymetry using blue-green ratio.

Implements log-ratio bathymetry algorithm using blue and green bands. Generally more sensitive in clearer waters compared to blue-red ratio.

A median filter is applied to the Rrs bands before the ratio to reduce radiometric noise, following Caballero & Stumpf (2019).

Parameters:

Name Type Description Default
image Image

Image with remote sensing reflectance bands 'Rrs_B2' (blue) and 'Rrs_B3' (green).

required
median_radius int

Radius in pixels for the circular median kernel (default: 1 → 3×3). Set to 0 to disable smoothing.

1

Returns:

Type Description
Image

Pseudo-bathymetry index (higher values = shallower water).

References

Caballero, I., & Stumpf, R. P. (2019). Retrieval of nearshore bathymetry from Sentinel-2A and 2B satellites in South Florida coastal waters. Estuarine, Coastal and Shelf Science, 226, 106277. https://doi.org/10.1016/j.ecss.2019.106277

Product Catalog

PRODUCTS module-attribute

PRODUCTS = {'spm_nechad2016': spm_nechad2016_665, 'spm_nechad2016_704': spm_nechad2016_704, 'spm_nechad2016_740': spm_nechad2016_740, 'tur_nechad2016': tur_nechad2016_665, 'tur_nechad2016_704': tur_nechad2016_704, 'tur_nechad2016_740': tur_nechad2016_740, 'chl_oc2': chl_oc2, 'chl_oc3': chl_oc3, 'chl_re_mishra': chl_re_mishra, 'pSDB_red': pSDB_red, 'pSDB_green': pSDB_green, 'Rrs_*': rrs}

Usage Example

import ee
from gee_acolite.water_quality import compute_water_bands, compute_water_mask, PRODUCTS

# Atmospherically corrected image
corrected_image = corrected_images.first()

# Create water mask
water_mask = compute_water_mask(corrected_image, settings)

# Compute all water products
water_params = compute_water_bands(
    corrected_image,
    settings,
    products=list(PRODUCTS.keys())
)

# Apply mask
water_params_masked = water_params.updateMask(water_mask)

# Example: get only SPM and Turbidity
selected_products = compute_water_bands(
    corrected_image,
    settings,
    products=['spm_nechad_R665', 'turbidity_dogliotti_R665']
)

# Visualize on map
Map = geemap.Map()
Map.centerObject(roi, 10)
Map.addLayer(water_params_masked.select('spm_nechad_R665'), 
             {'min': 0, 'max': 50, 'palette': ['blue', 'green', 'yellow', 'red']}, 
             'SPM')

Implemented Algorithms

Nechad et al. (2010) - SPM

Empirical algorithm calibrated for optical sensors:

\[ SPM = A_\lambda \cdot \frac{\rho_w(\lambda)}{1 - \rho_w(\lambda) / C_\lambda} \]

Where: - \(\rho_w(\lambda)\) = water reflectance - \(A_\lambda\), \(C_\lambda\) = band-specific coefficients

Dogliotti et al. (2015) - Turbidity

Switching algorithm for clear and turbid waters:

\[ Turb = \begin{cases} A_T \cdot \rho_w(\lambda) & \text{if } \rho_w < \rho_{threshold} \\ C \cdot \frac{\rho_w(\lambda)}{1 - \rho_w(\lambda) / D} & \text{if } \rho_w \geq \rho_{threshold} \end{cases} \]

OC3 - Chlorophyll-a

Empirical algorithm based on band ratio:

\[ \log_{10}(Chl-a) = a_0 + a_1 R + a_2 R^2 + a_3 R^3 + a_4 R^4 \]

Where: $$ R = \log_{10}\left(\frac{\max(Rrs_{443}, Rrs_{492})}{Rrs_{560}}\right) $$

Stumpf et al. (2003) - pSDB

Relative bathymetry using logarithmic ratio:

\[ pSDB = m_1 \cdot \frac{\ln(n \cdot Rrs_{\text{blue}})}{\ln(n \cdot Rrs_{\text{red}})} - m_0 \]

References

  • Nechad, B., Ruddick, K. G., & Park, Y. (2010). Calibration and validation of a generic multisensor algorithm for mapping of total suspended matter in turbid waters. Remote Sensing of Environment, 114(4), 854-866.
  • Dogliotti, A. I., Ruddick, K. G., Nechad, B., Doxaran, D., & Knaeps, E. (2015). A single algorithm to retrieve turbidity from remotely-sensed data in all coastal and estuarine waters. Remote Sensing of Environment, 156, 157-168.
  • Stumpf, R. P., Holderied, K., & Sinclair, M. (2003). Determination of water depth with high‐resolution satellite imagery over variable bottom types. Limnology and Oceanography, 48(1part2), 547-556.