Skip to content

Commit 09af28d

Browse files
authored
Merge pull request #103 from bopen/documentation
Documentation
2 parents 38772b7 + 1da2e52 commit 09af28d

File tree

3 files changed

+62
-21
lines changed

3 files changed

+62
-21
lines changed

README.md

+15-7
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,11 @@ Due to the inherent complexity and redundancy of the SAFE format *xarray-sentine
5858
maps it to a tree of *groups* where every *group* may be opened as a `Dataset`,
5959
but it may also contain *subgroups*, that are listed in the `subgroups` attribute.
6060

61+
The following sections show some example of xarray-sentinel usage.
62+
In the `notebooks` folder you
63+
can also find notebooks, one for each supported product, that allow you to explore the
64+
data in more detail using the xarray-sentinel functions.
65+
6166
### The root dataset
6267

6368
For example let's explore the Sentinel-1 SLC Stripmap product in the local folder
@@ -137,6 +142,9 @@ associated with the image line
137142
and `slant_range_time` is an `np.float64` coordinate that contains the two-way range time interval
138143
in seconds associated with the image pixel.
139144

145+
Since Sentinel-1 IPF version 3.40, a unique identifier for bursts has been added to the SLC product metadata.
146+
For these products, the list of the burst ids is stored the `burst_ids` dataset attribute.
147+
140148
### Metadata datasets
141149

142150
The measurement group contains several subgroups with metadata associated with the image. Currently,
@@ -230,14 +238,14 @@ but also because the measurement array is a collage of sub-images called *bursts
230238

231239
*xarray-sentinel* provides a helper function that crops a burst out of a measurement dataset for you.
232240

233-
You need to first open the desired measurement dataset, for example, the VH polarisation
234-
of the first IW swath of the `S1B_IW_SLC__1SDV_20210401T052622_20210401T052650_026269_032297_EFA4`
241+
You need to first open the desired measurement dataset, for example, the HH polarisation
242+
of the first IW swath of the `S1A_IW_SLC__1SDH_20220414T102209_20220414T102236_042768_051AA4_E677.SAFE`
235243
product, in the current folder:
236244

237245
```python-repl
238246
>>> slc_iw_v340_path = "tests/data/S1A_IW_SLC__1SDH_20220414T102209_20220414T102236_042768_051AA4_E677.SAFE"
239-
>>> slc_iw1_hh = xr.open_dataset(slc_iw_v340_path, group="IW1/HH", engine="sentinel-1")
240-
>>> slc_iw1_hh
247+
>>> slc_iw1_v340_hh = xr.open_dataset(slc_iw_v340_path, group="IW1/HH", engine="sentinel-1")
248+
>>> slc_iw1_v340_hh
241249
<xarray.Dataset>
242250
Dimensions: (pixel: 21169, line: 13500)
243251
Coordinates:
@@ -271,7 +279,7 @@ Now the 9th burst out of 9 can be cropped from the swath data using `burst_index
271279

272280
```python-repl
273281
>>> import xarray_sentinel
274-
>>> xarray_sentinel.crop_burst_dataset(slc_iw1_hh, burst_index=8)
282+
>>> xarray_sentinel.crop_burst_dataset(slc_iw1_v340_hh, burst_index=8)
275283
<xarray.Dataset>
276284
Dimensions: (slant_range_time: 21169, azimuth_time: 1500)
277285
Coordinates:
@@ -298,11 +306,11 @@ Attributes: ...
298306
299307
```
300308

301-
For products processed with processor versions 3.40 or higher, it is also possible to select the burst
309+
If IPF processor version is 3.40 or higher, it is also possible to select the burst
302310
to be cropped using the `burst_id` key:
303311

