Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Water Quality Mapping with PRISM L2A Reflectance

Overview

The Portable Remote Imaging SpectroMeter (PRISM) is a compact airborne pushbroom imaging spectrometer built by the NASA Jet Propulsion Laboratory (JPL) for studying coastal and inland waters. PRISM instrument covers spectral range from 350–1050 nm with with a 2.83 nm sampling per pixel, and a 0.88 mrad instantaneous field of view, with 608 cross-track pixels in a pushbroom configuration (Mouroulis et al., 2014).

In October-November 2023, NASA Gulfstream III flew PRISM instrument for the Biodiversity Survey of the Cape (BioSCape), a NASA and South African National Space Agency (SANSA) campaign to study the terrestrial and aquatic biodiversity of South Africa’s Greater Cape Floristic Region (Cardoso et al., 2025).

In this tutorial, we use PRISM Level 2A (L2A) surface reflectance from BioSCape campaign, for two water quality case studies:

  1. A red tide in Gordon’s Bay. On 15 November 2023, PRISM imaged a bloom of Noctiluca scintillans in Gordon’s Bay in the Western Cape province of South Africa. The bloom discolored the water and prompted local news reports warning residents to avoid swimming. We compare bloom and non-bloom spectra and map two chlorophyll indicators.

  2. Turbidity in Theewaterskloof Dam. Theewaterskloof is the largest dam in the Western Cape Water Supply System, which supplies Cape Town and the surrounding region. After heavy rain in the winter and spring of 2023, the dam was full. On 26 November 2023, PRISM imaged its turbid water, and we map turbidity with a semi-empirical algorithm.

Dataset Used

DatasetDOIShort Name
BioSCape: PRISM L2A Orthocorrected Surface ReflectanceGreenberg et al. (2026)BioSCape_PRISM_L2A_RFL_2494

The L2A reflectance was retrieved from (PRISM L1B radiance) with an optimal estimation atmospheric correction, the same method used for correction of the EMIT datasets (Thompson et al., 2020). The optimal estimation also produces a per-band reflectance uncertainty for every pixel. Please refer to the user guide for full dataset documentation.

Learning Objectives

  • Search for PRISM BioSCape L2A granules over a site with earthaccess, and open/stream selected files

  • Compare reflectance spectra and their uncertainties

  • Compute fluorescence line height (FLH), a chlorophyll index (NDCI), and turbidity

  • Export the derived maps as Cloud Optimized GeoTIFFs

Prerequisites

A free NASA Earthdata Login account is required. See the prerequisites page for setup instructions.

Import Libraries

Authentication

Use earthaccess.login() to authenticate with your NASA Earthdata Login credentials. If you have a .netrc file configured, login will complete without a prompt.

Search for the Collection

We search NASA’s Common Metadata Repository (CMR) for the collection by its DOI.

Title      : BioSCape: PRISM L2A Orthocorrected Surface Reflectance
ShortName  : BioSCape_PRISM_L2A_RFL_2494
Temporal   : 2023-10-17T00:00:00.000Z to 2023-11-26T23:59:59.999Z
Total size : 4.657 TB

Search for the Granules

Each PRISM flight line is split into scenes, and each granule is one scene. A granule contains four files that share the naming convention <flight line>_<scene>_L2A_OE_<version>_<product>:

ProductFormatContents
RFL_ORTNetCDFOrthorectified surface reflectance, plus atmospheric and surface state variables
UNC_ORTNetCDFReflectance uncertainty (one standard deviation)
RFL_ORT_QLGeoTIFFRGB quicklook
.yamlYAMLProcessing information

The flight line identifier prmYYYYMMDDthhmmss gives the date and start time of the flight line in UTC.

We search a dataset using its shortname BioSCape_PRISM_L2A_RFL_2494.

Granules found: 10202

We convert the granule metadata into a geopandas dataframe and plot the granule bounds.

Loading...

Export granule bounds for GIS (Optional)

We can optionally export the granule bounds into a GIS format such as geojson, kml or shapefile for further analysis in a desktop GIS.

Search granules for the two sites

We search for granules over each site on the day it was imaged.

Loading...

Several flight lines crossed each site. We map the footprints of all matching scenes.

Loading...

We will use the scenes from the original workshop tutorials:

  • Gordon’s Bay: flight line prm20231115t092332, scene 003, which covers the bloom in the bay

  • Theewaterskloof Dam: flight line prm20231126t080430, scene 002, which covers the northern part of the dam

Each granule has a public browse image, which we display below.

Loading...
Loading...

Open the Granules

