How to get several single band tiffs in one request

for the code mentioned below, i request one tiff composed of five bands averages, numOfCloudFreeSamples, numOfCloudySamples, percentageOfCloudFree, percentageOfCloudy
at run time, i receive a tiff with the aforementioned bands.

my question is, now i want to have each band as a seperate tiff respectively. in other words, in addition to the tiff that is composed of the aforementioned five bands, i want to have each band of those five as a seperate tiff file.

i want to achieve that in a single request, i do not want to make seperate request for each one-band-tiff. is it possible to modify the below posed evalscript so i can get six output tiffs in a single request, where
1st tiff is composed of five bands
2nd tiff is composed of averages band
3rd tiff is composed of numOfCloudFreeSamples band
4th tiff is composed of numOfCloudySamples band
5th tiff is composed of percentageOfCloudFree band
6th tiff is composed of percentageOfCloudy band

thanks

evalscript:

//VERSION=3
  // Script to extract a time series of NDVI values using 
  // Sentinel 2 Level 2A data and  metadata file.
  
  function setup() {
	return {
	  input: [{
		bands: ["B04", "B08", "CLD"],
		units: "DN"
	  }],
	  output: [
		{
		  id: "default",
		  bands: 5,
		  sampleType: "FLOAT32",
		  nodataValue: NaN,
		},
	  ],
	  mosaicking: Mosaicking.ORBIT
	}
  }

  // function updateOutput(output, collection) {
  //   output.default.bands = collection.scenes.length
  // }

  function samplesParser(samples) {
	const cloudFreeSamples = new Array();
	const cloudySamples = new Array();

	samples.forEach((sample) => {
	  if (sample.CLD > 0) {
		cloudySamples.push(sample);
	  } else {
		cloudFreeSamples.push(sample);
	  }
	});
	return [cloudySamples, cloudFreeSamples];
  }

  function calcAverage(samples) {
	let sum = 0;
	for(let i = 0;i < samples.length; i++) {
	  sum += samples[i];
	}
	let avg = sum / samples.length;
	return avg;
  }
  function calcNDVIForSamples(samples) {
	const ndvis = new Array(samples.length).fill(NaN);
	samples.forEach((sample, index) => {
	  ndvis[index] = (sample.B08 - sample.B04) / (sample.B08 + sample.B04) ;
	});
	return ndvis;
  }
  function calcPercentage(total, incidences) {
	if (total <= 0) {
	  return NaN;
	}
	return ((incidences / total) * 100);
  }
  function evaluatePixel(samples) {
	if (samples.length < 1) return [NaN, NaN, NaN, NaN, NaN];
	const parsedSamples = samplesParser(samples);
	const ndvis = calcNDVIForSamples(parsedSamples[1]);
	const averages = calcAverage(ndvis);
	const numOfCloudySamples = parsedSamples[0].length;
	const numOfCloudFreeSamples = parsedSamples[1].length;
	const totalNumOfSamples = numOfCloudySamples + numOfCloudFreeSamples;
	const percentageOfCloudy = calcPercentage(totalNumOfSamples, numOfCloudySamples);
	const percentageOfCloudFree = calcPercentage(totalNumOfSamples, numOfCloudFreeSamples);

	return [averages, numOfCloudFreeSamples, numOfCloudySamples, percentageOfCloudFree, percentageOfCloudy];
  }

Hi, yes this is possible.

First, you will need to specify the outputs in the setup function:

output: [
            {
                id: "default",
                bands: 5,
                sampleType: "FLOAT32",
                nodataValue: NaN,
            },
            {
                id: "averages",
                bands: 1,
                sampleType: "FLOAT32",
                nodataValue: NaN,
            },
            {
                id: "numOfCloudFreeSamples",
                bands: 1,
                sampleType: "FLOAT32",
                nodataValue: NaN,
            },
            {
                id: "numOfCloudySamples",
                bands: 1,
                sampleType: "FLOAT32",
                nodataValue: NaN,
            },
            {
                id: "percentageOfCloudFree",
                bands: 1,
                sampleType: "FLOAT32",
                nodataValue: NaN,
            },
            {
                id: "percentageOfCloudy",
                bands: 1,
                sampleType: "FLOAT32",
                nodataValue: NaN,
            }],
        mosaicking: "TILE"

Then you will need to adapt the return of evaluatePixel:

return {
  default: [averages, numOfCloudFreeSamples, numOfCloudySamples, percentageOfCloudFree, percentageOfCloudy],
  averages: [averages],
  numOfCloudFreeSamples: [numOfCloudFreeSamples],
  numOfCloudySamples: [numOfCloudySamples],
  percentageOfCloudFree: [percentageOfCloudFree],
  percentageOfCloudy: [percentageOfCloudy]
  };

Lastly, don’t forget to update your payload (in CURL or Python, or what ever method you are using) to match the outputs.

thank you, does that mean i will receive 6 tiff files?
i updated the json request to be as follows, it is correct please?:

 "output": {
            // "width": 1535.8321176437041,
            // "height": 1646.6445869835497,
            // "resx": LSP,
            // "resy": LSP,
            "responses": [
                {
                    "identifier": "default",
                    "format": {
                        "type": "image/tiff",
                    },
                    "identifier": "averages",
                    "format": {
                        "type": "image/tiff",
                    },
                    "identifier": "numOfCloudFreeSamples",
                    "format": {
                        "type": "image/tiff",
                    },
                    "identifier": "numOfCloudySamples",
                    "format": {
                        "type": "image/tiff",
                    },
                    "identifier": "percentageOfCloudFree",
                    "format": {
                        "type": "image/tiff",
                    },
                    "identifier": "percentageOfCloudy",
                    "format": {
                        "type": "image/tiff",
                    }
                },
            ]
          },

