3 Equivalent Water Thickness/Canopy Water Content from Imaging Spectroscopy Data

Summary

In this notebook we will explore how Equivalent Water Thickness (EWT) or Canopy Water Content (CWC) can be calculated from the Earth Surface Mineral Dust Source Investigation (EMIT) L2A Reflectance Product, then we will apply this knowledge to calculate CWC over the Jack and Laura Dangermond Preserve located near Santa Barbara, CA.

Background

Equivalent Water Thickness (EWT) is the predicted thickness or absorption path length in centimeters (cm) of water that would be required to yield an observed spectra. In the context of vegetation, this is equivalent to canopy water content (CWC) in g/cm^2 because a cm^3 of water has a mass of 1g.

CWC can be derived from surface reflectance spectra because they provide information about the composition of the target, including water content. Reflectance is the fraction of incoming solar radiation reflected by Earth’s surface. Different materials reflect varying proportions of radiation based upon their chemical composition and physical properties, giving materials their own unique spectral signature or fingerprint. In particular, liquid water causes characteristic absorption features to appear in the near-infrared wavelengths of the solar spectrum, which enables an estimation of its content.

CWC correlates with vegetation type and health, as well as wildfire risk. The methods used here to calculate CWC are based on the ISOFIT python package. The Beer-Lambert physical model used to calculate CWC is described in Green et al. (2006) and Bohn et al. (2020). It uses wavelength-dependent absorption coefficients of liquid water to determine the absorption path length as a function of absorption feature depth. Of note, this model does not account for multiple scattering effects within the canopy and may result in overestimation of CWC (Bohn et al., 2020).

The Jack and Laura Dangermond Preserve and its surrounding lands are one of the last “wild coastal” regions in Southern California. The preserve is over 24,000 acres and consists of several ecosystem types and is home to over 600 plant species and over 200 wildlife species.

More about the EMIT mission and EMIT products.

References

  • Shrestha, Rupesh. 2023. Equivalent water thickness/canopy water content from hyperspectral data. Jupyter Notebook. Oak Ridge National Laboratory Distributed Active Archive Center. https://github.com/rupesh2/ewt_cwc/tree/main
  • Bohn, N., L. Guanter, T. Kuester, R. Preusker, and K. Segl. 2020. Coupled retrieval of the three phases of water from spaceborne imaging spectroscopy measurements. Remote Sensing of Environment 242:111708. https://doi.org/10.1016/j.rse.2020.111708
  • Green, R.O., T.H. Painter, D.A. Roberts, and J. Dozier. 2006. Measuring the expressed abundance of the three phases of water with an imaging spectrometer over melting snow. Water Resources Research 42:W10402. https://doi.org/10.1029/2005WR004509
  • Thompson, D.R., V. Natraj, R.O. Green, M.C. Helmlinger, B.-C. Gao, and M.L. Eastwood. 2018. Optimal estimation for imaging spectrometer atmospheric correction. Remote Sensing of Environment 216:355–373. https://doi.org/10.1016/j.rse.2018.07.003

Requirements - NASA Earthdata Account
- No Python setup requirements if connected to the workshop cloud instance!
- Local Only Set up Python Environment - See setup_instructions.md in the /setup/ folder to set up a local compatible Python environment
- Downloaded necessary files. This is done at the end of the 01_Finding_Concurrent_Data notebook.

Learning Objectives
- Calculate CWC of a single pixel
- Calculate CWC of an ROI

Tutorial Outline

3.1 Setup
3.2 Opening EMIT Data
3.3 Extracting Reflectance of a Pixel
3.4 Calculating CWC
3.4.1 Single Point
3.4.2 DataFrame of Points
3.5 Applying Inversion in Parallel Across an ROI

3.1 Setup

Uncomment the below cell and install the ray python library. This is only necessary if using the 2i2c Cloud Instance.

# !pip install "ray[default]"

Import the required libraries.

# Import Packages
import os
# Some cells may generate warnings that we can ignore. Comment below lines to see.
import warnings
warnings.filterwarnings('ignore')

import numpy as np
import xarray as xr
from osgeo import gdal
import rasterio as rio
import rioxarray as rxr
from matplotlib import pyplot as plt
import hvplot.xarray
import hvplot.pandas
import pandas as pd
import earthaccess

