SAR example#

Let’s use EOReader with SAR data.

Warning: SAR data is processed with SNAP, so be sure to have it installed and that GPT is in your path.

Imports#

import os
import logging
import matplotlib.pyplot as plt

# EOReader
from eoreader.reader import Reader
from eoreader.bands import VV, HH, VV_DSPK, HH_DSPK, HILLSHADE, SLOPE, to_str
from eoreader.env_vars import DEM_PATH

Create logger#

# Create logger

logger = logging.getLogger("eoreader")
logger.setLevel(logging.INFO)

# create console handler and set level to debug
ch = logging.StreamHandler()
ch.setLevel(logging.INFO)

# create formatter
formatter = logging.Formatter('%(message)s')

# add formatter to ch
ch.setFormatter(formatter)

# add ch to logger
logger.addHandler(ch)

Open the COSMO-SkyMed product#

Please be aware that:

  • EOReader will orthorectify your SAR data to get UTM tiles.

  • complex data is not handled as is, EOReader will convert them to ground range.

# First of all, we need some VHR data, let's use some COSMO-SkyMed data
path = os.path.join("/home", "data", "DATA", "PRODS", "COSMO", "1st_GEN", "1001512-735097")

# Open your product
prod = Reader().open(path, remove_tmp=True)
prod
eoreader.CskProduct 'CSKS4_DGM_B_HI_09_HH_RA_FF_20201008224018_20201008224025'
Attributes:
	condensed_name: 20201008T224018_CSK_HH_HI_DGM
	path: /home/data/DATA/PRODS/COSMO/1st_GEN/1001512-735097
	constellation: COSMO-SkyMed
	sensor type: SAR
	product type: DGM
	default resolution: 5.0
	acquisition datetime: 2020-10-08T22:40:18.446381
	band mapping:
		HH: HH
		HH_DSPK: HH_DSPK
	needs extraction: True
	orbit direction: ASCENDING
# Plot the quicklook
prod.plot()
/opt/conda/lib/python3.10/site-packages/rasterio/__init__.py:277: NotGeoreferencedWarning: Dataset has no geotransform, gcps, or rpcs. The identity matrix will be returned.
  dataset = DatasetReader(path, driver=driver, sharing=sharing, **kwargs)
/opt/conda/lib/python3.10/site-packages/rioxarray/_io.py:924: NotGeoreferencedWarning: Dataset has no geotransform, gcps, or rpcs. The identity matrix will be returned.
  warnings.warn(str(rio_warning.message), type(rio_warning.message))  # type: ignore
