Download this notebook from github.


what is specific to TOPS SLC products?

Contrarily to GRD products SLC ones preserve the signal described in time per burst.

It means that a portion of SAR image can be seen up to 4 times by the sensor.

The different bursts are overlapping, and the subswaths are also overlapping.

SLC products also contains the phase information, and digital number in .tiff products are complex values.

How to read a TOPS SLC product with xsar ?

the TOPS (IW and EW) SLC products are distributed by ESA in .SAFE directories containing measurement and annotations per subswath and polarizations.

to open a multi dataset

[1]:
import xsar
import geoviews as gv
import holoviews as hv
import geoviews.feature as gf
hv.extension('bokeh')
path = xsar.get_test_file('S1A_IW_SLC__1SDV_20170907T102951_20170907T103021_018268_01EB76_Z020.SAFE')
sub_swath_IDs = xsar.Sentinel1Meta(path).subdatasets
sub_swath_IDs
[1]:
geometry
SENTINEL1_DS:/tmp/S1A_IW_SLC__1SDV_20170907T102951_20170907T103021_018268_01EB76_Z020.SAFE:IW1 POLYGON ((-67.47144 22.48036, -68.29402 22.627...
SENTINEL1_DS:/tmp/S1A_IW_SLC__1SDV_20170907T102951_20170907T103021_018268_01EB76_Z020.SAFE:IW2 POLYGON ((-68.28370 22.56667, -69.14890 22.716...
SENTINEL1_DS:/tmp/S1A_IW_SLC__1SDV_20170907T102951_20170907T103021_018268_01EB76_Z020.SAFE:IW3 POLYGON ((-69.13847 22.65528, -69.90347 22.784...
[2]:
multids = xsar.Sentinel1Meta(path)
#print(dir(multids.subdatasets))
for subswath in multids.subdatasets.index:
    print(subswath)
    onesubswath = xsar.Sentinel1Dataset(subswath)
    print(onesubswath.get_bursts_polygons()['geometry'])
SENTINEL1_DS:/tmp/S1A_IW_SLC__1SDV_20170907T102951_20170907T103021_018268_01EB76_Z020.SAFE:IW1
burst
0    POLYGON ((-67.51030 22.31627, -68.32788 22.462...
1    POLYGON ((-67.54546 22.15003, -68.36202 22.296...
2    POLYGON ((-67.58064 21.98355, -68.39618 22.130...
3    POLYGON ((-67.61584 21.81681, -68.43038 21.963...
4    POLYGON ((-67.65099 21.65019, -68.46453 21.797...
5    POLYGON ((-67.68614 21.48344, -68.49869 21.630...
6    POLYGON ((-67.71973 21.31678, -68.53187 21.463...
7    POLYGON ((-67.75480 21.15015, -68.56597 21.297...
8    POLYGON ((-67.78979 20.98376, -68.59997 21.131...
9    POLYGON ((-67.82474 20.81746, -68.63397 20.964...
Name: geometry, dtype: geometry
SENTINEL1_DS:/tmp/S1A_IW_SLC__1SDV_20170907T102951_20170907T103021_018268_01EB76_Z020.SAFE:IW2
burst
0    POLYGON ((-68.32084 22.40319, -69.18158 22.552...
1    POLYGON ((-68.35502 22.23689, -69.21468 22.386...
2    POLYGON ((-68.38922 22.07036, -69.24782 22.220...
3    POLYGON ((-68.42341 21.90382, -69.28095 22.053...
4    POLYGON ((-68.45756 21.73740, -69.31405 21.887...
5    POLYGON ((-68.49178 21.57049, -69.34724 21.720...
6    POLYGON ((-68.52465 21.40395, -69.37954 21.554...
7    POLYGON ((-68.55877 21.23740, -69.41263 21.388...
8    POLYGON ((-68.59282 21.07108, -69.44568 21.221...
9    POLYGON ((-68.62691 20.90451, -69.47877 21.055...
Name: geometry, dtype: geometry
SENTINEL1_DS:/tmp/S1A_IW_SLC__1SDV_20170907T102951_20170907T103021_018268_01EB76_Z020.SAFE:IW3
burst
0    POLYGON ((-69.17395 22.49225, -69.93513 22.620...
1    POLYGON ((-69.20712 22.32585, -69.96735 22.454...
2    POLYGON ((-69.24028 22.15942, -69.99957 22.288...
3    POLYGON ((-69.27343 21.99300, -70.03178 22.121...
4    POLYGON ((-69.30655 21.82670, -70.06397 21.955...
5    POLYGON ((-69.33978 21.65979, -70.09628 21.788...
6    POLYGON ((-69.37186 21.49329, -70.12790 21.622...
7    POLYGON ((-69.40496 21.32697, -70.16009 21.456...
8    POLYGON ((-69.43808 21.16053, -70.19231 21.290...
9    POLYGON ((-69.47121 20.99397, -70.22456 21.123...
Name: geometry, dtype: geometry
[3]:
#gv.tile_sources.Wikipedia*gv.Polygons(multids.bursts(only_valid_location=True)['geometry']).opts(width=800,height=450,alpha=0.5)
import pandas as pd
tmp = pd.concat([xsar.Sentinel1Dataset(onesubswath).get_bursts_polygons(only_valid_location=True) for onesubswath in multids.subdatasets.index])
gv.tile_sources.Wikipedia*gv.Polygons(tmp['geometry']).opts(width=800,height=450,alpha=0.5)

[3]:
[4]:
tmp2 = pd.concat([xsar.Sentinel1Dataset(onesubswath).get_bursts_polygons(only_valid_location=False) for onesubswath in multids.subdatasets.index])
gv.tile_sources.Wikipedia*gv.Polygons(tmp2['geometry']).opts(width=800,height=450,alpha=0.2)*gv.Polygons(tmp['geometry']).opts(width=800,height=450,alpha=0.2)

[4]:

to open a specific subswath

to open a subswath one can use xsar.Sentinel1Meta class to get an overview of the meta-data.

[5]:
s1meta = xsar.sentinel1_meta.Sentinel1Meta("SENTINEL1_DS:%s:IW3" % path)
s1meta
[5]:
<Sentinel1Meta single object>

to manipulate the data, the user have to open Sentinel1Dataset instance.

[6]:
s1ds = xsar.sentinel1_dataset.Sentinel1Dataset("SENTINEL1_DS:%s:IW3" % path)
s1ds
[6]:
full dataset coverage
SENTINEL1_DS:S1A_IW_SLC__1SDV_20170907T102951_20170907T103021_018268_01EB76_Z020.SAFE:IW3
pixel size 14.0 * 3.4 meters (line * sample)
coverage 187km * 79km (line * sample )

content of the Sentinel1Dataset python object

  • a datatree (xarray)

  • a dataset (xarray)

  • a s1meta object (class instance)

open the datatree

[7]:
s1ds.datatree
[7]:
<xarray.DatasetView>
Dimensions:  ()
Data variables:
    *empty*
Attributes: (12/17)
    name:              SENTINEL1_DS:/tmp/S1A_IW_SLC__1SDV_20170907T102951_201...
    short_name:        SENTINEL1_DS:S1A_IW_SLC__1SDV_20170907T102951_20170907...
    product:           SLC
    safe:              S1A_IW_SLC__1SDV_20170907T102951_20170907T103021_01826...
    swath:             IW
    multidataset:      False
    ...                ...
    footprint:         POLYGON ((-69.13847173718773 22.65528072350044, -69.90...
    coverage:          187km * 79km (line * sample )
    pixel_line_m:      <xarray.DataArray 'azimuthPixelSpacing' ()>\narray(13....
    pixel_sample_m:    <xarray.DataArray 'groundRangePixelSpacing' ()>\narray...
    orbit_pass:        Descending
    platform_heading:  -167.7216262988962

open dataset

[8]:
s1ds.dataset
[8]:
<xarray.Dataset>
Dimensions:         (pol: 2, line: 15070, sample: 23768)
Coordinates:
  * pol             (pol) object 'VV' 'VH'
  * line            (line) int64 0 1 2 3 4 5 ... 15065 15066 15067 15068 15069
  * sample          (sample) int64 0 1 2 3 4 5 ... 23763 23764 23765 23766 23767
Data variables:
    digital_number  (pol, line, sample) complex64 dask.array<chunksize=(1, 5000, 5000), meta=np.ndarray>
Attributes: (12/17)
    name:              SENTINEL1_DS:/tmp/S1A_IW_SLC__1SDV_20170907T102951_201...
    short_name:        SENTINEL1_DS:S1A_IW_SLC__1SDV_20170907T102951_20170907...
    product:           SLC
    safe:              S1A_IW_SLC__1SDV_20170907T102951_20170907T103021_01826...
    swath:             IW
    multidataset:      False
    ...                ...
    footprint:         POLYGON ((-69.13847173718773 22.65528072350044, -69.90...
    coverage:          187km * 79km (line * sample )
    pixel_line_m:      <xarray.DataArray 'azimuthPixelSpacing' ()>\narray(13....
    pixel_sample_m:    <xarray.DataArray 'groundRangePixelSpacing' ()>\narray...
    orbit_pass:        Descending
    platform_heading:  -167.7216262988962

get the s1meta

[ ]:

add high resolution interpolated variables

for instance longitudes, latitudes, incidence angle,…

Note that this step is automatically done by default for GRD products but not for SLC.

[9]:
s1ds.add_high_resolution_variables()
s1ds.dataset
[9]:
<xarray.Dataset>
Dimensions:               (pol: 2, line: 15070, sample: 23768)
Coordinates:
  * pol                   (pol) object 'VV' 'VH'
  * line                  (line) int64 0 1 2 3 4 ... 15066 15067 15068 15069
  * sample                (sample) int64 0 1 2 3 4 ... 23764 23765 23766 23767
    spatial_ref           int64 0
Data variables: (12/15)
    digital_number        (pol, line, sample) complex64 dask.array<chunksize=(1, 5000, 5000), meta=np.ndarray>
    time                  (line) datetime64[ns] 2017-09-07T10:29:53.785278 .....
    sampleSpacing         float64 2.33
    lineSpacing           float64 13.96
    ground_heading        (line, sample) float32 dask.array<chunksize=(3014, 2971), meta=np.ndarray>
    land_mask             (line, sample) int8 dask.array<chunksize=(3014, 2971), meta=np.ndarray>
    ...                    ...
    incidence             (line, sample) float64 dask.array<chunksize=(3014, 2971), meta=np.ndarray>
    elevation             (line, sample) float64 dask.array<chunksize=(3014, 2971), meta=np.ndarray>
    longitude             (line, sample) float64 dask.array<chunksize=(3014, 2971), meta=np.ndarray>
    latitude              (line, sample) float64 dask.array<chunksize=(3014, 2971), meta=np.ndarray>
    velocity              (line) float64 7.597e+03 7.597e+03 ... 7.597e+03
    range_ground_spacing  (sample) float64 dask.array<chunksize=(2971,), meta=np.ndarray>
Attributes: (12/17)
    name:              SENTINEL1_DS:/tmp/S1A_IW_SLC__1SDV_20170907T102951_201...
    short_name:        SENTINEL1_DS:S1A_IW_SLC__1SDV_20170907T102951_20170907...
    product:           SLC
    safe:              S1A_IW_SLC__1SDV_20170907T102951_20170907T103021_01826...
    swath:             IW
    multidataset:      False
    ...                ...
    footprint:         POLYGON ((-69.13847173718773 22.65528072350044, -69.90...
    coverage:          187km * 79km (line * sample )
    pixel_line_m:      <xarray.DataArray 'azimuthPixelSpacing' ()>\narray(13....
    pixel_sample_m:    <xarray.DataArray 'groundRangePixelSpacing' ()>\narray...
    orbit_pass:        Descending
    platform_heading:  -167.7216262988962

perform calibration and denoizing on sigma0, gamma0 and beta0

[10]:
s1ds.apply_calibration_and_denoising()
s1ds.dataset
[10]:
<xarray.Dataset>
Dimensions:               (pol: 2, line: 15070, sample: 23768)
Coordinates:
  * pol                   (pol) object 'VV' 'VH'
  * line                  (line) int64 0 1 2 3 4 ... 15066 15067 15068 15069
  * sample                (sample) int64 0 1 2 3 4 ... 23764 23765 23766 23767
    spatial_ref           int64 0
Data variables: (12/21)
    digital_number        (pol, line, sample) complex64 dask.array<chunksize=(1, 5000, 5000), meta=np.ndarray>
    time                  (line) datetime64[ns] 2017-09-07T10:29:53.785278 .....
    sampleSpacing         float64 2.33
    lineSpacing           float64 13.96
    ground_heading        (line, sample) float32 dask.array<chunksize=(3014, 2971), meta=np.ndarray>
    land_mask             (line, sample) int8 dask.array<chunksize=(3014, 2971), meta=np.ndarray>
    ...                    ...
    sigma0_raw            (pol, line, sample) float64 dask.array<chunksize=(1, 3014, 2971), meta=np.ndarray>
    nesz                  (pol, line, sample) float64 dask.array<chunksize=(1, 3014, 2971), meta=np.ndarray>
    gamma0_raw            (pol, line, sample) float64 dask.array<chunksize=(1, 3014, 2971), meta=np.ndarray>
    negz                  (pol, line, sample) float64 dask.array<chunksize=(1, 3014, 2971), meta=np.ndarray>
    sigma0                (pol, line, sample) float64 dask.array<chunksize=(1, 3014, 2971), meta=np.ndarray>
    gamma0                (pol, line, sample) float64 dask.array<chunksize=(1, 3014, 2971), meta=np.ndarray>
Attributes: (12/17)
    name:              SENTINEL1_DS:/tmp/S1A_IW_SLC__1SDV_20170907T102951_201...
    short_name:        SENTINEL1_DS:S1A_IW_SLC__1SDV_20170907T102951_20170907...
    product:           SLC
    safe:              S1A_IW_SLC__1SDV_20170907T102951_20170907T103021_01826...
    swath:             IW
    multidataset:      False
    ...                ...
    footprint:         POLYGON ((-69.13847173718773 22.65528072350044, -69.90...
    coverage:          187km * 79km (line * sample )
    pixel_line_m:      <xarray.DataArray 'azimuthPixelSpacing' ()>\narray(13....
    pixel_sample_m:    <xarray.DataArray 'groundRangePixelSpacing' ()>\narray...
    orbit_pass:        Descending
    platform_heading:  -167.7216262988962
[11]:
s1ds.dataset['ground_heading']
[11]:
<xarray.DataArray 'ground_heading' (line: 15070, sample: 23768)>
dask.array<ground_heading, shape=(15070, 23768), dtype=float32, chunksize=(3014, 2971), chunktype=numpy.ndarray>
Coordinates:
  * line         (line) int64 0 1 2 3 4 5 ... 15065 15066 15067 15068 15069
  * sample       (sample) int64 0 1 2 3 4 5 ... 23763 23764 23765 23766 23767
    spatial_ref  int64 0
Attributes:
    comment:  at ground level, computed from lon/lat in line direction

get azimuth time variable

the variable azimuth_time is given in annotations .xml files, it describes the date of acquisition of each pixel in the subswath. it is also called SAR “long time”, in opposition to the “short time” given by the slant_range_time variable

[12]:
aziHr = s1ds.dataset['time']
hv.Curve(aziHr.values)
[12]:

azimuth time variable estimated at the middle (in range) of the dataset selected is showing the bursts overlapping.

This variable is used to rasterize the 7 variables (longitude, latitude,…) described in the geolocationGrid at low resolution.

get the ground range spacing

One specificity of the SLC products is that the ground range spacing is not equal to the slant range spacing (it is the case in GRD products).

[13]:
#print(s1ds.s1meta.image['ground_pixel_spacing'])
#print(s1ds.s1meta.image['slant_pixel_spacing'])
print(s1ds.dataset['sampleSpacing'])
print(s1ds.dataset['lineSpacing'])
<xarray.DataArray 'sampleSpacing' ()>
array(2.329562)
Coordinates:
    spatial_ref  int64 0
Attributes:
    units:        m
    referential:  slant
<xarray.DataArray 'lineSpacing' ()>
array(13.95527)
Coordinates:
    spatial_ref  int64 0
Attributes:
    units:    m

The ground range spacing depends of the incidence angle.

\[grdRangeSpacing = \frac{slantRangeSpacing}{sinus(\theta)}\]

It is possible for the users to get the ground range spacing vector along the range axis.

[14]:
rgs = s1ds.dataset['range_ground_spacing']
rgs
[14]:
<xarray.DataArray 'range_ground_spacing' (sample: 23768)>
dask.array<true_divide, shape=(23768,), dtype=float64, chunksize=(2971,), chunktype=numpy.ndarray>
Coordinates:
  * sample       (sample) int64 0 1 2 3 4 5 ... 23763 23764 23765 23766 23767
    spatial_ref  int64 0
Attributes:
    history:  
[15]:
hv.Curve(rgs).opts(width=400,show_grid=True)

[15]:

get complex digital number

[16]:
s1ds.datatree['measurement']['digital_number']
[16]:
<xarray.DataArray 'digital_number' (pol: 2, line: 15070, sample: 23768)>
dask.array<concatenate, shape=(2, 15070, 23768), dtype=complex64, chunksize=(1, 5000, 5000), chunktype=numpy.ndarray>
Coordinates:
  * pol          (pol) object 'VV' 'VH'
  * line         (line) int64 0 1 2 3 4 5 ... 15065 15066 15067 15068 15069
  * sample       (sample) int64 0 1 2 3 4 5 ... 23763 23764 23765 23766 23767
    spatial_ref  int64 0
Attributes:
    comment:  denoised digital number, read at full resolution
    history:  digital_number: measurement/s1a-iw3-slc-v*-20170907t102953-2017...

equivalent to

[17]:
s1ds.dataset['digital_number']
[17]:
<xarray.DataArray 'digital_number' (pol: 2, line: 15070, sample: 23768)>
dask.array<concatenate, shape=(2, 15070, 23768), dtype=complex64, chunksize=(1, 5000, 5000), chunktype=numpy.ndarray>
Coordinates:
  * pol          (pol) object 'VV' 'VH'
  * line         (line) int64 0 1 2 3 4 5 ... 15065 15066 15067 15068 15069
  * sample       (sample) int64 0 1 2 3 4 5 ... 23763 23764 23765 23766 23767
    spatial_ref  int64 0
Attributes:
    comment:  denoised digital number, read at full resolution
    history:  digital_number: measurement/s1a-iw3-slc-v*-20170907t102953-2017...

additional informations