SHIFT V2 Reflectance Data Guide

This guide explains how to use the SHIFT V2 Reflectance data. The data is stored in an S3 bucket, which is mounted to your SMCE computing environment. You can access the data at the following location: s3/dh-shift-curated/aviris/v2

The SHIFT V2 Reflectance data has transitioned from ENVI files to NetCDF format. I have written a few functions to facilitate interaction with this new format. Below are some examples of how to work with the data.

[3]:
import sys
sys.path.append('s3/dh-shift-curated/aviris/utils')
from shift_v2_utils import find_overlap_shift_v2, open_shift_v2_data

You can use the find overlap function to find flightlines that overlap a shapefile formated as a geopandas dataframe

[4]:
import geopandas as gpd
from shapely.geometry import Polygon

polygon_1 = Polygon([
    (-119.8853015 , 34.42277795),
    (-119.86975941, 34.42312643),
    (-119.86921817, 34.4066284 ),
    (-119.88476322, 34.40623869),
    (-119.8853015 , 34.42277795)
])

polygon_2 = Polygon([
    (-119.8753015 , 34.41777795),
    (-119.85975941, 34.41812643),
    (-119.85921817, 34.4016284 ),
    (-119.87476322, 34.40123869),
    (-119.8753015 , 34.41777795)
])

# Create a GeoDataFrame
gdf = gpd.GeoDataFrame({
    'id': [1, 2],
    'name': ['Polygon 1', 'Polygon 2'],
    'geometry': [polygon_1, polygon_2]
}, crs="EPSG:4326")

The overlap function takes a GeoDataFrame as an argument. The GeoDataFrame must have its CRS set to EPSG:4326. You can filter the overlapping flightlines by date. The date argument can be provided as a single string or as a list of strings,

[5]:
# all dates
find_overlap_shift_v2(gdf)

# single date filter
find_overlap_shift_v2(gdf, dates='20220316')

