Metadata-Version: 2.4
Name: GRSdriver
Version: 2.0.3
Summary: Driver for L1C satellite images dedicated to GRS processor
Author-email: Tristan Harmel <tristan.harmel@gmail.com>
License-Expression: Apache-2.0
Project-URL: Documentation, https://grsdriver.readthedocs.io
Project-URL: Homepage, https://github.com/Tristanovsk/GRSdriver
Project-URL: Changelog, https://github.com/Tristanovsk/GRSdriver/blob/master/CHANGELOG.md
Keywords: Sentinel-2,Landsat-8,Landsat-9,driver,EO satellite
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Science/Research
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Scientific/Engineering :: GIS
Classifier: Topic :: Scientific/Engineering :: Atmospheric Science
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: importlib_resources
Requires-Dist: docopt
Requires-Dist: dask
Requires-Dist: xmltodict
Requires-Dist: rasterio
Requires-Dist: netCDF4
Requires-Dist: numba
Requires-Dist: numpy
Requires-Dist: scipy
Requires-Dist: pandas>=2.0.3
Requires-Dist: pyproj
Requires-Dist: rioxarray>=0.15.0
Requires-Dist: xarray
Requires-Dist: shapely
Requires-Dist: geopandas>=1.0
Provides-Extra: dev
Requires-Dist: pytest; extra == "dev"
Requires-Dist: build; extra == "dev"
Requires-Dist: twine; extra == "dev"
Provides-Extra: cli
Requires-Dist: matplotlib; extra == "cli"
Requires-Dist: cartopy; extra == "cli"
Provides-Extra: viz
Requires-Dist: matplotlib; extra == "viz"
Requires-Dist: holoviews; extra == "viz"
Requires-Dist: datashader; extra == "viz"
Requires-Dist: bokeh; extra == "viz"
Requires-Dist: colorcet; extra == "viz"
Requires-Dist: panel; extra == "viz"
Provides-Extra: tutorials
Requires-Dist: jupyterlab; extra == "tutorials"
Requires-Dist: matplotlib; extra == "tutorials"
Requires-Dist: cartopy; extra == "tutorials"
Requires-Dist: requests; extra == "tutorials"
Dynamic: license-file

<img src="https://raw.githubusercontent.com/Tristanovsk/GRSdriver/master/docs/source/_static/GRS_driver.png" alt="GRSdriver_icon" width="100"/>

# GRSdriver package
## Driver dedicated to load and prepare data for the GRS processor

