Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 19 additions & 6 deletions xrspatial/rasterize.py
Original file line number Diff line number Diff line change
Expand Up @@ -1517,6 +1517,12 @@ def _normalize_chunks(chunks, height, width):
else:
rchunk, cchunk = chunks

# Both axes must have positive chunk sizes. A zero would loop forever
# below; a negative would diverge.
if rchunk <= 0 or cchunk <= 0:
raise ValueError(
f"chunks must be positive, got ({rchunk}, {cchunk})")

row_chunks = []
remaining = height
while remaining > 0:
Expand Down Expand Up @@ -2099,9 +2105,9 @@ def rasterize(
name : str, default 'rasterize'
Name for the output DataArray.
resolution : float or (x_res, y_res), optional
Pixel size. When given with ``bounds``, computes ``width`` and
``height`` automatically. A single float uses the same
resolution for both axes.
Pixel size. Must be finite and ``> 0``. When given with
``bounds``, computes ``width`` and ``height`` automatically.
A single float uses the same resolution for both axes.
like : xr.DataArray, optional
Template raster. Width, height, bounds, and dtype are copied
from this array (any can still be overridden explicitly).
Expand Down Expand Up @@ -2130,9 +2136,9 @@ def rasterize(

chunks : int or (int, int), optional
If given, use the dask backend and split the output raster into
tiles of this size ``(row_chunk, col_chunk)``. A single int
uses the same chunk size for both axes. Combined with
``use_cuda`` to select dask+numpy vs dask+cupy.
tiles of this size ``(row_chunk, col_chunk)``. Both axes must be
``> 0``. A single int uses the same chunk size for both axes.
Combined with ``use_cuda`` to select dask+numpy vs dask+cupy.
max_pixels : int, default 1_000_000_000
Safety cap on the resolved output size (``width * height``). The
function raises ``ValueError`` before any host or device
Expand Down Expand Up @@ -2232,6 +2238,13 @@ def rasterize(
x_res = y_res = float(resolution)
else:
x_res, y_res = float(resolution[0]), float(resolution[1])
# Reject non-finite or non-positive resolution before dimension math.
# Without this, inf/-1 quietly produce a 1x1 raster, 0 raises an
# opaque ZeroDivisionError, and nan raises an int-conversion error.
for r in (x_res, y_res):
if not np.isfinite(r) or r <= 0:
raise ValueError(
f"resolution must be finite and > 0, got {resolution!r}")
final_width = max(int(np.ceil((xmax - xmin) / x_res)), 1)
final_height = max(int(np.ceil((ymax - ymin) / y_res)), 1)
elif like_width is not None:
Expand Down
22 changes: 22 additions & 0 deletions xrspatial/tests/test_rasterize.py
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,28 @@ def test_max_pixels_one_over_rejected(self):
width=101, height=100, bounds=(0, 0, 1, 1),
max_pixels=10_000)

@pytest.mark.parametrize("bad", [0, -1, -0.5, float('inf'),
-float('inf'), float('nan')])
def test_invalid_resolution_scalar(self, bad):
with pytest.raises(ValueError, match="resolution must be finite"):
rasterize([(box(0, 0, 1, 1), 1.0)],
resolution=bad, bounds=(0, 0, 1, 1))

@pytest.mark.parametrize("bad", [(0, 1), (1, 0), (-1, 1), (1, float('nan')),
(float('inf'), 1)])
def test_invalid_resolution_tuple(self, bad):
with pytest.raises(ValueError, match="resolution must be finite"):
rasterize([(box(0, 0, 1, 1), 1.0)],
resolution=bad, bounds=(0, 0, 1, 1))

@pytest.mark.parametrize("bad", [0, -1, (0, 1), (1, -1)])
def test_invalid_chunks(self, bad):
# chunks=0 used to hang (issue #2066); negative diverged.
with pytest.raises(ValueError, match="chunks must be positive"):
rasterize([(box(0, 0, 1, 1), 1.0)],
width=10, height=10, bounds=(0, 0, 1, 1),
chunks=bad)


# ---------------------------------------------------------------------------
# all_touched mode
Expand Down
Loading