304312
```python-repl
305-
>>> xarray_sentinel.crop_burst_dataset(slc_iw1_hh, burst_id=365923)
313+
>>> xarray_sentinel.crop_burst_dataset(slc_iw1_v340_hh, burst_id=365923)
306314
<xarray.Dataset>
307315
Dimensions: (slant_range_time: 21169, azimuth_time: 1500)
308316
Coordinates:

tests/test_30_xarray_backends.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -199,15 +199,15 @@ def test_burst_id_attribute() -> None:
199199
)
200200

201201
res = xr.open_dataset(product_path, engine="sentinel-1", group="IW1/HH") # type: ignore
202-
assert "bursts_ids" in res.attrs
203-
assert len(res.attrs["bursts_ids"]) == res.attrs["number_of_bursts"]
202+
assert "burst_ids" in res.attrs
203+
assert len(res.attrs["burst_ids"]) == res.attrs["number_of_bursts"]
204204

205205
product_path = (
206206
DATA_FOLDER
207207
/ "S1B_IW_SLC__1SDV_20210401T052622_20210401T052650_026269_032297_EFA4.SAFE"
208208
)
209209
res = xr.open_dataset(product_path, engine="sentinel-1", group="IW1/VV") # type: ignore
210-
assert "bursts_ids" not in res.attrs
210+
assert "burst_ids" not in res.attrs
211211

212212

213213
def test_open_pol_dataset_preferred_chunks() -> None:

xarray_sentinel/sentinel1.py

+44-11
Original file line numberDiff line numberDiff line change
@@ -462,10 +462,10 @@ def open_pol_dataset(
462462
swap_dims = {"line": "azimuth_time", "pixel": "slant_range_time"}
463463
else:
464464
if "burstId" in swath_timing["burstList"]["burst"][0]:
465-
bursts_ids = []
465+
burst_ids = []
466466
for burst in swath_timing["burstList"]["burst"]:
467-
bursts_ids.append(burst["burstId"]["$"])
468-
attrs["bursts_ids"] = bursts_ids
467+
burst_ids.append(burst["burstId"]["$"])
468+
attrs["burst_ids"] = burst_ids
469469
lines_per_burst = swath_timing["linesPerBurst"]
470470
attrs.update(
471471
{
@@ -579,6 +579,17 @@ def crop_burst_dataset(
579579
use_center: bool = False,
580580
burst_id: T.Optional[int] = None,
581581
) -> xr.Dataset:
582+
"""
583+
Returns the measurement dataset cropped to the selected burst.
584+
Only one keyword between 'burst_index' and 'azimuth_anx_time' and 'burst_id' must be defined.
585+
:param xr.Dataset pol_dataset: measurement dataset
586+
:param int burst_index: burst index can take values from 1 to the number of bursts
587+
:param float azimuth_anx_time: azimuth anx time of first line of the bursts
588+
To use the center instead of the first line, set `use_center=True`
589+
:param bool use_center: If `true`, it uses as reference the azimuth anx time of the burst center instead of the first line
590+
:param int burst_id: for product processed with Sentinel-1 IPF version 3.40 or higher,
591+
the burst can be selected using the relative burst id.
592+
"""
582593
burst_definitions = (
583594
(burst_index is not None)
584595
+ (azimuth_anx_time is not None)
@@ -595,16 +606,16 @@ def crop_burst_dataset(
595606
pol_dataset, azimuth_anx_time, use_center=use_center
596607
)
597608
elif burst_id is not None:
598-
bursts_ids = pol_dataset.attrs.get("bursts_ids")
599-
if bursts_ids is None:
609+
burst_ids = pol_dataset.attrs.get("burst_ids")
610+
if burst_ids is None:
600611
raise TypeError(
601-
"'bursts_ids' list can't be found in product attributes, "
612+
"'burst_ids' list can't be found in product attributes, "
602613
"probably Sentinel-1 IPF processor version is older than 3.40"
603614
)
604615
try:
605-
burst_index = bursts_ids.index(burst_id)
616+
burst_index = burst_ids.index(burst_id)
606617
except ValueError:
607-
raise KeyError(f"{burst_id=} not found in product {bursts_ids=}")
618+
raise KeyError(f"{burst_id=} not found in product {burst_ids=}")
608619
else:
609620
raise TypeError(
610621
"one keyword between 'burst_index' and 'azimuth_anx_time' must be defined"
@@ -625,9 +636,9 @@ def crop_burst_dataset(
625636
ds.attrs["azimuth_anx_time"] = burst_azimuth_anx_times.values[0] / ONE_SECOND
626637
ds = ds.swap_dims({"line": "azimuth_time", "pixel": "slant_range_time"})
627638
ds.attrs["burst_index"] = burst_index
628-
if "bursts_ids" in ds.attrs:
629-
ds.attrs["burst_id"] = ds.attrs["bursts_ids"][burst_index]
630-
_ = ds.attrs.pop("bursts_ids")
639+
if "burst_ids" in ds.attrs:
640+
ds.attrs["burst_id"] = ds.attrs["burst_ids"][burst_index]
641+
_ = ds.attrs.pop("burst_ids")
631642
_ = ds.attrs.pop("subgroups", None)
632643
return ds
633644

@@ -643,6 +654,11 @@ def mosaic_slc_iw(slc_iw_image: xr.Dataset, crop: int = 90) -> xr.Dataset:
643654
def calibrate_amplitude(
644655
digital_number: xr.DataArray, calibration_lut: xr.DataArray
645656
) -> xr.DataArray:
657+
"""Returns the calibrated amplitude. The calibration is done using the calibration LUT in the product metadata.
658+
:param digital_number: digital numbers to be calibrated
659+
:param calibration_lut: calibration LUT (sigmaNought, betaNought or gamma).
660+
The LUT can be opened using the measurement sub-group `calibration`
661+
"""
646662
calibration = calibration_lut.interp(
647663
line=digital_number.line,
648664
pixel=digital_number.pixel,
@@ -664,6 +680,14 @@ def calibrate_intensity(
664680
as_db: bool = False,
665681
min_db: T.Optional[float] = -40.0,
666682
) -> xr.DataArray:
683+
"""
684+
Returns the calibrated intensity. The calibration is done using the calibration LUT in the product metadata.
685+
:param digital_number: digital numbers to be calibrated
686+
:param calibration_lut: calibration LUT (sigmaNought, betaNought or gamma).
687+
The LUT can be opened using the measurement sub-group `calibration`.
688+
:param as_db: if True, returns the data in db
689+
:param min_db: minimal value in db, to avoid infinity values.
690+
"""
667691
amplitude = calibrate_amplitude(digital_number, calibration_lut)
668692
intensity = abs(amplitude) ** 2
669693
if as_db:
@@ -688,6 +712,15 @@ def slant_range_time_to_ground_range(
688712
slant_range_time: xr.DataArray,
689713
coordinate_conversion: xr.Dataset,
690714
) -> xr.DataArray:
715+
"""
716+
Convert the slant range time coordinates to ground range coordinates using the coordinate conversion `sr0`
717+
and `srgrCoefficients` product metadata
718+
:param azimuth_time: azimuth time coordinates
719+
:param slant_range_time: slant range time
720+
:param coordinate_conversion: coordinate conversion dataset.
721+
The coordinate conversion dataset can be opened using the measurement sub-groub `coordinate_conversion`
722+
:return:
723+
"""
691724
slant_range = SPEED_OF_LIGHT / 2.0 * slant_range_time
692725
cc = coordinate_conversion.interp(azimuth_time=azimuth_time)
693726
x = slant_range - cc.sr0

0 commit comments

Comments
 (0)