Skip to content

Commit e19d0c2

Browse files
authored
Fetch tiles from Python (#48)
1 parent 8d642b3 commit e19d0c2

File tree

3 files changed

+68
-3
lines changed

3 files changed

+68
-3
lines changed

python/python/async_tiff/_tiff.pyi

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from ._tile import Tile
12
from ._ifd import ImageFileDirectory
23
from .store import ObjectStore
34

@@ -8,3 +9,5 @@ class TIFF:
89
) -> TIFF: ...
910
@property
1011
def ifds(self) -> list[ImageFileDirectory]: ...
12+
async def fetch_tile(self, x: int, y: int, z: int) -> Tile: ...
13+
async def fetch_tiles(self, x: list[int], y: list[int], z: int) -> list[Tile]: ...

python/src/tiff.rs

Lines changed: 59 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,21 @@
1+
use std::sync::Arc;
2+
13
use async_tiff::reader::{AsyncFileReader, ObjectReader, PrefetchReader};
24
use async_tiff::TIFF;
5+
use pyo3::exceptions::PyIndexError;
36
use pyo3::prelude::*;
47
use pyo3::types::PyType;
58
use pyo3_async_runtimes::tokio::future_into_py;
69
use pyo3_object_store::PyObjectStore;
710

11+
use crate::tile::PyTile;
812
use crate::PyImageFileDirectory;
913

1014
#[pyclass(name = "TIFF", frozen)]
11-
pub(crate) struct PyTIFF(TIFF);
15+
pub(crate) struct PyTIFF {
16+
tiff: TIFF,
17+
reader: Arc<dyn AsyncFileReader>,
18+
}
1219

1320
#[pymethods]
1421
impl PyTIFF {
@@ -22,6 +29,7 @@ impl PyTIFF {
2229
prefetch: Option<u64>,
2330
) -> PyResult<Bound<'py, PyAny>> {
2431
let reader = ObjectReader::new(store.into_inner(), path.into());
32+
let object_reader = reader.clone();
2533

2634
let cog_reader = future_into_py(py, async move {
2735
let reader: Box<dyn AsyncFileReader> = if let Some(prefetch) = prefetch {
@@ -33,14 +41,62 @@ impl PyTIFF {
3341
} else {
3442
Box::new(reader)
3543
};
36-
Ok(PyTIFF(TIFF::try_open(reader).await.unwrap()))
44+
Ok(PyTIFF {
45+
tiff: TIFF::try_open(reader).await.unwrap(),
46+
reader: Arc::new(object_reader),
47+
})
3748
})?;
3849
Ok(cog_reader)
3950
}
4051

4152
#[getter]
4253
fn ifds(&self) -> Vec<PyImageFileDirectory> {
43-
let ifds = self.0.ifds();
54+
let ifds = self.tiff.ifds();
4455
ifds.as_ref().iter().map(|ifd| ifd.clone().into()).collect()
4556
}
57+
58+
fn fetch_tile<'py>(
59+
&'py self,
60+
py: Python<'py>,
61+
x: usize,
62+
y: usize,
63+
z: usize,
64+
) -> PyResult<Bound<'py, PyAny>> {
65+
let reader = self.reader.clone();
66+
let ifd = self
67+
.tiff
68+
.ifds()
69+
.as_ref()
70+
.get(z)
71+
.ok_or_else(|| PyIndexError::new_err(format!("No IFD found for z={}", z)))?
72+
// TODO: avoid this clone; add Arc to underlying rust code?
73+
.clone();
74+
future_into_py(py, async move {
75+
let tile = ifd.fetch_tile(x, y, reader.as_ref()).await.unwrap();
76+
Ok(PyTile::new(tile))
77+
})
78+
}
79+
80+
fn fetch_tiles<'py>(
81+
&'py self,
82+
py: Python<'py>,
83+
x: Vec<usize>,
84+
y: Vec<usize>,
85+
z: usize,
86+
) -> PyResult<Bound<'py, PyAny>> {
87+
let reader = self.reader.clone();
88+
let ifd = self
89+
.tiff
90+
.ifds()
91+
.as_ref()
92+
.get(z)
93+
.ok_or_else(|| PyIndexError::new_err(format!("No IFD found for z={}", z)))?
94+
// TODO: avoid this clone; add Arc to underlying rust code?
95+
.clone();
96+
future_into_py(py, async move {
97+
let tiles = ifd.fetch_tiles(&x, &y, reader.as_ref()).await.unwrap();
98+
let py_tiles = tiles.into_iter().map(PyTile::new).collect::<Vec<_>>();
99+
Ok(py_tiles)
100+
})
101+
}
46102
}

python/src/tile.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@ use crate::PyDecoderRegistry;
1313
#[pyclass(name = "Tile")]
1414
pub(crate) struct PyTile(Option<Tile>);
1515

16+
impl PyTile {
17+
pub(crate) fn new(tile: Tile) -> Self {
18+
Self(Some(tile))
19+
}
20+
}
21+
1622
#[pymethods]
1723
impl PyTile {
1824
#[getter]

0 commit comments

Comments
 (0)