async-tiff 0.3.0__cp310-cp310-macosx_11_0_arm64.whl → 0.5.0b2__cp310-cp310-macosx_11_0_arm64.whl

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.
async_tiff/__init__.py CHANGED
@@ -1,9 +1,31 @@
1
- from typing import TYPE_CHECKING
2
-
3
- from ._async_tiff import *
4
- from ._async_tiff import ___version
5
-
6
- if TYPE_CHECKING:
7
- from . import store
1
+ from . import enums, store
2
+ from ._async_tiff import (
3
+ TIFF,
4
+ Array,
5
+ Colormap,
6
+ DecoderRegistry,
7
+ GeoKeyDirectory,
8
+ ImageFileDirectory,
9
+ ThreadPool,
10
+ Tile,
11
+ ___version, # noqa: F403 # pyright:ignore[reportAttributeAccessIssue]
12
+ )
13
+ from ._decoder_runtime import Decoder
14
+ from ._input import ObspecInput
8
15
 
9
16
  __version__: str = ___version()
17
+
18
+ __all__ = [
19
+ "enums",
20
+ "store",
21
+ "Array",
22
+ "Colormap",
23
+ "Decoder",
24
+ "DecoderRegistry",
25
+ "GeoKeyDirectory",
26
+ "ImageFileDirectory",
27
+ "ThreadPool",
28
+ "TIFF",
29
+ "ObspecInput",
30
+ "Tile",
31
+ ]
async_tiff/_array.pyi ADDED
@@ -0,0 +1,45 @@
1
+ import sys
2
+
3
+ if sys.version_info >= (3, 12):
4
+ from collections.abc import Buffer
5
+ else:
6
+ from typing_extensions import Buffer
7
+
8
+ class Array(Buffer):
9
+ """A 3D array that implements Python's buffer protocol.
10
+
11
+ This allows zero-copy interoperability with numpy via `np.asarray(arr)`.
12
+ The array is immutable and exposes a read-only buffer.
13
+
14
+ Example:
15
+
16
+ ```python
17
+ import numpy as np
18
+ from async_tiff import Array
19
+
20
+ # Create from raw bytes
21
+ data = bytes([1, 2, 3, 4, 5, 6])
22
+ arr = Array(data, shape=(1, 2, 3), format="<B") # 1x2x3 uint8 array
23
+
24
+ # Convert to numpy (zero-copy view)
25
+ np_arr = np.asarray(arr)
26
+ assert np_arr.shape == (1, 2, 3)
27
+ assert np_arr.dtype == np.uint8
28
+ ```
29
+ """
30
+
31
+ # This is intended only for tests
32
+ # def __init__(
33
+ # self, data: Buffer, shape: tuple[int, int, int], format: str
34
+ # ) -> None: ...
35
+ def __buffer__(self, flags: int) -> memoryview[int]: ...
36
+ @property
37
+ def shape(self) -> tuple[int, int, int]:
38
+ """The shape of the array.
39
+
40
+ The interpretation depends on the PlanarConfiguration:
41
+
42
+ - PlanarConfiguration=1 (chunky): (height, width, bands)
43
+ - PlanarConfiguration=2 (planar): (bands, height, width)
44
+ """
45
+ ...
@@ -1,8 +1,19 @@
1
- from ._decoder import Decoder as Decoder
2
- from ._decoder import DecoderRegistry as DecoderRegistry
3
- from ._geo import GeoKeyDirectory as GeoKeyDirectory
4
- from ._ifd import ImageFileDirectory as ImageFileDirectory
5
- from ._thread_pool import ThreadPool as ThreadPool
6
- from ._tiff import ObspecInput as ObspecInput
7
- from ._tiff import TIFF as TIFF
8
- from ._tile import Tile as Tile
1
+ from ._array import Array
2
+ from ._colormap import Colormap
3
+ from ._decoder import DecoderRegistry
4
+ from ._geo import GeoKeyDirectory
5
+ from ._ifd import ImageFileDirectory
6
+ from ._thread_pool import ThreadPool
7
+ from ._tiff import TIFF
8
+ from ._tile import Tile
9
+
10
+ __all__ = [
11
+ "Array",
12
+ "Colormap",
13
+ "DecoderRegistry",
14
+ "GeoKeyDirectory",
15
+ "ImageFileDirectory",
16
+ "ThreadPool",
17
+ "TIFF",
18
+ "Tile",
19
+ ]
@@ -0,0 +1,14 @@
1
+ import sys
2
+
3
+ if sys.version_info >= (3, 12):
4
+ from collections.abc import Buffer
5
+ else:
6
+ from typing_extensions import Buffer
7
+
8
+ class Colormap(Buffer):
9
+ """A 1D array of u16 values representing a TIFF colormap.
10
+
11
+ Implements Python's buffer protocol for zero-copy access via `np.asarray()`.
12
+ """
13
+ def __buffer__(self, flags: int) -> memoryview[int]: ...
14
+ def __len__(self) -> int: ...
async_tiff/_decoder.pyi CHANGED
@@ -1,20 +1,10 @@
1
- from typing import Protocol
2
- from collections.abc import Buffer
3
-
4
- from .enums import CompressionMethod
5
-
6
- class Decoder(Protocol):
7
- """A custom Python-provided decompression algorithm."""
8
- # In the future, we could pass in photometric interpretation and jpeg tables as
9
- # well.
10
- @staticmethod
11
- def __call__(buffer: Buffer) -> Buffer:
12
- """A callback to decode compressed data."""
1
+ from ._decoder_runtime import Decoder
2
+ from .enums import Compression
13
3
 
