Skip to content

Minimal Python typing interface #19

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 25, 2025
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
3 changes: 3 additions & 0 deletions python/python/async_tiff/_async_tiff.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from ._geo import GeoKeyDirectory as GeoKeyDirectory
from ._ifd import ImageFileDirectory as ImageFileDirectory
from ._tiff import TIFF as TIFF
91 changes: 91 additions & 0 deletions python/python/async_tiff/_geo.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
class GeoKeyDirectory:
@property
def model_type(self) -> int | None: ...
@property
def raster_type(self) -> int | None: ...
@property
def citation(self) -> str | None: ...
@property
def geographic_type(self) -> int | None: ...
@property
def geog_citation(self) -> str | None: ...
@property
def geog_geodetic_datum(self) -> int | None: ...
@property
def geog_prime_meridian(self) -> int | None: ...
@property
def geog_linear_units(self) -> int | None: ...
@property
def geog_linear_unit_size(self) -> float | None: ...
@property
def geog_angular_units(self) -> int | None: ...
@property
def geog_angular_unit_size(self) -> float | None: ...
@property
def geog_ellipsoid(self) -> int | None: ...
@property
def geog_semi_major_axis(self) -> float | None: ...
@property
def geog_semi_minor_axis(self) -> float | None: ...
@property
def geog_inv_flattening(self) -> float | None: ...
@property
def geog_azimuth_units(self) -> int | None: ...
@property
def geog_prime_meridian_long(self) -> float | None: ...
@property
def projected_type(self) -> int | None: ...
@property
def proj_citation(self) -> str | None: ...
@property
def projection(self) -> int | None: ...
@property
def proj_coord_trans(self) -> int | None: ...
@property
def proj_linear_units(self) -> int | None: ...
@property
def proj_linear_unit_size(self) -> float | None: ...
@property
def proj_std_parallel1(self) -> float | None: ...
@property
def proj_std_parallel2(self) -> float | None: ...
@property
def proj_nat_origin_long(self) -> float | None: ...
@property
def proj_nat_origin_lat(self) -> float | None: ...
@property
def proj_false_easting(self) -> float | None: ...
@property
def proj_false_northing(self) -> float | None: ...
@property
def proj_false_origin_long(self) -> float | None: ...
@property
def proj_false_origin_lat(self) -> float | None: ...
@property
def proj_false_origin_easting(self) -> float | None: ...
@property
def proj_false_origin_northing(self) -> float | None: ...
@property
def proj_center_long(self) -> float | None: ...
@property
def proj_center_lat(self) -> float | None: ...
@property
def proj_center_easting(self) -> float | None: ...
@property
def proj_center_northing(self) -> float | None: ...
@property
def proj_scale_at_nat_origin(self) -> float | None: ...
@property
def proj_scale_at_center(self) -> float | None: ...
@property
def proj_azimuth_angle(self) -> float | None: ...
@property
def proj_straight_vert_pole_long(self) -> float | None: ...
@property
def vertical(self) -> int | None: ...
@property
def vertical_citation(self) -> str | None: ...
@property
def vertical_datum(self) -> int | None: ...
@property
def vertical_units(self) -> int | None: ...
98 changes: 98 additions & 0 deletions python/python/async_tiff/_ifd.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
from .enums import (
CompressionMethod,
PhotometricInterpretation,
PlanarConfiguration,
Predictor,
ResolutionUnit,
SampleFormat,
)
from ._geo import GeoKeyDirectory

class ImageFileDirectory:
@property
def new_subfile_type(self) -> int | None: ...
@property
def image_width(self) -> int:
"""The number of columns in the image, i.e., the number of pixels per row."""

@property
def image_height(self) -> int:
"""The number of rows of pixels in the image."""

@property
def bits_per_sample(self) -> list[int]: ...
@property
def compression(self) -> CompressionMethod: ...
@property
def photometric_interpretation(self) -> PhotometricInterpretation: ...
@property
def document_name(self) -> str | None: ...
@property
def image_description(self) -> str | None: ...
@property
def strip_offsets(self) -> list[int] | None: ...
@property
def orientation(self) -> int | None: ...
@property
def samples_per_pixel(self) -> int:
"""
The number of components per pixel.

SamplesPerPixel is usually 1 for bilevel, grayscale, and palette-color images.
SamplesPerPixel is usually 3 for RGB images. If this value is higher,
ExtraSamples should give an indication of the meaning of the additional
channels.
"""

