diff --git a/mkdocs.yml b/mkdocs.yml index 8d63f459eb41023d1af26e8a58f7863d6852850e..cc806fdc50e874af46bab93cc8166200a07fd20b 100755 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -2,7 +2,7 @@ theme: name: "material" icon: - repo: material/github + repo: fontawesome/brands/gitlab features: - content.code.annotate - navigation.tabs @@ -30,7 +30,7 @@ extra: feature: tabs: true social: - - icon: material/gitlab + - icon: fontawesome/brands/gitlab link: https://gitlab.irstea.fr/umr-tetis/scenes markdown_extensions: @@ -50,5 +50,6 @@ markdown_extensions: # rest of the navigation.. site_name: Scenes - +repo_url: https://gitlab.irstea.fr/umr-tetis/scenes +repo_name: umr-tetis/scenes docs_dir: doc/ diff --git a/scenes/dates.py b/scenes/dates.py index 0e2fe869d797f9edecdb4dfb1f038af288802ddd..c65972c2d3634fbe9b2bd94c65c08680e07bd676 100644 --- a/scenes/dates.py +++ b/scenes/dates.py @@ -5,12 +5,11 @@ This module aims to deal with dates. --- The `datetime.datetime` class is used as internal date type. -``` -#!python +``` py dt = datetime.datetime(year=2020, month=12, day=2) ``` Is equivalent to: -``` +``` py dt = str2datetime("02-12-2020") dt = str2datetime("2020-12-02") dt = str2datetime("02/12/2020") @@ -18,7 +17,7 @@ dt = str2datetime("02/12/2020") The `any2datetime` method returns a `datetime.datetime` instance, whatever the input is (`str` or `datetime.datetime`). -``` +``` py dt1 = datetime.datetime(year=2020, month=12, day=2) dt2 = str2datetime("02-12-2020") dt1_2 = any2datetime(dt1) # same @@ -26,7 +25,7 @@ dt2_2 = any2datetime("02-12-2020") # same ``` The `get_timestamp` method converts a `datetime.datetime` instance into a number of seconds (int). -``` +``` py ts = get_timestamp(dt) # 1606780800.0 ``` """ diff --git a/scenes/deepnets.py b/scenes/deepnets.py index eabfc8a359bd14370f209c88359e6b6ea3323d96..ee02abfec23e3fc83c60e91f36d8f8ebf40508ee 100644 --- a/scenes/deepnets.py +++ b/scenes/deepnets.py @@ -1,17 +1,17 @@ """ + This module provides tools to easily interact with deep learning models. OTBTF is needed to use this module. --- -# Super-resolution +## Super-resolution The SR4RS model can be applied over any `scenes.core.Source` instance. We recall that this model is intended to be used over Sentinel-2 optical images. For example, here is how we perform the super-resolution of a Theia S2 product. -``` -#!python +``` py import scenes archive = "SENTINEL2B_..._T31TEJ_D_V1-8.zip" s2_scene = scenes.sentinel.Sentinel22AScene(archive) diff --git a/scenes/download.py b/scenes/download.py index 89f74e4d0ffb36547c9d6af605990421ff599b25..5bf9f3f2a9341e50330633b02c74c4680cd30e6a 100644 --- a/scenes/download.py +++ b/scenes/download.py @@ -14,45 +14,44 @@ password_theia = thisisnotmyrealpassword ``` To instantiate the `scenes.download.TheiaDownloader`: -``` -#!python +``` py import scenes cfg_file = "config.txt" # Theia config file theia = scenes.download.TheiaDownloader(cfg_file) ``` -# Bounding box + temporal range +## Bounding box + temporal range The following example shows how to use the `scenes.download.TheiaDownloader` to search or download all Sentinel-2 images in a bounding box within a temporal range. -## Search +### Search When the `download_dir` is not set, the download is not performed, and only the search results are returned from the function. -``` +``` py bbox = scenes.spatial.BoundingBox(43.706, 43.708, 4.317, 4.420) trange = ("01/01/2020", "O1/O1/2022") results = theia.download_in_range(bbox, trange, level="LEVEL2A") print(results) ``` -## Download +### Download To download the files, the `download_dir` must be specified: -``` +``` py theia.download_in_range(bbox, trange, "/tmp/download/", "LEVEL2A") ``` When files already exist, the md5sum is computed and compared with the one in the catalog, in order to determine if it has to be downloaded again. If the file is already downloaded and is complete according to the md5sum, its download is skipped. -# Bounding box + single date +## Bounding box + single date In the same manner, the downloader can search or download the closest images from a specific date. The returned dict from the function is updated with a "delta" key storing the value of the number of days from the specific date. -``` +``` py # For searching only results = theia.download_in_range(bbox, trange, "LEVEL2A") diff --git a/scenes/indexation.py b/scenes/indexation.py index c656e8495a70e06327d6733c7da6521c5c2f1908..ebfc76dace7a9dd360521883ac2af5eba5a750e1 100644 --- a/scenes/indexation.py +++ b/scenes/indexation.py @@ -7,37 +7,36 @@ The `scenes.indexation.Index` uses an R-Tree to perform the indexation of object Example: spatio-temporal indexation of multiple `scenes.core.Scene` instances. -# Build a spatio temporal index +## Build a spatio temporal index -``` -#!python +``` py import scenes scenes_list = [...] # a list of various `scenes.core.Scene` instances. index = scenes.indexation.Index(scenes_list) # build the index ``` -# Search from a `BoundingBox` +## Search from a `BoundingBox` The `find_indices()` method returns the indices of the indexed `scenes.core.Scene` instances matching the query. The `find()` method returns the indexed `scenes.core.Scene` instances matching the query. -``` +``` py bbox = BoundingBox(43.706, 43.708, 4.317, 4.420) indices_results = index.find_indices(bbox) # spatial query returning scenes indices scenes_results = index.find(bbox) # spatial query returning scenes instances ``` -# Spatio-temporal search +## Spatio-temporal search A date min and/or a date max can be added in the query. -``` +``` py scenes_results = index.find(bbox, "01/01/2020" "01/01/2022") ``` -# Search from a vector data file +## Search from a vector data file The search can also be performed with a vector data file. -``` +``` py vec = "/path/to/vector.gpkg" scenes_results = index.find(vec, "01/01/2020" "01/01/2022") ``` diff --git a/scenes/sentinel.py b/scenes/sentinel.py index 5e266cf730c7359e98f668ff9d280be249a80968..8194aab3bb04b6e0d9f8133646d19a0cb8433165 100644 --- a/scenes/sentinel.py +++ b/scenes/sentinel.py @@ -3,34 +3,115 @@ This module contains Sentinel-2 classes (sources and scenes). Right now everything is ready to use THEIA L2A and L3A products. -# Scene +## `Scene` based classes 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 `Sentinel2SceneBase` class. +the generic abstract `Sentinel2SceneBase` class, which itself inherits from `Scene`. + +``` mermaid +classDiagram + + Scene <|-- Sentinel2SceneBase + Sentinel2SceneBase <|-- Sentinel22AScene + Sentinel2SceneBase <|-- Sentinel23AScene + + class Scene{ + +datetime acquisition_date + +int epsg + +bbox_wgs84 + +get_metadata() + +__repr__() + } + + class Sentinel2SceneBase{ + +__init__(archive, tag) + +get_file() + +get_band() + +get_metadata() + +Sentinel2Source get_10m_bands() + +Sentinel2Source get_20m_bands() + + } + + class Sentinel22AScene{ + +__init__(archive) + +str clm_r1_msk_file + +str clm_r2_msk_file + +str edg_r1_msk_file + +str edg_r2_msk_file + +get_metadata() + } + + class Sentinel23AScene{ + +__init__(archive) + +str flg_r1_msk_file + +str flg_r2_msk_file + +get_metadata() + } +``` -## Instantiation +### Instantiation `Sentinel22AScene` and `Sentinel23AScene` are instantiated from the archive (.zip file) or the product folder. -``` -#!python +``` py 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") ``` -## Metadata +### Metadata The scene metadata can be accessed with the `get_metadata()` method, like any `scenes.core.Scene` instance. -``` +``` py md_dict = sc_2a.get_metadata() for key, value in md_dict.items(): print(f"{key}: {value}") ``` -## Sources +## `Source` based classes + +The `Sentinel22ASource` and `Sentinel23ASource` classes carry imagery sources +respectively for Sentinel-2 Level 2A and Level 3A products. They both inherit from +the generic `Sentinel2Source` class, which itself inherits from `Source`. + +``` mermaid +classDiagram + + Source <|-- Sentinel2Source + Sentinel2Source <|-- Sentinel22ASource + Sentinel2Source <|-- Sentinel23ASource + + class Source{ + +__init__(root_scene, out, parent=None) + +Scene root_scene + +Source parent + +Source drilled(msk_vec_file, nodata=0) + +Source cld_msk_drilled(nodata=0) + +Source resample_over(ref_img, interpolator="nn", nodata=0) + +Source clip_over_img(ref_img) + +Source clip_over_vec(ref_vec) + +Source new_source(*args) + } + + class Sentinel2Source{ + +R1_SIZE + +R2_SIZE + +Sentinel2Source msk_drilled(msk_dict, exp, nodata=0) + } + + class Sentinel22ASource{ + +Sentinel22ASource cld_msk_drilled(nodata=0) + } + + class Sentinel23ASource{ + +Sentinel23ASource flg_msk_drilled(keep_flags_values=(3, 4), nodata=0) + } +``` + +The sources carry the following images for Level 2 and Level 3 products: - 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 @@ -48,14 +129,14 @@ 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): -``` +``` py drilled = bands_10m_2a.cld_msk_drilled() ``` The `Sentinel23ASource` implements the `Sentinel23ASource.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: -``` +``` py drilled = bands_10m_3a.flg_msk_drilled(keep_flags_values=(4,)) ``` @@ -63,7 +144,7 @@ drilled = bands_10m_3a.flg_msk_drilled(keep_flags_values=(4,)) hence implemented sources transformations (e.g. `scenes.core.Source.masked`, `scenes.core.Source.clip_over_img`, `scenes.core.Source.resample_over`, `scenes.core.Source.reproject`, etc. -``` +``` py clipped = drilled.clip_over_img(roi) reprojected = clipped.reproject(epsg=4328) ``` @@ -71,11 +152,11 @@ Note that the resulting transformed `Sentinel22ASource` and `Sentinel23ASource` instances of `Sentinel22ASource` and `Sentinel23ASource` even after generic operations implemented in `scenes.core.Source`. -# Usage with pyotb +## 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. -``` +``` py rgb_nice = pyotb.DynamicConvert(reprojected) rgb_nice.write("image.tif", pixel_type="uint8") ``` diff --git a/scenes/spot.py b/scenes/spot.py index 427b366516316bce1a42bd4a6ab43a705baaebc3..bf964958f1eccea7d0ceb06d52b434c0f5003862 100644 --- a/scenes/spot.py +++ b/scenes/spot.py @@ -5,15 +5,41 @@ for Spot 6/7 products. --- -# Scene +# `Scene` based class The `Spot67Scene` class carries metadata and image sources for Spot-6/7 sensors. +``` mermaid +classDiagram + + Scene <|-- Spot67Scene + + class Scene{ + +datetime acquisition_date + +int epsg + +bbox_wgs84 + +get_metadata() + +__repr__() + } + + class Spot67Scene{ + +float azimuth_angle + +float azimuth_angle_across + +float azimuth_angle_along + +float incidence_angle + +float sun_azimuth_angle + +float sun_elev_angle + +get_metadata() + +Spot67Source get_xs() + +Spot67Source get_pan() + +Spot67Source get_pxs() + } +``` + ## Instantiation A `Spot67Scene` is instantiated from the .XML DIMAP files of PAN and XS: -``` -#!python +``` py import scenes sc = scenes.spot.Spot67Scene(dimap_file_xs="DIM_SPOT6_MS..._1.XML", dimap_file_pan="DIM_SPOT6_P..._1.XML") @@ -23,7 +49,7 @@ sc = scenes.spot.Spot67Scene(dimap_file_xs="DIM_SPOT6_MS..._1.XML", The scene metadata can be accessed with the `get_metadata()` method, like any `scenes.core.Scene` instance. -``` +``` py md_dict = sc.get_metadata() for key, value in md_dict.items(): print(f"{key}: {value}") @@ -31,14 +57,39 @@ for key, value in md_dict.items(): ## Sources +The `Spot67Source` is the class for the different Spot-6/7 sources. + +``` mermaid +classDiagram + + Source <|-- Spot67Source + + class Source{ + +__init__(root_scene, out, parent=None) + +Scene root_scene + +Source parent + +Source drilled(msk_vec_file, nodata=0) + +Source cld_msk_drilled(nodata=0) + +Source resample_over(ref_img, interpolator="nn", nodata=0) + +Source clip_over_img(ref_img) + +Source clip_over_vec(ref_vec) + +Source new_source(*args) + } + + class Spot67Source{ + +Spot67Source cld_msk_drilled(nodata=0) + } + +``` + ### Different sources -The `Spot67Scene` delivers three kind of `Spot67Source` instances: +The `Spot67Scene` delivers three `Spot67Source` instances: - The multispectral image (xs) - The Panchromatic image (pan) - The pansharpenend image (pxs) -``` +``` py xs = sc.get_xs() pan = sc.get_pan() pxs = sc.get_pxs(method="bayes") @@ -53,7 +104,7 @@ Three level of radiometry are available for Spot-6/7 images: - TOA (Top Of Atmosphere) - TOC (Top Of Canopy) -``` +``` py p_dn = sc.get_pan() xs_toa = sc.get_xs(reflectance="toa") pxs_toc = sc.get_pxs(reflectance="toc") @@ -64,14 +115,14 @@ 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 zero-valued pixels: -``` +``` py pxs_drilled = pxs.cld_msk_drilled() ``` The `Spot67Source` inherits from `scenes.core.Source`, hence implemented sources transformations (e.g. `scenes.core.Source.masked()`, `scenes.core.Source.clip_over_img()`, `scenes.core.Source.resample_over()`, `scenes.core.Source.reproject()`, etc. -``` +``` py clipped = pxs_drilled.clip_over_img(roi) reprojected = clipped.reproject(epsg=4328) ``` @@ -82,7 +133,7 @@ Note that the resulting transformed `Spot67Source` is still an instance of 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. -``` +``` py rgb_nice = pyotb.DynamicConvert(reprojected) rgb_nice.write("image.tif", pixel_type="uint8") ```