neon module¶
This module contains functions to read and process NEON AOP hyperspectral data. More info about the data can be found at https://bit.ly/3Rfszdc. The source code is adapted from https://bit.ly/3KwyZkn. Credit goes to the original authors.
extract_neon(ds, lat, lon)
¶
Extracts NEON AOP data from a given xarray Dataset.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
ds |
xarray.Dataset |
The dataset containing the NEON AOP data. |
required |
lat |
float |
The latitude of the point to extract. |
required |
lon |
float |
The longitude of the point to extract. |
required |
Returns:
Type | Description |
---|---|
xarray.DataArray |
The extracted data. |
Source code in hypercoast/neon.py
def extract_neon(ds, lat, lon):
"""
Extracts NEON AOP data from a given xarray Dataset.
Args:
ds (xarray.Dataset): The dataset containing the NEON AOP data.
lat (float): The latitude of the point to extract.
lon (float): The longitude of the point to extract.
Returns:
xarray.DataArray: The extracted data.
"""
crs = ds.attrs["crs"]
x, y = convert_coords([[lat, lon]], "epsg:4326", crs)[0]
values = ds.sel(x=x, y=y, method="nearest")["reflectance"].values
da = xr.DataArray(
values, dims=["wavelength"], coords={"wavelength": ds.coords["wavelength"]}
)
return da
list_neon_datasets(filepath, print_node=False)
¶
Lists all the datasets in an HDF5 file.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
filepath |
str |
The path to the HDF5 file. |
required |
print_node |
bool |
If True, prints the node object of each dataset. If False, prints the name of each dataset. Defaults to False. |
False |
Source code in hypercoast/neon.py
def list_neon_datasets(filepath: str, print_node: bool = False) -> None:
"""
Lists all the datasets in an HDF5 file.
Args:
filepath (str): The path to the HDF5 file.
print_node (bool, optional): If True, prints the node object of each dataset.
If False, prints the name of each dataset. Defaults to False.
"""
f = h5py.File(filepath, "r")
if print_node:
def list_dataset(_, node):
if isinstance(node, h5py.Dataset):
print(node)
else:
def list_dataset(name, node):
if isinstance(node, h5py.Dataset):
print(name)
f.visititems(list_dataset)
neon_to_image(dataset, wavelengths=None, method='nearest', output=None, **kwargs)
¶
Converts an NEON dataset to an image.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
dataset |
Union[xr.Dataset, str] |
The dataset containing the NEON data or the file path to the dataset. |
required |
wavelengths |
np.ndarray |
The specific wavelengths to select. If None, all wavelengths are selected. Defaults to None. |
None |
method |
str |
The method to use for data interpolation. Defaults to "nearest". |
'nearest' |
output |
str |
The file path where the image will be saved. If None, the image will be returned as a PIL Image object. Defaults to None. |
None |
**kwargs |
Any |
Additional keyword arguments to be passed to
|
{} |
Returns:
Type | Description |
---|---|
Optional[rasterio.Dataset] |
The image converted from the dataset. If
|
Source code in hypercoast/neon.py
def neon_to_image(
dataset: Union[xr.Dataset, str],
wavelengths: Optional[np.ndarray] = None,
method: str = "nearest",
output: Optional[str] = None,
**kwargs: Any,
):
"""
Converts an NEON dataset to an image.
Args:
dataset (Union[xr.Dataset, str]): The dataset containing the NEON data
or the file path to the dataset.
wavelengths (np.ndarray, optional): The specific wavelengths to select. If None, all
wavelengths are selected. Defaults to None.
method (str, optional): The method to use for data interpolation.
Defaults to "nearest".
output (str, optional): The file path where the image will be saved. If
None, the image will be returned as a PIL Image object. Defaults to None.
**kwargs (Any): Additional keyword arguments to be passed to
`leafmap.array_to_image`.
Returns:
Optional[rasterio.Dataset]: The image converted from the dataset. If
`output` is provided, the image will be saved to the specified file
and the function will return None.
"""
from leafmap import array_to_image
if isinstance(dataset, str):
dataset = read_neon(dataset, method=method)
if wavelengths is not None:
dataset = dataset.sel(wavelength=wavelengths, method=method)
return array_to_image(
dataset["reflectance"],
output=output,
transpose=False,
dtype=np.float32,
**kwargs,
)
read_neon(filepath, wavelengths=None, method='nearest', **kwargs)
¶
Reads NEON AOP hyperspectral hdf5 files and returns an xarray dataset.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
filepath |
str |
The path to the hdf5 file. |
required |
wavelengths |
List[float] |
The wavelengths to select. If None, all wavelengths are selected. Defaults to None. |
None |
method |
str |
The method to use for selection. Defaults to "nearest". |
'nearest' |
**kwargs |
Any |
Additional arguments to pass to the selection method. |
{} |
Returns:
Type | Description |
---|---|
xr.Dataset |
The dataset containing the reflectance data. |
Source code in hypercoast/neon.py
def read_neon(
filepath: str,
wavelengths: Optional[List[float]] = None,
method: str = "nearest",
**kwargs: Any,
) -> xr.Dataset:
"""
Reads NEON AOP hyperspectral hdf5 files and returns an xarray dataset.
Args:
filepath (str): The path to the hdf5 file.
wavelengths (List[float], optional): The wavelengths to select. If None,
all wavelengths are selected. Defaults to None.
method (str, optional): The method to use for selection. Defaults to
"nearest".
**kwargs (Any): Additional arguments to pass to the selection method.
Returns:
xr.Dataset: The dataset containing the reflectance data.
"""
with h5py.File(filepath, "r") as f:
# Extract site code dynamically from NEON HDF file metadata
# At the root of `keys` NEON stores the site code, which is the `root` folder at the [0] index of object `keys`
site_code = list(f.keys())[0]
# Access the reflectance data using the site code
site_refl = f[site_code]["Reflectance"]
# Extract wavelengths
wavelengths_list = site_refl["Metadata"]["Spectral_Data"]["Wavelength"][
()
].tolist()
wavelengths_list = [round(num, 2) for num in wavelengths_list]
# Extract EPSG code
epsg_code = site_refl["Metadata"]["Coordinate_System"]["EPSG Code"][()]
epsg_code_number = int(epsg_code.decode("utf-8"))
# Extract map info
mapInfo_string = site_refl["Metadata"]["Coordinate_System"]["Map_Info"][
()
].decode("utf-8")
mapInfo_split = mapInfo_string.split(",")
res = float(mapInfo_split[5]), float(mapInfo_split[6])
# Extract reflectance array and shape
site_reflArray = site_refl["Reflectance_Data"]
refl_shape = site_reflArray.shape
# Calculate coordinates
xMin = float(mapInfo_split[3])
yMax = float(mapInfo_split[4])
xMax = xMin + (refl_shape[1] * res[0])
yMin = yMax - (refl_shape[0] * res[1])
# Handle scale factor and no-data value
scaleFactor = site_reflArray.attrs["Scale_Factor"]
noDataValue = site_reflArray.attrs["Data_Ignore_Value"]
da = site_reflArray[:, :, :].astype(float)
da[da == int(noDataValue)] = np.nan
da[da < 0] = np.nan
da[da > 10000] = np.nan
da = da / scaleFactor
coords = {
"y": np.linspace(yMax, yMin, da.shape[0]),
"x": np.linspace(xMin, xMax, da.shape[1]),
"wavelength": wavelengths_list,
}
xda = xr.DataArray(
da,
coords=coords,
dims=["y", "x", "wavelength"],
attrs={
"scale_factor": scaleFactor,
"no_data_value": noDataValue,
"crs": f"EPSG:{epsg_code_number}",
"transform": (res[0], 0.0, xMin, 0.0, -res[1], yMax),
},
)
if wavelengths is not None:
xda = xda.sel(wavelength=wavelengths, method=method, **kwargs)
dataset = xda.to_dataset(name="reflectance")
dataset.attrs = dataset["reflectance"].attrs
return dataset