Blocks after division

Hey all,

There are two raster:

1st raster: s2_cube_water_sum:
image
2nd raster: s2_count:

When we divided raster by raster: s2_cube_swf = s2_cube_water_sum / s2_count, the output shows some unexpected blocks on the right side. Do you know why this happens?
ID: j-8d5a45a6ad674b128eaa196eb60cf416

Hi Andrea,
I’m thinking this might happen if either one of the cubes has no tiles or nodata tiles in those areas.
We might be able to solve it by doing the various steps within the same cube, like we did to solve the memory use of the masking operation.
I can have a look if you have the python snippet that generates this result?

thanks,
Jeroen

start_date = '2021-05-01' 
start_date_dt_object = datetime.strptime(start_date, '%Y-%m-%d')
end_date = (start_date_dt_object + relativedelta(months = +1)).date()
start_date_exclusion = (start_date_dt_object + relativedelta(months = -2)).date() 
spatial_extent  = {'west': -74.06910760, 'east': -73.80597343, 'south': 4.689864510, 'north': 4.724080996, 'crs': 'epsg:4326'}

bands                = ['B02', 'B03', 'B04', 'B08', 'CLP', 'SCL' , 'sunAzimuthAngles', 'sunZenithAngles'] 

## Get the Sentinel-2 data for a 3 month window.
s2_cube = connection.load_collection(
    'SENTINEL2_L2A_SENTINELHUB',
    spatial_extent = spatial_extent,
    temporal_extent = [start_date_exclusion, end_date],
    bands = ['B02', 'B03', 'B04', 'B08', 'CLP', 'SCL', 'sunAzimuthAngles'] 
)

scl = s2_cube.band("SCL")
mask_scl = (scl == 3) | (scl == 8) | (scl == 9) | (scl == 10) |(scl == 11)
s2_cube = s2_cube.mask(mask_scl)
clp = s2_cube.band("CLP")
mask_clp = (clp / 255) > 0.3  
s2_cube = s2_cube.mask(mask_clp)

s2_count = s2_cube.filter_bands(bands = ["B08"])
s2_count = s2_count.reduce_dimension(reducer=lambda data: data.count(), dimension = "t")
s2_count = s2_count.add_dimension("bands", "count", type = "bands")

s2_cube = append_index(s2_cube,"NDWI") ## index 7
s2_cube = append_index(s2_cube,"NDVI") ## index 8

def water_function(data):
    ndwi = array_element(data, index = 7)
    ndvi = array_element(data, index = 8)
    water = 1 / (1 + exp(- (0.845 + (2.14 * ndvi) + (13.5 * ndwi))))
    return water

s2_cube_water = s2_cube.reduce_dimension(reducer = water_function, dimension = "bands")
s2_cube_water = s2_cube_water.add_dimension("bands", "water_prob", type = "bands") 
s2_cube_water_threshold = s2_cube_water.apply_dimension(dimension="bands", process=lambda x: if_(x > 0.75, x))
s2_cube_water_threshold = s2_cube_water_threshold.add_dimension("bands", "w_T75", type = "bands")

s2_cube_water_sum = s2_cube_water_threshold.reduce_dimension(reducer = "sum", dimension = "t")
s2_cube_water_sum = s2_cube_water_sum.add_dimension("bands", "sum", type = "bands") 
s2_cube_swf = s2_cube_water_sum / s2_count
s2_cube_swf = s2_cube_swf.add_dimension("bands", "swf", type = "bands") 

@stefaan.lippens
Could you have a look please?

This is another example use case for "Overlap-only" mode for merge_cubes · Issue #280 · Open-EO/openeo-processes · GitHub

The division operation

s2_cube_swf = s2_cube_water_sum / s2_count

is implemented with merge_cubes and and overlap resolver that implements the division.
This works properly for sections of bbox where both are s2_cube_water_sum and s2_count are non-null.

However if you have blocks where where s2_cube_water_sum is completely null or NaN, then the overlap resolver is not used and you get just

s2_cube_swf = s2_count

Likewise, in blocks where s2_count is completely null/NaN, the result is

s2_cube_swf = s2_cube_water_sum

I think this is what you are seeing here.

The current workaround is making sure both s2_cube_water_sum and s2_count are masked in the same way. Or use that mask to postprocess s2_cube_swf after division to hide the parts that are unintended.

I hope this makes sense

Yes, it makes sense and It works now. Thanks for supporting us with this :slight_smile: