This module contains Sentinel-2 classes (sources and scenes).
Overview
Right not, the following sentinel products are implemented:
- Level 2A from Theia (from local files)
- Level 3A from Theia (from local files)
- Level 2A from Microsoft Planetary Computer (from STAC)
Scene classes:
classDiagram
Scene <|-- Sentinel2SceneBase
Sentinel2SceneBase <|-- Sentinel2MPCScene
Sentinel2SceneBase <|-- Sentinel2TheiaScene
Sentinel2TheiaScene <|-- Sentinel22AScene
Sentinel2TheiaScene <|-- Sentinel23AScene
Source classes:
classDiagram
Source <|-- CommonImagerySource
CommonImagerySource <|-- Sentinel2TheiaGenericSource
Sentinel2TheiaGenericSource <|-- Sentinel2Theia2ASource
Sentinel2TheiaGenericSource <|-- Sentinel2Theia3ASource
The Sentinel2MPCScene class delivers sources which are of type
CommonImagerySource.
Sources
The Sentinel2SceneBase class contains generic methods to access all
Sentinel-2 products Sources members.
- Single band getters (e.g. b3, b4)
- Concatenated bands getters (e.g. 10m bands)
You can do the following to list available sources:
print(sc) # show everything, including the sources names
print(sc.sources.keys()) # show only the sources keys
Single image getters
Any Source corresponding to a specific raster asset can be retrieved using
the get_<name>() function call.
For instance this enables to retrieve the band 4:
Concatenated bands
The sources carry the following images for:
- The 10m spacing channels, in the following order: 4, 3, 2, 8
- The 20m spacing channels, in the following order: 5, 6, 7, 8a, 11, 12
bands_10m = sc.get_10m_bands()
bands_20m = sc.get_20m_bands()
Subset of bands can also be requested:
bands_10m_2a = sc_2a.get_10m_bands("b4")
bands_10m_2a = sc_2a.get_10m_bands("b4", "b8")
bands_10m_2a = sc_2a.get_10m_bands(["b4", "b8"])
Theia
The Sentinel22AScene and Sentinel23AScene classes carry metadata and
sources respectively for Sentinel-2 Level 2A and Level 3A products. They both
inherit from the generic abstract Sentinel2TheiaScene class.
Instantiation
Sentinel22AScene and Sentinel23AScene are instantiated from the archive
(.zip file) or the product folder.
import scenes
sc_2a = scenes.sentinel.Sentinel22AScene("SENTINEL2B_..._T31TEJ_D_V1-8.zip")
sc_3a = scenes.sentinel.Sentinel23AScene("SENTINEL2X_...L3A_T31TEJ_D_V1-0.zip")
The scene metadata can be accessed with the metadata attribute, like any
scenes.core.Scene instance.
md_dict = sc_2a.metadata
scenes.utils.pprint(md_dict)
Sources
The Sentinel2Theia2ASource and Sentinel2Theia3ASource classes carry imagery
sources respectively for Sentinel-2 Level 2A and Level 3A products. They both
inherit from the generic Sentinel2TheiaGenericSource class, which itself
inherits from CommonImagerySource.
classDiagram
Sentinel2TheiaGenericSource <|-- Sentinel2Theia2ASource
Sentinel2TheiaGenericSource <|-- Sentinel2Theia3ASource
class Sentinel2TheiaGenericSource{
+R1_SIZE
+R2_SIZE
+msk_drilled(msk_dict, exp, nodata=0)
}
class Sentinel2Theia2ASource{
+cld_msk_drilled(nodata=0)
}
class Sentinel2Theia3ASource{
+flg_msk_drilled(keep_flags_values=(3, 4), nodata=0)
}
The Sentinel2Theia2ASource implements the
Sentinel2Theia2ASource.cld_msk_drilled method, that enable to mask the cloud
masks over the root source, with the specified no-data value (default is 0).
The following example show how to derive a child source replacing the
pixels that are in the clouds with pixels at -10000 (which is the no-data
value in Theia products):
drilled = bands_10m_2a.cld_msk_drilled()
The Sentinel2Theia3ASource implements the
Sentinel2Theia3ASource.flg_msk_drilled method, that enable to mask the pixels
on a selection of labels of the quality mask. The following example shows how
to mask pixels of anything other that land with -10000:
drilled = bands_10m_3a.flg_msk_drilled(keep_flags_values=(4,))
Sentinel2Theia2ASource and Sentinel2Theia3ASource inherit from
scenes.core.CommonImagerySource, hence implemented sources transformations
(e.g. scenes.core.CommonImagerySource.masked,
scenes.core.CommonImagerySource.clip_over_img,
scenes.core.CommonImagerySource.resample_over,
scenes.core.CommonImagerySource.reproject, etc.)
clipped = drilled.clip_over_img(roi)
reprojected = clipped.reproject(epsg=4328)
Note that the resulting transformed Sentinel2Theia2ASource and
Sentinel2Theia3ASource are still instances of Sentinel2Theia2ASource and
Sentinel2Theia3ASource after generic operations implemented in
scenes.core.CommonImagerySource.
Usage with pyotb
As scenes.core.Source, it also can be used like any pyotb.core.OTBObject.
The following example show how to use an OTB application with a source at
input.
rgb_nice = pyotb.DynamicConvert(reprojected)
rgb_nice.write("image.tif", pixel_type="uint8")
Sentinel22AScene
Bases: Sentinel2TheiaScene
Sentinel-2 level 2A scene class.
The class carries all the metadata from the root_scene, and can be used to
retrieve its sources.
Source code in scenes/sentinel.py
| class Sentinel22AScene(Sentinel2TheiaScene):
"""
Sentinel-2 level 2A scene class.
The class carries all the metadata from the root_scene, and can be used to
retrieve its sources.
"""
def __init__(self, archive: str, tag: str = "FRE"):
"""
Args:
archive: .zip file or folder. Must be a product from MAJA.
tag: product tag (SRE/FRE)
"""
masks = ["clm_r1", "edg_r1", "clm_r2", "edg_r2"]
src_classes = {
**{key: Sentinel2Theia2ASource for key in self.bands_keys},
**{key: CommonImagerySource for key in masks}
}
super().__init__(archive=archive, tag=tag, src_classes=src_classes)
|
__init__(archive, tag='FRE')
Parameters:
| Name |
Type |
Description |
Default |
archive |
str
|
.zip file or folder. Must be a product from MAJA.
|
required
|
tag |
str
|
|
'FRE'
|
Source code in scenes/sentinel.py
| def __init__(self, archive: str, tag: str = "FRE"):
"""
Args:
archive: .zip file or folder. Must be a product from MAJA.
tag: product tag (SRE/FRE)
"""
masks = ["clm_r1", "edg_r1", "clm_r2", "edg_r2"]
src_classes = {
**{key: Sentinel2Theia2ASource for key in self.bands_keys},
**{key: CommonImagerySource for key in masks}
}
super().__init__(archive=archive, tag=tag, src_classes=src_classes)
|
Sentinel23AScene
Bases: Sentinel2TheiaScene
Sentinel-2 level 3A scene class.
The class carries all the metadata from the root_scene, and can be used to
retrieve its sources.
Source code in scenes/sentinel.py
| class Sentinel23AScene(Sentinel2TheiaScene):
"""Sentinel-2 level 3A scene class.
The class carries all the metadata from the root_scene, and can be used to
retrieve its sources.
"""
def __init__(self, archive: str, tag: str = "FRC"):
"""
Args:
archive: .zip file or folder. Must be a product from WASP.
tag: product tag (FRC)
"""
masks = ["flg_r1", "flg_r2"]
src_classes = {
**{key: Sentinel2Theia3ASource for key in self.bands_keys},
**{key: CommonImagerySource for key in masks}
}
super().__init__(archive=archive, tag=tag, src_classes=src_classes)
|
__init__(archive, tag='FRC')
Parameters:
| Name |
Type |
Description |
Default |
archive |
str
|
.zip file or folder. Must be a product from WASP.
|
required
|
tag |
str
|
|
'FRC'
|
Source code in scenes/sentinel.py
| def __init__(self, archive: str, tag: str = "FRC"):
"""
Args:
archive: .zip file or folder. Must be a product from WASP.
tag: product tag (FRC)
"""
masks = ["flg_r1", "flg_r2"]
src_classes = {
**{key: Sentinel2Theia3ASource for key in self.bands_keys},
**{key: CommonImagerySource for key in masks}
}
super().__init__(archive=archive, tag=tag, src_classes=src_classes)
|
Sentinel2MPCScene
Bases: Sentinel2SceneBase
class for Sentinel-2 images from mMicrosoft Planetary Computer
Source code in scenes/sentinel.py
| class Sentinel2MPCScene(Sentinel2SceneBase):
"""class for Sentinel-2 images from mMicrosoft Planetary Computer"""
def __init__(
self,
assets_paths: Dict[str, str],
):
"""
Args:
assets_paths: assets paths
"""
# Assets (spectral bands)
# We just use the same key for all Sentinel2SceneBase products
bands_names_mapping = {
"b4": "B04",
"b3": "B03",
"b2": "B02",
"b8": "B08",
"b5": "B05",
"b6": "B06",
"b7": "B07",
"b8a": "B8A",
"b11": "B11",
"b12": "B12"
}
updated_assets_paths = {
key: assets_paths[asset_key] for key, asset_key
in bands_names_mapping.items()
}
# Sources classes
src_classes = {
key: CommonImagerySource for key in updated_assets_paths
}
# Date, extent
b2_path = updated_assets_paths["b2"]
# Here, b2_path is something like ...T45WXU_20221019T062901_B03_10m.tif
epsg, extent = get_epsg_extent_wgs84(b2_path)
datestr = b2_path.split("_")[-3] # 20180630T105440
acquisition_date = datetime.strptime(datestr, '%Y%m%dT%H%M%S')
# Call parent constructor
super().__init__(
acquisition_date=acquisition_date,
epsg=epsg,
extent_wgs84=extent,
assets_paths=updated_assets_paths,
src_classes=src_classes
)
|
__init__(assets_paths)
Parameters:
| Name |
Type |
Description |
Default |
assets_paths |
Dict[str, str]
|
|
required
|
Source code in scenes/sentinel.py
| def __init__(
self,
assets_paths: Dict[str, str],
):
"""
Args:
assets_paths: assets paths
"""
# Assets (spectral bands)
# We just use the same key for all Sentinel2SceneBase products
bands_names_mapping = {
"b4": "B04",
"b3": "B03",
"b2": "B02",
"b8": "B08",
"b5": "B05",
"b6": "B06",
"b7": "B07",
"b8a": "B8A",
"b11": "B11",
"b12": "B12"
}
updated_assets_paths = {
key: assets_paths[asset_key] for key, asset_key
in bands_names_mapping.items()
}
# Sources classes
src_classes = {
key: CommonImagerySource for key in updated_assets_paths
}
# Date, extent
b2_path = updated_assets_paths["b2"]
# Here, b2_path is something like ...T45WXU_20221019T062901_B03_10m.tif
epsg, extent = get_epsg_extent_wgs84(b2_path)
datestr = b2_path.split("_")[-3] # 20180630T105440
acquisition_date = datetime.strptime(datestr, '%Y%m%dT%H%M%S')
# Call parent constructor
super().__init__(
acquisition_date=acquisition_date,
epsg=epsg,
extent_wgs84=extent,
assets_paths=updated_assets_paths,
src_classes=src_classes
)
|
Sentinel2SceneBase
Bases: Scene
Base class for Sentinel-2 images
Source code in scenes/sentinel.py
| class Sentinel2SceneBase(Scene):
"""Base class for Sentinel-2 images"""
bands_keys = BANDS_1 + BANDS_2
concatenated_bands_dict = {
"10m_bands": BANDS_1,
"20m_bands": BANDS_2
}
def __init__(
self,
acquisition_date: datetime,
epsg: int,
extent_wgs84: List[Tuple(float, float)],
assets_paths: List[str, str],
src_classes: Dict[str, Type[Source]] = None,
additional_metadata: Dict[str, Any] = None,
):
"""
Initialize the Sentinel-2 Scene
Args:
acquisition_date: Acquisition date
epsg: EPSG code
extent_wgs84: extent in WGS84 coordinates reference system
assets_paths: assets dictionary
{source_name, source_path/url}, e.g.:
{'b4': '/vsicurl/https://.../...B4.tif'}
src_classes: imagery sources class
additional_metadata: additional metadata
"""
assert all(key in src_classes for key in self.bands_keys), \
"Some keys in concatenated_bands_dict are not in src_classes"
# Sources
sources = {
key: partial(src_class, root_scene=self, out=assets_paths[key])
for key, src_class in src_classes.items()
}
# Sources for concatenated bands: get_b10m_bands, get_20m_bands
for key, default_bands_names in self.concatenated_bands_dict.items():
sources.update({
key: partial(
self._get_bands,
src_classes[default_bands_names[0]],
default_bands_names
)
})
super().__init__(
acquisition_date=acquisition_date,
epsg=epsg,
extent_wgs84=extent_wgs84,
assets_paths=assets_paths,
sources=sources,
additional_metadata=additional_metadata
)
def _get_bands(
self,
imagery_src_class,
default_bands_names: tuple(str),
*args
) -> Source:
"""
Returns a Source for the concatenated bands
Args:
imagery_src_class: source class
default_bands_names: names of available bands
*args: can be:
- names of the bands
- a name
- a list or a tuple of names
Returns:
Selected spectral bands
"""
bands = default_bands_names
if args:
bands = []
for arg in args:
bands += arg if isinstance(arg, (list, tuple)) else [arg]
for band in bands:
assert isinstance(band, str), f"{band} is not a string"
# user can use upper or lower cases
bands = [band.lower() for band in bands]
assert all(band in default_bands_names for band in bands), \
f"Some bands in {bands} are not in the available bands " \
f"{default_bands_names}"
concat = pyotb.ConcatenateImages(
[self.get_asset_path(band_name) for band_name in bands]
)
return imagery_src_class(self, concat)
|
__init__(acquisition_date, epsg, extent_wgs84, assets_paths, src_classes=None, additional_metadata=None)
Initialize the Sentinel-2 Scene
Parameters:
| Name |
Type |
Description |
Default |
acquisition_date |
datetime
|
|
required
|
epsg |
int
|
|
required
|
extent_wgs84 |
List[Tuple(float, float)]
|
extent in WGS84 coordinates reference system
|
required
|
assets_paths |
List[str, str]
|
assets dictionary
{source_name, source_path/url}, e.g.:
{'b4': '/vsicurl/https://.../...B4.tif'}
|
required
|
src_classes |
Dict[str, Type[Source]]
|
|
None
|
additional_metadata |
Dict[str, Any]
|
|
None
|
Source code in scenes/sentinel.py
| def __init__(
self,
acquisition_date: datetime,
epsg: int,
extent_wgs84: List[Tuple(float, float)],
assets_paths: List[str, str],
src_classes: Dict[str, Type[Source]] = None,
additional_metadata: Dict[str, Any] = None,
):
"""
Initialize the Sentinel-2 Scene
Args:
acquisition_date: Acquisition date
epsg: EPSG code
extent_wgs84: extent in WGS84 coordinates reference system
assets_paths: assets dictionary
{source_name, source_path/url}, e.g.:
{'b4': '/vsicurl/https://.../...B4.tif'}
src_classes: imagery sources class
additional_metadata: additional metadata
"""
assert all(key in src_classes for key in self.bands_keys), \
"Some keys in concatenated_bands_dict are not in src_classes"
# Sources
sources = {
key: partial(src_class, root_scene=self, out=assets_paths[key])
for key, src_class in src_classes.items()
}
# Sources for concatenated bands: get_b10m_bands, get_20m_bands
for key, default_bands_names in self.concatenated_bands_dict.items():
sources.update({
key: partial(
self._get_bands,
src_classes[default_bands_names[0]],
default_bands_names
)
})
super().__init__(
acquisition_date=acquisition_date,
epsg=epsg,
extent_wgs84=extent_wgs84,
assets_paths=assets_paths,
sources=sources,
additional_metadata=additional_metadata
)
|
Sentinel2Theia2ASource
Bases: Sentinel2TheiaGenericSource
Sentinel-2 level 2A source class
Source code in scenes/sentinel.py
| class Sentinel2Theia2ASource(Sentinel2TheiaGenericSource):
"""Sentinel-2 level 2A source class"""
def cld_msk_drilled(
self,
nodata: Union[float, int] = -10000
) -> Sentinel2Theia2ASource:
"""Return the source drilled from the cloud mask
Args:
nodata: nodata value inside holes (Default value = -10000)
Returns:
drilled source
"""
return self.msk_drilled(
msk_dict={
self.R1_SPC: self.root_scene.get_asset_path("clm_r1"),
self.R2_SPC: self.root_scene.get_asset_path("clm_r2")
},
exp="im1b1==0?255:0",
nodata=nodata
)
|
cld_msk_drilled(nodata=-10000)
Return the source drilled from the cloud mask
Parameters:
| Name |
Type |
Description |
Default |
nodata |
Union[float, int]
|
nodata value inside holes (Default value = -10000)
|
-10000
|
Returns:
Source code in scenes/sentinel.py
| def cld_msk_drilled(
self,
nodata: Union[float, int] = -10000
) -> Sentinel2Theia2ASource:
"""Return the source drilled from the cloud mask
Args:
nodata: nodata value inside holes (Default value = -10000)
Returns:
drilled source
"""
return self.msk_drilled(
msk_dict={
self.R1_SPC: self.root_scene.get_asset_path("clm_r1"),
self.R2_SPC: self.root_scene.get_asset_path("clm_r2")
},
exp="im1b1==0?255:0",
nodata=nodata
)
|
Sentinel2Theia3ASource
Bases: Sentinel2TheiaGenericSource
Sentinel-2 level 3A source class
Source code in scenes/sentinel.py
| class Sentinel2Theia3ASource(Sentinel2TheiaGenericSource):
"""Sentinel-2 level 3A source class"""
def flg_msk_drilled(
self,
keep_flags_values: Tuple[int] = (3, 4),
nodata: Union[float, int] = -10000
) -> Sentinel2Theia3ASource:
"""
Return the source drilled from the FLG mask
Args:
keep_flags_values: flags values to keep (Default value = (3, 4)).
Can be:
- 0 = No data
- 1 = Cloud
- 2 = Snow
- 3 = Water
- 4 = Land
(source:
https://labo.obs-mip.fr/multitemp/theias-l3a-product-format/)
nodata: nodata value inside holes (Default value = -10000)
Returns:
drilled source
"""
cond = "||".join([f"im1b1=={val}" for val in keep_flags_values])
exp = cond + "?255:0"
return self.msk_drilled(
msk_dict={
self.R1_SPC: self.root_scene.get_asset_path("flg_r1"),
self.R2_SPC: self.root_scene.get_asset_path("flg_r2")
},
exp=exp,
nodata=nodata
)
|
flg_msk_drilled(keep_flags_values=(3, 4), nodata=-10000)
Return the source drilled from the FLG mask
Parameters:
| Name |
Type |
Description |
Default |
keep_flags_values |
Tuple[int]
|
flags values to keep (Default value = (3, 4)).
Can be:
- 0 = No data
- 1 = Cloud
- 2 = Snow
- 3 = Water
- 4 = Land
(source:
https://labo.obs-mip.fr/multitemp/theias-l3a-product-format/)
|
(3, 4)
|
nodata |
Union[float, int]
|
nodata value inside holes (Default value = -10000)
|
-10000
|
Returns:
Source code in scenes/sentinel.py
| def flg_msk_drilled(
self,
keep_flags_values: Tuple[int] = (3, 4),
nodata: Union[float, int] = -10000
) -> Sentinel2Theia3ASource:
"""
Return the source drilled from the FLG mask
Args:
keep_flags_values: flags values to keep (Default value = (3, 4)).
Can be:
- 0 = No data
- 1 = Cloud
- 2 = Snow
- 3 = Water
- 4 = Land
(source:
https://labo.obs-mip.fr/multitemp/theias-l3a-product-format/)
nodata: nodata value inside holes (Default value = -10000)
Returns:
drilled source
"""
cond = "||".join([f"im1b1=={val}" for val in keep_flags_values])
exp = cond + "?255:0"
return self.msk_drilled(
msk_dict={
self.R1_SPC: self.root_scene.get_asset_path("flg_r1"),
self.R2_SPC: self.root_scene.get_asset_path("flg_r2")
},
exp=exp,
nodata=nodata
)
|
Sentinel2TheiaGenericSource
Bases: CommonImagerySource
Class for generic Sentinel-2 sources
Source code in scenes/sentinel.py
| class Sentinel2TheiaGenericSource(CommonImagerySource):
"""Class for generic Sentinel-2 sources"""
R1_SPC = 10.0
R2_SPC = 20.0
def msk_drilled(
self,
msk_dict: Dict[float, str],
exp: str,
nodata: Union[float, int] = 0
) -> Sentinel2TheiaGenericSource:
"""
Args:
msk_dict: dict of masks
exp: bandmath expression to form the 0-255 binary mask
nodata: no-data value in masked output (Default value = 0)
Returns:
new masked source
"""
img_spc = pyotb.ReadImageInfo(self)['spacingx']
if img_spc not in msk_dict:
raise KeyError(f"No mask for image spacing {img_spc}")
binary_mask = pyotb.BandMath({"il": msk_dict[img_spc], "exp": exp})
return self.masked(binary_mask=binary_mask, nodata=nodata)
|
msk_drilled(msk_dict, exp, nodata=0)
Parameters:
| Name |
Type |
Description |
Default |
msk_dict |
Dict[float, str]
|
|
required
|
exp |
str
|
bandmath expression to form the 0-255 binary mask
|
required
|
nodata |
Union[float, int]
|
no-data value in masked output (Default value = 0)
|
0
|
Returns:
Source code in scenes/sentinel.py
| def msk_drilled(
self,
msk_dict: Dict[float, str],
exp: str,
nodata: Union[float, int] = 0
) -> Sentinel2TheiaGenericSource:
"""
Args:
msk_dict: dict of masks
exp: bandmath expression to form the 0-255 binary mask
nodata: no-data value in masked output (Default value = 0)
Returns:
new masked source
"""
img_spc = pyotb.ReadImageInfo(self)['spacingx']
if img_spc not in msk_dict:
raise KeyError(f"No mask for image spacing {img_spc}")
binary_mask = pyotb.BandMath({"il": msk_dict[img_spc], "exp": exp})
return self.masked(binary_mask=binary_mask, nodata=nodata)
|
Sentinel2TheiaScene
Bases: Sentinel2SceneBase
Base class for Sentinel-2 images from Theia
Source code in scenes/sentinel.py
| class Sentinel2TheiaScene(Sentinel2SceneBase):
"""Base class for Sentinel-2 images from Theia"""
@abstractmethod
def __init__(
self,
archive: str,
tag: str,
src_classes: Dict[str, Type[Source]]
):
"""
Args:
archive: product .zip or directory
tag: pattern to match in filenames, e.g. "FRE"
src_classes: imagery sources classes dict
"""
self.archive = archive
# Retrieve the list of .tif files
is_zip = self.archive.lower().endswith(".zip")
if is_zip:
# print("Input type is a .zip archive")
files = utils.list_files_in_zip(self.archive)
self.files = [utils.to_vsizip(self.archive, f) for f in files]
else:
# print("Input type is a directory")
self.files = utils.find_files_in_all_subdirs(
self.archive, "*.tif", case_sensitive=False
)
# Assets
assets_paths = {}
# In Theia products, the suffixes are always uppercase (B4, ..., B8A)
# so we need to retrieve the files from the uppercase key.
# 1. Spectral bands
for key in self.bands_keys:
suffix = key.upper() # e.g. b4 --> B4
assets_paths[key] = self.get_file(endswith=f"_{tag}_{suffix}.tif")
# 2. Other assets (quality masks, etc)
for key in [key for key in src_classes if key not in assets_paths]:
suffix = key.upper() # e.g. cld_r1 --> CLD_R1
assets_paths[key] = self.get_file(endswith=f"_{suffix}.tif")
# Date, extent
b2_path = assets_paths["b2"]
epsg, extent = get_epsg_extent_wgs84(b2_path)
# Basename, e.g. SENTINEL2A_20180630-105440-000_L2A_T31TEJ_D_V1-8
b2_basepath = utils.basename(b2_path)
datestr = b2_basepath.split("_")[1] # e.g. 20180630-105440
acquisition_date = datetime.strptime(datestr, '%Y%m%d-%H%M%S-%f')
# Call parent constructor
super().__init__(
acquisition_date=acquisition_date,
epsg=epsg,
extent_wgs84=extent,
assets_paths=assets_paths,
src_classes=src_classes,
additional_metadata={"archive": archive}
)
def get_file(self, endswith: str) -> str:
"""
Return the specified file.
Args:
endswith: filtered extension
Returns:
the file
"""
filtered_files_list = [f for f in self.files if f.endswith(endswith)]
nb_matches = len(filtered_files_list)
if nb_matches == 1:
return filtered_files_list[0]
print(
"Warning: "
f"{self.archive}: found {nb_matches} occurrence(s) of file "
f"with suffix \"{endswith}\""
)
|
__init__(archive, tag, src_classes)
abstractmethod
Parameters:
| Name |
Type |
Description |
Default |
archive |
str
|
product .zip or directory
|
required
|
tag |
str
|
pattern to match in filenames, e.g. "FRE"
|
required
|
src_classes |
Dict[str, Type[Source]]
|
imagery sources classes dict
|
required
|
Source code in scenes/sentinel.py
| @abstractmethod
def __init__(
self,
archive: str,
tag: str,
src_classes: Dict[str, Type[Source]]
):
"""
Args:
archive: product .zip or directory
tag: pattern to match in filenames, e.g. "FRE"
src_classes: imagery sources classes dict
"""
self.archive = archive
# Retrieve the list of .tif files
is_zip = self.archive.lower().endswith(".zip")
if is_zip:
# print("Input type is a .zip archive")
files = utils.list_files_in_zip(self.archive)
self.files = [utils.to_vsizip(self.archive, f) for f in files]
else:
# print("Input type is a directory")
self.files = utils.find_files_in_all_subdirs(
self.archive, "*.tif", case_sensitive=False
)
# Assets
assets_paths = {}
# In Theia products, the suffixes are always uppercase (B4, ..., B8A)
# so we need to retrieve the files from the uppercase key.
# 1. Spectral bands
for key in self.bands_keys:
suffix = key.upper() # e.g. b4 --> B4
assets_paths[key] = self.get_file(endswith=f"_{tag}_{suffix}.tif")
# 2. Other assets (quality masks, etc)
for key in [key for key in src_classes if key not in assets_paths]:
suffix = key.upper() # e.g. cld_r1 --> CLD_R1
assets_paths[key] = self.get_file(endswith=f"_{suffix}.tif")
# Date, extent
b2_path = assets_paths["b2"]
epsg, extent = get_epsg_extent_wgs84(b2_path)
# Basename, e.g. SENTINEL2A_20180630-105440-000_L2A_T31TEJ_D_V1-8
b2_basepath = utils.basename(b2_path)
datestr = b2_basepath.split("_")[1] # e.g. 20180630-105440
acquisition_date = datetime.strptime(datestr, '%Y%m%d-%H%M%S-%f')
# Call parent constructor
super().__init__(
acquisition_date=acquisition_date,
epsg=epsg,
extent_wgs84=extent,
assets_paths=assets_paths,
src_classes=src_classes,
additional_metadata={"archive": archive}
)
|
get_file(endswith)
Return the specified file.
Parameters:
| Name |
Type |
Description |
Default |
endswith |
str
|
|
required
|
Returns:
Source code in scenes/sentinel.py
| def get_file(self, endswith: str) -> str:
"""
Return the specified file.
Args:
endswith: filtered extension
Returns:
the file
"""
filtered_files_list = [f for f in self.files if f.endswith(endswith)]
nb_matches = len(filtered_files_list)
if nb_matches == 1:
return filtered_files_list[0]
print(
"Warning: "
f"{self.archive}: found {nb_matches} occurrence(s) of file "
f"with suffix \"{endswith}\""
)
|
get_local_scenes(root_dir, tile=None)
Retrieve the sentinel scenes in the directory
Parameters:
| Name |
Type |
Description |
Default |
root_dir |
str
|
|
required
|
tile |
str
|
tile name (optional) e.g. 31TEJ
|
None
|
Returns:
Source code in scenes/sentinel.py
| def get_local_scenes(
root_dir: str,
tile: str = None
) -> List[Union[Sentinel22AScene, Sentinel23AScene]]:
"""
Retrieve the sentinel scenes in the directory
Args:
root_dir: directory
tile: tile name (optional) e.g. 31TEJ
Returns:
a list of sentinel scenes instances
"""
scenes_list = []
archives = utils.find_files_in_all_subdirs(
pth=root_dir, pattern="*.zip", case_sensitive=False
)
for archive in tqdm(archives):
candidate = get_scene(archive)
if candidate:
tile_name = archive.split("_")[3]
if not tile or tile_name == tile:
scenes_list.append(candidate)
return scenes_list
|
get_scene(archive)
Return the right Scene instance from the given archive (L2A or L3A)
Parameters:
| Name |
Type |
Description |
Default |
archive |
str
|
|
required
|
Returns:
Source code in scenes/sentinel.py
| def get_scene(archive: str) -> Union[Sentinel22AScene, Sentinel23AScene]:
"""
Return the right `Scene` instance from the given archive (L2A or L3A)
Args:
archive: L3A or L3A archive
Returns:
a Sentinel23AScene or Sentinel22AScene instance
"""
# todo: raise filenotfound if file/path not exist
splits = utils.basename(archive).split("_")
if len(splits) > 4:
level = splits[2]
if level == "L3A":
return Sentinel23AScene(archive)
if level == "L2A":
return Sentinel22AScene(archive)
print(f"Warning: file {archive} is not a valid Sentinel-2 product")
return None
|