Using world cover as mask timeless in eoworkflow

Hi

I want to try and use the 10m world cover by ESA as a feature mask timeless in my workflow.

I couldn’t find information on how to go about it.

My guess is to define a new data collection like this:

DataCollection.define(
        name='Global Land Cover',
        api_id='byoc-0b940c63-45dd-4e6b-8019-c3660b81b884',  #Type
        catalog_id='0b940c63-45dd-4e6b-8019-c3660b81b884', # collection_id
        service_url='services.sentinel-hub.com', # End point
        is_timeless=True
    )

and then maybe use it in SentinelHubInputTask with a filter task.

But I didn’t find any similar code in the EOlearn examples.

How can I accomplish this?

TNX

Hi @tonish,

The example below shows how you define new DataCollection using information from SH collections web page, and create SentinelHubInputTask that you can then add to your workflow.

from sentinelhub import (
    BBox, CRS, bbox_to_dimensions, 
    DataCollection, Band, Unit
)

from eolearn.core import EOPatch, FeatureType
from eolearn.io import SentinelHubInputTask

import matplotlib.pyplot as plt
import datetime as dt
import numpy as np

caspian_sea_bbox = BBox([49.9604, 44.7176, 51.0481, 45.2324], crs=CRS.WGS84)
time_interval = '2015-03-10', '2020-05-01'

glc = DataCollection.define(
    name='Global Land Cover, forest',
    api_id='byoc-f0a97620-0e88-4c1f-a1ac-bb388fabdf2c', # type 
    catalog_id='f0a97620-0e88-4c1f-a1ac-bb388fabdf2c',  # collectionId
    service_url='https://creodias.sentinel-hub.com',    # end point
    is_timeless=False,
    bands=[
        Band('Forest_Type_layer', (Unit.DN,), (np.float32,)),
        Band('Tree_CoverFraction_layer', (Unit.DN,), (np.float32,)),
    ]
)

input_task = SentinelHubInputTask(
    data_collection=glc,
    size=bbox_to_dimensions(caspian_sea_bbox, 200),
    bands_feature=(FeatureType.DATA, 'glc'),
    bands=['Forest_Type_layer']
)

eop = input_task.execute(bbox=caspian_sea_bbox, time_interval=time_interval)

The caveat is that the SentinelHubInputTask wants the results to be written to FeatureType.DATA, so you might have to create new Task that takes the particular time out of results and returns it as FeatureType.MASK_TIMELESS. Let us know if you run into additional troubles.

I modified your example and combined it with another input task to but I am getting the error:

ValueError: During execution of task SentinelHubInputTask: Trying to write data to an existing eopatch with a different timestamp.

Each task is working on its own but chaining them produces the error.
EDIT : glc_task returns 1 in the first dimension and add_data returns 2. But I couldn’t figure out how to resolve it.
I feel like there is something basic that I am missing here.
Also, how is this working without a SHconfig object?

AOI = BBox(bbox=[5.60, 52.68, 5.75, 52.63], crs=CRS.WGS84)
time_interval = '2020-01-01', '2020-01-20'

glc = DataCollection.define(
    name='Global Land Cover10m',
    api_id='byoc-0b940c63-45dd-4e6b-8019-c3660b81b884',  #Type
    catalog_id='0b940c63-45dd-4e6b-8019-c3660b81b884', # collection_id
    service_url='https://services.sentinel-hub.com', # End point
    is_timeless=False,
    bands=[
        Band('Map', (Unit.DN,), (np.float32,)),
    ]

band_names = ['B02', 'B03', 'B04']
add_data = SentinelHubInputTask(
    bands_feature=(FeatureType.DATA, 'BANDS'),
    bands = band_names,
    resolution=10,
    maxcc=0.8,
    time_difference=datetime.timedelta(minutes=120),
    data_collection=DataCollection.SENTINEL2_L1C,
    max_threads=5
)

glc_task = SentinelHubInputTask(
    data_collection=glc,
    size=bbox_to_dimensions(AOI, 200),
    bands_feature=(FeatureType.DATA, 'glc'),
    bands=['Map']
)

workflow = LinearWorkflow(glc_task,add_data)

result = workflow.execute({
    add_data: {'bbox': AOI, 'time_interval': time_interval},
    glc_task: {'bbox': AOI, 'time_interval': time_interval}
})

hi @batic

Running this example used to work but now it gives a strange error prompting that the units were requested in reflectance but DN is clearly requested by your code.

what could cause this?

Tnx

As per byoc docs, when requesting data from BYOC, the units should be DN. The example I’ve posted, still works.

I did miss your first response, though, sorry. To answer that one: glc_task and add_data tasks are requesting different data collections, that have different number of observations in the requested time interval. eo-learn is trying to enforce some structure in the EOPatch, tasks will fail if the temporal data the task is adding is not on the same “grid” (e.g. on equal timestamps) as the existing data in the EOPatch. In your case, you should probably create two EOPatches (one for GLC and one for S-2/L-8/… data). Another possible way forward would be to save one (e.g. GLC) as DATA_TIMELESS, if the GLC in your case “holds true” for all the timestamps of the other dataset you have in your EOPatch. (This second approach for instance makes sense with DEM data as well, see example).

Thanks @batic

I just tried again to run your example.

still receiving :

DownloadFailedException: Failed to download from:
https://creodias.sentinel-hub.com/api/v1/process
with HTTPError:
400 Client Error: Bad Request for url: https://creodias.sentinel-hub.com/api/v1/process
Server response: "{"error":{"status":400,"reason":"Bad Request","message":"Invalid script! Band 'Forest_Type_layer' of collection 'byoc-f0a97620-0e88-4c1f-a1ac-bb388fabdf2c' requested in unsupported units 'REFLECTANCE'! Supported units for this band: [DN]. See https://shforum.sinergise.com/t/units-improvements/4442 for more information.","code":"RENDERER_EXCEPTION"}}"

This is strange because DN is specified in your code. What could cause this?

I really don’t know: I have just run my example code, and it runs without a problem. Are you sure that your collection defined DN for units? E.g. try printing out the collection… For me:

glc

<DataCollection.Global Land Cover, forest: DataCollectionDefinition(
  api_id: byoc-f0a97620-0e88-4c1f-a1ac-bb388fabdf2c
  catalog_id: f0a97620-0e88-4c1f-a1ac-bb388fabdf2c
  service_url: https://creodias.sentinel-hub.com
  bands: (Band(name='Forest_Type_layer', units=(<Unit.DN: 'DN'>,), output_types=(<class 'numpy.float32'>,)), Band(name='Tree_CoverFraction_layer', units=(<Unit.DN: 'DN'>,), output_types=(<class 'numpy.float32'>,)))
  is_timeless: False
  has_cloud_coverage: False
)>

(of course, substitute glc with whatever data collection you use in your glc_task)

I get the exact same result as you

glc

<DataCollection.Global Land Cover, forest: DataCollectionDefinition(
api_id: byoc-f0a97620-0e88-4c1f-a1ac-bb388fabdf2c
catalog_id: f0a97620-0e88-4c1f-a1ac-bb388fabdf2c
service_url: https://creodias.sentinel-hub.com
bands: (Band(name=‘Forest_Type_layer’, units=(<Unit.DN: ‘DN’>,), output_types=(<class
‘numpy.float32’>,)), Band(name=‘Tree_CoverFraction_layer’, units=(<Unit.DN: ‘DN’>,),
output_types=(<class ‘numpy.float32’>,)))
is_timeless: False
has_cloud_coverage: False )>