14
4
  class DecoderRegistry:
15
5
  """A registry holding multiple decoder methods."""
16
6
  def __init__(
17
- self, custom_decoders: dict[CompressionMethod | int, Decoder] | None = None
7
+ self, custom_decoders: dict[Compression | int, Decoder] | None = None
18
8
  ) -> None:
19
9
  """Construct a new decoder registry.
20
10
 
@@ -0,0 +1,21 @@
1
+ from __future__ import annotations
2
+
3
+ import sys
4
+ from typing import TYPE_CHECKING, Protocol
5
+
6
+ if TYPE_CHECKING:
7
+ if sys.version_info >= (3, 12):
8
+ from collections.abc import Buffer
9
+ else:
10
+ from typing_extensions import Buffer
11
+
12
+
13
+ class Decoder(Protocol):
14
+ """A custom Python-provided decompression algorithm."""
15
+
16
+ # In the future, we could pass in photometric interpretation and jpeg tables as
17
+ # well.
18
+ @staticmethod
19
+ def __call__(buffer: Buffer) -> Buffer:
20
+ """A callback to decode compressed data."""
21
+ ...
async_tiff/_ifd.pyi CHANGED
@@ -1,14 +1,17 @@
1
- from collections.abc import Iterable
1
+ from collections.abc import Iterable, Sequence
2
2
  from typing import Any
3
+
4
+ from ._colormap import Colormap
5
+ from ._geo import GeoKeyDirectory
6
+ from ._tile import Tile
3
7
  from .enums import (
4
- CompressionMethod,
8
+ Compression,
5
9
  PhotometricInterpretation,
6
10
  PlanarConfiguration,
7
11
  Predictor,
8
12
  ResolutionUnit,
9
13
  SampleFormat,
10
14
  )
11
- from ._geo import GeoKeyDirectory
12
15
 
13
16
  Value = int | float | str | tuple[int, int] | list[Value]
14
17
 
@@ -34,11 +37,11 @@ class ImageFileDirectory:
34
37
  @property
35
38
  def bits_per_sample(self) -> list[int]: ...
36
39
  @property
37
- def compression(self) -> CompressionMethod | int:
40
+ def compression(self) -> Compression | int:
38
41
  """Access the compression tag.
39
42
 
40
43
  An `int` will be returned if the compression is not one of the values in