Yes, you will get the same number of tiff files as of outputs. The code looks correct to me, but you’ll have to test the request to see if everything is set correctly.

i teset the evalscript and the json request in the request-builder, but i received only one tiff!! i thought i will receive 6 tiffs?why that is happening please?

Can you please paste the request from the Request Builder here please, so I can help you debug? (The Request Preview box)

here it is, please have a look at it:

{
  "input": {
	"bounds": {
	  "bbox": [
		12.44693,
		41.870072,
		12.541001,
		41.917096
	  ]
	},
	"data": [
	  {
		"dataFilter": {
		  "timeRange": {
			"from": "2024-05-12T00:00:00Z",
			"to": "2024-06-12T23:59:59Z"
		  }
		},
		"type": "sentinel-2-l2a"
	  }
	]
  },
  "output": {
	"width": 512,
	"height": 343.697,
	"responses": [
	  {
						"identifier": "default",
						"format": {
							"type": "image/tiff",
						},
						"identifier": "averages",
						"format": {
							"type": "image/tiff",
						},
						"identifier": "numOfCloudFreeSamples",
						"format": {
							"type": "image/tiff",
						},
						"identifier": "numOfCloudySamples",
						"format": {
							"type": "image/tiff",
						},
						"identifier": "percentageOfCloudFree",
						"format": {
							"type": "image/tiff",
						},
						"identifier": "percentageOfCloudy",
						"format": {
							"type": "image/tiff",
						}
					},
	]
  },
  "evalscript": "//VERSION=3\n  // Script to extract a time series of NDVI values using \n  // Sentinel 2 Level 2A data and  metadata file.\n  \n  function setup() {\n    return {\n      input: [{\n        bands: [\"B04\", \"B08\", \"CLD\"],\n        units: \"DN\"\n      }],\n      output: [\n        {\n          id: \"default\",\n          bands: 5,\n          sampleType: \"FLOAT32\",\n          nodataValue: NaN,\n        },\n        {\n          id: \"averages\",\n          bands: 1,\n          sampleType: \"FLOAT32\",\n          nodataValue: NaN,\n        },\n        {\n          id: \"numOfCloudFreeSamples\",\n          bands: 1,\n          sampleType: \"FLOAT32\",\n          nodataValue: NaN,\n        },\n        {\n          id: \"numOfCloudySamples\",\n          bands: 1,\n          sampleType: \"FLOAT32\",\n          nodataValue: NaN,\n        },\n        {\n          id: \"percentageOfCloudFree\",\n          bands: 1,\n          sampleType: \"FLOAT32\",\n          nodataValue: NaN,\n        },\n        {\n          id: \"percentageOfCloudy\",\n          bands: 1,\n          sampleType: \"FLOAT32\",\n          nodataValue: NaN,\n        }\n      ],\n      mosaicking: Mosaicking.ORBIT\n    }\n  }\n\n  // function updateOutput(output, collection) {\n  //   output.default.bands = collection.scenes.length\n  // }\n\n  function samplesParser(samples) {\n    const cloudFreeSamples = new Array();\n    const cloudySamples = new Array();\n\n    samples.forEach((sample) => {\n      if (sample.CLD > 0) {\n        cloudySamples.push(sample);\n      } else {\n        cloudFreeSamples.push(sample);\n      }\n    });\n    return [cloudySamples, cloudFreeSamples];\n  }\n\n  function calcAverage(samples) {\n    let sum = 0;\n    for(let i = 0;i < samples.length; i++) {\n      sum += samples[i];\n    }\n    let avg = sum / samples.length;\n    return avg;\n  }\n  function calcNDVIForSamples(samples) {\n    const ndvis = new Array(samples.length).fill(NaN);\n    samples.forEach((sample, index) => {\n      ndvis[index] = (sample.B08 - sample.B04) / (sample.B08 + sample.B04) ;\n    });\n    return ndvis;\n  }\n  function calcPercentage(total, incidences) {\n    if (total <= 0) {\n      return NaN;\n    }\n    return ((incidences / total) * 100);\n  }\n  function evaluatePixel(samples) {\n    if (samples.length < 1) return [NaN, NaN, NaN, NaN, NaN];\n    const parsedSamples = samplesParser(samples);\n    const ndvis = calcNDVIForSamples(parsedSamples[1]);\n    const averages = calcAverage(ndvis);\n    const numOfCloudySamples = parsedSamples[0].length;\n    const numOfCloudFreeSamples = parsedSamples[1].length;\n    const totalNumOfSamples = numOfCloudySamples + numOfCloudFreeSamples;\n    const percentageOfCloudy = calcPercentage(totalNumOfSamples, numOfCloudySamples);\n    const percentageOfCloudFree = calcPercentage(totalNumOfSamples, numOfCloudFreeSamples);\n\n    return {\n      default: [averages, numOfCloudFreeSamples, numOfCloudySamples, percentageOfCloudFree, percentageOfCloudy],\n      averages: [averages],\n      numOfCloudFreeSamples: [numOfCloudFreeSamples],\n      numOfCloudySamples: [numOfCloudySamples],\n      percentageOfCloudFree: [percentageOfCloudFree],\n      percentageOfCloudy: [percentageOfCloudy]\n    };\n  }"
}

I just ran your exact request, and it returned me a .tar file that contains 6 Geotiffs. Are you sure you are not getting this result with your request?

now i am getting this error shown in the screen shot posted below

This means that the outputs in your setup function don’t match the outputs of the payload. Please check your code for discrepancies.