11from __future__ import annotations
22
3+ from functools import cached_property
34from typing import TYPE_CHECKING , Literal , Self
45
56from affine import Affine
@@ -95,14 +96,18 @@ def block_size(self, bidx: int, i: int, j: int) -> int:
9596 """
9697 raise NotImplementedError ()
9798
98- @property
99+ @cached_property
99100 def bounds (self ) -> tuple [float , float , float , float ]:
100101 """Returns the bounds of the dataset in the units of its coordinate reference system.
101102
102103 Returns:
103104 (lower left x, lower left y, upper right x, upper right y)
104105 """
105- raise NotImplementedError ()
106+ transform = self .transform
107+ (left , top ) = transform * (0 , 0 )
108+ (right , bottom ) = transform * (self .width , self .height )
109+
110+ return (left , bottom , right , top )
106111
107112 @property
108113 def colorinterp (self ) -> list [str ]:
@@ -201,9 +206,13 @@ def is_tiled(self) -> bool:
201206 raise NotImplementedError ()
202207
203208 @property
204- def nodata (self ) -> float | int | None :
209+ def nodata (self ) -> float | None :
205210 """The dataset's single nodata value."""
206- raise NotImplementedError ()
211+ nodata = self ._primary_ifd .gdal_nodata
212+ if nodata is None :
213+ return None
214+
215+ return float (nodata )
207216
208217 @property
209218 def photometric (self ) -> PhotometricInterp | None :
@@ -212,17 +221,18 @@ def photometric(self) -> PhotometricInterp | None:
212221 # https://rasterio.readthedocs.io/en/stable/api/rasterio.enums.html#rasterio.enums.PhotometricInterp
213222 raise NotImplementedError ()
214223
215- @property
224+ @cached_property
216225 def res (self ) -> tuple [float , float ]:
217226 """Returns the (width, height) of pixels in the units of its coordinate reference system."""
218- raise NotImplementedError ()
227+ transform = self .transform
228+ return (transform .a , - transform .e )
219229
220230 @property
221231 def shape (self ) -> tuple [int , int ]:
222232 """Get the shape (height, width) of the full image."""
223- raise NotImplementedError ( )
233+ return ( self . height , self . width )
224234
225- @property
235+ @cached_property
226236 def transform (self ) -> Affine :
227237 """The dataset's georeferencing transformation matrix
228238
@@ -275,12 +285,12 @@ def xy(
275285 self ,
276286 row : int ,
277287 col : int ,
278- offset : Literal ["center" , "ul" , "ur" , "ll" , "lr" ] = "center" ,
288+ offset : Literal ["center" , "ul" , "ur" , "ll" , "lr" ] | str = "center" ,
279289 ) -> tuple [float , float ]:
280290 """Get the coordinates x, y of a pixel at row, col.
281291
282292 The pixel's center is returned by default, but a corner can be returned
283- by setting `offset` to one of `ul, ur, ll, lr `.
293+ by setting `offset` to one of `"ul"`, `"ur"`, `"ll"`, `"lr" `.
284294
285295 Parameters:
286296 row: Pixel row.
@@ -289,7 +299,27 @@ def xy(
289299 pixel or for a corner.
290300
291301 """
292- raise NotImplementedError ()
302+ transform = self .transform
303+
304+ if offset == "center" :
305+ c = col + 0.5
306+ r = row + 0.5
307+ elif offset == "ul" :
308+ c = col
309+ r = row
310+ elif offset == "ur" :
311+ c = col + 1
312+ r = row
313+ elif offset == "ll" :
314+ c = col
315+ r = row + 1
316+ elif offset == "lr" :
317+ c = col + 1
318+ r = row + 1
319+ else :
320+ raise ValueError (f"Invalid offset value: { offset } " )
321+
322+ return transform * (c , r )
293323
294324
295325def has_geokeys (ifd : ImageFileDirectory ) -> bool :
0 commit comments