Skip to content

Commit

Permalink
patch: fix rasterio 1.4.x regression (#1132)
Browse files Browse the repository at this point in the history
* patch: fix rasterio 1.4.x regression

* remove broken link (#1065)

* fix upload coverage (#1075)
  • Loading branch information
bjlittle authored Oct 3, 2024
1 parent 57fc7ed commit 8cc8d69
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 31 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/ci-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ jobs:
posargs: ["--xvfb-backend xvfb --durations=10"]
include:
- version: "py312"
coverage: "--cov-report=xml --cov=geovista"
coverage: "--cov-report= --cov=geovista"

steps:
- uses: actions/checkout@v4
Expand Down Expand Up @@ -131,13 +131,13 @@ jobs:
- name: "prepare coverage"
if: ${{ matrix.coverage }}
run: |
mv .coverage .coverage${{ strategy.job-index }}
mv .coverage ci-test-coverage${{ strategy.job-index }}
- if: ${{ matrix.coverage }}
uses: actions/upload-artifact@v4
with:
name: coverage-artifacts-${{ github.job }}-${{ strategy.job-index }}
path: ${{ github.workspace }}/.coverage*
path: ${{ github.workspace }}/ci-test-coverage*


coverage:
Expand Down Expand Up @@ -169,7 +169,7 @@ jobs:

- name: "create coverage report"
run: |
coverage combine .coverage*
coverage combine ci-test-coverage*
coverage xml --omit=*/_version.py
- name: "upload coverage report"
Expand Down
14 changes: 13 additions & 1 deletion src/geovista/bridge.py
Original file line number Diff line number Diff line change
Expand Up @@ -867,7 +867,19 @@ def from_tiff(
cols, rows = np.meshgrid(
np.arange(src.width), np.arange(src.height), indexing="xy"
)
xs, ys = rio.transform.xy(src.transform, rows, cols)
# rasterio 1.4.0 (regression) expects 1-D arrays, fixed in 1.4.1
# see https://github.com/rasterio/rasterio/issues/3191
xs, ys = rio.transform.xy(src.transform, rows.flatten(), cols.flatten())

# ensure we have arrays, rather than a list of arrays
xs, ys = np.asanyarray(xs), np.asanyarray(ys)

# ensure shape is maintained (rasterio 1.4.1 regression)
if xs.shape != src.shape:
xs = xs.reshape(src.shape)

if ys.shape != src.shape:
ys = ys.reshape(src.shape)

# create the geotiff mesh
mesh = cls.from_2d(
Expand Down
4 changes: 2 additions & 2 deletions src/geovista/examples/rectilinear/from_1d__oisst.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@
The resulting mesh contains quad cells.
The example uses NOAA/NECI 1/4° Daily Optimum Interpolation Sea Surface Temperature
(OISST) v2.1 Advanced Very High Resolution Radiometer (AVHRR) gridded data
(https://doi.org/10.25921/RE9P-PT57). The data targets the mesh faces/cells.
(OISST) v2.1 Advanced Very High Resolution Radiometer (AVHRR) gridded data.
The data targets the mesh faces/cells.
Note that, a threshold is also applied to remove land ``NaN`` cells, and a
NASA Blue Marble base layer is rendered along with Natural Earth coastlines.
Expand Down
4 changes: 2 additions & 2 deletions src/geovista/examples/rectilinear/from_1d__oisst_eqc.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@
The resulting mesh contains quad cells.
The example uses NOAA/NECI 1/4° Daily Optimum Interpolation Sea Surface Temperature
(OISST) v2.1 Advanced Very High Resolution Radiometer (AVHRR) gridded data
(https://doi.org/10.25921/RE9P-PT57). The data targets the mesh faces/cells.
(OISST) v2.1 Advanced Very High Resolution Radiometer (AVHRR) gridded data.
The data targets the mesh faces/cells.
Note that, a threshold is also applied to remove land ``NaN`` cells, and a
NASA Blue Marble base layer is rendered along with Natural Earth coastlines.
Expand Down
65 changes: 43 additions & 22 deletions tests/bridge/test_from_tiff.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def test_rgb_band_fail(mocker):
_ = Transform.from_tiff(fname, rgb=True)


@pytest.mark.parametrize("rgb", [True])
@pytest.mark.parametrize("rgb", [True, False])
@pytest.mark.parametrize("band", [1, 2, 3])
@pytest.mark.parametrize("unit", ["m", None])
def test_rgb_band(mocker, rgb, band, unit):
Expand All @@ -55,12 +55,14 @@ def test_rgb_band(mocker, rgb, band, unit):
data = mocker.sentinel.data
mocked_read = mocker.MagicMock(return_value=data)
transform = mocker.sentinel.transform
height, width = 2, 3
height, width = pixels_shape = 2, 3
n_pixels = height * width
kwargs = {
"count": band,
"crs": crs,
"height": height,
"read": mocked_read,
"shape": pixels_shape,
"transform": transform,
"units": [unit] * band,
"width": width,
Expand All @@ -74,10 +76,13 @@ def test_rgb_band(mocker, rgb, band, unit):
"numpy.dstack", return_value=mocker.MagicMock(reshape=mocked_reshape)
)

cols, rows = mocker.sentinel.cols, mocker.sentinel.rows
cols_flatten = mocker.sentinel.cols_flatten
rows_flatten = mocker.sentinel.rows_flatten
cols = mocker.MagicMock(flatten=mocker.MagicMock(return_value=cols_flatten))
rows = mocker.MagicMock(flatten=mocker.MagicMock(return_value=rows_flatten))
mocked_meshgrid = mocker.patch("numpy.meshgrid", return_value=(cols, rows))

xs, ys = mocker.sentinel.xs, mocker.sentinel.ys
xs, ys = np.arange(n_pixels), np.arange(n_pixels)
mocked_xy = mocker.patch("rasterio.transform.xy", return_value=(xs, ys))

expected = mocker.sentinel.mesh
Expand All @@ -102,27 +107,35 @@ def test_rgb_band(mocker, rgb, band, unit):
assert len(args) == 2
np.testing.assert_array_equal(args[0], np.arange(width))
np.testing.assert_array_equal(args[1], np.arange(height))
assert mocked_meshgrid.call_args.kwargs == {"indexing": "xy"}

mocked_xy.assert_called_once_with(transform, rows, cols)

kwargs = {"radius": None, "zlevel": None, "zscale": None, "clean": None}
mocked_from_2d.assert_called_once_with(
xs,
ys,
data=data,
name=name.format(units=str(unit)),
crs=crs,
rgb=rgb,
**kwargs,
)
mocked_xy.assert_called_once_with(transform, rows_flatten, cols_flatten)

expected_kwargs = {
"data": data,
"name": name.format(units=str(unit)),
"crs": crs,
"rgb": rgb,
"radius": None,
"zlevel": None,
"zscale": None,
"clean": None,
}
mocked_from_2d.assert_called_once()
args = mocked_from_2d.call_args.args
assert len(args) == 2
assert args[0].shape == pixels_shape
assert args[1].shape == pixels_shape
assert mocked_from_2d.call_args.kwargs == expected_kwargs


@pytest.mark.parametrize("sieve", [True])
@pytest.mark.parametrize("rgb", [False, True])
@pytest.mark.parametrize("masked", [False, True])
def test_extract(mocker, masked, rgb, sieve):
"""Test extract behaviour with and without image masking."""
height, width = 2, 3
pixels_shape = height, width = 2, 3
n_pixels = height * width
band = 3 if rgb else 1
crs = mocker.sentinel.crs
dtypes = ["uint8"] * band
Expand All @@ -145,16 +158,20 @@ def test_extract(mocker, masked, rgb, sieve):
"dtypes": dtypes,
"height": height,
"read": mocked_read,
"shape": pixels_shape,
"transform": transform,
"width": width,
}
dataset = mocker.MagicMock(**kwargs)
mocker.patch("rasterio.open").return_value.__enter__.return_value = dataset

cols, rows = mocker.sentinel.cols, mocker.sentinel.rows
cols_flatten = mocker.sentinel.cols_flatten
rows_flatten = mocker.sentinel.rows_flatten
cols = mocker.MagicMock(flatten=mocker.MagicMock(return_value=cols_flatten))
rows = mocker.MagicMock(flatten=mocker.MagicMock(return_value=rows_flatten))
mocked_meshgrid = mocker.patch("numpy.meshgrid", return_value=(cols, rows))

xs, ys = mocker.sentinel.xs, mocker.sentinel.ys
xs, ys = np.arange(n_pixels), np.arange(n_pixels)
mocked_xy = mocker.patch("rasterio.transform.xy", return_value=(xs, ys))

expected_mesh = mocker.sentinel.mesh
Expand Down Expand Up @@ -186,15 +203,14 @@ def test_extract(mocker, masked, rgb, sieve):
assert len(args) == 2
np.testing.assert_array_equal(args[0], np.arange(width))
np.testing.assert_array_equal(args[1], np.arange(height))
assert mocked_meshgrid.call_args.kwargs == {"indexing": "xy"}

mocked_xy.assert_called_once_with(transform, rows, cols)
mocked_xy.assert_called_once_with(transform, rows_flatten, cols_flatten)

if rgb:
data = np.dstack(data).reshape(-1, band)
mask = mask[0]

mocked_from_2d.assert_called_once()
assert mocked_from_2d.call_args.args == (xs, ys)
expected_kwargs = {
"name": None,
"crs": crs,
Expand All @@ -204,6 +220,11 @@ def test_extract(mocker, masked, rgb, sieve):
"zscale": None,
"clean": None,
}
mocked_from_2d.assert_called_once()
args = mocked_from_2d.call_args.args
assert len(args) == 2
assert args[0].shape == pixels_shape
assert args[1].shape == pixels_shape
kwargs = mocked_from_2d.call_args.kwargs
actual = kwargs.pop("data")
assert kwargs == expected_kwargs
Expand Down

0 comments on commit 8cc8d69

Please sign in to comment.