Rectification to a Sentinel-3 OLCI Scene¶
In this notebook, we apply a rectification algorithm to a Sentinel-3 OLCI Level-1B scene.
The Sentinel-3 OLCI Level-1B product is provided with two-dimensional geolocation coordinates, meaning that each pixel is associated with its own latitude and longitude values. This results in an irregular (curvilinear) grid rather than a regular, evenly spaced grid.
To enable standard geospatial processing and analysis, we rectify the dataset onto a regular grid using the rectification algorithm.
import matplotlib.pyplot as plt
import numpy as np
import xarray as xr
from xcube_resampling.rectify import rectify_dataset
%%time
source_ds = xr.open_zarr("./inputdata/S3-OLCI-L2A.zarr.zip", consolidated=False)
source_ds
CPU times: user 107 ms, sys: 20 ms, total: 127 ms Wall time: 127 ms
<xarray.Dataset> Size: 72MB
Dimensions: (y: 1890, x: 1189)
Coordinates:
lat (y, x) float64 18MB dask.array<chunksize=(512, 512), meta=np.ndarray>
lon (y, x) float64 18MB dask.array<chunksize=(512, 512), meta=np.ndarray>
Dimensions without coordinates: y, x
Data variables:
quality_flags (y, x) uint32 9MB dask.array<chunksize=(512, 512), meta=np.ndarray>
rtoa_3 (y, x) float32 9MB dask.array<chunksize=(512, 512), meta=np.ndarray>
rtoa_6 (y, x) float32 9MB dask.array<chunksize=(512, 512), meta=np.ndarray>
rtoa_8 (y, x) float32 9MB dask.array<chunksize=(512, 512), meta=np.ndarray>
Attributes:
Conventions: CF-1.4
product_type: C2RCC_OLCI
start_date: 04-JUL-2018 09:21:55.677316
stop_date: 04-JUL-2018 09:23:18.811790The provided latitude (lat) and longitude (lon) coordinates are terrain-corrected
using a digital elevation model (DEM). This means that the geolocation accounts for
topographic variations instead of assuming a smooth ellipsoidal Earth surface.
As a result, the coordinate fields are not smoothly varying across the scene. Instead, they reflect terrain-induced distortions introduced by the DEM. Areas with strong topographic variability therefore show stronger spatial irregularities in the coordinate gradients (highlighted in the right figure below).
%%time
lon_grad = source_ds.lat.differentiate("y").rename("Vertical Gradient")
fig, ax = plt.subplots(1, 2, figsize=(14, 5))
source_ds.rtoa_8.plot(ax=ax[0], cmap="gray", vmax=0.2)
lon_grad.plot(ax=ax[1], cmap="viridis", robust=True)
CPU times: user 763 ms, sys: 333 ms, total: 1.1 s Wall time: 1.3 s
<matplotlib.collections.QuadMesh at 0x7c887a9cd810>
For some applications, however, we prefer a regular grid that is not influenced
by terrain-related distortions. To achieve this, we rectify the dataset onto a
regular grid using the rectify_dataset method.
As interpolation method, we use nearest. Alternatively, we could specify 0,
which is shorthand for nearest-neighbor interpolation.
%%time
target_ds = rectify_dataset(source_ds, interp_methods="nearest")
target_ds
CPU times: user 787 ms, sys: 36.9 ms, total: 824 ms Wall time: 718 ms
<xarray.Dataset> Size: 56MB
Dimensions: (lon: 1636, lat: 2132)
Coordinates:
spatial_ref int64 8B 0
* lon (lon) float64 13kB 12.69 12.7 12.7 12.71 ... 20.0 20.0 20.0
* lat (lat) float64 17kB 60.64 60.64 60.63 ... 55.21 55.2 55.2
Data variables:
quality_flags (lat, lon) uint32 14MB dask.array<chunksize=(512, 512), meta=np.ndarray>
rtoa_3 (lat, lon) float32 14MB dask.array<chunksize=(512, 512), meta=np.ndarray>
rtoa_6 (lat, lon) float32 14MB dask.array<chunksize=(512, 512), meta=np.ndarray>
rtoa_8 (lat, lon) float32 14MB dask.array<chunksize=(512, 512), meta=np.ndarray>
Attributes:
Conventions: CF-1.4
product_type: C2RCC_OLCI
start_date: 04-JUL-2018 09:21:55.677316
stop_date: 04-JUL-2018 09:23:18.811790%%time
target_ds.rtoa_8.plot(cmap="gray", vmax=0.2)
CPU times: user 8.43 s, sys: 1.91 s, total: 10.3 s Wall time: 1.76 s
<matplotlib.collections.QuadMesh at 0x7c886010e710>
You can also change the chunk size in the output dataset with the keyword argument tile_size.
%%time
target_ds = rectify_dataset(source_ds, interp_methods=0, tile_size=2048)
target_ds
CPU times: user 380 ms, sys: 29.1 ms, total: 409 ms Wall time: 349 ms
<xarray.Dataset> Size: 56MB
Dimensions: (lon: 1636, lat: 2132)
Coordinates:
spatial_ref int64 8B 0
* lon (lon) float64 13kB 12.69 12.7 12.7 12.71 ... 20.0 20.0 20.0
* lat (lat) float64 17kB 60.64 60.64 60.63 ... 55.21 55.2 55.2
Data variables:
quality_flags (lat, lon) uint32 14MB dask.array<chunksize=(2048, 1636), meta=np.ndarray>
rtoa_3 (lat, lon) float32 14MB dask.array<chunksize=(2048, 1636), meta=np.ndarray>
rtoa_6 (lat, lon) float32 14MB dask.array<chunksize=(2048, 1636), meta=np.ndarray>
rtoa_8 (lat, lon) float32 14MB dask.array<chunksize=(2048, 1636), meta=np.ndarray>
Attributes:
Conventions: CF-1.4
product_type: C2RCC_OLCI
start_date: 04-JUL-2018 09:21:55.677316
stop_date: 04-JUL-2018 09:23:18.811790%%time
target_ds.rtoa_8.plot(cmap="gray", vmax=0.2)
CPU times: user 1.74 s, sys: 740 ms, total: 2.48 s Wall time: 1.77 s
<matplotlib.collections.QuadMesh at 0x7c88605d0f50>
We can also apply a different interpolation method, as shown in the next cell.
%%time
target_ds_bilin = rectify_dataset(source_ds, interp_methods="bilinear")
target_ds_bilin
CPU times: user 842 ms, sys: 26.9 ms, total: 869 ms Wall time: 761 ms
<xarray.Dataset> Size: 56MB
Dimensions: (lon: 1636, lat: 2132)
Coordinates:
spatial_ref int64 8B 0
* lon (lon) float64 13kB 12.69 12.7 12.7 12.71 ... 20.0 20.0 20.0
* lat (lat) float64 17kB 60.64 60.64 60.63 ... 55.21 55.2 55.2
Data variables:
quality_flags (lat, lon) uint32 14MB dask.array<chunksize=(512, 512), meta=np.ndarray>
rtoa_3 (lat, lon) float32 14MB dask.array<chunksize=(512, 512), meta=np.ndarray>
rtoa_6 (lat, lon) float32 14MB dask.array<chunksize=(512, 512), meta=np.ndarray>
rtoa_8 (lat, lon) float32 14MB dask.array<chunksize=(512, 512), meta=np.ndarray>
Attributes:
Conventions: CF-1.4
product_type: C2RCC_OLCI
start_date: 04-JUL-2018 09:21:55.677316
stop_date: 04-JUL-2018 09:23:18.811790%%time
target_ds_bilin.rtoa_8.plot(cmap="gray", vmax=0.2)
CPU times: user 8.42 s, sys: 1.42 s, total: 9.83 s Wall time: 1.38 s
<matplotlib.collections.QuadMesh at 0x7c8831b811d0>
Next, we visualize the difference between bilinear and nearest-neighbor interpolation.
(target_ds_bilin.rtoa_8 - target_ds.rtoa_8).plot(vmin=-0.02, vmax=+0.02, cmap="RdBu")
<matplotlib.collections.QuadMesh at 0x7c8831a27110>