L2A GRS image#

Simple example to open and visualize L2A GRS image

import os, glob

import matplotlib
import matplotlib.pyplot as plt

import numpy as np
import xarray as xr
import rioxarray as rio
import cartopy.crs as ccrs

opj = os.path.join

Open and check#

file = '/data/satellite/Sentinel-2/L2A/31TFJ/2021/08/20/S2A_MSIL2Agrs_20210820T104021_N0500_R008_T31TFJ_20230122T133008/S2A_MSIL2Agrs_20210820T104021_N0500_R008_T31TFJ_20230122T133008.nc'
l2a = xr.open_dataset(file,decode_coords='all')
l2a
<xarray.Dataset>
Dimensions:             (wl: 11, y: 5490, x: 5490)
Coordinates:
  * wl                  (wl) int64 443 490 560 665 705 740 783 842 865 1610 2190
    time                datetime64[ns] ...
  * x                   (x) float64 6e+05 6e+05 6e+05 ... 7.098e+05 7.098e+05
  * y                   (y) float64 4.9e+06 4.9e+06 ... 4.79e+06 4.79e+06
    central_wavelength  (wl) float32 ...
    band                int64 ...
    spatial_ref         int64 ...
Data variables:
    Rrs                 (wl, y, x) float32 ...
    BRDFg               (y, x) float32 ...
    aot550              (y, x) float32 ...
    mask                (y, x) uint8 ...
    vza                 (y, x) float32 ...
    sza                 (y, x) float32 ...
    raa                 (y, x) float32 ...
    flags               (y, x) int64 ...
    dem                 (y, x) float32 ...
Attributes: (12/72)
    long_name:                           CA BLUE GREEN RED VRE_1 VRE_2 VRE_3 ...
    constellation:                       Sentinel-2
    constellation_id:                    S2
    product_path:                        /data/satellite/Sentinel-2/L1C/31TFJ...
    product_name:                        S2A_MSIL1C_20210820T104021_N0500_R00...
    product_filename:                    S2A_MSIL1C_20210820T104021_N0500_R00...
    ...                                  ...
    vis_swir_index_threshold:            0.0
    hcld_threshold:                      0.003
    dirdata:                             /data/grs/grsdata
    abs_gas_file:                        /home/harmel/Dropbox/Dropbox/work/gi...
    water_vapor_transmittance_file:      /home/harmel/Dropbox/Dropbox/work/gi...
    metadata_profile:                    datacube

Geoinformation and plotting#

You can get the projection information and then plot the RGB image as follows:

str_epsg = str(l2a.rio.crs)
zone = str_epsg[-2:]
is_south = str_epsg[2] == 7
proj = ccrs.UTM(zone, is_south)
plt.figure(figsize=(10,10))
l2a.Rrs.sel(wl=[665,560,490]).where(l2a.mask==0).plot.imshow(rgb='wl', robust=True,subplot_kws=dict(projection=proj))
<matplotlib.image.AxesImage at 0x7f5ae31521f0>
/home/harmel/anaconda3/envs/grstbx/lib/python3.9/site-packages/matplotlib/cm.py:496: RuntimeWarning: invalid value encountered in cast
  xx = (xx * 255).astype(np.uint8)
../_images/28135794bf30ab665ec03e4a01e2da9dec4c7c57c05f1623385006831fe39ac7.png

Reprojection#

If you want to reproject in EPSG:4326 Geodetic coordinate system for World (not recommended)

l2a_reproj = l2a.rio.reproject(4326)
l2a_reproj
<xarray.Dataset>
Dimensions:             (x: 6477, y: 4695, wl: 11)
Coordinates:
  * x                   (x) float64 4.232 4.232 4.233 ... 5.626 5.626 5.626
  * y                   (y) float64 44.25 44.25 44.25 ... 43.24 43.24 43.24
    central_wavelength  (wl) float32 442.7 492.7 559.9 ... 1.614e+03 2.202e+03
  * wl                  (wl) int64 443 490 560 665 705 740 783 842 865 1610 2190
    time                datetime64[ns] 2021-08-20T10:40:21
    band                int64 1
    spatial_ref         int64 0