/opt/conda/lib/python3.10/site-packages/rasterio/__init__.py:287: NotGeoreferencedWarning: The given matrix is equal to Affine.identity or its flipped counterpart. GDAL may ignore this matrix and save no geotransform without raising an error. This behavior is somewhat driver-specific.
  dataset = writer(
Warning 1: /tmp/tmp76e5z02s/tmp_20201008T224018_CSK_HH_HI_DGM/20201008T224018_CSK_HH_HI_DGM_QLK.tif: Metadata exceeding 32000 bytes cannot be written into GeoTIFF. Transferred to PAM instead.
../_images/c4dda7dee0320a1a07321d3e12d00929f53d1333adadba52d83382fb420d0a36.png
# Get the band information
prod.bands
eoreader.SarBand 'HH'
Attributes:
	id: HH
	eoreader_name: HH
	gsd (m): 5.0
	asset_role: intensity
eoreader.SarBand 'HH_DSPK'
Attributes:
	id: HH_DSPK
	eoreader_name: HH_DSPK
	gsd (m): 5.0
	asset_role: intensity
# Print some data
print(f"Acquisition datetime: {prod.datetime}")
print(f"Condensed name: {prod.condensed_name}")

# Open here some more interesting geographical data: extent and footprint
extent = prod.extent()
footprint = prod.footprint()

base = extent.plot(color='cyan', edgecolor='black')
footprint.plot(ax=base, color='blue', edgecolor='black', alpha=0.5)
Acquisition datetime: 2020-10-08 22:40:18.446381
Condensed name: 20201008T224018_CSK_HH_HI_DGM
Executing processing graph
....10%....20%....30%....40%....50%....60%....70%....80%....90% done.
/opt/conda/lib/python3.10/site-packages/rasterio/__init__.py:277: NotGeoreferencedWarning: Dataset has no geotransform, gcps, or rpcs. The identity matrix will be returned.
  dataset = DatasetReader(path, driver=driver, sharing=sharing, **kwargs)
<AxesSubplot: >
../_images/cf749ad3a44988a2369c688e5109ad83b1fce89e0e6aa5b58f632a46fec419ad.png

For SAR data, the footprint needs the orthorectified data ! For that, SNAP uses its own DEM, but you can change it when positionning the EOREADER_SNAP_DEM_NAME environment variable.

Available DEMs are:

  • ACE2_5Min

  • ACE30

  • ASTER 1sec GDEM

  • Copernicus 30m Global DEM (by default)

  • Copernicus 90m Global DEM

  • GETASSE30

  • SRTM 1Sec HGT

  • SRTM 3Sec

  • External DEM

For example:

import os
from eoreader.env_vars import SNAP_DEM_NAME
os.environ[SNAP_DEM_NAME] = "GETASSE30"

Warning: If External DEM is set, you must specify the DEM you want by positioning the EOREADER_DEM_PATH to a DEM that can be read by SNAP.

Load bands#

# Set the DEM
os.environ[DEM_PATH] = os.path.join("/home", "data", "DS2", "BASES_DE_DONNEES", "GLOBAL", "COPDEM_30m", "COPDEM_30m.vrt")

# Select some bands you wish to load without knowing if they exist
bands = [VV, HH, VV_DSPK, HH_DSPK, HILLSHADE, SLOPE]

# Only keep those selected
ok_bands = [band for band in bands if prod.has_band(band)]

# This product does not have VV band and HILLSHADE band cannot be computed from SAR band
print(to_str(ok_bands))
['HH', 'HH_DSPK', 'SLOPE']
# Load those bands as a dict of xarray.DataArray, with a 20m resolution
band_dict = prod.load(ok_bands, resolution=20.)
band_dict[HH]
Executing processing graph
first_line_time metadata value is null
last_line_time metadata value is null
...10%...21%...32%...43%...54%...65%...76%...87%. done.
<xarray.DataArray 'HH' (band: 1, y: 2473, x: 2688)>
array([[[nan, nan, nan, ..., nan, nan, nan],
        [nan, nan, nan, ..., nan, nan, nan],
        [nan, nan, nan, ..., nan, nan, nan],
        ...,
        [nan, nan, nan, ..., nan, nan, nan],
        [nan, nan, nan, ..., nan, nan, nan],
        [nan, nan, nan, ..., nan, nan, nan]]], dtype=float32)
Coordinates:
  * x            (x) float64 2.058e+05 2.059e+05 ... 2.596e+05 2.596e+05
  * y            (y) float64 1.746e+06 1.746e+06 ... 1.697e+06 1.697e+06
  * band         (band) int64 1
    spatial_ref  int64 0
Attributes:
    AREA_OR_POINT:             Area
    bands:                     1
    Band_1:                    Sigma0_HH_db
    band_names:                Sigma0_HH_db 
    byte_order:                1
    coordinate_system_string:  PROJCS["WGS 84 / UTM zone 49N", GEOGCS["WGS 84...
    data_gain_values:          1.0
    data_offset_values:        0.0
    data_type:                 4
    description:               HIMAGE - Unit: intensity_db
    file_type:                 ENVI Standard
    header_offset:             0
    interleave:                bsq
    lines:                     9893
    long_name:                 HH
    samples:                   10752
    scale_factor:              1.0
    add_offset:                0.0
    constellation:             COSMO-SkyMed
    constellation_id:          CSK
    product_path:              /home/data/DATA/PRODS/COSMO/1st_GEN/1001512-73...
    product_name:              CSKS4_DGM_B_HI_09_HH_RA_FF_20201008224018_2020...
    product_filename:          1001512-735097
    instrument:                SAR-2000
    product_type:              DGM
    acquisition_date:          20201008T224018
    condensed_name:            20201008T224018_CSK_HH_HI_DGM
    orbit_direction:           ASCENDING
Some SAR band (i.e. COSMO) may contain null pixels that are not really nodata (but very low values like water).

This can lead the Terrain Correction step to create large nodata area when projecting on a DEM.

If it happens, you can set the keyword SAR_INTERP_NA to True when loading or stacking SAR data to fill these area with interpolated data.

from eoreader.keywords import SAR_INTERP_NA
band_dict = prod.load(
    ok_bands, 
    resolution=20., 
    **{SAR_INTERP_NA: True}
)
# Plot a subsampled version
band_dict[SLOPE][:, ::10, ::10].plot()
<matplotlib.collections.QuadMesh at 0x7ff8763bbfa0>
../_images/1df0a4002a307b7401c9a6b6b38b3713d2f65ee5818b458ad3c14d797653fd1f.png

Stack some data#

# You can also stack those bands
stack = prod.stack(ok_bands)
stack
<xarray.DataArray 'HH_HH_DSPK_SLOPE' (z: 3, y: 9893, x: 10752)>
array([[[        nan,         nan,         nan, ...,         nan,
                 nan,         nan],
        [        nan,         nan,         nan, ...,         nan,
                 nan,         nan],
        [        nan,         nan,         nan, ...,         nan,
                 nan,         nan],
        ...,
        [        nan,         nan,         nan, ...,         nan,
                 nan,         nan],
        [        nan,         nan,         nan, ...,         nan,
                 nan,         nan],
        [        nan,         nan,         nan, ...,         nan,
                 nan,         nan]],

       [[        nan,         nan,         nan, ...,         nan,
                 nan,         nan],
        [        nan,         nan,         nan, ...,         nan,
                 nan,         nan],
        [        nan,         nan,         nan, ...,         nan,
                 nan,         nan],
...
        [        nan,         nan,         nan, ...,         nan,
                 nan,         nan],
        [        nan,         nan,         nan, ...,         nan,
                 nan,         nan],
        [        nan,         nan,         nan, ...,         nan,
                 nan,         nan]],

       [[ 0.23764595,  0.23764595,  0.23764595, ...,  0.        ,
          0.        ,  0.        ],
        [ 0.23764595,  0.23764595,  0.23764595, ...,  0.        ,
          0.        ,  0.        ],
        [ 0.23764595,  0.23764595,  0.23764595, ...,  0.        ,
          0.        ,  0.        ],
        ...,
        [16.46719   , 16.46719   , 16.46719   , ...,  0.08529702,
          0.08529702,  0.08529702],
        [16.46719   , 16.46719   , 16.46719   , ...,  0.08529702,
          0.08529702,  0.08529702],
        [16.81581   , 16.81581   , 16.81581   , ...,  0.0906441 ,
          0.0906441 ,  0.0906441 ]]], dtype=float32)
Coordinates:
  * x            (x) float64 2.058e+05 2.058e+05 ... 2.596e+05 2.596e+05
  * y            (y) float64 1.746e+06 1.746e+06 ... 1.697e+06 1.697e+06
    spatial_ref  int64 0
  * z            (z) object MultiIndex
  * variable     (z) object 'HH' 'HH_DSPK' 'SLOPE'
  * band         (z) int64 1 1 1
Attributes:
    long_name:         HH HH_DSPK SLOPE
    constellation:     COSMO-SkyMed
    constellation_id:  CSK
    product_path:      /home/data/DATA/PRODS/COSMO/1st_GEN/1001512-735097
    product_name:      CSKS4_DGM_B_HI_09_HH_RA_FF_20201008224018_20201008224025
    product_filename:  1001512-735097
    instrument:        SAR-2000
    product_type:      DGM
    acquisition_date:  20201008T224018
    condensed_name:    20201008T224018_CSK_HH_HI_DGM
    orbit_direction:   ASCENDING
# Plot a subsampled version
nrows = len(stack)
fig, axes = plt.subplots(nrows=nrows, figsize=(3 * nrows, 6 * nrows), subplot_kw={"box_aspect": 1})

for i in range(nrows):
    stack[i, ::10, ::10].plot(x="x", y="y", ax=axes[i])
../_images/6f934cc02880a5a8f410ef1435321767301e65a6e632306bd3a14c6872b14771.png