forked from Open-EO/openeo-processes-dask
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'Open-EO:main' into main
- Loading branch information
Showing
10 changed files
with
179 additions
and
65 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
59 changes: 59 additions & 0 deletions
59
openeo_processes_dask/process_implementations/cubes/indices.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
import xarray as xr | ||
|
||
from openeo_processes_dask.process_implementations.data_model import RasterCube | ||
from openeo_processes_dask.process_implementations.exceptions import ( | ||
BandExists, | ||
DimensionAmbiguous, | ||
NirBandAmbiguous, | ||
RedBandAmbiguous, | ||
) | ||
from openeo_processes_dask.process_implementations.math import normalized_difference | ||
|
||
__all__ = ["ndvi"] | ||
|
||
|
||
def ndvi(data: RasterCube, nir="nir", red="red", target_band=None): | ||
if len(data.openeo.band_dims) == 0: | ||
raise DimensionAmbiguous( | ||
"Dimension of type `bands` is not available or is ambiguous." | ||
) | ||
band_dim = data.openeo.band_dims[0] | ||
available_bands = data.coords[band_dim] | ||
|
||
if nir not in available_bands or red not in available_bands: | ||
try: | ||
data = data.set_xindex("common_name") | ||
except (ValueError, KeyError): | ||
pass | ||
|
||
if ( | ||
nir not in available_bands | ||
and "common_name" in data.xindexes._coord_name_id.keys() | ||
and nir not in data.coords["common_name"].data | ||
): | ||
raise NirBandAmbiguous( | ||
"The NIR band can't be resolved, please specify the specific NIR band name." | ||
) | ||
elif ( | ||
red not in available_bands | ||
and "common_name" in data.xindexes._coord_name_id.keys() | ||
and red not in data.coords["common_name"].data | ||
): | ||
raise RedBandAmbiguous( | ||
"The Red band can't be resolved, please specify the specific Red band name." | ||
) | ||
|
||
nir_band_dim = "common_name" if nir not in available_bands else band_dim | ||
red_band_dim = "common_name" if red not in available_bands else band_dim | ||
|
||
nir_band = data.sel({nir_band_dim: nir}) | ||
red_band = data.sel({red_band_dim: red}) | ||
|
||
nd = normalized_difference(nir_band, red_band) | ||
if target_band is not None: | ||
if target_band in data.coords: | ||
raise BandExists("A band with the specified target name exists.") | ||
nd = nd.expand_dims(band_dim).assign_coords({band_dim: [target_band]}) | ||
nd = xr.merge([data, nd]) | ||
nd.attrs = data.attrs | ||
return nd |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
[tool.poetry] | ||
name = "openeo-processes-dask" | ||
version = "2023.7.1" | ||
version = "2023.7.5" | ||
description = "Python implementations of many OpenEO processes, dask-friendly by default." | ||
authors = ["Lukas Weidenholzer <[email protected]>", "Sean Hoyal <[email protected]>", "Valentina Hutter <[email protected]>"] | ||
maintainers = ["EODC Staff <[email protected]>"] | ||
|
@@ -31,9 +31,8 @@ rasterio = { version = "^1.3.4", optional = true } | |
dask-geopandas = { version = ">=0.2.0,<1", optional = true } | ||
xgboost = { version = "^1.5.1", optional = true } | ||
rioxarray = { version = ">=0.12.0,<1", optional = true } | ||
odc-algo = { version = "==0.2.3", optional = true } | ||
openeo-pg-parser-networkx = { version = ">=2023.5.1", optional = true } | ||
odc-geo = { version = "^0.3.2", optional = true } | ||
odc-geo = { version = ">=0.3.2,<1", optional = true } | ||
stac_validator = { version = ">=3.3.1", optional = true } | ||
stackstac = { version = ">=0.4.3", optional = true } | ||
pystac_client = { version = ">=0.6.1", optional = true } | ||
|
@@ -50,7 +49,6 @@ pytest-cov = "^4.0.0" | |
|
||
[tool.poetry.extras] | ||
implementations = ["geopandas", "xarray", "dask", "rasterio", "dask-geopandas", "rioxarray", "openeo-pg-parser-networkx", "odc-geo", "stackstac", "planetary_computer", "pystac_client", "stac_validator"] | ||
experimental = ["odc-algo"] | ||
ml = ["xgboost"] | ||
|
||
[tool.pytest.ini_options] | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
import numpy as np | ||
import pytest | ||
|
||
from openeo_processes_dask.process_implementations.cubes.indices import ndvi | ||
from openeo_processes_dask.process_implementations.cubes.load import load_stac | ||
from openeo_processes_dask.process_implementations.exceptions import ( | ||
BandExists, | ||
DimensionAmbiguous, | ||
NirBandAmbiguous, | ||
RedBandAmbiguous, | ||
) | ||
from tests.conftest import _random_raster_data | ||
from tests.general_checks import general_output_checks | ||
|
||
|
||
def test_ndvi(bounding_box): | ||
url = "./tests/data/stac/s2_l2a_test_item.json" | ||
input_cube = load_stac( | ||
url=url, | ||
spatial_extent=bounding_box, | ||
bands=["red", "nir"], | ||
).isel({"x": slice(0, 20), "y": slice(0, 20)}) | ||
|
||
# Test whether this works with different band names | ||
input_cube = input_cube.rename({"band": "b"}) | ||
|
||
import dask.array as da | ||
|
||
numpy_data = _random_raster_data(input_cube.data.shape, dtype=np.float64) | ||
|
||
input_cube.data = da.from_array(numpy_data, chunks=("auto", "auto", "auto", -1)) | ||
|
||
output = ndvi(input_cube) | ||
|
||
band_dim = input_cube.openeo.band_dims[0] | ||
assert band_dim not in output.dims | ||
|
||
expected_results = ( | ||
input_cube.sel({band_dim: "nir"}) - input_cube.sel({band_dim: "red"}) | ||
) / (input_cube.sel({band_dim: "nir"}) + input_cube.sel({band_dim: "red"})) | ||
|
||
general_output_checks( | ||
input_cube=input_cube, output_cube=output, expected_results=expected_results | ||
) | ||
|
||
cube_with_resolvable_coords = input_cube.assign_coords( | ||
{band_dim: ["blue", "yellow"]} | ||
) | ||
output = ndvi(cube_with_resolvable_coords) | ||
general_output_checks( | ||
input_cube=cube_with_resolvable_coords, | ||
output_cube=output, | ||
expected_results=expected_results, | ||
) | ||
|
||
with pytest.raises(DimensionAmbiguous): | ||
ndvi(output) | ||
|
||
cube_with_nir_unresolvable = cube_with_resolvable_coords | ||
cube_with_nir_unresolvable.common_name.data = np.array(["blue", "red"]) | ||
|
||
with pytest.raises(NirBandAmbiguous): | ||
ndvi(cube_with_nir_unresolvable) | ||
|
||
cube_with_red_unresolvable = cube_with_resolvable_coords | ||
cube_with_red_unresolvable.common_name.data = np.array(["nir", "yellow"]) | ||
|
||
with pytest.raises(RedBandAmbiguous): | ||
ndvi(cube_with_red_unresolvable) | ||
|
||
cube_with_nothing_resolvable = cube_with_resolvable_coords | ||
cube_with_nothing_resolvable = cube_with_nothing_resolvable.drop_vars("common_name") | ||
with pytest.raises(KeyError): | ||
ndvi(cube_with_nothing_resolvable) | ||
|
||
target_band = "yay" | ||
output_with_extra_dim = ndvi(input_cube, target_band=target_band) | ||
assert len(output_with_extra_dim.dims) == len(output.dims) + 1 | ||
assert ( | ||
len(output_with_extra_dim.coords[band_dim]) | ||
== len(input_cube.coords[band_dim]) + 1 | ||
) | ||
|
||
with pytest.raises(BandExists): | ||
output_with_extra_dim = ndvi(input_cube, target_band="time") |