├── .clippy.toml ├── src ├── python │ ├── mod.rs │ └── adapters.rs ├── io │ ├── mod.rs │ ├── nvtiff.rs │ └── geotiff.rs └── lib.rs ├── python ├── tests │ ├── test_cog3pio.py │ ├── test_xarray_backend.py │ └── test_io_geotiff.py └── cog3pio │ ├── __init__.py │ └── xarray_backend.py ├── .gitignore ├── docs ├── api.md ├── conf.py ├── myst.yml ├── index.md ├── .readthedocs.yaml ├── quickstart.md └── changelog.md ├── .github └── workflows │ ├── release.yml │ ├── lint.yml │ ├── benchmarks.yml │ └── ci.yml ├── LICENSE-MIT ├── pyproject.toml ├── README.md ├── Cargo.toml ├── .release-plz.toml ├── benches └── read_cog.rs ├── LICENSE-APACHE └── Cargo.lock /.clippy.toml: -------------------------------------------------------------------------------- 1 | doc-valid-idents = ["DLPack", "GeoTIFF", ".."] 2 | -------------------------------------------------------------------------------- /src/python/mod.rs: -------------------------------------------------------------------------------- 1 | /// Adapter interface from Rust to Python 2 | #[cfg(not(doctest))] 3 | pub mod adapters; 4 | -------------------------------------------------------------------------------- /python/tests/test_cog3pio.py: -------------------------------------------------------------------------------- 1 | from packaging.version import Version 2 | 3 | from cog3pio import __version__ 4 | 5 | 6 | def test_version(): 7 | assert Version(version=__version__) >= Version(version="0.0.1") 8 | -------------------------------------------------------------------------------- /src/io/mod.rs: -------------------------------------------------------------------------------- 1 | /// Read and write GeoTIFF files using the [`tiff`] crate (CPU backend) 2 | pub mod geotiff; 3 | /// Read and write GeoTIFF files using the [`nvtiff_sys`] crate (CUDA GPU backend) 4 | #[cfg(feature = "cuda")] 5 | pub mod nvtiff; 6 | -------------------------------------------------------------------------------- /python/cog3pio/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | cog3pio - Cloud-optimized GeoTIFF ... Parallel I/O 3 | """ 4 | 5 | from importlib.metadata import version 6 | 7 | from .cog3pio import CogReader, read_geotiff # noqa: F401 8 | 9 | __doc__ = cog3pio.__doc__ 10 | __version__ = version("cog3pio") # e.g. 0.1.2.dev3+g0ab3cd78 11 | 12 | if hasattr(cog3pio, "__all__"): 13 | __all__ = cog3pio.__all__ 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | 3 | # Byte-compiled / optimized / DLL files 4 | __pycache__/ 5 | .pytest_cache/ 6 | *.py[cod] 7 | 8 | # C extensions 9 | *.so 10 | 11 | # Data files and folders 12 | benches/*.tif 13 | 14 | # Distribution / packaging 15 | .Python 16 | .venv/ 17 | env/ 18 | bin/ 19 | build/ 20 | develop-eggs/ 21 | dist/ 22 | eggs/ 23 | lib/ 24 | lib64/ 25 | parts/ 26 | sdist/ 27 | var/ 28 | include/ 29 | man/ 30 | venv/ 31 | *.egg-info/ 32 | .installed.cfg 33 | *.egg 34 | 35 | # MyST build outputs 36 | _build 37 | -------------------------------------------------------------------------------- /docs/api.md: -------------------------------------------------------------------------------- 1 | # API Reference 2 | 3 | These are the Python docs for `cog3pio`, for the Rust docs, see 4 | . 5 | 6 | ## DLPack 7 | 8 | ```{eval-rst} 9 | .. autoclass:: cog3pio.CogReader 10 | :members: 11 | :special-members: __dlpack__, __dlpack_device__ 12 | ``` 13 | 14 | ## Xarray 15 | 16 | ```{eval-rst} 17 | .. autoclass:: cog3pio.xarray_backend.Cog3pioBackendEntrypoint 18 | ``` 19 | 20 | 21 | ## NumPy 22 | 23 | ```{eval-rst} 24 | .. autofunction:: cog3pio.read_geotiff 25 | ``` 26 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # General configurations. 2 | needs_sphinx = "6.2" 3 | extensions = [ 4 | "myst_parser", 5 | "sphinx_ext_mystmd", 6 | "sphinx.ext.autodoc", 7 | "sphinx.ext.napoleon", 8 | ] 9 | # Options for source files. 10 | exclude_patterns = [ 11 | "_build", 12 | ] 13 | # Options for napoleon. 14 | # https://www.sphinx-doc.org/en/master/usage/extensions/napoleon.html#module-sphinx.ext.napoleon 15 | napoleon_use_admonition_for_examples = True 16 | napoleon_use_rtype = False 17 | napoleon_use_ivar = True 18 | -------------------------------------------------------------------------------- /docs/myst.yml: -------------------------------------------------------------------------------- 1 | # See docs at: https://mystmd.org/guide/frontmatter 2 | version: 1 3 | project: 4 | id: 0c0b30a3-67fd-4260-bea5-1d77fb40395d 5 | title: "cog3pio" 6 | description: "Cloud-optimized GeoTIFF ... Parallel I/O" 7 | # keywords: [] 8 | # authors: [] 9 | github: https://github.com/weiji14/cog3pio 10 | # To autogenerate a Table of Contents, run "jupyter book init --write-toc" 11 | toc: 12 | # Auto-generated by `myst init --write-toc` 13 | - file: index.md 14 | - file: quickstart.md 15 | - pattern: _build/myst-asts/api.myst.json 16 | - file: changelog.md 17 | 18 | site: 19 | template: book-theme 20 | options: 21 | # favicon: favicon.ico 22 | # logo: site_logo.png 23 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | # ⚙️ *cog3pio* - Cloud-optimized GeoTIFF ... Parallel I/O 2 | 3 | *Practice the Three Acts of CoGnizance - Use GeoTIFF-tiles, Be GDAL-less, Go GPU-accelerated* 4 | 5 | ## Installation 6 | 7 | ### Rust 8 | 9 | ```bash 10 | cargo add cog3pio 11 | ``` 12 | 13 | ### Python 14 | 15 | ```bash 16 | pip install cog3pio 17 | ``` 18 | 19 | > [!TIP] 20 | > The API for this crate/library is still unstable and subject to change, so you may 21 | > want to pin to a specific git commit using either: 22 | > - `cargo add --git https://github.com/weiji14/cog3pio.git --rev ` 23 | > - `pip install git+https://github.com/weiji14/cog3pio.git@` 24 | > 25 | > where `` is a commit hashsum obtained from 26 | > https://github.com/weiji14/cog3pio/commits/main 27 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Publish to crates.io 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | permissions: {} 8 | 9 | jobs: 10 | publish-to-crates-io: 11 | name: Publish Rust 🦀 package 📦 to crates.io 12 | runs-on: ubuntu-24.04 13 | environment: cratesio # Optional: for enhanced security 14 | permissions: 15 | id-token: write # Required for OIDC token exchange 16 | contents: read # Required to checkout repository 17 | 18 | steps: 19 | - name: Checkout repository 20 | uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 21 | with: 22 | fetch-depth: 0 23 | persist-credentials: false 24 | 25 | # - name: Authenticate with registry 26 | # uses: rust-lang/crates-io-auth-action@v1 27 | # id: auth 28 | 29 | - name: Publish package 📦 to crates.io 30 | run: cargo publish 31 | env: 32 | CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} # ${{ steps.auth.outputs.token }} 33 | -------------------------------------------------------------------------------- /docs/.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | # Read the Docs configuration file for Sphinx projects 2 | # See https://docs.readthedocs.com/platform/stable/config-file/v2.html for details 3 | 4 | # Required 5 | version: 2 6 | 7 | # Set the OS, Python version and other tools you might need 8 | build: 9 | os: "ubuntu-24.04" 10 | tools: 11 | nodejs: "23" 12 | python: "3.12" 13 | rust: "1.86" 14 | jobs: 15 | install: 16 | - pip install .[docs] 17 | # https://github.com/jupyter-book/sphinx-ext-mystmd/pull/2 18 | - pip install --ignore-installed git+https://github.com/weiji14/sphinx-ext-mystmd@e995908b3a898b9c9d5d3fec4ff1478f1f4c1ccd 19 | - pip list 20 | post_install: 21 | - npm install -g mystmd 22 | - myst --version 23 | pre_build: 24 | - "cd docs/ && sphinx-build --builder myst . _build/myst-asts" 25 | - "ls -lh docs/_build/" 26 | build: 27 | html: 28 | - "cd docs/ && myst build --html" 29 | post_build: 30 | - "mkdir --parents $READTHEDOCS_OUTPUT/html/" 31 | - "mv docs/_build/html/* $READTHEDOCS_OUTPUT/html/" 32 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 [fullname] 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /python/tests/test_xarray_backend.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tests for xarray 'cog3pio' backend engine. 3 | """ 4 | 5 | import numpy as np 6 | import pytest 7 | import xarray as xr 8 | 9 | try: 10 | import rioxarray 11 | 12 | HAS_RIOXARRAY = True 13 | except ImportError: 14 | HAS_RIOXARRAY = False 15 | 16 | 17 | # %% 18 | @pytest.mark.benchmark 19 | @pytest.mark.parametrize( 20 | "engine", 21 | [ 22 | "cog3pio", 23 | pytest.param( 24 | "rasterio", 25 | marks=pytest.mark.skipif( 26 | condition=not HAS_RIOXARRAY, reason="Could not import 'rioxarray'" 27 | ), 28 | ), 29 | ], 30 | ) 31 | def test_xarray_backend_open_dataarray(engine): 32 | """ 33 | Ensure that passing engine='cog3pio' to xarray.open_dataarray works, and benchmark 34 | against engine="rasterio" (rioxarray). 35 | """ 36 | with xr.open_dataarray( 37 | filename_or_obj="https://github.com/cogeotiff/rio-tiler/raw/6.4.1/tests/fixtures/cog_nodata_nan.tif", 38 | engine=engine, 39 | ) as da: 40 | assert da.sizes == {'band': 1, 'y': 549, 'x': 549} 41 | assert da.x.min() == 500080.0 42 | assert da.x.max() == 609680.0 43 | assert da.y.min() == 5190340.0 44 | assert da.y.max() == 5299940.0 45 | assert da.dtype == "float32" 46 | # np.testing.assert_allclose(actual=da.mean(), desired=0.181176) 47 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["maturin>=1.4,<2.0"] 3 | build-backend = "maturin" 4 | 5 | [project] 6 | name = "cog3pio" 7 | requires-python = ">=3.12" 8 | license = "MIT OR Apache-2.0" 9 | license-files = ["LICENSE-MIT", "LICENSE-APACHE"] 10 | classifiers = [ 11 | "Development Status :: 4 - Beta", 12 | "Intended Audience :: Science/Research", 13 | "Topic :: Scientific/Engineering", 14 | "Topic :: Software Development :: Libraries", 15 | "Operating System :: MacOS", 16 | "Operating System :: Microsoft :: Windows", 17 | "Operating System :: POSIX", 18 | "Operating System :: Unix", 19 | "Programming Language :: Rust", 20 | "Programming Language :: Python :: Free Threading", 21 | "Programming Language :: Python :: Implementation :: CPython", 22 | ] 23 | dependencies = [ 24 | "numpy>=2.0", 25 | "xarray>=2023.12.0", 26 | ] 27 | dynamic = ["version"] 28 | 29 | [project.optional-dependencies] 30 | benchmark = [ 31 | "pytest-codspeed", 32 | "rioxarray", 33 | ] 34 | docs = [ 35 | "jupyter-book>=2.0.0b2", 36 | "myst_parser", 37 | "sphinx", 38 | # https://github.com/jupyter-book/sphinx-ext-mystmd/pull/2 39 | # "sphinx-ext-mystmd @ git+https://github.com/weiji14/sphinx-ext-mystmd@e995908b3a898b9c9d5d3fec4ff1478f1f4c1ccd", 40 | "sphinx-ext-mystmd", 41 | ] 42 | tests = [ 43 | "pytest", 44 | ] 45 | 46 | [project.entry-points."xarray.backends"] 47 | cog3pio = "cog3pio.xarray_backend:Cog3pioBackendEntrypoint" 48 | 49 | [tool.maturin] 50 | python-source = "python" 51 | features = ["pyo3"] 52 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | # Run linters 2 | # 3 | # Lint Rust code using cargo clippy, and GitHub Actions workflows using zizmor. Apply 4 | # static analysis rules to catch common mistakes and improves code style. 5 | 6 | name: Lint 7 | 8 | on: 9 | push: 10 | branches: ["main"] 11 | pull_request: 12 | types: [opened, reopened, synchronize] 13 | branches: ["main"] 14 | 15 | permissions: {} 16 | 17 | # Make sure CI fails on all warnings, including Clippy lints 18 | env: 19 | CARGO_TERM_COLOR: always 20 | RUSTFLAGS: "-Dwarnings" 21 | 22 | jobs: 23 | clippy_check: 24 | runs-on: ubuntu-24.04 25 | 26 | steps: 27 | - name: Checkout repository 28 | uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 29 | with: 30 | persist-credentials: false 31 | 32 | - name: Run Clippy 33 | run: cargo clippy --all-targets 34 | 35 | format_check: 36 | runs-on: ubuntu-24.04 37 | 38 | steps: 39 | - name: Checkout repository 40 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 41 | with: 42 | persist-credentials: false 43 | 44 | - name: Run Rustfmt 45 | run: cargo fmt --check -- --config imports_granularity=Module,group_imports=StdExternalCrate 46 | 47 | zizmor: 48 | runs-on: ubuntu-24.04 49 | steps: 50 | - name: Checkout repository 51 | uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 52 | with: 53 | persist-credentials: false 54 | 55 | - name: Install the latest version of uv 56 | uses: astral-sh/setup-uv@f94ec6bedd8674c4426838e6b50417d36b6ab231 # v5.3.1 57 | 58 | - name: Run zizmor 🌈 59 | run: uvx zizmor --color=always . 60 | env: 61 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 62 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cog3pio 2 | 3 | [![docs.rs](https://img.shields.io/docsrs/cog3pio?label=docs.rs%20latest)](https://docs.rs/cog3pio) 4 | [![crates.io](https://img.shields.io/crates/v/cog3pio)](https://crates.io/crates/cog3pio) 5 | [![Latest version on PyPI](https://img.shields.io/pypi/v/cog3pio)](https://pypi.org/project/cog3pio) 6 | [![Latest version on conda-forge](https://img.shields.io/conda/v/conda-forge/cog3pio)](https://anaconda.org/conda-forge/cog3pio) 7 | [![Digital Object Identifier for the Zenodo archive](https://zenodo.org/badge/DOI/10.5281/15702154.svg)](https://doi.org/10.5281/zenodo.15702154) 8 | 9 | Cloud-optimized GeoTIFF ... Parallel I/O 10 | 11 | Yet another attempt at creating a GeoTIFF reader, in Rust, with Python bindings. 12 | 13 | 14 | ## Roadmap 15 | 16 | 2024 Q1: 17 | - [x] Multi-band reader to [`ndarray`](https://github.com/rust-ndarray/ndarray) (relying 18 | on [`image-tiff`](https://crates.io/crates/tiff)) 19 | - [x] Read from HTTP remote storage (using 20 | [`object-store`](https://crates.io/crates/object_store)) 21 | 22 | 2024 Q2-Q4: 23 | - [x] Integration with `xarray` as a 24 | [`BackendEntrypoint`](https://docs.xarray.dev/en/v2024.02.0/internals/how-to-add-new-backend.html) 25 | - [x] Implement single-band GeoTIFF reader for multiple dtypes (uint/int/float) (based 26 | on [`geotiff`](https://crates.io/crates/geotiff) crate) 27 | 28 | 2025 Q1-Q2: 29 | - [x] Support for [`DLPack`](https://dmlc.github.io/dlpack/latest/index.html) protocol 30 | (through [`dlpark`](https://crates.io/crates/dlpark)) 31 | - [x] Initial release on crates.io and PyPI 32 | 33 | 2025 Q3-Q4: 34 | - [ ] GPU-based decoding (via [`nvTIFF`](https://crates.io/crates/nvtiff-sys)) 35 | - [ ] Asynchronous I/O (refactor to [`async-tiff`](https://crates.io/crates/async-tiff)) 36 | 37 | 2026: 38 | - [ ] Direct-to-GPU loading 39 | 40 | 41 | ## Related crates 42 | 43 | - https://github.com/developmentseed/async-tiff 44 | - https://github.com/feefladder/tiff2 45 | - https://github.com/georust/geotiff 46 | - https://github.com/jblindsay/whitebox-tools 47 | - https://github.com/pka/georaster 48 | -------------------------------------------------------------------------------- /python/cog3pio/xarray_backend.py: -------------------------------------------------------------------------------- 1 | """ 2 | An xarray backend for reading GeoTIFF files using the 'cog3pio' engine. 3 | """ 4 | 5 | import os 6 | 7 | import numpy as np 8 | import xarray as xr 9 | from xarray.backends import BackendEntrypoint 10 | 11 | from cog3pio import CogReader 12 | 13 | 14 | # %% 15 | class Cog3pioBackendEntrypoint(BackendEntrypoint): 16 | """ 17 | Xarray backend to read GeoTIFF files using 'cog3pio' engine. 18 | 19 | Examples 20 | -------- 21 | Read a GeoTIFF from a HTTP url into an xarray.DataArray: 22 | 23 | >>> import xarray as xr 24 | ... 25 | >>> # Read GeoTIFF into an xarray.DataArray 26 | >>> dataarray: xr.DataArray = xr.open_dataarray( 27 | ... filename_or_obj="https://github.com/OSGeo/gdal/raw/v3.11.0/autotest/gcore/data/byte_zstd.tif", 28 | ... engine="cog3pio", 29 | ... ) 30 | >>> dataarray.sizes 31 | Frozen({'band': 1, 'y': 20, 'x': 20}) 32 | >>> dataarray.dtype 33 | dtype('uint8') 34 | """ 35 | 36 | description = "Use .tif files in Xarray" 37 | open_dataset_parameters = ["filename_or_obj"] 38 | url = "https://github.com/weiji14/cog3pio" 39 | 40 | def open_dataset( 41 | self, 42 | filename_or_obj: str, 43 | *, 44 | drop_variables=None, 45 | # other backend specific keyword arguments 46 | # `chunks` and `cache` DO NOT go here, they are handled by xarray 47 | ) -> xr.Dataset: 48 | cog = CogReader(path=filename_or_obj) 49 | 50 | array: np.ndarray = np.from_dlpack(cog) 51 | x_coords, y_coords = cog.xy_coords() 52 | 53 | channels, height, width = array.shape 54 | dataarray: xr.DataArray = xr.DataArray( 55 | data=array, 56 | coords={ 57 | "band": np.arange(stop=channels, dtype=np.uint8), 58 | "y": y_coords, 59 | "x": x_coords, 60 | }, 61 | name=None, 62 | attrs=None, 63 | ) 64 | 65 | return dataarray.to_dataset(name="raster") 66 | 67 | def guess_can_open(self, filename_or_obj): 68 | try: 69 | _, ext = os.path.splitext(filename_or_obj) 70 | except TypeError: 71 | return False 72 | return ext in {".tif", ".tiff"} 73 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cog3pio" 3 | version = "0.0.1" 4 | edition = "2024" 5 | description = "Cloud-optimized GeoTIFF ... Parallel I/O" 6 | readme = "README.md" 7 | repository = "https://github.com/weiji14/cog3pio" 8 | license = "MIT OR Apache-2.0" 9 | rust-version = "1.85.0" 10 | authors = ["Wei Ji <23487320+weiji14@users.noreply.github.com>"] 11 | 12 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 13 | [lib] 14 | name = "cog3pio" 15 | crate-type = ["cdylib", "rlib"] 16 | 17 | [dependencies] 18 | bytes = "1.5.0" 19 | cudarc = { version = "0.17.7", features = [ 20 | "cuda-version-from-build-system", 21 | "fallback-latest", 22 | ], optional = true } 23 | dlpark = { git = "https://github.com/SunDoge/dlpark.git", version = "0.6.0", features = [ 24 | "half", 25 | "ndarray", 26 | ], rev = "2bedd1fc8ad59e56e3b74c63c94efb3fdc8c2ada" } 27 | geo = "0.29.0" 28 | ndarray = "0.17.1" 29 | numpy = { version = "0.27.1", optional = true } 30 | nvtiff-sys = { version = "0.1.2", optional = true } 31 | object_store = { version = "0.12.3", features = ["http"] } 32 | pyo3 = { version = "0.27.1", features = [ 33 | "abi3-py312", 34 | "extension-module", 35 | ], optional = true } 36 | tiff = { version = "0.10.0", features = ["zstd"] } 37 | tokio = { version = "1.48.0", features = ["rt-multi-thread"], optional = true } 38 | url = { version = "2.5.7", optional = true } 39 | 40 | [dev-dependencies] 41 | criterion = { package = "codspeed-criterion-compat", version = "4.1.0" } 42 | gdal = { git = "https://github.com/georust/gdal.git", version = "0.18.0", features = [ 43 | "array", 44 | ], rev = "8b9e04938298055f2a22dce5c21c1a6e73678ecb" } 45 | # comment out gdal-src and gdal-sys if libgdal-dev is installed locally 46 | gdal-src = { git = "https://github.com/georust/gdal.git", version = "0.3.0", features = [ 47 | "driver_gtiff", 48 | "nobuild", 49 | ], rev = "8b9e04938298055f2a22dce5c21c1a6e73678ecb" } 50 | gdal-sys = { git = "https://github.com/georust/gdal.git", version = "0.11.0", features = [ 51 | "bundled", 52 | ], rev = "8b9e04938298055f2a22dce5c21c1a6e73678ecb" } 53 | half = { version = "2.6.0", features = ["num-traits"] } 54 | rstest = "0.26.1" 55 | tempfile = "3.10.1" 56 | tokio = { version = "*", features = ["rt-multi-thread"] } 57 | url = "2.5.7" 58 | 59 | [features] 60 | cuda = ["cudarc", "dlpark/cuda", "nvtiff-sys"] 61 | pyo3 = ["dlpark/pyo3", "dep:numpy", "dep:pyo3", "dep:tokio", "dep:url"] 62 | 63 | [lints.clippy] 64 | pedantic = "warn" 65 | 66 | [package.metadata.docs.rs] 67 | all-features = true 68 | rustdoc-args = ["--cfg", "docsrs"] 69 | 70 | [profile.test] 71 | opt-level = 2 72 | 73 | [[bench]] 74 | name = "read_cog" 75 | harness = false 76 | -------------------------------------------------------------------------------- /docs/quickstart.md: -------------------------------------------------------------------------------- 1 | # Quickstart 2 | 3 | There are three ways to read a GeoTIFF with `cog3pio`'s Python bindings into CPU memory. 4 | Take your pick: 5 | 6 | | Output | DLPack protocol | coordinates | any dtype | 7 | |:---------:|:---------------:|:-----------:|:---------:| 8 | | PyCapsule | ✅ | ❌ | ✅ | 9 | | xarray | ❓ | ✅ | ✅ | 10 | | numpy | ✅ | ❌ | ❌ | 11 | 12 | Notes: 13 | - [DLPack - an in-memory tensor structure]( https://data-apis.org/array-api/latest/design_topics/data_interchange.html#dlpack-an-in-memory-tensor-structure) 14 | - Coordinates in xarray are computed from the GeoTIFF's affine transformation 15 | - Currently supported dtypes include uint (u8/u16/u32/u64), int (i8/i16/i32/i64) and 16 | float (f16/f32/f64). 17 | 18 | 19 | ## PyCapsule (DLPack) 20 | 21 | Read a GeoTIFF file from a HTTP url via the [`CogReader`](../api#dlpack) class into an 22 | object that conforms to the 23 | [Python Specification for DLPack](https://dmlc.github.io/dlpack/latest/python_spec.html), 24 | whereby the `__dlpack__()` method returns a 25 | [PyCapsule](https://docs.python.org/3/c-api/capsule.html#c.PyCapsule) object containing 26 | a [`DLManagedTensorVersioned`](https://dmlc.github.io/dlpack/latest/c_api.html#c.DLManagedTensorVersioned). 27 | 28 | ```python 29 | import numpy as np 30 | from cog3pio import CogReader 31 | 32 | cog = CogReader(path="https://github.com/OSGeo/gdal/raw/v3.11.0/autotest/gcore/data/float16.tif") 33 | assert hasattr(cog, "__dlpack__") 34 | assert hasattr(cog, "__dlpack_device__") 35 | 36 | array: np.ndarray = np.from_dlpack(cog) 37 | assert array.shape == (1, 20, 20) 38 | assert array.dtype == "float16" 39 | 40 | # or with Pytorch, after https://github.com/pytorch/pytorch/pull/145000 41 | # tensor: torch.Tensor = torch.from_dlpack(cog) 42 | # ... 43 | ``` 44 | 45 | ## Xarray 46 | 47 | Read GeoTIFF file from a HTTP url via the [`Cog3pioBackendEntrypoint`](../api#xarray) 48 | engine into an `xarray.DataArray` object (akin to 49 | [`rioxarray`](https://corteva.github.io/rioxarray)). 50 | 51 | ```python 52 | import xarray as xr 53 | 54 | # Read GeoTIFF into an xarray.DataArray 55 | dataarray: xr.DataArray = xr.open_dataarray( 56 | filename_or_obj="https://github.com/cogeotiff/rio-tiler/raw/7.8.0/tests/fixtures/cog_dateline.tif", 57 | engine="cog3pio", 58 | ) 59 | assert dataarray.sizes == {'band': 1, 'y': 2355, 'x': 2325} 60 | assert dataarray.dtype == "uint16" 61 | ``` 62 | 63 | ## NumPy 64 | 65 | Read a GeoTIFF file from a HTTP url via the [`read_geotiff`](../api#numpy) function 66 | into a `numpy.ndarray` (akin to [`rasterio`](https://rasterio.readthedocs.io)). 67 | 68 | ```python 69 | import numpy as np 70 | from cog3pio import read_geotiff 71 | 72 | # Read GeoTIFF into a numpy array 73 | array: np.ndarray = read_geotiff( 74 | path="https://github.com/cogeotiff/rio-tiler/raw/6.4.0/tests/fixtures/cog_nodata_nan.tif" 75 | ) 76 | assert array.shape == (1, 549, 549) # bands, height, width 77 | assert array.dtype == "float32" 78 | ``` 79 | 80 | > [!NOTE] 81 | > The `read_geotiff` function supports reading single or multi-band GeoTIFF files into a 82 | > float32 array only. If you wish to read into other dtypes (e.g. uint16), please use 83 | > the [Xarray](quickstart#xarray) or [DLPack](quickstart#pycapsule-dlpack) methods 84 | > instead which supports reading into different dtypes. 85 | -------------------------------------------------------------------------------- /.github/workflows/benchmarks.yml: -------------------------------------------------------------------------------- 1 | # Run performance benchmarks 2 | # 3 | # Continuous benchmarking with CodSpeed. Measures the execution speed of Rust benchmarks 4 | # via cargo-codspeed, and Python tests marked with @pytest.mark.benchmark decorator. 5 | 6 | name: Benchmarks 7 | 8 | on: 9 | # Run on pushes to the main branch 10 | push: 11 | branches: [main] 12 | # Run on pull requests 13 | pull_request: 14 | types: [opened, reopened, synchronize] 15 | # `workflow_dispatch` allows CodSpeed to trigger backtest 16 | # performance analysis in order to generate initial data. 17 | workflow_dispatch: 18 | 19 | env: 20 | CARGO_TERM_COLOR: always 21 | 22 | permissions: {} 23 | 24 | jobs: 25 | rust-benchmarks: 26 | name: "Run Rust benchmarks" 27 | runs-on: ubuntu-24.04 28 | container: 29 | image: ghcr.io/osgeo/gdal:ubuntu-small-3.11.5 30 | options: --privileged 31 | steps: 32 | - name: Install dev dependencies and setup git 33 | run: | 34 | apt update 35 | apt install -y build-essential cmake git libclang-dev pkg-config 36 | git config --global --add safe.directory $GITHUB_WORKSPACE 37 | 38 | - name: Checkout repository 39 | uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 40 | with: 41 | persist-credentials: false 42 | 43 | - name: Download sample GeoTIFF 44 | run: | 45 | curl --create-dir --remote-name --output-dir benches https://sentinel-cogs.s3.us-west-2.amazonaws.com/sentinel-s2-l2a-cogs/37/M/BV/2024/10/S2A_37MBV_20241029_0_L2A/TCI.tif 46 | ls -lh benches/ 47 | 48 | - name: Setup rust toolchain, cache and cargo-codspeed binary 49 | uses: moonrepo/setup-rust@ede6de059f8046a5e236c94046823e2af11ca670 # v1.2.2 50 | with: 51 | channel: 1.85.0 # msrv 52 | cache: false 53 | cache-target: release 54 | bins: cargo-codspeed 55 | 56 | - name: Build the benchmark target(s) 57 | run: | 58 | cargo remove --dev gdal-src gdal-sys 59 | cargo codspeed build 60 | 61 | - name: Run the benchmarks 62 | uses: CodSpeedHQ/action@6a8e2b874c338bf81cc5e8be715ada75908d3871 # v4.3.4 63 | with: 64 | mode: instrumentation 65 | run: cargo codspeed run 66 | 67 | python-benchmarks: 68 | name: "Run Python benchmarks" 69 | runs-on: ubuntu-24.04 70 | defaults: 71 | run: 72 | shell: bash -l {0} 73 | 74 | steps: 75 | # Checkout current git repository 76 | - name: Checkout 77 | uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 78 | with: 79 | persist-credentials: false 80 | 81 | # Setup Python interpreter 82 | - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 83 | with: 84 | python-version: "3.12" 85 | 86 | # Build binary distribution wheel 87 | - name: Build wheels 88 | uses: PyO3/maturin-action@60d11847b29f81ca5375519a8eb33cc336ba4bfa # v1.41.0 89 | with: 90 | target: x86_64 91 | args: --release --out dist --find-interpreter 92 | sccache: "true" 93 | manylinux: auto 94 | 95 | # Install the package that we want to test 96 | - name: Install the package 97 | run: | 98 | set -e 99 | python -m pip install cog3pio[benchmark,tests] --find-links dist --force-reinstall 100 | python -m pip list 101 | 102 | # Run the benchmark tests 103 | - name: Run benchmarks 104 | uses: CodSpeedHQ/action@6a8e2b874c338bf81cc5e8be715ada75908d3871 # v4.3.4 105 | with: 106 | mode: instrumentation 107 | run: python -m pytest --verbose --codspeed 108 | -------------------------------------------------------------------------------- /.release-plz.toml: -------------------------------------------------------------------------------- 1 | [[package]] 2 | name = "cog3pio" 3 | changelog_path = "docs/changelog.md" 4 | 5 | [changelog] 6 | header = """ 7 | # Changelog\n 8 | All notable changes to this project will be documented in this file.\n 9 | """ 10 | # template for the changelog body 11 | # https://keats.github.io/tera/docs/#introduction 12 | body = """ 13 | ---\n 14 | {% if version %}\ 15 | ## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }} 16 | {% else %}\ 17 | ## Unreleased 18 | {% endif %}\ 19 | {% for group, commits in commits | group_by(attribute="group") %} 20 | ### {{ group | upper_first }} 21 | {% for commit in commits 22 | | filter(attribute="scope") 23 | | sort(attribute="scope") %} 24 | - *({{commit.scope}})* {{ commit.message | split(pat="\n") | first | upper_first }} 25 | {%- if commit.breaking %} 26 | {% raw %} {% endraw %}- **BREAKING**: {{commit.breaking_description}} 27 | {%- endif -%} 28 | {%- endfor -%} 29 | {% raw %}\n{% endraw %}\ 30 | {%- for commit in commits %} 31 | {%- if commit.scope -%} 32 | {% else -%} 33 | - {{ commit.message | split(pat="\n") | first | upper_first }} 34 | {% if commit.breaking -%} 35 | {% raw %} {% endraw %}- **BREAKING**: {{commit.breaking_description}} 36 | {% endif -%} 37 | {% endif -%} 38 | {% endfor -%} 39 | {% raw %}{% endraw %}\ 40 | {% endfor %} 41 | {%- if remote.contributors %} 42 | ### 🧑‍🤝‍🧑 Contributors 43 | {% for contributor in remote.contributors %} 44 | - [@{{ contributor.username }}](https://github.com/{{ contributor.username }}) 45 | {%- endfor %} 46 | {% raw %}\n{% endraw %}\ 47 | {% endif -%} 48 | {%- macro username(commit) -%} 49 | {% if commit.remote.username %} {% endif -%} 50 | {% endmacro -%} 51 | """ 52 | trim = true 53 | 54 | # process each line of a commit as an individual commit 55 | commit_preprocessors = [ 56 | # Replace the issue/PR number with the link. 57 | { pattern = "\\(#([0-9]+)\\)", replace = "([#${1}](https://github.com/weiji14/cog3pio/pull/${1}))" }, 58 | # Replace gitmoji 59 | { pattern = ':art:', replace = "🎨" }, 60 | { pattern = ':arrow_down:', replace = "⬇️" }, 61 | { pattern = ':arrow_up:', replace = "⬆️" }, 62 | { pattern = ':boom:', replace = "💥" }, 63 | { pattern = ':bug:', replace = "🐛" }, 64 | { pattern = ':construction_worker:', replace = "👷" }, 65 | { pattern = ':heavy_minus_sign:', replace = "➖" }, 66 | { pattern = ':heavy_plus_sign:', replace = "➕" }, 67 | { pattern = ':lock:', replace = "🔒️" }, 68 | { pattern = ':loud_sound:', replace = "🔊" }, 69 | { pattern = ':mag:', replace = "🔍️" }, 70 | { pattern = ':memo:', replace = "📝" }, 71 | { pattern = ':pushpin:', replace = "📌" }, 72 | { pattern = ':recycle:', replace = "♻️" }, 73 | { pattern = ':rocket:', replace = "🚀" }, 74 | { pattern = ':rotating_light:', replace = "🚨" }, 75 | { pattern = ':seedling:', replace = "🌱" }, 76 | { pattern = ':sparkles:', replace = "✨" }, 77 | { pattern = ':triangular_flag_on_post:', replace = "🚩" }, 78 | { pattern = ':truck:', replace = "🚚" }, 79 | { pattern = ':wrench:', replace = "🔧" }, 80 | ] 81 | # regex for parsing and grouping commits 82 | commit_parsers = [ 83 | # Gitmoji 84 | { message = "^(💥|:boom:|🚀|:rocket:)", group = " 🌈 Highlights" }, 85 | { message = "^(✨|:sparkles:)", group = " ✨ Features" }, 86 | { message = "^(🐛|:bug:)", group = " 🐛 Bug Fixes" }, 87 | { message = "^(♻️|:recycle:|🚚|:truck:|🎨|:art:)", group = " 🏭 Refactors" }, 88 | { message = "^(📝|:memo:|🔍️|:mag:)", group = " 📝 Documentation" }, 89 | { message = "^(👷|:construction_worker:|🔧|:wrench:|⬆️|:arrow_up:|➕|:heavy_plus_sign:|➖|:heavy_minus_sign:|⬇️|:arrow_down:|📌|:pushpin:|🚩|:triangular_flag_on_post:|🔒️|:lock:|🚨|:rotating_light:|🌱|:seedling:|🔊|:loud_sound:)", group = " 🧰 Maintenance" }, 90 | ] 91 | # sort the commits inside sections by oldest/newest order 92 | sort_commits = "newest" 93 | -------------------------------------------------------------------------------- /docs/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | --- 6 | 7 | ## [0.0.1] - 2025-06-28 8 | 9 | ### 🌈 Highlights 10 | 11 | - 💥 Read into DLPack tensor ([#32](https://github.com/weiji14/cog3pio/pull/32)) 12 | - ✨ Implement cog3pio xarray BackendEntrypoint ([#14](https://github.com/weiji14/cog3pio/pull/14)) 13 | 14 | ### ✨ Features 15 | 16 | - ✨ Support decoding ZSTD compressed and half-precision TIFFs ([#46](https://github.com/weiji14/cog3pio/pull/46)) 17 | - ✨ Support reading 3-band RGB images ([#31](https://github.com/weiji14/cog3pio/pull/31)) 18 | - ✨ Support reading uint/int/float dtypes ([#18](https://github.com/weiji14/cog3pio/pull/18)) 19 | - ✨ Support reading multi-band GeoTIFF files ([#13](https://github.com/weiji14/cog3pio/pull/13)) 20 | - ✨ Implement PyCogReader struct with new and to_numpy methods ([#12](https://github.com/weiji14/cog3pio/pull/12)) 21 | - ✨ CogReader ndarray method to decode TIFF into an ndarray Array ([#10](https://github.com/weiji14/cog3pio/pull/10)) 22 | - ✨ Get affine transformation from GeoTIFF ([#8](https://github.com/weiji14/cog3pio/pull/8)) 23 | - ✨ Read GeoTIFF files from remote urls via object_store ([#5](https://github.com/weiji14/cog3pio/pull/5)) 24 | - ✨ A read_geotiff function for reading GeoTIFF into ndarray ([#3](https://github.com/weiji14/cog3pio/pull/3)) 25 | 26 | ### 🏭 Refactors 27 | 28 | - ♻️ Refactor to return 3D arrays of shape (band, height, width) ([#11](https://github.com/weiji14/cog3pio/pull/11)) 29 | - 🚚 Move pyo3 functions under src/python/adapters.rs ([#9](https://github.com/weiji14/cog3pio/pull/9)) 30 | - 🎨 Initial CogReader struct with decoder field ([#7](https://github.com/weiji14/cog3pio/pull/7)) 31 | - ♻️ Refactor unit test to be non-square ([#6](https://github.com/weiji14/cog3pio/pull/6)) 32 | 33 | ### 📝 Documentation 34 | 35 | - 📝 Move installation and example commands into separate pages ([#47](https://github.com/weiji14/cog3pio/pull/47)) 36 | - 📝 Initialize Python documentation page ([#35](https://github.com/weiji14/cog3pio/pull/35)) 37 | 38 | ### 🧰 Maintenance 39 | 40 | - 👷 GitHub Actions CI workflow to publish to crates.io ([#49](https://github.com/weiji14/cog3pio/pull/49)) 41 | - 🔧 Switch changelog generator config from git-cliff to release-plz ([#48](https://github.com/weiji14/cog3pio/pull/48)) 42 | - 📌 Unpin sphinx-ext-mystmd in docs extras ([#44](https://github.com/weiji14/cog3pio/pull/44)) 43 | - 🔊 Enable verbose logging for pypa/gh-action-pypi-publish ([#42](https://github.com/weiji14/cog3pio/pull/42)) 44 | - 👷 Upload to TestPyPI on prerelease and release tags ([#40](https://github.com/weiji14/cog3pio/pull/40)) 45 | - 👷 Adjust CI workflow conditions for release trigger ([#38](https://github.com/weiji14/cog3pio/pull/38)) 46 | - 🔧 Configure readthedocs documentation build ([#36](https://github.com/weiji14/cog3pio/pull/36)) 47 | - 👷 Build free-threaded wheels on CI and upload to TestPyPI ([#34](https://github.com/weiji14/cog3pio/pull/34)) 48 | - 🚨 Setup CI to lint using cargo fmt + clippy pedantic fixes ([#33](https://github.com/weiji14/cog3pio/pull/33)) 49 | - 👷 Run aarch64 CI tests on ubuntu-24.04-arm ([#30](https://github.com/weiji14/cog3pio/pull/30)) 50 | - ⬆️ SPEC 0: Bump min version to Python 3.12, NumPy 2.0, xarray 2023.12.0 ([#29](https://github.com/weiji14/cog3pio/pull/29)) 51 | - 📌 Pin MSRV to 1.78.0 ([#28](https://github.com/weiji14/cog3pio/pull/28)) 52 | - ⬆️ Bump pyo3 from 0.20.3 to 0.25.0, numpy from 0.20.0 to 0.25.0 ([#15](https://github.com/weiji14/cog3pio/pull/15), [#19](https://github.com/weiji14/cog3pio/pull/19), [#21](https://github.com/weiji14/cog3pio/pull/21), [#25](https://github.com/weiji14/cog3pio/pull/25)) 53 | - 🔒️ Add zizmor to statically analyze GitHub Actions workflows ([#24](https://github.com/weiji14/cog3pio/pull/24)) 54 | - 👷 Run CI on ubuntu-24.04, macos-15, windows-2025 ([#23](https://github.com/weiji14/cog3pio/pull/23)) 55 | - 🚨 Setup CI to run linting using cargo clippy ([#22](https://github.com/weiji14/cog3pio/pull/22)) 56 | - ⬆️ Bump geo from 0.28.0 rev 481196b to 0.29.0 ([#20](https://github.com/weiji14/cog3pio/pull/20)) 57 | - 👷 Setup CI job matrix to run cargo test ([#17](https://github.com/weiji14/cog3pio/pull/17)) 58 | - 👷 Setup benchmark workflow with pytest-codspeed ([#4](https://github.com/weiji14/cog3pio/pull/4)) 59 | - 👷 Setup GitHub Actions Continuous Integration tests ([#2](https://github.com/weiji14/cog3pio/pull/2)) 60 | - 🌱 Initialize Cargo.toml and pyproject.toml with maturin ([#1](https://github.com/weiji14/cog3pio/pull/1)) 61 | 62 | ### 🧑‍🤝‍🧑 Contributors 63 | 64 | - [@weiji14](https://github.com/weiji14) 65 | -------------------------------------------------------------------------------- /python/tests/test_io_geotiff.py: -------------------------------------------------------------------------------- 1 | """ 2 | Test I/O on GeoTIFF files. 3 | """ 4 | 5 | import os 6 | import tempfile 7 | import urllib.request 8 | 9 | import numpy as np 10 | import pytest 11 | from cog3pio import CogReader, read_geotiff 12 | 13 | 14 | # %% 15 | @pytest.fixture(scope="module", name="geotiff_path") 16 | def fixture_geotiff_path(): 17 | """ 18 | Filepath to a sample single-band GeoTIFF file. 19 | """ 20 | with tempfile.TemporaryDirectory() as tmpdir: 21 | geotiff_path = os.path.join(tmpdir, "float32.tif") 22 | urllib.request.urlretrieve( 23 | url="https://github.com/pka/georaster/raw/v0.1.0/data/tiff/float32.tif", 24 | filename=geotiff_path, 25 | ) 26 | yield geotiff_path 27 | 28 | 29 | @pytest.mark.benchmark 30 | def test_read_geotiff_local(geotiff_path): 31 | """ 32 | Read a single-band GeoTIFF file from a local file path. 33 | """ 34 | array = read_geotiff(path=geotiff_path) 35 | assert array.shape == (1, 20, 20) 36 | assert array.dtype == "float32" 37 | 38 | 39 | @pytest.mark.benchmark 40 | def test_read_geotiff_remote(): 41 | """ 42 | Read a single-band GeoTIFF file from a remote URL. 43 | """ 44 | array = read_geotiff( 45 | path="https://github.com/pka/georaster/raw/v0.1.0/data/tiff/float32.tif" 46 | ) 47 | assert array.shape == (1, 20, 20) 48 | assert array.dtype == "float32" 49 | 50 | 51 | @pytest.mark.benchmark 52 | def test_read_geotiff_multi_band(): 53 | """ 54 | Read a multi-band GeoTIFF file from a remote URL. 55 | """ 56 | array = read_geotiff( 57 | path="https://github.com/locationtech/geotrellis/raw/v3.7.1/raster/data/one-month-tiles-multiband/result.tif" 58 | ) 59 | assert array.shape == (2, 512, 512) 60 | assert array.dtype == "float32" 61 | 62 | 63 | def test_read_geotiff_invalid_filepath(): 64 | """ 65 | Check that a ValueError is raised when an invalid filepath is passed to read_geotiff. 66 | """ 67 | with pytest.raises(ValueError, match=r"Cannot parse path: \\invalid\\path"): 68 | read_geotiff(path=r"\invalid\path") 69 | 70 | 71 | def test_read_geotiff_invalid_remote_url(): 72 | """ 73 | Check that a ValueError is raised when an invalid remote url is passed to read_geotiff. 74 | """ 75 | with pytest.raises(ValueError, match="Cannot parse url: protocol://file.ext"): 76 | read_geotiff(path="protocol://file.ext") 77 | 78 | 79 | def test_read_geotiff_missing_url(): 80 | """ 81 | Check that a FileNotFoundError is raised when a url pointing to a non-existent file 82 | is passed to read_geotiff. 83 | """ 84 | with pytest.raises( 85 | FileNotFoundError, match="Cannot find file: https://example.com/geo.tif" 86 | ): 87 | read_geotiff(path="https://example.com/geo.tif") 88 | 89 | 90 | def test_read_geotiff_unsupported_colortype(): 91 | """ 92 | Check that a ValueError is raised when an unsupported GeoTIFF (with ColorType::RGB) 93 | is passed to read_geotiff. 94 | """ 95 | with pytest.raises( 96 | ValueError, 97 | match="unsupported error: Photometric interpretation " 98 | r"RGBPalette with bits per sample \[8\] is unsupported", 99 | ): 100 | read_geotiff( 101 | path="https://github.com/GenericMappingTools/gmtserver-admin/raw/caf0dbd015f0154687076dd31dc8baff62c95040/cache/earth_day_HD.tif" 102 | ) 103 | 104 | 105 | def test_read_geotiff_unsupported_dtype(): 106 | """ 107 | Check that a ValueError is raised when an unsupported GeoTIFF (of ComplexInt16 type) 108 | is passed to read_geotiff. 109 | """ 110 | with pytest.raises( 111 | ValueError, 112 | match=r"unsupported error: sample format \[Unknown\(5\)\] is unsupported", 113 | ): 114 | read_geotiff( 115 | path="https://github.com/corteva/rioxarray/raw/0.15.1/test/test_data/input/cint16.tif" 116 | ) 117 | 118 | 119 | def test_CogReader_to_dlpack(): 120 | """ 121 | Ensure that the CogReader class's `__dlpack__` method produces a dl_tensor that 122 | can be read into a numpy.ndarray. 123 | """ 124 | cog = CogReader( 125 | path="https://github.com/rasterio/rasterio/raw/1.3.9/tests/data/float32.tif" 126 | ) 127 | 128 | assert hasattr(cog, "__dlpack__") 129 | assert hasattr(cog, "__dlpack_device__") 130 | array = np.from_dlpack(cog) 131 | 132 | assert array.shape == (1, 2, 3) # band, height, width 133 | np.testing.assert_equal( 134 | actual=array, 135 | desired=np.array( 136 | [[[1.41, 1.23, 0.78], [0.32, -0.23, -1.88]]], dtype=np.float32 137 | ), 138 | ) 139 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(docsrs, feature(doc_cfg))] 2 | #![warn(missing_docs)] 3 | //! # Cloud-optimized GeoTIFF ... Parallel I/O 4 | //! 5 | //! A reader for [Cloud Optimized GeoTIFF (COG)](https://www.cogeo.org) files. 6 | //! 7 | //! There are two backends. A CPU one and a GPU (CUDA) one. 8 | //! 9 | //! **Note**: Python bindings (via [`pyo3`]) are documented over at 10 | //! . 11 | //! 12 | //! # CPU decoder 13 | //! 14 | //! Uses [`tiff`] to decode TIFF images. Pixel data is stored with a shape of 15 | //! (channels, height, width) using either: 16 | //! - [`CogReader`](crate::io::geotiff::CogReader) - returns a 17 | //! [DLPack](https://dmlc.github.io/dlpack/latest) data structure in CPU-memory via 18 | //! [`dlpark`](https://docs.rs/dlpark) 19 | //! - [`read_geotiff`](crate::io::geotiff::read_geotiff) - returns a 3D Array via 20 | //! [`ndarray`](https://docs.rs/ndarray) 21 | //! 22 | //! ## Examples 23 | //! 24 | //! ### DLPack 25 | //! 26 | //! Retrieve a GeoTIFF file stream via the [`object_store`] crate, pass it into the 27 | //! [`CogReader::new`](crate::io::geotiff::CogReader::new) method to instantiate a 28 | //! [`CogReader`](crate::io::geotiff::CogReader) struct, and call 29 | //! [`.dlpack()`](crate::io::geotiff::CogReader::dlpack) to get a 30 | //! [`dlpark::SafeManagedTensorVersioned`] output. 31 | //! 32 | //! ```rust 33 | //! use std::io::Cursor; 34 | //! 35 | //! use bytes::Bytes; 36 | //! use cog3pio::io::geotiff::CogReader; 37 | //! use dlpark::ffi::DataType; 38 | //! use dlpark::prelude::TensorView; 39 | //! use dlpark::SafeManagedTensorVersioned; 40 | //! use object_store::path::Path; 41 | //! use object_store::{parse_url, GetResult, ObjectStore}; 42 | //! use tokio; 43 | //! use url::Url; 44 | //! 45 | //! #[tokio::main] 46 | //! async fn main() { 47 | //! let cog_url: &str = 48 | //! "https://github.com/cogeotiff/rio-tiler/raw/7.8.0/tests/fixtures/cog_dateline.tif"; 49 | //! let tif_url: Url = Url::parse(cog_url).unwrap(); 50 | //! let (store, location): (Box, Path) = parse_url(&tif_url).unwrap(); 51 | //! 52 | //! let stream: Cursor = { 53 | //! let result: GetResult = store.get(&location).await.unwrap(); 54 | //! let bytes: Bytes = result.bytes().await.unwrap(); 55 | //! Cursor::new(bytes) 56 | //! }; 57 | //! 58 | //! // Read GeoTIFF into a dlpark::versioned::SafeManagedTensorVersioned 59 | //! let mut cog = CogReader::new(stream).unwrap(); 60 | //! let tensor: SafeManagedTensorVersioned = cog.dlpack().unwrap(); 61 | //! assert_eq!(tensor.shape(), [1, 2355, 2325]); 62 | //! assert_eq!(tensor.data_type(), &DataType::U16); 63 | //! } 64 | //! ``` 65 | //! 66 | //! ### Ndarray 67 | //! 68 | //! Retrieve a GeoTIFF file stream via the [`object_store`] crate, and pass it into the 69 | //! [`read_geotiff`](crate::io::geotiff::read_geotiff) function to get an 70 | //! [`ndarray::Array3`] output. 71 | //! 72 | //! ```rust 73 | //! use std::io::Cursor; 74 | //! 75 | //! use bytes::Bytes; 76 | //! use cog3pio::io::geotiff::read_geotiff; 77 | //! use ndarray::Array3; 78 | //! use object_store::path::Path; 79 | //! use object_store::{parse_url, GetResult, ObjectStore}; 80 | //! use tokio; 81 | //! use url::Url; 82 | //! 83 | //! #[tokio::main] 84 | //! async fn main() { 85 | //! let cog_url: &str = 86 | //! "https://github.com/cogeotiff/rio-tiler/raw/7.8.0/tests/fixtures/cog_nodata_nan.tif"; 87 | //! let tif_url: Url = Url::parse(cog_url).unwrap(); 88 | //! let (store, location): (Box, Path) = parse_url(&tif_url).unwrap(); 89 | //! 90 | //! let stream: Cursor = { 91 | //! let result: GetResult = store.get(&location).await.unwrap(); 92 | //! let bytes: Bytes = result.bytes().await.unwrap(); 93 | //! Cursor::new(bytes) 94 | //! }; 95 | //! 96 | //! // Read GeoTIFF into an ndarray::Array 97 | //! let arr: Array3 = read_geotiff::(stream).unwrap(); 98 | //! assert_eq!(arr.dim(), (1, 549, 549)); 99 | //! assert_eq!(arr[[0, 500, 500]], 0.13482364); 100 | //! } 101 | //! ``` 102 | //! 103 | //! Note that the output dtype is specified either using a type hint 104 | //! (`let arr: Array3`) or via a turbofish operator (`read_geotiff::`). 105 | //! Currently supported dtypes include uint (u8/u16/u32/u64), int (i8/i16/i32/i64) and 106 | //! float (f16/f32/f64). 107 | //! 108 | //! # GPU (CUDA) decoder 109 | //! 110 | //! Uses [`nvtiff_sys`] to decode TIFF images. Pixel data is stored as a flattened 1D 111 | //! array in row-major order (i.e. rows-first, columns-next). Use: 112 | //! - [`CudaCogReader`](crate::io::nvtiff::CudaCogReader) - returns a 113 | //! [DLPack](https://dmlc.github.io/dlpack/latest) data structure in CUDA-memory via 114 | //! [`dlpark`](https://docs.rs/dlpark) 115 | 116 | /// Modules for handling Input/Output of GeoTIFF data 117 | pub mod io; 118 | /// Modules for Python to interface with Rust code 119 | #[cfg(feature = "pyo3")] 120 | mod python; 121 | -------------------------------------------------------------------------------- /benches/read_cog.rs: -------------------------------------------------------------------------------- 1 | // Benchmark tests on reading a Cloud-optimized GeoTIFF (CoG) into memory (CPU or GPU) 2 | // 3 | // Libraries compared: 4 | // - nvTIFF (Enable NVIDIA network repository and do `sudo apt install nvtiff nvcomp-cuda-12`) 5 | // - GDAL 6 | // - image-tiff 7 | // 8 | // Steps: 9 | // - Download Sentinel-2 True-Colour Image (TCI) file (318.0MB, DEFLATE compression) from 10 | // https://sentinel-cogs.s3.us-west-2.amazonaws.com/sentinel-s2-l2a-cogs/37/M/BV/2024/10/S2A_37MBV_20241029_0_L2A/TCI.tif 11 | // to `benches/` folder. 12 | // - Run `cargo bench` (CPU-only) or `cargo bench --features cuda` (with CUDA-enabled GPU) 13 | // 14 | // References: 15 | // - https://github.com/microsoft/pytorch-cloud-geotiff-optimization/blob/5fb6d1294163beff822441829dcd63a3791b7808/configs/search.yaml#L6 16 | 17 | use std::fs::File; 18 | #[cfg(feature = "cuda")] 19 | use std::sync::Arc; 20 | #[cfg(feature = "cuda")] 21 | use std::time::Duration; 22 | 23 | #[cfg(feature = "cuda")] 24 | use bytes::Bytes; 25 | use cog3pio::io::geotiff::CogReader; 26 | #[cfg(feature = "cuda")] 27 | use cog3pio::io::nvtiff::CudaCogReader; 28 | use criterion::{BenchmarkId, Criterion, Throughput, criterion_group, criterion_main}; 29 | #[cfg(feature = "cuda")] 30 | use cudarc::driver::{CudaContext, CudaStream}; 31 | use dlpark::SafeManagedTensorVersioned; 32 | use dlpark::traits::TensorView; 33 | use gdal::raster::Buffer; 34 | use gdal::{Dataset, DatasetOptions, GdalOpenFlags}; 35 | use ndarray::Array2; 36 | 37 | // nvtiff 38 | #[cfg(feature = "cuda")] 39 | fn read_geotiff_nvtiff(fpath: &str) { 40 | let v = std::fs::read(fpath).unwrap(); 41 | let bytes = Bytes::copy_from_slice(&v); 42 | 43 | let ctx: Arc = CudaContext::new(0).unwrap(); // Set on GPU:0 44 | let cuda_stream: Arc = ctx.default_stream(); 45 | 46 | let cog = CudaCogReader::new(&bytes, &cuda_stream).unwrap(); 47 | let tensor: SafeManagedTensorVersioned = cog.dlpack().unwrap(); 48 | 49 | assert_eq!(tensor.num_elements(), 3 * 10980 * 10980); 50 | // drop(cog); 51 | // cuda_stream.synchronize().unwrap(); 52 | } 53 | 54 | // gdal 55 | fn read_geotiff_gdal(fpath: &str) { 56 | let options = DatasetOptions { 57 | open_flags: GdalOpenFlags::default(), 58 | allowed_drivers: Some(&["LIBERTIFF"]), 59 | open_options: Some(&["NUM_THREADS=4"]), 60 | sibling_files: None, 61 | }; 62 | let ds = Dataset::open_ex(fpath, options).unwrap(); 63 | 64 | for b in 1..3 { 65 | let band = ds.rasterband(b).unwrap(); 66 | let buffer: Buffer = band.read_band_as::().unwrap(); 67 | let array: Array2<_> = buffer.to_array().unwrap(); 68 | 69 | assert_eq!(array.shape(), [10980, 10980]); 70 | 71 | #[cfg(feature = "cuda")] 72 | { 73 | // Copy from CPU (host) memory to CUDA (device) memory 74 | let ctx: Arc = CudaContext::new(0).unwrap(); // Set on GPU:0 75 | let cuda_stream: Arc = ctx.default_stream(); 76 | let mut cuda_mem = cuda_stream.alloc_zeros::(3 * 10980 * 10980).unwrap(); 77 | 78 | cuda_stream 79 | .memcpy_htod(array.as_slice().unwrap(), &mut cuda_mem) 80 | .unwrap(); 81 | } 82 | } 83 | } 84 | 85 | // image-tiff 86 | fn read_geotiff_image_tiff(fpath: &str) { 87 | let file = File::open(fpath).unwrap(); 88 | 89 | let mut cog = CogReader::new(file).unwrap(); 90 | let tensor: SafeManagedTensorVersioned = cog.dlpack().unwrap(); 91 | 92 | assert_eq!(tensor.num_elements(), 3 * 10980 * 10980); 93 | 94 | #[cfg(feature = "cuda")] 95 | { 96 | // Copy from CPU (host) memory to CUDA (device) memory 97 | let ctx: Arc = CudaContext::new(0).unwrap(); // Set on GPU:0 98 | let cuda_stream: Arc = ctx.default_stream(); 99 | let mut cuda_mem = cuda_stream.alloc_zeros::(3 * 10980 * 10980).unwrap(); 100 | 101 | let slice: &[u8] = tensor.as_slice_untyped(); 102 | cuda_stream.memcpy_htod(slice, &mut cuda_mem).unwrap(); 103 | } 104 | } 105 | 106 | fn criterion_benchmark(c: &mut Criterion) { 107 | let mut group = c.benchmark_group("read_cog"); 108 | 109 | let fsize: u64 = std::fs::metadata("benches/TCI.tif").unwrap().len(); 110 | group.throughput(Throughput::BytesDecimal(fsize)); // 318MB filesize 111 | 112 | // GPU decoding using nvTIFF, reduce sample size because of CUDA memory leak 113 | #[cfg(feature = "cuda")] 114 | group 115 | .sample_size(10) 116 | .nresamples(2) 117 | .warm_up_time(Duration::from_millis(1)) 118 | .measurement_time(Duration::from_secs(2)); 119 | // .noise_threshold(0.15); 120 | #[cfg(feature = "cuda")] 121 | group.bench_with_input( 122 | BenchmarkId::new("0_nvTIFF_GPU", "Sentinel-2 TCI"), 123 | "benches/TCI.tif", 124 | |b, p| b.iter(|| read_geotiff_nvtiff(p)), 125 | ); 126 | 127 | // CPU decoding using GDAL 128 | group.sample_size(30); 129 | group.bench_with_input( 130 | BenchmarkId::new("1_gdal_CPU", "Sentinel-2 TCI"), 131 | "benches/TCI.tif", 132 | |b, p| b.iter(|| read_geotiff_gdal(p)), 133 | ); 134 | 135 | // CPU decoding based on image-tiff 136 | group.sample_size(30); 137 | group.bench_with_input( 138 | BenchmarkId::new("2_image-tiff_CPU", "Sentinel-2 TCI"), 139 | "benches/TCI.tif", 140 | |b, p| b.iter(|| read_geotiff_image_tiff(p)), 141 | ); 142 | 143 | group.finish(); 144 | } 145 | 146 | criterion_group!(benches, criterion_benchmark); 147 | criterion_main!(benches); 148 | -------------------------------------------------------------------------------- /src/python/adapters.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::doc_markdown)] 2 | use std::io::Cursor; 3 | 4 | use bytes::Bytes; 5 | use dlpark::SafeManagedTensorVersioned; 6 | use dlpark::ffi::Device; 7 | use ndarray::Array3; 8 | use numpy::{PyArray1, PyArray3, ToPyArray}; 9 | use object_store::{ObjectStore, parse_url}; 10 | use pyo3::exceptions::{PyBufferError, PyFileNotFoundError, PyValueError}; 11 | use pyo3::prelude::{PyModule, PyResult, Python, pyclass, pyfunction, pymethods, pymodule}; 12 | use pyo3::types::PyModuleMethods; 13 | use pyo3::{Bound, PyErr, wrap_pyfunction}; 14 | use url::Url; 15 | 16 | use crate::io::geotiff::{CogReader, read_geotiff}; 17 | 18 | /// Python class interface to a Cloud-optimized GeoTIFF reader. 19 | /// 20 | /// Parameters 21 | /// ---------- 22 | /// path : str 23 | /// The path to the file, or a url to a remote file. 24 | /// 25 | /// Returns 26 | /// ------- 27 | /// reader : cog3pio.CogReader 28 | /// A new CogReader instance for decoding GeoTIFF files. 29 | /// 30 | /// Examples 31 | /// -------- 32 | /// Read a GeoTIFF from a HTTP url into a DLPack tensor: 33 | /// 34 | /// >>> import numpy as np 35 | /// >>> from cog3pio import CogReader 36 | /// ... 37 | /// >>> cog = CogReader( 38 | /// ... path="https://github.com/rasterio/rasterio/raw/refs/tags/1.4.3/tests/data/RGBA.uint16.tif" 39 | /// ...) 40 | /// >>> array: np.ndarray = np.from_dlpack(cog) 41 | /// >>> array.shape 42 | /// (4, 411, 634) 43 | /// >>> array.dtype 44 | /// dtype('uint16') 45 | #[pyclass] 46 | #[pyo3(name = "CogReader")] 47 | struct PyCogReader { 48 | inner: CogReader>, 49 | } 50 | 51 | #[pymethods] 52 | impl PyCogReader { 53 | #[new] 54 | fn new(path: &str) -> PyResult { 55 | let stream: Cursor = path_to_stream(path)?; 56 | let reader = 57 | CogReader::new(stream).map_err(|err| PyValueError::new_err(err.to_string()))?; 58 | 59 | Ok(Self { inner: reader }) 60 | } 61 | 62 | /// Get image pixel data from GeoTIFF as a DLPack capsule 63 | /// 64 | /// Returns 65 | /// ------- 66 | /// tensor : PyCapsule 67 | /// 3D tensor of shape (band, height, width) containing the GeoTIFF pixel data. 68 | fn __dlpack__(&mut self) -> PyResult { 69 | // Convert from ndarray (Rust) to DLPack (Python) 70 | let tensor: SafeManagedTensorVersioned = self 71 | .inner 72 | .dlpack() 73 | .map_err(|err| PyValueError::new_err(err.to_string()))?; 74 | 75 | Ok(tensor) 76 | } 77 | 78 | /// Get device type and device ID in DLPack format. 79 | /// 80 | /// Meant for use by `from_dlpack()`. 81 | /// 82 | /// Returns 83 | /// ------- 84 | /// device : (int, int) 85 | /// A tuple (device_type, device_id) in DLPack format. 86 | #[staticmethod] 87 | fn __dlpack_device__() -> (i32, i32) { 88 | let device = Device::CPU; 89 | (device.device_type as i32, device.device_id) 90 | } 91 | 92 | /// Get list of x and y coordinates. 93 | /// 94 | /// Determined based on an Affine transformation matrix built from the 95 | /// `ModelPixelScaleTag` and `ModelTiepointTag` TIFF tags. Note that non-zero 96 | /// rotation (set by `ModelTransformationTag` is currently unsupported. 97 | /// 98 | /// Returns 99 | /// ------- 100 | /// coords : (np.ndarray, np.ndarray) 101 | /// A tuple (x_coords, y_coords) of np.ndarray objects representing the GeoTIFF's 102 | /// x- and y-coordinates. 103 | #[allow(clippy::type_complexity)] 104 | fn xy_coords<'py>( 105 | &mut self, 106 | py: Python<'py>, 107 | ) -> PyResult<(Bound<'py, PyArray1>, Bound<'py, PyArray1>)> { 108 | let (x_coords, y_coords) = self 109 | .inner 110 | .xy_coords() 111 | .map_err(|err| PyValueError::new_err(err.to_string()))?; 112 | 113 | Ok((x_coords.to_pyarray(py), y_coords.to_pyarray(py))) 114 | } 115 | } 116 | 117 | /// Read from a filepath or url into a byte stream 118 | fn path_to_stream(path: &str) -> PyResult> { 119 | // Parse URL into ObjectStore and path 120 | let file_or_url = match Url::from_file_path(path) { 121 | // Parse local filepath 122 | Ok(filepath) => filepath, 123 | // Parse remote URL 124 | Err(()) => Url::parse(path) 125 | .map_err(|_| PyValueError::new_err(format!("Cannot parse path: {path}")))?, 126 | }; 127 | let (store, location) = parse_url(&file_or_url) 128 | .map_err(|_| PyValueError::new_err(format!("Cannot parse url: {file_or_url}")))?; 129 | 130 | // Initialize async runtime 131 | let runtime = tokio::runtime::Builder::new_current_thread() 132 | .enable_all() 133 | .build()?; 134 | 135 | // Get TIFF file stream asynchronously 136 | let stream = runtime.block_on(async { 137 | let result = store 138 | .get(&location) 139 | .await 140 | .map_err(|_| PyFileNotFoundError::new_err(format!("Cannot find file: {path}")))?; 141 | let bytes = result.bytes().await.map_err(|_| { 142 | PyBufferError::new_err(format!("Failed to stream data from {path} into bytes.")) 143 | })?; 144 | // Return cursor to in-memory buffer 145 | Ok::, PyErr>(Cursor::new(bytes)) 146 | })?; 147 | Ok(stream) 148 | } 149 | 150 | /// Read a GeoTIFF file from a path on disk or a url into an ndarray. 151 | /// 152 | /// Parameters 153 | /// ---------- 154 | /// path : str 155 | /// The path to the file, or a url to a remote file. 156 | /// 157 | /// Returns 158 | /// ------- 159 | /// array : np.ndarray 160 | /// 3D array of shape (band, height, width) containing the GeoTIFF pixel data. 161 | /// 162 | /// Raises 163 | /// ------ 164 | /// ValueError 165 | /// If a TIFF which has a non-float32 dtype is passed in. Please use 166 | /// `cog3pio.CogReader` for reading TIFFs with other dtypes (e.g. uint16). 167 | /// 168 | /// Examples 169 | /// -------- 170 | /// Read a GeoTIFF from a HTTP url into a numpy.ndarray: 171 | /// 172 | /// >>> from cog3pio import read_geotiff 173 | /// ... 174 | /// >>> array = read_geotiff("https://github.com/pka/georaster/raw/v0.2.0/data/tiff/float32.tif") 175 | /// >>> array.shape 176 | /// (1, 20, 20) 177 | #[pyfunction] 178 | #[pyo3(name = "read_geotiff")] 179 | fn read_geotiff_py<'py>(path: &str, py: Python<'py>) -> PyResult>> { 180 | // Open URL with TIFF decoder 181 | let stream = path_to_stream(path)?; 182 | 183 | // Decode TIFF into DLPack tensor 184 | let array: Array3 = 185 | read_geotiff(stream).map_err(|err| PyValueError::new_err(err.to_string()))?; 186 | 187 | Ok(array.to_pyarray(py)) 188 | } 189 | 190 | /// A Python module implemented in Rust. The name of this function must match 191 | /// the `lib.name` setting in the `Cargo.toml`, else Python will not be able to 192 | /// import the module. 193 | #[pymodule] 194 | fn cog3pio(_py: Python, m: &Bound<'_, PyModule>) -> PyResult<()> { 195 | // Register Python classes 196 | m.add_class::()?; 197 | // Register Python functions 198 | m.add_function(wrap_pyfunction!(read_geotiff_py, m)?)?; 199 | Ok(()) 200 | } 201 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | # This file is autogenerated by maturin v1.8.7 2 | # To update, run 3 | # 4 | # maturin generate-ci --pytest github 5 | # 6 | name: CI 7 | 8 | on: 9 | push: 10 | branches: ["main"] 11 | release: 12 | types: [published] 13 | pull_request: 14 | types: [opened, reopened, synchronize] 15 | branches: ["main"] 16 | 17 | env: 18 | CARGO_TERM_COLOR: always 19 | 20 | permissions: 21 | contents: read 22 | 23 | jobs: 24 | rust: 25 | runs-on: ubuntu-24.04 26 | strategy: 27 | matrix: 28 | toolchain: 29 | - 1.85.0 # msrv 30 | - stable 31 | - beta 32 | - nightly 33 | steps: 34 | - name: Checkout repository 35 | uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 36 | with: 37 | persist-credentials: false 38 | 39 | - name: Update Rust toolchain 40 | run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }} 41 | 42 | - name: Build 43 | run: cargo build --verbose 44 | 45 | - name: Run tests 46 | run: cargo test --verbose 47 | 48 | linux: 49 | runs-on: ${{ matrix.platform.runner || 'ubuntu-24.04' }} 50 | strategy: 51 | matrix: 52 | platform: 53 | - target: x86_64 54 | - target: aarch64 55 | runner: ubuntu-24.04-arm 56 | - target: armv7 57 | - target: s390x 58 | - target: ppc64le 59 | steps: 60 | - name: Checkout repository 61 | uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 62 | with: 63 | persist-credentials: false 64 | 65 | - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 66 | with: 67 | python-version: "3.12" 68 | 69 | - name: Build wheels 70 | uses: PyO3/maturin-action@aef21716ff3dcae8a1c301d23ec3e4446972a6e3 # v1.49.1 71 | with: 72 | target: ${{ matrix.platform.target }} 73 | args: --release --out dist 74 | sccache: ${{ !startsWith(github.ref, 'refs/tags/') }} 75 | manylinux: "2_28" 76 | 77 | - name: Build free-threaded wheels 78 | uses: PyO3/maturin-action@aef21716ff3dcae8a1c301d23ec3e4446972a6e3 # v1.49.1 79 | with: 80 | target: ${{ matrix.platform.target }} 81 | args: --release --out dist --interpreter python3.13t 82 | sccache: ${{ !startsWith(github.ref, 'refs/tags/') }} 83 | manylinux: "2_28" 84 | 85 | - name: Upload wheels 86 | uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 87 | with: 88 | name: wheels-linux-${{ matrix.platform.target }} 89 | path: dist/ 90 | 91 | - name: pytest 92 | if: ${{ endswith(matrix.platform.target, '64') }} # x86_64 and aarch64 93 | shell: bash 94 | run: | 95 | set -e 96 | python3 -m venv .venv 97 | source .venv/bin/activate 98 | pip install cog3pio[tests] --find-links dist --force-reinstall 99 | pytest --verbose 100 | 101 | - name: pytest 102 | if: ${{ !endswith(matrix.platform.target, '64') && !startsWith(github.ref, 'refs/tags/') }} # armv7, s390x and ppc64le 103 | uses: uraimo/run-on-arch-action@d94c13912ea685de38fccc1109385b83fd79427d # v3.0.1 104 | with: 105 | arch: ${{ matrix.platform.target }} 106 | distro: ubuntu24.04 107 | githubToken: ${{ github.token }} 108 | install: | 109 | apt update 110 | apt install -y --no-install-recommends \ 111 | gcc g++ gfortran libopenblas-dev liblapack-dev ninja-build \ 112 | pkg-config python3-pip python3-dev python3-venv 113 | run: | 114 | set -e 115 | python3 -m venv .venv 116 | source .venv/bin/activate 117 | pip3 install -U pip 118 | pip3 install cog3pio[tests] --find-links dist --force-reinstall 119 | pytest --verbose 120 | 121 | windows: 122 | runs-on: windows-2025 123 | strategy: 124 | matrix: 125 | target: [x64] 126 | steps: 127 | - name: Checkout repository 128 | uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 129 | with: 130 | persist-credentials: false 131 | 132 | - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 133 | with: 134 | python-version: "3.12" 135 | architecture: ${{ matrix.target }} 136 | 137 | - name: Support longpaths 138 | run: git config --system core.longpaths true 139 | 140 | - name: Build wheels 141 | uses: PyO3/maturin-action@aef21716ff3dcae8a1c301d23ec3e4446972a6e3 # v1.49.1 142 | with: 143 | target: ${{ matrix.target }} 144 | args: --release --out dist 145 | sccache: ${{ !startsWith(github.ref, 'refs/tags/') }} 146 | 147 | - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 148 | with: 149 | python-version: "3.14t" 150 | architecture: ${{ matrix.target }} 151 | 152 | - name: Build free-threaded wheels 153 | uses: PyO3/maturin-action@aef21716ff3dcae8a1c301d23ec3e4446972a6e3 # v1.49.1 154 | with: 155 | target: ${{ matrix.target }} 156 | args: --release --out dist -i python3.14t 157 | sccache: ${{ !startsWith(github.ref, 'refs/tags/') }} 158 | 159 | - name: Upload wheels 160 | uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 161 | with: 162 | name: wheels-windows-${{ matrix.target }} 163 | path: dist/ 164 | 165 | - name: pytest 166 | shell: bash 167 | run: | 168 | set -e 169 | python3 -m venv .venv 170 | source .venv/Scripts/activate 171 | pip install cog3pio[tests] --find-links dist --force-reinstall 172 | pytest --verbose 173 | 174 | macos: 175 | runs-on: ${{ matrix.platform.runner }} 176 | strategy: 177 | matrix: 178 | platform: 179 | - runner: macos-15-intel 180 | target: x86_64 181 | - runner: macos-14 182 | target: aarch64 183 | steps: 184 | - name: Checkout repository 185 | uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 186 | with: 187 | persist-credentials: false 188 | 189 | - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 190 | with: 191 | python-version: "3.12" 192 | 193 | - name: Build wheels 194 | uses: PyO3/maturin-action@aef21716ff3dcae8a1c301d23ec3e4446972a6e3 # v1.49.1 195 | with: 196 | target: ${{ matrix.platform.target }} 197 | args: --release --out dist 198 | sccache: ${{ !startsWith(github.ref, 'refs/tags/') }} 199 | 200 | - name: Build free-threaded wheels 201 | uses: PyO3/maturin-action@aef21716ff3dcae8a1c301d23ec3e4446972a6e3 # v1.49.1 202 | with: 203 | target: ${{ matrix.platform.target }} 204 | args: --release --out dist -i python3.13t 205 | sccache: ${{ !startsWith(github.ref, 'refs/tags/') }} 206 | 207 | - name: Upload wheels 208 | uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 209 | with: 210 | name: wheels-macos-${{ matrix.platform.target }} 211 | path: dist/ 212 | 213 | - name: pytest 214 | run: | 215 | set -e 216 | python3 -m venv .venv 217 | source .venv/bin/activate 218 | pip install cog3pio[tests] --find-links dist --force-reinstall 219 | pytest --verbose 220 | 221 | sdist: 222 | runs-on: ubuntu-24.04 223 | steps: 224 | - name: Checkout repository 225 | uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 226 | with: 227 | persist-credentials: false 228 | 229 | - name: Build sdist 230 | uses: PyO3/maturin-action@aef21716ff3dcae8a1c301d23ec3e4446972a6e3 # v1.49.1 231 | with: 232 | command: sdist 233 | args: --out dist 234 | 235 | - name: Upload sdist 236 | uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 237 | with: 238 | name: wheels-sdist 239 | path: dist/ 240 | 241 | publish-to-testpypi: 242 | name: Publish Python 🐍 distribution 📦 to TestPyPI 243 | if: startsWith(github.ref, 'refs/tags/') 244 | needs: [linux, windows, macos, sdist] 245 | runs-on: ubuntu-24.04 246 | environment: 247 | name: testpypi 248 | url: https://test.pypi.org/project/cog3pio 249 | permissions: 250 | id-token: write # IMPORTANT: mandatory for trusted OIDC publishing 251 | 252 | steps: 253 | - name: Download built wheels 254 | uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1 255 | with: 256 | path: dist/ 257 | merge-multiple: true 258 | 259 | - name: Publish distribution 📦 to TestPyPI 260 | uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0 261 | with: 262 | repository-url: https://test.pypi.org/legacy/ 263 | verbose: true 264 | 265 | publish-to-pypi: 266 | name: Publish Python 🐍 distribution 📦 to PyPI 267 | runs-on: ubuntu-24.04 268 | environment: 269 | name: pypi 270 | url: https://pypi.org/project/cog3pio/ 271 | if: github.event.release.prerelease == false && startsWith(github.ref, 'refs/tags/') 272 | needs: [linux, windows, macos, sdist] 273 | permissions: 274 | id-token: write # IMPORTANT: mandatory for trusted OIDC publishing 275 | steps: 276 | - name: Download built wheels 277 | uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1 278 | with: 279 | path: dist/ 280 | merge-multiple: true 281 | 282 | - name: Publish distribution 📦 to PyPI 283 | uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0 284 | with: 285 | verbose: true 286 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /src/io/nvtiff.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::c_void; 2 | use std::sync::Arc; 3 | 4 | use bytes::Bytes; 5 | use cudarc::driver::{CudaSlice, CudaStream, CudaView, DevicePtr}; 6 | use dlpark::SafeManagedTensorVersioned; 7 | use dlpark::ffi::{DataType, DataTypeCode}; 8 | use dlpark::traits::InferDataType; 9 | use nvtiff_sys::result::{NvTiffError, NvTiffStatusError}; 10 | use nvtiff_sys::{ 11 | NvTiffResult, NvTiffResultCheck, nvtiffDecodeCheckSupported, nvtiffDecodeImage, 12 | nvtiffDecodeParams, nvtiffDecoder, nvtiffDecoderCreateSimple, nvtiffFileInfo, 13 | nvtiffSampleFormat, nvtiffStatus_t, nvtiffStream, nvtiffStreamCreate, nvtiffStreamGetFileInfo, 14 | nvtiffStreamParse, 15 | }; 16 | 17 | /// Cloud-optimized GeoTIFF reader using [`nvTIFF`](https://developer.nvidia.com/nvtiff) 18 | /// 19 | /// # Examples 20 | /// 21 | /// ## DLPack 22 | /// 23 | /// Retrieve a GeoTIFF file stream via the [`object_store`] crate, and set up a CUDA 24 | /// stream via the [`cudarc`] crate. Pass the file stream and CUDA stream into the 25 | /// [`CudaCogReader::new`](crate::io::nvtiff::CudaCogReader::new) method to instantiate 26 | /// a [`CudaCogReader`] struct, and call 27 | /// [`.dlpack()`](crate::io::nvtiff::CudaCogReader::dlpack) to get a 28 | /// [`dlpark::SafeManagedTensorVersioned`] output. 29 | /// 30 | /// ```rust 31 | /// use std::sync::Arc; 32 | /// 33 | /// use bytes::Bytes; 34 | /// use cog3pio::io::nvtiff::CudaCogReader; 35 | /// use cudarc::driver::{CudaContext, CudaStream}; 36 | /// use dlpark::SafeManagedTensorVersioned; 37 | /// use dlpark::ffi::DataType; 38 | /// use dlpark::prelude::TensorView; 39 | /// use object_store::path::Path; 40 | /// use object_store::{GetResult, ObjectStore, parse_url}; 41 | /// use tokio; 42 | /// use url::Url; 43 | /// 44 | /// #[tokio::main] 45 | /// async fn main() { 46 | /// let cog_url: &str = 47 | /// "https://github.com/cogeotiff/rio-tiler/raw/7.9.0/tests/fixtures/cog_nodata_float_nan.tif"; 48 | /// let tif_url: Url = Url::parse(cog_url).unwrap(); 49 | /// let (store, location): (Box, Path) = parse_url(&tif_url).unwrap(); 50 | /// 51 | /// let result: GetResult = store.get(&location).await.unwrap(); 52 | /// let bytes: Bytes = result.bytes().await.unwrap(); 53 | /// 54 | /// let ctx: Arc = cudarc::driver::CudaContext::new(0).unwrap(); // Set on GPU:0 55 | /// let cuda_stream: Arc = ctx.default_stream(); 56 | /// 57 | /// // Read GeoTIFF into a dlpark::versioned::SafeManagedTensorVersioned 58 | /// let mut cog = CudaCogReader::new(&bytes, &cuda_stream).unwrap(); 59 | /// let tensor: SafeManagedTensorVersioned = cog.dlpack().unwrap(); 60 | /// assert_eq!(tensor.shape(), [7088886]); // [1, 2667, 2658] 61 | /// assert_eq!(tensor.data_type(), &DataType::F32); 62 | /// } 63 | /// ``` 64 | /// 65 | /// Note that the DLPack output is a flattened 1D array in row-major order (i.e. 66 | /// rows-first, columns-next). Common dtypes such as uint (u8/u16/u32/u64), int 67 | /// (i8/i16/i32/i64) and float (f32/f64) should be mostly supported. Other dtypes such 68 | /// as f16, complex32, etc and certain compression schemes are not supported yet. 69 | pub struct CudaCogReader { 70 | tiff_stream: *mut nvtiffStream, 71 | num_bytes: usize, 72 | dtype: DataType, 73 | cuda_slice: CudaSlice, 74 | } 75 | 76 | impl CudaCogReader { 77 | /// Create a new Cloud-optimized GeoTIFF decoder that decodes from a CUDA stream 78 | /// buffer 79 | /// 80 | /// # Errors 81 | /// Will return [`nvtiff_sys::result::NvTiffError::StatusError`] if nvTIFF failed to 82 | /// parse the TIFF data or metadata from the byte stream buffer. 83 | /// 84 | /// # Panics 85 | /// Will panic if [`CudaStream::alloc_zeros`] failed to allocate bytes on CUDA 86 | /// device memory, usually due to 87 | /// [`cudarc::driver::sys::cudaError_enum::CUDA_ERROR_OUT_OF_MEMORY`] 88 | pub fn new(byte_stream: &Bytes, cuda_stream: &Arc) -> NvTiffResult { 89 | // Step 0: Init TIFF stream on host (CPU) 90 | let mut host_stream = std::mem::MaybeUninit::uninit(); 91 | let mut tiff_stream: *mut nvtiffStream = host_stream.as_mut_ptr(); 92 | 93 | let status_cpustream: nvtiffStatus_t::Type = 94 | unsafe { nvtiffStreamCreate(&raw mut tiff_stream) }; 95 | dbg!(status_cpustream); 96 | status_cpustream.result()?; 97 | 98 | // Step 1: Parse the TIFF data from byte stream buffer 99 | let status_parse: u32 = 100 | unsafe { nvtiffStreamParse(byte_stream.as_ptr(), usize::MAX, tiff_stream) }; 101 | dbg!(status_parse); 102 | status_parse.result()?; 103 | 104 | // Step 2: Extract file-level metadata information from the TIFF stream 105 | let mut file_info = nvtiffFileInfo::default(); 106 | let status_fileinfo: u32 = 107 | unsafe { nvtiffStreamGetFileInfo(tiff_stream, &raw mut file_info) }; 108 | dbg!(status_fileinfo); 109 | // dbg!(file_info); 110 | status_fileinfo.result()?; 111 | 112 | // Step 3a: Determine dtype from sample_format and bits_per_pixel 113 | // Assume that all samples/bands have the same dtype 114 | let sample_format: u32 = file_info.sample_format[0]; 115 | let dtype_code: DataTypeCode = match sample_format { 116 | nvtiffSampleFormat::NVTIFF_SAMPLEFORMAT_UINT => DataTypeCode::UInt, 117 | nvtiffSampleFormat::NVTIFF_SAMPLEFORMAT_INT => DataTypeCode::Int, 118 | nvtiffSampleFormat::NVTIFF_SAMPLEFORMAT_IEEEFP => DataTypeCode::Float, 119 | _ => unimplemented!( 120 | "non uint/int/float dtypes (e.g. complex int/float) not implemented yet" 121 | ), 122 | }; 123 | let bits: u16 = file_info.bits_per_pixel / file_info.samples_per_pixel; 124 | let dtype: DataType = DataType { 125 | code: dtype_code, 126 | bits: u8::try_from(bits) 127 | .map_err(|_| NvTiffError::StatusError(NvTiffStatusError::TiffNotSupported))?, 128 | lanes: 1, 129 | }; 130 | let bytes_per_pixel: usize = file_info.bits_per_pixel as usize / 8; 131 | 132 | // Step 3b: Allocate memory on device, get pointer, do the TIFF decoding 133 | let num_bytes: usize = file_info.image_width as usize // Width 134 | * file_info.image_height as usize // Height 135 | * bytes_per_pixel; // Bytes per pixel (e.g. 4 bytes for f32) 136 | dbg!(num_bytes); 137 | let image_stream: CudaSlice = 138 | cuda_stream 139 | .alloc_zeros::(num_bytes) 140 | .unwrap_or_else(|err| { 141 | panic!("Failed to allocate {num_bytes} bytes on CUDA device: {err}") 142 | }); 143 | 144 | Ok(Self { 145 | tiff_stream, 146 | num_bytes, 147 | dtype, 148 | cuda_slice: image_stream, 149 | }) 150 | } 151 | 152 | /// Decode GeoTIFF image to a [`dlpark::SafeManagedTensorVersioned`] 153 | /// 154 | /// # Errors 155 | /// 156 | /// Will raise [`nvtiff_sys::result::NvTiffError::StatusError`] if decoding failed 157 | /// due to e.g. TIFF stream not being supported by nvTIFF, missing 158 | /// nvCOMP/nvJPEG/nvJPEG2K libraries, etc. 159 | pub fn dlpack(&self) -> NvTiffResult { 160 | // Step 1b: Init CUDA stream on device (GPU) 161 | let stream: &Arc = self.cuda_slice.stream(); 162 | let cuda_stream: *mut nvtiff_sys::CUstream_st = stream.cu_stream().cast::<_>(); 163 | 164 | // Step 1c: Init decoder handle 165 | let mut decoder_handle = std::mem::MaybeUninit::zeroed(); 166 | let mut nvtiff_decoder: *mut nvtiffDecoder = decoder_handle.as_mut_ptr(); 167 | 168 | let status_decoder: u32 = 169 | unsafe { nvtiffDecoderCreateSimple(&raw mut nvtiff_decoder, cuda_stream) }; 170 | dbg!(status_decoder); 171 | status_decoder.result()?; 172 | 173 | // Step 3a: Check if image is supported first 174 | let mut params = std::mem::MaybeUninit::zeroed(); 175 | let decode_params: *mut nvtiffDecodeParams = params.as_mut_ptr(); 176 | let status_check: u32 = unsafe { 177 | nvtiffDecodeCheckSupported( 178 | self.tiff_stream, // TODO keep lifetime on this? 179 | nvtiff_decoder, 180 | decode_params, 181 | 0, // image_id 182 | ) 183 | }; 184 | dbg!(status_check); // 4: NVTIFF_STATUS_TIFF_NOT_SUPPORTED; 2: NVTIFF_STATUS_INVALID_PARAMETER 185 | status_check.result()?; 186 | 187 | // Step 3b: Do the TIFF decoding to allocated device memory 188 | let (image_ptr, _record): (u64, _) = self.cuda_slice.device_ptr(stream); 189 | let image_out_d = image_ptr as *mut c_void; 190 | let status_decode: u32 = unsafe { 191 | nvtiffDecodeImage( 192 | self.tiff_stream, 193 | nvtiff_decoder, 194 | decode_params, 195 | 0, // image_id 196 | image_out_d, 197 | cuda_stream, 198 | ) 199 | }; 200 | dbg!(status_decode); // 4: NVTIFF_STATUS_TIFF_NOT_SUPPORTED; 8: NVTIFF_STATUS_INTERNAL_ERROR 201 | status_decode.result()?; 202 | 203 | // dbg!(self.tiff_stream); // TODO need this to avoid panic on status_check/status_decode? 204 | 205 | // Create CudaSlice from pointer 206 | let cuslice: CudaSlice = 207 | unsafe { stream.upgrade_device_ptr(image_ptr, self.num_bytes) }; 208 | 209 | // Transmute from u8 to actual dtype before putting into DLPack tensor 210 | let len_elem: usize = self.num_bytes / (self.dtype.bits as usize / 8); 211 | let tensor: SafeManagedTensorVersioned = match self.dtype { 212 | DataType::U8 => SafeManagedTensorVersioned::new(cuslice) 213 | .map_err(|_| NvTiffError::StatusError(NvTiffStatusError::AllocatorFailure))?, 214 | DataType::U16 => cudaslice_to_tensor::(cuslice, len_elem)?, 215 | DataType::U32 => cudaslice_to_tensor::(cuslice, len_elem)?, 216 | DataType::U64 => cudaslice_to_tensor::(cuslice, len_elem)?, 217 | DataType::I8 => cudaslice_to_tensor::(cuslice, len_elem)?, 218 | DataType::I16 => cudaslice_to_tensor::(cuslice, len_elem)?, 219 | DataType::I32 => cudaslice_to_tensor::(cuslice, len_elem)?, 220 | DataType::I64 => cudaslice_to_tensor::(cuslice, len_elem)?, 221 | DataType::F32 => cudaslice_to_tensor::(cuslice, len_elem)?, 222 | DataType::F64 => cudaslice_to_tensor::(cuslice, len_elem)?, 223 | dtype => { 224 | unimplemented!("Converting {dtype:?} into DLPack not supported yet.") 225 | } 226 | }; 227 | 228 | Ok(tensor) 229 | } 230 | } 231 | 232 | /// Transmute `CudaSlice` into a `CudaView`, and then convert to a DLPack tensor. 233 | fn cudaslice_to_tensor( 234 | cuslice: CudaSlice, 235 | len_elem: usize, 236 | ) -> NvTiffResult { 237 | let cuview: CudaView<_> = unsafe { cuslice.transmute::(len_elem) } 238 | .ok_or(NvTiffError::StatusError(NvTiffStatusError::BadTiff))?; 239 | let tensor = SafeManagedTensorVersioned::new(cuview) 240 | // TODO raise error from err string 241 | .map_err(|_| NvTiffError::StatusError(NvTiffStatusError::AllocatorFailure))?; 242 | cuslice.leak(); 243 | 244 | Ok(tensor) 245 | } 246 | 247 | #[cfg(test)] 248 | mod tests { 249 | 250 | use std::sync::Arc; 251 | 252 | use cudarc::driver::{CudaContext, CudaSlice, CudaStream}; 253 | use dlpark::SafeManagedTensorVersioned; 254 | use dlpark::ffi::DataType; 255 | use dlpark::prelude::TensorView; 256 | use object_store::parse_url; 257 | use rstest::rstest; 258 | use url::Url; 259 | 260 | use crate::io::nvtiff::CudaCogReader; 261 | 262 | #[tokio::test] 263 | async fn cudacogreader_dlpack() { 264 | let cog_url: &str = 265 | "https://github.com/rasterio/rasterio/raw/refs/tags/1.4.3/tests/data/float32.tif"; 266 | let tif_url = Url::parse(cog_url).unwrap(); 267 | let (store, location) = parse_url(&tif_url).unwrap(); 268 | 269 | let result = store.get(&location).await.unwrap(); 270 | let bytes = result.bytes().await.unwrap(); 271 | 272 | // let v = std::fs::read("benches/float32.tif").unwrap(); 273 | // let bytes = Bytes::copy_from_slice(&v); 274 | 275 | // Step 1: Init CUDA stream on device (GPU) 276 | let ctx: Arc = cudarc::driver::CudaContext::new(0).unwrap(); // Set on GPU:0 277 | let cuda_stream: Arc = ctx.default_stream(); 278 | 279 | // Step 2: Do the TIFF decoding 280 | let cog: CudaCogReader = CudaCogReader::new(&bytes, &cuda_stream).unwrap(); 281 | let tensor: SafeManagedTensorVersioned = cog.dlpack().unwrap(); 282 | 283 | assert_eq!(tensor.data_type(), &DataType::F32); 284 | // assert_eq!(tensor.shape(), [1, 2, 3]); // TODO should be 3D tensor 285 | assert_eq!(tensor.shape(), [6]); 286 | 287 | // Step 3: Transfer decoded bytes from device to host, and check results 288 | let mut image_out_h: Vec = vec![0.0; tensor.num_elements()]; 289 | let cuslice: CudaSlice<_> = unsafe { 290 | cuda_stream.upgrade_device_ptr(tensor.data_ptr() as u64, tensor.num_elements()) 291 | }; 292 | cuda_stream 293 | .memcpy_dtoh(&cuslice.clone(), &mut image_out_h) 294 | .unwrap(); 295 | dbg!(image_out_h.clone()); 296 | assert_eq!(image_out_h, vec![1.41, 1.23, 0.78, 0.32, -0.23, -1.88]); 297 | } 298 | 299 | #[rstest] 300 | #[case::u8("byte.tif", DataType::U8)] 301 | #[case::u16("uint16.tif", DataType::U16)] 302 | #[case::u32("uint32.tif", DataType::U32)] 303 | // #[case::u64("uint64.tif", DataType::U64)] // TiffNotSupported 304 | #[case::i16("int16.tif", DataType::I16)] 305 | #[case::i32("int32.tif", DataType::I32)] 306 | // #[case::i64("int64.tif", DataType::I64)] // TiffNotSupported 307 | #[tokio::test] 308 | async fn cudacogreader_dlpack_uint_int_dtypes(#[case] filename: &str, #[case] dtype: DataType) { 309 | let cog_url: String = format!( 310 | "https://github.com/OSGeo/gdal/raw/v3.12.0beta1/autotest/gcore/data/{filename}", 311 | ); 312 | let tif_url = Url::parse(cog_url.as_str()).unwrap(); 313 | let (store, location) = parse_url(&tif_url).unwrap(); 314 | 315 | let result = store.get(&location).await.unwrap(); 316 | let bytes = result.bytes().await.unwrap(); 317 | 318 | // Step 1: Init CUDA stream on device (GPU) 319 | let ctx: Arc = cudarc::driver::CudaContext::new(0).unwrap(); // Set on GPU:0 320 | let cuda_stream: Arc = ctx.default_stream(); 321 | 322 | // Step 2: Do the TIFF decoding 323 | let cog: CudaCogReader = CudaCogReader::new(&bytes, &cuda_stream).unwrap(); 324 | let tensor: SafeManagedTensorVersioned = cog.dlpack().unwrap(); 325 | 326 | assert_eq!(tensor.data_type(), &dtype); 327 | // assert_eq!(tensor.shape(), [1, 20, 20]); // TODO should be 3D tensor 328 | assert_eq!(tensor.shape(), [400]); 329 | } 330 | } 331 | -------------------------------------------------------------------------------- /src/io/geotiff.rs: -------------------------------------------------------------------------------- 1 | use std::io::{Error, Read, Seek}; 2 | 3 | use dlpark::SafeManagedTensorVersioned; 4 | use dlpark::traits::InferDataType; 5 | use geo::AffineTransform; 6 | use ndarray::{Array, Array1, Array3, ArrayView3, ArrayViewD}; 7 | use tiff::decoder::{Decoder, DecodingResult, Limits}; 8 | use tiff::tags::Tag; 9 | use tiff::{ColorType, TiffError, TiffFormatError, TiffResult, TiffUnsupportedError}; 10 | 11 | /// Cloud-optimized GeoTIFF reader 12 | pub struct CogReader { 13 | /// TIFF decoder 14 | decoder: Decoder, 15 | } 16 | 17 | impl CogReader { 18 | /// Create a new GeoTIFF decoder that decodes from a stream buffer 19 | /// 20 | /// # Errors 21 | /// 22 | /// Will return [`tiff::TiffFormatError`] if TIFF stream signature cannot be found 23 | /// or is invalid, e.g. from a corrupted input file. 24 | pub fn new(stream: R) -> TiffResult { 25 | // Open TIFF stream with decoder 26 | let mut decoder = Decoder::new(stream)?; 27 | decoder = decoder.with_limits(Limits::unlimited()); 28 | 29 | Ok(Self { decoder }) 30 | } 31 | 32 | /// Decode GeoTIFF image to a [`dlpark::SafeManagedTensorVersioned`] 33 | #[allow(clippy::missing_errors_doc)] 34 | pub fn dlpack(&mut self) -> TiffResult { 35 | // Count number of bands 36 | let num_bands: usize = self.num_samples()?; 37 | // Get image dimensions 38 | let (width, height): (u32, u32) = self.decoder.dimensions()?; 39 | 40 | // Get image pixel data 41 | let decode_result = self.decoder.read_image()?; 42 | 43 | let shape = (num_bands, height as usize, width as usize); 44 | let tensor: SafeManagedTensorVersioned = match decode_result { 45 | DecodingResult::U8(img_data) => shape_vec_to_tensor(shape, img_data)?, 46 | DecodingResult::U16(img_data) => shape_vec_to_tensor(shape, img_data)?, 47 | DecodingResult::U32(img_data) => shape_vec_to_tensor(shape, img_data)?, 48 | DecodingResult::U64(img_data) => shape_vec_to_tensor(shape, img_data)?, 49 | DecodingResult::I8(img_data) => shape_vec_to_tensor(shape, img_data)?, 50 | DecodingResult::I16(img_data) => shape_vec_to_tensor(shape, img_data)?, 51 | DecodingResult::I32(img_data) => shape_vec_to_tensor(shape, img_data)?, 52 | DecodingResult::I64(img_data) => shape_vec_to_tensor(shape, img_data)?, 53 | DecodingResult::F16(img_data) => shape_vec_to_tensor(shape, img_data)?, 54 | DecodingResult::F32(img_data) => shape_vec_to_tensor(shape, img_data)?, 55 | DecodingResult::F64(img_data) => shape_vec_to_tensor(shape, img_data)?, 56 | }; 57 | 58 | Ok(tensor) 59 | } 60 | 61 | /// Number of samples per pixel, also known as channels or bands 62 | fn num_samples(&mut self) -> TiffResult { 63 | let color_type = self.decoder.colortype()?; 64 | let num_bands: usize = match color_type { 65 | ColorType::Multiband { 66 | bit_depth: _, 67 | num_samples, 68 | } => num_samples as usize, 69 | ColorType::Gray(_) => 1, 70 | ColorType::RGB(_) => 3, 71 | _ => { 72 | return Err(TiffError::UnsupportedError( 73 | TiffUnsupportedError::UnsupportedColorType(color_type), 74 | )); 75 | } 76 | }; 77 | Ok(num_bands) 78 | } 79 | 80 | /// Affine transformation for 2D matrix extracted from TIFF tag metadata, used to transform 81 | /// image pixel (row, col) coordinates to and from geographic/projected (x, y) coordinates. 82 | /// 83 | /// ```text 84 | /// | x' | | a b c | | x | 85 | /// | y' | = | d e f | | y | 86 | /// | 1 | | 0 0 1 | | 1 | 87 | /// ``` 88 | /// 89 | /// where (`x'` and `y'`) are world coordinates, and (`x`, `y`) are the pixel's 90 | /// image coordinates. Letters a to f represent: 91 | /// 92 | /// - `a` - width of a pixel (x-resolution) 93 | /// - `b` - row rotation (typically zero) 94 | /// - `c` - x-coordinate of the *center* of the upper-left pixel (x-origin) 95 | /// - `d` - column rotation (typically zero) 96 | /// - `e` - height of a pixel (y-resolution, typically negative) 97 | /// - `f` - y-coordinate of the *center* of the upper-left pixel (y-origin) 98 | /// 99 | /// References: 100 | /// - 101 | fn transform(&mut self) -> TiffResult> { 102 | // Get x and y axis rotation (not yet implemented) 103 | let (x_rotation, y_rotation): (f64, f64) = 104 | match self.decoder.get_tag_f64_vec(Tag::ModelTransformationTag) { 105 | Ok(_model_transformation) => unimplemented!("Non-zero rotation is not handled yet"), 106 | Err(_) => (0.0, 0.0), 107 | }; 108 | 109 | // Get pixel size in x and y direction 110 | let pixel_scale: Vec = self.decoder.get_tag_f64_vec(Tag::ModelPixelScaleTag)?; 111 | let [x_scale, y_scale, _z_scale] = pixel_scale[0..3] else { 112 | return Err(TiffError::FormatError(TiffFormatError::InvalidTag)); 113 | }; 114 | 115 | // Get x and y coordinates of upper left pixel 116 | let tie_points: Vec = self.decoder.get_tag_f64_vec(Tag::ModelTiepointTag)?; 117 | let [_i, _j, _k, x_origin, y_origin, _z_origin] = tie_points[0..6] else { 118 | return Err(TiffError::FormatError(TiffFormatError::InvalidTag)); 119 | }; 120 | 121 | // Create affine transformation matrix 122 | let transform = AffineTransform::new( 123 | x_scale, x_rotation, x_origin, y_rotation, -y_scale, y_origin, 124 | ); 125 | 126 | Ok(transform) 127 | } 128 | 129 | /// Get list of x and y coordinates 130 | /// 131 | /// Determined based on an [`geo::AffineTransform`] matrix built from the 132 | /// [`tiff::tags::Tag::ModelPixelScaleTag`] and 133 | /// [`tiff::tags::Tag::ModelTiepointTag`] tags. Note that non-zero rotation (set by 134 | /// [`tiff::tags::Tag::ModelTransformationTag`]) is currently unsupported. 135 | /// 136 | /// # Errors 137 | /// 138 | /// Will return [`tiff::TiffFormatError::RequiredTagNotFound`] if the TIFF file is 139 | /// missing tags required to build an Affine transformation matrix. 140 | pub fn xy_coords(&mut self) -> TiffResult<(Array1, Array1)> { 141 | let transform = self.transform()?; // affine transformation matrix 142 | 143 | // Get spatial resolution in x and y dimensions 144 | let x_res: &f64 = &transform.a(); 145 | let y_res: &f64 = &transform.e(); 146 | 147 | // Get xy coordinate of the center of the top left pixel 148 | let x_origin: &f64 = &(transform.xoff() + x_res / 2.0); 149 | let y_origin: &f64 = &(transform.yoff() + y_res / 2.0); 150 | 151 | // Get number of pixels along the x and y dimensions 152 | let (x_pixels, y_pixels): (u32, u32) = self.decoder.dimensions()?; 153 | 154 | // Get xy coordinate of the center of the bottom right pixel 155 | let x_end: f64 = x_origin + x_res * f64::from(x_pixels); 156 | let y_end: f64 = y_origin + y_res * f64::from(y_pixels); 157 | 158 | // Get array of x-coordinates and y-coordinates 159 | let x_coords = Array::range(x_origin.to_owned(), x_end, x_res.to_owned()); 160 | let y_coords = Array::range(y_origin.to_owned(), y_end, y_res.to_owned()); 161 | 162 | Ok((x_coords, y_coords)) 163 | } 164 | } 165 | 166 | /// Convert Vec into an Array3 with a shape of (channels, height, width), and then 167 | /// output it as a DLPack tensor. 168 | fn shape_vec_to_tensor( 169 | shape: (usize, usize, usize), 170 | vec: Vec, 171 | ) -> TiffResult { 172 | let array_data = Array3::from_shape_vec(shape, vec) 173 | .map_err(|_| TiffFormatError::InconsistentSizesEncountered)?; 174 | let tensor = SafeManagedTensorVersioned::new(array_data) 175 | .map_err(|err| TiffError::IoError(Error::other(err.to_string())))?; 176 | Ok(tensor) 177 | } 178 | 179 | /// Synchronously read a GeoTIFF file into an [`ndarray::Array`] 180 | /// 181 | /// # Errors 182 | /// 183 | /// Will return [`tiff::TiffError::IoError`] (Data type mismatch) if the specified 184 | /// output data type is different to the dtype of the input TIFF file. 185 | pub fn read_geotiff(stream: R) -> TiffResult> { 186 | // Open TIFF stream with decoder 187 | let mut reader = CogReader::new(stream)?; 188 | 189 | // Decode TIFF into DLPack 190 | let tensor: SafeManagedTensorVersioned = reader.dlpack()?; 191 | 192 | // Count number of bands 193 | let num_bands: usize = reader.num_samples()?; 194 | // Get image dimensions 195 | let (width, height): (u32, u32) = reader.decoder.dimensions()?; 196 | 197 | // Convert DLPack tensor to ndarray 198 | let view = ArrayViewD::::try_from(&tensor) 199 | .map_err(|err| TiffError::IoError(Error::other(err.to_string())))?; 200 | let array: ArrayView3 = view 201 | .into_shape_with_order((num_bands, height as usize, width as usize)) 202 | .map_err(|err| TiffError::IoError(Error::other(err.to_string())))?; 203 | 204 | Ok(array.to_owned()) 205 | } 206 | 207 | #[cfg(test)] 208 | mod tests { 209 | use std::io::{Cursor, Seek, SeekFrom}; 210 | 211 | use dlpark::SafeManagedTensorVersioned; 212 | use dlpark::ffi::DataType; 213 | use dlpark::prelude::TensorView; 214 | use geo::AffineTransform; 215 | use ndarray::{Array3, s}; 216 | use object_store::parse_url; 217 | use tempfile::tempfile; 218 | use tiff::encoder::{TiffEncoder, colortype}; 219 | use url::Url; 220 | 221 | use crate::io::geotiff::{CogReader, read_geotiff}; 222 | 223 | #[test] 224 | fn test_read_geotiff() { 225 | // Generate some data 226 | let mut image_data = Vec::new(); 227 | for y in 0..10u8 { 228 | for x in 0..20u8 { 229 | let val = y + x; 230 | image_data.push(f32::from(val)); 231 | } 232 | } 233 | 234 | // Write a BigTIFF file 235 | let mut file = tempfile().unwrap(); 236 | let mut bigtiff = TiffEncoder::new_big(&mut file).unwrap(); 237 | bigtiff 238 | .write_image::(20, 10, &image_data) // width, height, data 239 | .unwrap(); 240 | file.seek(SeekFrom::Start(0)).unwrap(); 241 | 242 | // Read a BigTIFF file 243 | let arr: Array3 = read_geotiff(file).unwrap(); 244 | assert_eq!(arr.ndim(), 3); 245 | assert_eq!(arr.dim(), (1, 10, 20)); // (channels, height, width) 246 | let first_band = arr.slice(s![0, .., ..]); 247 | assert_eq!(first_band.nrows(), 10); // y-axis 248 | assert_eq!(first_band.ncols(), 20); // x-axis 249 | assert_eq!(arr.mean(), Some(14.0)); 250 | } 251 | 252 | #[tokio::test] 253 | async fn test_read_geotiff_multi_band() { 254 | let cog_url: &str = "https://github.com/locationtech/geotrellis/raw/v3.7.1/raster/data/one-month-tiles-multiband/result.tif"; 255 | let tif_url = Url::parse(cog_url).unwrap(); 256 | let (store, location) = parse_url(&tif_url).unwrap(); 257 | 258 | let result = store.get(&location).await.unwrap(); 259 | let bytes = result.bytes().await.unwrap(); 260 | let stream = Cursor::new(bytes); 261 | 262 | let array: Array3 = read_geotiff(stream).unwrap(); 263 | 264 | assert_eq!(array.dim(), (2, 512, 512)); 265 | assert_eq!(array.mean(), Some(225.17654)); 266 | } 267 | 268 | #[tokio::test] 269 | async fn test_read_geotiff_uint16_dtype() { 270 | let cog_url: &str = 271 | "https://github.com/OSGeo/gdal/raw/v3.9.2/autotest/gcore/data/uint16.tif"; 272 | let tif_url = Url::parse(cog_url).unwrap(); 273 | let (store, location) = parse_url(&tif_url).unwrap(); 274 | 275 | let result = store.get(&location).await.unwrap(); 276 | let bytes = result.bytes().await.unwrap(); 277 | let stream = Cursor::new(bytes); 278 | 279 | let array: Array3 = read_geotiff(stream).unwrap(); 280 | 281 | assert_eq!(array.dim(), (1, 20, 20)); 282 | assert_eq!(array.mean(), Some(126)); 283 | } 284 | 285 | #[tokio::test] 286 | async fn test_read_geotiff_float16_dtype() { 287 | let cog_url: &str = 288 | "https://github.com/OSGeo/gdal/raw/v3.11.0/autotest/gcore/data/float16.tif"; 289 | let tif_url = Url::parse(cog_url).unwrap(); 290 | let (store, location) = parse_url(&tif_url).unwrap(); 291 | 292 | let result = store.get(&location).await.unwrap(); 293 | let bytes = result.bytes().await.unwrap(); 294 | let stream = Cursor::new(bytes); 295 | 296 | let array: Array3 = read_geotiff(stream).unwrap(); 297 | 298 | assert_eq!(array.dim(), (1, 20, 20)); 299 | assert_eq!(array.mean(), Some(half::f16::from_f32_const(127.125))); 300 | } 301 | 302 | #[tokio::test] 303 | async fn test_cogreader_dlpack() { 304 | let cog_url: &str = "https://github.com/rasterio/rasterio/raw/1.3.9/tests/data/float32.tif"; 305 | let tif_url = Url::parse(cog_url).unwrap(); 306 | let (store, location) = parse_url(&tif_url).unwrap(); 307 | 308 | let result = store.get(&location).await.unwrap(); 309 | let bytes = result.bytes().await.unwrap(); 310 | let stream = Cursor::new(bytes); 311 | 312 | let mut cog = CogReader::new(stream).unwrap(); 313 | let tensor: SafeManagedTensorVersioned = cog.dlpack().unwrap(); 314 | 315 | assert_eq!(tensor.shape(), [1, 2, 3]); 316 | assert_eq!(tensor.data_type(), &DataType::F32); 317 | let values: Vec = tensor 318 | .as_slice_untyped() 319 | .to_vec() 320 | .chunks_exact(4) 321 | .map(TryInto::try_into) 322 | .map(Result::unwrap) 323 | .map(f32::from_le_bytes) 324 | .collect(); 325 | assert_eq!(values, vec![1.41, 1.23, 0.78, 0.32, -0.23, -1.88]); 326 | } 327 | 328 | #[tokio::test] 329 | async fn test_cogreader_num_samples() { 330 | let cog_url: &str = "https://github.com/developmentseed/titiler/raw/refs/tags/0.22.2/src/titiler/mosaic/tests/fixtures/TCI.tif"; 331 | let tif_url = Url::parse(cog_url).unwrap(); 332 | let (store, location) = parse_url(&tif_url).unwrap(); 333 | 334 | let result = store.get(&location).await.unwrap(); 335 | let bytes = result.bytes().await.unwrap(); 336 | let stream = Cursor::new(bytes); 337 | 338 | let mut cog = CogReader::new(stream).unwrap(); 339 | assert_eq!(cog.num_samples().unwrap(), 3); 340 | } 341 | 342 | #[tokio::test] 343 | async fn test_cogreader_transform() { 344 | let cog_url: &str = 345 | "https://github.com/cogeotiff/rio-tiler/raw/6.4.0/tests/fixtures/cog_nodata_nan.tif"; 346 | let tif_url = Url::parse(cog_url).unwrap(); 347 | let (store, location) = parse_url(&tif_url).unwrap(); 348 | 349 | let result = store.get(&location).await.unwrap(); 350 | let bytes = result.bytes().await.unwrap(); 351 | let stream = Cursor::new(bytes); 352 | 353 | let mut cog = CogReader::new(stream).unwrap(); 354 | let transform = cog.transform().unwrap(); 355 | 356 | assert_eq!( 357 | transform, 358 | AffineTransform::new(200.0, 0.0, 499_980.0, 0.0, -200.0, 5_300_040.0) 359 | ); 360 | } 361 | } 362 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "adler2" 7 | version = "2.0.1" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" 10 | 11 | [[package]] 12 | name = "ahash" 13 | version = "0.8.11" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" 16 | dependencies = [ 17 | "cfg-if", 18 | "once_cell", 19 | "version_check", 20 | "zerocopy", 21 | ] 22 | 23 | [[package]] 24 | name = "aho-corasick" 25 | version = "1.1.3" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" 28 | dependencies = [ 29 | "memchr", 30 | ] 31 | 32 | [[package]] 33 | name = "allocator-api2" 34 | version = "0.2.16" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" 37 | 38 | [[package]] 39 | name = "android-tzdata" 40 | version = "0.1.1" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" 43 | 44 | [[package]] 45 | name = "android_system_properties" 46 | version = "0.1.5" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" 49 | dependencies = [ 50 | "libc", 51 | ] 52 | 53 | [[package]] 54 | name = "anes" 55 | version = "0.1.6" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" 58 | 59 | [[package]] 60 | name = "anstyle" 61 | version = "1.0.11" 62 | source = "registry+https://github.com/rust-lang/crates.io-index" 63 | checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" 64 | 65 | [[package]] 66 | name = "anyhow" 67 | version = "1.0.100" 68 | source = "registry+https://github.com/rust-lang/crates.io-index" 69 | checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" 70 | 71 | [[package]] 72 | name = "approx" 73 | version = "0.5.1" 74 | source = "registry+https://github.com/rust-lang/crates.io-index" 75 | checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" 76 | dependencies = [ 77 | "num-traits", 78 | ] 79 | 80 | [[package]] 81 | name = "async-trait" 82 | version = "0.1.77" 83 | source = "registry+https://github.com/rust-lang/crates.io-index" 84 | checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" 85 | dependencies = [ 86 | "proc-macro2", 87 | "quote", 88 | "syn", 89 | ] 90 | 91 | [[package]] 92 | name = "atomic-waker" 93 | version = "1.1.2" 94 | source = "registry+https://github.com/rust-lang/crates.io-index" 95 | checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" 96 | 97 | [[package]] 98 | name = "autocfg" 99 | version = "1.1.0" 100 | source = "registry+https://github.com/rust-lang/crates.io-index" 101 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 102 | 103 | [[package]] 104 | name = "base64" 105 | version = "0.22.1" 106 | source = "registry+https://github.com/rust-lang/crates.io-index" 107 | checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" 108 | 109 | [[package]] 110 | name = "bindgen" 111 | version = "0.71.1" 112 | source = "registry+https://github.com/rust-lang/crates.io-index" 113 | checksum = "5f58bf3d7db68cfbac37cfc485a8d711e87e064c3d0fe0435b92f7a407f9d6b3" 114 | dependencies = [ 115 | "bitflags 2.10.0", 116 | "cexpr", 117 | "clang-sys", 118 | "itertools 0.13.0", 119 | "log", 120 | "prettyplease", 121 | "proc-macro2", 122 | "quote", 123 | "regex", 124 | "rustc-hash", 125 | "shlex", 126 | "syn", 127 | ] 128 | 129 | [[package]] 130 | name = "bitflags" 131 | version = "1.3.2" 132 | source = "registry+https://github.com/rust-lang/crates.io-index" 133 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 134 | 135 | [[package]] 136 | name = "bitflags" 137 | version = "2.10.0" 138 | source = "registry+https://github.com/rust-lang/crates.io-index" 139 | checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" 140 | 141 | [[package]] 142 | name = "bumpalo" 143 | version = "3.15.3" 144 | source = "registry+https://github.com/rust-lang/crates.io-index" 145 | checksum = "8ea184aa71bb362a1157c896979544cc23974e08fd265f29ea96b59f0b4a555b" 146 | 147 | [[package]] 148 | name = "byteorder" 149 | version = "1.5.0" 150 | source = "registry+https://github.com/rust-lang/crates.io-index" 151 | checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" 152 | 153 | [[package]] 154 | name = "bytes" 155 | version = "1.5.0" 156 | source = "registry+https://github.com/rust-lang/crates.io-index" 157 | checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" 158 | 159 | [[package]] 160 | name = "cast" 161 | version = "0.3.0" 162 | source = "registry+https://github.com/rust-lang/crates.io-index" 163 | checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" 164 | 165 | [[package]] 166 | name = "cc" 167 | version = "1.2.27" 168 | source = "registry+https://github.com/rust-lang/crates.io-index" 169 | checksum = "d487aa071b5f64da6f19a3e848e3578944b726ee5a4854b82172f02aa876bfdc" 170 | dependencies = [ 171 | "jobserver", 172 | "libc", 173 | "shlex", 174 | ] 175 | 176 | [[package]] 177 | name = "cexpr" 178 | version = "0.6.0" 179 | source = "registry+https://github.com/rust-lang/crates.io-index" 180 | checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" 181 | dependencies = [ 182 | "nom", 183 | ] 184 | 185 | [[package]] 186 | name = "cfg-if" 187 | version = "1.0.0" 188 | source = "registry+https://github.com/rust-lang/crates.io-index" 189 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 190 | 191 | [[package]] 192 | name = "cfg_aliases" 193 | version = "0.2.1" 194 | source = "registry+https://github.com/rust-lang/crates.io-index" 195 | checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" 196 | 197 | [[package]] 198 | name = "chrono" 199 | version = "0.4.34" 200 | source = "registry+https://github.com/rust-lang/crates.io-index" 201 | checksum = "5bc015644b92d5890fab7489e49d21f879d5c990186827d42ec511919404f38b" 202 | dependencies = [ 203 | "android-tzdata", 204 | "iana-time-zone", 205 | "num-traits", 206 | "serde", 207 | "windows-targets 0.52.3", 208 | ] 209 | 210 | [[package]] 211 | name = "ciborium" 212 | version = "0.2.2" 213 | source = "registry+https://github.com/rust-lang/crates.io-index" 214 | checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" 215 | dependencies = [ 216 | "ciborium-io", 217 | "ciborium-ll", 218 | "serde", 219 | ] 220 | 221 | [[package]] 222 | name = "ciborium-io" 223 | version = "0.2.2" 224 | source = "registry+https://github.com/rust-lang/crates.io-index" 225 | checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" 226 | 227 | [[package]] 228 | name = "ciborium-ll" 229 | version = "0.2.2" 230 | source = "registry+https://github.com/rust-lang/crates.io-index" 231 | checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" 232 | dependencies = [ 233 | "ciborium-io", 234 | "half", 235 | ] 236 | 237 | [[package]] 238 | name = "clang-sys" 239 | version = "1.8.1" 240 | source = "registry+https://github.com/rust-lang/crates.io-index" 241 | checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" 242 | dependencies = [ 243 | "glob", 244 | "libc", 245 | "libloading", 246 | ] 247 | 248 | [[package]] 249 | name = "clap" 250 | version = "4.5.51" 251 | source = "registry+https://github.com/rust-lang/crates.io-index" 252 | checksum = "4c26d721170e0295f191a69bd9a1f93efcdb0aff38684b61ab5750468972e5f5" 253 | dependencies = [ 254 | "clap_builder", 255 | ] 256 | 257 | [[package]] 258 | name = "clap_builder" 259 | version = "4.5.51" 260 | source = "registry+https://github.com/rust-lang/crates.io-index" 261 | checksum = "75835f0c7bf681bfd05abe44e965760fea999a5286c6eb2d59883634fd02011a" 262 | dependencies = [ 263 | "anstyle", 264 | "clap_lex", 265 | ] 266 | 267 | [[package]] 268 | name = "clap_lex" 269 | version = "0.7.6" 270 | source = "registry+https://github.com/rust-lang/crates.io-index" 271 | checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" 272 | 273 | [[package]] 274 | name = "cmake" 275 | version = "0.1.54" 276 | source = "registry+https://github.com/rust-lang/crates.io-index" 277 | checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0" 278 | dependencies = [ 279 | "cc", 280 | ] 281 | 282 | [[package]] 283 | name = "codspeed" 284 | version = "4.1.0" 285 | source = "registry+https://github.com/rust-lang/crates.io-index" 286 | checksum = "c3b847e05a34be5c38f3f2a5052178a3bd32e6b5702f3ea775efde95c483a539" 287 | dependencies = [ 288 | "anyhow", 289 | "cc", 290 | "colored", 291 | "getrandom 0.2.12", 292 | "glob", 293 | "libc", 294 | "nix", 295 | "serde", 296 | "serde_json", 297 | "statrs", 298 | ] 299 | 300 | [[package]] 301 | name = "codspeed-criterion-compat" 302 | version = "4.1.0" 303 | source = "registry+https://github.com/rust-lang/crates.io-index" 304 | checksum = "30a0e2a53beb18dec493ec133f226e0d35e8bb2fdc638ccf7351696eabee416c" 305 | dependencies = [ 306 | "clap", 307 | "codspeed", 308 | "codspeed-criterion-compat-walltime", 309 | "colored", 310 | "regex", 311 | ] 312 | 313 | [[package]] 314 | name = "codspeed-criterion-compat-walltime" 315 | version = "4.1.0" 316 | source = "registry+https://github.com/rust-lang/crates.io-index" 317 | checksum = "1f652d6e6d40bba0f5c244744db94d92a26b7f083df18692df88fb0772f1c793" 318 | dependencies = [ 319 | "anes", 320 | "cast", 321 | "ciborium", 322 | "clap", 323 | "codspeed", 324 | "criterion-plot", 325 | "is-terminal", 326 | "itertools 0.10.5", 327 | "num-traits", 328 | "once_cell", 329 | "oorandom", 330 | "plotters", 331 | "rayon", 332 | "regex", 333 | "serde", 334 | "serde_derive", 335 | "serde_json", 336 | "tinytemplate", 337 | "walkdir", 338 | ] 339 | 340 | [[package]] 341 | name = "cog3pio" 342 | version = "0.0.1" 343 | dependencies = [ 344 | "bytes", 345 | "codspeed-criterion-compat", 346 | "cudarc", 347 | "dlpark", 348 | "gdal", 349 | "gdal-src", 350 | "gdal-sys", 351 | "geo", 352 | "half", 353 | "ndarray", 354 | "numpy", 355 | "nvtiff-sys", 356 | "object_store", 357 | "pyo3", 358 | "rstest", 359 | "tempfile", 360 | "tiff", 361 | "tokio", 362 | "url", 363 | ] 364 | 365 | [[package]] 366 | name = "colored" 367 | version = "2.2.0" 368 | source = "registry+https://github.com/rust-lang/crates.io-index" 369 | checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" 370 | dependencies = [ 371 | "lazy_static", 372 | "windows-sys 0.52.0", 373 | ] 374 | 375 | [[package]] 376 | name = "core-foundation" 377 | version = "0.9.4" 378 | source = "registry+https://github.com/rust-lang/crates.io-index" 379 | checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" 380 | dependencies = [ 381 | "core-foundation-sys", 382 | "libc", 383 | ] 384 | 385 | [[package]] 386 | name = "core-foundation" 387 | version = "0.10.1" 388 | source = "registry+https://github.com/rust-lang/crates.io-index" 389 | checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" 390 | dependencies = [ 391 | "core-foundation-sys", 392 | "libc", 393 | ] 394 | 395 | [[package]] 396 | name = "core-foundation-sys" 397 | version = "0.8.6" 398 | source = "registry+https://github.com/rust-lang/crates.io-index" 399 | checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" 400 | 401 | [[package]] 402 | name = "crc32fast" 403 | version = "1.4.2" 404 | source = "registry+https://github.com/rust-lang/crates.io-index" 405 | checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" 406 | dependencies = [ 407 | "cfg-if", 408 | ] 409 | 410 | [[package]] 411 | name = "criterion-plot" 412 | version = "0.5.0" 413 | source = "registry+https://github.com/rust-lang/crates.io-index" 414 | checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" 415 | dependencies = [ 416 | "cast", 417 | "itertools 0.10.5", 418 | ] 419 | 420 | [[package]] 421 | name = "crossbeam-deque" 422 | version = "0.8.5" 423 | source = "registry+https://github.com/rust-lang/crates.io-index" 424 | checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" 425 | dependencies = [ 426 | "crossbeam-epoch", 427 | "crossbeam-utils", 428 | ] 429 | 430 | [[package]] 431 | name = "crossbeam-epoch" 432 | version = "0.9.18" 433 | source = "registry+https://github.com/rust-lang/crates.io-index" 434 | checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" 435 | dependencies = [ 436 | "crossbeam-utils", 437 | ] 438 | 439 | [[package]] 440 | name = "crossbeam-utils" 441 | version = "0.8.20" 442 | source = "registry+https://github.com/rust-lang/crates.io-index" 443 | checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" 444 | 445 | [[package]] 446 | name = "crunchy" 447 | version = "0.2.4" 448 | source = "registry+https://github.com/rust-lang/crates.io-index" 449 | checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" 450 | 451 | [[package]] 452 | name = "cudarc" 453 | version = "0.17.7" 454 | source = "registry+https://github.com/rust-lang/crates.io-index" 455 | checksum = "ff0da1a70ec91e66731c1752deb9fda3044f1154fe4ceb5873e3f96ed34cafa3" 456 | dependencies = [ 457 | "libloading", 458 | ] 459 | 460 | [[package]] 461 | name = "displaydoc" 462 | version = "0.2.5" 463 | source = "registry+https://github.com/rust-lang/crates.io-index" 464 | checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" 465 | dependencies = [ 466 | "proc-macro2", 467 | "quote", 468 | "syn", 469 | ] 470 | 471 | [[package]] 472 | name = "dlpark" 473 | version = "0.6.0" 474 | source = "git+https://github.com/SunDoge/dlpark.git?rev=2bedd1fc8ad59e56e3b74c63c94efb3fdc8c2ada#2bedd1fc8ad59e56e3b74c63c94efb3fdc8c2ada" 475 | dependencies = [ 476 | "bitflags 2.10.0", 477 | "cudarc", 478 | "half", 479 | "ndarray", 480 | "pyo3", 481 | "snafu", 482 | ] 483 | 484 | [[package]] 485 | name = "earcutr" 486 | version = "0.4.3" 487 | source = "registry+https://github.com/rust-lang/crates.io-index" 488 | checksum = "79127ed59a85d7687c409e9978547cffb7dc79675355ed22da6b66fd5f6ead01" 489 | dependencies = [ 490 | "itertools 0.11.0", 491 | "num-traits", 492 | ] 493 | 494 | [[package]] 495 | name = "either" 496 | version = "1.10.0" 497 | source = "registry+https://github.com/rust-lang/crates.io-index" 498 | checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" 499 | 500 | [[package]] 501 | name = "equivalent" 502 | version = "1.0.1" 503 | source = "registry+https://github.com/rust-lang/crates.io-index" 504 | checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" 505 | 506 | [[package]] 507 | name = "errno" 508 | version = "0.3.13" 509 | source = "registry+https://github.com/rust-lang/crates.io-index" 510 | checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" 511 | dependencies = [ 512 | "libc", 513 | "windows-sys 0.60.2", 514 | ] 515 | 516 | [[package]] 517 | name = "fastrand" 518 | version = "2.0.1" 519 | source = "registry+https://github.com/rust-lang/crates.io-index" 520 | checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" 521 | 522 | [[package]] 523 | name = "filetime" 524 | version = "0.2.23" 525 | source = "registry+https://github.com/rust-lang/crates.io-index" 526 | checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" 527 | dependencies = [ 528 | "cfg-if", 529 | "libc", 530 | "redox_syscall", 531 | "windows-sys 0.52.0", 532 | ] 533 | 534 | [[package]] 535 | name = "flate2" 536 | version = "1.1.2" 537 | source = "registry+https://github.com/rust-lang/crates.io-index" 538 | checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" 539 | dependencies = [ 540 | "crc32fast", 541 | "miniz_oxide", 542 | ] 543 | 544 | [[package]] 545 | name = "float_next_after" 546 | version = "1.0.0" 547 | source = "registry+https://github.com/rust-lang/crates.io-index" 548 | checksum = "8bf7cc16383c4b8d58b9905a8509f02926ce3058053c056376248d958c9df1e8" 549 | 550 | [[package]] 551 | name = "fnv" 552 | version = "1.0.7" 553 | source = "registry+https://github.com/rust-lang/crates.io-index" 554 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 555 | 556 | [[package]] 557 | name = "form_urlencoded" 558 | version = "1.2.2" 559 | source = "registry+https://github.com/rust-lang/crates.io-index" 560 | checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" 561 | dependencies = [ 562 | "percent-encoding", 563 | ] 564 | 565 | [[package]] 566 | name = "futures" 567 | version = "0.3.30" 568 | source = "registry+https://github.com/rust-lang/crates.io-index" 569 | checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" 570 | dependencies = [ 571 | "futures-channel", 572 | "futures-core", 573 | "futures-executor", 574 | "futures-io", 575 | "futures-sink", 576 | "futures-task", 577 | "futures-util", 578 | ] 579 | 580 | [[package]] 581 | name = "futures-channel" 582 | version = "0.3.30" 583 | source = "registry+https://github.com/rust-lang/crates.io-index" 584 | checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" 585 | dependencies = [ 586 | "futures-core", 587 | "futures-sink", 588 | ] 589 | 590 | [[package]] 591 | name = "futures-core" 592 | version = "0.3.31" 593 | source = "registry+https://github.com/rust-lang/crates.io-index" 594 | checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" 595 | 596 | [[package]] 597 | name = "futures-executor" 598 | version = "0.3.30" 599 | source = "registry+https://github.com/rust-lang/crates.io-index" 600 | checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" 601 | dependencies = [ 602 | "futures-core", 603 | "futures-task", 604 | "futures-util", 605 | ] 606 | 607 | [[package]] 608 | name = "futures-io" 609 | version = "0.3.30" 610 | source = "registry+https://github.com/rust-lang/crates.io-index" 611 | checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" 612 | 613 | [[package]] 614 | name = "futures-macro" 615 | version = "0.3.30" 616 | source = "registry+https://github.com/rust-lang/crates.io-index" 617 | checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" 618 | dependencies = [ 619 | "proc-macro2", 620 | "quote", 621 | "syn", 622 | ] 623 | 624 | [[package]] 625 | name = "futures-sink" 626 | version = "0.3.30" 627 | source = "registry+https://github.com/rust-lang/crates.io-index" 628 | checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" 629 | 630 | [[package]] 631 | name = "futures-task" 632 | version = "0.3.30" 633 | source = "registry+https://github.com/rust-lang/crates.io-index" 634 | checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" 635 | 636 | [[package]] 637 | name = "futures-timer" 638 | version = "3.0.3" 639 | source = "registry+https://github.com/rust-lang/crates.io-index" 640 | checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" 641 | 642 | [[package]] 643 | name = "futures-util" 644 | version = "0.3.30" 645 | source = "registry+https://github.com/rust-lang/crates.io-index" 646 | checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" 647 | dependencies = [ 648 | "futures-channel", 649 | "futures-core", 650 | "futures-io", 651 | "futures-macro", 652 | "futures-sink", 653 | "futures-task", 654 | "memchr", 655 | "pin-project-lite", 656 | "pin-utils", 657 | "slab", 658 | ] 659 | 660 | [[package]] 661 | name = "gdal" 662 | version = "0.18.0" 663 | source = "git+https://github.com/georust/gdal.git?rev=8b9e04938298055f2a22dce5c21c1a6e73678ecb#8b9e04938298055f2a22dce5c21c1a6e73678ecb" 664 | dependencies = [ 665 | "bitflags 2.10.0", 666 | "chrono", 667 | "gdal-sys", 668 | "geo-types", 669 | "ndarray", 670 | "semver", 671 | "thiserror", 672 | ] 673 | 674 | [[package]] 675 | name = "gdal-src" 676 | version = "0.3.0+3.11.4" 677 | source = "git+https://github.com/georust/gdal.git?rev=8b9e04938298055f2a22dce5c21c1a6e73678ecb#8b9e04938298055f2a22dce5c21c1a6e73678ecb" 678 | dependencies = [ 679 | "cmake", 680 | "link-cplusplus", 681 | "proj-sys", 682 | ] 683 | 684 | [[package]] 685 | name = "gdal-sys" 686 | version = "0.11.0" 687 | source = "git+https://github.com/georust/gdal.git?rev=8b9e04938298055f2a22dce5c21c1a6e73678ecb#8b9e04938298055f2a22dce5c21c1a6e73678ecb" 688 | dependencies = [ 689 | "gdal-src", 690 | "pkg-config", 691 | "semver", 692 | ] 693 | 694 | [[package]] 695 | name = "geo" 696 | version = "0.29.0" 697 | source = "registry+https://github.com/rust-lang/crates.io-index" 698 | checksum = "81d088357a9cc60cec8253b3578f6834b4a3aa20edb55f5d1c030c36d8143f11" 699 | dependencies = [ 700 | "earcutr", 701 | "float_next_after", 702 | "geo-types", 703 | "geographiclib-rs", 704 | "i_overlay", 705 | "log", 706 | "num-traits", 707 | "robust", 708 | "rstar", 709 | "spade", 710 | ] 711 | 712 | [[package]] 713 | name = "geo-types" 714 | version = "0.7.17" 715 | source = "registry+https://github.com/rust-lang/crates.io-index" 716 | checksum = "75a4dcd69d35b2c87a7c83bce9af69fd65c9d68d3833a0ded568983928f3fc99" 717 | dependencies = [ 718 | "approx", 719 | "num-traits", 720 | "rstar", 721 | "serde", 722 | ] 723 | 724 | [[package]] 725 | name = "geographiclib-rs" 726 | version = "0.2.4" 727 | source = "registry+https://github.com/rust-lang/crates.io-index" 728 | checksum = "e6e5ed84f8089c70234b0a8e0aedb6dc733671612ddc0d37c6066052f9781960" 729 | dependencies = [ 730 | "libm", 731 | ] 732 | 733 | [[package]] 734 | name = "getrandom" 735 | version = "0.2.12" 736 | source = "registry+https://github.com/rust-lang/crates.io-index" 737 | checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" 738 | dependencies = [ 739 | "cfg-if", 740 | "js-sys", 741 | "libc", 742 | "wasi 0.11.0+wasi-snapshot-preview1", 743 | "wasm-bindgen", 744 | ] 745 | 746 | [[package]] 747 | name = "getrandom" 748 | version = "0.3.3" 749 | source = "registry+https://github.com/rust-lang/crates.io-index" 750 | checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" 751 | dependencies = [ 752 | "cfg-if", 753 | "libc", 754 | "r-efi", 755 | "wasi 0.14.2+wasi-0.2.4", 756 | ] 757 | 758 | [[package]] 759 | name = "glob" 760 | version = "0.3.2" 761 | source = "registry+https://github.com/rust-lang/crates.io-index" 762 | checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" 763 | 764 | [[package]] 765 | name = "h2" 766 | version = "0.4.12" 767 | source = "registry+https://github.com/rust-lang/crates.io-index" 768 | checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" 769 | dependencies = [ 770 | "atomic-waker", 771 | "bytes", 772 | "fnv", 773 | "futures-core", 774 | "futures-sink", 775 | "http", 776 | "indexmap", 777 | "slab", 778 | "tokio", 779 | "tokio-util", 780 | "tracing", 781 | ] 782 | 783 | [[package]] 784 | name = "half" 785 | version = "2.6.0" 786 | source = "registry+https://github.com/rust-lang/crates.io-index" 787 | checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" 788 | dependencies = [ 789 | "cfg-if", 790 | "crunchy", 791 | "num-traits", 792 | ] 793 | 794 | [[package]] 795 | name = "hash32" 796 | version = "0.3.1" 797 | source = "registry+https://github.com/rust-lang/crates.io-index" 798 | checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" 799 | dependencies = [ 800 | "byteorder", 801 | ] 802 | 803 | [[package]] 804 | name = "hashbrown" 805 | version = "0.14.3" 806 | source = "registry+https://github.com/rust-lang/crates.io-index" 807 | checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" 808 | dependencies = [ 809 | "ahash", 810 | "allocator-api2", 811 | ] 812 | 813 | [[package]] 814 | name = "hashbrown" 815 | version = "0.16.0" 816 | source = "registry+https://github.com/rust-lang/crates.io-index" 817 | checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" 818 | 819 | [[package]] 820 | name = "heapless" 821 | version = "0.8.0" 822 | source = "registry+https://github.com/rust-lang/crates.io-index" 823 | checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" 824 | dependencies = [ 825 | "hash32", 826 | "stable_deref_trait", 827 | ] 828 | 829 | [[package]] 830 | name = "heck" 831 | version = "0.5.0" 832 | source = "registry+https://github.com/rust-lang/crates.io-index" 833 | checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 834 | 835 | [[package]] 836 | name = "hermit-abi" 837 | version = "0.5.2" 838 | source = "registry+https://github.com/rust-lang/crates.io-index" 839 | checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" 840 | 841 | [[package]] 842 | name = "http" 843 | version = "1.3.1" 844 | source = "registry+https://github.com/rust-lang/crates.io-index" 845 | checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" 846 | dependencies = [ 847 | "bytes", 848 | "fnv", 849 | "itoa", 850 | ] 851 | 852 | [[package]] 853 | name = "http-body" 854 | version = "1.0.1" 855 | source = "registry+https://github.com/rust-lang/crates.io-index" 856 | checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" 857 | dependencies = [ 858 | "bytes", 859 | "http", 860 | ] 861 | 862 | [[package]] 863 | name = "http-body-util" 864 | version = "0.1.3" 865 | source = "registry+https://github.com/rust-lang/crates.io-index" 866 | checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" 867 | dependencies = [ 868 | "bytes", 869 | "futures-core", 870 | "http", 871 | "http-body", 872 | "pin-project-lite", 873 | ] 874 | 875 | [[package]] 876 | name = "httparse" 877 | version = "1.10.1" 878 | source = "registry+https://github.com/rust-lang/crates.io-index" 879 | checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" 880 | 881 | [[package]] 882 | name = "humantime" 883 | version = "2.1.0" 884 | source = "registry+https://github.com/rust-lang/crates.io-index" 885 | checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" 886 | 887 | [[package]] 888 | name = "hyper" 889 | version = "1.7.0" 890 | source = "registry+https://github.com/rust-lang/crates.io-index" 891 | checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e" 892 | dependencies = [ 893 | "atomic-waker", 894 | "bytes", 895 | "futures-channel", 896 | "futures-core", 897 | "h2", 898 | "http", 899 | "http-body", 900 | "httparse", 901 | "itoa", 902 | "pin-project-lite", 903 | "pin-utils", 904 | "smallvec", 905 | "tokio", 906 | "want", 907 | ] 908 | 909 | [[package]] 910 | name = "hyper-rustls" 911 | version = "0.27.7" 912 | source = "registry+https://github.com/rust-lang/crates.io-index" 913 | checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" 914 | dependencies = [ 915 | "http", 916 | "hyper", 917 | "hyper-util", 918 | "rustls", 919 | "rustls-native-certs 0.8.1", 920 | "rustls-pki-types", 921 | "tokio", 922 | "tokio-rustls", 923 | "tower-service", 924 | ] 925 | 926 | [[package]] 927 | name = "hyper-util" 928 | version = "0.1.7" 929 | source = "registry+https://github.com/rust-lang/crates.io-index" 930 | checksum = "cde7055719c54e36e95e8719f95883f22072a48ede39db7fc17a4e1d5281e9b9" 931 | dependencies = [ 932 | "bytes", 933 | "futures-channel", 934 | "futures-util", 935 | "http", 936 | "http-body", 937 | "hyper", 938 | "pin-project-lite", 939 | "socket2 0.5.6", 940 | "tokio", 941 | "tower", 942 | "tower-service", 943 | "tracing", 944 | ] 945 | 946 | [[package]] 947 | name = "i_float" 948 | version = "1.3.1" 949 | source = "registry+https://github.com/rust-lang/crates.io-index" 950 | checksum = "f5fe043aae28ce70bd2f78b2f5f82a3654d63607c82594da4dabb8b6cb81f2b2" 951 | dependencies = [ 952 | "serde", 953 | ] 954 | 955 | [[package]] 956 | name = "i_key_sort" 957 | version = "0.2.0" 958 | source = "registry+https://github.com/rust-lang/crates.io-index" 959 | checksum = "347c253b4748a1a28baf94c9ce133b6b166f08573157e05afe718812bc599fcd" 960 | 961 | [[package]] 962 | name = "i_overlay" 963 | version = "1.7.2" 964 | source = "registry+https://github.com/rust-lang/crates.io-index" 965 | checksum = "a469f68cb8a7cef375b2b0f581faf5859b4b50600438c00d46b71acc25ebbd0c" 966 | dependencies = [ 967 | "i_float", 968 | "i_key_sort", 969 | "i_shape", 970 | "i_tree", 971 | "rayon", 972 | ] 973 | 974 | [[package]] 975 | name = "i_shape" 976 | version = "1.3.1" 977 | source = "registry+https://github.com/rust-lang/crates.io-index" 978 | checksum = "1b44852d57a991c7dedaf76c55bc44f677f547ff899a430d29e13efd6133d7d8" 979 | dependencies = [ 980 | "i_float", 981 | "serde", 982 | ] 983 | 984 | [[package]] 985 | name = "i_tree" 986 | version = "0.8.3" 987 | source = "registry+https://github.com/rust-lang/crates.io-index" 988 | checksum = "155181bc97d770181cf9477da51218a19ee92a8e5be642e796661aee2b601139" 989 | 990 | [[package]] 991 | name = "iana-time-zone" 992 | version = "0.1.60" 993 | source = "registry+https://github.com/rust-lang/crates.io-index" 994 | checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" 995 | dependencies = [ 996 | "android_system_properties", 997 | "core-foundation-sys", 998 | "iana-time-zone-haiku", 999 | "js-sys", 1000 | "wasm-bindgen", 1001 | "windows-core", 1002 | ] 1003 | 1004 | [[package]] 1005 | name = "iana-time-zone-haiku" 1006 | version = "0.1.2" 1007 | source = "registry+https://github.com/rust-lang/crates.io-index" 1008 | checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" 1009 | dependencies = [ 1010 | "cc", 1011 | ] 1012 | 1013 | [[package]] 1014 | name = "icu_collections" 1015 | version = "2.0.0" 1016 | source = "registry+https://github.com/rust-lang/crates.io-index" 1017 | checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" 1018 | dependencies = [ 1019 | "displaydoc", 1020 | "potential_utf", 1021 | "yoke", 1022 | "zerofrom", 1023 | "zerovec", 1024 | ] 1025 | 1026 | [[package]] 1027 | name = "icu_locale_core" 1028 | version = "2.0.0" 1029 | source = "registry+https://github.com/rust-lang/crates.io-index" 1030 | checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" 1031 | dependencies = [ 1032 | "displaydoc", 1033 | "litemap", 1034 | "tinystr", 1035 | "writeable", 1036 | "zerovec", 1037 | ] 1038 | 1039 | [[package]] 1040 | name = "icu_normalizer" 1041 | version = "2.0.0" 1042 | source = "registry+https://github.com/rust-lang/crates.io-index" 1043 | checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" 1044 | dependencies = [ 1045 | "displaydoc", 1046 | "icu_collections", 1047 | "icu_normalizer_data", 1048 | "icu_properties", 1049 | "icu_provider", 1050 | "smallvec", 1051 | "zerovec", 1052 | ] 1053 | 1054 | [[package]] 1055 | name = "icu_normalizer_data" 1056 | version = "2.0.0" 1057 | source = "registry+https://github.com/rust-lang/crates.io-index" 1058 | checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" 1059 | 1060 | [[package]] 1061 | name = "icu_properties" 1062 | version = "2.0.1" 1063 | source = "registry+https://github.com/rust-lang/crates.io-index" 1064 | checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" 1065 | dependencies = [ 1066 | "displaydoc", 1067 | "icu_collections", 1068 | "icu_locale_core", 1069 | "icu_properties_data", 1070 | "icu_provider", 1071 | "potential_utf", 1072 | "zerotrie", 1073 | "zerovec", 1074 | ] 1075 | 1076 | [[package]] 1077 | name = "icu_properties_data" 1078 | version = "2.0.1" 1079 | source = "registry+https://github.com/rust-lang/crates.io-index" 1080 | checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" 1081 | 1082 | [[package]] 1083 | name = "icu_provider" 1084 | version = "2.0.0" 1085 | source = "registry+https://github.com/rust-lang/crates.io-index" 1086 | checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" 1087 | dependencies = [ 1088 | "displaydoc", 1089 | "icu_locale_core", 1090 | "stable_deref_trait", 1091 | "tinystr", 1092 | "writeable", 1093 | "yoke", 1094 | "zerofrom", 1095 | "zerotrie", 1096 | "zerovec", 1097 | ] 1098 | 1099 | [[package]] 1100 | name = "idna" 1101 | version = "1.1.0" 1102 | source = "registry+https://github.com/rust-lang/crates.io-index" 1103 | checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" 1104 | dependencies = [ 1105 | "idna_adapter", 1106 | "smallvec", 1107 | "utf8_iter", 1108 | ] 1109 | 1110 | [[package]] 1111 | name = "idna_adapter" 1112 | version = "1.2.1" 1113 | source = "registry+https://github.com/rust-lang/crates.io-index" 1114 | checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" 1115 | dependencies = [ 1116 | "icu_normalizer", 1117 | "icu_properties", 1118 | ] 1119 | 1120 | [[package]] 1121 | name = "indexmap" 1122 | version = "2.12.0" 1123 | source = "registry+https://github.com/rust-lang/crates.io-index" 1124 | checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f" 1125 | dependencies = [ 1126 | "equivalent", 1127 | "hashbrown 0.16.0", 1128 | ] 1129 | 1130 | [[package]] 1131 | name = "indoc" 1132 | version = "2.0.4" 1133 | source = "registry+https://github.com/rust-lang/crates.io-index" 1134 | checksum = "1e186cfbae8084e513daff4240b4797e342f988cecda4fb6c939150f96315fd8" 1135 | 1136 | [[package]] 1137 | name = "ipnet" 1138 | version = "2.9.0" 1139 | source = "registry+https://github.com/rust-lang/crates.io-index" 1140 | checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" 1141 | 1142 | [[package]] 1143 | name = "is-terminal" 1144 | version = "0.4.17" 1145 | source = "registry+https://github.com/rust-lang/crates.io-index" 1146 | checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" 1147 | dependencies = [ 1148 | "hermit-abi", 1149 | "libc", 1150 | "windows-sys 0.61.2", 1151 | ] 1152 | 1153 | [[package]] 1154 | name = "itertools" 1155 | version = "0.10.5" 1156 | source = "registry+https://github.com/rust-lang/crates.io-index" 1157 | checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" 1158 | dependencies = [ 1159 | "either", 1160 | ] 1161 | 1162 | [[package]] 1163 | name = "itertools" 1164 | version = "0.11.0" 1165 | source = "registry+https://github.com/rust-lang/crates.io-index" 1166 | checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" 1167 | dependencies = [ 1168 | "either", 1169 | ] 1170 | 1171 | [[package]] 1172 | name = "itertools" 1173 | version = "0.13.0" 1174 | source = "registry+https://github.com/rust-lang/crates.io-index" 1175 | checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" 1176 | dependencies = [ 1177 | "either", 1178 | ] 1179 | 1180 | [[package]] 1181 | name = "itertools" 1182 | version = "0.14.0" 1183 | source = "registry+https://github.com/rust-lang/crates.io-index" 1184 | checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" 1185 | dependencies = [ 1186 | "either", 1187 | ] 1188 | 1189 | [[package]] 1190 | name = "itoa" 1191 | version = "1.0.10" 1192 | source = "registry+https://github.com/rust-lang/crates.io-index" 1193 | checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" 1194 | 1195 | [[package]] 1196 | name = "jobserver" 1197 | version = "0.1.33" 1198 | source = "registry+https://github.com/rust-lang/crates.io-index" 1199 | checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" 1200 | dependencies = [ 1201 | "getrandom 0.3.3", 1202 | "libc", 1203 | ] 1204 | 1205 | [[package]] 1206 | name = "js-sys" 1207 | version = "0.3.68" 1208 | source = "registry+https://github.com/rust-lang/crates.io-index" 1209 | checksum = "406cda4b368d531c842222cf9d2600a9a4acce8d29423695379c6868a143a9ee" 1210 | dependencies = [ 1211 | "wasm-bindgen", 1212 | ] 1213 | 1214 | [[package]] 1215 | name = "lazy_static" 1216 | version = "1.5.0" 1217 | source = "registry+https://github.com/rust-lang/crates.io-index" 1218 | checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 1219 | 1220 | [[package]] 1221 | name = "libc" 1222 | version = "0.2.174" 1223 | source = "registry+https://github.com/rust-lang/crates.io-index" 1224 | checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" 1225 | 1226 | [[package]] 1227 | name = "libloading" 1228 | version = "0.8.8" 1229 | source = "registry+https://github.com/rust-lang/crates.io-index" 1230 | checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" 1231 | dependencies = [ 1232 | "cfg-if", 1233 | "windows-targets 0.53.5", 1234 | ] 1235 | 1236 | [[package]] 1237 | name = "libm" 1238 | version = "0.2.8" 1239 | source = "registry+https://github.com/rust-lang/crates.io-index" 1240 | checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" 1241 | 1242 | [[package]] 1243 | name = "libsqlite3-sys" 1244 | version = "0.30.1" 1245 | source = "registry+https://github.com/rust-lang/crates.io-index" 1246 | checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" 1247 | dependencies = [ 1248 | "pkg-config", 1249 | "vcpkg", 1250 | ] 1251 | 1252 | [[package]] 1253 | name = "link-cplusplus" 1254 | version = "1.0.10" 1255 | source = "registry+https://github.com/rust-lang/crates.io-index" 1256 | checksum = "4a6f6da007f968f9def0d65a05b187e2960183de70c160204ecfccf0ee330212" 1257 | dependencies = [ 1258 | "cc", 1259 | ] 1260 | 1261 | [[package]] 1262 | name = "linux-raw-sys" 1263 | version = "0.4.13" 1264 | source = "registry+https://github.com/rust-lang/crates.io-index" 1265 | checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" 1266 | 1267 | [[package]] 1268 | name = "linux-raw-sys" 1269 | version = "0.9.4" 1270 | source = "registry+https://github.com/rust-lang/crates.io-index" 1271 | checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" 1272 | 1273 | [[package]] 1274 | name = "litemap" 1275 | version = "0.8.0" 1276 | source = "registry+https://github.com/rust-lang/crates.io-index" 1277 | checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" 1278 | 1279 | [[package]] 1280 | name = "lock_api" 1281 | version = "0.4.11" 1282 | source = "registry+https://github.com/rust-lang/crates.io-index" 1283 | checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" 1284 | dependencies = [ 1285 | "autocfg", 1286 | "scopeguard", 1287 | ] 1288 | 1289 | [[package]] 1290 | name = "log" 1291 | version = "0.4.21" 1292 | source = "registry+https://github.com/rust-lang/crates.io-index" 1293 | checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" 1294 | 1295 | [[package]] 1296 | name = "matrixmultiply" 1297 | version = "0.3.8" 1298 | source = "registry+https://github.com/rust-lang/crates.io-index" 1299 | checksum = "7574c1cf36da4798ab73da5b215bbf444f50718207754cb522201d78d1cd0ff2" 1300 | dependencies = [ 1301 | "autocfg", 1302 | "rawpointer", 1303 | ] 1304 | 1305 | [[package]] 1306 | name = "memchr" 1307 | version = "2.7.1" 1308 | source = "registry+https://github.com/rust-lang/crates.io-index" 1309 | checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" 1310 | 1311 | [[package]] 1312 | name = "memoffset" 1313 | version = "0.9.0" 1314 | source = "registry+https://github.com/rust-lang/crates.io-index" 1315 | checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" 1316 | dependencies = [ 1317 | "autocfg", 1318 | ] 1319 | 1320 | [[package]] 1321 | name = "mime" 1322 | version = "0.3.17" 1323 | source = "registry+https://github.com/rust-lang/crates.io-index" 1324 | checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" 1325 | 1326 | [[package]] 1327 | name = "minimal-lexical" 1328 | version = "0.2.1" 1329 | source = "registry+https://github.com/rust-lang/crates.io-index" 1330 | checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" 1331 | 1332 | [[package]] 1333 | name = "miniz_oxide" 1334 | version = "0.8.9" 1335 | source = "registry+https://github.com/rust-lang/crates.io-index" 1336 | checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" 1337 | dependencies = [ 1338 | "adler2", 1339 | ] 1340 | 1341 | [[package]] 1342 | name = "mio" 1343 | version = "1.1.0" 1344 | source = "registry+https://github.com/rust-lang/crates.io-index" 1345 | checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873" 1346 | dependencies = [ 1347 | "libc", 1348 | "wasi 0.11.0+wasi-snapshot-preview1", 1349 | "windows-sys 0.61.2", 1350 | ] 1351 | 1352 | [[package]] 1353 | name = "ndarray" 1354 | version = "0.17.1" 1355 | source = "registry+https://github.com/rust-lang/crates.io-index" 1356 | checksum = "0c7c9125e8f6f10c9da3aad044cc918cf8784fa34de857b1aa68038eb05a50a9" 1357 | dependencies = [ 1358 | "matrixmultiply", 1359 | "num-complex", 1360 | "num-integer", 1361 | "num-traits", 1362 | "portable-atomic", 1363 | "portable-atomic-util", 1364 | "rawpointer", 1365 | ] 1366 | 1367 | [[package]] 1368 | name = "nix" 1369 | version = "0.30.1" 1370 | source = "registry+https://github.com/rust-lang/crates.io-index" 1371 | checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" 1372 | dependencies = [ 1373 | "bitflags 2.10.0", 1374 | "cfg-if", 1375 | "cfg_aliases", 1376 | "libc", 1377 | ] 1378 | 1379 | [[package]] 1380 | name = "nom" 1381 | version = "7.1.3" 1382 | source = "registry+https://github.com/rust-lang/crates.io-index" 1383 | checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" 1384 | dependencies = [ 1385 | "memchr", 1386 | "minimal-lexical", 1387 | ] 1388 | 1389 | [[package]] 1390 | name = "num-complex" 1391 | version = "0.4.5" 1392 | source = "registry+https://github.com/rust-lang/crates.io-index" 1393 | checksum = "23c6602fda94a57c990fe0df199a035d83576b496aa29f4e634a8ac6004e68a6" 1394 | dependencies = [ 1395 | "num-traits", 1396 | ] 1397 | 1398 | [[package]] 1399 | name = "num-integer" 1400 | version = "0.1.46" 1401 | source = "registry+https://github.com/rust-lang/crates.io-index" 1402 | checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" 1403 | dependencies = [ 1404 | "num-traits", 1405 | ] 1406 | 1407 | [[package]] 1408 | name = "num-traits" 1409 | version = "0.2.19" 1410 | source = "registry+https://github.com/rust-lang/crates.io-index" 1411 | checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" 1412 | dependencies = [ 1413 | "autocfg", 1414 | "libm", 1415 | ] 1416 | 1417 | [[package]] 1418 | name = "numpy" 1419 | version = "0.27.1" 1420 | source = "registry+https://github.com/rust-lang/crates.io-index" 1421 | checksum = "7aac2e6a6e4468ffa092ad43c39b81c79196c2bb773b8db4085f695efe3bba17" 1422 | dependencies = [ 1423 | "libc", 1424 | "ndarray", 1425 | "num-complex", 1426 | "num-integer", 1427 | "num-traits", 1428 | "pyo3", 1429 | "pyo3-build-config", 1430 | "rustc-hash", 1431 | ] 1432 | 1433 | [[package]] 1434 | name = "nvtiff-sys" 1435 | version = "0.1.2" 1436 | source = "registry+https://github.com/rust-lang/crates.io-index" 1437 | checksum = "045c95d4e228534b7bce63ca80d76b7eda5a224b0d5321df1f45cd32b32416e2" 1438 | dependencies = [ 1439 | "bindgen", 1440 | ] 1441 | 1442 | [[package]] 1443 | name = "object_store" 1444 | version = "0.12.3" 1445 | source = "registry+https://github.com/rust-lang/crates.io-index" 1446 | checksum = "efc4f07659e11cd45a341cd24d71e683e3be65d9ff1f8150061678fe60437496" 1447 | dependencies = [ 1448 | "async-trait", 1449 | "base64", 1450 | "bytes", 1451 | "chrono", 1452 | "form_urlencoded", 1453 | "futures", 1454 | "http", 1455 | "http-body-util", 1456 | "humantime", 1457 | "hyper", 1458 | "itertools 0.14.0", 1459 | "parking_lot", 1460 | "percent-encoding", 1461 | "quick-xml", 1462 | "rand 0.9.2", 1463 | "reqwest", 1464 | "ring", 1465 | "serde", 1466 | "serde_json", 1467 | "serde_urlencoded", 1468 | "thiserror", 1469 | "tokio", 1470 | "tracing", 1471 | "url", 1472 | "walkdir", 1473 | "wasm-bindgen-futures", 1474 | "web-time", 1475 | ] 1476 | 1477 | [[package]] 1478 | name = "once_cell" 1479 | version = "1.21.3" 1480 | source = "registry+https://github.com/rust-lang/crates.io-index" 1481 | checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" 1482 | 1483 | [[package]] 1484 | name = "oorandom" 1485 | version = "11.1.5" 1486 | source = "registry+https://github.com/rust-lang/crates.io-index" 1487 | checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" 1488 | 1489 | [[package]] 1490 | name = "openssl-probe" 1491 | version = "0.1.5" 1492 | source = "registry+https://github.com/rust-lang/crates.io-index" 1493 | checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" 1494 | 1495 | [[package]] 1496 | name = "parking_lot" 1497 | version = "0.12.1" 1498 | source = "registry+https://github.com/rust-lang/crates.io-index" 1499 | checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" 1500 | dependencies = [ 1501 | "lock_api", 1502 | "parking_lot_core", 1503 | ] 1504 | 1505 | [[package]] 1506 | name = "parking_lot_core" 1507 | version = "0.9.9" 1508 | source = "registry+https://github.com/rust-lang/crates.io-index" 1509 | checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" 1510 | dependencies = [ 1511 | "cfg-if", 1512 | "libc", 1513 | "redox_syscall", 1514 | "smallvec", 1515 | "windows-targets 0.48.5", 1516 | ] 1517 | 1518 | [[package]] 1519 | name = "percent-encoding" 1520 | version = "2.3.2" 1521 | source = "registry+https://github.com/rust-lang/crates.io-index" 1522 | checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" 1523 | 1524 | [[package]] 1525 | name = "pin-project" 1526 | version = "1.1.10" 1527 | source = "registry+https://github.com/rust-lang/crates.io-index" 1528 | checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" 1529 | dependencies = [ 1530 | "pin-project-internal", 1531 | ] 1532 | 1533 | [[package]] 1534 | name = "pin-project-internal" 1535 | version = "1.1.10" 1536 | source = "registry+https://github.com/rust-lang/crates.io-index" 1537 | checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" 1538 | dependencies = [ 1539 | "proc-macro2", 1540 | "quote", 1541 | "syn", 1542 | ] 1543 | 1544 | [[package]] 1545 | name = "pin-project-lite" 1546 | version = "0.2.13" 1547 | source = "registry+https://github.com/rust-lang/crates.io-index" 1548 | checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" 1549 | 1550 | [[package]] 1551 | name = "pin-utils" 1552 | version = "0.1.0" 1553 | source = "registry+https://github.com/rust-lang/crates.io-index" 1554 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 1555 | 1556 | [[package]] 1557 | name = "pkg-config" 1558 | version = "0.3.32" 1559 | source = "registry+https://github.com/rust-lang/crates.io-index" 1560 | checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" 1561 | 1562 | [[package]] 1563 | name = "plotters" 1564 | version = "0.3.7" 1565 | source = "registry+https://github.com/rust-lang/crates.io-index" 1566 | checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" 1567 | dependencies = [ 1568 | "num-traits", 1569 | "plotters-backend", 1570 | "plotters-svg", 1571 | "wasm-bindgen", 1572 | "web-sys", 1573 | ] 1574 | 1575 | [[package]] 1576 | name = "plotters-backend" 1577 | version = "0.3.7" 1578 | source = "registry+https://github.com/rust-lang/crates.io-index" 1579 | checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" 1580 | 1581 | [[package]] 1582 | name = "plotters-svg" 1583 | version = "0.3.7" 1584 | source = "registry+https://github.com/rust-lang/crates.io-index" 1585 | checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" 1586 | dependencies = [ 1587 | "plotters-backend", 1588 | ] 1589 | 1590 | [[package]] 1591 | name = "portable-atomic" 1592 | version = "1.6.0" 1593 | source = "registry+https://github.com/rust-lang/crates.io-index" 1594 | checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0" 1595 | 1596 | [[package]] 1597 | name = "portable-atomic-util" 1598 | version = "0.2.4" 1599 | source = "registry+https://github.com/rust-lang/crates.io-index" 1600 | checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" 1601 | dependencies = [ 1602 | "portable-atomic", 1603 | ] 1604 | 1605 | [[package]] 1606 | name = "potential_utf" 1607 | version = "0.1.3" 1608 | source = "registry+https://github.com/rust-lang/crates.io-index" 1609 | checksum = "84df19adbe5b5a0782edcab45899906947ab039ccf4573713735ee7de1e6b08a" 1610 | dependencies = [ 1611 | "zerovec", 1612 | ] 1613 | 1614 | [[package]] 1615 | name = "ppv-lite86" 1616 | version = "0.2.17" 1617 | source = "registry+https://github.com/rust-lang/crates.io-index" 1618 | checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" 1619 | 1620 | [[package]] 1621 | name = "prettyplease" 1622 | version = "0.2.33" 1623 | source = "registry+https://github.com/rust-lang/crates.io-index" 1624 | checksum = "9dee91521343f4c5c6a63edd65e54f31f5c92fe8978c40a4282f8372194c6a7d" 1625 | dependencies = [ 1626 | "proc-macro2", 1627 | "syn", 1628 | ] 1629 | 1630 | [[package]] 1631 | name = "proc-macro-crate" 1632 | version = "3.4.0" 1633 | source = "registry+https://github.com/rust-lang/crates.io-index" 1634 | checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" 1635 | dependencies = [ 1636 | "toml_edit", 1637 | ] 1638 | 1639 | [[package]] 1640 | name = "proc-macro2" 1641 | version = "1.0.95" 1642 | source = "registry+https://github.com/rust-lang/crates.io-index" 1643 | checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" 1644 | dependencies = [ 1645 | "unicode-ident", 1646 | ] 1647 | 1648 | [[package]] 1649 | name = "proj-sys" 1650 | version = "0.27.0" 1651 | source = "registry+https://github.com/rust-lang/crates.io-index" 1652 | checksum = "48ffa255a853264169516c9fc21f524446aca5b091daab6052f2a9b3e5e90359" 1653 | dependencies = [ 1654 | "cmake", 1655 | "flate2", 1656 | "libsqlite3-sys", 1657 | "link-cplusplus", 1658 | "pkg-config", 1659 | "tar", 1660 | ] 1661 | 1662 | [[package]] 1663 | name = "pyo3" 1664 | version = "0.27.1" 1665 | source = "registry+https://github.com/rust-lang/crates.io-index" 1666 | checksum = "37a6df7eab65fc7bee654a421404947e10a0f7085b6951bf2ea395f4659fb0cf" 1667 | dependencies = [ 1668 | "indoc", 1669 | "libc", 1670 | "memoffset", 1671 | "once_cell", 1672 | "portable-atomic", 1673 | "pyo3-build-config", 1674 | "pyo3-ffi", 1675 | "pyo3-macros", 1676 | "unindent", 1677 | ] 1678 | 1679 | [[package]] 1680 | name = "pyo3-build-config" 1681 | version = "0.27.1" 1682 | source = "registry+https://github.com/rust-lang/crates.io-index" 1683 | checksum = "f77d387774f6f6eec64a004eac0ed525aab7fa1966d94b42f743797b3e395afb" 1684 | dependencies = [ 1685 | "target-lexicon", 1686 | ] 1687 | 1688 | [[package]] 1689 | name = "pyo3-ffi" 1690 | version = "0.27.1" 1691 | source = "registry+https://github.com/rust-lang/crates.io-index" 1692 | checksum = "2dd13844a4242793e02df3e2ec093f540d948299a6a77ea9ce7afd8623f542be" 1693 | dependencies = [ 1694 | "libc", 1695 | "pyo3-build-config", 1696 | ] 1697 | 1698 | [[package]] 1699 | name = "pyo3-macros" 1700 | version = "0.27.1" 1701 | source = "registry+https://github.com/rust-lang/crates.io-index" 1702 | checksum = "eaf8f9f1108270b90d3676b8679586385430e5c0bb78bb5f043f95499c821a71" 1703 | dependencies = [ 1704 | "proc-macro2", 1705 | "pyo3-macros-backend", 1706 | "quote", 1707 | "syn", 1708 | ] 1709 | 1710 | [[package]] 1711 | name = "pyo3-macros-backend" 1712 | version = "0.27.1" 1713 | source = "registry+https://github.com/rust-lang/crates.io-index" 1714 | checksum = "70a3b2274450ba5288bc9b8c1b69ff569d1d61189d4bff38f8d22e03d17f932b" 1715 | dependencies = [ 1716 | "heck", 1717 | "proc-macro2", 1718 | "pyo3-build-config", 1719 | "quote", 1720 | "syn", 1721 | ] 1722 | 1723 | [[package]] 1724 | name = "quick-error" 1725 | version = "2.0.1" 1726 | source = "registry+https://github.com/rust-lang/crates.io-index" 1727 | checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" 1728 | 1729 | [[package]] 1730 | name = "quick-xml" 1731 | version = "0.38.3" 1732 | source = "registry+https://github.com/rust-lang/crates.io-index" 1733 | checksum = "42a232e7487fc2ef313d96dde7948e7a3c05101870d8985e4fd8d26aedd27b89" 1734 | dependencies = [ 1735 | "memchr", 1736 | "serde", 1737 | ] 1738 | 1739 | [[package]] 1740 | name = "quinn" 1741 | version = "0.11.6" 1742 | source = "registry+https://github.com/rust-lang/crates.io-index" 1743 | checksum = "62e96808277ec6f97351a2380e6c25114bc9e67037775464979f3037c92d05ef" 1744 | dependencies = [ 1745 | "bytes", 1746 | "pin-project-lite", 1747 | "quinn-proto", 1748 | "quinn-udp", 1749 | "rustc-hash", 1750 | "rustls", 1751 | "socket2 0.5.6", 1752 | "thiserror", 1753 | "tokio", 1754 | "tracing", 1755 | ] 1756 | 1757 | [[package]] 1758 | name = "quinn-proto" 1759 | version = "0.11.9" 1760 | source = "registry+https://github.com/rust-lang/crates.io-index" 1761 | checksum = "a2fe5ef3495d7d2e377ff17b1a8ce2ee2ec2a18cde8b6ad6619d65d0701c135d" 1762 | dependencies = [ 1763 | "bytes", 1764 | "getrandom 0.2.12", 1765 | "rand 0.8.5", 1766 | "ring", 1767 | "rustc-hash", 1768 | "rustls", 1769 | "rustls-pki-types", 1770 | "slab", 1771 | "thiserror", 1772 | "tinyvec", 1773 | "tracing", 1774 | "web-time", 1775 | ] 1776 | 1777 | [[package]] 1778 | name = "quinn-udp" 1779 | version = "0.5.14" 1780 | source = "registry+https://github.com/rust-lang/crates.io-index" 1781 | checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" 1782 | dependencies = [ 1783 | "cfg_aliases", 1784 | "libc", 1785 | "once_cell", 1786 | "socket2 0.6.1", 1787 | "tracing", 1788 | "windows-sys 0.60.2", 1789 | ] 1790 | 1791 | [[package]] 1792 | name = "quote" 1793 | version = "1.0.41" 1794 | source = "registry+https://github.com/rust-lang/crates.io-index" 1795 | checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" 1796 | dependencies = [ 1797 | "proc-macro2", 1798 | ] 1799 | 1800 | [[package]] 1801 | name = "r-efi" 1802 | version = "5.3.0" 1803 | source = "registry+https://github.com/rust-lang/crates.io-index" 1804 | checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" 1805 | 1806 | [[package]] 1807 | name = "rand" 1808 | version = "0.8.5" 1809 | source = "registry+https://github.com/rust-lang/crates.io-index" 1810 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 1811 | dependencies = [ 1812 | "libc", 1813 | "rand_chacha 0.3.1", 1814 | "rand_core 0.6.4", 1815 | ] 1816 | 1817 | [[package]] 1818 | name = "rand" 1819 | version = "0.9.2" 1820 | source = "registry+https://github.com/rust-lang/crates.io-index" 1821 | checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" 1822 | dependencies = [ 1823 | "rand_chacha 0.9.0", 1824 | "rand_core 0.9.3", 1825 | ] 1826 | 1827 | [[package]] 1828 | name = "rand_chacha" 1829 | version = "0.3.1" 1830 | source = "registry+https://github.com/rust-lang/crates.io-index" 1831 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 1832 | dependencies = [ 1833 | "ppv-lite86", 1834 | "rand_core 0.6.4", 1835 | ] 1836 | 1837 | [[package]] 1838 | name = "rand_chacha" 1839 | version = "0.9.0" 1840 | source = "registry+https://github.com/rust-lang/crates.io-index" 1841 | checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" 1842 | dependencies = [ 1843 | "ppv-lite86", 1844 | "rand_core 0.9.3", 1845 | ] 1846 | 1847 | [[package]] 1848 | name = "rand_core" 1849 | version = "0.6.4" 1850 | source = "registry+https://github.com/rust-lang/crates.io-index" 1851 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 1852 | dependencies = [ 1853 | "getrandom 0.2.12", 1854 | ] 1855 | 1856 | [[package]] 1857 | name = "rand_core" 1858 | version = "0.9.3" 1859 | source = "registry+https://github.com/rust-lang/crates.io-index" 1860 | checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" 1861 | dependencies = [ 1862 | "getrandom 0.3.3", 1863 | ] 1864 | 1865 | [[package]] 1866 | name = "rawpointer" 1867 | version = "0.2.1" 1868 | source = "registry+https://github.com/rust-lang/crates.io-index" 1869 | checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" 1870 | 1871 | [[package]] 1872 | name = "rayon" 1873 | version = "1.10.0" 1874 | source = "registry+https://github.com/rust-lang/crates.io-index" 1875 | checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" 1876 | dependencies = [ 1877 | "either", 1878 | "rayon-core", 1879 | ] 1880 | 1881 | [[package]] 1882 | name = "rayon-core" 1883 | version = "1.12.1" 1884 | source = "registry+https://github.com/rust-lang/crates.io-index" 1885 | checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" 1886 | dependencies = [ 1887 | "crossbeam-deque", 1888 | "crossbeam-utils", 1889 | ] 1890 | 1891 | [[package]] 1892 | name = "redox_syscall" 1893 | version = "0.4.1" 1894 | source = "registry+https://github.com/rust-lang/crates.io-index" 1895 | checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" 1896 | dependencies = [ 1897 | "bitflags 1.3.2", 1898 | ] 1899 | 1900 | [[package]] 1901 | name = "regex" 1902 | version = "1.11.1" 1903 | source = "registry+https://github.com/rust-lang/crates.io-index" 1904 | checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" 1905 | dependencies = [ 1906 | "aho-corasick", 1907 | "memchr", 1908 | "regex-automata", 1909 | "regex-syntax", 1910 | ] 1911 | 1912 | [[package]] 1913 | name = "regex-automata" 1914 | version = "0.4.9" 1915 | source = "registry+https://github.com/rust-lang/crates.io-index" 1916 | checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" 1917 | dependencies = [ 1918 | "aho-corasick", 1919 | "memchr", 1920 | "regex-syntax", 1921 | ] 1922 | 1923 | [[package]] 1924 | name = "regex-syntax" 1925 | version = "0.8.5" 1926 | source = "registry+https://github.com/rust-lang/crates.io-index" 1927 | checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" 1928 | 1929 | [[package]] 1930 | name = "relative-path" 1931 | version = "1.9.3" 1932 | source = "registry+https://github.com/rust-lang/crates.io-index" 1933 | checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2" 1934 | 1935 | [[package]] 1936 | name = "reqwest" 1937 | version = "0.12.5" 1938 | source = "registry+https://github.com/rust-lang/crates.io-index" 1939 | checksum = "c7d6d2a27d57148378eb5e111173f4276ad26340ecc5c49a4a2152167a2d6a37" 1940 | dependencies = [ 1941 | "base64", 1942 | "bytes", 1943 | "futures-core", 1944 | "futures-util", 1945 | "h2", 1946 | "http", 1947 | "http-body", 1948 | "http-body-util", 1949 | "hyper", 1950 | "hyper-rustls", 1951 | "hyper-util", 1952 | "ipnet", 1953 | "js-sys", 1954 | "log", 1955 | "mime", 1956 | "once_cell", 1957 | "percent-encoding", 1958 | "pin-project-lite", 1959 | "quinn", 1960 | "rustls", 1961 | "rustls-native-certs 0.7.3", 1962 | "rustls-pemfile", 1963 | "rustls-pki-types", 1964 | "serde", 1965 | "serde_json", 1966 | "serde_urlencoded", 1967 | "sync_wrapper", 1968 | "tokio", 1969 | "tokio-rustls", 1970 | "tokio-util", 1971 | "tower-service", 1972 | "url", 1973 | "wasm-bindgen", 1974 | "wasm-bindgen-futures", 1975 | "wasm-streams", 1976 | "web-sys", 1977 | "winreg", 1978 | ] 1979 | 1980 | [[package]] 1981 | name = "ring" 1982 | version = "0.17.14" 1983 | source = "registry+https://github.com/rust-lang/crates.io-index" 1984 | checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" 1985 | dependencies = [ 1986 | "cc", 1987 | "cfg-if", 1988 | "getrandom 0.2.12", 1989 | "libc", 1990 | "untrusted", 1991 | "windows-sys 0.52.0", 1992 | ] 1993 | 1994 | [[package]] 1995 | name = "robust" 1996 | version = "1.1.0" 1997 | source = "registry+https://github.com/rust-lang/crates.io-index" 1998 | checksum = "cbf4a6aa5f6d6888f39e980649f3ad6b666acdce1d78e95b8a2cb076e687ae30" 1999 | 2000 | [[package]] 2001 | name = "rstar" 2002 | version = "0.12.0" 2003 | source = "registry+https://github.com/rust-lang/crates.io-index" 2004 | checksum = "133315eb94c7b1e8d0cb097e5a710d850263372fd028fff18969de708afc7008" 2005 | dependencies = [ 2006 | "heapless", 2007 | "num-traits", 2008 | "smallvec", 2009 | ] 2010 | 2011 | [[package]] 2012 | name = "rstest" 2013 | version = "0.26.1" 2014 | source = "registry+https://github.com/rust-lang/crates.io-index" 2015 | checksum = "f5a3193c063baaa2a95a33f03035c8a72b83d97a54916055ba22d35ed3839d49" 2016 | dependencies = [ 2017 | "futures-timer", 2018 | "futures-util", 2019 | "rstest_macros", 2020 | ] 2021 | 2022 | [[package]] 2023 | name = "rstest_macros" 2024 | version = "0.26.1" 2025 | source = "registry+https://github.com/rust-lang/crates.io-index" 2026 | checksum = "9c845311f0ff7951c5506121a9ad75aec44d083c31583b2ea5a30bcb0b0abba0" 2027 | dependencies = [ 2028 | "cfg-if", 2029 | "glob", 2030 | "proc-macro-crate", 2031 | "proc-macro2", 2032 | "quote", 2033 | "regex", 2034 | "relative-path", 2035 | "rustc_version", 2036 | "syn", 2037 | "unicode-ident", 2038 | ] 2039 | 2040 | [[package]] 2041 | name = "rustc-hash" 2042 | version = "2.1.1" 2043 | source = "registry+https://github.com/rust-lang/crates.io-index" 2044 | checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" 2045 | 2046 | [[package]] 2047 | name = "rustc_version" 2048 | version = "0.4.1" 2049 | source = "registry+https://github.com/rust-lang/crates.io-index" 2050 | checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" 2051 | dependencies = [ 2052 | "semver", 2053 | ] 2054 | 2055 | [[package]] 2056 | name = "rustix" 2057 | version = "0.38.31" 2058 | source = "registry+https://github.com/rust-lang/crates.io-index" 2059 | checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" 2060 | dependencies = [ 2061 | "bitflags 2.10.0", 2062 | "errno", 2063 | "libc", 2064 | "linux-raw-sys 0.4.13", 2065 | "windows-sys 0.52.0", 2066 | ] 2067 | 2068 | [[package]] 2069 | name = "rustix" 2070 | version = "1.0.7" 2071 | source = "registry+https://github.com/rust-lang/crates.io-index" 2072 | checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" 2073 | dependencies = [ 2074 | "bitflags 2.10.0", 2075 | "errno", 2076 | "libc", 2077 | "linux-raw-sys 0.9.4", 2078 | "windows-sys 0.52.0", 2079 | ] 2080 | 2081 | [[package]] 2082 | name = "rustls" 2083 | version = "0.23.31" 2084 | source = "registry+https://github.com/rust-lang/crates.io-index" 2085 | checksum = "c0ebcbd2f03de0fc1122ad9bb24b127a5a6cd51d72604a3f3c50ac459762b6cc" 2086 | dependencies = [ 2087 | "once_cell", 2088 | "ring", 2089 | "rustls-pki-types", 2090 | "rustls-webpki", 2091 | "subtle", 2092 | "zeroize", 2093 | ] 2094 | 2095 | [[package]] 2096 | name = "rustls-native-certs" 2097 | version = "0.7.3" 2098 | source = "registry+https://github.com/rust-lang/crates.io-index" 2099 | checksum = "e5bfb394eeed242e909609f56089eecfe5fda225042e8b171791b9c95f5931e5" 2100 | dependencies = [ 2101 | "openssl-probe", 2102 | "rustls-pemfile", 2103 | "rustls-pki-types", 2104 | "schannel", 2105 | "security-framework 2.9.2", 2106 | ] 2107 | 2108 | [[package]] 2109 | name = "rustls-native-certs" 2110 | version = "0.8.1" 2111 | source = "registry+https://github.com/rust-lang/crates.io-index" 2112 | checksum = "7fcff2dd52b58a8d98a70243663a0d234c4e2b79235637849d15913394a247d3" 2113 | dependencies = [ 2114 | "openssl-probe", 2115 | "rustls-pki-types", 2116 | "schannel", 2117 | "security-framework 3.4.0", 2118 | ] 2119 | 2120 | [[package]] 2121 | name = "rustls-pemfile" 2122 | version = "2.2.0" 2123 | source = "registry+https://github.com/rust-lang/crates.io-index" 2124 | checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" 2125 | dependencies = [ 2126 | "rustls-pki-types", 2127 | ] 2128 | 2129 | [[package]] 2130 | name = "rustls-pki-types" 2131 | version = "1.12.0" 2132 | source = "registry+https://github.com/rust-lang/crates.io-index" 2133 | checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" 2134 | dependencies = [ 2135 | "web-time", 2136 | "zeroize", 2137 | ] 2138 | 2139 | [[package]] 2140 | name = "rustls-webpki" 2141 | version = "0.103.4" 2142 | source = "registry+https://github.com/rust-lang/crates.io-index" 2143 | checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc" 2144 | dependencies = [ 2145 | "ring", 2146 | "rustls-pki-types", 2147 | "untrusted", 2148 | ] 2149 | 2150 | [[package]] 2151 | name = "ryu" 2152 | version = "1.0.17" 2153 | source = "registry+https://github.com/rust-lang/crates.io-index" 2154 | checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" 2155 | 2156 | [[package]] 2157 | name = "same-file" 2158 | version = "1.0.6" 2159 | source = "registry+https://github.com/rust-lang/crates.io-index" 2160 | checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" 2161 | dependencies = [ 2162 | "winapi-util", 2163 | ] 2164 | 2165 | [[package]] 2166 | name = "schannel" 2167 | version = "0.1.23" 2168 | source = "registry+https://github.com/rust-lang/crates.io-index" 2169 | checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" 2170 | dependencies = [ 2171 | "windows-sys 0.52.0", 2172 | ] 2173 | 2174 | [[package]] 2175 | name = "scopeguard" 2176 | version = "1.2.0" 2177 | source = "registry+https://github.com/rust-lang/crates.io-index" 2178 | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 2179 | 2180 | [[package]] 2181 | name = "security-framework" 2182 | version = "2.9.2" 2183 | source = "registry+https://github.com/rust-lang/crates.io-index" 2184 | checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" 2185 | dependencies = [ 2186 | "bitflags 1.3.2", 2187 | "core-foundation 0.9.4", 2188 | "core-foundation-sys", 2189 | "libc", 2190 | "security-framework-sys", 2191 | ] 2192 | 2193 | [[package]] 2194 | name = "security-framework" 2195 | version = "3.4.0" 2196 | source = "registry+https://github.com/rust-lang/crates.io-index" 2197 | checksum = "60b369d18893388b345804dc0007963c99b7d665ae71d275812d828c6f089640" 2198 | dependencies = [ 2199 | "bitflags 2.10.0", 2200 | "core-foundation 0.10.1", 2201 | "core-foundation-sys", 2202 | "libc", 2203 | "security-framework-sys", 2204 | ] 2205 | 2206 | [[package]] 2207 | name = "security-framework-sys" 2208 | version = "2.15.0" 2209 | source = "registry+https://github.com/rust-lang/crates.io-index" 2210 | checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" 2211 | dependencies = [ 2212 | "core-foundation-sys", 2213 | "libc", 2214 | ] 2215 | 2216 | [[package]] 2217 | name = "semver" 2218 | version = "1.0.26" 2219 | source = "registry+https://github.com/rust-lang/crates.io-index" 2220 | checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" 2221 | 2222 | [[package]] 2223 | name = "serde" 2224 | version = "1.0.228" 2225 | source = "registry+https://github.com/rust-lang/crates.io-index" 2226 | checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" 2227 | dependencies = [ 2228 | "serde_core", 2229 | "serde_derive", 2230 | ] 2231 | 2232 | [[package]] 2233 | name = "serde_core" 2234 | version = "1.0.228" 2235 | source = "registry+https://github.com/rust-lang/crates.io-index" 2236 | checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" 2237 | dependencies = [ 2238 | "serde_derive", 2239 | ] 2240 | 2241 | [[package]] 2242 | name = "serde_derive" 2243 | version = "1.0.228" 2244 | source = "registry+https://github.com/rust-lang/crates.io-index" 2245 | checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" 2246 | dependencies = [ 2247 | "proc-macro2", 2248 | "quote", 2249 | "syn", 2250 | ] 2251 | 2252 | [[package]] 2253 | name = "serde_json" 2254 | version = "1.0.145" 2255 | source = "registry+https://github.com/rust-lang/crates.io-index" 2256 | checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" 2257 | dependencies = [ 2258 | "itoa", 2259 | "memchr", 2260 | "ryu", 2261 | "serde", 2262 | "serde_core", 2263 | ] 2264 | 2265 | [[package]] 2266 | name = "serde_urlencoded" 2267 | version = "0.7.1" 2268 | source = "registry+https://github.com/rust-lang/crates.io-index" 2269 | checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" 2270 | dependencies = [ 2271 | "form_urlencoded", 2272 | "itoa", 2273 | "ryu", 2274 | "serde", 2275 | ] 2276 | 2277 | [[package]] 2278 | name = "shlex" 2279 | version = "1.3.0" 2280 | source = "registry+https://github.com/rust-lang/crates.io-index" 2281 | checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 2282 | 2283 | [[package]] 2284 | name = "slab" 2285 | version = "0.4.9" 2286 | source = "registry+https://github.com/rust-lang/crates.io-index" 2287 | checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" 2288 | dependencies = [ 2289 | "autocfg", 2290 | ] 2291 | 2292 | [[package]] 2293 | name = "smallvec" 2294 | version = "1.13.1" 2295 | source = "registry+https://github.com/rust-lang/crates.io-index" 2296 | checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" 2297 | 2298 | [[package]] 2299 | name = "snafu" 2300 | version = "0.8.6" 2301 | source = "registry+https://github.com/rust-lang/crates.io-index" 2302 | checksum = "320b01e011bf8d5d7a4a4a4be966d9160968935849c83b918827f6a435e7f627" 2303 | dependencies = [ 2304 | "snafu-derive", 2305 | ] 2306 | 2307 | [[package]] 2308 | name = "snafu-derive" 2309 | version = "0.8.6" 2310 | source = "registry+https://github.com/rust-lang/crates.io-index" 2311 | checksum = "1961e2ef424c1424204d3a5d6975f934f56b6d50ff5732382d84ebf460e147f7" 2312 | dependencies = [ 2313 | "heck", 2314 | "proc-macro2", 2315 | "quote", 2316 | "syn", 2317 | ] 2318 | 2319 | [[package]] 2320 | name = "socket2" 2321 | version = "0.5.6" 2322 | source = "registry+https://github.com/rust-lang/crates.io-index" 2323 | checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" 2324 | dependencies = [ 2325 | "libc", 2326 | "windows-sys 0.52.0", 2327 | ] 2328 | 2329 | [[package]] 2330 | name = "socket2" 2331 | version = "0.6.1" 2332 | source = "registry+https://github.com/rust-lang/crates.io-index" 2333 | checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" 2334 | dependencies = [ 2335 | "libc", 2336 | "windows-sys 0.60.2", 2337 | ] 2338 | 2339 | [[package]] 2340 | name = "spade" 2341 | version = "2.12.1" 2342 | source = "registry+https://github.com/rust-lang/crates.io-index" 2343 | checksum = "93f5ef1f863aca7d1d7dda7ccfc36a0a4279bd6d3c375176e5e0712e25cb4889" 2344 | dependencies = [ 2345 | "hashbrown 0.14.3", 2346 | "num-traits", 2347 | "robust", 2348 | "smallvec", 2349 | ] 2350 | 2351 | [[package]] 2352 | name = "stable_deref_trait" 2353 | version = "1.2.0" 2354 | source = "registry+https://github.com/rust-lang/crates.io-index" 2355 | checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 2356 | 2357 | [[package]] 2358 | name = "statrs" 2359 | version = "0.18.0" 2360 | source = "registry+https://github.com/rust-lang/crates.io-index" 2361 | checksum = "2a3fe7c28c6512e766b0874335db33c94ad7b8f9054228ae1c2abd47ce7d335e" 2362 | dependencies = [ 2363 | "approx", 2364 | "num-traits", 2365 | ] 2366 | 2367 | [[package]] 2368 | name = "subtle" 2369 | version = "2.6.1" 2370 | source = "registry+https://github.com/rust-lang/crates.io-index" 2371 | checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" 2372 | 2373 | [[package]] 2374 | name = "syn" 2375 | version = "2.0.101" 2376 | source = "registry+https://github.com/rust-lang/crates.io-index" 2377 | checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" 2378 | dependencies = [ 2379 | "proc-macro2", 2380 | "quote", 2381 | "unicode-ident", 2382 | ] 2383 | 2384 | [[package]] 2385 | name = "sync_wrapper" 2386 | version = "1.0.2" 2387 | source = "registry+https://github.com/rust-lang/crates.io-index" 2388 | checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" 2389 | 2390 | [[package]] 2391 | name = "synstructure" 2392 | version = "0.13.2" 2393 | source = "registry+https://github.com/rust-lang/crates.io-index" 2394 | checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" 2395 | dependencies = [ 2396 | "proc-macro2", 2397 | "quote", 2398 | "syn", 2399 | ] 2400 | 2401 | [[package]] 2402 | name = "tar" 2403 | version = "0.4.44" 2404 | source = "registry+https://github.com/rust-lang/crates.io-index" 2405 | checksum = "1d863878d212c87a19c1a610eb53bb01fe12951c0501cf5a0d65f724914a667a" 2406 | dependencies = [ 2407 | "filetime", 2408 | "libc", 2409 | "xattr", 2410 | ] 2411 | 2412 | [[package]] 2413 | name = "target-lexicon" 2414 | version = "0.13.2" 2415 | source = "registry+https://github.com/rust-lang/crates.io-index" 2416 | checksum = "e502f78cdbb8ba4718f566c418c52bc729126ffd16baee5baa718cf25dd5a69a" 2417 | 2418 | [[package]] 2419 | name = "tempfile" 2420 | version = "3.10.1" 2421 | source = "registry+https://github.com/rust-lang/crates.io-index" 2422 | checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" 2423 | dependencies = [ 2424 | "cfg-if", 2425 | "fastrand", 2426 | "rustix 0.38.31", 2427 | "windows-sys 0.52.0", 2428 | ] 2429 | 2430 | [[package]] 2431 | name = "thiserror" 2432 | version = "2.0.12" 2433 | source = "registry+https://github.com/rust-lang/crates.io-index" 2434 | checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" 2435 | dependencies = [ 2436 | "thiserror-impl", 2437 | ] 2438 | 2439 | [[package]] 2440 | name = "thiserror-impl" 2441 | version = "2.0.12" 2442 | source = "registry+https://github.com/rust-lang/crates.io-index" 2443 | checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" 2444 | dependencies = [ 2445 | "proc-macro2", 2446 | "quote", 2447 | "syn", 2448 | ] 2449 | 2450 | [[package]] 2451 | name = "tiff" 2452 | version = "0.10.0" 2453 | source = "registry+https://github.com/rust-lang/crates.io-index" 2454 | checksum = "bd35f8bd40ae1e2b51e86f7a665f81160dc82b785f5de1f193a52f1298a76586" 2455 | dependencies = [ 2456 | "flate2", 2457 | "half", 2458 | "quick-error", 2459 | "weezl", 2460 | "zstd", 2461 | "zune-jpeg", 2462 | ] 2463 | 2464 | [[package]] 2465 | name = "tinystr" 2466 | version = "0.8.1" 2467 | source = "registry+https://github.com/rust-lang/crates.io-index" 2468 | checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" 2469 | dependencies = [ 2470 | "displaydoc", 2471 | "zerovec", 2472 | ] 2473 | 2474 | [[package]] 2475 | name = "tinytemplate" 2476 | version = "1.2.1" 2477 | source = "registry+https://github.com/rust-lang/crates.io-index" 2478 | checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" 2479 | dependencies = [ 2480 | "serde", 2481 | "serde_json", 2482 | ] 2483 | 2484 | [[package]] 2485 | name = "tinyvec" 2486 | version = "1.6.0" 2487 | source = "registry+https://github.com/rust-lang/crates.io-index" 2488 | checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" 2489 | dependencies = [ 2490 | "tinyvec_macros", 2491 | ] 2492 | 2493 | [[package]] 2494 | name = "tinyvec_macros" 2495 | version = "0.1.1" 2496 | source = "registry+https://github.com/rust-lang/crates.io-index" 2497 | checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" 2498 | 2499 | [[package]] 2500 | name = "tokio" 2501 | version = "1.48.0" 2502 | source = "registry+https://github.com/rust-lang/crates.io-index" 2503 | checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" 2504 | dependencies = [ 2505 | "bytes", 2506 | "libc", 2507 | "mio", 2508 | "pin-project-lite", 2509 | "socket2 0.6.1", 2510 | "tokio-macros", 2511 | "windows-sys 0.61.2", 2512 | ] 2513 | 2514 | [[package]] 2515 | name = "tokio-macros" 2516 | version = "2.6.0" 2517 | source = "registry+https://github.com/rust-lang/crates.io-index" 2518 | checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" 2519 | dependencies = [ 2520 | "proc-macro2", 2521 | "quote", 2522 | "syn", 2523 | ] 2524 | 2525 | [[package]] 2526 | name = "tokio-rustls" 2527 | version = "0.26.2" 2528 | source = "registry+https://github.com/rust-lang/crates.io-index" 2529 | checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" 2530 | dependencies = [ 2531 | "rustls", 2532 | "tokio", 2533 | ] 2534 | 2535 | [[package]] 2536 | name = "tokio-util" 2537 | version = "0.7.10" 2538 | source = "registry+https://github.com/rust-lang/crates.io-index" 2539 | checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" 2540 | dependencies = [ 2541 | "bytes", 2542 | "futures-core", 2543 | "futures-sink", 2544 | "pin-project-lite", 2545 | "tokio", 2546 | "tracing", 2547 | ] 2548 | 2549 | [[package]] 2550 | name = "toml_datetime" 2551 | version = "0.7.3" 2552 | source = "registry+https://github.com/rust-lang/crates.io-index" 2553 | checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533" 2554 | dependencies = [ 2555 | "serde_core", 2556 | ] 2557 | 2558 | [[package]] 2559 | name = "toml_edit" 2560 | version = "0.23.7" 2561 | source = "registry+https://github.com/rust-lang/crates.io-index" 2562 | checksum = "6485ef6d0d9b5d0ec17244ff7eb05310113c3f316f2d14200d4de56b3cb98f8d" 2563 | dependencies = [ 2564 | "indexmap", 2565 | "toml_datetime", 2566 | "toml_parser", 2567 | "winnow", 2568 | ] 2569 | 2570 | [[package]] 2571 | name = "toml_parser" 2572 | version = "1.0.4" 2573 | source = "registry+https://github.com/rust-lang/crates.io-index" 2574 | checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e" 2575 | dependencies = [ 2576 | "winnow", 2577 | ] 2578 | 2579 | [[package]] 2580 | name = "tower" 2581 | version = "0.4.13" 2582 | source = "registry+https://github.com/rust-lang/crates.io-index" 2583 | checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" 2584 | dependencies = [ 2585 | "futures-core", 2586 | "futures-util", 2587 | "pin-project", 2588 | "pin-project-lite", 2589 | "tokio", 2590 | "tower-layer", 2591 | "tower-service", 2592 | ] 2593 | 2594 | [[package]] 2595 | name = "tower-layer" 2596 | version = "0.3.3" 2597 | source = "registry+https://github.com/rust-lang/crates.io-index" 2598 | checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" 2599 | 2600 | [[package]] 2601 | name = "tower-service" 2602 | version = "0.3.2" 2603 | source = "registry+https://github.com/rust-lang/crates.io-index" 2604 | checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" 2605 | 2606 | [[package]] 2607 | name = "tracing" 2608 | version = "0.1.40" 2609 | source = "registry+https://github.com/rust-lang/crates.io-index" 2610 | checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" 2611 | dependencies = [ 2612 | "pin-project-lite", 2613 | "tracing-attributes", 2614 | "tracing-core", 2615 | ] 2616 | 2617 | [[package]] 2618 | name = "tracing-attributes" 2619 | version = "0.1.27" 2620 | source = "registry+https://github.com/rust-lang/crates.io-index" 2621 | checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" 2622 | dependencies = [ 2623 | "proc-macro2", 2624 | "quote", 2625 | "syn", 2626 | ] 2627 | 2628 | [[package]] 2629 | name = "tracing-core" 2630 | version = "0.1.32" 2631 | source = "registry+https://github.com/rust-lang/crates.io-index" 2632 | checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" 2633 | dependencies = [ 2634 | "once_cell", 2635 | ] 2636 | 2637 | [[package]] 2638 | name = "try-lock" 2639 | version = "0.2.5" 2640 | source = "registry+https://github.com/rust-lang/crates.io-index" 2641 | checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" 2642 | 2643 | [[package]] 2644 | name = "unicode-ident" 2645 | version = "1.0.20" 2646 | source = "registry+https://github.com/rust-lang/crates.io-index" 2647 | checksum = "462eeb75aeb73aea900253ce739c8e18a67423fadf006037cd3ff27e82748a06" 2648 | 2649 | [[package]] 2650 | name = "unindent" 2651 | version = "0.2.3" 2652 | source = "registry+https://github.com/rust-lang/crates.io-index" 2653 | checksum = "c7de7d73e1754487cb58364ee906a499937a0dfabd86bcb980fa99ec8c8fa2ce" 2654 | 2655 | [[package]] 2656 | name = "untrusted" 2657 | version = "0.9.0" 2658 | source = "registry+https://github.com/rust-lang/crates.io-index" 2659 | checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" 2660 | 2661 | [[package]] 2662 | name = "url" 2663 | version = "2.5.7" 2664 | source = "registry+https://github.com/rust-lang/crates.io-index" 2665 | checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" 2666 | dependencies = [ 2667 | "form_urlencoded", 2668 | "idna", 2669 | "percent-encoding", 2670 | "serde", 2671 | ] 2672 | 2673 | [[package]] 2674 | name = "utf8_iter" 2675 | version = "1.0.4" 2676 | source = "registry+https://github.com/rust-lang/crates.io-index" 2677 | checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" 2678 | 2679 | [[package]] 2680 | name = "vcpkg" 2681 | version = "0.2.15" 2682 | source = "registry+https://github.com/rust-lang/crates.io-index" 2683 | checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" 2684 | 2685 | [[package]] 2686 | name = "version_check" 2687 | version = "0.9.4" 2688 | source = "registry+https://github.com/rust-lang/crates.io-index" 2689 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 2690 | 2691 | [[package]] 2692 | name = "walkdir" 2693 | version = "2.5.0" 2694 | source = "registry+https://github.com/rust-lang/crates.io-index" 2695 | checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" 2696 | dependencies = [ 2697 | "same-file", 2698 | "winapi-util", 2699 | ] 2700 | 2701 | [[package]] 2702 | name = "want" 2703 | version = "0.3.1" 2704 | source = "registry+https://github.com/rust-lang/crates.io-index" 2705 | checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" 2706 | dependencies = [ 2707 | "try-lock", 2708 | ] 2709 | 2710 | [[package]] 2711 | name = "wasi" 2712 | version = "0.11.0+wasi-snapshot-preview1" 2713 | source = "registry+https://github.com/rust-lang/crates.io-index" 2714 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 2715 | 2716 | [[package]] 2717 | name = "wasi" 2718 | version = "0.14.2+wasi-0.2.4" 2719 | source = "registry+https://github.com/rust-lang/crates.io-index" 2720 | checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" 2721 | dependencies = [ 2722 | "wit-bindgen-rt", 2723 | ] 2724 | 2725 | [[package]] 2726 | name = "wasm-bindgen" 2727 | version = "0.2.91" 2728 | source = "registry+https://github.com/rust-lang/crates.io-index" 2729 | checksum = "c1e124130aee3fb58c5bdd6b639a0509486b0338acaaae0c84a5124b0f588b7f" 2730 | dependencies = [ 2731 | "cfg-if", 2732 | "wasm-bindgen-macro", 2733 | ] 2734 | 2735 | [[package]] 2736 | name = "wasm-bindgen-backend" 2737 | version = "0.2.91" 2738 | source = "registry+https://github.com/rust-lang/crates.io-index" 2739 | checksum = "c9e7e1900c352b609c8488ad12639a311045f40a35491fb69ba8c12f758af70b" 2740 | dependencies = [ 2741 | "bumpalo", 2742 | "log", 2743 | "once_cell", 2744 | "proc-macro2", 2745 | "quote", 2746 | "syn", 2747 | "wasm-bindgen-shared", 2748 | ] 2749 | 2750 | [[package]] 2751 | name = "wasm-bindgen-futures" 2752 | version = "0.4.41" 2753 | source = "registry+https://github.com/rust-lang/crates.io-index" 2754 | checksum = "877b9c3f61ceea0e56331985743b13f3d25c406a7098d45180fb5f09bc19ed97" 2755 | dependencies = [ 2756 | "cfg-if", 2757 | "js-sys", 2758 | "wasm-bindgen", 2759 | "web-sys", 2760 | ] 2761 | 2762 | [[package]] 2763 | name = "wasm-bindgen-macro" 2764 | version = "0.2.91" 2765 | source = "registry+https://github.com/rust-lang/crates.io-index" 2766 | checksum = "b30af9e2d358182b5c7449424f017eba305ed32a7010509ede96cdc4696c46ed" 2767 | dependencies = [ 2768 | "quote", 2769 | "wasm-bindgen-macro-support", 2770 | ] 2771 | 2772 | [[package]] 2773 | name = "wasm-bindgen-macro-support" 2774 | version = "0.2.91" 2775 | source = "registry+https://github.com/rust-lang/crates.io-index" 2776 | checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66" 2777 | dependencies = [ 2778 | "proc-macro2", 2779 | "quote", 2780 | "syn", 2781 | "wasm-bindgen-backend", 2782 | "wasm-bindgen-shared", 2783 | ] 2784 | 2785 | [[package]] 2786 | name = "wasm-bindgen-shared" 2787 | version = "0.2.91" 2788 | source = "registry+https://github.com/rust-lang/crates.io-index" 2789 | checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838" 2790 | 2791 | [[package]] 2792 | name = "wasm-streams" 2793 | version = "0.4.0" 2794 | source = "registry+https://github.com/rust-lang/crates.io-index" 2795 | checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129" 2796 | dependencies = [ 2797 | "futures-util", 2798 | "js-sys", 2799 | "wasm-bindgen", 2800 | "wasm-bindgen-futures", 2801 | "web-sys", 2802 | ] 2803 | 2804 | [[package]] 2805 | name = "web-sys" 2806 | version = "0.3.68" 2807 | source = "registry+https://github.com/rust-lang/crates.io-index" 2808 | checksum = "96565907687f7aceb35bc5fc03770a8a0471d82e479f25832f54a0e3f4b28446" 2809 | dependencies = [ 2810 | "js-sys", 2811 | "wasm-bindgen", 2812 | ] 2813 | 2814 | [[package]] 2815 | name = "web-time" 2816 | version = "1.1.0" 2817 | source = "registry+https://github.com/rust-lang/crates.io-index" 2818 | checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" 2819 | dependencies = [ 2820 | "js-sys", 2821 | "wasm-bindgen", 2822 | ] 2823 | 2824 | [[package]] 2825 | name = "weezl" 2826 | version = "0.1.10" 2827 | source = "registry+https://github.com/rust-lang/crates.io-index" 2828 | checksum = "a751b3277700db47d3e574514de2eced5e54dc8a5436a3bf7a0b248b2cee16f3" 2829 | 2830 | [[package]] 2831 | name = "winapi" 2832 | version = "0.3.9" 2833 | source = "registry+https://github.com/rust-lang/crates.io-index" 2834 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 2835 | dependencies = [ 2836 | "winapi-i686-pc-windows-gnu", 2837 | "winapi-x86_64-pc-windows-gnu", 2838 | ] 2839 | 2840 | [[package]] 2841 | name = "winapi-i686-pc-windows-gnu" 2842 | version = "0.4.0" 2843 | source = "registry+https://github.com/rust-lang/crates.io-index" 2844 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 2845 | 2846 | [[package]] 2847 | name = "winapi-util" 2848 | version = "0.1.6" 2849 | source = "registry+https://github.com/rust-lang/crates.io-index" 2850 | checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" 2851 | dependencies = [ 2852 | "winapi", 2853 | ] 2854 | 2855 | [[package]] 2856 | name = "winapi-x86_64-pc-windows-gnu" 2857 | version = "0.4.0" 2858 | source = "registry+https://github.com/rust-lang/crates.io-index" 2859 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 2860 | 2861 | [[package]] 2862 | name = "windows-core" 2863 | version = "0.52.0" 2864 | source = "registry+https://github.com/rust-lang/crates.io-index" 2865 | checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" 2866 | dependencies = [ 2867 | "windows-targets 0.52.3", 2868 | ] 2869 | 2870 | [[package]] 2871 | name = "windows-link" 2872 | version = "0.2.1" 2873 | source = "registry+https://github.com/rust-lang/crates.io-index" 2874 | checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" 2875 | 2876 | [[package]] 2877 | name = "windows-sys" 2878 | version = "0.48.0" 2879 | source = "registry+https://github.com/rust-lang/crates.io-index" 2880 | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" 2881 | dependencies = [ 2882 | "windows-targets 0.48.5", 2883 | ] 2884 | 2885 | [[package]] 2886 | name = "windows-sys" 2887 | version = "0.52.0" 2888 | source = "registry+https://github.com/rust-lang/crates.io-index" 2889 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 2890 | dependencies = [ 2891 | "windows-targets 0.52.3", 2892 | ] 2893 | 2894 | [[package]] 2895 | name = "windows-sys" 2896 | version = "0.60.2" 2897 | source = "registry+https://github.com/rust-lang/crates.io-index" 2898 | checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" 2899 | dependencies = [ 2900 | "windows-targets 0.53.5", 2901 | ] 2902 | 2903 | [[package]] 2904 | name = "windows-sys" 2905 | version = "0.61.2" 2906 | source = "registry+https://github.com/rust-lang/crates.io-index" 2907 | checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" 2908 | dependencies = [ 2909 | "windows-link", 2910 | ] 2911 | 2912 | [[package]] 2913 | name = "windows-targets" 2914 | version = "0.48.5" 2915 | source = "registry+https://github.com/rust-lang/crates.io-index" 2916 | checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" 2917 | dependencies = [ 2918 | "windows_aarch64_gnullvm 0.48.5", 2919 | "windows_aarch64_msvc 0.48.5", 2920 | "windows_i686_gnu 0.48.5", 2921 | "windows_i686_msvc 0.48.5", 2922 | "windows_x86_64_gnu 0.48.5", 2923 | "windows_x86_64_gnullvm 0.48.5", 2924 | "windows_x86_64_msvc 0.48.5", 2925 | ] 2926 | 2927 | [[package]] 2928 | name = "windows-targets" 2929 | version = "0.52.3" 2930 | source = "registry+https://github.com/rust-lang/crates.io-index" 2931 | checksum = "d380ba1dc7187569a8a9e91ed34b8ccfc33123bbacb8c0aed2d1ad7f3ef2dc5f" 2932 | dependencies = [ 2933 | "windows_aarch64_gnullvm 0.52.3", 2934 | "windows_aarch64_msvc 0.52.3", 2935 | "windows_i686_gnu 0.52.3", 2936 | "windows_i686_msvc 0.52.3", 2937 | "windows_x86_64_gnu 0.52.3", 2938 | "windows_x86_64_gnullvm 0.52.3", 2939 | "windows_x86_64_msvc 0.52.3", 2940 | ] 2941 | 2942 | [[package]] 2943 | name = "windows-targets" 2944 | version = "0.53.5" 2945 | source = "registry+https://github.com/rust-lang/crates.io-index" 2946 | checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" 2947 | dependencies = [ 2948 | "windows-link", 2949 | "windows_aarch64_gnullvm 0.53.1", 2950 | "windows_aarch64_msvc 0.53.1", 2951 | "windows_i686_gnu 0.53.1", 2952 | "windows_i686_gnullvm", 2953 | "windows_i686_msvc 0.53.1", 2954 | "windows_x86_64_gnu 0.53.1", 2955 | "windows_x86_64_gnullvm 0.53.1", 2956 | "windows_x86_64_msvc 0.53.1", 2957 | ] 2958 | 2959 | [[package]] 2960 | name = "windows_aarch64_gnullvm" 2961 | version = "0.48.5" 2962 | source = "registry+https://github.com/rust-lang/crates.io-index" 2963 | checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" 2964 | 2965 | [[package]] 2966 | name = "windows_aarch64_gnullvm" 2967 | version = "0.52.3" 2968 | source = "registry+https://github.com/rust-lang/crates.io-index" 2969 | checksum = "68e5dcfb9413f53afd9c8f86e56a7b4d86d9a2fa26090ea2dc9e40fba56c6ec6" 2970 | 2971 | [[package]] 2972 | name = "windows_aarch64_gnullvm" 2973 | version = "0.53.1" 2974 | source = "registry+https://github.com/rust-lang/crates.io-index" 2975 | checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" 2976 | 2977 | [[package]] 2978 | name = "windows_aarch64_msvc" 2979 | version = "0.48.5" 2980 | source = "registry+https://github.com/rust-lang/crates.io-index" 2981 | checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" 2982 | 2983 | [[package]] 2984 | name = "windows_aarch64_msvc" 2985 | version = "0.52.3" 2986 | source = "registry+https://github.com/rust-lang/crates.io-index" 2987 | checksum = "8dab469ebbc45798319e69eebf92308e541ce46760b49b18c6b3fe5e8965b30f" 2988 | 2989 | [[package]] 2990 | name = "windows_aarch64_msvc" 2991 | version = "0.53.1" 2992 | source = "registry+https://github.com/rust-lang/crates.io-index" 2993 | checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" 2994 | 2995 | [[package]] 2996 | name = "windows_i686_gnu" 2997 | version = "0.48.5" 2998 | source = "registry+https://github.com/rust-lang/crates.io-index" 2999 | checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" 3000 | 3001 | [[package]] 3002 | name = "windows_i686_gnu" 3003 | version = "0.52.3" 3004 | source = "registry+https://github.com/rust-lang/crates.io-index" 3005 | checksum = "2a4e9b6a7cac734a8b4138a4e1044eac3404d8326b6c0f939276560687a033fb" 3006 | 3007 | [[package]] 3008 | name = "windows_i686_gnu" 3009 | version = "0.53.1" 3010 | source = "registry+https://github.com/rust-lang/crates.io-index" 3011 | checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" 3012 | 3013 | [[package]] 3014 | name = "windows_i686_gnullvm" 3015 | version = "0.53.1" 3016 | source = "registry+https://github.com/rust-lang/crates.io-index" 3017 | checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" 3018 | 3019 | [[package]] 3020 | name = "windows_i686_msvc" 3021 | version = "0.48.5" 3022 | source = "registry+https://github.com/rust-lang/crates.io-index" 3023 | checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" 3024 | 3025 | [[package]] 3026 | name = "windows_i686_msvc" 3027 | version = "0.52.3" 3028 | source = "registry+https://github.com/rust-lang/crates.io-index" 3029 | checksum = "28b0ec9c422ca95ff34a78755cfa6ad4a51371da2a5ace67500cf7ca5f232c58" 3030 | 3031 | [[package]] 3032 | name = "windows_i686_msvc" 3033 | version = "0.53.1" 3034 | source = "registry+https://github.com/rust-lang/crates.io-index" 3035 | checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" 3036 | 3037 | [[package]] 3038 | name = "windows_x86_64_gnu" 3039 | version = "0.48.5" 3040 | source = "registry+https://github.com/rust-lang/crates.io-index" 3041 | checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" 3042 | 3043 | [[package]] 3044 | name = "windows_x86_64_gnu" 3045 | version = "0.52.3" 3046 | source = "registry+https://github.com/rust-lang/crates.io-index" 3047 | checksum = "704131571ba93e89d7cd43482277d6632589b18ecf4468f591fbae0a8b101614" 3048 | 3049 | [[package]] 3050 | name = "windows_x86_64_gnu" 3051 | version = "0.53.1" 3052 | source = "registry+https://github.com/rust-lang/crates.io-index" 3053 | checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" 3054 | 3055 | [[package]] 3056 | name = "windows_x86_64_gnullvm" 3057 | version = "0.48.5" 3058 | source = "registry+https://github.com/rust-lang/crates.io-index" 3059 | checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" 3060 | 3061 | [[package]] 3062 | name = "windows_x86_64_gnullvm" 3063 | version = "0.52.3" 3064 | source = "registry+https://github.com/rust-lang/crates.io-index" 3065 | checksum = "42079295511643151e98d61c38c0acc444e52dd42ab456f7ccfd5152e8ecf21c" 3066 | 3067 | [[package]] 3068 | name = "windows_x86_64_gnullvm" 3069 | version = "0.53.1" 3070 | source = "registry+https://github.com/rust-lang/crates.io-index" 3071 | checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" 3072 | 3073 | [[package]] 3074 | name = "windows_x86_64_msvc" 3075 | version = "0.48.5" 3076 | source = "registry+https://github.com/rust-lang/crates.io-index" 3077 | checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" 3078 | 3079 | [[package]] 3080 | name = "windows_x86_64_msvc" 3081 | version = "0.52.3" 3082 | source = "registry+https://github.com/rust-lang/crates.io-index" 3083 | checksum = "0770833d60a970638e989b3fa9fd2bb1aaadcf88963d1659fd7d9990196ed2d6" 3084 | 3085 | [[package]] 3086 | name = "windows_x86_64_msvc" 3087 | version = "0.53.1" 3088 | source = "registry+https://github.com/rust-lang/crates.io-index" 3089 | checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" 3090 | 3091 | [[package]] 3092 | name = "winnow" 3093 | version = "0.7.13" 3094 | source = "registry+https://github.com/rust-lang/crates.io-index" 3095 | checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" 3096 | dependencies = [ 3097 | "memchr", 3098 | ] 3099 | 3100 | [[package]] 3101 | name = "winreg" 3102 | version = "0.52.0" 3103 | source = "registry+https://github.com/rust-lang/crates.io-index" 3104 | checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5" 3105 | dependencies = [ 3106 | "cfg-if", 3107 | "windows-sys 0.48.0", 3108 | ] 3109 | 3110 | [[package]] 3111 | name = "wit-bindgen-rt" 3112 | version = "0.39.0" 3113 | source = "registry+https://github.com/rust-lang/crates.io-index" 3114 | checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" 3115 | dependencies = [ 3116 | "bitflags 2.10.0", 3117 | ] 3118 | 3119 | [[package]] 3120 | name = "writeable" 3121 | version = "0.6.1" 3122 | source = "registry+https://github.com/rust-lang/crates.io-index" 3123 | checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" 3124 | 3125 | [[package]] 3126 | name = "xattr" 3127 | version = "1.5.0" 3128 | source = "registry+https://github.com/rust-lang/crates.io-index" 3129 | checksum = "0d65cbf2f12c15564212d48f4e3dfb87923d25d611f2aed18f4cb23f0413d89e" 3130 | dependencies = [ 3131 | "libc", 3132 | "rustix 1.0.7", 3133 | ] 3134 | 3135 | [[package]] 3136 | name = "yoke" 3137 | version = "0.8.0" 3138 | source = "registry+https://github.com/rust-lang/crates.io-index" 3139 | checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" 3140 | dependencies = [ 3141 | "serde", 3142 | "stable_deref_trait", 3143 | "yoke-derive", 3144 | "zerofrom", 3145 | ] 3146 | 3147 | [[package]] 3148 | name = "yoke-derive" 3149 | version = "0.8.0" 3150 | source = "registry+https://github.com/rust-lang/crates.io-index" 3151 | checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" 3152 | dependencies = [ 3153 | "proc-macro2", 3154 | "quote", 3155 | "syn", 3156 | "synstructure", 3157 | ] 3158 | 3159 | [[package]] 3160 | name = "zerocopy" 3161 | version = "0.7.32" 3162 | source = "registry+https://github.com/rust-lang/crates.io-index" 3163 | checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" 3164 | dependencies = [ 3165 | "zerocopy-derive", 3166 | ] 3167 | 3168 | [[package]] 3169 | name = "zerocopy-derive" 3170 | version = "0.7.32" 3171 | source = "registry+https://github.com/rust-lang/crates.io-index" 3172 | checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" 3173 | dependencies = [ 3174 | "proc-macro2", 3175 | "quote", 3176 | "syn", 3177 | ] 3178 | 3179 | [[package]] 3180 | name = "zerofrom" 3181 | version = "0.1.6" 3182 | source = "registry+https://github.com/rust-lang/crates.io-index" 3183 | checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" 3184 | dependencies = [ 3185 | "zerofrom-derive", 3186 | ] 3187 | 3188 | [[package]] 3189 | name = "zerofrom-derive" 3190 | version = "0.1.6" 3191 | source = "registry+https://github.com/rust-lang/crates.io-index" 3192 | checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" 3193 | dependencies = [ 3194 | "proc-macro2", 3195 | "quote", 3196 | "syn", 3197 | "synstructure", 3198 | ] 3199 | 3200 | [[package]] 3201 | name = "zeroize" 3202 | version = "1.8.1" 3203 | source = "registry+https://github.com/rust-lang/crates.io-index" 3204 | checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" 3205 | 3206 | [[package]] 3207 | name = "zerotrie" 3208 | version = "0.2.2" 3209 | source = "registry+https://github.com/rust-lang/crates.io-index" 3210 | checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" 3211 | dependencies = [ 3212 | "displaydoc", 3213 | "yoke", 3214 | "zerofrom", 3215 | ] 3216 | 3217 | [[package]] 3218 | name = "zerovec" 3219 | version = "0.11.4" 3220 | source = "registry+https://github.com/rust-lang/crates.io-index" 3221 | checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" 3222 | dependencies = [ 3223 | "yoke", 3224 | "zerofrom", 3225 | "zerovec-derive", 3226 | ] 3227 | 3228 | [[package]] 3229 | name = "zerovec-derive" 3230 | version = "0.11.1" 3231 | source = "registry+https://github.com/rust-lang/crates.io-index" 3232 | checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" 3233 | dependencies = [ 3234 | "proc-macro2", 3235 | "quote", 3236 | "syn", 3237 | ] 3238 | 3239 | [[package]] 3240 | name = "zstd" 3241 | version = "0.13.3" 3242 | source = "registry+https://github.com/rust-lang/crates.io-index" 3243 | checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" 3244 | dependencies = [ 3245 | "zstd-safe", 3246 | ] 3247 | 3248 | [[package]] 3249 | name = "zstd-safe" 3250 | version = "7.2.4" 3251 | source = "registry+https://github.com/rust-lang/crates.io-index" 3252 | checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d" 3253 | dependencies = [ 3254 | "zstd-sys", 3255 | ] 3256 | 3257 | [[package]] 3258 | name = "zstd-sys" 3259 | version = "2.0.15+zstd.1.5.7" 3260 | source = "registry+https://github.com/rust-lang/crates.io-index" 3261 | checksum = "eb81183ddd97d0c74cedf1d50d85c8d08c1b8b68ee863bdee9e706eedba1a237" 3262 | dependencies = [ 3263 | "cc", 3264 | "pkg-config", 3265 | ] 3266 | 3267 | [[package]] 3268 | name = "zune-core" 3269 | version = "0.4.12" 3270 | source = "registry+https://github.com/rust-lang/crates.io-index" 3271 | checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" 3272 | 3273 | [[package]] 3274 | name = "zune-jpeg" 3275 | version = "0.4.18" 3276 | source = "registry+https://github.com/rust-lang/crates.io-index" 3277 | checksum = "7384255a918371b5af158218d131530f694de9ad3815ebdd0453a940485cb0fa" 3278 | dependencies = [ 3279 | "zune-core", 3280 | ] 3281 | --------------------------------------------------------------------------------