Skip to content

Commit 51add7c

Browse files
committed
Merge remote-tracking branch 'origin/main' into declare-typed
2 parents 5fa8bb4 + 09af28d commit 51add7c

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
@@ -465,10 +465,10 @@ def open_pol_dataset(
465465
swap_dims = {"line": "azimuth_time", "pixel": "slant_range_time"}
466466
else:
467467
if "burstId" in swath_timing["burstList"]["burst"][0]:
468-
bursts_ids = []
468+
burst_ids = []
469469
for burst in swath_timing["burstList"]["burst"]:
470-
bursts_ids.append(burst["burstId"]["$"])
471-
attrs["bursts_ids"] = bursts_ids
470+
burst_ids.append(burst["burstId"]["$"])
471+
attrs["burst_ids"] = burst_ids
472472
lines_per_burst = swath_timing["linesPerBurst"]
473473
attrs.update(
474474
{
@@ -582,6 +582,17 @@ def crop_burst_dataset(
582582
use_center: bool = False,
583583
burst_id: T.Optional[int] = None,
584584
) -> DataArrayOrDataset:
585+
"""
586+
Returns the measurement dataset cropped to the selected burst.
587+
Only one keyword between 'burst_index' and 'azimuth_anx_time' and 'burst_id' must be defined.
588+
:param xr.Dataset pol_dataset: measurement dataset
589+
:param int burst_index: burst index can take values from 1 to the number of bursts
590+
:param float azimuth_anx_time: azimuth anx time of first line of the bursts
591+
To use the center instead of the first line, set `use_center=True`
592+
:param bool use_center: If `true`, it uses as reference the azimuth anx time of the burst center instead of the first line
593+
:param int burst_id: for product processed with Sentinel-1 IPF version 3.40 or higher,
594+
the burst can be selected using the relative burst id.
595+
"""
585596
burst_definitions = (
586597
(burst_index is not None)
587598
+ (azimuth_anx_time is not None)
@@ -598,16 +609,16 @@ def crop_burst_dataset(
598609
pol_dataset, azimuth_anx_time, use_center=use_center
599610
)
600611
elif burst_id is not None:
601-
bursts_ids = pol_dataset.attrs.get("bursts_ids")
602-
if bursts_ids is None:
612+
burst_ids = pol_dataset.attrs.get("burst_ids")
613+
if burst_ids is None:
603614
raise TypeError(
604-
"'bursts_ids' list can't be found in product attributes, "
615+
"'burst_ids' list can't be found in product attributes, "
605616
"probably Sentinel-1 IPF processor version is older than 3.40"
606617
)
607618
try:
608-
burst_index = bursts_ids.index(burst_id)
619+
burst_index = burst_ids.index(burst_id)
609620
except ValueError:
610-
raise KeyError(f"{burst_id=} not found in product {bursts_ids=}")
621+
raise KeyError(f"{burst_id=} not found in product {burst_ids=}")
611622
else:
612623
raise TypeError(
613624
"one keyword between 'burst_index' and 'azimuth_anx_time' must be defined"
@@ -628,9 +639,9 @@ def crop_burst_dataset(
628639
ds.attrs["azimuth_anx_time"] = burst_azimuth_anx_times.values[0] / ONE_SECOND
629640
ds = ds.swap_dims({"line": "azimuth_time", "pixel": "slant_range_time"})
630641
ds.attrs["burst_index"] = burst_index
631-
if "bursts_ids" in ds.attrs:
632-
ds.attrs["burst_id"] = ds.attrs["bursts_ids"][burst_index]
633-
_ = ds.attrs.pop("bursts_ids")
642+
if "burst_ids" in ds.attrs:
643+
ds.attrs["burst_id"] = ds.attrs["burst_ids"][burst_index]
644+
_ = ds.attrs.pop("burst_ids")
634645
_ = ds.attrs.pop("subgroups", None)
635646
return ds
636647

@@ -648,6 +659,11 @@ def mosaic_slc_iw(
648659
def calibrate_amplitude(
649660
digital_number: xr.DataArray, calibration_lut: xr.DataArray
650661
) -> xr.DataArray:
662+
"""Returns the calibrated amplitude. The calibration is done using the calibration LUT in the product metadata.
663+
:param digital_number: digital numbers to be calibrated
664+
:param calibration_lut: calibration LUT (sigmaNought, betaNought or gamma).
665+
The LUT can be opened using the measurement sub-group `calibration`
666+
"""
651667
calibration = calibration_lut.interp(
652668
line=digital_number.line,
653669
pixel=digital_number.pixel,
@@ -669,6 +685,14 @@ def calibrate_intensity(
669685
as_db: bool = False,
670686
min_db: T.Optional[float] = -40.0,
671687
) -> xr.DataArray:
688+
"""
689+
Returns the calibrated intensity. The calibration is done using the calibration LUT in the product metadata.
690+
:param digital_number: digital numbers to be calibrated
691+
:param calibration_lut: calibration LUT (sigmaNought, betaNought or gamma).
692+
The LUT can be opened using the measurement sub-group `calibration`.
693+
:param as_db: if True, returns the data in db
694+
:param min_db: minimal value in db, to avoid infinity values.
695+
"""
672696
amplitude = calibrate_amplitude(digital_number, calibration_lut)
673697
intensity = abs(amplitude) ** 2
674698
if as_db:
@@ -693,6 +717,15 @@ def slant_range_time_to_ground_range(
693717
slant_range_time: xr.DataArray,
694718
coordinate_conversion: xr.Dataset,
695719
) -> xr.DataArray:
720+
"""
721+
Convert the slant range time coordinates to ground range coordinates using the coordinate conversion `sr0`
722+
and `srgrCoefficients` product metadata
723+
:param azimuth_time: azimuth time coordinates
724+
:param slant_range_time: slant range time
725+
:param coordinate_conversion: coordinate conversion dataset.
726+
The coordinate conversion dataset can be opened using the measurement sub-groub `coordinate_conversion`
727+
:return:
728+
"""
696729
slant_range = SPEED_OF_LIGHT / 2.0 * slant_range_time
697730
cc = coordinate_conversion.interp(azimuth_time=azimuth_time)
698731
x = slant_range - cc.sr0

0 commit comments

Comments
 (0)