From 18a9681144f4b5dbd0a987bf129463387ee9cf30 Mon Sep 17 00:00:00 2001 From: vincentsarago Date: Wed, 24 Jan 2024 12:39:43 +0100 Subject: [PATCH] rename resampling option in xarrayReader --- CHANGES.md | 13 ++++++++ docs/src/readers.md | 4 +-- rio_tiler/io/xarray.py | 49 +++++++++++++++++++++++------ tests/test_io_xarray.py | 68 +++++++++++++++++++++++++++++++++++++++-- 4 files changed, 120 insertions(+), 14 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 8d2d569e..53740217 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,16 @@ +# 6.4.0 (2024-01-24) + +* deprecate `resampling_method` in `rio_tiler.io.xarray.XarrayReader` method and add `reproject_method` (to match the `rio_tiler.io.Reader` options) + + ```python + # before + with XarrayReader(data) as dst: + img = dst.tile(0, 0, 1, resampling_method="cubic") + + # now + with XarrayReader(data) as dst: + img_cubic = dst.tile(0, 0, 1, reproject_method="cubic") + ``` # 6.3.1 (2024-01-22) diff --git a/docs/src/readers.md b/docs/src/readers.md index 24c15535..4d484056 100644 --- a/docs/src/readers.md +++ b/docs/src/readers.md @@ -1064,7 +1064,7 @@ from rio_tiler.io import XarrayReader from rio_tiler.models import ImageData with XarrayReader(data) as src: - # src.tile(tile_x, tile_y, tile_z, tilesize, resampling_method) + # src.tile(tile_x, tile_y, tile_z, tilesize, reproject_method) img = src.tile(1, 2, 3) assert isinstance(img, ImageData) assert img.crs == WEB_MERCATOR_CRS @@ -1077,7 +1077,7 @@ from rio_tiler.io import XarrayReader from rio_tiler.models import ImageData with XarrayReader(data) as src: - # src.part((minx, miny, maxx, maxy), dst_crs, bounds_crs, resampling_method) + # src.part((minx, miny, maxx, maxy), dst_crs, bounds_crs, reproject_method) img = src.part((10, 10, 20, 20)) assert isinstance(img, ImageData) assert img.crs == WGS84_CRS diff --git a/rio_tiler/io/xarray.py b/rio_tiler/io/xarray.py index fd40bec7..f2772e37 100644 --- a/rio_tiler/io/xarray.py +++ b/rio_tiler/io/xarray.py @@ -202,7 +202,8 @@ def tile( tile_y: int, tile_z: int, tilesize: int = 256, - resampling_method: WarpResampling = "nearest", + resampling_method: Optional[WarpResampling] = None, + reproject_method: WarpResampling = "nearest", auto_expand: bool = True, nodata: Optional[NoData] = None, ) -> ImageData: @@ -213,13 +214,22 @@ def tile( tile_y (int): Tile's vertical index. tile_z (int): Tile's zoom level index. tilesize (int, optional): Output image size. Defaults to `256`. - resampling_method (WarpResampling, optional): WarpKernel resampling algorithm. Defaults to `nearest`. + resampling_method (WarpResampling, optional): **DEPRECATED**, WarpKernel resampling algorithm. Defaults to `nearest`. + reproject_method (WarpResampling, optional): WarpKernel resampling algorithm. Defaults to `nearest`. auto_expand (boolean, optional): When True, rioxarray's clip_box will expand clip search if only 1D raster found with clip. When False, will throw `OneDimensionalRaster` error if only 1 x or y data point is found. Defaults to True. + nodata (int or float, optional): Overwrite dataset internal nodata value. Returns: rio_tiler.models.ImageData: ImageData instance with data, mask and tile spatial info. """ + if resampling_method: + warnings.warn( + "`resampling_method` is deprecated and will be removed in rio-tiler 7.0, please use `reproject_method`", + DeprecationWarning, + ) + reproject_method = resampling_method + if not self.tile_exists(tile_x, tile_y, tile_z): raise TileOutsideBounds( f"Tile {tile_z}/{tile_x}/{tile_y} is outside bounds" @@ -242,7 +252,7 @@ def tile( dst_crs, shape=(tilesize, tilesize), transform=from_bounds(*tile_bounds, height=tilesize, width=tilesize), - resampling=Resampling[resampling_method], + resampling=Resampling[reproject_method], nodata=nodata, ) @@ -268,7 +278,8 @@ def part( bbox: BBox, dst_crs: Optional[CRS] = None, bounds_crs: CRS = WGS84_CRS, - resampling_method: WarpResampling = "nearest", + resampling_method: Optional[WarpResampling] = None, + reproject_method: WarpResampling = "nearest", auto_expand: bool = True, nodata: Optional[NoData] = None, ) -> ImageData: @@ -278,13 +289,22 @@ def part( bbox (tuple): Output bounds (left, bottom, right, top) in target crs ("dst_crs"). dst_crs (rasterio.crs.CRS, optional): Overwrite target coordinate reference system. bounds_crs (rasterio.crs.CRS, optional): Bounds Coordinate Reference System. Defaults to `epsg:4326`. - resampling_method (WarpResampling, optional): WarpKernel resampling algorithm. Defaults to `nearest`. + resampling_method (WarpResampling, optional): **DEPRECATED**, WarpKernel resampling algorithm. Defaults to `nearest`. + reproject_method (WarpResampling, optional): WarpKernel resampling algorithm. Defaults to `nearest`. auto_expand (boolean, optional): When True, rioxarray's clip_box will expand clip search if only 1D raster found with clip. When False, will throw `OneDimensionalRaster` error if only 1 x or y data point is found. Defaults to True. + nodata (int or float, optional): Overwrite dataset internal nodata value. Returns: rio_tiler.models.ImageData: ImageData instance with data, mask and input spatial info. """ + if resampling_method: + warnings.warn( + "`resampling_method` is deprecated and will be removed in rio-tiler 7.0, please use `reproject_method`", + DeprecationWarning, + ) + reproject_method = resampling_method + dst_crs = dst_crs or bounds_crs ds = self.input @@ -309,7 +329,7 @@ def part( dst_crs, shape=(h, w), transform=dst_transform, - resampling=Resampling[resampling_method], + resampling=Resampling[reproject_method], nodata=nodata, ) @@ -362,6 +382,7 @@ def point( lon (float): Longitude. lat (float): Latitude. coord_crs (rasterio.crs.CRS, optional): Coordinate Reference System of the input coords. Defaults to `epsg:4326`. + nodata (int or float, optional): Overwrite dataset internal nodata value. Returns: PointData @@ -396,7 +417,8 @@ def feature( shape: Dict, dst_crs: Optional[CRS] = None, shape_crs: CRS = WGS84_CRS, - resampling_method: WarpResampling = "nearest", + resampling_method: Optional[WarpResampling] = None, + reproject_method: WarpResampling = "nearest", nodata: Optional[NoData] = None, ) -> ImageData: """Read part of a dataset defined by a geojson feature. @@ -405,12 +427,21 @@ def feature( shape (dict): Valid GeoJSON feature. dst_crs (rasterio.crs.CRS, optional): Overwrite target coordinate reference system. shape_crs (rasterio.crs.CRS, optional): Input geojson coordinate reference system. Defaults to `epsg:4326`. - resampling_method (WarpResampling, optional): WarpKernel resampling algorithm. Defaults to `nearest`. + resampling_method (WarpResampling, optional): **DEPRECATED**, WarpKernel resampling algorithm. Defaults to `nearest`. + reproject_method (WarpResampling, optional): WarpKernel resampling algorithm. Defaults to `nearest`. + nodata (int or float, optional): Overwrite dataset internal nodata value. Returns: rio_tiler.models.ImageData: ImageData instance with data, mask and input spatial info. """ + if resampling_method: + warnings.warn( + "`resampling_method` is deprecated and will be removed in rio-tiler 7.0, please use `reproject_method`", + DeprecationWarning, + ) + reproject_method = resampling_method + if not dst_crs: dst_crs = shape_crs @@ -434,7 +465,7 @@ def feature( dst_crs, shape=(h, w), transform=dst_transform, - resampling=Resampling[resampling_method], + resampling=Resampling[reproject_method], nodata=nodata, ) diff --git a/tests/test_io_xarray.py b/tests/test_io_xarray.py index 43cf2dd5..e87f38fd 100644 --- a/tests/test_io_xarray.py +++ b/tests/test_io_xarray.py @@ -46,7 +46,7 @@ def test_xarray_reader(): assert img.dataset_statistics == ((arr.min(), arr.max()),) # Tests for auto_expand - ## Test that a high-zoom tile will error with auto_expand=False + # Test that a high-zoom tile will error with auto_expand=False tms = morecantile.tms.get("WebMercatorQuad") zoom = 10 x, y = tms.xy(-170, -80) @@ -58,8 +58,8 @@ def test_xarray_reader(): str(error.value) == "At least one of the clipped raster x,y coordinates has only one point." ) - ## - ## Test that a high-zoom tile will succeed with auto_expand=True (and that is the default) + + # Test that a high-zoom tile will succeed with auto_expand=True (and that is the default) img = dst.tile(tile.x, tile.y, zoom) assert img.count == 1 assert img.width == 256 @@ -262,3 +262,65 @@ def test_xarray_reader_internal_nodata(): assert not img.mask.all() # not all the mask value are set to 255 assert img.array.mask[0, 0, 0] # the top left pixel should be masked assert not img.array.mask[0, 50, 100] # pixel 50,100 shouldn't be masked + + +def test_xarray_reader_resampling(): + """test XarrayReader.""" + arr = numpy.arange(0.0, 33 * 35).reshape(1, 33, 35) + data = xarray.DataArray( + arr, + dims=("time", "y", "x"), + coords={ + "x": list(range(-170, 180, 10)), + "y": list(range(-80, 85, 5)), + "time": [datetime(2022, 1, 1)], + }, + ) + data.attrs.update({"valid_min": arr.min(), "valid_max": arr.max()}) + + data.rio.write_crs("epsg:4326", inplace=True) + + with XarrayReader(data) as dst: + # TILE + # default nearest + img = dst.tile(0, 0, 1) + img_cubic = dst.tile(0, 0, 1, reproject_method="cubic") + assert not numpy.array_equal(img.array, img_cubic.array) + + with pytest.warns(DeprecationWarning): + _ = dst.tile(0, 0, 1, resampling_method="nearest") + + # PART + img = dst.part((-160, -80, 160, 80), dst_crs="epsg:3857") + img_cubic = dst.part( + (-160, -80, 160, 80), dst_crs="epsg:3857", reproject_method="cubic" + ) + assert not numpy.array_equal(img.array, img_cubic.array) + + with pytest.warns(DeprecationWarning): + _ = dst.part((-160, -80, 160, 80), resampling_method="nearest") + + feat = { + "type": "Feature", + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [-180.0, 0], + [-180.0, 85.0511287798066], + [0, 85.0511287798066], + [0, 6.023673383202919e-13], + [-180.0, 0], + ] + ], + }, + "properties": {"title": "XYZ tile (0, 0, 1)"}, + } + + # FEATURE + img = dst.feature(feat, dst_crs="epsg:3857") + img_cubic = dst.feature(feat, dst_crs="epsg:3857", reproject_method="cubic") + assert not numpy.array_equal(img.array, img_cubic.array) + + with pytest.warns(DeprecationWarning): + _ = dst.feature(feat, resampling_method="nearest")