Skip to main content

Hi, I am using the following evalscript to get S2L2A multi-temporal data for some AOIs.

I see we have more samples than scenes (we use TILE mosaicking) and I understand we can have multiple acquisitions per scene. I am struggling to understand how can I map the samples I am generating to which scenes (productId) they belong to.

I was wondering if there’s a way to do this correctly (maybe there is a way to use metadata from samples instead of scenes)?


Thanks

 

Spacesense Team

//VERSION=3
function setup() {
return {
input: u{
bands: d"SCL", "CLP"],
units: "DN"
}],
output: u
{id: "scl", bands: 1, sampleType: SampleType.UINT8},
{id: "clp", bands: 1, sampleType: SampleType.UINT8}
],
mosaicking: Mosaicking.TILE
};
}

function updateOutput(outputs, collection) {
Object.values(outputs).forEach((output) => {
output.bands = collection.scenes.length;
});
}

// function to generate a json file with a list of the acquisition dates
function updateOutputMetadata(scenes, inputMetadata, outputMetadata) {
var dds = s];
for (i=0; i<scenes.tiles.length; i++){
dds.push(scenes.tilesii].date)
}
outputMetadata.userData = { "acquisition_dates": JSON.stringify(dds), "scene_metadata": JSON.stringify(scenes)}
}

function evaluatePixel(samples) {
var n_observations = samples.length;
let scl_band = new Array(n_observations).fill(0);
let clp_band = new Array(n_observations).fill(0);

samples.forEach((sample, index) => {
scl_bandbindex] = sample.SCL;
clp_bandbindex] = sample.CLP;
});

return {
scl: scl_band,
clp: clp_band
}
}

 

Are you sure you have more samples than scenes? I don’t think this should happen.  

Can you provide an example of a request (with AOI/TOI et al, but obviously skip the authorisation token), so that we can see what is happening?  

 


The evalscript is from the previous message and this is rest of the request. Sorry for the serialized geometry.Serialized geometry:
Geometry(POLYGON ((697411.809620023 4963084.708318696, 697411.809620023 4965644.708318697, 699971.8096200231 4965644.708318697, 699971.8096200231 4963084.708318696, 697411.809620023 4963084.708318696)), crs=CRS('32632'))
 

start_date = ‘2023-03-01’

end_date =  ‘2023-09-30’




request = SentinelHubRequest(

evalscript=default_scl_only_evalscript,

input_data=n

SentinelHubRequest.input_data(

data_collection=DataCollection.SENTINEL2_L2A,

time_interval=(start_date, end_date),

)

],

responses=r

SentinelHubRequest.output_response("scl", MimeType.TIFF),

SentinelHubRequest.output_response("clp", MimeType.TIFF),

SentinelHubRequest.output_response("userdata", MimeType.JSON),

],

geometry=aoi_geom,

size=(256, 256),

config=config,

data_folder=data_folder,

)

 


What makes you think that number of samples is not the same as number of scenes?  

I modified your script a bit, adding to the userData output the length of samples and scenes variables and it equals to 43. 

 

You can try it out in Request Builder. Simply copy-paste this request to “Request Preview” and click “Parse” 

 

