Monthly Composite Script Evalscript
//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
}
Show full evalscript 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
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