@property
def rows_per_strip(self) -> int | None: ...
@property
def strip_byte_counts(self) -> int | None: ...
@property
def min_sample_value(self) -> int | None: ...
@property
def max_sample_value(self) -> int | None: ...
@property
def x_resolution(self) -> float | None:
"""The number of pixels per ResolutionUnit in the ImageWidth direction."""

@property
def y_resolution(self) -> float | None:
"""The number of pixels per ResolutionUnit in the ImageLength direction."""

@property
def planar_configuration(self) -> PlanarConfiguration: ...
@property
def resolution_unit(self) -> ResolutionUnit | None: ...
@property
def software(self) -> str | None: ...
@property
def date_time(self) -> str | None: ...
@property
def artist(self) -> str | None: ...
@property
def host_computer(self) -> str | None: ...
@property
def predictor(self) -> Predictor | None: ...
@property
def tile_width(self) -> int: ...
@property
def tile_height(self) -> int: ...
@property
def tile_offsets(self) -> list[int]: ...
@property
def tile_byte_counts(self) -> list[int]: ...
@property
def extra_samples(self) -> bytes | None: ...
@property
def sample_format(self) -> list[SampleFormat]: ...
@property
def jpeg_tables(self) -> bytes | None: ...
@property
def copyright(self) -> str | None: ...
@property
def geo_key_directory(self) -> GeoKeyDirectory | None: ...
@property
def model_pixel_scale(self) -> list[float] | None: ...
@property
def model_tiepoint(self) -> list[float] | None: ...
11 changes: 11 additions & 0 deletions python/python/async_tiff/_tiff.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from typing import Any

from ._ifd import ImageFileDirectory

class TIFF:
@classmethod
async def open(
cls, path: str, *, store: Any, prefetch: int | None = 16384
) -> TIFF: ...
@property
def ifds(self) -> list[ImageFileDirectory]: ...
1 change: 1 addition & 0 deletions python/src/tiff.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ impl PyTIFF {
Ok(cog_reader)
}

#[getter]
fn ifds(&self) -> Vec<PyImageFileDirectory> {
let ifds = self.0.ifds();
ifds.as_ref().iter().map(|ifd| ifd.clone().into()).collect()
Expand Down
10 changes: 8 additions & 2 deletions python/tests/test_cog.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
import async_tiff
from time import time
from async_tiff import TIFF
from async_tiff.store import S3Store

store = S3Store("sentinel-cogs", region="us-west-2", skip_signature=True)
path = "sentinel-s2-l2a-cogs/12/S/UF/2022/6/S2B_12SUF_20220609_0_L2A/B04.tif"

# 2 min, 15s
tiff = await TIFF.open(path, store=store, prefetch=32768)
ifds = tiff.ifds()

start = time()
tiff = await TIFF.open(path, store=store, prefetch=32768)
end = time()
end - start

ifds = tiff.ifds
ifd = ifds[0]
ifd.compression
ifd.tile_height
Expand Down
3 changes: 0 additions & 3 deletions src/cog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,8 @@ mod test {
let cog_reader = COGReader::try_open(Box::new(reader.clone())).await.unwrap();

let ifd = &cog_reader.ifds.as_ref()[1];
// dbg!(ifd.geotransform());
dbg!(ifd);
let tile = ifd.get_tile(0, 0, Box::new(reader)).await.unwrap();
std::fs::write("img.buf", tile).unwrap();
// dbg!(tile.len());
}

#[ignore = "local file"]
Expand Down
4 changes: 0 additions & 4 deletions src/ifd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ impl ImageFileDirectories {
next_ifd_offset = ifd.next_ifd_offset();
ifds.push(ifd);
}
dbg!(&ifds[0].compression);

Ok(Self { ifds })
}
Expand Down Expand Up @@ -771,7 +770,6 @@ impl ImageFileDirectory {
async fn read_tag(cursor: &mut AsyncCursor) -> Result<(Tag, Value)> {
let code = cursor.read_u16().await?;
let tag_name = Tag::from_u16_exhaustive(code);
// dbg!(&tag_name);

let current_cursor_position = cursor.position();

Expand Down Expand Up @@ -823,8 +821,6 @@ async fn read_tag_value(
// 2a: the value is 5-8 bytes and we're in BigTiff mode.
// We don't support bigtiff yet

// dbg!(value_byte_length);
// dbg!(tag_type);
// NOTE: we should only be reading value_byte_length when it's 4 bytes or fewer. Right now
// we're reading even if it's 8 bytes, but then only using the first 4 bytes of this
// buffer.
Expand Down