Skip to content

Commit

Permalink
Implement geobox.zoom_to(resolution=) overload #79
Browse files Browse the repository at this point in the history
  • Loading branch information
Kirill888 committed Mar 28, 2023
1 parent f2feaa8 commit 36ad1b1
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 9 deletions.
11 changes: 8 additions & 3 deletions odc/geo/gcp.py
Expand Up @@ -11,7 +11,7 @@
from .geobox import GeoBox, GeoBoxBase
from .geom import Geometry, multipoint
from .math import Poly2d, affine_from_pts, align_up, resolution_from_affine, unstack_xy
from .types import XY, MaybeInt, Resolution, SomeShape, wh_
from .types import XY, MaybeInt, Resolution, SomeResolution, SomeShape, wh_

SomePointSet = Union[np.ndarray, Geometry, List[Geometry], List[XY[float]]]

Expand Down Expand Up @@ -255,7 +255,12 @@ def zoom_out(self, factor: float) -> "GCPGeoBox":
_shape, _affine = self.compute_zoom_out(factor)
return GCPGeoBox(_shape, self._mapping, _affine)

def zoom_to(self, shape: Union[SomeShape, int, float]) -> "GCPGeoBox":
def zoom_to(
self,
shape: Union[SomeShape, int, float, None] = None,
*,
resolution: Optional[SomeResolution] = None,
) -> "GCPGeoBox":
"""
Compute :py:class:`~odc.geo.geobox.GCPGeoBox` with changed resolution.
Expand All @@ -264,7 +269,7 @@ def zoom_to(self, shape: Union[SomeShape, int, float]) -> "GCPGeoBox":
:returns:
GCPGeoBox covering the same region but with different number of pixels and therefore resolution.
"""
_shape, _affine = self.compute_zoom_to(shape)
_shape, _affine = self.compute_zoom_to(shape, resolution=resolution)
return GCPGeoBox(_shape, self._mapping, _affine)

def __str__(self):
Expand Down
31 changes: 26 additions & 5 deletions odc/geo/geobox.py
Expand Up @@ -309,7 +309,10 @@ def compute_zoom_out(self, factor: float) -> Tuple[Shape2d, Affine]:
return (shape_((ny, nx)), A)

def compute_zoom_to(
self, shape: Union[SomeShape, int, float]
self,
shape: Union[SomeShape, int, float, None] = None,
*,
resolution: Optional[SomeResolution] = None,
) -> Tuple[Shape2d, Affine]:
"""
Change GeoBox shape.
Expand All @@ -319,6 +322,14 @@ def compute_zoom_to(
:returns:
GeoBox covering the same region but with different number of pixels and therefore resolution.
"""
if shape is None:
if resolution is None:
raise ValueError("Have to supply shape or resolution")
new_geobox = GeoBox.from_bbox(
self.boundingbox, resolution=resolution, tight=True
)
return new_geobox.shape, new_geobox.affine

if isinstance(shape, (int, float)):
nmax = max(*self._shape)
return self.compute_zoom_out(nmax / shape)
Expand Down Expand Up @@ -834,7 +845,12 @@ def zoom_out(self, factor: float) -> "GeoBox":
_shape, _affine = self.compute_zoom_out(factor)
return GeoBox(_shape, _affine, self._crs)

def zoom_to(self, shape: Union[SomeShape, int, float]) -> "GeoBox":
def zoom_to(
self,
shape: Union[SomeShape, int, float, None] = None,
*,
resolution: Optional[SomeResolution] = None,
) -> "GeoBox":
"""
Change GeoBox shape.
Expand All @@ -843,7 +859,7 @@ def zoom_to(self, shape: Union[SomeShape, int, float]) -> "GeoBox":
:returns:
GeoBox covering the same region but with different number of pixels and therefore resolution.
"""
_shape, _affine = self.compute_zoom_to(shape)
_shape, _affine = self.compute_zoom_to(shape, resolution=resolution)
return GeoBox(_shape, _affine, self._crs)

def flipy(self) -> "GeoBox":
Expand Down Expand Up @@ -1130,9 +1146,14 @@ def zoom_out(gbox: GeoBox, factor: float) -> GeoBox:
return gbox.zoom_out(factor)


def zoom_to(gbox: GeoBox, shape: SomeShape) -> GeoBox:
def zoom_to(
gbox: GeoBox,
shape: Union[SomeShape, int, float, None] = None,
*,
resolution: Optional[SomeResolution] = None,
) -> GeoBox:
"""Alias for :py:meth:`odc.geo.geobox.GeoBox.zoom_to`."""
return gbox.zoom_to(shape)
return gbox.zoom_to(shape, resolution=resolution)


def rotate(gbox: GeoBox, deg: float) -> GeoBox:
Expand Down
39 changes: 38 additions & 1 deletion tests/test_geobox.py
Expand Up @@ -7,7 +7,7 @@
import pytest
from affine import Affine

from odc.geo import CRS, geom, ixy_, resyx_, wh_, xy_
from odc.geo import CRS, geom, ixy_, resxy_, resyx_, wh_, xy_
from odc.geo.geobox import (
AnchorEnum,
GeoBox,
Expand Down Expand Up @@ -296,6 +296,36 @@ def test_geobox_scale_down():
assert gbox_.extent.contains(gbox.extent)


@pytest.mark.parametrize(
"geobox",
[
GeoBox.from_bbox((-10, -2, 5, 4), "epsg:4326", tight=True, resolution=0.2),
GeoBox.from_bbox((-10, -2, 5, 4), "epsg:3857", tight=True, resolution=1),
GeoBox.from_bbox(
(-10, -2, 5, 4), "epsg:3857", tight=True, resolution=resxy_(1, 2)
),
],
)
@pytest.mark.parametrize("shape", [256, (128, 128), (33, 11)])
def test_zoom_to_shape(geobox: GeoBox, shape):
assert geobox.zoom_to(shape).crs == geobox.crs

if isinstance(shape, int):
assert max(geobox.zoom_to(shape).shape) == shape
else:
assert geobox.zoom_to(shape).shape == shape


def test_zoom_to_resolution():
geobox = GeoBox.from_bbox((-3, -4, 3, 4), epsg4326, resolution=1)
assert geobox.shape == (8, 6) and geobox.resolution.xy == (1, -1)
assert geobox.zoom_to(resolution=2).resolution.xy == (2, -2)
assert geobox.zoom_to(resolution=2).zoom_to(resolution=1) == geobox

with pytest.raises(ValueError):
geobox.zoom_to()


def test_non_st():
A = mkA(rot=10, translation=(-10, 20), shear=0.3)
assert is_affine_st(A) is False
Expand Down Expand Up @@ -337,6 +367,13 @@ def test_from_polygon():
assert 32601 <= gbox.crs.epsg <= 32660


def test_from_polygon_compat_align():
box = geom.box(1, 13, 17, 37, "epsg:4326")
assert GeoBox.from_geopolygon(box, 2, align=xy_(1, 1)) == GeoBox.from_geopolygon(
box, 2, anchor=xy_(0.5, 0.5)
)


def test_from_bbox():
bbox = (1, 13, 17, 37)
shape = (23, 47)
Expand Down

0 comments on commit 36ad1b1

Please sign in to comment.