s = ({
evalscript : `
//VERSION=3
function setup() {
return ({
input: [{
bands:["B04", "B03", "B02"],
}],
output: [{
id: "default",
bands: 3,
sampleType: SampleType.${image_config.format.out_type}
}]
});
}
const typeFactor = ('${image_config.format.out_type}'=='UINT16') ? 65535 : 255;
function stretchType(arr, off, scale) {
if (!arr) { return arr;}
switch (arr.length) {
case 4: arr[3] = typeFactor * (off + arr[3]*scale);
case 3: arr[2] = typeFactor * (off + arr[2]*scale);
case 2: arr[1] = typeFactor * (off + arr[1]*scale);
default: arr[0] = typeFactor * (off + arr[0]*scale);
}
return arr;
}
//== PARAMETERS ===========================
const c0r = 0.031;
const tx = 0.1;
const ty0 = 0.1;
const ty1 = 1.0;
const max = 0.4;
const sat = 1.35;
const debug = false; // Set to 'true' to highlight out-of-range values
const atmRatios = [0.9, 1.6, 2.7]; // Rayleigh-derived consts for automated atmosphere offsets
//== FUNCTIONS ============================
const sRGBenc = C => C < 0.0031308 ? (12.92 * C) : (1.055 * Math.pow(C, 0.41666) - 0.055);
// atmospheric adjustment
const atm2p = (a, c0, c1) => (a - c0) / c1;
const atm1p = (a, c0) => atm2p(a, c0, (1 - c0)**2);
const atm = (a, ii) => (typeof cManual !== 'undefined')
? (cManual[ii] instanceof Array)
? atm2p(a, cManual[ii][0], cManual[ii][1])
: atm1p(a, cManual[ii])
: atm1p(a, c0r * atmRatios[ii]);
//contrast enhancement
const adjFun = (a, cx, cy, cmax) => {
const ar = a / cmax;
const txr = cx / cmax;
const bot = (2 * txr - 1) * ar - txr;
return ar * (1 + (txr - cy) * (1 - ar) / bot);
};
const adj2 = (a, cx, cy0, cy1, max) => {
const blend = adjFun(a, 0.5 * max, (max - cx)/max, max);
return (1 - blend) * adjFun(a, cx, cy0, max) + blend * adjFun(a, cx, cy1, max);
}
const adj = a => adj2(a, tx, ty0, ty1, max);
const satEnh = rgbArr => {
const avg = (rgbArr[0]+rgbArr[1]+rgbArr[2])/3.0;
return [
avg * (1 - sat) + rgbArr[0] * sat,
avg * (1 - sat) + rgbArr[1] * sat,
avg * (1 - sat) + rgbArr[2] * sat
]
};
const checkDebug = arr => {
if (!debug) {
return arr;
}
const minC = Math.min(arr[0], arr[1], arr[2]);
if (minC < 0) {
return [
arr[0] < 0 ? 1 : 0,
arr[1] < 0 ? 1 : 0,
arr[2] < 0 ? 1 : 0
];
}
const maxC = Math.max(arr[0], arr[1], arr[2]);
if (maxC > 1) {
return [
arr[0] > 1 ? 1 : 0,
arr[1] > 1 ? 1 : 0,
arr[2] > 1 ? 1 : 0
];
}
return arr;
};
//== SCRIPT ============================
function evaluatePixel(sample) {
const rgb = satEnh([
adj(atm(sample.B04, 0)),
adj(atm(sample.B03, 1)),
adj(atm(sample.B02, 2))
]);
const retLin = checkDebug(rgb);
return stretchType([
sRGBenc(retLin[0]),
sRGBenc(retLin[1]),
sRGBenc(retLin[2])
], 0, 1);
}
`
})