[![PyPI](https://img.shields.io/pypi/v/GRSdriver)](https://pypi.org/project/GRSdriver/)
[![Python](https://img.shields.io/pypi/pyversions/GRSdriver)](https://pypi.org/project/GRSdriver/)
[![tests](https://github.com/Tristanovsk/GRSdriver/actions/workflows/tests.yml/badge.svg)](https://github.com/Tristanovsk/GRSdriver/actions/workflows/tests.yml)
[![Documentation](https://readthedocs.org/projects/grsdriver/badge/?version=latest)](https://grsdriver.readthedocs.io)
[![License](https://img.shields.io/pypi/l/GRSdriver)](https://github.com/Tristanovsk/GRSdriver/blob/master/LICENSE)

GRSdriver loads a Level-1 satellite image into a single `xarray.Dataset` ready for the
[GRS](https://github.com/CNES/GRSprocessor) atmospheric and sunglint correction:

- Sentinel-2 L1C (`.SAFE`): top-of-atmosphere reflectance of the selected bands on a common grid,
  with accurate viewing angles computed **per band and per detector** from the granule metadata
- Landsat 8 and 9 Collection-2 L1: reflectance, brightness temperature of the thermal bands,
  sun/viewing angles and L1 quality flags
- spectral response functions and central wavelengths of each band
- spatial subsetting, parallel reading, lazy (dask) loading and low memory options for full tiles
- visualization tools

Full documentation: https://grsdriver.readthedocs.io

## What's new in 2.0

- **pip-only install**: EOReader and the GDAL Python bindings are no longer needed.
- **~2x faster**: full Sentinel-2 tile at 20 m in ~30 s (bands read and angles computed in
  parallel), fully lazy dask mode in ~20 s.
- **Fixes**: Sentinel-2 angles of processing baselines < 4.0, alignment of the Landsat
  panchromatic band, size of the `SpatioTemp` boxes.
- **Upgrading from 1.x**: the drivers no longer take `**kwargs` (they were passed to EOReader) and
  have no `.reader` attribute; a `subset` given as a sequence is a bounding box
  `(minx, miny, maxx, maxy)` in the product CRS. See the
  [CHANGELOG](https://github.com/Tristanovsk/GRSdriver/blob/master/CHANGELOG.md).

## Installation

Python 3.9 to 3.13:

```commandline
pip install GRSdriver
```

No separate GDAL installation is needed: rasterio and geopandas ship with their own GDAL
(including the JPEG2000 driver for Sentinel-2). Optional extras:

| Extra | For | Install |
|---|---|---|
| `cli` | `grs_driver` command line (RGB quicklooks) | `pip install "GRSdriver[cli]"` |
| `viz` | interactive visualization tools (`GRSdriver.visual`) | `pip install "GRSdriver[viz]"` |
| `tutorials` | running the tutorial notebooks | `pip install "GRSdriver[tutorials]"` |

### From the repository

To run the tutorials or work on the code:
```commandline
git clone https://github.com/Tristanovsk/GRSdriver.git
cd GRSdriver
pip install -e ".[cli,viz,tutorials,dev]"
```
or with conda: `conda env create -f environment.yml && conda activate grsdriver`.

## Quick start

```python
from GRSdriver import Sentinel2Driver, LandsatDriver

# Sentinel-2: 20 m bands, viewing angles computed on a 60 m grid
l1c = Sentinel2Driver('S2B_MSIL1C_..._.SAFE', resolution=20)
l1c.load_product()
prod = l1c.prod          # bands, vza, raa, sza, SRF, wl_true + metadata

# Landsat 8/9
l1 = LandsatDriver('LC09_L1TP_..._02_T1', resolution=30)
l1.load_product()

# region of interest: any geometry (GeoSeries, any CRS), here a lon/lat box
import geopandas as gpd
from shapely.geometry import box
roi = gpd.GeoSeries([box(5.0, 43.38, 5.25, 43.58)], crs=4326)
l1c = Sentinel2Driver('S2B_MSIL1C_..._.SAFE', resolution=20, subset=roi)
```

The bands are read by 4 threads in parallel: set `n_workers` (e.g. `n_workers=1`) to trade speed
for memory. For a full Sentinel-2 tile at 10 m, `Sentinel2Driver(..., lazy_geom=True)` keeps the
per-band viewing angles lazy and saves several GB of memory. Set `GRSDRIVER_USE_DASK=1` before
importing GRSdriver to get a fully lazy (dask-backed) product.

### Command line

`grs_driver` writes an RGB quicklook (PNG) of a Sentinel-2 or Landsat product:
```commandline
grs_driver S2B_MSIL1C_..._.SAFE --odir ./quicklooks --resolution 60
```

## Tutorials

The notebooks are in [`docs/source/tutorials/`](https://github.com/Tristanovsk/GRSdriver/tree/master/docs/source/tutorials) and rendered with their outputs in the
[documentation](https://grsdriver.readthedocs.io):

| Notebook | Content |
|---|---|
| `GRSdriver_basics` | load a Sentinel-2 L1C image, geometry, RGB, NDWI water mask |
| `GRSdriver_subsetting` | subset a time series of images and build a datacube |
| `GRSdriver_landsat9_TIR` | Landsat 9 with its thermal bands |
| `GRSdriver_landsat9_L1_CAMS` | Landsat 9 with CAMS atmospheric data |
| `sentinel2_download` | download Sentinel-2 L1C images |

The paths to the images are those of the author's machine: set them to your own data. See
[Updating the tutorials](https://grsdriver.readthedocs.io/en/latest/contributing.html) to rerun
them for the documentation.

## Sentinel-2 viewing geometry

The MSI swath is covered by 12 staggered detectors: odd and even detectors look in opposite
directions, and the bands of a detector are shifted in the focal plane. Each band therefore has
its own viewing angles, with jumps at the detector borders. GRSdriver computes them per band and
per detector from the L1C metadata (see
[Methods](https://grsdriver.readthedocs.io/en/latest/methods.html)).

![RGB image](https://raw.githubusercontent.com/Tristanovsk/GRSdriver/master/docs/source/_static/s2_rgb.png)

Over water, sunglint is so directional that its reflectance changes abruptly at the detector
borders: here +23 % in B11 (1610 nm) between detectors 10 and 11 (right: viewing azimuth). These
stripes are a property of the target, not an artifact, and their size depends on the sea
roughness, i.e. on the wind speed
([Fougnie et al., 2016](https://doi.org/10.1117/12.2237597);
[Harmel et al., 2018](https://doi.org/10.1016/j.rse.2017.10.022)):

![sunglint at detector borders](https://raw.githubusercontent.com/Tristanovsk/GRSdriver/master/docs/source/_static/s2_sunglint_detectors.png)

Viewing azimuth of each band computed by GRSdriver:

![viewing azimuth per band](https://raw.githubusercontent.com/Tristanovsk/GRSdriver/master/docs/source/_static/s2_view_azimuth_bands.png)

![across-track profile](https://raw.githubusercontent.com/Tristanovsk/GRSdriver/master/docs/source/_static/s2_detector_profile.png)

## Example

![example gif](https://raw.githubusercontent.com/Tristanovsk/GRSdriver/master/docs/source/_static/GRSdriver_visual_tool_optimized.gif)

## 2D-fitting method for angle computation

![example files](https://raw.githubusercontent.com/Tristanovsk/GRSdriver/master/docs/source/_static/example_2D_fitting_one_band_v3.png)
![example files](https://raw.githubusercontent.com/Tristanovsk/GRSdriver/master/docs/source/_static/example_scattering_angle_all_bands.png)
![example files](https://raw.githubusercontent.com/Tristanovsk/GRSdriver/master/docs/source/_static/example_reflectance_all_bands.png)

## Example of water masking

![example files](https://raw.githubusercontent.com/Tristanovsk/GRSdriver/master/docs/source/_static/example_ndwi_mask.png)

## Changes and contributing

See the [CHANGELOG](https://github.com/Tristanovsk/GRSdriver/blob/master/CHANGELOG.md). Tests run
with `pytest tests` (the tests that need satellite products are skipped when they are not
available); the [contributing guide](https://grsdriver.readthedocs.io/en/latest/contributing.html)
explains how to update the tutorials and make a release.