from modules.emit_tools import emit_xarray
from modules.ewt_calc import calc_ewt
from scipy.optimize import least_squares

3.2 Open an EMIT File

EMIT L2A Reflectance Data are distributed in a non-orthocorrected spatially raw NetCDF4 (.nc) format consisting of the data and its associated metadata. To work with this data, we will use the emit_xarray function from the emit_tools.py module included in the repository.

3.2.1 Streaming Data

As we did in the previous notebook, to stream the data, log in using earthaccess, then start and https session and open the url for the scene.

# Login to NASA Earthdata
earthaccess.login(persist=True)

# Get Https Session using Earthdata Login Info
fs = earthaccess.get_fsspec_https_session()

# Define Local Filepath
fp = fs.open('https://data.lpdaac.earthdatacloud.nasa.gov/lp-prod-protected/EMITL2ARFL.001/EMIT_L2A_RFL_001_20230401T203751_2309114_002/EMIT_L2A_RFL_001_20230401T203751_2309114_002.nc')

3.2.2 Downloading Data

If you have downloaded the data, you can just set the filepath.

# fp = '../data/EMIT_L2A_RFL_001_20230401T203751_2309114_002.nc'

Open the file using the emit_xarray function.

ds = emit_xarray(fp,ortho=True).load()

Set the fill values equal to np.nan improved visualization.

ds.reflectance.data[ds.reflectance.data == -9999] = np.nan

Similarly to what we did in the previous notebook, we can plot a single band to get an idea of where our scene is. This is the same scene we processed earlier.

emit_layer = ds.sel(wavelengths=850,method='nearest')
emit_layer.hvplot.image(cmap='viridis',geo=True, tiles='ESRI', frame_width=720,frame_height=405, alpha=0.7, fontscale=2).opts(
    title=f"{emit_layer.wavelengths:.3f} {emit_layer.wavelengths.units}", xlabel='Longitude',ylabel='Latitude')
Unable to display output for mime type(s): 

3.3 Extracting Reflectance of a Single Pixel

We can mask out the -.01 values used to represent the region of the spectra with strong atmospheric water vapor absorption features.

ds['reflectance'].data[:,:,ds['good_wavelengths'].data==0] = np.nan

Retrieve the spectra from a sample point by providing a latitude and longitude along with a method using the sel function. This will select the pixel closest to the provided coordinates.

point = ds.sel(latitude=34.5399,longitude=-120.3529, method='nearest')
point
<xarray.Dataset> Size: 5kB
Dimensions:           (wavelengths: 285)
Coordinates:
  * wavelengths       (wavelengths) float32 1kB 381.0 388.4 ... 2.493e+03
    fwhm              (wavelengths) float32 1kB 8.415 8.415 ... 8.807 8.809
    good_wavelengths  (wavelengths) float32 1kB 1.0 1.0 1.0 1.0 ... 1.0 1.0 1.0
    latitude          float64 8B 34.54
    longitude         float64 8B -120.4
    elev              float32 4B 124.2
    spatial_ref       int64 8B 0
Data variables:
    reflectance       (wavelengths) float32 1kB 0.01718 0.01754 ... 0.02455