41
- `CompressionMethod`.
44
+ `Compression`.
42
45
  """
43
46
  @property
44
47
  def photometric_interpretation(self) -> PhotometricInterpretation: ...
@@ -114,4 +117,34 @@ class ImageFileDirectory:
114
117
  @property
115
118
  def model_tiepoint(self) -> list[float] | None: ...
116
119
  @property
120
+ def model_transformation(self) -> list[float] | None: ...
121
+ @property
122
+ def gdal_nodata(self) -> str | None: ...
123
+ @property
124
+ def gdal_metadata(self) -> str | None: ...
125
+ @property
117
126
  def other_tags(self) -> dict[int, Value]: ...
127
+ @property
128
+ def colormap(self) -> Colormap | None:
129
+ """The colormap for palette-color images."""
130
+ ...
131
+ async def fetch_tile(self, x: int, y: int) -> Tile:
132
+ """Fetch a single tile.
133
+
134
+ Args:
135
+ x: The column index within the ifd to read from.
136
+ y: The row index within the ifd to read from.
137
+
138
+ Returns:
139
+ Tile response.
140
+ """
141
+ async def fetch_tiles(self, x: Sequence[int], y: Sequence[int]) -> list[Tile]:
142
+ """Fetch multiple tiles concurrently.
143
+
144
+ Args:
145
+ x: The column indexes within the ifd to read from.
146
+ y: The row indexes within the ifd to read from.
147
+
148
+ Returns:
149
+ Tile responses.
150
+ """
async_tiff/_input.py ADDED
@@ -0,0 +1,8 @@
1
+ from typing import Protocol
2
+
3
+ # Fix exports
4
+ from obspec._get import GetRangeAsync, GetRangesAsync
5
+
6
+
7
+ class ObspecInput(GetRangeAsync, GetRangesAsync, Protocol):
8
+ """Supported obspec input to reader."""
async_tiff/_tiff.pyi CHANGED
@@ -1,16 +1,11 @@
1
- from typing import Protocol
2
-
3
- # Fix exports
4
- from obspec._get import GetRangeAsync, GetRangesAsync
1
+ from typing import Sequence
5
2
 
6
3
  from ._ifd import ImageFileDirectory
4
+ from ._input import ObspecInput
7
5
  from ._tile import Tile
8
6
  from .enums import Endianness
9
7
  from .store import ObjectStore
10
8
 
11
- class ObspecInput(GetRangeAsync, GetRangesAsync, Protocol):
12
- """Supported obspec input to reader."""
13
-
14
9
  class TIFF:
15
10
  @classmethod
16
11
  async def open(
@@ -39,6 +34,17 @@ class TIFF:
39
34
  @property
40
35
  def endianness(self) -> Endianness:
41
36
  """The endianness of this TIFF file."""
37
+
38
+ def ifd(self, index: int) -> ImageFileDirectory:
39
+ """Access a specific IFD by index.
40
+
41
+ Args:
42
+ index: The IFD index to access.
43
+
44
+ Returns:
45
+ The requested IFD.
46
+ """
47
+
42
48
  @property
43
49
  def ifds(self) -> list[ImageFileDirectory]:
44
50
  """Access the underlying IFDs of this TIFF.
@@ -57,7 +63,9 @@ class TIFF:
57
63
  Returns:
58
64
  Tile response.
59
65
  """
60
- async def fetch_tiles(self, x: list[int], y: list[int], z: int) -> list[Tile]:
66
+ async def fetch_tiles(
67
+ self, x: Sequence[int], y: Sequence[int], z: int
68
+ ) -> list[Tile]:
61
69
  """Fetch multiple tiles concurrently.
62
70
 
63
71
  Args:
async_tiff/_tile.pyi CHANGED
@@ -1,8 +1,9 @@
1
1
  from collections.abc import Buffer
2
2
 
3
- from .enums import CompressionMethod
3
+ from ._array import Array
4
4
  from ._decoder import DecoderRegistry
5
5
  from ._thread_pool import ThreadPool
6
+ from .enums import Compression
6
7
 
7
8
  class Tile:
8
9
  """A representation of a TIFF image tile."""
@@ -16,20 +17,42 @@ class Tile:
16
17
  def compressed_bytes(self) -> Buffer:
17
18
  """The compressed bytes underlying this tile."""
18
19
  @property
19
- def compression_method(self) -> CompressionMethod | int:
20
+ def compression_method(self) -> Compression | int:
20
21
  """The compression method used by this tile."""
