From 0dee174e4089e6d922a788662bb8c1782dcca320 Mon Sep 17 00:00:00 2001 From: Max Jones <14077947+maxrjones@users.noreply.github.com> Date: Mon, 11 Mar 2024 18:18:38 -0400 Subject: [PATCH 1/3] Change nodata from rioxarray default to nan in pyramid_reproject --- ndpyramid/core.py | 8 +++++++- tests/test_pyramids.py | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/ndpyramid/core.py b/ndpyramid/core.py index 1b3c7f6..0252f06 100644 --- a/ndpyramid/core.py +++ b/ndpyramid/core.py @@ -1,5 +1,6 @@ from __future__ import annotations # noqa: F401 +import contextlib import typing from collections import defaultdict @@ -131,12 +132,17 @@ def pyramid_reproject( dst_transform = projection_model.transform(dim=dim) def reproject(da, var): - return da.rio.reproject( + da = da.rio.reproject( projection_model._crs, resampling=Resampling[resampling_dict[var]], shape=(dim, dim), transform=dst_transform, ) + da = da.where(da != da.rio.nodata) + with contextlib.suppress(KeyError): + del da.attrs['_FillValue'] + del da.encoding['_FillValue'] + return da # create the data array for each level plevels[lkey] = xr.Dataset(attrs=ds.attrs) diff --git a/tests/test_pyramids.py b/tests/test_pyramids.py index 3be0007..333c5f7 100644 --- a/tests/test_pyramids.py +++ b/tests/test_pyramids.py @@ -32,6 +32,7 @@ def test_reprojected_pyramid(temperature, benchmark): assert pyramid.ds.attrs['multiscales'] assert len(pyramid.ds.attrs['multiscales'][0]['datasets']) == levels assert pyramid.ds.attrs['multiscales'][0]['datasets'][0]['crs'] == 'EPSG:3857' + assert np.isnan(pyramid['0'].air.isel(time=0, x=0, y=0).values) pyramid.to_zarr(MemoryStore()) From a87eaa898bf9a8d5848cb7fdc5ff988734f949bd Mon Sep 17 00:00:00 2001 From: Max Jones <14077947+maxrjones@users.noreply.github.com> Date: Tue, 12 Mar 2024 11:04:26 -0400 Subject: [PATCH 2/3] Separate out new test --- tests/test_pyramids.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/tests/test_pyramids.py b/tests/test_pyramids.py index 333c5f7..bd54fdf 100644 --- a/tests/test_pyramids.py +++ b/tests/test_pyramids.py @@ -28,14 +28,23 @@ def test_reprojected_pyramid(temperature, benchmark): pytest.importorskip('rioxarray') levels = 2 temperature = temperature.rio.write_crs('EPSG:4326') - pyramid = benchmark(lambda: pyramid_reproject(temperature, levels=2)) + pyramid = benchmark(lambda: pyramid_reproject(temperature, levels=levels)) assert pyramid.ds.attrs['multiscales'] assert len(pyramid.ds.attrs['multiscales'][0]['datasets']) == levels assert pyramid.ds.attrs['multiscales'][0]['datasets'][0]['crs'] == 'EPSG:3857' - assert np.isnan(pyramid['0'].air.isel(time=0, x=0, y=0).values) pyramid.to_zarr(MemoryStore()) +def test_reprojected_pyramid_fill(temperature, benchmark): + """ + Test for https://github.com/carbonplan/ndpyramid/issues/93. + """ + pytest.importorskip('rioxarray') + temperature = temperature.rio.write_crs('EPSG:4326') + pyramid = benchmark(lambda: pyramid_reproject(temperature, levels=2)) + assert np.isnan(pyramid['0'].air.isel(time=0, x=0, y=0).values) + + @pytest.mark.parametrize('regridder_apply_kws', [None, {'keep_attrs': False}]) def test_regridded_pyramid(temperature, regridder_apply_kws, benchmark): pytest.importorskip('xesmf') From 7d74911762f5db110bd5cdba91c4759354bf7440 Mon Sep 17 00:00:00 2001 From: Max Jones <14077947+maxrjones@users.noreply.github.com> Date: Tue, 12 Mar 2024 12:25:28 -0400 Subject: [PATCH 3/3] Set FillValue before reprojection --- ndpyramid/core.py | 7 ++----- tests/test_pyramids.py | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/ndpyramid/core.py b/ndpyramid/core.py index 0252f06..34e2463 100644 --- a/ndpyramid/core.py +++ b/ndpyramid/core.py @@ -1,10 +1,10 @@ from __future__ import annotations # noqa: F401 -import contextlib import typing from collections import defaultdict import datatree as dt +import numpy as np import xarray as xr from .common import Projection @@ -132,16 +132,13 @@ def pyramid_reproject( dst_transform = projection_model.transform(dim=dim) def reproject(da, var): + da.encoding['_FillValue'] = np.nan da = da.rio.reproject( projection_model._crs, resampling=Resampling[resampling_dict[var]], shape=(dim, dim), transform=dst_transform, ) - da = da.where(da != da.rio.nodata) - with contextlib.suppress(KeyError): - del da.attrs['_FillValue'] - del da.encoding['_FillValue'] return da # create the data array for each level diff --git a/tests/test_pyramids.py b/tests/test_pyramids.py index bd54fdf..e5f60be 100644 --- a/tests/test_pyramids.py +++ b/tests/test_pyramids.py @@ -41,7 +41,7 @@ def test_reprojected_pyramid_fill(temperature, benchmark): """ pytest.importorskip('rioxarray') temperature = temperature.rio.write_crs('EPSG:4326') - pyramid = benchmark(lambda: pyramid_reproject(temperature, levels=2)) + pyramid = benchmark(lambda: pyramid_reproject(temperature, levels=1)) assert np.isnan(pyramid['0'].air.isel(time=0, x=0, y=0).values)