Skip to content

Commit 51fb128

Browse files
committed
Fix bounds and res of rotated image
1 parent 103768d commit 51fb128

File tree

1 file changed

+24
-11
lines changed

1 file changed

+24
-11
lines changed

src/async_geotiff/_geotiff.py

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
from __future__ import annotations
22

3+
import math
34
from dataclasses import dataclass, field
45
from functools import cached_property
5-
from typing import TYPE_CHECKING, Self, cast
6+
from typing import TYPE_CHECKING, Self
67

78
import numpy as np
89
from affine import Affine
@@ -161,17 +162,25 @@ def bounds(self) -> tuple[float, float, float, float]:
161162
lower left x, lower left y, upper right x, upper right y
162163
163164
"""
164-
transform = self.transform
165-
166-
# TODO: Remove type casts once affine supports typing overloads for matmul
167-
# https://github.com/rasterio/affine/pull/137
168-
(left, top) = cast("tuple[float, float]", transform * (0, 0))
169-
(right, bottom) = cast(
170-
"tuple[float, float]",
171-
transform * (self.width, self.height),
165+
# Transform all four corners to handle rotated images correctly
166+
# Pixel coordinates of corners: (x, y, 1) for affine transform
167+
corners_pixel = np.array(
168+
[
169+
[0, 0, 1],
170+
[self.width, 0, 1],
171+
[0, self.height, 1],
172+
[self.width, self.height, 1],
173+
],
172174
)
173175

174-
return (left, bottom, right, top)
176+
# Apply affine transform: transform @ corners_pixel.T
177+
transform_matrix = np.array(self.transform).reshape(3, 3)
178+
corners_geo = (transform_matrix @ corners_pixel.T)[:2].T
179+
180+
min_x, min_y = corners_geo.min(axis=0)
181+
max_x, max_y = corners_geo.max(axis=0)
182+
183+
return (float(min_x), float(min_y), float(max_x), float(max_y))
175184

176185
# @property
177186
# def colorinterp(self) -> list[str]:
@@ -346,7 +355,11 @@ def photometric(self) -> PhotometricInterpretation | None: # noqa: PLR0911
346355
def res(self) -> tuple[float, float]:
347356
"""Return the (width, height) of pixels in the units of its CRS."""
348357
transform = self.transform
349-
return (transform.a, -transform.e)
358+
# For rotated images, resolution is the magnitude of the pixel size
359+
# calculated from the transform matrix components
360+
res_x = math.sqrt(transform.a**2 + transform.d**2)
361+
res_y = math.sqrt(transform.b**2 + transform.e**2)
362+
return (res_x, res_y)
350363

351364
@property
352365
def shape(self) -> tuple[int, int]:

0 commit comments

Comments
 (0)