21
- async def decode_async(
22
+ def decode_sync(
23
+ self,
24
+ *,
25
+ decoder_registry: DecoderRegistry | None = None,
26
+ ) -> Array:
27
+ """Decode this tile's data.
28
+
29
+ **Note**: This is a blocking function and will perform the tile decompression on
30
+ the current thread. Prefer using the asynchronous `decode` method, which will
31
+ offload decompression to a thread pool.
32
+
33
+ Keyword Args:
34
+ decoder_registry: the decoders to use for decompression. Defaults to None, in which case a default decoder registry is used.
35
+
36
+ Returns:
37
+ Decoded tile data as an Array instance.
38
+ """
39
+
40
+ async def decode(
22
41
  self,
23
42
  *,
24
43
  decoder_registry: DecoderRegistry | None = None,
25
44
  pool: ThreadPool | None = None,
26
- ) -> Buffer:
45
+ ) -> Array:
27
46
  """Decode this tile's data.
28
47
 
48
+ This is an asynchronous function that will offload the tile decompression to a
49
+ thread pool.
50
+
29
51
  Keyword Args:
30
- decoder_registry: the decoders to use for decompression. Defaults to None.
31
- pool: the thread pool on which to run decompression. Defaults to None.
52
+ decoder_registry: the decoders to use for decompression. Defaults to None, in which case a default decoder registry is used.
53
+ pool: the thread pool on which to run decompression. Defaults to None, in
54
+ which case, a default thread pool is used.
32
55
 
33
56
  Returns:
34
- Decoded tile data as a buffer.
57
+ Decoded tile data as an Array instance.
35
58
  """
async_tiff/enums.py CHANGED
@@ -1,7 +1,7 @@
1
1
  from enum import IntEnum
2
2
 
3
3
 
