From ad8ed3717eb8c78f21199226084d7386e3d3ee49 Mon Sep 17 00:00:00 2001
From: Remi Cresson <remi.cresson@inrae.fr>
Date: Tue, 6 Jun 2023 19:45:36 +0200
Subject: [PATCH] Doc: fix reflectance

---
 doc/index.md | 188 +++++++++++++++------------------------------------
 1 file changed, 53 insertions(+), 135 deletions(-)

diff --git a/doc/index.md b/doc/index.md
index 7a16c5c..cdc584c 100644
--- a/doc/index.md
+++ b/doc/index.md
@@ -20,6 +20,9 @@ pip install https://gitlab.irstea.fr/umr-tetis/scenes/-/archive/develop/scenes-d
 
 `Scene` instances have generic methods and attributes.
 
+- **Imagery sources**: imagery sources can be accessed using `get_xxx()` 
+methods, where `xxx` is the name of the source to get. Any source can be used 
+in OTB, PyOTB pipelines, or with numpy directly.
 - **Metadata**: metadata access with the `get_metadata()` method.
 ``` py
 for key, value in sc.get_metadata():
@@ -37,29 +40,36 @@ Spot 6/7 scenes are instantiated from XS and PAN XML files of DIMAPS products.
 
 ``` py
 from scenes.spot import Spot67Scene
-sc = Spot67Scene(dimap_file_xs="/path/to/DIM_SPOT7_MS_..._1.XML",
-                 dimap_file_pan="/path/to/DIM_SPOT7_P_..._1.XML")
+sc = Spot67Scene(
+    dimap_file_xs="/path/to/DIM_SPOT7_MS_..._1.XML",
+    dimap_file_pan="/path/to/DIM_SPOT7_P_..._1.XML"
+)
 ```
 
 ### Image sources
 
-Spot-6/7 scenes have 3 images sources.
+Spot-6/7 scenes have 3 images sources: `xs`, `pan` and `pxs`.
 
 | Source | `Scene` method | Description                                                        |
 |--------|----------------|--------------------------------------------------------------------|
 | XS     | `get_xs()`     | 4 channels (Red, Green, Blue, Near Infrared), ~6m physical spacing | 
 | Pan    | `get_pan()`    | 1 channel (visible domain), ~1.5m physical spacing                 | 
-| PXS    | `get_pxs(method=...)` | 4 channels (same as XS), ~1.5m physical spacing             |  
+| PXS    | `get_pxs()`    | 4 channels (same as XS), ~1.5m physical spacing                    |  
 
 Each source can be computed in DN/TOA/TOC reflectance.
 
 ``` py
 xs_raw = sc.get_xs()  # DN
-xs_raw = sc.get_xs(reflectance="dn")  # DN
-xs_toa = sc.get_xs(reflectance="toa")  # TOA
-xs_toc = sc.get_xs(reflectance="toc")  # TOC
+xs_toa = sc.get_xs().reflectance(level="toa")  # TOA
+xs_toc = sc.get_xs().reflectance(level="toc")  # TOC
 ```
 
+!!! Warning
+
+    OTB version must be >= 8.1.0 to support optical calibration using source 
+    `reflectance()` method.
+    kwargs of `reflectance()` are the same as `pyotb.OpticalCalibration`.
+
 Each source can be masked with the cloud masks of the original product.
 The no-data value can be chosen.
 
@@ -67,21 +77,22 @@ The no-data value can be chosen.
 xs_toa_cld = xs_toa.cld_msk_drilled()  # (1)
 ```
 
-1. To change the no-data inside the clouds mask: `masked_src = src.cld_msk_drilled(nodata=-999)`
+1. To change the no-data inside the clouds mask: 
+`masked_src = src.cld_msk_drilled(nodata=-999)`
 
 ### Examples
 
 Computing NDVI from XS image in TOA reflectance:
 
 ``` py
-xs = toa.get_xs(reflectance="toa")       # (1)
+xs = toa.get_xs().reflectance(level="toa")  # (1)
 exp = "(im1b4-im1b1)/(im1b4+im1b1)"
-ndvi = pyotb.bandmath(exp=exp, il=[xs])  # (2)
+ndvi = pyotb.bandmath(exp=exp, il=[xs])     # (2)
 ndvi.write("ndvi.tif")
 ```
 
 1. `xs` is a `scenes.spot.Spot67Source` instance
-2. `ndvi` is a `pyotb.app` that inputs `xs`
+2. `ndvi` is a `pyotb.App` object that inputs `xs`
 
 The next example is a set of preprocessing operations on a Spot-6/7 XS image:
 
@@ -91,10 +102,10 @@ The next example is a set of preprocessing operations on a Spot-6/7 XS image:
 4. clip the result over a reference raster
 
 ``` py
-pxs = sc.get_pxs(reflectance="toa")        # (1)
-drilled = pxs.cld_msk_drilled()            # (2)
+pxs = sc.get_pxs().reflectance(level="toa")  # (1)
+drilled = pxs.cld_msk_drilled()              # (2)
 ref_img = "/tmp/S2A_2020...._FRE_10m.tif"
-subset = drilled.clip_over_img(ref_img)    # (3)
+subset = drilled.clip_over_img(ref_img)      # (3)
 subset.write("subset.tif")
 ```
 