Attributes: (12/40)
    ncei_template_version:             NCEI_NetCDF_Swath_Template_v2.0
    summary:                           The Earth Surface Mineral Dust Source ...
    keywords:                          Imaging Spectroscopy, minerals, EMIT, ...
    Conventions:                       CF-1.63
    sensor:                            EMIT (Earth Surface Mineral Dust Sourc...
    instrument:                        EMIT
    ...                                ...
    spatial_ref:                       GEOGCS["WGS 84",DATUM["WGS_1984",SPHER...
    geotransform:                      [-1.20992414e+02  5.42232520e-04 -0.00...
    day_night_flag:                    Day
    title:                             EMIT L2A Estimated Surface Reflectance...
    granule_id:                        EMIT_L2A_RFL_001_20230401T203751_23091...
    Orthorectified:                    True

We can plot this information to see the spectra.

point.hvplot.line(x='wavelengths',y='reflectance',color='black').opts(title=f"Latitude: {point.latitude.values:.3f} Longitude: {point.longitude.values:.3f}")
Unable to display output for mime type(s): 

3.4 Calculating CWC

As mentioned in the background we use the surface reflectance to estimate CWC. The unique spectral signatures allow identification and quantification based upon the wavelength-dependent absorption coefficients of liquid water. The EMIT mission has applied similar approaches to identify dust source minerals as well as methane point source emissions. The path length of liquid water absorption can be estimated by utilizing a least squares inversion to minimize the residuals between the EMIT reflectance and the Beer-Lambert model (Green et al.,2006), which relates the wavelength-dependent absorption to the path length the photons are traveling through the material. During the inversion, the path lengths are iteratively adjusted to match the modeled spectra to the EMIT reflectance within the water absorption feature region from 850 to 1100 nm.

First, define a Beer-Lambert Model function that returns the vector of residuals between measured and modeled surface reflectance.

# https://github.com/isofit/isofit/blob/main/isofit/inversion/inverse_simple.py#L514C1-L532C17
def beer_lambert_model(x, y, wl, alpha_lw):
    """Function, which computes the vector of residuals between measured and modeled surface reflectance optimizing
    for path length of surface liquid water based on the Beer-Lambert attenuation law.

    Args:
        x:        state vector (liquid water path length, intercept, slope)
        y:        measurement (surface reflectance spectrum)
        wl:       instrument wavelengths
        alpha_lw: wavelength dependent absorption coefficients of liquid water

    Returns:
        resid: residual between modeled and measured surface reflectance
    """

    attenuation = np.exp(-x[0] * 1e7 * alpha_lw)
    rho = (x[1] + x[2] * wl) * attenuation
    resid = rho - y

    return resid

We need some lab measurements of the complex refractive index of liquid water to obtain the wavelength-dependent absorption coefficients. They are calculated by taking four times the product of Pi and the imaginary part of the refractive index, divided by wavelength. The refractive index of liquid water per wavelength is provided by the k_liquid_water_ice.csv in the data folder. We can also preview this data to get a better understanding of the information we are using.

wp_fp = '../data/k_liquid_water_ice.csv'
k_wi = pd.read_csv(wp_fp)
k_wi.head()
wvl_1 T = 22°C wvl_2 T = -8°C wvl_3 T = -25°C wvl_4 T = -7°C wvl_5 T = 25°C (H) wvl_6 T = 20°C wvl_7 T = 25°C (S) Index
0 666.7 2.470000e-08 NaN NaN NaN NaN 660.0 1.660000e-08 650.0 1.640000e-08 650.0 1.870000e-08 650.12971 1.674130e-08 0
1 667.6 2.480000e-08 NaN NaN NaN NaN 670.0 1.890000e-08 675.0 2.230000e-08 651.0 1.890000e-08 654.63616 1.777420e-08 1
2 668.4 2.480000e-08 NaN NaN NaN NaN 680.0 2.090000e-08 700.0 3.350000e-08 652.0 1.910000e-08 660.69347 1.939950e-08 2
3 669.3 2.520000e-08 NaN NaN NaN NaN 690.0 2.400000e-08 725.0 9.150000e-08 653.0 1.940000e-08 665.27314 2.031380e-08 3
4 670.2 2.530000e-08 NaN NaN NaN NaN 700.0 2.900000e-08 750.0 1.560000e-07 654.0 1.970000e-08 669.88461 2.097930e-08 4
fig, axs = plt.subplots(2,4, figsize=(15, 6),  sharex=True, sharey=True, constrained_layout=True)
axs = axs.ravel()
col_n = 0
for i in range(0, 7):
    x = k_wi.iloc[:, col_n+i]
    y = k_wi.iloc[:, col_n+i+1]
    axs[i].scatter(x, y)
    axs[i].set_title(y.name)
    col_n+=1
fig.supylabel('imaginary parts of refractive index')
fig.supxlabel('wavelength')
plt.show()

Now define a function to get the desired data from the csv file.

# https://github.com/isofit/isofit/blob/dev/isofit/core/common.py#L461C1-L488C26
def get_refractive_index(k_wi, a, b, col_wvl, col_k):
    """Convert refractive index table entries to numpy array.

    Args:
        k_wi:    variable
        a:       start line
        b:       end line
        col_wvl: wavelength column in pandas table
        col_k:   k column in pandas table

    Returns:
        wvl_arr: array of wavelengths
        k_arr:   array of imaginary parts of refractive index
    """

    wvl_ = []
    k_ = []

    for ii in range(a, b):
        wvl = k_wi.at[ii, col_wvl]
        k = k_wi.at[ii, col_k]
        wvl_.append(wvl)
        k_.append(k)

    wvl_arr = np.asarray(wvl_)
    k_arr = np.asarray(k_)

    return wvl_arr, k_arr

Lastly, to calculate CWC we define a function that uses least squares optimization to minimize the residuals of our Beer-Lambert Model and find a likely path length of liquid water.

# https://github.com/isofit/isofit/blob/main/isofit/inversion/inverse_simple.py#L443C1-L511C24
def invert_liquid_water(
    rfl_meas: np.array,
    wl: np.array,
    l_shoulder: float = 850,
    r_shoulder: float = 1100,
    lw_init: tuple = (0.02, 0.3, 0.0002),
    lw_bounds: tuple = ([0, 0.5], [0, 1.0], [-0.0004, 0.0004]),
    ewt_detection_limit: float = 0.5,
    return_abs_co: bool = False,
):
    """Given a reflectance estimate, fit a state vector including liquid water path length
    based on a simple Beer-Lambert surface model.

    Args:
        rfl_meas:            surface reflectance spectrum
        wl:                  instrument wavelengths, must be same size as rfl_meas
        l_shoulder:          wavelength of left absorption feature shoulder
        r_shoulder:          wavelength of right absorption feature shoulder
        lw_init:             initial guess for liquid water path length, intercept, and slope
        lw_bounds:           lower and upper bounds for liquid water path length, intercept, and slope
        ewt_detection_limit: upper detection limit for ewt
        return_abs_co:       if True, returns absorption coefficients of liquid water

    Returns:
        solution: estimated liquid water path length, intercept, and slope based on a given surface reflectance
    """
    
    # Ensure least squares is done with float64 datatype (added)
    wl = np.float64(wl)
    
    # params needed for liquid water fitting
    lw_feature_left = np.argmin(abs(l_shoulder - wl))
    lw_feature_right = np.argmin(abs(r_shoulder - wl))
    wl_sel = wl[lw_feature_left : lw_feature_right + 1]

    # adjust upper detection limit for ewt if specified
    if ewt_detection_limit != 0.5:
        lw_bounds[0][1] = ewt_detection_limit

    # load imaginary part of liquid water refractive index and calculate wavelength dependent absorption coefficient
    # __file__ should live at isofit/isofit/inversion/
    
    
    data_dir_path = "../data/"
    path_k = os.path.join(data_dir_path,"k_liquid_water_ice.csv")
    
    #isofit_path = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
    #path_k = os.path.join(isofit_path, "data", "iop", "k_liquid_water_ice.xlsx")

    # k_wi = pd.read_excel(io=path_k, sheet_name="Sheet1", engine="openpyxl")
    # wl_water, k_water = get_refractive_index(
    #     k_wi=k_wi, a=0, b=982, col_wvl="wvl_6", col_k="T = 20°C"
    # )
    k_wi = pd.read_csv(path_k)
    wl_water, k_water = get_refractive_index(
        k_wi=k_wi, a=0, b=982, col_wvl="wvl_6", col_k="T = 20°C"
    )
    kw = np.interp(x=wl_sel, xp=wl_water, fp=k_water)
    abs_co_w = 4 * np.pi * kw / wl_sel

    rfl_meas_sel = rfl_meas[lw_feature_left : lw_feature_right + 1]

    x_opt = least_squares(
        fun=beer_lambert_model,
        x0=lw_init,
        jac="2-point",
        method="trf",
        bounds=(
            np.array([lw_bounds[ii][0] for ii in range(3)]),
            np.array([lw_bounds[ii][1] for ii in range(3)]),
        ),
        max_nfev=15,
        args=(rfl_meas_sel, wl_sel, abs_co_w),
    )

    solution = x_opt.x

    if return_abs_co:
        return solution, abs_co_w
    else:
        return solution

3.4.1 CWC of a Single Point

Now that we have all of the pieces, we can estimate the CWC of a single pixel using our invert_liquid_water function. By default there is a detection limit of 0.5, if we are hitting this threshold we can adjust by changing the ewt_detection_limit argument.

ewt = invert_liquid_water(point.reflectance.values,point.wavelengths.values)
print(f"EWT for ({point.longitude.values:.3f},{point.latitude.values:.3f}): {ewt[0]:.3f} cm")
EWT for (-120.353,34.540): 0.149 cm

3.4.2 CWC of a DataFrame of Points

We can also apply this function to the point data we selected in the previous notebook with the interactive plot.

Read in the csv file we created.

points_df = pd.read_csv("../data/emit_click_data.csv")
points_df
id x y 381.00558 388.4092 395.81583 403.2254 410.638 418.0536 425.47214 ... 2426.4402 2433.8303 2441.2183 2448.6064 2455.9944 2463.3816 2470.7678 2478.153 2485.5386 2492.9238
0 0 -120.569492 34.574552 0.018037 0.017798 0.017567 0.017348 0.017523 0.018065 0.018957 ... 0.034822 0.034534 0.030492 0.031268 0.027023 0.026327 0.024637 0.023704 0.015793 0.013392
1 1 -120.568286 34.591054 0.012029 0.011957 0.011888 0.011826 0.012031 0.012486 0.013151 ... 0.020698 0.022204 0.020115 0.019911 0.016761 0.017437 0.015805 0.016146 0.014353 0.012923
2 2 -120.371752 34.484274 0.020833 0.021323 0.021819 0.022317 0.023065 0.024095 0.025361 ... 0.059581 0.058129 0.053620 0.053622 0.048783 0.044683 0.044064 0.041562 0.034080 0.028254
3 3 -120.181848 34.483303 0.024848 0.028178 0.034840 0.038940 0.043875 0.047232 0.050809 ... 0.131800 0.135400 0.122960 0.118844 0.108201 0.112605 0.106271 0.101222 0.070507 0.059903
4 4 -120.407924 34.591054 0.040804 0.044085 0.048493 0.054733 0.059349 0.064003 0.067749 ... 0.266081 0.263995 0.258724 0.252158 0.228920 0.226200 0.212124 0.181446 0.130708 0.111522
5 5 -120.438851 34.712298 0.015452 0.016034 0.016616 0.017204 0.017848 0.018621 0.019477 ... 0.046427 0.046706 0.043317 0.044288 0.038928 0.037683 0.038662 0.035936 0.034616 0.033542
6 6 -120.512401 34.648230 0.044019 0.039991 0.039511 0.045176 0.045753 0.047011 0.048643 ... 0.081432 0.080438 0.076009 0.076724 0.069166 0.069164 0.066233 0.063523 0.059582 0.056769
7 7 -120.350229 34.626389 0.018255 0.018679 0.019107 0.019550 0.020291 0.021369 0.022694 ... 0.050578 0.047565 0.046092 0.046638 0.039017 0.036911 0.037903 0.033778 0.028663 0.028218
8 8 -120.355052 34.713269 0.017943 0.018290 0.018641 0.019003 0.019586 0.020435 0.021492 ... 0.047776 0.048551 0.045117 0.045340 0.039024 0.038904 0.039077 0.035324 0.030073 0.029010
9 9 -120.243522 34.645318 0.021041 0.021242 0.021448 0.021661 0.022252 0.023214 0.024442 ... 0.049286 0.047242 0.042362 0.045277 0.039692 0.040241 0.037825 0.034632 0.029134 0.029248

10 rows × 288 columns

Now create an array of wavelengths from the column names starting with the first wavelength value (column 3).

# Get wavelength values
wavelength_values = points_df.columns[3::].to_numpy()

Iterate by row through our dataframe, selecting the reflectance values and providing them to the invert_liquid_water function. Afterwards, add an CWC column to our dataframe.

# Create empty list
ewt_values = []
# Iterate through rows
for _i in points_df.index.to_list():
    # Get reflectance array to pass to function
    rfl_values = points_df.iloc[_i,3::]
    # Use invert liquid water function and append results to list
    ewt_values.append(invert_liquid_water(rfl_values,wavelength_values)[0])    
# Add to our existing dataframe at Column Index 3
points_df.insert(3, "ewt", ewt_values)
points_df
id x y ewt 381.00558 388.4092 395.81583 403.2254 410.638 418.0536 ... 2426.4402 2433.8303 2441.2183 2448.6064 2455.9944 2463.3816 2470.7678 2478.153 2485.5386 2492.9238
0 0 -120.569492 34.574552 2.773249e-01 0.018037 0.017798 0.017567 0.017348 0.017523 0.018065 ... 0.034822 0.034534 0.030492 0.031268 0.027023 0.026327 0.024637 0.023704 0.015793 0.013392
1 1 -120.568286 34.591054 2.495512e-01 0.012029 0.011957 0.011888 0.011826 0.012031 0.012486 ... 0.020698 0.022204 0.020115 0.019911 0.016761 0.017437 0.015805 0.016146 0.014353 0.012923
2 2 -120.371752 34.484274 1.639785e-01 0.020833 0.021323 0.021819 0.022317 0.023065 0.024095 ... 0.059581 0.058129 0.053620 0.053622 0.048783 0.044683 0.044064 0.041562 0.034080 0.028254
3 3 -120.181848 34.483303 5.938623e-02 0.024848 0.028178 0.034840 0.038940 0.043875 0.047232 ... 0.131800 0.135400 0.122960 0.118844 0.108201 0.112605 0.106271 0.101222 0.070507 0.059903
4 4 -120.407924 34.591054 4.001294e-11 0.040804 0.044085 0.048493 0.054733 0.059349 0.064003 ... 0.266081 0.263995 0.258724 0.252158 0.228920 0.226200 0.212124 0.181446 0.130708 0.111522
5 5 -120.438851 34.712298 6.409762e-02 0.015452 0.016034 0.016616 0.017204 0.017848 0.018621 ... 0.046427 0.046706 0.043317 0.044288 0.038928 0.037683 0.038662 0.035936 0.034616 0.033542
6 6 -120.512401 34.648230 2.607293e-10 0.044019 0.039991 0.039511 0.045176 0.045753 0.047011 ... 0.081432 0.080438 0.076009 0.076724 0.069166 0.069164 0.066233 0.063523 0.059582 0.056769
7 7 -120.350229 34.626389 1.505599e-01 0.018255 0.018679 0.019107 0.019550 0.020291 0.021369 ... 0.050578 0.047565 0.046092 0.046638 0.039017 0.036911 0.037903 0.033778 0.028663 0.028218
8 8 -120.355052 34.713269 1.379378e-01 0.017943 0.018290 0.018641 0.019003 0.019586 0.020435 ... 0.047776 0.048551 0.045117 0.045340 0.039024 0.038904 0.039077 0.035324 0.030073 0.029010
9 9 -120.243522 34.645318 1.954748e-01 0.021041 0.021242 0.021448 0.021661 0.022252 0.023214 ... 0.049286 0.047242 0.042362 0.045277 0.039692 0.040241 0.037825 0.034632 0.029134 0.029248

10 rows × 289 columns

For larger sets of point data we would want to do this in parallel. We can also calculate CWC for an area, where we definitely need to utilize parallel processing.

3.5 CWC Calculation of an ROI

In the previous notebook, we subset our region of interest and exported the file. Since the CWC calculation is computationally intensive, it can take a while to process large scenes, so it is more efficient to do this spatial subsetting up front. We can use a function included in the ewt_calc.py module to calculate CWC on a cropped image, and create a cloud-optimized GeoTIFF (COG) file containing the results.

Set our input filepaths and output directory.

fp = '../data/EMIT_L2A_RFL_001_20230401T203751_2309114_002_dangermond.nc'
out_dir = '../data/'
roi_ds = xr.open_dataset(fp, decode_coords='all')
roi_ds
<xarray.Dataset> Size: 73MB
Dimensions:           (wavelengths: 285, latitude: 244, longitude: 262)
Coordinates:
  * wavelengths       (wavelengths) float32 1kB 381.0 388.4 ... 2.493e+03
    fwhm              (wavelengths) float32 1kB ...
    good_wavelengths  (wavelengths) float32 1kB ...
  * latitude          (latitude) float64 2kB 34.57 34.57 34.57 ... 34.44 34.44
  * longitude         (longitude) float64 2kB -120.5 -120.5 ... -120.4 -120.4
    elev              (latitude, longitude) float32 256kB ...
    spatial_ref       int64 8B ...
Data variables:
    reflectance       (latitude, longitude, wavelengths) float32 73MB ...
Attributes: (12/40)
    ncei_template_version:             NCEI_NetCDF_Swath_Template_v2.0
    summary:                           The Earth Surface Mineral Dust Source ...
    keywords:                          Imaging Spectroscopy, minerals, EMIT, ...
    Conventions:                       CF-1.63
    sensor:                            EMIT (Earth Surface Mineral Dust Sourc...
    instrument:                        EMIT
    ...                                ...
    spatial_ref:                       GEOGCS["WGS 84",DATUM["WGS_1984",SPHER...
    geotransform:                      [-1.20992414e+02  5.42232520e-04 -0.00...
    day_night_flag:                    Day
    title:                             EMIT L2A Estimated Surface Reflectance...
    granule_id:                        EMIT_L2A_RFL_001_20230401T203751_23091...
    Orthorectified:                    True
roi_ds.sel(wavelengths=850,method='nearest').hvplot.image(x='longitude',y='latitude',cmap='viridis',geo=True, tiles='ESRI', frame_width=720,frame_height=405, alpha=0.7, fontscale=2).opts(
    title=f"Dangermond ROI - RFL at 850 nm", xlabel='Longitude',ylabel='Latitude')
Unable to display output for mime type(s): 

Use the calc_ewt function to calculate CWC of the cropped image. This function will also create a COG file containing the CWC results. We can also specify the number of CPUs to use manually with a n_cpu argument, or leave it blank to use all but one of the available CPUs. If we set the return_cwc argument to true, the function will also return the COG.

This will take some time, about 5 minutes, because we’re doing the calculation for roughly 63,000 pixels. Also note that here we provide the ewt_detection_limit to increase it from the default of 0.5 in the function. We do this because there are several regions containing plants that hold significant quantities of water in this scene.

%%time
ds_cwc = calc_ewt(fp, out_dir, ewt_detection_limit=1.5, return_cwc=True)
2024-05-20 18:20:14,143 WARNING services.py:2009 -- WARNING: The object store is using /tmp instead of /dev/shm because /dev/shm has only 67104768 bytes available. This will harm performance! You may be able to free up space by deleting files in /dev/shm. If you are inside a Docker container, you can increase /dev/shm size by passing '--shm-size=2.37gb' to 'docker run' (or add it to the run_options list in a Ray cluster config). Make sure to set this to more than 30% of available RAM.
2024-05-20 18:20:14,292 INFO worker.py:1740 -- Started a local Ray instance. View the dashboard at 127.0.0.1:8265 
CPU times: user 848 ms, sys: 351 ms, total: 1.2 s
Wall time: 5min 1s
ds_cwc
<xarray.Dataset> Size: 771kB
Dimensions:      (latitude: 244, longitude: 262)
Coordinates:
  * latitude     (latitude) float64 2kB 34.57 34.57 34.57 ... 34.44 34.44 34.44
  * longitude    (longitude) float64 2kB -120.5 -120.5 -120.5 ... -120.4 -120.4
    elev         (latitude, longitude) float32 256kB ...
    spatial_ref  int64 8B ...
Data variables:
    cwc          (latitude, longitude) float64 511kB nan nan nan ... nan nan nan
Attributes: (12/13)
    flight_line:            emit20230401t203751_o09114_s000
    time_coverage_start:    2023-04-01T20:37:51+0000
    time_coverage_end:      2023-04-01T20:38:03+0000
    easternmost_longitude:  -119.71545648976226
    northernmost_latitude:  34.7990749308955
    westernmost_longitude:  -120.992414074966
    ...                     ...
    spatialResolution:      0.000542232520256367
    spatial_ref:            GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84...
    geotransform:           [-1.20992414e+02  5.42232520e-04 -0.00000000e+00 ...
    day_night_flag:         Day
    title:                  EMIT Estimated Equivalent Water Thickness (EWT) /...
    granule_id:             EMIT_L2A_RFL_001_20230401T203751_2309114_002

Plot CWC of our ROI.

ds_cwc.hvplot.image(x='longitude',y='latitude',cmap='viridis',geo=True, tiles='ESRI', frame_width=720,frame_height=405, alpha=0.7, fontscale=2).opts(
    title=f"{ds_cwc.cwc.long_name} ({ds_cwc.cwc.units})", xlabel='Longitude',ylabel='Latitude')
Unable to display output for mime type(s): 

Contact Info:

Email: LPDAAC@usgs.gov
Voice: +1-866-573-3222
Organization: Land Processes Distributed Active Archive Center (LP DAAC)¹
Website: https://lpdaac.usgs.gov/
Date last modified: 05-20-2024

¹Work performed under USGS contract 140G0121D0001 for NASA contract NNG14HH33I.