Normalised Difference Snow Index, NDSI

//VERSION=3
//Reference: https://earth.esa.int/web/sentinel/technical-guides/sentinel-2-msi/level-2a/algorithm

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

function evaluatePixel(samples) {
  let val = index(samples.B03, samples.B11);

  if (val > 0.42)
    imgVals = [0, 0.8, 1, samples.dataMask];
  else
    imgVals = [2.5 * samples.B04, 2.5 * samples.B03, 2.5 * samples.B02, samples.dataMask];

  return imgVals
}
//VERSION=3
//Reference: https://earth.esa.int/web/sentinel/technical-guides/sentinel-2-msi/level-2a/algorithm

function setup() {
    return {
        input: ["B03", "B11", "B04", "B02", "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.B03, samples.B11);
    let imgVals = null;
    // 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;

    if (val > 0.42)
        imgVals = [0, 0.8, 1, samples.dataMask];
    else
        imgVals = [2.5 * samples.B04, 2.5 * samples.B03, 2.5 * samples.B02, samples.dataMask];

    return {
        default: imgVals,
        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 (auto-converted from 1)

// Normalised Difference Snow Index
// Source: https://earth.esa.int/web/sentinel/technical-guides/sentinel-2-msi/level-2a/algorithm
// values above 0.42 are usually snow

function setup() {
  return {
    input: ["B03", "B11"],
    output: { bands: 1, sampleType: "FLOAT32" }
  }
}

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

Evaluate and Visualize

General description of the script

For Sentinel-2, the index looks like this:

\[NDSI = \frac{B3-B11}{B3+B11}\]

The Sentinel-2 normalized difference snow index is a ratio of two bands: one in the VIR (Band 3) and one in the SWIR (Band 11). Values above 0.42 are usually snow. More info here.

The Sentinel-2 normalised difference snow index can be used to differentiate between cloud and snow cover as snow absorbs in the short-wave infrared light, but reflects the visible light, whereas cloud is generally reflective in both wavelengths. In the visualization script snow cover is represented in bright vivid blue.

Description of representative images

Visualized NDSI over New Zealand, acquired on 2019-09-19.

NDSI Visualized

The NDSI script applied to Klagenfurt, Austria returning raw values.

NDSI Raw