@@ -112,14 +123,16 @@ Superimpose an image over a reference image.
 In the example below, `ref_img` is another `scenes.core.Source` instance.
 
 ``` py
-toa = sc.get_pxs(reflectance="toa")
+toa = sc.get_pxs().reflectance(level="toa")
 superimposed = toa.resample_over(ref_img)
 superimposed.write("superimposed.tif")
 ```
 
 ## Sentinel-2 
 
-Currently, Level-2 and Level-3 products from the [Theia land data center](https://www.theia-land.fr/en/product/sentinel-2-surface-reflectance/) are supported.
+Currently, Level-2 and Level-3 products from the 
+[Theia land data center](https://www.theia-land.fr/en/product/sentinel-2-surface-reflectance/) 
+and Level-2 products from Microsoft Planetary Computer are supported
 
 ### Instantiation
 
@@ -131,9 +144,16 @@ sc_level2 = Sentinel22AScene("/path/to/SENTINEL2A_..._V1-8/.zip")
 sc_level3 = Sentinel23AScene("/path/to/SENTINEL2X_...L3A_T31TEJ_D/.zip")
 ```
 
+Note that you can also instanciate uncomplete products, e.g. only 10m spacing 
+bands. In this case, a warning will be displayed.
+
 ### Sources
 
-Sentinel-2 images include 2 sources.
+Sentinel-2 images include the following sources:
+- single band, 10m spacing: `b4`, `b3`, `b2`, `b8`
+- single band, 20m spacing: `b5`, `b6`, `b7`, `b8a`, `b11`, `b12`
+- concatenated bands: `10m_bands`, `20m_bands`
+- for Theia products, quality masks: `clm_r1`, `clm_r2`, `edg_r1`, `edg_r2`
 
 | Source    | `Scene` method    | Description                                               |
 |-----------|-------------------|-----------------------------------------------------------|
@@ -149,7 +169,8 @@ src = sc_level2.get_10m_bands()
 masked_src = src.cld_msk_drilled()  # (1)
 ```
 
-1. To change the no-data inside the clouds mask: `masked_src = src.cld_msk_drilled(nodata=-999)`
+1. To change the no-data inside the clouds mask: 
+`masked_src = src.cld_msk_drilled(nodata=-999)`
 
 For Level 3 products, using the quality mask:
 
@@ -158,16 +179,21 @@ src = sc_level3.get_10m_bands()
 masked_src = src.flg_msk_drilled()  # (1)
 ```
 
-1. To change the no-data inside the clouds mask: `masked_src = src.flg_msk_drilled(nodata=-999)`.
-Also, the `keep_flags_values` can be used to select the pixels to keep from a list of values in the quality mask (land, water, snow, ...).
+1. To change the no-data inside the clouds mask: 
+`masked_src = src.flg_msk_drilled(nodata=-999)`.
+Also, the `keep_flags_values` can be used to select the pixels to keep from a 
+list of values in the quality mask (land, water, snow, ...).
 
 ## Spatial and temporal indexation
 
-Scenes includes a module to perform spatial and temporal indexation of `Scene` instances.
+Scenes includes a module to perform spatial and temporal indexation of `Scene` 
+instances.
 
 ### Query in space
 
-Perform a query in space (WGS84 bounding box) and time (optional) with an indexation structure.
+Perform a query in space (WGS84 bounding box) and time (optional) with an 
+indexation structure.
+
 ``` py
 from scenes import spot, indexation
 scs = spot.get_spot67_scenes(root_dir)  # (1)
@@ -184,127 +210,19 @@ for sc in matches:
 ### Query in space and time
 
 To perform searches in time:
+
 ``` py
 matches1 = idx.find(bbox_wgs84=bbox, "01/02/2019", "01-12-2020")  # (1)
 matches2 = idx.find(bbox_wgs84=bbox, "01/02/2019")                # (2)
 matches3 = idx.find(bbox_wgs84=bbox, date_max="01/02/2019")       # (3)
 ```
 
-1. A few date formats are supported: "dd/mm/yyyy", "dd-mm-yyyy", and "yyyy-mm-dd"
+1. A few date formats are supported: "dd/mm/yyyy", "dd-mm-yyyy", and 
+"yyyy-mm-dd"
 2. Here we don't have upper bound
-3. You can also specify only an upper bound, without lower bound (use the keywords `date_min` and `date_max`
-
-
-## Architecture
-
-### Scene class
-
-The scene class handles all the metadata and the image sources.
-
-
-``` mermaid
-classDiagram
-
-    Scene <|-- Spot67Scene
-    Scene <|-- Sentinel2SceneBase
-    Sentinel2SceneBase <|-- Sentinel22AScene
-    Sentinel2SceneBase <|-- Sentinel23AScene
-
-    Scene --*  Source: root_scene
-
-    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()
-
-    }
-    
-    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()  
-    }
-
-```
-
-### Source class
-
-The source stores the image pipeline that delivers the pixels.
+3. You can also specify only an upper bound, without lower bound (use the 
+keywords `date_min` and `date_max`
 
-``` mermaid
-classDiagram
-
-    Source <|-- Spot67Source
-    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 Spot67Source{
-        +Spot67Source cld_msk_drilled(nodata=0)
-    }
-    
-    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)
-    }
-
-```
 
 ### Implement a new sensor from scratch
 
-- 
GitLab