As we see above, each scene contains multiple granules. Here we only need the reflectance (RFL_ORT.nc) for both scenes, and the uncertainty (UNC_ORT.nc) for Gordon’s Bay, so we pass those file links directly and open and stream the granule with earthaccess.open().

Loading...

Loading...

Loading...

[<File-like object HTTPFileSystem, https://data.ornldaac.earthdata.nasa.gov/protected/bioscape/BioSCape_PRISM_L2A_RFL/data/prm20231115t092332_003_L2A_OE_3a030e80_RFL_ORT.nc>, <File-like object HTTPFileSystem, https://data.ornldaac.earthdata.nasa.gov/protected/bioscape/BioSCape_PRISM_L2A_RFL/data/prm20231115t092332_003_L2A_OE_3a030e80_UNC_ORT.nc>, <File-like object HTTPFileSystem, https://data.ornldaac.earthdata.nasa.gov/protected/bioscape/BioSCape_PRISM_L2A_RFL/data/prm20231126t080430_002_L2A_OE_3a030e80_RFL_ORT.nc>]

Explore the File Structure

We open one of the reflectance file as an xarray.DataTree to view its groups.

Loading...

The reflectance file has three parts:

GroupVariablesDescription
/ (root)easting, northing, transverse_mercatorUTM coordinates of the pixel centers and the coordinate reference system (CRS)
/reflectancereflectance, wavelength, fwhmSurface reflectance with dimensions (wavelength, northing, easting)
/state_variablessurface_classification, aerosol_optical_thickness, water_vapor, sun_glint, sky_glint, ...Atmospheric and surface properties retrieved with the reflectance. surface_classification is 0 for land and 1 for water.

The uncertainty file (UNC_ORT) has the same layout, with an /uncertainty group in place of /reflectance. The nodata value is -9999, which xarray masks as no data or NaN.

Function to open a PRISM L2A file

The function below opens a PRISM L2A file and returns each group as an xarray.Dataset with coordinates and the CRS attached. We will use this open_prism_l2a function throughout this notebook

Part 1: A Red Tide in Gordon’s Bay

Now, let’s open the reflectance and uncertainty file from Gordon’s Bay and print its details.

prm20231115t092332_003_L2A_OE_3a030e80_RFL_ORT.nc
CRS        : WGS 84 / UTM zone 34S
Pixel size : 3.5 m
Shape      : {'wavelength': 246, 'y': 911, 'x': 784}

True-Color Image and Surface Classification

We make a true-color image from the red (657 nm), green (546 nm), and blue (481 nm) bands. Water is much darker than land, so we calculate the contrast stretch from water pixels only. This brings out color differences in the water and saturates the land.

We first define two python functions - one for true color and another for contrast stretching. These functions we will use later throughout the notebook.

We plot the true color RGB image with the contrast stretched for water, along with the land/water surface classification.

<Figure size 1600x800 with 3 Axes>

The bloom is visible as long, bright, cream-colored streaks with red-orange fringes running north to south through the bay. These are dense surface accumulations of Noctiluca. Notice that the classification marks the brightest streaks as land. Their reflectance in the near infrared is high, like land. Note that if we mask with surface_classification, the densest parts of the bloom will be removed.

Compare Spectra with Uncertainty

We compare spectra at four locations - the core of a bloom streak, the red water at its edge, turquoise water, and dark water away from the bloom. The uncertainty file gives the one-standard-deviation uncertainty for each band, which we plot as a shaded band around each spectrum.

<Figure size 1600x800 with 2 Axes>

The spectra differ clearly:

  • The bloom streak is bright from ~560 nm into the near infrared (NIR), with a dip near 675 nm where chlorophyll-a absorbs light. Its shape resembles field spectra of dense Noctiluca blooms (Mol et al., 2007).

  • The red water peaks near 580–600 nm and has a second peak near 700 nm. This 700 nm peak is typical of high-biomass water.

  • The turquoise and dark water peak in the green (~570 nm) and fall close to zero in the NIR, as expected for water.

  • The red water and dark water spectra are negative near near-UV and blue, and the red water spectrum is also negative beyond ~940 nm. This is typical of the red water across the bloom.

Negative Reflectance

Water is dark, so small errors in the atmospheric correction can produce large relative errors in water reflectance. Before we apply an algorithm, we check how many water pixels have negative reflectance in each band.

<Figure size 1200x400 with 1 Axes>

In the figure above,

  • In the NIR (> 720 nm), water absorbs almost all light and its true reflectance is close to zero. Small random errors therefore make many pixels slightly negative. This is expected.

  • In the violet and blue (412 and 443 nm), water reflectance should be clearly above zero, yet most water pixels are negative. This shows a systematic over-correction at short wavelengths over this water.

  • In the green, red, and red-edge bands (550–710 nm), reflectance is almost always positive.

We use two of the red and red-edge algorithms that avoid the blue bands. They are also better suited to high-biomass, optically complex water like a red tide.

Fluorescence Line Height (FLH)

Chlorophyll-a in phytoplankton re-emits some of the light it absorbs as fluorescence, with a peak near 681 nm. Fluorescence line height measures the height of that peak above a straight baseline drawn between two neighboring bands (Letelier and Abbott, 1996):

FLH=Rrs(λfl)−[Rrs(λlow)+λfl−λlowλhigh−λlow×(Rrs(λhigh)−Rrs(λlow))]FLH = R_{rs}(\lambda_{fl}) - \left[R_{rs}(\lambda_{low}) + \frac{\lambda_{fl}-\lambda_{low}}{\lambda_{high}-\lambda_{low}} \times \left(R_{rs}(\lambda_{high}) - R_{rs}(\lambda_{low})\right)\right]

where λfl≈\lambda_{fl} \approx 681 nm is the fluorescence band, and λlow≈\lambda_{low} \approx 665 nm and λhigh≈\lambda_{high} \approx 750 nm are the baseline bands.

FLH is usually calculated from water-leaving radiance (RrsR_{rs}, in sr⁻¹), and not using remote sensing reflectance. The key difference is:

  • Water-leaving radiance is the radiance that exits the water and is detected by a satellite sensor after traveling through the atmosphere.

  • Remote sensing reflectance is the ratio of water-leaving radiance to downwelling irradiance just above the surface of the water, representing a normalized reflectance value.

The calculation of FLH is done directly from the radiance values because the fluorescence signal itself is an addition to the radiance at that wavelength, caused by chlorophyll fluorescence in the water column. It captures the deviation in radiance at the chlorophyll fluorescence wavelength (around 681 nm) compared to the baseline radiance, which is estimated by interpolating between radiance at surrounding bands.

We define two python functions for a baseline-subtracted peak heights, which we will use later.

The L2A product is a reflectance factor, which for water is πRrs\pi R_{rs}, so we divide by π\pi to get RrsR_{rs}. We then compute FLH for each points.

Bloom streak FLH = -0.01011 sr-1
Red water FLH = -0.00069 sr-1
Turquoise water FLH = 0.00013 sr-1
Dark water FLH = 0.00021 sr-1

FLH is positive in the turquoise and dark water, but negative in the bloom streak and red water.

Normalized Difference Chlorophyll Index (NDCI)

FLH works best at low to moderate chlorophyll-a. In dense blooms, strong chlorophyll absorption near 675 nm and high particle scattering shift the red reflectance peak toward ~700 nm, which we saw in the red water spectrum, and FLH can become negative. The Normalized Difference Chlorophyll Index (Mishra and Mishra, 2012) uses that ~708 nm peak instead:

NDCI=R(708)−R(665)R(708)+R(665)NDCI = \frac{R(708) - R(665)}{R(708) + R(665)}

NDCI increases with chlorophyll-a concentration and was designed for turbid, productive water. Because it is a normalized difference, it can be calculated directly from the reflectance factor.

Bloom streak     NDCI = 0.409
Red water        NDCI = 0.159
Turquoise water  NDCI = -0.455
Dark water       NDCI = 0.025

Map FLH and NDCI

We map both indices side by side.

<Figure size 1200x600 with 4 Axes>

The two indicators show different patterns:

  • FLH is highest in a narrow band of nearshore water along the northeast shore, and lowest in and around the bloom streaks.

  • NDCI is highest in the red water around the bloom streaks, and lowest in the turquoise water in the center of the bay.

NDCI highlights the dense bloom, where FLH is suppressed. Converting either index into chlorophyll-a concentration requires calibration with field measurements.

Export Maps as a Cloud Optimized GeoTIFF

We stack FLH and NDCI into one dataset and export it as a Cloud Optimized GeoTIFF (COG), with one named band per variable. The helper function below also writes the CRS to the GeoTIFF.

Saved: prm20231115t092332_003_L2A_OE_3a030e80_FLH_NDCI.nc.tif

Part 2: Turbidity in Theewaterskloof Dam

Turbidity measures the cloudiness of water caused by suspended particles such as silt, clay, organic matter, plankton, and other microscopic organisms. High turbidity reduces light penetration and aquatic plant growth, can harm fish and other aquatic organisms, and is an important measure of water quality.

True-Color RGB Image and Water Spectra

We open the Theewaterskloof Dam scene and print summary details.

prm20231126t080430_002_L2A_OE_3a030e80_RFL_ORT.nc
CRS        : WGS 84 / UTM zone 34S
Pixel size : 2.6 m
Shape      : {'wavelength': 246, 'y': 801, 'x': 1387}

Now, we plot a true-color RGB image, and compare the spectra of three water pixels.

<Figure size 1000x400 with 2 Axes>

The dam water reflectance rises from the blue to a broad maximum between ~600 and ~700 nm, then drops sharply after 710 nm. A small peak near 810 nm is typical of water with high suspended sediment. As with Gordon’s Bay, the blue reflectance is negative, but turbidity algorithms use the red and NIR bands.

Calculate Turbidity

We use the single-band, semi-empirical turbidity algorithm of Nechad et al. (2010), with the coefficients of Dogliotti et al. (2015):

T=A ρw1−ρw/CT = \frac{A \, \rho_w}{1 - \rho_w / C}

where TT is turbidity in Formazin Nephelometric Units (FNU), ρw\rho_w is the water leaving reflectance, and AA and CC are calibration coefficients for a given wavelength:

WavelengthAC
645 nm (red)228.10.1641
859 nm (NIR)3078.90.2112

The red band is sensitive in low-to-moderate turbidity but saturates in very turbid water. The NIR band is used in very turbid water. Dogliotti switch between them based on ρw\rho_w at 645nm, the red algorithm below 0.05, the NIR algorithm above 0.07, and a linear blend in between.

Median rho_w(645): 0.0678

We compute Nechad’s turbidity for red and NIR bands as well as Dogliotti’s blended turbidity over water as ρw\rho_w.

Loading...

Compare the Turbidity Maps

<Figure size 800x1200 with 6 Axes>

Export the Turbidity Maps

We export the three turbidity estimates as a three-band COG. The GeoTIFFs can be opened in GIS software such as QGIS for further analysis.

Saved: prm20231126t080430_002_L2A_OE_3a030e80_turbidity.nc.tif
References
  1. Mouroulis, P., Van Gorp, B., Green, R. O., Dierssen, H., Wilson, D. W., Eastwood, M., Boardman, J., Gao, B.-C., Cohen, D., Franklin, B., Loya, F., Lundeen, S., Mazer, A., McCubbin, I., Randall, D., Richardson, B., Rodriguez, J. I., Sarture, C., Urquiza, E., … Yee, K. (2014). Portable Remote Imaging Spectrometer coastal ocean sensor: design, characteristics, and first flight results. Applied Optics, 53(7), 1363. 10.1364/ao.53.001363
  2. Cardoso, A. W., Hestir, E. L., Slingsby, J. A., Forbes, C. J., Moncrieff, G. R., Turner, W., Skowno, A. L., Nesslage, J., Brodrick, P. G., Gaddis, K. D., & Wilson, A. M. (2025). The biodiversity survey of the Cape (BioSCape), integrating remote sensing with biodiversity science. Npj Biodiversity, 4(1). 10.1038/s44185-024-00071-5
  3. Greenberg, E., Brodrick, P. G., Thompson, D. R., Chlus, A. M., Bohn, N., Montgomery, J., Chapman, J. W., Wilder, B. A., Eastwood, M., Lundeen, S. R., Eckert, R., Olson-Duvall, W., Kokaly, R. F., & Green, R. O. (2026). BioSCape: PRISM L2A Orthocorrected Surface Reflectance. ORNL Distributed Active Archive Center. 10.3334/ORNLDAAC/2494
  4. Thompson, D. R., Eckert, R., Bernas, M., Brodrick, P. G., Chlus, A. M., Chapman, J. W., Eastwood, M., Bender, H., Geier, S., Greenberg, E., Keymeulen, D., Liggett, E., Olson-Duvall, W., Rios, L. M., Brunner, E. D., Jensen, D. J., Zandbergen, S., Small, Z., Shaw, L. A., … Green, R. O. (2026). BioSCape: PRISM L1B Calibrated Radiance. ORNL Distributed Active Archive Center. 10.3334/ORNLDAAC/2493
  5. Letelier, R. (1996). An analysis of chlorophyll fluorescence algorithms for the moderate resolution imaging spectrometer (MODIS). Remote Sensing of Environment, 58(2), 215–223. 10.1016/s0034-4257(96)00073-9
  6. Mishra, S., & Mishra, D. R. (2012). Normalized difference chlorophyll index: A novel model for remote estimation of chlorophyll-a concentration in turbid productive waters. Remote Sensing of Environment, 117, 394–406. 10.1016/j.rse.2011.10.016
  7. 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. 10.1016/j.rse.2009.11.022
  8. 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. 10.1016/j.rse.2014.09.020