Skip to content

Commit 6f85358

Browse files
authored
Merge pull request #423 from tonkintaylor/copilot/implement-raster-astype
Implement `Raster.astype`
2 parents 5901b94 + 4c3cb01 commit 6f85358

3 files changed

Lines changed: 93 additions & 1 deletion

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ from rastr import Raster
137137

138138
- [`Raster.apply(func)`](https://rastr.readthedocs.io/en/stable/autoapi/rastr/raster/#rastr.raster.Raster.apply) - apply a function to cell values.
139139
- [`Raster.abs()`](https://rastr.readthedocs.io/en/stable/autoapi/rastr/raster/#rastr.raster.Raster.abs) - absolute value of cell values.
140+
- [`Raster.astype(dtype)`](https://rastr.readthedocs.io/en/stable/autoapi/rastr/raster/#rastr.raster.Raster.astype) - cast cell values to a specified dtype.
140141
- [`Raster.clamp()`](https://rastr.readthedocs.io/en/stable/autoapi/rastr/raster/#rastr.raster.Raster.clamp) - clip cell values to an `(a_min, a_max)` range.
141142
- [`Raster.exp()`](https://rastr.readthedocs.io/en/stable/autoapi/rastr/raster/#rastr.raster.Raster.exp) - exponential of cell values.
142143
- [`Raster.log()`](https://rastr.readthedocs.io/en/stable/autoapi/rastr/raster/#rastr.raster.Raster.log) - logarithm of cell values.

src/rastr/raster.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
from folium import Map
4747
from matplotlib.axes import Axes
4848
from matplotlib.image import AxesImage
49-
from numpy.typing import ArrayLike, NDArray
49+
from numpy.typing import ArrayLike, DTypeLike, NDArray
5050
from rasterio.io import BufferedDatasetWriter, DatasetReader, DatasetWriter
5151
from shapely.geometry.base import BaseGeometry
5252
from typing_extensions import Self
@@ -404,6 +404,21 @@ def clamp(
404404
raster_meta=self.raster_meta,
405405
)
406406

407+
def astype(self, dtype: DTypeLike) -> Self:
408+
"""Cast the raster array to a specified dtype.
409+
410+
Returns a new raster with the array cast to the given dtype. The original
411+
raster is not modified.
412+
413+
Args:
414+
dtype: Target data type (e.g. ``"float32"``, ``np.int16``).
415+
416+
Returns:
417+
A new Raster instance with the array cast to the specified dtype.
418+
"""
419+
cls = self.__class__
420+
return cls(arr=self.arr.astype(dtype), raster_meta=self.raster_meta)
421+
407422
def set_crs(self, crs: CRS | str, *, allow_override: bool = False) -> Self:
408423
"""Set the CRS of the raster without reprojecting.
409424

tests/rastr/test_raster.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1329,6 +1329,82 @@ def test_preserves_dtype(self):
13291329
assert result.arr.dtype == np.int32
13301330
assert result.raster_meta == raster_meta
13311331

1332+
class TestAstype:
1333+
def test_converts_to_float32(self):
1334+
# Arrange
1335+
raster_meta = RasterMeta(
1336+
crs=CRS.from_epsg(2193),
1337+
transform=Affine(1.0, 0.0, 0.0, 0.0, 1.0, 0.0),
1338+
)
1339+
raster = Raster(
1340+
arr=np.array([[1.0, 2.0], [3.0, 4.0]], dtype=np.float64),
1341+
raster_meta=raster_meta,
1342+
)
1343+
1344+
# Act
1345+
result = raster.astype("float32")
1346+
1347+
# Assert
1348+
assert result.arr.dtype == np.float32
1349+
assert result.raster_meta == raster_meta
1350+
1351+
def test_converts_to_int16(self):
1352+
# Arrange
1353+
raster_meta = RasterMeta(
1354+
crs=CRS.from_epsg(2193),
1355+
transform=Affine(1.0, 0.0, 0.0, 0.0, 1.0, 0.0),
1356+
)
1357+
raster = Raster(
1358+
arr=np.array([[1.0, 2.0], [3.0, 4.0]], dtype=np.float64),
1359+
raster_meta=raster_meta,
1360+
)
1361+
1362+
# Act
1363+
result = raster.astype(np.int16)
1364+
1365+
# Assert
1366+
assert result.arr.dtype == np.int16
1367+
np.testing.assert_array_equal(result.arr, np.array([[1, 2], [3, 4]]))
1368+
1369+
def test_preserves_values(self):
1370+
# Arrange
1371+
raster_meta = RasterMeta(
1372+
crs=CRS.from_epsg(2193),
1373+
transform=Affine(1.0, 0.0, 0.0, 0.0, 1.0, 0.0),
1374+
)
1375+
raster = Raster(
1376+
arr=np.array([[1, 2], [3, 4]], dtype=np.int32),
1377+
raster_meta=raster_meta,
1378+
)
1379+
1380+
# Act
1381+
result = raster.astype(np.float64)
1382+
1383+
# Assert
1384+
np.testing.assert_array_equal(
1385+
result.arr, np.array([[1.0, 2.0], [3.0, 4.0]])
1386+
)
1387+
1388+
def test_subclass_return_type(self):
1389+
# Arrange
1390+
class MyRaster(Raster):
1391+
pass
1392+
1393+
raster_meta = RasterMeta(
1394+
crs=CRS.from_epsg(2193),
1395+
transform=Affine(1.0, 0.0, 0.0, 0.0, 1.0, 0.0),
1396+
)
1397+
raster = MyRaster(
1398+
arr=np.array([[1.0, 2.0], [3.0, 4.0]], dtype=np.float64),
1399+
raster_meta=raster_meta,
1400+
)
1401+
1402+
# Act
1403+
result = raster.astype("float32")
1404+
1405+
# Assert
1406+
assert isinstance(result, MyRaster)
1407+
13321408
class TestSetCRS:
13331409
def test_crs_object(self, example_raster: Raster) -> None:
13341410
# Arrange

0 commit comments

Comments
 (0)