async-geotiff 0.1.0b3__tar.gz → 0.1.0b5__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (23) hide show
  1. async_geotiff-0.1.0b5/PKG-INFO +128 -0
  2. async_geotiff-0.1.0b5/README.md +95 -0
  3. {async_geotiff-0.1.0b3 → async_geotiff-0.1.0b5}/pyproject.toml +23 -7
  4. async_geotiff-0.1.0b5/src/async_geotiff/__init__.py +22 -0
  5. async_geotiff-0.1.0b5/src/async_geotiff/_array.py +121 -0
  6. async_geotiff-0.1.0b5/src/async_geotiff/_fetch.py +154 -0
  7. {async_geotiff-0.1.0b3 → async_geotiff-0.1.0b5}/src/async_geotiff/_geotiff.py +87 -111
  8. {async_geotiff-0.1.0b3 → async_geotiff-0.1.0b5}/src/async_geotiff/_overview.py +44 -13
  9. async_geotiff-0.1.0b5/src/async_geotiff/_read.py +185 -0
  10. async_geotiff-0.1.0b5/src/async_geotiff/_tile.py +26 -0
  11. async_geotiff-0.1.0b5/src/async_geotiff/_transform.py +90 -0
  12. async_geotiff-0.1.0b5/src/async_geotiff/_windows.py +76 -0
  13. async_geotiff-0.1.0b5/src/async_geotiff/colormap.py +104 -0
  14. async_geotiff-0.1.0b5/src/async_geotiff/exceptions.py +5 -0
  15. {async_geotiff-0.1.0b3 → async_geotiff-0.1.0b5}/src/async_geotiff/tms.py +2 -2
  16. async_geotiff-0.1.0b3/PKG-INFO +0 -53
  17. async_geotiff-0.1.0b3/README.md +0 -20
  18. async_geotiff-0.1.0b3/src/async_geotiff/__init__.py +0 -10
  19. {async_geotiff-0.1.0b3 → async_geotiff-0.1.0b5}/LICENSE +0 -0
  20. {async_geotiff-0.1.0b3 → async_geotiff-0.1.0b5}/src/async_geotiff/_crs.py +0 -0
  21. {async_geotiff-0.1.0b3 → async_geotiff-0.1.0b5}/src/async_geotiff/_version.py +0 -0
  22. {async_geotiff-0.1.0b3 → async_geotiff-0.1.0b5}/src/async_geotiff/enums.py +0 -0
  23. {async_geotiff-0.1.0b3 → async_geotiff-0.1.0b5}/src/async_geotiff/py.typed +0 -0
