|
3 | 3 |
|
4 | 4 | import io |
5 | 5 | from dataclasses import replace |
| 6 | +from typing import Any |
6 | 7 |
|
7 | 8 | import cf_xarray # noqa: F401 - Enable cf accessor |
8 | 9 | import morecantile |
|
28 | 29 | MissingParameterError, |
29 | 30 | VariableNotFoundError, |
30 | 31 | check_transparent_pixels, |
| 32 | + max_render_shape, |
31 | 33 | ) |
32 | 34 | from xpublish_tiles.pipeline import ( |
33 | 35 | apply_query, |
34 | 36 | bbox_overlap, |
35 | 37 | pipeline, |
| 38 | + subset_to_bbox, |
36 | 39 | ) |
37 | 40 | from xpublish_tiles.testing.datasets import ( |
38 | 41 | CUBED_SPHERE, |
|
74 | 77 | WGS84_TMS, |
75 | 78 | TileTestParam, |
76 | 79 | ) |
77 | | -from xpublish_tiles.types import ImageFormat, OutputBBox, OutputCRS, QueryParams, RGBData |
| 80 | +from xpublish_tiles.types import ( |
| 81 | + ImageFormat, |
| 82 | + OutputBBox, |
| 83 | + OutputCRS, |
| 84 | + PopulatedRenderContext, |
| 85 | + QueryParams, |
| 86 | + RGBData, |
| 87 | +) |
78 | 88 |
|
79 | 89 |
|
80 | 90 | @st.composite |
@@ -1250,13 +1260,50 @@ def test_apply_query_rgb_keeps_band_dim(): |
1250 | 1260 | array = validated["foo"] |
1251 | 1261 | assert isinstance(array.datatype, RGBData) |
1252 | 1262 | assert array.datatype.band_dim == "rgb" |
1253 | | - assert array.da.dims[0] == "rgb" |
| 1263 | + assert "rgb" in array.da.dims |
1254 | 1264 |
|
1255 | 1265 | # without the rgb variant the band dim is squeezed like any extra dim |
1256 | 1266 | validated = apply_query(ds, variables=["foo"], selectors={"rgb": "red"}) |
1257 | 1267 | assert validated["foo"].da.dims == ("latitude", "longitude") |
1258 | 1268 |
|
1259 | 1269 |
|
| 1270 | +@pytest.mark.asyncio |
| 1271 | +async def test_rgb_lazy_array_stays_sliceable(tmp_path): |
| 1272 | + """The rgb variant must not transpose the lazy array: xarray rewrites a |
| 1273 | + lazy transpose as a vectorized indexer over every element, and zarr then |
| 1274 | + gathers pointwise with full-size int64 index arrays. The band dim is moved |
| 1275 | + first only once the subset is in memory.""" |
| 1276 | + from xarray.core.indexing import LazilyIndexedArray, LazilyVectorizedIndexedArray |
| 1277 | + |
| 1278 | + RGB.create().to_zarr( |
| 1279 | + tmp_path / "rgb.zarr", mode="w", zarr_format=3, consolidated=False |
| 1280 | + ) |
| 1281 | + ds = xr.open_zarr( |
| 1282 | + tmp_path / "rgb.zarr", chunks=None, consolidated=False, zarr_format=3 |
| 1283 | + ) |
| 1284 | + |
| 1285 | + validated = apply_query(ds, variables=["foo"], selectors={}, rgb=True) |
| 1286 | + data: Any = validated["foo"].da.variable._data |
| 1287 | + chain: list[Any] = [data] |
| 1288 | + while hasattr(data, "array"): |
| 1289 | + data = data.array |
| 1290 | + chain.append(data) |
| 1291 | + assert any(isinstance(a, LazilyIndexedArray) for a in chain), chain |
| 1292 | + assert not any(isinstance(a, LazilyVectorizedIndexedArray) for a in chain), chain |
| 1293 | + |
| 1294 | + query = create_query_params(Tile(x=0, y=0, z=0), WEBMERC_TMS, variant="rgb") |
| 1295 | + contexts = await subset_to_bbox( |
| 1296 | + validated, |
| 1297 | + bbox=query.bbox, |
| 1298 | + crs=query.crs, |
| 1299 | + max_shape=max_render_shape(style="raster", width=256, height=256), |
| 1300 | + ) |
| 1301 | + context = contexts["foo"] |
| 1302 | + assert isinstance(context, PopulatedRenderContext) |
| 1303 | + (patch,) = context.patches |
| 1304 | + assert patch.da.dims[0] == "rgb" |
| 1305 | + |
| 1306 | + |
1260 | 1307 | @pytest.mark.asyncio |
1261 | 1308 | async def test_rgb_band_selector_renders_single_band(png_snapshot): |
1262 | 1309 | """Colormap variants pick one band with a selector.""" |
|
0 commit comments