Skip to content

Commit

Permalink
UT create_cutline check callable operator passed rasies exception if …
Browse files Browse the repository at this point in the history
…not one of the accepted methods by rasterio.transform.rowcol
  • Loading branch information
Martino committed Oct 25, 2024
1 parent 251350a commit 43eaee3
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 13 deletions.
4 changes: 4 additions & 0 deletions rio_tiler/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,7 @@ class MissingCRS(RioTilerError):

class InvalidGeographicBounds(RioTilerError):
"""Invalid Geographic bounds."""


class InvalidRowColOperator(RioTilerError):
"""The rasterio rowcol 'op' parameter must be one of: math.floor, math.ceil, or round."""
17 changes: 11 additions & 6 deletions rio_tiler/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@

from rio_tiler.colormap import apply_cmap
from rio_tiler.constants import WEB_MERCATOR_CRS, WGS84_CRS
from rio_tiler.errors import RioTilerError
from rio_tiler.errors import InvalidRowColOperator, RioTilerError
from rio_tiler.types import BBox, ColorMapType, IntervalTuple, RIOResampling


Expand Down Expand Up @@ -122,9 +122,12 @@ def get_array_statistics(
percentiles_names = [f"percentile_{int(p)}" for p in percentiles]

if coverage is not None:
assert coverage.shape == (
data.shape[1],
data.shape[2],
assert (
coverage.shape
== (
data.shape[1],
data.shape[2],
)
), f"Invalid shape ({coverage.shape}) for Coverage, expected {(data.shape[1], data.shape[2])}"

else:
Expand Down Expand Up @@ -649,7 +652,7 @@ def _calculateRatio(
def _convert_to_raster_space(
src_dst: Union[DatasetReader, DatasetWriter, WarpedVRT],
poly_coordinates: List,
op: Callable[[float], int]
op: Callable[[float], int],
) -> List[str]:
polygons = []
for point in poly_coordinates:
Expand Down Expand Up @@ -683,7 +686,9 @@ def create_cutline(

# Validate that the function provided is one of the allowed methods
if op not in {math.floor, math.ceil, round}:
raise RioTilerError("The 'op' parameter must be one of: math.floor, math.ceil, or round.")
raise InvalidRowColOperator(
"The rasterio rowcol 'op' parameter must be one of: math.floor, math.ceil, or round."
)

geometry = _validate_shape_input(geometry)
geom_type = geometry["type"]
Expand Down
9 changes: 7 additions & 2 deletions tests/test_io_rasterio.py
Original file line number Diff line number Diff line change
Expand Up @@ -752,7 +752,9 @@ def test_equality_part_feature():
}
img_feat = src.feature(feat)

cutline = create_cutline(src.dataset, feat, geometry_crs="epsg:4326", op=math.floor)
cutline = create_cutline(
src.dataset, feat, geometry_crs="epsg:4326", op=math.floor
)
bbox = featureBounds(feat)
img_part = src.part(bbox, vrt_options={"cutline": cutline})

Expand All @@ -763,11 +765,14 @@ def test_equality_part_feature():
# I would assume this is due to rounding issue or reprojection of the cutline by GDAL
# After some debugging locally I found out the rasterized mask is more precise
# numpy.testing.assert_array_equal(img_part.mask, img_feat.mask)
# NOTE reply: can this rounding be fixed with a different operator passed to rasterio rowcol?

# Re-Projection
img_feat = src.feature(feat, dst_crs="epsg:3857")

cutline = create_cutline(src.dataset, feat, geometry_crs="epsg:4326", op=math.floor)
cutline = create_cutline(
src.dataset, feat, geometry_crs="epsg:4326", op=math.floor
)
bbox = featureBounds(feat)
img_part = src.part(bbox, vrt_options={"cutline": cutline}, dst_crs="epsg:3857")

Expand Down
26 changes: 21 additions & 5 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

from rio_tiler import colormap, utils
from rio_tiler.constants import WEB_MERCATOR_TMS, WGS84_CRS
from rio_tiler.errors import RioTilerError
from rio_tiler.errors import InvalidRowColOperator, RioTilerError
from rio_tiler.expression import parse_expression
from rio_tiler.io import Reader

Expand Down Expand Up @@ -289,7 +289,9 @@ def test_cutline():
feature_bounds = featureBounds(feat)

with Reader(COGEO) as src:
cutline = utils.create_cutline(src.dataset, feat, geometry_crs="epsg:4326", op=math.floor)
cutline = utils.create_cutline(
src.dataset, feat, geometry_crs="epsg:4326", op=math.floor
)
data, mask = src.part(feature_bounds, vrt_options={"cutline": cutline})
assert not mask.all()

Expand All @@ -315,7 +317,9 @@ def test_cutline():

with Reader(COGEO) as src:
with pytest.raises(RioTilerError):
utils.create_cutline(src.dataset, feat_line, geometry_crs="epsg:4326", op=math.floor)
utils.create_cutline(
src.dataset, feat_line, geometry_crs="epsg:4326", op=math.floor
)

feat_mp = {
"type": "MultiPolygon",
Expand All @@ -342,7 +346,9 @@ def test_cutline():
}

with Reader(COGEO) as src:
c = utils.create_cutline(src.dataset, feat_mp, geometry_crs="epsg:4326", op=math.floor)
c = utils.create_cutline(
src.dataset, feat_mp, geometry_crs="epsg:4326", op=math.floor
)
assert "MULTIPOLYGON" in c

bad_poly = {
Expand All @@ -362,7 +368,9 @@ def test_cutline():

with Reader(COGEO) as src:
with pytest.raises(RioTilerError):
utils.create_cutline(src.dataset, bad_poly, geometry_crs="epsg:4326", op=math.floor)
utils.create_cutline(
src.dataset, bad_poly, geometry_crs="epsg:4326", op=math.floor
)

triangle_over_image_edge = {
"type": "Polygon",
Expand All @@ -388,6 +396,14 @@ def test_cutline():
assert sum(mask[0, :]) == 0 # first line
assert sum(mask[-1, :]) == 0 # last line

# Check create_cutline operator callable is one of rasterio rowcol accepted method
with Reader(COGEO) as src:
invalid_op = abs
with pytest.raises(InvalidRowColOperator):
utils.create_cutline(
src.dataset, bad_poly, geometry_crs="epsg:4326", op=invalid_op
)


def test_parse_expression():
"""test parsing rio-tiler expression."""
Expand Down

0 comments on commit 43eaee3

Please sign in to comment.