4
- class CompressionMethod(IntEnum):
4
+ class Compression(IntEnum):
5
5
  """
6
6
  See [TIFF compression
7
7
  tags](https://www.awaresystems.be/imaging/tiff/tifftags/compression.html) for
@@ -19,6 +19,9 @@ class CompressionMethod(IntEnum):
19
19
  Deflate = 8
20
20
  OldDeflate = 0x80B2
21
21
  PackBits = 0x8005
22
+ WebP = 50001
23
+ JPEG2k = 34712
24
+ ZSTD = 0xC350
22
25
 
23
26
 
24
27
  class Endianness(IntEnum):
@@ -57,5 +60,5 @@ class ResolutionUnit(IntEnum):
57
60
  class SampleFormat(IntEnum):
58
61
  Uint = 1
59
62
  Int = 2
60
- IEEEFP = 3
63
+ Float = 3
61
64
  Void = 4
@@ -0,0 +1,103 @@
1
+ Metadata-Version: 2.4
2
+ Name: async-tiff
3
+ Version: 0.5.0b2
4
+ Classifier: Programming Language :: Rust
5
+ Classifier: Programming Language :: Python :: Implementation :: CPython
6
+ Classifier: Programming Language :: Python :: Implementation :: PyPy
7
+ Requires-Dist: obspec>=0.1.0
8
+ Requires-Python: >=3.10
9
+ Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
10
+
11
+ # async-tiff
12
+
13
+ [![PyPI][pypi_badge]][pypi_link]
14
+
15
+ [pypi_badge]: https://badge.fury.io/py/async-tiff.svg
16
+ [pypi_link]: https://pypi.org/project/async-tiff/
17
+
18
+ Fast, low-level async TIFF reader for Python.
19
+
20
+ This documentation is for the Python bindings. [Refer here for the Rust crate documentation](https://docs.rs/async-tiff).
21
+
22
+ For a higher-level API to read GeoTIFF files, visit [`async-geotiff`][async-geotiff].
23
+
24
+ [async-geotiff]: https://developmentseed.org/async-geotiff
25
+
26
+ ## Examples
27
+
28
+ ### Reading NAIP
29
+
30
+ ```py
31
+ from async_tiff import TIFF
32
+ from async_tiff.store import S3Store
33
+
34
+ # You'll also need to provide credentials to access a requester pays bucket
35
+ store = S3Store("naip-visualization", region="us-west-2", request_payer=True)
36
+ path = "ny/2022/60cm/rgb/40073/m_4007307_sw_18_060_20220803.tif"
37
+
38
+ tiff = await TIFF.open(path, store=store)
39
+ primary_ifd = tiff.ifds[0]
40
+
41
+ primary_ifd.geo_key_directory.citation
42
+ # 'NAD83 / UTM zone 18N'
43
+
44
+ primary_ifd.geo_key_directory.projected_type
45
+ # 26918
46
+ # (EPSG code)
47
+
48
+ primary_ifd.sample_format
49
+ # [<SampleFormat.Uint: 1>, <SampleFormat.Uint: 1>, <SampleFormat.Uint: 1>]
50
+
51
+ primary_ifd.bits_per_sample
52
+ # [8, 8, 8]
53
+
54
+ tile = await tiff.fetch_tile(0, 0, 4)
55
+ array = await tile.decode()
56
+
57
+ # Use rasterio and matplotlib for visualization
58
+ import numpy as np
59
+ from rasterio.plot import reshape_as_raster, show
60
+
61
+ # Zero-copy conversion of the rust array into a numpy array
62
+ np_array = np.asarray(array)
63
+
64
+ # Then we need to reshape the "image" axes into "raster" axes
65
+ # https://rasterio.readthedocs.io/en/stable/topics/image_processing.html
66
+ show(reshape_as_raster(np_array), adjust=True)
67
+ ```
68
+
69
+ ![](assets/naip-example.jpg)
70
+
71
+
72
+ ### Reading Sentinel 2 L2A
73
+
74
+ ```py
75
+ import numpy as np
76
+ from async_tiff import TIFF
77
+ from async_tiff.store import S3Store
78
+
79
+ store = S3Store("sentinel-cogs", region="us-west-2", skip_signature=True)
80
+ path = "sentinel-s2-l2a-cogs/12/S/UF/2022/6/S2B_12SUF_20220609_0_L2A/B04.tif"
81
+
82
+ tiff = await TIFF.open(path, store=store)
83
+ primary_ifd = tiff.ifds[0]
84
+ # Text readable citation
85
+ primary_ifd.geo_key_directory.citation
86
+ # EPSG code
87
+ primary_ifd.geo_key_directory.projected_type
88
+
89
+ primary_ifd.sample_format[0]
90
+ # <SampleFormat.Uint: 1>
91
+ primary_ifd.bits_per_sample[0]
92
+ # 16
93
+
94
+ tile = await tiff.fetch_tile(0, 0, 0)
95
+ array = await tile.decode()
96
+
97
+ # Zero-copy conversion of the rust array into a numpy array
98
+ np_array = np.asarray(array)
99
+ np_array.shape
100
+ # (1024, 1024, 1)
101
+ ```
102
+
103
+
@@ -0,0 +1,18 @@
1
+ async_tiff/__init__.py,sha256=hFou5CyaCeqAHIpsxyfNv28t15tehoFv7iLbgchVW54,579
2
+ async_tiff/_array.pyi,sha256=muitV2BmObMPBF1O1mgo9a89posx7-ubsl4XftG5WO4,1257
3
+ async_tiff/_async_tiff.cpython-310-darwin.so,sha256=9tUUodgJcJojxDr64Fil8FDbS3sxR1hF5sHTgSRwEBg,7857008
4
+ async_tiff/_async_tiff.pyi,sha256=I0Z9ey5GYZYfjJB4ZEyPUzHTsw0GcA5KBrAbTiThMc8,410
5
+ async_tiff/_colormap.pyi,sha256=NrXLqUq3U81bI0zG8OLgQwv_JM0YS-MrVgx-1nD6j3g,402
6
+ async_tiff/_decoder.pyi,sha256=tRHzZbZj1V6lDireFpNQ-f2h2tPFqh2ExYu4L6v0Cqc,709
7
+ async_tiff/_decoder_runtime.py,sha256=N2IC7XTgC1TmGmB7ZXCJbrVVSbn6G85cWgJ6k87XpT8,553
8
+ async_tiff/_geo.pyi,sha256=h71Y9zZ5vYxXaXAR4QAYmBJ0FOhLfXMMJA85p474eTE,3451
9
+ async_tiff/_ifd.pyi,sha256=eKH2FAboDE3B5kK2U45j0RsN5MwD3cUeDVF5WTgu0as,4755
10
+ async_tiff/_input.py,sha256=f6FCam4uyKaWXy-npUQ-nePaXeOsWMdXt2WcyH5fhq0,203
11
+ async_tiff/_thread_pool.pyi,sha256=fbDu9kbo8RNlq7bBBxuuu_6ub20L5cAfXBM-J7Y0PC8,181
12
+ async_tiff/_tiff.pyi,sha256=8MtZcmOPjr72lo8ohNuSwRi9N8YRZBtcjZwwJxEAO0Q,2249
13
+ async_tiff/_tile.pyi,sha256=NBUciMH6nan6qQLt30p9msUiY4BKh4ZmW4r9CsqtYho,1961
14
+ async_tiff/enums.py,sha256=C1XnevNtV_Hp5gTU7pLZRsNAfNx-4nu_f1NpbE1dN0U,1030
15
+ async_tiff/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
+ async_tiff-0.5.0b2.dist-info/METADATA,sha256=50rXi50a5Ku3aism82VG4fsq0XisM-DsVDykkRdjGWQ,2741
17
+ async_tiff-0.5.0b2.dist-info/WHEEL,sha256=lgXxQDOXaD3ZlhS7NozX7pozuWR_5xphTDPgxH3-aMg,105
18
+ async_tiff-0.5.0b2.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: maturin (1.10.2)
2
+ Generator: maturin (1.11.5)
3
3
  Root-Is-Purelib: false
4
4
  Tag: cp310-cp310-macosx_11_0_arm64
@@ -1,8 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: async-tiff
3
- Version: 0.3.0
4
- Classifier: Programming Language :: Rust
5
- Classifier: Programming Language :: Python :: Implementation :: CPython
6
- Classifier: Programming Language :: Python :: Implementation :: PyPy
7
- Requires-Dist: obspec>=0.1.0b3
8
- Requires-Python: >=3.10
@@ -1,14 +0,0 @@
1
- async_tiff-0.3.0.dist-info/METADATA,sha256=PTm7vdwh4yt5Y9ZWHHE6qeCj0zt363Ks4UPUA4Sc00U,291
2
- async_tiff-0.3.0.dist-info/WHEEL,sha256=IiUSfnZcnKtfg3tUHJ05sdhsIxnb0hSO-4fdRXTiMXo,105
3
- async_tiff/__init__.py,sha256=KTVCBLwRsYMz1f4fe0uVpKPhfymTMGNXe4WKxrqtjcQ,173
4
- async_tiff/_async_tiff.cpython-310-darwin.so,sha256=I_QB6jt0C3KDqqz44S_X4PRodrIbmoGnTUXR1InTXBQ,7425248
5
- async_tiff/_async_tiff.pyi,sha256=FBpM_a-AvnhF-eA5_zpKDSLWbh4lEFBTz8FxxzpJBrU,371
6
- async_tiff/_decoder.pyi,sha256=GezYsb_FpLOG8d7JN4FO3aC_itMwigBr6cF8qzg05M8,1043
7
- async_tiff/_geo.pyi,sha256=h71Y9zZ5vYxXaXAR4QAYmBJ0FOhLfXMMJA85p474eTE,3451
8
- async_tiff/_ifd.pyi,sha256=UrGR1eoj1K_uDIWL2tP0CX0iCCjFnmhuMDlZEBDOOcE,3784
9
- async_tiff/_thread_pool.pyi,sha256=fbDu9kbo8RNlq7bBBxuuu_6ub20L5cAfXBM-J7Y0PC8,181
10
- async_tiff/_tiff.pyi,sha256=2K_KXMeTwKTPrTLblB6st5SMQicsuDCPGxLAiST2rXU,2151
11
- async_tiff/_tile.pyi,sha256=GzmAw-ysBx_s0JuQsY0AmF7UFDK_NIUMv3xubCATt2U,1102
12
- async_tiff/enums.py,sha256=nvtKnM7IMMu6MxuModr4Ml9fiew7DIR5BaWA1YoFl5c,983
13
- async_tiff/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
- async_tiff-0.3.0.dist-info/RECORD,,