Skip to content

Commit

Permalink
Allow float tile_buffer (#405)
Browse files Browse the repository at this point in the history
* Add tests of the expected behaviour

* Add `tile_buffer` `float` support in `COGReader.tile()`

* :facepalm

Co-authored-by: Bernhard Stadlbauer <bstadlbauer@blackshark.ai>
Co-authored-by: vincentsarago <vincent.sarago@gmail.com>
  • Loading branch information
3 people authored Oct 19, 2021
1 parent e1cbc84 commit fe3fd98
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 13 deletions.
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ with COGReader(

* compare dataset bounds and tile bounds in TMS crs in `rio_tiler.io.base.SpatialMixin.tile_exists` method to allow dataset and TMS not compatible with WGS84 crs (https://github.com/cogeotiff/rio-tiler/pull/429)
* use `httpx` package instead of requests (author @rodrigoalmeida94, https://github.com/cogeotiff/rio-tiler/pull/431)
* allow **half pixel** `tile_buffer` around the tile (e.g 0.5 -> 257x257, 1.5 -> 259x259) (author @bstadlbauer, https://github.com/cogeotiff/rio-tiler/pull/405)

**breaking changes**

Expand Down
4 changes: 4 additions & 0 deletions rio_tiler/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ class TileOutsideBounds(RioTilerError):
"""Z-X-Y Tile is outside image bounds."""


class IncorrectTileBuffer(RioTilerError):
"""Tile buffer is a float but not half of an integer"""


class PointOutsideBounds(RioTilerError):
"""Point is outside image bounds."""

Expand Down
41 changes: 28 additions & 13 deletions rio_tiler/io/cogeo.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import attr
import numpy
import rasterio
from morecantile import Tile, TileMatrixSet
from morecantile import BoundingBox, Tile, TileMatrixSet
from rasterio import transform
from rasterio.crs import CRS
from rasterio.enums import Resampling
Expand All @@ -17,8 +17,13 @@
from rasterio.warp import calculate_default_transform, transform_bounds

from .. import reader
from ..constants import WEB_MERCATOR_TMS, WGS84_CRS, BBox, Indexes, NoData
from ..errors import ExpressionMixingWarning, NoOverviewWarning, TileOutsideBounds
from ..constants import WEB_MERCATOR_TMS, WGS84_CRS, BBox, Indexes, NoData, NumType
from ..errors import (
ExpressionMixingWarning,
IncorrectTileBuffer,
NoOverviewWarning,
TileOutsideBounds,
)
from ..expression import apply_expression, parse_expression
from ..models import BandStatistics, ImageData, ImageStatistics, Info
from ..utils import (
Expand Down Expand Up @@ -325,7 +330,7 @@ def tile(
tilesize: int = 256,
indexes: Optional[Indexes] = None,
expression: Optional[str] = None,
tile_buffer: Optional[int] = None,
tile_buffer: Optional[NumType] = None,
**kwargs: Any,
) -> ImageData:
"""Read a Web Map tile from a COG.
Expand All @@ -337,6 +342,7 @@ def tile(
tilesize (int, optional): Output image size. Defaults to `256`.
indexes (int or sequence of int, optional): Band indexes.
expression (str, optional): rio-tiler expression (e.g. b1/b2+b3).
tile_buffer (int or float, optional): Buffer on each side of the given tile. It must be a multiple of `0.5`. Output **tilesize** will be expanded to `tilesize + 2 * tile_buffer` (e.g 0.5 = 257x257, 1.0 = 258x258).
kwargs (optional): Options to forward to the `COGReader.part` method.
Returns:
Expand All @@ -349,16 +355,25 @@ def tile(
)

tile_bounds = self.tms.xy_bounds(Tile(x=tile_x, y=tile_y, z=tile_z))
if tile_buffer:
x_res = (tile_bounds[2] - tile_bounds[0]) / tilesize
y_res = (tile_bounds[3] - tile_bounds[1]) / tilesize
tile_bounds = (
tile_bounds[0] - x_res * tile_buffer,
tile_bounds[1] - y_res * tile_buffer,
tile_bounds[2] + x_res * tile_buffer,
tile_bounds[3] + y_res * tile_buffer,
if tile_buffer is not None:
if tile_buffer % 0.5:
raise IncorrectTileBuffer(
"`tile_buffer` must be a multiple of `0.5` (e.g: 0.5, 1, 1.5, ...)."
)

x_res = (tile_bounds.right - tile_bounds.left) / tilesize
y_res = (tile_bounds.top - tile_bounds.bottom) / tilesize

# Buffered Tile Bounds
tile_bounds = BoundingBox(
tile_bounds.left - x_res * tile_buffer,
tile_bounds.bottom - y_res * tile_buffer,
tile_bounds.right + x_res * tile_buffer,
tile_bounds.top + y_res * tile_buffer,
)
tilesize += tile_buffer * 2

# Buffered Tile Size
tilesize += int(tile_buffer * 2)

return self.part(
tile_bounds,
Expand Down
26 changes: 26 additions & 0 deletions tests/test_io_cogeo.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from rio_tiler.errors import (
AlphaBandWarning,
ExpressionMixingWarning,
IncorrectTileBuffer,
NoOverviewWarning,
TileOutsideBounds,
)
Expand Down Expand Up @@ -257,6 +258,31 @@ def test_tile_invalid_bounds():
cog.tile(38, 24, 7)


def test_tile_with_incorrect_float_buffer():
with pytest.raises(IncorrectTileBuffer):
with COGReader(COGEO) as cog:
cog.tile(43, 24, 7, tile_buffer=0.8)


def test_tile_with_int_buffer():
with COGReader(COGEO) as cog:
data, mask = cog.tile(43, 24, 7, tile_buffer=1)
assert data.shape == (1, 258, 258)
assert mask.all()

with COGReader(COGEO) as cog:
data, mask = cog.tile(43, 24, 7, tile_buffer=0)
assert data.shape == (1, 256, 256)
assert mask.all()


def test_tile_with_correct_float_buffer():
with COGReader(COGEO) as cog:
data, mask = cog.tile(43, 24, 7, tile_buffer=0.5)
assert data.shape == (1, 257, 257)
assert mask.all()


def test_point_valid():
"""Read point."""
lon = -56.624124590533825
Expand Down

0 comments on commit fe3fd98

Please sign in to comment.