Download this notebook from github.


XSAR example

open a dataset with xsar.open_dataset

[1]:
import xsar
import os
import numpy as np

[2]:
# get test file. You can replace with an path to other SAFE
filename = xsar.get_test_file('S1A_IW_GRDH_1SDV_20170907T103020_20170907T103045_018268_01EB76_Z010.SAFE')

Open a dataset with a xsar.Sentinel1Meta object

A xsar.Sentinel1Meta object handles all attributes and methods that can’t be embdeded in a xarray.Dataset object. It can also replace a filename in xarray.open_dataset

[3]:
sar_meta = xsar.Sentinel1Meta(filename)
sar_meta
[3]:
<Sentinel1Meta single object>

If holoviews extension is loaded, the <Sentinel1Meta objet> have a nice representation. (matplolib is also a valid extension)

[4]:
import holoviews as hv
hv.extension('bokeh')
sar_meta
[4]:
<Sentinel1Meta single object>

sar_meta object is an xsar.Sentinel1Meta object that can be given to xarray.open_dataset or xsar.Sentinel1Dataset , as if it was a filename:

[5]:
sar_ds = xsar.Sentinel1Dataset(sar_meta)
sar_ds
[5]:
full dataset coverage
SENTINEL1_DS:S1A_IW_GRDH_1SDV_20170907T103020_20170907T103045_018268_01EB76_Z010.SAFE:IW
pixel size 10.0 * 10.0 meters (line * sample)
coverage 170km * 251km (line * sample )

Open a dataset at lower resolution

resolution keyword can be used to open a dataset at lower resolution.

It might be:

  • a dict {'line': 20, 'sample': 20}: 20*20 pixels. so if sensor resolution is 10m, the final resolution will be 100m

  • a string like '200m': Sensor resolution will be automatically used to compute the window size

Then we can instantiate a xsar.Sentinel1Dataset, with the given resolution. Note that the above pixel size has changed.

[6]:
sar_ds = xsar.Sentinel1Dataset(sar_meta, resolution='200m')
sar_ds
[6]:
full dataset coverage
SENTINEL1_DS:S1A_IW_GRDH_1SDV_20170907T103020_20170907T103045_018268_01EB76_Z010.SAFE:IW
pixel size 200.0 * 200.0 meters (line * sample)
coverage 170km * 251km (line * sample )

Extract a sub image of 10*10km around a lon/lat point

Convert (lon,lat) to (line, sample)

we can use sar_meta.ll2coords to convert (lon,lat) to (line, sample):

[7]:
# from a shapely object
point_lonlat =  sar_meta.footprint.centroid
point_coords = sar_meta.ll2coords(point_lonlat.x, point_lonlat.y)
point_coords
[7]:
(8421.566059451885, 12589.902956209495)

The result is floating, because it is the position inside the pixel. If real indexes from existing dataset is needed, you’ll have to use sar_ds.ll2coords Result will be the nearest (line, sample) in the dataset

[8]:
point_coords = sar_ds.ll2coords(point_lonlat.x, point_lonlat.y)
point_coords
[8]:
(8429.5, 12589.5)

Extract the sub-image

[9]:
box_size = 10000 # 10km
dist = {'line' : int(np.round(box_size / 2 / sar_meta.pixel_line_m)), 'sample': int(np.round(box_size / 2 / sar_meta.pixel_sample_m))}
dist
[9]:
{'line': 500, 'sample': 500}

The xarray/dask dataset is available as a property : sar_ds.dataset. This attribute can be set to a new values, so the attributes like pixel spacing and coverage are correctly recomputed:

[10]:
# select 10*10 km around point_coords
sar_ds.dataset = sar_ds.dataset.sel(line=slice(point_coords[0] - dist['line'], point_coords[0] + dist['line']), sample=slice(point_coords[1] - dist['sample'], point_coords[1] + dist['sample']))
sar_ds
[10]:
dataset slice
SENTINEL1_DS:S1A_IW_GRDH_1SDV_20170907T103020_20170907T103045_018268_01EB76_Z010.SAFE:IW
pixel size 200.0 * 200.0 meters (line * sample)
coverage 170km * 251km (line * sample )
[11]:
sar_ds.dataset
[11]:
<xarray.Dataset>
Dimensions:               (pol: 2, line: 838, sample: 1259)
Coordinates:
  * pol                   (pol) object 'VV' 'VH'
  * line                  (line) float64 9.5 29.5 49.5 ... 1.673e+04 1.675e+04
  * sample                (sample) float64 9.5 29.5 49.5 ... 2.515e+04 2.517e+04
    spatial_ref           int64 0
Data variables: (12/21)
    digital_number        (pol, line, sample) uint16 dask.array<chunksize=(1, 838, 1259), meta=np.ndarray>
    time                  (line) timedelta64[ns] 17416 days 10:30:20.95128985...
    sampleSpacing         float64 200.0
    lineSpacing           float64 200.0
    ground_heading        (line, sample) float32 dask.array<chunksize=(838, 1259), meta=np.ndarray>
    land_mask             (line, sample) int8 dask.array<chunksize=(838, 1259), meta=np.ndarray>
    ...                    ...
    sigma0_raw            (pol, line, sample) float64 dask.array<chunksize=(1, 838, 1259), meta=np.ndarray>
    nesz                  (pol, line, sample) float64 dask.array<chunksize=(1, 838, 1259), meta=np.ndarray>
    gamma0_raw            (pol, line, sample) float64 dask.array<chunksize=(1, 838, 1259), meta=np.ndarray>
    negz                  (pol, line, sample) float64 dask.array<chunksize=(1, 838, 1259), meta=np.ndarray>
    sigma0                (pol, line, sample) float64 dask.array<chunksize=(1, 838, 1259), meta=np.ndarray>
    gamma0                (pol, line, sample) float64 dask.array<chunksize=(1, 838, 1259), meta=np.ndarray>
Attributes: (12/17)
    name:              SENTINEL1_DS:/tmp/S1A_IW_GRDH_1SDV_20170907T103020_201...
    short_name:        SENTINEL1_DS:S1A_IW_GRDH_1SDV_20170907T103020_20170907...
    product:           GRDH
    safe:              S1A_IW_GRDH_1SDV_20170907T103020_20170907T103045_01826...
    swath:             IW
    multidataset:      False
    ...                ...
    footprint:         POLYGON ((-67.84221143971432 20.72564283093837, -70.22...
    coverage:          170km * 251km (line * sample )
    pixel_line_m:      <xarray.DataArray 'azimuthPixelSpacing' ()>\narray(10....
    pixel_sample_m:    <xarray.DataArray 'groundRangePixelSpacing' ()>\narray...
    orbit_pass:        Descending
    platform_heading:  -167.7668824808032