44from hypothesis import given
55from hypothesis .strategies import lists
66
7- from tests .storage_data import ers_granules , s5p_granules , umbra_granules
8- from tilebox .storage .granule import ASFStorageGranule , CopernicusStorageGranule , UmbraStorageGranule , _asf_download_urls
7+ from tests .storage_data import ers_granules , landsat_granules , s5p_granules , umbra_granules
8+ from tilebox .storage .granule import (
9+ ASFStorageGranule ,
10+ CopernicusStorageGranule ,
11+ UmbraStorageGranule ,
12+ USGSLandsatStorageGranule ,
13+ _asf_download_urls ,
14+ _thumbnail_relative_to_eodata_location ,
15+ )
916
1017
1118def _asf_granule_to_datapoint (granule : ASFStorageGranule ) -> xr .Dataset :
@@ -53,7 +60,6 @@ def _umbra_granule_to_datapoint(granule: UmbraStorageGranule) -> xr.Dataset:
5360 datapoint = xr .Dataset ()
5461 datapoint .coords ["time" ] = np .array (granule .time ).astype ("datetime64[ns]" )
5562 datapoint ["granule_name" ] = granule .granule_name
56- datapoint ["processing_level" ] = granule .processing_level
5763 datapoint ["location" ] = granule .location
5864 return datapoint
5965
@@ -76,12 +82,51 @@ def test_granule_from_umbra_datapoints(granules: list[UmbraStorageGranule]) -> N
7682 assert UmbraStorageGranule .from_data (dataset .isel (time = i )) == granules [i ]
7783
7884
85+ @pytest .mark .parametrize (
86+ ("thumbnail_url" , "location" , "expected" ),
87+ [
88+ (
89+ "https://catalogue.dataspace.copernicus.eu/get-object?path=/Sentinel-1/SAR/EW_GRDM_1S/2025/08/07/S1A_EW_GRDM_1SDH_20250807T111242_20250807T111346_060429_078305_DB6A.SAFE/preview/thumbnail.png" ,
90+ "/eodata/Sentinel-1/SAR/EW_GRDM_1S/2025/08/07/S1A_EW_GRDM_1SDH_20250807T111242_20250807T111346_060429_078305_DB6A.SAFE" ,
91+ "preview/thumbnail.png" ,
92+ ),
93+ (
94+ "https://catalogue.dataspace.copernicus.eu/get-object?path=/Sentinel-2/MSI/L1C/2025/08/07/S2B_MSIL1C_20250807T004159_N0511_R045_T08XNR_20250807T004945.SAFE/S2B_MSIL1C_20250807T004159_N0511_R045_T08XNR_20250807T004945-ql.jpg" ,
95+ "/eodata/Sentinel-2/MSI/L1C/2025/08/07/S2B_MSIL1C_20250807T004159_N0511_R045_T08XNR_20250807T004945.SAFE" ,
96+ "S2B_MSIL1C_20250807T004159_N0511_R045_T08XNR_20250807T004945-ql.jpg" ,
97+ ),
98+ (
99+ "https://catalogue.dataspace.copernicus.eu/get-object?path=/Sentinel-3/OLCI/OL_2_LFR___/2025/08/07/S3A_OL_2_LFR____20250807T011653_20250807T011953_20250807T033036_0179_129_074_1620_PS1_O_NR_003.SEN3/quicklook.jpg" ,
100+ "/eodata/Sentinel-3/OLCI/OL_2_LFR___/2025/08/07/S3A_OL_2_LFR____20250807T011653_20250807T011953_20250807T033036_0179_129_074_1620_PS1_O_NR_003.SEN3" ,
101+ "quicklook.jpg" ,
102+ ),
103+ (
104+ "https://catalogue.dataspace.copernicus.eu/get-object?path=/Sentinel-3/SLSTR/SL_1_RBT___/2025/08/07/S3B_SL_1_RBT____20250807T002314_20250807T002614_20250807T025411_0179_109_316_0720_ESA_O_NR_004.SEN3/quicklook.jpg" ,
105+ "/eodata/Sentinel-3/SLSTR/SL_1_RBT___/2025/08/07/S3B_SL_1_RBT____20250807T002314_20250807T002614_20250807T025411_0179_109_316_0720_ESA_O_NR_004.SEN3" ,
106+ "quicklook.jpg" ,
107+ ),
108+ (
109+ "https://catalogue.dataspace.copernicus.eu/get-object?path=/Sentinel-3/SYNERGY/SY_2_VG1___/2025/08/04/S3A_SY_2_VG1____20250804T000000_20250804T235959_20250806T202029_AUSTRALASIA_______PS1_O_NT_002.SEN3/quicklook.jpg" ,
110+ "/eodata/Sentinel-3/SYNERGY/SY_2_VG1___/2025/08/04/S3A_SY_2_VG1____20250804T000000_20250804T235959_20250806T202029_AUSTRALASIA_______PS1_O_NT_002.SEN3" ,
111+ "quicklook.jpg" ,
112+ ),
113+ ],
114+ )
115+ def test_thumbnail_relative_to_eodata_location (thumbnail_url : str , location : str , expected : str ) -> None :
116+ assert (
117+ _thumbnail_relative_to_eodata_location (
118+ thumbnail_url ,
119+ location ,
120+ )
121+ == expected
122+ )
123+
124+
79125def _copernicus_granule_to_datapoint (granule : CopernicusStorageGranule ) -> xr .Dataset :
80126 datapoint = xr .Dataset ()
81127 datapoint .coords ["time" ] = np .array (granule .time ).astype ("datetime64[ns]" )
82128 datapoint ["granule_name" ] = granule .granule_name
83129 datapoint ["location" ] = granule .location
84- datapoint ["file_size" ] = granule .file_size
85130 return datapoint
86131
87132
@@ -101,3 +146,31 @@ def test_granule_from_copernicus_datapoints(granules: list[CopernicusStorageGran
101146
102147 for i in range (len (granules )): # converting a dataset with a time dimension of 1 should still work though
103148 assert CopernicusStorageGranule .from_data (dataset .isel (time = i )) == granules [i ]
149+
150+
151+ def _landsat_granule_to_datapoint (granule : USGSLandsatStorageGranule ) -> xr .Dataset :
152+ datapoint = xr .Dataset ()
153+ datapoint .coords ["time" ] = np .array (granule .time ).astype ("datetime64[ns]" )
154+ datapoint ["granule_name" ] = granule .granule_name
155+ datapoint ["location" ] = granule .location
156+ if granule .thumbnail is not None :
157+ datapoint ["thumbnail" ] = f"{ granule .location } /{ granule .thumbnail } "
158+ return datapoint
159+
160+
161+ @given (landsat_granules ())
162+ def test_granule_from_landsat_datapoint (granule : USGSLandsatStorageGranule ) -> None :
163+ datapoint = _landsat_granule_to_datapoint (granule )
164+ assert USGSLandsatStorageGranule .from_data (datapoint ) == granule
165+ assert USGSLandsatStorageGranule .from_data (USGSLandsatStorageGranule .from_data (datapoint )) == granule
166+
167+
168+ @given (lists (landsat_granules (), min_size = 2 , max_size = 5 ))
169+ def test_granule_from_landsat_datapoints (granules : list [USGSLandsatStorageGranule ]) -> None :
170+ datapoints = [_landsat_granule_to_datapoint (granule ) for granule in granules ]
171+ dataset = xr .concat (datapoints , dim = "time" )
172+ with pytest .raises (ValueError , match = ".*more than one granule.*" ):
173+ USGSLandsatStorageGranule .from_data (dataset )
174+
175+ for i in range (len (granules )): # converting a dataset with a time dimension of 1 should still work though
176+ assert USGSLandsatStorageGranule .from_data (dataset .isel (time = i )) == granules [i ]
0 commit comments