Skip to main content Link Menu Expand (external link) Document Search Copy Copied

Monthly Composite Script

//VERSION=3 (auto-converted from 1)
/*
Author: Karasiak Nicolas
*/

// Put 3 to have synthesis of the last 90 days
var numberOfMonthsToUse = 1;
// Thresold to consider pixel as snow
var NDSIthresold = 0.2;
// In order to dismiss snow from water
var redThresold = 0.2;
// In order to dismiss clouds
var blueThresold = 0.12;
var highBlueThresold = 0.45;

var numberOfTimesForWater = 2; // minimum number of times to identify as water

var stretchMin = 0;
var stretchMax = 1;

function setup() {
  return {
    input: [{
      bands: [
          "B02",
          "B03",
          "B04",
          "B05",
          "B08",
          "B11"
      ]
    }],
    output: { bands: 3 },
    mosaicking: "ORBIT"
  }
}


function NDSI(sample) {
    return ((sample.B03 - sample.B11) / (0.01 + sample.B03 + sample.B11));
}

function NDWI(sample) {
    return ((sample.B03 - sample.B08) / (sample.B03 + sample.B08));

}

function median(values) {
    // from https://stackoverflow.com/questions/45309447/calculating-median-javascript
    if (values.length === 0) return 0;
    if (values.length === 1) return values[0];

    values.sort(function(a, b) {
        return a - b;
    });

    var half = Math.floor(values.length / 2);
    return values[half];
}

function r(a, b) {
    return (a / b);
}

function stretch(val, min, max) {
    return (val - min) / (max - min);
}

function indexOfMaxRatio(a, b) {
    ratios = [];
    for (i = 0; i < a.length; i++) {
        ratios.push(r(a[i], b[i]));
    }
    if (ratios.length === 0) {
        return -1;
    }

    var max = ratios[0];
    var maxIndex = 0;

    for (var i = 1; i < ratios.length; i++) {
        if (ratios[i] > max) {
            maxIndex = i;
            max = ratios[i];
        }
    }

    return maxIndex;
}

function evaluatePixel(samples, scenes) {
    // for snow scene
    let snowyCount = 0;
    let snowB02 = [];
    let snowB03 = [];
    let snowB04 = [];

    // for unsnow scene
    let B02 = [];
    let B03 = [];
    let B04 = [];
    let B05 = [];
    let B08 = [];

    // for high blue scenes
    let highB02 = [];
    let highB03 = [];
    let highB04 = [];
    let highB05 = [];
    let highB08 = [];

    // isWater
    let isWater = 0;

    // to manage image length between tiles
    let realSampleLength = 0;

    for (i = 0; i < samples.length; i++) {
        // in order to avoid black pixel (the ones between tiles)
        if ((samples[i].B02 > 0) & (samples[i].B03 > 0)) {
            realSampleLength++;
            if (samples[i].B02 < blueThresold) {
                B02.push(samples[i].B02);
                B03.push(samples[i].B03);
                B04.push(samples[i].B04);
                B05.push(samples[i].B05);
                B08.push(samples[i].B08);
            } else if ((samples[i].B02 < highBlueThresold) & (samples[i].B02 > blueThresold)) {
                highB02.push(samples[i].B02);
                highB03.push(samples[i].B03);
                highB04.push(samples[i].B04);
                highB05.push(samples[i].B05);
                highB08.push(samples[i].B08);
            }
            if ((NDSI(samples[i]) > NDSIthresold) & (samples[i].B04 > redThresold)) {
                snowyCount++;
                snowB02.push(samples[i].B02);
                snowB03.push(samples[i].B03);
                snowB04.push(samples[i].B04);
            }
            if ((samples[i].B02 < blueThresold) & (NDWI(samples[i]) > 0)) {
                isWater++;
            }
        }
    }
    if ((B02.length > 0)) {
        if (isWater > 2) {
            bestRatio = indexOfMaxRatio(B02, B08);
        } else {
            bestRatio = indexOfMaxRatio(B08, B03);
        }
        colorMap = [stretch((2.8 * B04[bestRatio] + 0.1 * B05[bestRatio]), stretchMin, stretchMax), stretch((2.8 * B03[bestRatio] + 0.15 * B08[bestRatio]), stretchMin, stretchMax), stretch((2.8 * B02[bestRatio]), stretchMin, stretchMax)];
    } else if ((highB02.length > 0) & (B02.length < 1)) {
        if (isWater > 2) {
            bestRatio = indexOfMaxRatio(B02, B08);
        } else {
            bestRatio = indexOfMaxRatio(highB03, highB02);
        }
        colorMap = [stretch((2.8 * highB04[bestRatio] + 0.1 * highB05[bestRatio]), stretchMin, stretchMax), stretch((2.8 * highB03[bestRatio] + 0.15 * highB08[bestRatio]), stretchMin, stretchMax), stretch((2.8 * highB02[bestRatio]), stretchMin, stretchMax)];
    } else if ((snowyCount > 0) & (highB02.length < 1) & (B02.length < 1)) {
        // snowColorMap
        colorMap = [1.1 * median(snowB04), 1.3 * median(snowB03), 1.1 * median(snowB02)];
    } else {
        colorMap = [1, 0, 0];
    }

    return colorMap;
}

function preProcessScenes (collections) {
    collections.scenes.orbits = collections.scenes.orbits.filter(function (orbit) {
        var orbitDateFrom = new Date(orbit.dateFrom)
        return orbitDateFrom.getTime() >= (collections.to.getTime() - (numberOfMonthsToUse * 31 * 24 * 3600 * 1000));
    })
    return collections
}

Evaluate and Visualize

General description of the script

Monthly composite (31 days before the chosen date), computed with best bands ratio. This script is here for those who want a cloud free image representing the last 31 days.

In order to select the best pixel in a month (and avoid cloud), a selection is made using a ratio :

  • When blue < 0.12, date is chosen where max ratio of B08 against B02.
  • If no pixel available above, when blue < 0.45, date is chosen where max ratio of B03 against B02.
  • If water is detected, date is chosen where max ratio of B02 against B08.
  • If snow is detected, median of scene with snow.

Author of the script

Karasiak Nicolas

Description of representative images

Lac léman, composite from 2019-03-29

Lac léman, composite from 2019-03-29

South Madagascar, composite from 2019-04-26

South Madagascar, composite from 2019-04-26

South Madagascar, composite from 2019-04-26

South Madagascar, composite from 2019-04-26

See the supplementary material for more examples.

Credits

Thanks to :

  • Pierre Markuse for his natural color script
  • Harel Dan for his temporal script