# multiple dates filter
flightlines = find_overlap_shift_v2(gdf, dates=['20220316', '20220914'])
flightlines
[5]:
index flightline date path geometry index_right id name
0 306 ang20220316t184402_001 20220316 s3/dh-shift-curated/aviris/v2/L2a/20220316/ang... POLYGON ((-119.77003 34.38405, -119.76854 34.4... 1 2 Polygon 2
1 307 ang20220316t184402_002 20220316 s3/dh-shift-curated/aviris/v2/L2a/20220316/ang... POLYGON ((-119.85591 34.39932, -119.85424 34.4... 1 2 Polygon 2
2 1418 ang20220914t170915_000 20220914 s3/dh-shift-curated/aviris/v2/L2a/20220914/ang... POLYGON ((-119.80109 34.37997, -119.79822 34.4... 1 2 Polygon 2
3 1426 ang20220914t171806_003 20220914 s3/dh-shift-curated/aviris/v2/L2a/20220914/ang... POLYGON ((-119.80085 34.31877, -119.79571 34.4... 1 2 Polygon 2
4 1428 ang20220914t172925_001 20220914 s3/dh-shift-curated/aviris/v2/L2a/20220914/ang... POLYGON ((-119.77325 34.38317, -119.77163 34.4... 1 2 Polygon 2
5 1429 ang20220914t172925_002 20220914 s3/dh-shift-curated/aviris/v2/L2a/20220914/ang... POLYGON ((-119.86728 34.39939, -119.86555 34.4... 1 2 Polygon 2
6 307 ang20220316t184402_002 20220316 s3/dh-shift-curated/aviris/v2/L2a/20220316/ang... POLYGON ((-119.85591 34.39932, -119.85424 34.4... 0 1 Polygon 1
7 1426 ang20220914t171806_003 20220914 s3/dh-shift-curated/aviris/v2/L2a/20220914/ang... POLYGON ((-119.80085 34.31877, -119.79571 34.4... 0 1 Polygon 1
8 1428 ang20220914t172925_001 20220914 s3/dh-shift-curated/aviris/v2/L2a/20220914/ang... POLYGON ((-119.77325 34.38317, -119.77163 34.4... 0 1 Polygon 1
9 1429 ang20220914t172925_002 20220914 s3/dh-shift-curated/aviris/v2/L2a/20220914/ang... POLYGON ((-119.86728 34.39939, -119.86555 34.4... 0 1 Polygon 1

Using the output of the overlap function, you can use the path to open the dataset of interest. By default, the function will return the reflectance group. However, if you want to load additional groups, you can specify them by name: [‘reflectance’, ‘water_vapor’, ‘aerosol_optical_thickness’].

The mask_and_scale argument controls how Xarray handles “NoData” values. By default, mask_and_scale is set to False, meaning Xarray will use the coded NoData value of -9999. If you set mask_and_scale to True, Xarray will fill NoData values with np.nan instead.

[6]:
path = flightlines.iloc[5].path
ds = open_shift_v2_data(path)
ds = open_shift_v2_data(path, groups=['reflectance', 'water_vapor', 'aerosol_optical_thickness'], mask_and_scale=False)
ds
[6]:
<xarray.Dataset>
Dimensions:                    (easting: 2259, northing: 991, wavelength: 425)
Coordinates:
  * easting                    (easting) float64 7.762e+05 ... 7.88e+05
  * northing                   (northing) float64 3.816e+06 ... 3.811e+06
    transverse_mercator        int64 0
  * wavelength                 (wavelength) float32 377.2 382.2 ... 2.501e+03
Data variables:
    fwhm                       (wavelength) float32 dask.array<chunksize=(425,), meta=np.ndarray>
    reflectance                (wavelength, northing, easting) float32 dask.array<chunksize=(425, 991, 2259), meta=np.ndarray>
    water_vapor                (northing, easting) float32 dask.array<chunksize=(991, 2259), meta=np.ndarray>
    aerosol_optical_thickness  (northing, easting) float32 dask.array<chunksize=(991, 2259), meta=np.ndarray>
Attributes: (12/23)
    Conventions:                       CF-1.6
    date_created:                      2024-12-30T18:52:40Z
    summary:                           The Airborne Visible / Infrared Imagin...
    keywords:                          Imaging Spectroscopy, AVIRIS, AVIRIS-NG
    sensor:                            Airborne Visible / Infrared Imaging Sp...
    instrument:                        AVIRIS-NG
    ...                                ...
    ncei_template_version:             NCEI_NetCDF_Grid_Template_v2.0
    title:                             AVIRIS-NG L2A Surface reflectance (fli...
    processing_level:                  L2A
    time_coverage_start:               2022-09-14T17:33:37Z
    time_coverage_end:                 2022-09-14T17:33:37Z
    product_version:                   0

The data is lazily loaded using Dask, meaning it is not fully loaded into memory until necessary. You can perform operations, such as clipping the data, using tools like rioxarray without actually loading the data into memory.

[7]:
clipped = ds.rio.clip(gdf.to_crs(ds.rio.crs).geometry)
clipped
[7]:
<xarray.Dataset>
Dimensions:                    (wavelength: 425, easting: 334, northing: 470)
Coordinates:
    transverse_mercator        int64 0
  * wavelength                 (wavelength) float32 377.2 382.2 ... 2.501e+03
  * easting                    (easting) float64 7.863e+05 ... 7.88e+05
  * northing                   (northing) float64 3.814e+06 ... 3.811e+06
Data variables:
    fwhm                       (wavelength) float32 dask.array<chunksize=(425,), meta=np.ndarray>
    reflectance                (wavelength, northing, easting) float32 dask.array<chunksize=(425, 470, 334), meta=np.ndarray>
    water_vapor                (northing, easting) float32 dask.array<chunksize=(470, 334), meta=np.ndarray>
    aerosol_optical_thickness  (northing, easting) float32 dask.array<chunksize=(470, 334), meta=np.ndarray>
Attributes: (12/23)
    Conventions:                       CF-1.6
    date_created:                      2024-12-30T18:52:40Z
    summary:                           The Airborne Visible / Infrared Imagin...
    keywords:                          Imaging Spectroscopy, AVIRIS, AVIRIS-NG
    sensor:                            Airborne Visible / Infrared Imaging Sp...
    instrument:                        AVIRIS-NG
    ...                                ...
    ncei_template_version:             NCEI_NetCDF_Grid_Template_v2.0
    title:                             AVIRIS-NG L2A Surface reflectance (fli...
    processing_level:                  L2A
    time_coverage_start:               2022-09-14T17:33:37Z
    time_coverage_end:                 2022-09-14T17:33:37Z
    product_version:                   0

Once you have clipped the data you can load it into memory using compute. Loading the data into memory might take some time. Once the data has been accessed once following access attempts should be much quicker.

[8]:
%%time
clipped.reflectance.compute()
CPU times: user 14.6 s, sys: 1.72 s, total: 16.3 s
Wall time: 16.4 s
[8]:
<xarray.DataArray 'reflectance' (wavelength: 425, northing: 470, easting: 334)>
array([[[-9999., -9999., -9999., ..., -9999., -9999., -9999.],
        [-9999., -9999., -9999., ..., -9999., -9999., -9999.],
        [-9999., -9999., -9999., ..., -9999., -9999., -9999.],
        ...,
        [-9999., -9999., -9999., ..., -9999., -9999., -9999.],
        [-9999., -9999., -9999., ..., -9999., -9999., -9999.],
        [-9999., -9999., -9999., ..., -9999., -9999., -9999.]],

       [[-9999., -9999., -9999., ..., -9999., -9999., -9999.],
        [-9999., -9999., -9999., ..., -9999., -9999., -9999.],
        [-9999., -9999., -9999., ..., -9999., -9999., -9999.],
        ...,
        [-9999., -9999., -9999., ..., -9999., -9999., -9999.],
        [-9999., -9999., -9999., ..., -9999., -9999., -9999.],
        [-9999., -9999., -9999., ..., -9999., -9999., -9999.]],

       [[-9999., -9999., -9999., ..., -9999., -9999., -9999.],
        [-9999., -9999., -9999., ..., -9999., -9999., -9999.],
        [-9999., -9999., -9999., ..., -9999., -9999., -9999.],
        ...,
...
        [-9999., -9999., -9999., ..., -9999., -9999., -9999.],
        [-9999., -9999., -9999., ..., -9999., -9999., -9999.],
        [-9999., -9999., -9999., ..., -9999., -9999., -9999.]],

       [[-9999., -9999., -9999., ..., -9999., -9999., -9999.],
        [-9999., -9999., -9999., ..., -9999., -9999., -9999.],
        [-9999., -9999., -9999., ..., -9999., -9999., -9999.],
        ...,
        [-9999., -9999., -9999., ..., -9999., -9999., -9999.],
        [-9999., -9999., -9999., ..., -9999., -9999., -9999.],
        [-9999., -9999., -9999., ..., -9999., -9999., -9999.]],

       [[-9999., -9999., -9999., ..., -9999., -9999., -9999.],
        [-9999., -9999., -9999., ..., -9999., -9999., -9999.],
        [-9999., -9999., -9999., ..., -9999., -9999., -9999.],
        ...,
        [-9999., -9999., -9999., ..., -9999., -9999., -9999.],
        [-9999., -9999., -9999., ..., -9999., -9999., -9999.],
        [-9999., -9999., -9999., ..., -9999., -9999., -9999.]]],
      dtype=float32)
Coordinates:
    transverse_mercator  int64 0
  * wavelength           (wavelength) float32 377.2 382.2 ... 2.501e+03
  * easting              (easting) float64 7.863e+05 7.863e+05 ... 7.88e+05
  * northing             (northing) float64 3.814e+06 3.814e+06 ... 3.811e+06
Attributes:
    _QuantizeBitGroomNumberOfSignificantDigits:  5
    long_name:                                   Surface hemispherical direct...
    grid_mapping:                                transverse_mercator
    orthorectified:                              True
    _FillValue:                                  -9999.0