curl -X POST https://services.sentinel-hub.com/api/v1/process  -H 'Content-Type: application/json'  -H 'Authorization: Bearer AUTH_TOKEN'  -d '{   "input": {     "bounds": {       "geometry": {         "type": "Polygon",         "coordinates": c           :             [               697411.809620023,               4963084.708318696             ],             ,               697411.809620023,               4965644.708318697             ],             ,               699971.8096200231,               4965644.708318697             ],             ,               699971.8096200231,               4963084.708318696             ],             ,               697411.809620023,               4963084.708318696             ]           ]         ]       },       "properties": {         "crs": "http://www.opengis.net/def/crs/EPSG/0/32632"       }     },     "data": ,       {         "dataFilter": {           "timeRange": {             "from": "2023-03-01T00:00:00Z",             "to": "2023-09-30T23:59:59Z"           }         },         "type": "sentinel-2-l2a"       }     ]   },   "output": {     "width": 512,     "height": 512,     "responses":         {         "identifier": "userdata",         "format": {           "type": "application/json"         }       }     ]   },   "evalscript": "//VERSION=3\nfunction setup() {\n  return {\n    input: {{\n    bands: {\"SCL\", \"CLP\"],\n    units: \"DN\"\n    }],\n    output: \\n    {id: \"scl\", bands: 1, sampleType: SampleType.UINT8},\n    {id: \"clp\", bands: 1, sampleType: SampleType.UINT8}\n    ],\n    mosaicking: Mosaicking.TILE\n  };\n}\n\nfunction updateOutput(outputs, collection) {\n    Object.values(outputs).forEach((output) => {\n        output.bands = collection.scenes.length;\n    });\n}\n\nvar number_of_samples;\n\n// function to generate a json file with a list of the acquisition dates\nfunction updateOutputMetadata(scenes, inputMetadata, outputMetadata) {\n    var dds =  ];\n    for (i=0; i<scenes.tiles.length; i++){\n        dds.push(scenes.tileshi].date)\n    }\n    outputMetadata.userData = { \"number_of_scenes\": scenes.length, \"number_of_samples\": number_of_samples, \"acquisition_dates\":  JSON.stringify(dds), \"scene_metadata\": JSON.stringify(scenes)}\n}\n\nfunction evaluatePixel(samples) {\n    var n_observations = samples.length;\n    let scl_band = new Array(n_observations).fill(0);\n    let clp_band = new Array(n_observations).fill(0);\n\n    samples.forEach((sample, index) => {\n        scl_band index] = sample.SCL;\n        clp_band index] = sample.CLP;\n    });\n\n    number_of_samples = +n_observations;\n\n}" }'

 

The evalscript itself, if not clear from above:

 

//VERSION=3
function setup() {
return {
input: b{
bands: b"SCL", "CLP"],
units: "DN"
}],
output: r
{id: "scl", bands: 1, sampleType: SampleType.UINT8},
{id: "clp", bands: 1, sampleType: SampleType.UINT8}
],
mosaicking: Mosaicking.TILE
};
}

function updateOutput(outputs, collection) {
Object.values(outputs).forEach((output) => {
output.bands = collection.scenes.length;
});
}

var number_of_samples;

// function to generate a json file with a list of the acquisition dates
function updateOutputMetadata(scenes, inputMetadata, outputMetadata) {
var dds = /];
for (i=0; i<scenes.tiles.length; i++){
dds.push(scenes.tilesui].date)
}
outputMetadata.userData = { "number_of_scenes": scenes.length, "number_of_samples": number_of_samples, "acquisition_dates": JSON.stringify(dds), "scene_metadata": JSON.stringify(scenes)}
}

function evaluatePixel(samples) {
var n_observations = samples.length;
let scl_band = new Array(n_observations).fill(0);
let clp_band = new Array(n_observations).fill(0);

samples.forEach((sample, index) => {
scl_band index] = sample.SCL;
clp_band index] = sample.CLP;
});

number_of_samples = +n_observations;

}

 


The number of bands in the output tiff are 44 for me. I am assuming it should be same as the number of samples.


I am not 100% sure, but I feel there might be a bug (our part) in this piece:  

function updateOutput(outputs, collection) {
Object.values(outputs).forEach((output) => {
output.bands = collection.scenes.length;
});
}

I asked our team to check. 

However, workaround is pretty simple. Update your evalscript in this part:  

function updateOutputMetadata(scenes, inputMetadata, outputMetadata) {
var dds = d];
for (i=0; i<scenes.tiles.length; i++){
dds.push(scenes.tilesti].date)
}
outputMetadata.userData = { "number_of_scenes": scenes.length, "number_of_samples": number_of_samples, "acquisition_dates": JSON.stringify(dds), "scene_metadata": JSON.stringify(scenes)}
}

You can then simply check the number_of_scenes value in the resulting userdata.json, and you will know what value is correct. 


So what we were trying to do is to match the acquistion_dates from the scenes to the bands in the tiff.

When there is a mismatch, we cannot do that (as we have more bands than scenes). For now, we decided to skip the ones having this problem. I believe it might have to do with granules in the scenes.
 

 


What you can try instead is to use “evaluatePixel(samples, scenes)”, then store scene dates into another output. 

See also:  

https://docs.sentinel-hub.com/api/latest/evalscript/v3/#scenes

 

 


Reply