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
8 changes: 6 additions & 2 deletions xrspatial/geotiff/_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,15 @@
MAX_PIXELS_DEFAULT = 1_000_000_000


class PixelSafetyLimitError(ValueError):
"""Raised when a requested TIFF allocation exceeds max_pixels."""


def _check_dimensions(width, height, samples, max_pixels):
"""Raise ValueError if the requested allocation exceeds *max_pixels*."""
"""Raise PixelSafetyLimitError if the request exceeds *max_pixels*."""
total = width * height * samples
if total > max_pixels:
raise ValueError(
raise PixelSafetyLimitError(
f"TIFF image dimensions ({width} x {height} x {samples} = "
f"{total:,} pixels) exceed the safety limit of "
f"{max_pixels:,} pixels. Pass a larger max_pixels value to "
Expand Down
5 changes: 4 additions & 1 deletion xrspatial/geotiff/_vrt.py
Original file line number Diff line number Diff line change
Expand Up @@ -626,7 +626,7 @@ def read_vrt(vrt_path: str, *, window=None,
-------
(np.ndarray, VRTDataset) tuple
"""
from ._reader import read_to_array
from ._reader import PixelSafetyLimitError, read_to_array

with open(vrt_path, 'r') as f:
xml_str = f.read()
Expand Down Expand Up @@ -871,10 +871,13 @@ def read_vrt(vrt_path: str, *, window=None,
src.filename,
window=(read_r0, read_c0, read_r1, read_c1),
band=src.band - 1, # convert 1-based to 0-based
max_pixels=max_pixels,
)
except (
OSError, ValueError, struct.error,
) + _CODEC_DECODE_EXCEPTIONS as e:
if isinstance(e, PixelSafetyLimitError):
raise
# Under XRSPATIAL_GEOTIFF_STRICT=1, surface the read failure
# so partial mosaics are caught in CI. Default mode warns
# once per missing source then continues, preserving the
Expand Down
35 changes: 35 additions & 0 deletions xrspatial/geotiff/tests/test_vrt_source_max_pixels_1796.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
"""VRT source reads must honor the caller's max_pixels budget (#1796)."""
from __future__ import annotations

import os

import numpy as np
import pytest

from xrspatial.geotiff import to_geotiff, read_vrt


def test_vrt_source_read_forwards_max_pixels(tmp_path):
"""A tiny VRT output cannot force an oversized source-window decode."""
src = tmp_path / "tmp_1796_source.tif"
data = np.arange(16, dtype=np.uint8).reshape(4, 4)
to_geotiff(data, str(src), compression='none')

vrt = tmp_path / "tmp_1796_source_cap.vrt"
vrt.write_text(
'<VRTDataset rasterXSize="1" rasterYSize="1">\n'
' <VRTRasterBand dataType="Byte" band="1">\n'
' <SimpleSource>\n'
f' <SourceFilename relativeToVRT="1">{os.path.basename(src)}'
'</SourceFilename>\n'
' <SourceBand>1</SourceBand>\n'
' <SrcRect xOff="0" yOff="0" xSize="4" ySize="4"/>\n'
' <DstRect xOff="0" yOff="0" xSize="1" ySize="1"/>\n'
' </SimpleSource>\n'
' </VRTRasterBand>\n'
'</VRTDataset>\n'
)

with pytest.raises(ValueError, match="exceed"):
read_vrt(str(vrt), max_pixels=1)

Loading