Data variables:
    Rrs                 (wl, y, x) float32 nan nan nan nan ... nan nan nan nan
    BRDFg               (y, x) float32 nan nan nan nan nan ... nan nan nan nan
    aot550              (y, x) float32 nan nan nan nan nan ... nan nan nan nan
    mask                (y, x) uint8 255 255 255 255 255 ... 255 255 255 255 255
    vza                 (y, x) float32 nan nan nan nan nan ... nan nan nan nan
    sza                 (y, x) float32 nan nan nan nan nan ... nan nan nan nan
    raa                 (y, x) float32 nan nan nan nan nan ... nan nan nan nan
    flags               (y, x) int64 0 0 0 0 0 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0 0
    dem                 (y, x) float32 nan nan nan nan nan ... nan nan nan nan
Attributes: (12/72)
    long_name:                           CA BLUE GREEN RED VRE_1 VRE_2 VRE_3 ...
    constellation:                       Sentinel-2
    constellation_id:                    S2
    product_path:                        /data/satellite/Sentinel-2/L1C/31TFJ...
    product_name:                        S2A_MSIL1C_20210820T104021_N0500_R00...
    product_filename:                    S2A_MSIL1C_20210820T104021_N0500_R00...
    ...                                  ...
    vis_swir_index_threshold:            0.0
    hcld_threshold:                      0.003
    dirdata:                             /data/grs/grsdata
    abs_gas_file:                        /home/harmel/Dropbox/Dropbox/work/gi...
    water_vapor_transmittance_file:      /home/harmel/Dropbox/Dropbox/work/gi...
    metadata_profile:                    datacube

Subset with projected coordinates#

The easiest and fastest way to extract from lon/lat coordinates is to convert the lon/lat bounds into the projected coordinates in the geographic system of your image:

from pyproj import CRS, Transformer

lonmin,lonmax=4.92,5.3
latmin,latmax=43.6,43.35

in_crs = CRS.from_epsg(4326)
out_crs = CRS.from_epsg(l2a.rio.crs.to_epsg())

transform = Transformer.from_crs(in_crs, out_crs, always_xy=True)
minx,miny = transform.transform(lonmin, latmax)
maxx,maxy = transform.transform(lonmax, latmin)

l2a_subset = l2a.rio.clip_box(
    minx=minx,
    miny=miny,
    maxx=maxx,
    maxy=maxy)
l2a_subset
<xarray.Dataset>
Dimensions:             (wl: 11, x: 1502, y: 1428)
Coordinates:
  * wl                  (wl) int64 443 490 560 665 705 740 783 842 865 1610 2190
    time                datetime64[ns] 2021-08-20T10:40:21
  * x                   (x) float64 6.556e+05 6.556e+05 ... 6.856e+05 6.856e+05
  * y                   (y) float64 4.83e+06 4.83e+06 ... 4.801e+06 4.801e+06
    central_wavelength  (wl) float32 442.7 492.7 559.9 ... 1.614e+03 2.202e+03
    band                int64 1
    spatial_ref         int64 0
Data variables:
    Rrs                 (wl, y, x) float32 nan nan nan nan ... nan nan nan nan
    BRDFg               (y, x) float32 nan nan nan nan nan ... nan nan nan nan
    aot550              (y, x) float32 0.152 0.152 0.152 ... 0.172 0.172 0.172
    mask                (y, x) uint8 1 1 1 1 1 1 1 1 1 1 ... 1 1 1 1 1 1 1 1 1 1
    vza                 (y, x) float32 9.11 9.11 9.11 9.11 ... nan nan nan nan
    sza                 (y, x) float32 nan nan nan nan nan ... nan nan nan nan
    raa                 (y, x) float32 122.8 122.8 122.8 122.8 ... nan nan nan
    flags               (y, x) int64 512 512 512 512 512 ... 513 513 513 513 513
    dem                 (y, x) float32 46.0 46.0 45.0 45.0 ... 0.0 0.0 0.0 0.0