@@ -0,0 +1,128 @@
1
+ Metadata-Version: 2.4
2
+ Name: async-geotiff
3
+ Version: 0.1.0b5
4
+ Summary: Async GeoTIFF reader for Python
5
+ Keywords: geotiff,tiff,async,cog,raster,gis
6
+ Author: Kyle Barron
7
+ Author-email: Kyle Barron <kyle@developmentseed.org>
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Programming Language :: Python :: 3.14
18
+ Classifier: Topic :: Scientific/Engineering :: GIS
19
+ Classifier: Typing :: Typed
20
+ Requires-Dist: affine>=2.4.0
21
+ Requires-Dist: async-tiff>=0.5.0b3
22
+ Requires-Dist: numpy>=2.0
23
+ Requires-Dist: pyproj>=3.3.0
24
+ Requires-Dist: morecantile>=7.0,<8.0 ; extra == 'morecantile'
25
+ Requires-Python: >=3.11
26
+ Project-URL: Changelog, https://github.com/developmentseed/async-geotiff/blob/main/CHANGELOG.md
27
+ Project-URL: Documentation, https://developmentseed.github.io/async-geotiff/
28
+ Project-URL: Homepage, https://github.com/developmentseed/async-geotiff
29
+ Project-URL: Issues, https://github.com/developmentseed/async-geotiff/issues
30
+ Project-URL: Repository, https://github.com/developmentseed/async-geotiff
31
+ Provides-Extra: morecantile
32
+ Description-Content-Type: text/markdown
33
+
34
+ # async-geotiff
35
+
36
+ Fast, async GeoTIFF and [Cloud-Optimized GeoTIFF][cogeo] (COG) reader for Python, wrapping the Rust-based [Async-TIFF][async-tiff] library.
37
+
38
+ [async-tiff]: https://github.com/developmentseed/async-tiff
39
+ [cogeo]: https://cogeo.org/
40
+
41
+ ## Features
42
+
43
+ - Read-only support for GeoTIFF and COG formats.
44
+ - High-level, familiar, easy to use API.
45
+ - Performance-focused:
46
+ - Rust core ensures native performance.
47
+ - CPU-bound tasks like image decoding happen in a thread pool, without blocking the async executor.
48
+ - Buffer protocol integration for zero-copy data sharing between Rust and Python.
49
+ - Lightweight with no GDAL dependency.
50
+ - Integration with [obstore] for efficient data access on object stores.
51
+ - Full type hinting for all operations.
52
+ - Broad decompression support: Deflate, LZW, JPEG, JPEG2000, WebP, ZSTD.
53
+
54
+ **Anti-Features** (features explicitly not in scope):
55
+
56
+ - No pixel resampling.
57
+ - No warping/reprojection.
58
+
59
+ Resampling and warping bring significant additional complexity and are out of scope for this library.
60
+
61
+ [obstore]: https://developmentseed.org/obstore/latest/
62
+ [obspec]: https://developmentseed.org/obspec/latest/
63
+
64
+ ## Example
65
+
66
+ First create a "store", such as an [`S3Store`][S3Store], [`GCSStore`][GCSStore], [`AzureStore`][AzureStore], or [`LocalStore`][LocalStore] for reading data from AWS S3, Google Cloud, Azure Storage, or local files. Refer to [obstore] documentation for more information.
67
+
68
+ [S3Store]: https://developmentseed.org/obstore/latest/api/store/aws/#obstore.store.S3Store
69
+ [GCSStore]: https://developmentseed.org/obstore/latest/api/store/gcs/#obstore.store.GCSStore
70
+ [AzureStore]: https://developmentseed.org/obstore/latest/api/store/azure/#obstore.store.AzureStore
71
+ [LocalStore]: https://developmentseed.org/obstore/latest/api/store/local/#obstore.store.LocalStore
72
+
73
+ ```py
74
+ from obstore.store import S3Store
75
+
76
+ store = S3Store("sentinel-cogs", region="us-west-2", skip_signature=True)
77
+ path = "sentinel-s2-l2a-cogs/12/S/UF/2022/6/S2B_12SUF_20220609_0_L2A/TCI.tif"
78
+ ```
79
+
80
+ Then open a `GeoTIFF`:
81
+
82
+ ```py
83
+ from async_geotiff import GeoTIFF
84
+
85
+ geotiff = await GeoTIFF.open(path, store=store)
86
+ ```
87
+
88
+ On the `GeoTIFF` instance you have metadata about the image, such as its affine transform and Coordinate Reference System:
89
+
90
+ ```py
91
+ geotiff.transform
92
+ # Affine(10.0, 0.0, 300000.0,
93
+ # 0.0, -10.0, 4100040.0)
94
+
95
+ geotiff.crs
96
+ # <Projected CRS: EPSG:32612>
97
+ # Name: WGS 84 / UTM zone 12N
98
+ ```
99
+
100
+ For a COG, you can access the overviews, or reduced resolution versions, of the image:
101
+
102
+ ```py
103
+ # Overviews are ordered from finest to coarsest resolution
104
+ # In this case, access the second-coarsest resolution version of the image
105
+ overview = geotiff.overviews[-2]
106
+ ```
107
+
108
+ Then we can read data from the image. This loads a 512-pixel square from the
109
+ upper-left corner of the selected overview.
110
+
111
+ ```py
112
+ from async_geotiff import Window
113
+
114
+ window = Window(col_off=0, row_off=0, width=512, height=512)
115
+ array = await overview.read(window=window)
116
+ ```
117
+
118
+ This `Array` instance has `data`, `mask`, and some other metadata about the fetched array data.
119
+
120
+ Plot, using [`rasterio.plot.show`](https://rasterio.readthedocs.io/en/stable/api/rasterio.plot.html#rasterio.plot.show) (requires `matplotlib`):
121
+
122
+ ```py
123
+ import rasterio.plot
124
+
125
+ rasterio.plot.show(array.data)
126
+ ```
127
+
128
+ ![](assets/sentinel_2_plot.jpg)
@@ -0,0 +1,95 @@
1
+ # async-geotiff
2
+
3
+ Fast, async GeoTIFF and [Cloud-Optimized GeoTIFF][cogeo] (COG) reader for Python, wrapping the Rust-based [Async-TIFF][async-tiff] library.
4
+
5
+ [async-tiff]: https://github.com/developmentseed/async-tiff
6
+ [cogeo]: https://cogeo.org/
7
+
8
+ ## Features
9
+
10
+ - Read-only support for GeoTIFF and COG formats.
11
+ - High-level, familiar, easy to use API.
12
+ - Performance-focused:
13
+ - Rust core ensures native performance.
14
+ - CPU-bound tasks like image decoding happen in a thread pool, without blocking the async executor.
15
+ - Buffer protocol integration for zero-copy data sharing between Rust and Python.
16
+ - Lightweight with no GDAL dependency.
17
+ - Integration with [obstore] for efficient data access on object stores.
18
+ - Full type hinting for all operations.
19
+ - Broad decompression support: Deflate, LZW, JPEG, JPEG2000, WebP, ZSTD.
20
+
21
+ **Anti-Features** (features explicitly not in scope):
22
+
23
+ - No pixel resampling.
24
+ - No warping/reprojection.
25
+
26
+ Resampling and warping bring significant additional complexity and are out of scope for this library.
27
+
28
+ [obstore]: https://developmentseed.org/obstore/latest/
29
+ [obspec]: https://developmentseed.org/obspec/latest/
30
+
31
+ ## Example
32
+
33
+ First create a "store", such as an [`S3Store`][S3Store], [`GCSStore`][GCSStore], [`AzureStore`][AzureStore], or [`LocalStore`][LocalStore] for reading data from AWS S3, Google Cloud, Azure Storage, or local files. Refer to [obstore] documentation for more information.
34
+
35
+ [S3Store]: https://developmentseed.org/obstore/latest/api/store/aws/#obstore.store.S3Store
36
+ [GCSStore]: https://developmentseed.org/obstore/latest/api/store/gcs/#obstore.store.GCSStore
37
+ [AzureStore]: https://developmentseed.org/obstore/latest/api/store/azure/#obstore.store.AzureStore
38
+ [LocalStore]: https://developmentseed.org/obstore/latest/api/store/local/#obstore.store.LocalStore
39
+
40
+ ```py
41
+ from obstore.store import S3Store
42
+
43
+ store = S3Store("sentinel-cogs", region="us-west-2", skip_signature=True)
44
+ path = "sentinel-s2-l2a-cogs/12/S/UF/2022/6/S2B_12SUF_20220609_0_L2A/TCI.tif"
45
+ ```
46
+
47
+ Then open a `GeoTIFF`:
48
+
49
+ ```py
50
+ from async_geotiff import GeoTIFF
51
+
52
+ geotiff = await GeoTIFF.open(path, store=store)
53
+ ```
54
+
55
+ On the `GeoTIFF` instance you have metadata about the image, such as its affine transform and Coordinate Reference System:
56
+
57
+ ```py
58
+ geotiff.transform
59
+ # Affine(10.0, 0.0, 300000.0,
60
+ # 0.0, -10.0, 4100040.0)
61
+
62
+ geotiff.crs
63
+ # <Projected CRS: EPSG:32612>
64
+ # Name: WGS 84 / UTM zone 12N
65
+ ```
66
+
67
+ For a COG, you can access the overviews, or reduced resolution versions, of the image:
68
+
69
+ ```py
70
+ # Overviews are ordered from finest to coarsest resolution
71
+ # In this case, access the second-coarsest resolution version of the image
72
+ overview = geotiff.overviews[-2]
73
+ ```
74
+
75
+ Then we can read data from the image. This loads a 512-pixel square from the
76
+ upper-left corner of the selected overview.
77
+
78
+ ```py
79
+ from async_geotiff import Window
80
+
81
+ window = Window(col_off=0, row_off=0, width=512, height=512)
82
+ array = await overview.read(window=window)
83
+ ```
84
+
85
+ This `Array` instance has `data`, `mask`, and some other metadata about the fetched array data.
86
+
87
+ Plot, using [`rasterio.plot.show`](https://rasterio.readthedocs.io/en/stable/api/rasterio.plot.html#rasterio.plot.show) (requires `matplotlib`):
88
+
89
+ ```py
90
+ import rasterio.plot
91
+
92
+ rasterio.plot.show(array.data)
93
+ ```
94
+
95
+ ![](assets/sentinel_2_plot.jpg)
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "async-geotiff"
3
- version = "0.1.0-beta.3"
3
+ version = "0.1.0-beta.5"
4
4
  description = "Async GeoTIFF reader for Python"
5
5
  readme = "README.md"
6
6
  authors = [{ name = "Kyle Barron", email = "kyle@developmentseed.org" }]
@@ -22,7 +22,7 @@ classifiers = [
22
22
  keywords = ["geotiff", "tiff", "async", "cog", "raster", "gis"]
23
23
  dependencies = [
24
24
  "affine>=2.4.0",
25
- "async-tiff>=0.4.0",
25
+ "async-tiff>=0.5.0-beta.3",
26
26
  "numpy>=2.0",
27
27
  "pyproj>=3.3.0",
28
28
  ]
@@ -43,6 +43,7 @@ dev = [
43
43
  "build>=1.4.0",
44
44
  "ipykernel>=7.1.0",
45
45
  "jsonschema>=4.26.0",
46
+ "matplotlib>=3.10.8",
46
47
  "morecantile>=7.0.2",
47
48
  "obstore>=0.8.2",
48
49
  "pydantic>=2.12.5",
@@ -51,6 +52,19 @@ dev = [
51
52
  "rasterio>=1.4.4",
52
53
  "types-jsonschema>=4.26.0.20260109",
53
54
  ]
55
+ docs = [
56
+ # Workaround for https://github.com/mkdocs/mkdocs/issues/4032
57
+ "click<8.3",
58
+ "mkdocs-material[imaging]>=9.5.49",
59
+ "mkdocs>=1.6.1",
60
+ "mkdocstrings[python]>=1.0",
61
+ "mike>=2.1.3",
62
+ "griffe-inherited-docstrings>=1.0.1",
63
+ # We use ruff format ourselves, but mkdocstrings requires black to be
64
+ # installed to format signatures in the docs
65
+ "black>=26",
66
+ ]
67
+
54
68
 
55
69
  [build-system]
56
70
  requires = ["uv_build>=0.8.8,<0.9.0"]
@@ -65,6 +79,7 @@ module = [
65
79
  # https://github.com/rasterio/affine/issues/135
66
80
  "affine.*",
67
81
  "async_tiff.store.*",
82
+ "rasterio.*",
68
83
  ]
69
84
  ignore_missing_imports = true
70
85
 
@@ -81,9 +96,10 @@ ignore = [
81
96
 
82
97
  [tool.ruff.lint.per-file-ignores]
83
98
  "tests/*" = [
84
- "ANN001", # annotation in function argument
85
- "ANN201", # return type annotation
86
- "S101", # assert
87
- "SLF001", # private member access
88
- "D", # docstring
99
+ "ANN001", # annotation in function argument
100
+ "ANN201", # return type annotation
101
+ "PLR2004", # Magic value used in comparison
102
+ "S101", # assert
103
+ "SLF001", # private member access
104
+ "D", # docstring
89
105
  ]
@@ -0,0 +1,22 @@
1
+ """Async GeoTIFF and [Cloud-Optimized GeoTIFF][cogeo] (COG) reader for Python.
2
+
3
+ [cogeo]: https://cogeo.org/
4
+ """
5
+
6
+ from . import exceptions
7
+ from ._array import Array
8
+ from ._geotiff import GeoTIFF
9
+ from ._overview import Overview
10
+ from ._tile import Tile
11
+ from ._version import __version__
12
+ from ._windows import Window
13
+
14
+ __all__ = [
15
+ "Array",
16
+ "GeoTIFF",
17
+ "Overview",
18
+ "Tile",
19
+ "Window",
20
+ "__version__",
21
+ "exceptions",
22
+ ]
@@ -0,0 +1,121 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from typing import TYPE_CHECKING, Self
5
+
6
+ import numpy as np
7
+ from async_tiff.enums import PlanarConfiguration
8
+ from numpy.ma import MaskedArray
9
+
10
+ from async_geotiff._transform import TransformMixin
11
+
12
+ if TYPE_CHECKING:
13
+ from affine import Affine
14
+ from async_tiff import Array as AsyncTiffArray
15
+ from numpy.typing import NDArray
16
+ from pyproj.crs import CRS
17
+
18
+
19
+ @dataclass(frozen=True, kw_only=True, eq=False)
20
+ class Array(TransformMixin):
21
+ """An array representation of data from a GeoTIFF."""
22
+
23
+ data: NDArray
24
+ """The array data with shape (bands, height, width)."""
25
+
26
+ mask: NDArray[np.bool_] | None
27
+ """The mask array with shape (height, width), if any.
28
+
29
+ Values of True indicate valid data; False indicates no data.
30
+ """
31
+
32
+ width: int
33
+ """The width of the array in pixels."""
34
+
35
+ height: int
36
+ """The height of the array in pixels."""
37
+
38
+ count: int
39
+ """The number of bands in the array."""
40
+
41
+ transform: Affine
42
+ """The affine transform mapping pixel coordinates to geographic coordinates."""
43
+
44
+ crs: CRS
45
+ """The coordinate reference system of the array."""
46
+
47
+ nodata: float | None = None
48
+ """The nodata value for the array, if any."""
49
+
50
+ @classmethod
51
+ def _create( # noqa: PLR0913
52
+ cls,
53
+ *,
54
+ data: AsyncTiffArray,
55
+ mask: AsyncTiffArray | None,
56
+ planar_configuration: PlanarConfiguration,
57
+ transform: Affine,
58
+ crs: CRS,
59
+ nodata: float | None = None,
60
+ ) -> Self:
61
+ """Create an Array from async_tiff data.
62
+
63
+ Handles axis reordering to ensure data is always in (bands, height, width)
64
+ order, matching rasterio's convention.
65
+ """
66
+ data_arr = np.asarray(data, copy=False)
67
+ if mask is not None:
68
+ mask_arr = np.asarray(mask, copy=False).astype(np.bool_, copy=False)
69
+ assert mask_arr.ndim == 3 # noqa: S101, PLR2004
70
+ assert mask_arr.shape[2] == 1 # noqa: S101
71
+ # This assumes it's always (height, width, 1)
72
+ mask_arr = np.squeeze(mask_arr, axis=2)
73
+ else:
74
+ mask_arr = None
75
+
76
+ assert data_arr.ndim == 3, f"Expected 3D array, got {data_arr.ndim}D" # noqa: S101, PLR2004
77
+
78
+ # async_tiff returns data in the native TIFF order:
79
+ # - Chunky (pixel interleaved): (height, width, bands)
80
+ # - Planar (band interleaved): (bands, height, width)
81
+ # We always want (bands, height, width) to match rasterio.
82
+ if planar_configuration == PlanarConfiguration.Chunky:
83
+ # Transpose from (H, W, C) to (C, H, W)
84
+ data_arr = np.moveaxis(data_arr, -1, 0)
85
+
86
+ count, height, width = data_arr.shape
87
+
88
+ return cls(
89
+ data=data_arr,
90
+ mask=mask_arr,
91
+ width=width,
92
+ height=height,
93
+ count=count,
94
+ transform=transform,
95
+ crs=crs,
96
+ nodata=nodata,
97
+ )
98
+
99
+ def as_masked(self) -> MaskedArray:
100
+ """Return the data as a masked array using the Array mask or nodata value.
101
+
102
+ !!! warning
103
+ In a numpy [`MaskedArray`][numpy.ma.MaskedArray], `True`
104
+ indicates invalid (masked) data and `False` indicates valid data.
105
+
106
+ This is the inverse convention of a GeoTIFF's mask. The boolean array
107
+ [`Array.mask`][async_geotiff.Array.mask] uses `True` for valid data and
108
+ `False` for invalid data.
109
+
110
+ Returns:
111
+ A masked array with the same shape as `data`, where invalid data
112
+ (as indicated by the mask) is masked out.
113
+
114
+ """
115
+ if self.mask is not None:
116
+ return MaskedArray(self.data, mask=~self.mask)
117
+
118
+ if self.nodata is not None:
119
+ return np.ma.masked_equal(self.data, self.nodata)
120
+
121
+ return MaskedArray(self.data)
@@ -0,0 +1,154 @@
1
+ from __future__ import annotations
2
+
3
+ import asyncio
4
+ from typing import TYPE_CHECKING, Protocol
5
+
6
+ from affine import Affine
7
+
8
+ from async_geotiff._array import Array
9
+ from async_geotiff._tile import Tile
10
+ from async_geotiff._transform import HasTransform
11
+
12
+ if TYPE_CHECKING:
13
+ from async_tiff import TIFF, ImageFileDirectory
14
+ from async_tiff import Array as AsyncTiffArray
15
+ from pyproj import CRS
16
+
17
+
18
+ class HasTiffReference(HasTransform, Protocol):
19
+ """Protocol for objects that hold a TIFF reference and can request tiles."""
20
+
21
+ @property
22
+ def _ifd(self) -> ImageFileDirectory:
23
+ """The data IFD for this image (index, IFD)."""
24
+ ...
25
+
26
+ @property
27
+ def _mask_ifd(self) -> ImageFileDirectory | None:
28
+ """The mask IFD for this image (index, IFD), if any."""
29
+ ...
30
+
31
+ @property
32
+ def _tiff(self) -> TIFF:
33
+ """A reference to the underlying TIFF object."""
34
+ ...
35
+
36
+ @property
37
+ def crs(self) -> CRS:
38
+ """The coordinate reference system."""
39
+ ...
40
+
41
+ @property
42
+ def tile_height(self) -> int:
43
+ """The height of tiles in pixels."""
44
+ ...
45
+
46
+ @property
47
+ def tile_width(self) -> int:
48
+ """The width of tiles in pixels."""
49
+ ...
50
+
51
+ @property
52
+ def nodata(self) -> int | float | None:
53
+ """The nodata value for the image, if any."""
54
+ ...
55
+
56
+
57
+ class FetchTileMixin:
58
+ """Mixin for fetching tiles from a GeoTIFF.
59
+
60
+ Classes using this mixin must implement HasTiffReference.
61
+ """
62
+
63
+ async def fetch_tile(
64
+ self: HasTiffReference,
65
+ x: int,
66
+ y: int,
67
+ ) -> Tile:
68
+ tile_fut = self._ifd.fetch_tile(x, y)
69
+
70
+ mask_data: AsyncTiffArray | None = None
71
+ if self._mask_ifd is not None:
72
+ mask_fut = self._mask_ifd.fetch_tile(x, y)
73
+ tile, mask = await asyncio.gather(tile_fut, mask_fut)
74
+ tile_data, mask_data = await asyncio.gather(tile.decode(), mask.decode())
75
+ else:
76
+ tile = await tile_fut
77
+ tile_data = await tile.decode()
78
+
79
+ tile_transform = self.transform * Affine.translation(
80
+ x * self.tile_width,
81
+ y * self.tile_height,
82
+ )
83
+
84
+ array = Array._create( # noqa: SLF001
85
+ data=tile_data,
86
+ mask=mask_data,
87
+ planar_configuration=self._ifd.planar_configuration,
88
+ crs=self.crs,
89
+ transform=tile_transform,
90
+ nodata=self.nodata,
91
+ )
92
+ return Tile(
93
+ x=x,
94
+ y=y,
95
+ _ifd=self._ifd,
96
+ array=array,
97
+ )
98
+
99
+ async def fetch_tiles(
100
+ self: HasTiffReference,
101
+ xs: list[int],
102
+ ys: list[int],
103
+ ) -> list[Tile]:
104
+ """Fetch multiple tiles from this overview.
105
+
106
+ Args:
107
+ xs: The x coordinates of the tiles.
108
+ ys: The y coordinates of the tiles.
109
+
110
+ """
111
+ tiles_fut = self._ifd.fetch_tiles(xs, ys)
112
+
113
+ decoded_masks: list[AsyncTiffArray | None] = [None] * len(xs)
114
+ if self._mask_ifd is not None:
115
+ masks_fut = self._mask_ifd.fetch_tiles(xs, ys)
116
+ tiles, masks = await asyncio.gather(tiles_fut, masks_fut)
117
+
118
+ decoded_tile_futs = [tile.decode() for tile in tiles]
119
+ decoded_mask_futs = [mask.decode() for mask in masks]
120
+ decoded_tiles = await asyncio.gather(*decoded_tile_futs)
121
+ decoded_masks = await asyncio.gather(*decoded_mask_futs)
122
+ else:
123
+ tiles = await tiles_fut
124
+ decoded_tiles = await asyncio.gather(*[tile.decode() for tile in tiles])
125
+
126
+ final_tiles: list[Tile] = []
127
+ for x, y, tile_data, mask_data in zip(
128
+ xs,
129
+ ys,
130
+ decoded_tiles,
131
+ decoded_masks,
132
+ strict=True,
133
+ ):
134
+ tile_transform = self.transform * Affine.translation(
135
+ x * self.tile_width,
136
+ y * self.tile_height,
137
+ )
138
+ array = Array._create( # noqa: SLF001
139
+ data=tile_data,
140
+ mask=mask_data,
141
+ planar_configuration=self._ifd.planar_configuration,
142
+ crs=self.crs,
143
+ transform=tile_transform,
144
+ nodata=self.nodata,
145
+ )
146
+ tile = Tile(
147
+ x=x,
148
+ y=y,
149
+ _ifd=self._ifd,
150
+ array=array,
151
+ )
152
+ final_tiles.append(tile)
153
+
154
+ return final_tiles