Three small metadata follow-ups in xrspatial/reproject/__init__.py after PR #1446 (which added attrs preservation and popped stale grid attrs).
1. Re-emit fresh transform and res for the new output grid
Both reproject() (around line 693) and merge() (around line 1572) currently pop transform and res and never replace them. The output grid is fully known at that point: _compute_output_grid returns res_x, res_y, and bounds (left, bottom, right, top). We can emit:
attrs['res'] = (res_x, res_y)
attrs['transform'] = (res_x, 0.0, left, 0.0, -res_y, top) (rasterio/affine 6-tuple)
The xrspatial/geotiff reader does not put transform or res into attrs itself (it only stores crs_wkt, crs, nodata, etc.), so there is no in-repo convention to match. The 6-tuple form is the rasterio standard and what most external code expects when it sees attrs['transform'] on a DataArray. The tuple (res_x, res_y) is consistent with rasterio's Affine.scale accessors.
2. merge() does not use _find_spatial_dims
Lines 1482 and 1555-1556 hardcode the spatial dims as the last two, which breaks for inputs shaped (y, x, band) or other layouts where the last dims are not spatial. The helper _find_spatial_dims already exists at line 63 and should be used here, matching what reproject() already does.
3. _FillValue propagation
_detect_nodata reads _FillValue as a fallback nodata key, but neither reproject() nor merge() writes _FillValue on output. If the input raster used _FillValue, the output silently switches to nodata only. Round-trip serialization breaks.
Fix: when the input has _FillValue, set both nodata and _FillValue on the output. When it does not, leave it absent.
Tests
Extend TestMetadataPreservation in xrspatial/tests/test_reproject.py covering fresh transform/res emission, _find_spatial_dims use in merge with lat/lon dims, and _FillValue propagation for both reproject() and merge().
Three small metadata follow-ups in
xrspatial/reproject/__init__.pyafter PR #1446 (which added attrs preservation and popped stale grid attrs).1. Re-emit fresh
transformandresfor the new output gridBoth
reproject()(around line 693) andmerge()(around line 1572) currently poptransformandresand never replace them. The output grid is fully known at that point:_compute_output_gridreturnsres_x,res_y, andbounds(left, bottom, right, top). We can emit:attrs['res'] = (res_x, res_y)attrs['transform'] = (res_x, 0.0, left, 0.0, -res_y, top)(rasterio/affine 6-tuple)The
xrspatial/geotiffreader does not puttransformorresintoattrsitself (it only storescrs_wkt,crs,nodata, etc.), so there is no in-repo convention to match. The 6-tuple form is the rasterio standard and what most external code expects when it seesattrs['transform']on a DataArray. The tuple(res_x, res_y)is consistent with rasterio'sAffine.scaleaccessors.2.
merge()does not use_find_spatial_dimsLines 1482 and 1555-1556 hardcode the spatial dims as the last two, which breaks for inputs shaped
(y, x, band)or other layouts where the last dims are not spatial. The helper_find_spatial_dimsalready exists at line 63 and should be used here, matching whatreproject()already does.3.
_FillValuepropagation_detect_nodatareads_FillValueas a fallback nodata key, but neitherreproject()normerge()writes_FillValueon output. If the input raster used_FillValue, the output silently switches tonodataonly. Round-trip serialization breaks.Fix: when the input has
_FillValue, set bothnodataand_FillValueon the output. When it does not, leave it absent.Tests
Extend
TestMetadataPreservationinxrspatial/tests/test_reproject.pycovering fresh transform/res emission,_find_spatial_dimsuse in merge withlat/londims, and_FillValuepropagation for bothreproject()andmerge().