Attributes: (12/72)
    long_name:                           CA BLUE GREEN RED VRE_1 VRE_2 VRE_3 ...
    constellation:                       Sentinel-2
    constellation_id:                    S2
    product_path:                        /data/satellite/Sentinel-2/L1C/31TFJ...
    product_name:                        S2A_MSIL1C_20210820T104021_N0500_R00...
    product_filename:                    S2A_MSIL1C_20210820T104021_N0500_R00...
    ...                                  ...
    vis_swir_index_threshold:            0.0
    hcld_threshold:                      0.003
    dirdata:                             /data/grs/grsdata
    abs_gas_file:                        /home/harmel/Dropbox/Dropbox/work/gi...
    water_vapor_transmittance_file:      /home/harmel/Dropbox/Dropbox/work/gi...
    metadata_profile:                    datacube
plt.figure(figsize=(10,10))
l2a_subset.Rrs.sel(wl=[665,560,490]).where(l2a_subset.mask==0).plot.imshow(rgb='wl',
                                                                           robust=True,
                                                                           subplot_kws=dict(projection=proj))
<matplotlib.image.AxesImage at 0x7f5ae5eb80d0>
/home/harmel/anaconda3/envs/grstbx/lib/python3.9/site-packages/matplotlib/cm.py:496: RuntimeWarning: invalid value encountered in cast
  xx = (xx * 255).astype(np.uint8)
../_images/972f04353b968c0da2d20674162a66824168ffcaf5a9c58eac93923a272b355f.png

GRStbx#

Otherwise, I might be interested in using GRSTBX (Tristanovsk/grstbx) that provides the tools for subsettint and visualizing temporal datacubes

import grstbx
from grstbx import visual
import geopandas as gpd
import holoviews as hv
hv.extension('bokeh')
v=visual.ViewSpectral(l2a.Rrs,reproject=True)

The next cell will render the visualization tool. For subsetting, you can choose the polygon drawer tool on the right side and then press long to generate the polygon. Short clicks will add a new segment and long click end the polygon.

v.visu()

The next cell was commented for rendendering in Readthedocs (please uncomment for proper use of the notebook)

#geom_ = v.get_geom(v.aoi_stream,crs=l2a.rio.crs)
#geom_.to_file("roi_grs_visu_simple.json", driver="GeoJSON")
geom_ = gpd.read_file("roi_grs_visu_simple.json", driver="GeoJSON")
Rrs_clipped = l2a.Rrs.sel(wl=slice(400,1000)).where(l2a.mask==0).rio.clip(geom_.geometry.values)
plt.figure(figsize=(10,10))
Rrs_clipped.sel(wl=[665,560,490]).plot.imshow(rgb='wl',
                                              robust=True,
                                              subplot_kws=dict(projection=proj))
<matplotlib.image.AxesImage at 0x7f5c0a526070>
/home/harmel/anaconda3/envs/grstbx/lib/python3.9/site-packages/matplotlib/cm.py:496: RuntimeWarning: invalid value encountered in cast
  xx = (xx * 255).astype(np.uint8)
../_images/3982c2ba77cf50a6fb1b2eaaab5951b48f4c132ce99020a376c8f159bbf0de4d.png
Rrs_clipped.plot.imshow(col='wl',col_wrap=3, robust=True, cmap=plt.cm.Spectral_r,
                                              subplot_kws=dict(projection=proj))
<xarray.plot.facetgrid.FacetGrid at 0x7f5c23ac33d0>
../_images/a584811195d07e405c163360704261801b00e89451ca3c6df698a9a0037126ab.png