In this tutorial, we will explore Level 1 Single Look Complex (SLC) dataset, collected by Uninhabited Aerial Vehicle Synthetic Aperture Radar (UAVSAR), a polarimetric L-band synthetic aperture radar flown on the NASA Gulfstream-III aircraft as part of NASA Delta-X campaign. This tutorial is modified from a jupyter notebook developed by Talib Oliver-Cabrera of NASA JPL for Delta-X Applications Workshop.
import earthaccess
import numpy as np
import pandas as pd
import geopandas as gpd
import xarray as xr
import hvplot.xarray
from os import path
import warnings
# suppress future warnings
warnings.filterwarnings('ignore', category=FutureWarning)Authentication¶
We recommend authenticating your Earthdata Login (EDL) information using the earthaccess python library as follows:
# works if the EDL login already been persisted to a netrc
try:
auth = earthaccess.login(strategy="netrc")
except FileNotFoundError:
# ask for EDL credentials and persist them in a .netrc file
auth = earthaccess.login(strategy="interactive", persist=True)Search granules¶
We will using the earthaccess module to search the granules within the UAVSAR dataset.
doi = '10.3334/ORNLDAAC/1984' # UAVSAR SLC dataset
granules = earthaccess.search_data(
doi=doi
)
print(f'Granules found: {len(granules)}')Granules found: 2392
Let’s plot the bounding boxes of the datas
gdf = gpd.GeoDataFrame(granules,
geometry=gpd.GeoSeries(granules, crs=4326))
gdf.explore(color='red', alpha=0.9,
tiles='https://mt1.google.com/vt/lyrs=s&x={x}&y={y}&z={z}',
attr='Google')Let’s print the first few granules link.
# print the first 15 granules
[path.basename(g.data_links()[0]) for g in granules][:15]['atchaf_19809_02_BU_s1_2x8.llh',
'atchaf_19809_02_BU.dop',
'atchaf_19809_21023_000_210327_L090VV_02_BU_s1_1x1.slc',
'atchaf_19809_21023_000_210327_L090VV_02_BU.ann',
'atchaf_19809_21023_000_210327_L090HV_02_BU.ann',
'atchaf_19809_21023_000_210327_L090HH_02_BU.ann',
'atchaf_19809_21023_000_210327_L090HH_02_BU_s1_1x1.slc',
'atchaf_19809_21023_000_210327_L090HV_02_BU_s1_1x1.slc',
'atchaf_19809_21023_000_210327_L090VH_02_BU.ann',
'atchaf_19809_02_BU_s1_2x8.lkv',
'atchaf_19809_21023_000_210327_L090VH_02_BU_s1_1x1.slc',
'atchaf_06309_21023_001_210327_L090VV_02_BU_s1_1x1.slc',
'atchaf_06309_21023_001_210327_L090VV_02_BU.ann',
'atchaf_06309_21023_001_210327_L090HH_02_BU.ann',
'atchaf_06309_21023_001_210327_L090VH_02_BU.ann']As we see above, there are five different kinds of file extensions in the dataset. Each flight ID contains 4 slant range single look complex (SLC) with corresponding 4 metadata files (ANN) There are also additional a latitude/longitude/height (LLH), a look vector (LKV), a doppler (DOP) files for each line ID, as shown in the table below. The same area was sampled at approximately 30-minute intervals. The SLCs are not corrected for residual baseline (BU).
| File name | Description |
|---|---|
| *.slc | SLC stacks in the HH, HV, VH, and VV polarizations |
| *.ann | Annotation files with metadata for each SLC files |
| *.llh | Latitude, longitude, and height corresponding to SLC pixels |
| *lkv | Look vector (east, north, up) at the target pointing from the aircraft to the ground |
| *.dop | Doppler centroid versus slant range |
Download granules¶
Let’s download granules from only one flight ID (4 slc and 4 ann), with sitename wterre, line id as 34202, flight id as 21028, data take counter as 017, and polarization VV.
granules = earthaccess.search_data(
granule_name = f"*wterre_34202_21028_017*VV_*", # retrieve only one flight ID
doi=doi
)
# print file name
print(f'Granules found: {len(granules)}')Granules found: 3
Let’s print the filenames and the file size of these 3 granules.
# granule name and size
g_name = [path.basename(g.data_links()[0]) for g in granules]
g_size = [g.size()*0.001 for g in granules]
# print as table
pd.set_option('display.max_colwidth', None)
pd.DataFrame({'FileName': g_name, 'Size(GB)': g_size})Using the earthaccess, we will now download ~10 GB worth of data to your local directory data. This step will take a while to complete.
earthaccess.download(granules, local_path="data")
[PosixPath('data/wterre_34202_21028_017_210407_L090VV_02_BU_s2_1x1.slc'),
PosixPath('data/wterre_34202_21028_017_210407_L090VV_02_BU_s1_1x1.slc'),
PosixPath('data/wterre_34202_21028_017_210407_L090VV_02_BU.ann')]Annotation file¶
Let’s take a look at annotation file with VV polarization.
slc_f = "data/wterre_34202_21028_017_210407_L090VV_02_BU"
with open(f"{slc_f}.ann", "r") as f:
print(f.read())File size parameters¶
In the above file, take a note of “File size parameters” section, which we will use to create SLC images in the next section.
slc_1_1x1 Columns (pixels) = 9900 ; samples in SLC 1x1 segment 1
slc_1_1x1 Rows (pixels) = 66664 ; lines in SLC 1x1 segment 1
slc_1_1x4 Columns (pixels) = 9900 ; samples in SLC 1x4 segment 1
slc_1_1x4 Rows (pixels) = 16666 ; lines in SLC 1x4 segment 1
slc_1_2x8 Columns (pixels) = 4950 ; samples in SLC 2x8 segment 1
slc_1_2x8 Rows (pixels) = 8333 ; lines in SLC 2x8 segment 1
llh_1_2x8 Columns (pixels) = 4950 ; samples in LLH segment 1
llh_1_2x8 Rows (pixels) = 8333 ; lines in LLH segment 1
lkv_1_2x8 Columns (pixels) = 4950 ; samples in LKV segment 1
lkv_1_2x8 Rows (pixels) = 8333 ; lines in LKV segment 1
slc_2_1x1 Columns (pixels) = 9900 ; samples in SLC 1x1 segment 2
slc_2_1x1 Rows (pixels) = 55408 ; lines in SLC 1x1 segment 2
slc_2_1x4 Columns (pixels) = 9900 ; samples in SLC 1x4 segment 2
slc_2_1x4 Rows (pixels) = 13852 ; lines in SLC 1x4 segment 2
slc_2_2x8 Columns (pixels) = 4950 ; samples in SLC 2x8 segment 2
slc_2_2x8 Rows (pixels) = 6926 ; lines in SLC 2x8 segment 2
llh_2_2x8 Columns (pixels) = 4950 ; samples in LLH segment 2
llh_2_2x8 Rows (pixels) = 6926 ; lines in LLH segment 2
lkv_2_2x8 Columns (pixels) = 4950 ; samples in LKV segment 2
lkv_2_2x8 Rows (pixels) = 6926 ; lines in LKV segment 2Read and visualize SLC image¶
Let’s read the Segment 1 SLC image.
cols_seg1 = 9900
rows_seg1 = 66664
# Read files
slc_seg1 = np.memmap(f"{slc_f}_s1_1x1.slc",
dtype=np.complex64).reshape(rows_seg1, cols_seg1)
# Extract backscatter and phase
backscatter_seg1 = np.abs(slc_seg1)
phase_seg_1 = np.angle(slc_seg1)Now, let’s read the Segment 2 SLC image.
cols_seg2 = 9900
rows_seg2 = 55408
# Read files
slc_seg2 = np.memmap(f"{slc_f}_s2_1x1.slc",
dtype=np.complex64).reshape(rows_seg2, cols_seg2)
# Extract backscatter and phase
backscatter_seg2 = np.abs(slc_seg2)
phase_seg_2 = np.angle(slc_seg2)Let’s plot backscatter and phase images from both Segments 1 and 2.
# backscatter segment 1
da = xr.DataArray(backscatter_seg1, dims=("y", "x"))
p1 = da.hvplot.image(
x='x', y='y', rasterize=True, data_aspect=1, frame_width=100,
cmap='gray', title='backscatter segment1'
)
# phase segment 1
da = xr.DataArray(phase_seg_1, dims=("y", "x"))
p2 = da.hvplot.image(
x='x', y='y', rasterize=True, data_aspect=1, frame_width=100,
cmap='hsv', title='phase segment1'
)
# backscatter segment 2
da = xr.DataArray(backscatter_seg2, dims=("y", "x"))
p3 = da.hvplot.image(
x='x', y='y', rasterize=True, data_aspect=1, frame_width=100,
cmap='gray', title='backscatter segment2'
)
# phase segment 1
da = xr.DataArray(phase_seg_2, dims=("y", "x"))
p4 = da.hvplot.image(
x='x', y='y', rasterize=True, data_aspect=1, frame_width=100,
cmap='hsv', title='phase segment2'
)
(p1 + p2 + p3 + p4).cols(4)Generate a single array using the acquisition segments¶
Let’s take the backscatter from both Segments 1 and 2 and generate one full array.
merged_backscatter = np.concatenate((backscatter_seg1, backscatter_seg2), axis=0)
# print merged backscatter dims
merged_backscatter.shape(122072, 9900)da = xr.DataArray(merged_backscatter, dims=("y", "x"))
da.hvplot.image(
x='x', y='y', rasterize=True, data_aspect=1, frame_width=100,
cmap='gray', title='phase segment2'
)- Jones, C., Simard, M., Lou, Y., & Oliver-Cabrera, T. (2022). Delta-X: UAVSAR L1 Single Look Complex (SLC) Stack Products, MRD, Louisiana, 2021. ORNL Distributed Active Archive Center. 10.3334/ORNLDAAC/1984