Normalized Difference Moisture Index (NDMI)

//VERSION=3
const moistureRamps = [
   [-0.8, 0x800000],
   [-0.24, 0xff0000],
   [-0.032, 0xffff00],
   [0.032, 0x00ffff],
   [0.24, 0x0000ff],
   [0.8, 0x000080]
];

const viz = new ColorRampVisualizer(moistureRamps);

function setup() {
   return {
      input: ["B8A", "B11", "dataMask"],
      output: { bands: 4 }
   };
}

function evaluatePixel(samples) {
   let val = index(samples.B8A, samples.B11);
   let imgVals = viz.process(val);
   return imgVals.concat(samples.dataMask);
}
//VERSION=3
const moistureRamps = [
    [-0.8, 0x800000],
    [-0.24, 0xff0000],
    [-0.032, 0xffff00],
    [0.032, 0x00ffff],
    [0.24, 0x0000ff],
    [0.8, 0x000080]
];

const viz = new ColorRampVisualizer(moistureRamps);

function setup() {
    return {
        input: ["B03", "B04", "B8A", "B11", "dataMask"],
        output: [
            { id: "default", bands: 4 },
            { id: "index", bands: 1, sampleType: "FLOAT32" },
            { id: "eobrowserStats", bands: 2, sampleType: 'FLOAT32' },
            { id: "dataMask", bands: 1 }
        ]
    };
}

function evaluatePixel(samples) {
    let val = index(samples.B8A, samples.B11);
    // The library for tiffs works well only if there is only one channel returned.
    // So we encode the "no data" as NaN here and ignore NaNs on frontend.
    const indexVal = samples.dataMask === 1 ? val : NaN;
    let imgVals = viz.process(val);

    return {
        default: imgVals.concat(samples.dataMask),
        index: [indexVal],
        eobrowserStats: [val, isCloud(samples) ? 1 : 0],
        dataMask: [samples.dataMask]
    };
}

function isCloud(samples) {
    const NGDR = index(samples.B03, samples.B04);
    const bRatio = (samples.B03 - 0.175) / (0.39 - 0.175);
    return bRatio > 1 || (bRatio > 0 && NGDR > 0);
}
//VERSION=3
function setup() {
    return {
        input: ["B8A", "B11"],
        output: { bands: 1, sampleType: "FLOAT32" }
    };
}

function evaluatePixel(samples) {
    return [index(samples.B8A, samples.B11)];
}

Evaluate and Visualize

General description of the script

The NDMI is a normalized difference moisture index, that uses NIR and SWIR bands to display moisture. The SWIR band reflects changes in both the vegetation water content and the spongy mesophyll structure in vegetation canopies, while the NIR reflectance is affected by leaf internal structure and leaf dry matter content but not by water content. The combination of the NIR with the SWIR removes variations induced by leaf internal structure and leaf dry matter content, improving the accuracy in retrieving the vegetation water content. The amount of water available in the internal leaf structure largely controls the spectral reflectance in the SWIR interval of the electromagnetic spectrum. SWIR reflectance is therefore negatively related to leaf water content. In short, NDMI is used to monitor changes in water content of leaves, and was proposed by Gao. NDMI is computed using the near infrared (NIR) and the short wave infrared (SWIR) reflectances:

Sentinel-2 NDMI = (B08 - B11) / (B08 + B11)

Landsat 4-5 TM NDMI = (B04 - B05) / (B04 + B05)

Landsat 7 ETM+ NDMI = (B04 - B05) / (B04 + B05)

Landsat 8 NDMI = (B05 - B06) / (B05 + B06)

MODIS NDMI = (B02 - B06) / (B02 + B06)

NDWI index is often used synonymously with the NDMI index, often using NIR-SWIR combination as one of the two options. Gao, referenced above, also called the index NDWI. NDMI seems to be consistently described using NIR-SWIR combination. As the indices with these two combinations work very differently, with NIR-SWIR highlighting differences in water content of leaves, and GREEN-NIR highlighting differences in water content of water bodies, we have decided to separate the indices on our repository as NDMI using NIR-SWIR, and NDWI using GREEN-NIR.

Description of representative images

The NDMI of Rome, Italy. Acquired on 08.10.2017, processed by Sentinel Hub.

NDWI

The NDMI of Betsiboka river, Madagascar. Acquired on 2020-08-01, processed by Sentinel Hub.

NDWI

References