diff --git a/.github/workflows/check_black.yml b/.github/workflows/check_black.yml deleted file mode 100644 index 9c6949e1b..000000000 --- a/.github/workflows/check_black.yml +++ /dev/null @@ -1,39 +0,0 @@ -# This workflow will test the code base using the LATEST version of black - -# IMPORTANT: Black is under development. Hence, minor fommatting changes between -# different version are expected. -# If this test fails, install the latest version of black and then run black. -# Preferably, run black only on the files that you have modified. -# This will faciliate the revision of the proposed changes. - -name: Check Black - -on: - # Triggers the workflow on push or pull request events but only for the master branch - push: - branches: [ master ] - pull_request: - branches: [ master ] - -jobs: - build: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v4 - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install black - - - name: Black version - run: black --version - - - name: Black check - working-directory: ${{github.workspace}} - run: black --check . diff --git a/.github/workflows/test_pysteps.yml b/.github/workflows/test_pysteps.yml index 29611ef99..047c36d8c 100644 --- a/.github/workflows/test_pysteps.yml +++ b/.github/workflows/test_pysteps.yml @@ -4,17 +4,38 @@ on: # Triggers the workflow on push or pull request events to the master branch push: branches: - - v1.x - main pull_request: - branches: - - v1.x - - main + branches: + - main jobs: + + formatting_checks: + name: Check formatting + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v7 + - name: Install uv + uses: astral-sh/setup-uv@v10.3.3 + - name: Sync uv + working-directory: ${{github.workspace}} + run: uv sync --only-dev --frozen + - name: Run bandit + working-directory: ${{github.workspace}} + run: uv run bandit -c pyproject.toml -r . + - name: Run import sorter + working-directory: ${{github.workspace}} + run: uv run --no-sync ruff check + - name: Run ruff + working-directory: ${{github.workspace}} + run: uv run --no-sync ruff format --check + unit_tests: name: Unit Tests (${{ matrix.python-version }}, ${{ matrix.os }}) runs-on: ${{ matrix.os }} + needs: formatting_checks strategy: fail-fast: false @@ -29,39 +50,18 @@ jobs: steps: - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.python-version }} - # need headless opencv on Linux, see https://github.com/conda-forge/opencv-feedstock/issues/401 - - name: Install mamba and create environment for Linux - if: matrix.os == 'ubuntu-latest' - uses: mamba-org/setup-micromamba@v1 - with: - # https://github.com/mamba-org/setup-micromamba/issues/225 - micromamba-version: 1.5.10-0 - environment-file: ci/ci_test_env.yml - environment-name: test_environment - generate-run-shell: false - create-args: >- - python=${{ matrix.python-version }} - libopencv=*=headless* - - - name: Install mamba and create environment (not Linux) - if: matrix.os != 'ubuntu-latest' - uses: mamba-org/setup-micromamba@v1 - with: - # https://github.com/mamba-org/setup-micromamba/issues/225 - micromamba-version: 1.5.10-0 - environment-file: ci/ci_test_env.yml - environment-name: test_environment - generate-run-shell: false - create-args: python=${{ matrix.python-version }} + - name: Install uv + uses: astral-sh/setup-uv@v10.3.3 - - name: Install pygrib - run: mamba install --quiet pygrib + - name: Install dependencies (Linux) + if: matrix.os == 'ubuntu-latest' + working-directory: ${{github.workspace}} + run: | + sudo apt update + sudo apt install libgdal-dev libproj-dev libhdf5-dev libopencv-dev libeccodes-dev - - name: Install pysteps for MacOS + - name: Install pysteps (MacOS) if: matrix.os == 'macos-latest' working-directory: ${{github.workspace}} env: @@ -73,36 +73,51 @@ jobs: brew update-reset brew update gcc-13 --version || brew install gcc@13 - pip install . + brew install gdal + uv python install ${{ matrix.python-version }} + uv sync --python ${{ matrix.python-version }} --frozen + bash add_gdal_brew.sh + uv sync --python ${{ matrix.python-version }} --all-extras --frozen - - name: Install pysteps - if: matrix.os != 'macos-latest' + - name: Install pysteps (Linux) + if: matrix.os == 'ubuntu-latest' working-directory: ${{github.workspace}} - run: pip install . + run: | + uv python install ${{ matrix.python-version }} + uv sync --python ${{ matrix.python-version }} --frozen + bash add_gdal.sh + uv sync --python ${{ matrix.python-version }} --all-extras --frozen + + - name: Install pysteps (Windows) + if: matrix.os == 'windows-latest' + working-directory: ${{github.workspace}} + run: | + uv python install ${{ matrix.python-version }} + uv sync --python ${{ matrix.python-version }} --frozen + uv sync --python ${{ matrix.python-version }} --extra optional --extra gdal --frozen - name: Download pysteps data env: PYSTEPS_DATA_PATH: ${{github.workspace}}/pysteps_data working-directory: ${{github.workspace}}/ci - run: python fetch_pysteps_data.py + run: uv run fetch_pysteps_data.py - name: Check imports working-directory: ${{github.workspace}}/pysteps_data run: | - python --version - python -c "import pysteps; print(pysteps.__file__)" - python -c "from pysteps import motion" - python -c "from pysteps.motion import vet" - python -c "from pysteps.motion import proesmans" + uv run python --version + uv run python -c "import pysteps; print(pysteps.__file__)" + uv run python -c "from pysteps import motion" + uv run python -c "from pysteps.motion import vet" + uv run python -c "from pysteps.motion import proesmans" - name: Run tests and coverage report working-directory: ${{github.workspace}}/pysteps_data env: PYSTEPSRC: ${{github.workspace}}/pysteps_data/pystepsrc - PYSTEPS_REPO_ROOT: ${{github.workspace}} - run: pytest --pyargs pysteps --cov=pysteps --cov-report=xml --cov-report=term -ra + run: uv run pytest --pyargs pysteps --cov=pysteps --cov-report=xml --cov-report=term -ra - - name: Upload coverage to Codecov (Linux only) + - name: Upload coverage to Codecov (Linux) if: matrix.os == 'ubuntu-latest' uses: codecov/codecov-action@v4 env: diff --git a/.gitignore b/.gitignore index c2006db35..79c3e7a4b 100644 --- a/.gitignore +++ b/.gitignore @@ -63,7 +63,6 @@ doc/_build/ # pyenv .python-version - # Environments .env .venv @@ -95,3 +94,11 @@ venv.bak/ # Running local tests /tmp /pysteps/tests/tmp/ +/aux +/mrms +/nwp +/radar +/saf +pystepsrc +pystepsrc.1 +filename \ No newline at end of file diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7050fa3c9..702b4990b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,13 +1,28 @@ repos: -- repo: https://github.com/psf/black - rev: 26.1.0 + - repo: local hooks: - - id: black - language_version: python3 - -- repo: https://github.com/PyCQA/bandit - rev: 1.9.4 + - id: run-bandit + name: run-bandit + entry: bandit -c pyproject.toml -r . + language: system + verbose: true + pass_filenames: false + stages: [ pre-commit ] + - repo: local hooks: - - id: bandit - args: ["-c", "bandit.yaml"] - additional_dependencies: ["bandit[yaml]"] + - id: sort-imports + name: sort-imports + entry: uv run ruff check --fix + language: system + verbose: true + pass_filenames: false + stages: [ pre-commit ] + - repo: local + hooks: + - id: run-ruff + name: run-ruff + entry: uv run ruff format + language: system + verbose: true + pass_filenames: false + stages: [ pre-commit ] diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 473297b14..51213e2c8 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -16,7 +16,7 @@ Workflow for code contributions ------------------------------- We welcome all kinds of contributions, like documentation updates, bug fixes, or new features. -The workflow for the contibutions uses the usual +The workflow for the contributions uses the usual `GitHub pull-request flow `_. If you have ideas for new contributions to the project, feel free to get in touch with the pysteps community on our @@ -50,33 +50,20 @@ In a nutshell, the main steps to follow for contributing to pysteps are: Setting up the Development environment -------------------------------------- -The recommended way to setup up the developer environment is the Anaconda -(commonly referred to as Conda). -Conda quickly installs, runs, and updates packages and their dependencies. +The recommended way to set up the developer environment is with :code:`uv`. +uv quickly installs, runs, and updates packages and their dependencies. It also allows you to create, save, load, and switch between different environments on your local computer. Before continuing, Mac OSX users also need to install a more recent compiler. See instructions `here `__. -The developer environment can be created from the file -`environment_dev.yml `_ -in the project's root directory by running the command:: +The developer environment can be created by syncing the optional and dev dependencies:: - conda env create -f environment_dev.yml - -This will create the **pysteps_dev** environment that can be activated using:: - - conda activate pysteps_dev - -Once the environment is activated, the latest version of pysteps can be installed -in development mode, in such a way that the project appears to be installed, -but yet is still editable from the source tree:: - - pip install -e + uv sync --extra optional To test if the installation went fine, you can try importing pysteps from the python interpreter by running:: - python -c "import pysteps" + uv run python -c "import pysteps" Fork the repository @@ -108,17 +95,15 @@ root:: The pre-commit hooks are scripts executed automatically in every commit to identify simple issues with the code. When an issue is identified (the pre-commit script exits with non-zero status), the hook aborts the commit and prints the error. -Currently, pysteps only tests that the code to be committed complies with black's format style. -In case that the commit is aborted, you only need to run black in the entire source code. -This can be done by running :code:`black .` or :code:`pre-commit run --all-files`. -The latter is recommended since it indicates if the commit contained any formatting errors (that are automatically corrected). -Black's configuration is stored in the `pyproject.toml` file to ensure that the same configuration is used in every development environment. -This configuration is automatically loaded when black is run from any directory in the -pysteps project. +Currently, PySTEPS only tests that the code to be committed complies with :code:`bandit`, :code:`ruff format` and :code:`ruff`'s import sorter. +In case that the commit is aborted, can run these to try to find the issues:: -IMPORTANT: Periodically update the black version used in the pre-commit hook by running:: + uv run bandit -c bandit.yaml . + uv run ruff check + uv run ruff format --check - pre-commit autoupdate +This can also be done by running :code:`pre-commit run --all-files`. The latter is recommended since it indicates if the +commit contained any formatting errors (that are automatically corrected). For more information about git hooks and the pre-commit package, see: @@ -146,12 +131,12 @@ We strongly suggest following the Since PEP8 is a set of recommendations, these are the most important good coding practices for the pysteps project: * Always use four spaces for indentation (don’t use tabs). -* Max line-length: 88 characters (note that we don't use the PEP8's 79 value). Enforced by `black`. -* Always indent wrapped code for readability. Enforced by `black`. -* Avoid extraneous whitespace. Enforced by `black`. -* Don’t use whitespace to line up assignment operators (=, :). Enforced by `black`. +* Max line-length: 88 characters (note that we don't use the PEP8's 79 value). Enforced by `ruff`. +* Always indent wrapped code for readability. Enforced by `ruff`. +* Avoid extraneous whitespace. Enforced by `ruff`. +* Don’t use whitespace to line up assignment operators (=, :). Enforced by `ruff`. * Avoid writing multiple statements in the same line. -* Naming conventions should follow the recomendations from +* Naming conventions should follow the recommendations from the `Google's python style guide `__, summarized as follows: .. raw:: html @@ -273,26 +258,6 @@ For more suggestions on good coding practices for python, check these guidelines - `Google's python style guide `__ - `PEP8 `__ - -**Using Black auto-formatter** - -To ensure a minimal style consistency, we use -`black `__ to auto-format to the source code. -The black configuration used in the pysteps project is defined in the pyproject.toml, -and it is automatically detected by black. - -Black can be installed using any of the following:: - - conda install black - - #For the latest version: - conda install -c conda-forge black - - pip install black - -Check the `official documentation `__ -for more information. - **Docstrings** Every module, function, or class must have a docstring that describe its @@ -450,9 +415,6 @@ Core developers should follow the steps to prepare a new release (version): the `Python Package Index `_ (PyPI). See `Packaging the pysteps project `__ for a detailed description of this process. -#. Update the conda-forge pysteps-feedstock following this guidelines: - `Updating the conda-forge pysteps-feedstock `__ - Credits ------- diff --git a/README.rst b/README.rst index 40a9033e3..b33c1bff0 100644 --- a/README.rst +++ b/README.rst @@ -10,7 +10,7 @@ pysteps - Python framework for short-term ensemble prediction systems * - docs - |stable| |colab| |gallery| * - status - - |test| |docs| |codecov| |codacy| |black| + - |test| |docs| |codecov| |codacy| |formatting| * - package - |github| |conda| |pypi| |zenodo| * - community @@ -25,9 +25,9 @@ pysteps - Python framework for short-term ensemble prediction systems :alt: Test pysteps :target: https://github.com/pySTEPS/pysteps/actions?query=workflow%3A"Test+Pysteps" -.. |black| image:: https://github.com/pySTEPS/pysteps/workflows/Check%20Black/badge.svg - :alt: Check Black - :target: https://github.com/pySTEPS/pysteps/actions?query=workflow%3A"Check+Black" +.. |formatting| image:: https://github.com/pySTEPS/pysteps/workflows/Check%20formatting/badge.svg + :alt: Check formatting + :target: https://github.com/pySTEPS/pysteps/actions?query=workflow%3A"Check+formatting" .. |codecov| image:: https://codecov.io/gh/pySTEPS/pysteps/branch/master/graph/badge.svg :alt: Coverage @@ -97,11 +97,9 @@ Use pysteps to compute and plot a radar extrapolation nowcast in Google Colab wi Installation ============ -The recommended way to install pysteps is with `conda `_ from the conda-forge channel:: +PySTEPS can be installed using `uv `_:: - $ conda install -c conda-forge pysteps - -More details can be found in the `installation guide `_. + uv sync Usage ===== diff --git a/add_gdal.sh b/add_gdal.sh new file mode 100755 index 000000000..c44524e69 --- /dev/null +++ b/add_gdal.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash + +# exit on errors, and make sure to show it all +set -e -o pipefail + +function install_correct_gdal_version() { + IFS=" " + version_string=$(dpkg -s libgdal-dev | grep -i version) + read -ra version_array <<< "$version_string" + IFS="+" + read -ra version_value <<< "${version_array[1]}" + version="${version_value[0]}" + echo "Adding GDAL version ${version} to uv." + uv add --optional gdal gdal[numpy]=="$version" --frozen +} + +install_correct_gdal_version + diff --git a/add_gdal_brew.sh b/add_gdal_brew.sh new file mode 100755 index 000000000..2308b34c9 --- /dev/null +++ b/add_gdal_brew.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +# exit on errors, and make sure to show it all +set -e -o pipefail + +function install_correct_gdal_version() { + version="$(brew info --json gdal | jq -r '(.[] | (.installed[] | .version))')" + IFS="_" + read -ra version_value <<< "${version[0]}" + version="${version_value[0]}" + echo "Adding GDAL version ${version} to uv." + uv add --optional gdal gdal[numpy]=="$version" --frozen +} + +install_correct_gdal_version diff --git a/bandit.yaml b/bandit.yaml deleted file mode 100644 index cc15b0f9b..000000000 --- a/bandit.yaml +++ /dev/null @@ -1,3 +0,0 @@ -# skip check for assert usage in the tests -assert_used: - skips: ['*test_*.py'] diff --git a/ci/ci_test_env.yml b/ci/ci_test_env.yml deleted file mode 100644 index 519ea0ae4..000000000 --- a/ci/ci_test_env.yml +++ /dev/null @@ -1,37 +0,0 @@ -# pysteps development environment -name: test_environment -channels: - - conda-forge - - defaults -dependencies: - - python>=3.11 - - pip - - mamba - # Minimal dependencies - - numpy - - cython - - jsmin - - jsonschema - - matplotlib - - netCDF4 - - opencv - - pillow - - pyproj - - scipy - - xarray - # Optional dependencies - - dask - - pyfftw - - cartopy - - h5py - - PyWavelets - - pandas - - scikit-image - - scikit-learn - - rasterio - - gdal - # Test dependencies - - pytest - - pytest-cov - - pip: - - cookiecutter diff --git a/doc/source/conf.py b/doc/source/conf.py index fa8eed2d7..3396ec749 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -3,12 +3,12 @@ # All configuration values have a default; values that are commented out # serve to show the default. +import json import os import subprocess import sys from datetime import datetime -import json from jsmin import jsmin # If extensions (or modules to document with autodoc) are in another directory, diff --git a/environment.yml b/environment.yml deleted file mode 100644 index d4390308a..000000000 --- a/environment.yml +++ /dev/null @@ -1,16 +0,0 @@ -name: pysteps -channels: -- conda-forge -- defaults -dependencies: - - python>=3.10 - - jsmin - - jsonschema - - matplotlib - - netCDF4 - - numpy - - opencv - - pillow - - pyproj - - scipy - - xarray diff --git a/environment_dev.yml b/environment_dev.yml deleted file mode 100644 index 3bc473c09..000000000 --- a/environment_dev.yml +++ /dev/null @@ -1,36 +0,0 @@ -# pysteps development environment -name: pysteps_dev -channels: - - conda-forge - - defaults -dependencies: - - python>=3.10 - - pip - - jsmin - - jsonschema - - matplotlib - - netCDF4 - - numpy - - opencv - - pillow - - pyproj - - scipy - - pytest - - pywavelets - - cython - - dask - - pyfftw - - h5py - - pygrib - - black - - pytest-cov - - codecov - - pre_commit - - cartopy>=0.18 - - scikit-image - - scikit-learn - - pandas - - rasterio - - xarray - - geotiff - - cookiecutter diff --git a/examples/advection_correction.py b/examples/advection_correction.py index 75ba3d692..fe49b1785 100644 --- a/examples/advection_correction.py +++ b/examples/advection_correction.py @@ -17,13 +17,14 @@ """ from datetime import datetime + import matplotlib.pyplot as plt import numpy as np +from scipy.ndimage import map_coordinates from pysteps import io, motion, rcparams from pysteps.utils import conversion, dimension from pysteps.visualization import plot_precip_field -from scipy.ndimage import map_coordinates ################################################################################ # Read the radar input images diff --git a/examples/anvil_nowcast.py b/examples/anvil_nowcast.py index 573612ed7..3af7cf01c 100644 --- a/examples/anvil_nowcast.py +++ b/examples/anvil_nowcast.py @@ -10,13 +10,14 @@ Load the libraries. """ -from datetime import datetime, timedelta import warnings +from datetime import datetime, timedelta warnings.simplefilter("ignore") import matplotlib.pyplot as plt import numpy as np -from pysteps import motion, io, rcparams, utils + +from pysteps import io, motion, rcparams, utils from pysteps.nowcasts import anvil, extrapolation, sprog from pysteps.utils import transformation from pysteps.visualization import plot_precip_field diff --git a/examples/data_transformations.py b/examples/data_transformations.py index 3b846c743..e6d411efc 100644 --- a/examples/data_transformations.py +++ b/examples/data_transformations.py @@ -1,228 +1,441 @@ -# -*- coding: utf-8 -*- -""" -Data transformations -==================== - -The statistics of intermittent precipitation rates are particularly non-Gaussian -and display an asymmetric distribution bounded at zero. -Such properties restrict the usage of well-established statistical methods that -assume symmetric or Gaussian data. - -A common workaround is to introduce a suitable data transformation to approximate -a normal distribution. - -In this example, we test the data transformation methods available in pysteps -in order to obtain a more symmetric distribution of the precipitation data -(excluding the zeros). -The currently available transformations include the Box-Cox, dB, square-root and -normal quantile transforms. - -""" - -from datetime import datetime - -import matplotlib.pyplot as plt -import numpy as np -from scipy.stats import skew - -from pysteps import io, rcparams -from pysteps.utils import conversion, transformation - -############################################################################### -# Read the radar input images -# --------------------------- -# -# First, we will import the sequence of radar composites. -# You need the pysteps-data archive downloaded and the pystepsrc file -# configured with the data_source paths pointing to data folders. - -# Selected case -date = datetime.strptime("201609281600", "%Y%m%d%H%M") -data_source = rcparams.data_sources["fmi"] - - -############################################################################### -# Load the data from the archive -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -root_path = data_source["root_path"] -path_fmt = data_source["path_fmt"] -fn_pattern = data_source["fn_pattern"] -fn_ext = data_source["fn_ext"] -importer_name = data_source["importer"] -importer_kwargs = data_source["importer_kwargs"] -timestep = data_source["timestep"] - -# Get 1 hour of observations in the data archive -fns = io.archive.find_by_date( - date, root_path, path_fmt, fn_pattern, fn_ext, timestep, num_next_files=11 -) - -# Read the radar composites -importer = io.get_method(importer_name, "importer") -precip_dataset = io.read_timeseries(fns, importer, **importer_kwargs) - -# Convert to rain rate -precip_dataset = conversion.to_rainrate(precip_dataset) -precip_var = precip_dataset.attrs["precip_var"] - -# Keep only positive rainfall values -rainrate_attrs = precip_dataset[precip_var].attrs -mask = precip_dataset[precip_var].values > rainrate_attrs["zerovalue"] - -############################################################################### -# Test data transformations -# ------------------------- - - -# Define method to visualize the data distribution with boxplots and plot the -# corresponding skewness -def plot_distribution(data, labels, skw): - N = len(data) - fig, ax1 = plt.subplots() - ax2 = ax1.twinx() - - ax2.plot(np.arange(N + 2), np.zeros(N + 2), ":r") - ax1.boxplot(data, sym="", medianprops={"color": "k"}) - ax1.set_xticks(np.arange(1, N + 1), labels=labels) - - ymax = [] - for i in range(N): - y = skw[i] - x = i + 1 - ax2.plot(x, y, "*r", ms=10, markeredgecolor="k") - ymax.append(np.max(data[i])) - - # ylims - ylims = np.percentile(ymax, 50) - ax1.set_ylim((-1 * ylims, ylims)) - ylims = np.max(np.abs(skw)) - ax2.set_ylim((-1.1 * ylims, 1.1 * ylims)) - - # labels - ax1.set_ylabel(r"Standardized values [$\sigma$]") - ax2.set_ylabel(r"Skewness []", color="r") - ax2.tick_params(axis="y", labelcolor="r") - - -############################################################################### -# Box-Cox transform -# ~~~~~~~~~~~~~~~~~ -# The Box-Cox transform is a well-known power transformation introduced by -# `Box and Cox (1964)`_. In its one-parameter version, the Box-Cox transform -# takes the form T(x) = ln(x) for lambda = 0, or T(x) = (x**lambda - 1)/lambda -# otherwise. -# -# To find a suitable lambda, we will experiment with a range of values -# and select the one that produces the most symmetric distribution, i.e., the -# lambda associated with a value of skewness closest to zero. -# To visually compare the results, the transformed data are standardized. -# -# .. _`Box and Cox (1964)`: https://doi.org/10.1111/j.2517-6161.1964.tb00553.x - -data = [] -labels = [] -skw = [] - -# Test a range of values for the transformation parameter Lambda -Lambdas = np.linspace(-0.4, 0.4, 11) -for i, Lambda in enumerate(Lambdas): - R_ = ( - transformation.boxcox_transform(precip_dataset, Lambda=Lambda)[precip_var] - .values[mask] - .flatten() - ) - R_ = (R_ - np.mean(R_)) / np.std(R_) - data.append(R_) - labels.append("{0:.2f}".format(Lambda)) - skw.append(skew(R_)) # skewness - -# Plot the transformed data distribution as a function of lambda -plot_distribution(data, labels, skw) -plt.title("Box-Cox transform") -plt.tight_layout() -plt.show() - -# Best lambda -idx_best = np.argmin(np.abs(skw)) -Lambda = Lambdas[idx_best] - -print("Best parameter lambda: %.2f\n(skewness = %.2f)" % (Lambda, skw[idx_best])) - -############################################################################### -# Compare data transformations -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -data = [] -labels = [] -skw = [] - -############################################################################### -# Rain rates -# ~~~~~~~~~~ -# First, let's have a look at the original rain rate values. -R = precip_dataset[precip_var].values[mask].flatten() -data.append((R - np.mean(R)) / np.std(R)) -labels.append("R") -skw.append(skew(R)) - -############################################################################### -# dB transform -# ~~~~~~~~~~~~ -# We transform the rainfall data into dB units: 10*log(R) - -R_ = transformation.dB_transform(precip_dataset)[precip_var].values[mask].flatten() -data.append((R_ - np.mean(R_)) / np.std(R_)) -labels.append("dB") -skw.append(skew(R_)) - -############################################################################### -# Square-root transform -# ~~~~~~~~~~~~~~~~~~~~~ -# Transform the data using the square-root: sqrt(R) - -R_ = transformation.sqrt_transform(precip_dataset)[precip_var].values[mask].flatten() -data.append((R_ - np.mean(R_)) / np.std(R_)) -labels.append("sqrt") -skw.append(skew(R_)) - -############################################################################### -# Box-Cox transform -# ~~~~~~~~~~~~~~~~~ -# We now apply the Box-Cox transform using the best parameter lambda found above. - -R_ = ( - transformation.boxcox_transform(precip_dataset, Lambda=Lambda)[precip_var] - .values[mask] - .flatten() -) -data.append((R_ - np.mean(R_)) / np.std(R_)) -labels.append("Box-Cox\n($\lambda=$%.2f)" % Lambda) -skw.append(skew(R_)) - -############################################################################### -# Normal quantile transform -# ~~~~~~~~~~~~~~~~~~~~~~~~~ -# At last, we apply the empirical normal quantile (NQ) transform as described in -# `Bogner et al (2012)`_. -# -# .. _`Bogner et al (2012)`: http://dx.doi.org/10.5194/hess-16-1085-2012 - -R_ = transformation.NQ_transform(precip_dataset)[precip_var].values[mask].flatten() -data.append((R_ - np.mean(R_)) / np.std(R_)) -labels.append("NQ") -skw.append(skew(R_)) - -############################################################################### -# By plotting all the results, we can notice first of all the strongly asymmetric -# distribution of the original data (R) and that all transformations manage to -# reduce its skewness. Among these, the Box-Cox transform (using the best parameter -# lambda) and the normal quantile (NQ) transform provide the best correction. -# Despite not producing a perfectly symmetric distribution, the square-root (sqrt) -# transform has the strong advantage of being defined for zeros, too, while all -# other transformations need an arbitrary rule for non-positive values. - -plot_distribution(data, labels, skw) -plt.title("Data transforms") -plt.tight_layout() -plt.show() +""" +The statistics of intermittent precipitation rates are particularly non-Gaussian +and display an asymmetric distribution bounded at zero. +Such properties restrict the usage of well-established statistical methods that +assume symmetric or Gaussian data. + +A common workaround is to introduce a suitable data transformation to approximate +a normal distribution. + +In this example, we test the data transformation methods available in pysteps +in order to obtain a more symmetric distribution of the precipitation data +(excluding the zeros). +The currently available transformations include the Box-Cox, dB, square-root and +normal quantile transforms. + +""" + +from datetime import datetime + +import matplotlib.pyplot as plt +import numpy as np +from scipy.stats import skew + +from pysteps import io, rcparams +from pysteps.utils import conversion, transformation + +############################################################################### +# Read the radar input images +# --------------------------- +# +# First, we will import the sequence of radar composites. +# You need the pysteps-data archive downloaded and the pystepsrc file +# configured with the data_source paths pointing to data folders. + +# Selected case +date = datetime.strptime("201609281600", "%Y%m%d%H%M") +data_source = rcparams.data_sources["fmi"] + + +############################################################################### +# Load the data from the archive +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +root_path = data_source["root_path"] +path_fmt = data_source["path_fmt"] +fn_pattern = data_source["fn_pattern"] +fn_ext = data_source["fn_ext"] +importer_name = data_source["importer"] +importer_kwargs = data_source["importer_kwargs"] +timestep = data_source["timestep"] + +# Get 1 hour of observations in the data archive +fns = io.archive.find_by_date( + date, root_path, path_fmt, fn_pattern, fn_ext, timestep, num_next_files=11 +) + +# Read the radar composites +importer = io.get_method(importer_name, "importer") +Z, _, metadata = io.read_timeseries(fns, importer, **importer_kwargs) + +# Keep only positive rainfall values +Z = Z[Z > metadata["zerovalue"]].flatten() + +# Convert to rain rate +R, metadata = conversion.to_rainrate(Z, metadata) + +############################################################################### +# Test data transformations +# ------------------------- + + +# Define method to visualize the data distribution with boxplots and plot the +# corresponding skewness +def plot_distribution(data, labels, skw): + N = len(data) + fig, ax1 = plt.subplots() + ax2 = ax1.twinx() + + ax2.plot(np.arange(N + 2), np.zeros(N + 2), ":r") + ax1.boxplot(data, sym="", medianprops={"color": "k"}) + ax1.set_xticks(np.arange(1, N + 1), labels=labels) + + ymax = [] + for i in range(N): + y = skw[i] + x = i + 1 + ax2.plot(x, y, "*r", ms=10, markeredgecolor="k") + ymax.append(np.max(data[i])) + + # ylims + ylims = np.percentile(ymax, 50) + ax1.set_ylim((-1 * ylims, ylims)) + ylims = np.max(np.abs(skw)) + ax2.set_ylim((-1.1 * ylims, 1.1 * ylims)) + + # labels + ax1.set_ylabel(r"Standardized values [$\sigma$]") + ax2.set_ylabel(r"Skewness []", color="r") + ax2.tick_params(axis="y", labelcolor="r") + + +############################################################################### +# Box-Cox transform +# ~~~~~~~~~~~~~~~~~ +# The Box-Cox transform is a well-known power transformation introduced by +# `Box and Cox (1964)`_. In its one-parameter version, the Box-Cox transform +# takes the form T(x) = ln(x) for lambda = 0, or T(x) = (x**lambda - 1)/lambda +# otherwise. +# +# To find a suitable lambda, we will experiment with a range of values +# and select the one that produces the most symmetric distribution, i.e., the +# lambda associated with a value of skewness closest to zero. +# To visually compare the results, the transformed data are standardized. +# +# .. _`Box and Cox (1964)`: https://doi.org/10.1111/j.2517-6161.1964.tb00553.x + +data = [] +labels = [] +skw = [] + +# Test a range of values for the transformation parameter Lambda +Lambdas = np.linspace(-0.4, 0.4, 11) +for i, Lambda in enumerate(Lambdas): + R_, _ = transformation.boxcox_transform(R, metadata, Lambda) + R_ = (R_ - np.mean(R_)) / np.std(R_) + data.append(R_) + labels.append("{0:.2f}".format(Lambda)) + skw.append(skew(R_)) # skewness + +# Plot the transformed data distribution as a function of lambda +plot_distribution(data, labels, skw) +plt.title("Box-Cox transform") +plt.tight_layout() +plt.show() + +# Best lambda +idx_best = np.argmin(np.abs(skw)) +Lambda = Lambdas[idx_best] + +print("Best parameter lambda: %.2f\n(skewness = %.2f)" % (Lambda, skw[idx_best])) + +############################################################################### +# Compare data transformations +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +data = [] +labels = [] +skw = [] + +############################################################################### +# Rain rates +# ~~~~~~~~~~ +# First, let's have a look at the original rain rate values. + +data.append((R - np.mean(R)) / np.std(R)) +labels.append("R") +skw.append(skew(R)) + +############################################################################### +# dB transform +# ~~~~~~~~~~~~ +# We transform the rainfall data into dB units: 10*log(R) + +R_, _ = transformation.dB_transform(R, metadata) +data.append((R_ - np.mean(R_)) / np.std(R_)) +labels.append("dB") +skw.append(skew(R_)) + +############################################################################### +# Square-root transform +# ~~~~~~~~~~~~~~~~~~~~~ +# Transform the data using the square-root: sqrt(R) + +R_, _ = transformation.sqrt_transform(R, metadata) +data.append((R_ - np.mean(R_)) / np.std(R_)) +labels.append("sqrt") +skw.append(skew(R_)) + +############################################################################### +# Box-Cox transform +# ~~~~~~~~~~~~~~~~~ +# We now apply the Box-Cox transform using the best parameter lambda found above. + +R_, _ = transformation.boxcox_transform(R, metadata, Lambda) +data.append((R_ - np.mean(R_)) / np.std(R_)) +labels.append("Box-Cox\n($\lambda=$%.2f)" % Lambda) +skw.append(skew(R_)) + +############################################################################### +# Normal quantile transform +# ~~~~~~~~~~~~~~~~~~~~~~~~~ +# At last, we apply the empirical normal quantile (NQ) transform as described in +# `Bogner et al (2012)`_. +# +# .. _`Bogner et al (2012)`: http://dx.doi.org/10.5194/hess-16-1085-2012 + +R_, _ = transformation.NQ_transform(R, metadata) +data.append((R_ - np.mean(R_)) / np.std(R_)) +labels.append("NQ") +skw.append(skew(R_)) + +############################################################################### +# By plotting all the results, we can notice first of all the strongly asymmetric +# distribution of the original data (R) and that all transformations manage to +# reduce its skewness. Among these, the Box-Cox transform (using the best parameter +# lambda) and the normal quantile (NQ) transform provide the best correction. +# Despite not producing a perfectly symmetric distribution, the square-root (sqrt) +# transform has the strong advantage of being defined for zeros, too, while all +# other transformations need an arbitrary rule for non-positive values. + +plot_distribution(data, labels, skw) +plt.title("Data transforms") +plt.tight_layout() +plt.show() +# -*- coding: utf-8 -*- +""" +Data transformations + +The statistics of intermittent precipitation rates are particularly non-Gaussian +and display an asymmetric distribution bounded at zero. +Such properties restrict the usage of well-established statistical methods that +assume symmetric or Gaussian data. + +A common workaround is to introduce a suitable data transformation to approximate +a normal distribution. + +In this example, we test the data transformation methods available in pysteps +in order to obtain a more symmetric distribution of the precipitation data +(excluding the zeros). +The currently available transformations include the Box-Cox, dB, square-root and +normal quantile transforms. + +""" + +from datetime import datetime + +import matplotlib.pyplot as plt +import numpy as np +from scipy.stats import skew + +from pysteps import io, rcparams +from pysteps.utils import conversion, transformation + +############################################################################### +# Read the radar input images +# --------------------------- +# +# First, we will import the sequence of radar composites. +# You need the pysteps-data archive downloaded and the pystepsrc file +# configured with the data_source paths pointing to data folders. + +# Selected case +date = datetime.strptime("201609281600", "%Y%m%d%H%M") +data_source = rcparams.data_sources["fmi"] + + +############################################################################### +# Load the data from the archive +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +root_path = data_source["root_path"] +path_fmt = data_source["path_fmt"] +fn_pattern = data_source["fn_pattern"] +fn_ext = data_source["fn_ext"] +importer_name = data_source["importer"] +importer_kwargs = data_source["importer_kwargs"] +timestep = data_source["timestep"] + +# Get 1 hour of observations in the data archive +fns = io.archive.find_by_date( + date, root_path, path_fmt, fn_pattern, fn_ext, timestep, num_next_files=11 +) + +# Read the radar composites +importer = io.get_method(importer_name, "importer") +precip_dataset = io.read_timeseries(fns, importer, **importer_kwargs) + +# Convert to rain rate +precip_dataset = conversion.to_rainrate(precip_dataset) +precip_var = precip_dataset.attrs["precip_var"] + +# Keep only positive rainfall values +rainrate_attrs = precip_dataset[precip_var].attrs +mask = precip_dataset[precip_var].values > rainrate_attrs["zerovalue"] + +############################################################################### +# Test data transformations +# ------------------------- + + +# Define method to visualize the data distribution with boxplots and plot the +# corresponding skewness +def plot_distribution(data, labels, skw): + N = len(data) + fig, ax1 = plt.subplots() + ax2 = ax1.twinx() + + ax2.plot(np.arange(N + 2), np.zeros(N + 2), ":r") + ax1.boxplot(data, sym="", medianprops={"color": "k"}) + ax1.set_xticks(np.arange(1, N + 1), labels=labels) + + ymax = [] + for i in range(N): + y = skw[i] + x = i + 1 + ax2.plot(x, y, "*r", ms=10, markeredgecolor="k") + ymax.append(np.max(data[i])) + + # ylims + ylims = np.percentile(ymax, 50) + ax1.set_ylim((-1 * ylims, ylims)) + ylims = np.max(np.abs(skw)) + ax2.set_ylim((-1.1 * ylims, 1.1 * ylims)) + + # labels + ax1.set_ylabel(r"Standardized values [$\sigma$]") + ax2.set_ylabel(r"Skewness []", color="r") + ax2.tick_params(axis="y", labelcolor="r") + + +############################################################################### +# Box-Cox transform +# ~~~~~~~~~~~~~~~~~ +# The Box-Cox transform is a well-known power transformation introduced by +# `Box and Cox (1964)`_. In its one-parameter version, the Box-Cox transform +# takes the form T(x) = ln(x) for lambda = 0, or T(x) = (x**lambda - 1)/lambda +# otherwise. +# +# To find a suitable lambda, we will experiment with a range of values +# and select the one that produces the most symmetric distribution, i.e., the +# lambda associated with a value of skewness closest to zero. +# To visually compare the results, the transformed data are standardized. +# +# .. _`Box and Cox (1964)`: https://doi.org/10.1111/j.2517-6161.1964.tb00553.x + +data = [] +labels = [] +skw = [] + +# Test a range of values for the transformation parameter Lambda +Lambdas = np.linspace(-0.4, 0.4, 11) +for i, Lambda in enumerate(Lambdas): + R_ = ( + transformation.boxcox_transform(precip_dataset, Lambda=Lambda)[precip_var] + .values[mask] + .flatten() + ) + R_ = (R_ - np.mean(R_)) / np.std(R_) + data.append(R_) + labels.append("{0:.2f}".format(Lambda)) + skw.append(skew(R_)) # skewness + +# Plot the transformed data distribution as a function of lambda +plot_distribution(data, labels, skw) +plt.title("Box-Cox transform") +plt.tight_layout() +plt.show() + +# Best lambda +idx_best = np.argmin(np.abs(skw)) +Lambda = Lambdas[idx_best] + +print("Best parameter lambda: %.2f\n(skewness = %.2f)" % (Lambda, skw[idx_best])) + +############################################################################### +# Compare data transformations +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +data = [] +labels = [] +skw = [] + +############################################################################### +# Rain rates +# ~~~~~~~~~~ +# First, let's have a look at the original rain rate values. +R = precip_dataset[precip_var].values[mask].flatten() +data.append((R - np.mean(R)) / np.std(R)) +labels.append("R") +skw.append(skew(R)) + +############################################################################### +# dB transform +# ~~~~~~~~~~~~ +# We transform the rainfall data into dB units: 10*log(R) + +R_ = transformation.dB_transform(precip_dataset)[precip_var].values[mask].flatten() +data.append((R_ - np.mean(R_)) / np.std(R_)) +labels.append("dB") +skw.append(skew(R_)) + +############################################################################### +# Square-root transform +# ~~~~~~~~~~~~~~~~~~~~~ +# Transform the data using the square-root: sqrt(R) + +R_ = transformation.sqrt_transform(precip_dataset)[precip_var].values[mask].flatten() +data.append((R_ - np.mean(R_)) / np.std(R_)) +labels.append("sqrt") +skw.append(skew(R_)) + +############################################################################### +# Box-Cox transform +# ~~~~~~~~~~~~~~~~~ +# We now apply the Box-Cox transform using the best parameter lambda found above. + +R_ = ( + transformation.boxcox_transform(precip_dataset, Lambda=Lambda)[precip_var] + .values[mask] + .flatten() +) +data.append((R_ - np.mean(R_)) / np.std(R_)) +labels.append("Box-Cox\n($\lambda=$%.2f)" % Lambda) +skw.append(skew(R_)) + +############################################################################### +# Normal quantile transform +# ~~~~~~~~~~~~~~~~~~~~~~~~~ +# At last, we apply the empirical normal quantile (NQ) transform as described in +# `Bogner et al (2012)`_. +# +# .. _`Bogner et al (2012)`: http://dx.doi.org/10.5194/hess-16-1085-2012 + +R_ = transformation.NQ_transform(precip_dataset)[precip_var].values[mask].flatten() +data.append((R_ - np.mean(R_)) / np.std(R_)) +labels.append("NQ") +skw.append(skew(R_)) + +############################################################################### +# By plotting all the results, we can notice first of all the strongly asymmetric +# distribution of the original data (R) and that all transformations manage to +# reduce its skewness. Among these, the Box-Cox transform (using the best parameter +# lambda) and the normal quantile (NQ) transform provide the best correction. +# Despite not producing a perfectly symmetric distribution, the square-root (sqrt) +# transform has the strong advantage of being defined for zeros, too, while all +# other transformations need an arbitrary rule for non-positive values. + +plot_distribution(data, labels, skw) +plt.title("Data transforms") +plt.tight_layout() +plt.show() diff --git a/examples/ens_kalman_filter_blended_forecast.py b/examples/ens_kalman_filter_blended_forecast.py index 4bce60de6..217ec82ae 100644 --- a/examples/ens_kalman_filter_blended_forecast.py +++ b/examples/ens_kalman_filter_blended_forecast.py @@ -22,6 +22,7 @@ from datetime import datetime import numpy as np +import pysteps_nwp_importers from matplotlib import pyplot as plt from pysteps_nwp_importers.importer_dwd_nwp import unstructured2regular diff --git a/examples/my_first_nowcast.ipynb b/examples/my_first_nowcast.ipynb index 5b69fafbe..f649c0352 100644 --- a/examples/my_first_nowcast.ipynb +++ b/examples/my_first_nowcast.ipynb @@ -142,7 +142,7 @@ "outputs": [], "source": [ "# Import the helper functions\n", - "from pysteps.datasets import download_pysteps_data, create_default_pystepsrc\n", + "from pysteps.datasets import create_default_pystepsrc, download_pysteps_data\n", "\n", "# Download the pysteps data in the \"pysteps_data\"\n", "download_pysteps_data(\"pysteps_data\")" @@ -266,11 +266,11 @@ }, "outputs": [], "source": [ - "from pysteps.datasets import load_dataset\n", - "\n", "# We'll import the time module to measure the time the importer needed\n", "import time\n", "\n", + "from pysteps.datasets import load_dataset\n", + "\n", "start_time = time.time()\n", "\n", "# Import the data\n", @@ -414,6 +414,7 @@ "outputs": [], "source": [ "from matplotlib import pyplot as plt\n", + "\n", "from pysteps.visualization import plot_precip_field\n", "\n", "# Set a figure size that looks nice ;)\n", diff --git a/examples/plot_linear_blending.py b/examples/plot_linear_blending.py index 4f28a893c..b211829c7 100644 --- a/examples/plot_linear_blending.py +++ b/examples/plot_linear_blending.py @@ -15,7 +15,7 @@ from matplotlib import pyplot as plt import pysteps -from pysteps import io, rcparams, nowcasts, blending +from pysteps import blending, io, nowcasts, rcparams from pysteps.utils import conversion, transformation from pysteps.visualization import plot_precip_field from pysteps.xarray_helpers import convert_input_to_xarray_dataset diff --git a/examples/plot_noise_generators.py b/examples/plot_noise_generators.py index 09edeaa49..3d61eddd0 100644 --- a/examples/plot_noise_generators.py +++ b/examples/plot_noise_generators.py @@ -1,185 +1,362 @@ -#!/bin/env python -""" -Generation of stochastic noise -============================== - -This example script shows how to run the stochastic noise field generators -included in pysteps. - -These noise fields are used as perturbation terms during an extrapolation -nowcast in order to represent the uncertainty in the evolution of the rainfall -field. -""" - -import os -from pprint import pprint - -import numpy as np -from matplotlib import cm -from matplotlib import pyplot as plt - -from pysteps import io, rcparams -from pysteps.noise.fftgenerators import ( - generate_noise_2d_fft_filter, - initialize_nonparam_2d_fft_filter, - initialize_param_2d_fft_filter, -) -from pysteps.utils import conversion, rapsd, transformation -from pysteps.visualization import plot_precip_field, plot_spectrum1d -from pysteps.xarray_helpers import geodata_from_dataset - -############################################################################### -# Read precipitation field -# ------------------------ -# -# First thing, the radar composite is imported and transformed in units -# of dB. -# This image will be used to train the Fourier filters that are necessary to -# produce the fields of spatially correlated noise. - -# Import the example radar composite -root_path = rcparams.data_sources["mch"]["root_path"] -filename = os.path.join(root_path, "20160711", "AQC161932100V_00005.801.gif") -precip_dataset = io.import_mch_gif(filename, product="AQC", unit="mm", accutime=5.0) - -# Convert to mm/h -precip_dataset = conversion.to_rainrate(precip_dataset) -precip_var = precip_dataset.attrs["precip_var"] - -# Nicely print the metadata -pprint(precip_dataset[precip_var].attrs) - -# Plot the rainfall field -geodata = geodata_from_dataset(precip_dataset) -plot_precip_field(precip_dataset[precip_var], geodata=geodata) -plt.show() - -# Log-transform the data -precip_dataset = transformation.dB_transform( - precip_dataset, threshold=0.1, zerovalue=-15.0 -) - -# Assign the fill value to all the Nans -R = precip_dataset[precip_var].values -R[~np.isfinite(R)] = precip_dataset[precip_var].attrs["zerovalue"] - -############################################################################### -# Parametric filter -# ----------------- -# -# In the parametric approach, a power-law model is used to approximate the power -# spectral density (PSD) of a given rainfall field. -# -# The parametric model uses a piece-wise linear function with two spectral -# slopes (beta1 and beta2) and one breaking point - -# Fit the parametric PSD to the observation -Fp = initialize_param_2d_fft_filter(R) - -# Compute the observed and fitted 1D PSD -L = np.max(Fp["input_shape"]) -if L % 2 == 1: - wn = np.arange(0, int(L / 2) + 1) -else: - wn = np.arange(0, int(L / 2)) -R_, freq = rapsd(R, fft_method=np.fft, return_freq=True) -f = np.exp(Fp["model"](np.log(wn), *Fp["pars"])) - -# Extract the scaling break in km, beta1 and beta2 -w0 = L / np.exp(Fp["pars"][0]) -b1 = Fp["pars"][2] -b2 = Fp["pars"][3] - -# Plot the observed power spectrum and the model -fig, ax = plt.subplots() -plot_scales = [512, 256, 128, 64, 32, 16, 8, 4] -plot_spectrum1d( - freq, - R_, - x_units="km", - y_units="dBR", - color="k", - ax=ax, - label="Observed", - wavelength_ticks=plot_scales, -) -plot_spectrum1d( - freq, - f, - x_units="km", - y_units="dBR", - color="r", - ax=ax, - label="Fit", - wavelength_ticks=plot_scales, -) -plt.legend() -ax.set_title( - "Radially averaged log-power spectrum of R\n" - r"$\omega_0=%.0f km, \beta_1=%.1f, \beta_2=%.1f$" % (w0, b1, b2) -) -plt.show() - -############################################################################### -# Nonparametric filter -# -------------------- -# -# In the nonparametric approach, the Fourier filter is obtained directly -# from the power spectrum of the observed precipitation field R. - -Fnp = initialize_nonparam_2d_fft_filter(R) - -############################################################################### -# Noise generator -# --------------- -# -# The parametric and nonparametric filters obtained above can now be used -# to produce N realizations of random fields of prescribed power spectrum, -# hence with the same correlation structure as the initial rainfall field. - -seed = 42 -num_realizations = 3 - -# Generate noise -Np = [] -Nnp = [] -for k in range(num_realizations): - Np.append(generate_noise_2d_fft_filter(Fp, seed=seed + k)) - Nnp.append(generate_noise_2d_fft_filter(Fnp, seed=seed + k)) - -# Plot the generated noise fields - -fig, ax = plt.subplots(nrows=2, ncols=3) - -# parametric noise -ax[0, 0].imshow(Np[0], cmap=cm.RdBu_r, vmin=-3, vmax=3) -ax[0, 1].imshow(Np[1], cmap=cm.RdBu_r, vmin=-3, vmax=3) -ax[0, 2].imshow(Np[2], cmap=cm.RdBu_r, vmin=-3, vmax=3) - -# nonparametric noise -ax[1, 0].imshow(Nnp[0], cmap=cm.RdBu_r, vmin=-3, vmax=3) -ax[1, 1].imshow(Nnp[1], cmap=cm.RdBu_r, vmin=-3, vmax=3) -ax[1, 2].imshow(Nnp[2], cmap=cm.RdBu_r, vmin=-3, vmax=3) - -for i in range(2): - for j in range(3): - ax[i, j].set_xticks([]) - ax[i, j].set_yticks([]) -plt.tight_layout() -plt.show() - -############################################################################### -# The above figure highlights the main limitation of the parametric approach -# (top row), that is, the assumption of an isotropic power law scaling -# relationship, meaning that anisotropic structures such as rainfall bands -# cannot be represented. -# -# Instead, the nonparametric approach (bottom row) allows generating -# perturbation fields with anisotropic structures, but it also requires a -# larger sample size and is sensitive to the quality of the input data, e.g. -# the presence of residual clutter in the radar image. -# -# In addition, both techniques assume spatial stationarity of the covariance -# structure of the field. - -# sphinx_gallery_thumbnail_number = 3 +#!/bin/env python +""" +Generation of stochastic noise + +This example script shows how to run the stochastic noise field generators +included in pysteps. + +These noise fields are used as perturbation terms during an extrapolation +nowcast in order to represent the uncertainty in the evolution of the rainfall +field. +""" + +import os +from pprint import pprint + +import numpy as np +from matplotlib import cm +from matplotlib import pyplot as plt + +from pysteps import io, rcparams +from pysteps.noise.fftgenerators import ( + generate_noise_2d_fft_filter, + initialize_nonparam_2d_fft_filter, + initialize_param_2d_fft_filter, +) +from pysteps.utils import conversion, rapsd, transformation +from pysteps.visualization import plot_precip_field, plot_spectrum1d + +############################################################################### +# Read precipitation field +# ------------------------ +# +# First thing, the radar composite is imported and transformed in units +# of dB. +# This image will be used to train the Fourier filters that are necessary to +# produce the fields of spatially correlated noise. + +# Import the example radar composite +root_path = rcparams.data_sources["mch"]["root_path"] +filename = os.path.join(root_path, "20160711", "AQC161932100V_00005.801.gif") +R, _, metadata = io.import_mch_gif(filename, product="AQC", unit="mm", accutime=5.0) + +# Convert to mm/h +R, metadata = conversion.to_rainrate(R, metadata) + +# Nicely print the metadata +pprint(metadata) + +# Plot the rainfall field +plot_precip_field(R, geodata=metadata) +plt.show() + +# Log-transform the data +R, metadata = transformation.dB_transform(R, metadata, threshold=0.1, zerovalue=-15.0) + +# Assign the fill value to all the Nans +R[~np.isfinite(R)] = metadata["zerovalue"] + +############################################################################### +# Parametric filter +# ----------------- +# +# In the parametric approach, a power-law model is used to approximate the power +# spectral density (PSD) of a given rainfall field. +# +# The parametric model uses a piece-wise linear function with two spectral +# slopes (beta1 and beta2) and one breaking point + +# Fit the parametric PSD to the observation +Fp = initialize_param_2d_fft_filter(R) + +# Compute the observed and fitted 1D PSD +L = np.max(Fp["input_shape"]) +if L % 2 == 1: + wn = np.arange(0, int(L / 2) + 1) +else: + wn = np.arange(0, int(L / 2)) +R_, freq = rapsd(R, fft_method=np.fft, return_freq=True) +f = np.exp(Fp["model"](np.log(wn), *Fp["pars"])) + +# Extract the scaling break in km, beta1 and beta2 +w0 = L / np.exp(Fp["pars"][0]) +b1 = Fp["pars"][2] +b2 = Fp["pars"][3] + +# Plot the observed power spectrum and the model +fig, ax = plt.subplots() +plot_scales = [512, 256, 128, 64, 32, 16, 8, 4] +plot_spectrum1d( + freq, + R_, + x_units="km", + y_units="dBR", + color="k", + ax=ax, + label="Observed", + wavelength_ticks=plot_scales, +) +plot_spectrum1d( + freq, + f, + x_units="km", + y_units="dBR", + color="r", + ax=ax, + label="Fit", + wavelength_ticks=plot_scales, +) +plt.legend() +ax.set_title( + "Radially averaged log-power spectrum of R\n" + r"$\omega_0=%.0f km, \beta_1=%.1f, \beta_2=%.1f$" % (w0, b1, b2) +) +plt.show() + +############################################################################### +# Nonparametric filter +# -------------------- +# +# In the nonparametric approach, the Fourier filter is obtained directly +# from the power spectrum of the observed precipitation field R. + +Fnp = initialize_nonparam_2d_fft_filter(R) + +############################################################################### +# Noise generator +# --------------- +# +# The parametric and nonparametric filters obtained above can now be used +# to produce N realizations of random fields of prescribed power spectrum, +# hence with the same correlation structure as the initial rainfall field. + +seed = 42 +num_realizations = 3 + +# Generate noise +Np = [] +Nnp = [] +for k in range(num_realizations): + Np.append(generate_noise_2d_fft_filter(Fp, seed=seed + k)) + Nnp.append(generate_noise_2d_fft_filter(Fnp, seed=seed + k)) + +# Plot the generated noise fields + +fig, ax = plt.subplots(nrows=2, ncols=3) + +# parametric noise +ax[0, 0].imshow(Np[0], cmap=cm.RdBu_r, vmin=-3, vmax=3) +ax[0, 1].imshow(Np[1], cmap=cm.RdBu_r, vmin=-3, vmax=3) +ax[0, 2].imshow(Np[2], cmap=cm.RdBu_r, vmin=-3, vmax=3) + +# nonparametric noise +ax[1, 0].imshow(Nnp[0], cmap=cm.RdBu_r, vmin=-3, vmax=3) +ax[1, 1].imshow(Nnp[1], cmap=cm.RdBu_r, vmin=-3, vmax=3) +ax[1, 2].imshow(Nnp[2], cmap=cm.RdBu_r, vmin=-3, vmax=3) + +for i in range(2): + for j in range(3): + ax[i, j].set_xticks([]) + ax[i, j].set_yticks([]) +plt.tight_layout() +plt.show() + +############################################################################### +# The above figure highlights the main limitation of the parametric approach +# (top row), that is, the assumption of an isotropic power law scaling +# relationship, meaning that anisotropic structures such as rainfall bands +# cannot be represented. +# +# Instead, the nonparametric approach (bottom row) allows generating +# perturbation fields with anisotropic structures, but it also requires a +# larger sample size and is sensitive to the quality of the input data, e.g. +# the presence of residual clutter in the radar image. +# +# In addition, both techniques assume spatial stationarity of the covariance +# structure of the field. + +# sphinx_gallery_thumbnail_number = 3 +#!/bin/env python +""" +Generation of stochastic noise + +This example script shows how to run the stochastic noise field generators +included in pysteps. + +These noise fields are used as perturbation terms during an extrapolation +nowcast in order to represent the uncertainty in the evolution of the rainfall +field. +""" + +import os +from pprint import pprint + +import numpy as np +from matplotlib import cm +from matplotlib import pyplot as plt + +from pysteps import io, rcparams +from pysteps.noise.fftgenerators import ( + generate_noise_2d_fft_filter, + initialize_nonparam_2d_fft_filter, + initialize_param_2d_fft_filter, +) +from pysteps.utils import conversion, rapsd, transformation +from pysteps.visualization import plot_precip_field, plot_spectrum1d +from pysteps.xarray_helpers import geodata_from_dataset + +############################################################################### +# Read precipitation field +# ------------------------ +# +# First thing, the radar composite is imported and transformed in units +# of dB. +# This image will be used to train the Fourier filters that are necessary to +# produce the fields of spatially correlated noise. + +# Import the example radar composite +root_path = rcparams.data_sources["mch"]["root_path"] +filename = os.path.join(root_path, "20160711", "AQC161932100V_00005.801.gif") +precip_dataset = io.import_mch_gif(filename, product="AQC", unit="mm", accutime=5.0) + +# Convert to mm/h +precip_dataset = conversion.to_rainrate(precip_dataset) +precip_var = precip_dataset.attrs["precip_var"] + +# Nicely print the metadata +pprint(precip_dataset[precip_var].attrs) + +# Plot the rainfall field +geodata = geodata_from_dataset(precip_dataset) +plot_precip_field(precip_dataset[precip_var], geodata=geodata) +plt.show() + +# Log-transform the data +precip_dataset = transformation.dB_transform( + precip_dataset, threshold=0.1, zerovalue=-15.0 +) + +# Assign the fill value to all the Nans +R = precip_dataset[precip_var].values +R[~np.isfinite(R)] = precip_dataset[precip_var].attrs["zerovalue"] + +############################################################################### +# Parametric filter +# ----------------- +# +# In the parametric approach, a power-law model is used to approximate the power +# spectral density (PSD) of a given rainfall field. +# +# The parametric model uses a piece-wise linear function with two spectral +# slopes (beta1 and beta2) and one breaking point + +# Fit the parametric PSD to the observation +Fp = initialize_param_2d_fft_filter(R) + +# Compute the observed and fitted 1D PSD +L = np.max(Fp["input_shape"]) +if L % 2 == 1: + wn = np.arange(0, int(L / 2) + 1) +else: + wn = np.arange(0, int(L / 2)) +R_, freq = rapsd(R, fft_method=np.fft, return_freq=True) +f = np.exp(Fp["model"](np.log(wn), *Fp["pars"])) + +# Extract the scaling break in km, beta1 and beta2 +w0 = L / np.exp(Fp["pars"][0]) +b1 = Fp["pars"][2] +b2 = Fp["pars"][3] + +# Plot the observed power spectrum and the model +fig, ax = plt.subplots() +plot_scales = [512, 256, 128, 64, 32, 16, 8, 4] +plot_spectrum1d( + freq, + R_, + x_units="km", + y_units="dBR", + color="k", + ax=ax, + label="Observed", + wavelength_ticks=plot_scales, +) +plot_spectrum1d( + freq, + f, + x_units="km", + y_units="dBR", + color="r", + ax=ax, + label="Fit", + wavelength_ticks=plot_scales, +) +plt.legend() +ax.set_title( + "Radially averaged log-power spectrum of R\n" + r"$\omega_0=%.0f km, \beta_1=%.1f, \beta_2=%.1f$" % (w0, b1, b2) +) +plt.show() + +############################################################################### +# Nonparametric filter +# -------------------- +# +# In the nonparametric approach, the Fourier filter is obtained directly +# from the power spectrum of the observed precipitation field R. + +Fnp = initialize_nonparam_2d_fft_filter(R) + +############################################################################### +# Noise generator +# --------------- +# +# The parametric and nonparametric filters obtained above can now be used +# to produce N realizations of random fields of prescribed power spectrum, +# hence with the same correlation structure as the initial rainfall field. + +seed = 42 +num_realizations = 3 + +# Generate noise +Np = [] +Nnp = [] +for k in range(num_realizations): + Np.append(generate_noise_2d_fft_filter(Fp, seed=seed + k)) + Nnp.append(generate_noise_2d_fft_filter(Fnp, seed=seed + k)) + +# Plot the generated noise fields + +fig, ax = plt.subplots(nrows=2, ncols=3) + +# parametric noise +ax[0, 0].imshow(Np[0], cmap=cm.RdBu_r, vmin=-3, vmax=3) +ax[0, 1].imshow(Np[1], cmap=cm.RdBu_r, vmin=-3, vmax=3) +ax[0, 2].imshow(Np[2], cmap=cm.RdBu_r, vmin=-3, vmax=3) + +# nonparametric noise +ax[1, 0].imshow(Nnp[0], cmap=cm.RdBu_r, vmin=-3, vmax=3) +ax[1, 1].imshow(Nnp[1], cmap=cm.RdBu_r, vmin=-3, vmax=3) +ax[1, 2].imshow(Nnp[2], cmap=cm.RdBu_r, vmin=-3, vmax=3) + +for i in range(2): + for j in range(3): + ax[i, j].set_xticks([]) + ax[i, j].set_yticks([]) +plt.tight_layout() +plt.show() + +############################################################################### +# The above figure highlights the main limitation of the parametric approach +# (top row), that is, the assumption of an isotropic power law scaling +# relationship, meaning that anisotropic structures such as rainfall bands +# cannot be represented. +# +# Instead, the nonparametric approach (bottom row) allows generating +# perturbation fields with anisotropic structures, but it also requires a +# larger sample size and is sensitive to the quality of the input data, e.g. +# the presence of residual clutter in the radar image. +# +# In addition, both techniques assume spatial stationarity of the covariance +# structure of the field. + +# sphinx_gallery_thumbnail_number = 3 diff --git a/pyproject.toml b/pyproject.toml index 17b9b0036..5e346124c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,27 +1,125 @@ [build-system] requires = [ "wheel", - "setuptools>=40.8.0", - "Cython>=0.29.2", - "numpy>=1.13" + "setuptools>=84", + "Cython>=3", + "numpy>=2", ] -# setuptools 40.8.0 is the first version of setuptools that offers -# a PEP 517 backend that closely mimics directly executing setup.py. -build-backend = "setuptools.build_meta:__legacy__" -#https://pip.pypa.io/en/stable/reference/pip/#pep-517-and-518-support +build-backend = "setuptools.build_meta" +[project] +name = "PySTEPS" +description = "Python framework for short-term ensemble prediction systems" +keywords = ["advection", "ensemble prediction", "forecast verification", "hydrology", "nowcasting", "optical flow", "precipitation", "rainfall", "rainfall prediction", "stochastic processes", "weather", "weather radar",] +license = "BSD-3-Clause" +classifiers = [ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Science/Research", + "Topic :: Scientific/Engineering", + "Topic :: Scientific/Engineering :: Atmospheric Science", + "Topic :: Scientific/Engineering :: Hydrology", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Operating System :: OS Independent", +] +readme = "README.rst" +version = "2.0.0" +requires-python = ">=3.11.0,<3.14.0" +dependencies = [ + "jsmin", + "jsonschema", + "matplotlib", + "netCDF4", + "numpy", + "opencv-python", + "pandas", + "pillow", + "pyproj", + "scipy", + "xarray", +] + +[project.optional-dependencies] +optional = [ + "cartopy>=0.18", + "cookiecutter", + "dask", + "geotiff", + "h5py", + "pandas", + "pyfftw", + "pywavelets", + "rasterio", + "scikit-image", + "scikit-learn", +] +pygrib = [ + "pygrib" +] +gdal =[ + # gdal must match what's installed locally -- see add_gdal[_brew].sh + "pip", + "setuptools>=67", + "wheel", +] + +[dependency-groups] +dev = [ + "bandit", + "pre-commit", + "pytest", + "pytest-cov", + "ruff", +] + +[project.urls] +Homepage = "https://github.com/you/my-package" +Source = "https://github.com/pySTEPS/pysteps" +Issues = "https://github.com/pySTEPS/pysteps/issues" +CI = "https://github.com/pySTEPS/pysteps/actions" +Changelog = "https://github.com/pySTEPS/pysteps/releases" +Documentation = "https://pysteps.readthedocs.io" -# Define black parameters for the project -# https://black.readthedocs.io/en/stable/pyproject_toml.html#configuration-format -[tool.black] -target-version = ['py36'] +######################################################################################### +# Style checking +######################################################################################### + +[tool.bandit] +assert_used = { skips = ["*/test_*.py"] } +skips = [ + # 2026-10-04: These are allowed so the tests can pass. Please check this list for unwanted issues. + 'B101', # allow asserts + 'B301', # allow pickles + 'B403', # allow pickle to be imported + 'B603', # allow subprocess + 'B404', # allow subprocess to be imported + 'B607', # allow process to start with partial path\ + 'B310', # allow request.urlretrieve +] +exclude_dirs = [ + ".venv", + "dist", + "pysteps/tests" +] + +[tool.ruff] line-length = 88 -exclude = ''' -/( - \.eggs - | \.git - | _build - | build - | dist -)/ -''' +# In addition to the standard set of exclusions, omit build and dist +extend-exclude = [ + "*build", + "*dist", +] + +[tool.ruff.lint] +select = [ + "I", # sort imports! +] + +[tool.ruff.format] +# Use `\n` line endings for all files +line-ending = "lf" + +[tool.setuptools] +py-modules = ["pysteps"] diff --git a/pysteps/__init__.py b/pysteps/__init__.py index f6126d340..961ba2ca7 100644 --- a/pysteps/__init__.py +++ b/pysteps/__init__.py @@ -8,20 +8,22 @@ from jsonschema import Draft4Validator # import subpackages -from . import blending -from . import cascade -from . import datasets -from . import decorators -from . import downscaling -from . import exceptions -from . import extrapolation -from . import io -from . import motion -from . import noise -from . import nowcasts -from . import postprocessing -from . import timeseries -from . import utils +from . import ( + blending, + cascade, + datasets, + decorators, + downscaling, + exceptions, + extrapolation, + io, + motion, + noise, + nowcasts, + postprocessing, + timeseries, + utils, +) from . import verification as vf from . import visualization as plt diff --git a/pysteps/blending/__init__.py b/pysteps/blending/__init__.py index 640d81d01..b70d1b559 100644 --- a/pysteps/blending/__init__.py +++ b/pysteps/blending/__init__.py @@ -2,6 +2,7 @@ """Methods for blending NWP model(s) with nowcasts.""" from pysteps.blending.interface import get_method + from .clim import * from .skill_scores import * from .utils import * diff --git a/pysteps/blending/ens_kalman_filter_methods.py b/pysteps/blending/ens_kalman_filter_methods.py index b015353f9..e2f0fb28b 100644 --- a/pysteps/blending/ens_kalman_filter_methods.py +++ b/pysteps/blending/ens_kalman_filter_methods.py @@ -77,7 +77,6 @@ class EnsembleKalmanFilter: - def __init__(self, config, params): self._config = config @@ -399,7 +398,6 @@ def get_weighting_for_probability_matching( class MaskedEnKF(EnsembleKalmanFilter): - def __init__(self, config, params): EnsembleKalmanFilter.__init__(self, config, params) diff --git a/pysteps/blending/interface.py b/pysteps/blending/interface.py index 554b5c07c..14d5c572d 100644 --- a/pysteps/blending/interface.py +++ b/pysteps/blending/interface.py @@ -13,9 +13,7 @@ from functools import partial -from pysteps.blending import linear_blending -from pysteps.blending import steps -from pysteps.blending import pca_ens_kalman_filter +from pysteps.blending import linear_blending, pca_ens_kalman_filter, steps _blending_methods = dict() _blending_methods["linear_blending"] = linear_blending.forecast @@ -68,5 +66,5 @@ def get_method(name): raise ValueError( f"Unknown blending method {name}." "The available methods are: " - f"{*list(_blending_methods.keys()),}" + f"{(*list(_blending_methods.keys()),)}" ) from None diff --git a/pysteps/blending/skill_scores.py b/pysteps/blending/skill_scores.py index 8bdccaaec..c9c1aebd9 100644 --- a/pysteps/blending/skill_scores.py +++ b/pysteps/blending/skill_scores.py @@ -16,6 +16,7 @@ """ import numpy as np + from pysteps.blending import clim diff --git a/pysteps/blending/utils.py b/pysteps/blending/utils.py index 4cae9c28a..a18e9d89b 100644 --- a/pysteps/blending/utils.py +++ b/pysteps/blending/utils.py @@ -19,9 +19,9 @@ """ import datetime -from typing import Any, Callable import warnings from pathlib import Path +from typing import Any, Callable import numpy as np import xarray as xr @@ -405,8 +405,7 @@ def preprocess_nwp_data( if not NETCDF4_IMPORTED: raise MissingOptionalDependency( - "netCDF4 package is required to save the NWP data, " - "but it is not installed" + "netCDF4 package is required to save the NWP data, but it is not installed" ) if "ens_number" in precip_nwp_dataset.dims: diff --git a/pysteps/cascade/decomposition.py b/pysteps/cascade/decomposition.py index c96152c0e..37bb427d6 100644 --- a/pysteps/cascade/decomposition.py +++ b/pysteps/cascade/decomposition.py @@ -71,6 +71,7 @@ """ import numpy as np + from pysteps import utils @@ -171,7 +172,8 @@ def decomposition_fft(field, bp_filter, **kwargs): ): raise ValueError( "Dimension mismatch between field and bp_filter: " - "int(field.shape[1]/2)+1=%d , " % (int(field.shape[1] / 2) + 1) + "int(field.shape[1]/2)+1=%d , " + % (int(field.shape[1] / 2) + 1) + "bp_filter['weights_2d'].shape[2]" "=%d" % bp_filter["weights_2d"].shape[2] ) @@ -182,7 +184,8 @@ def decomposition_fft(field, bp_filter, **kwargs): ): raise ValueError( "Dimension mismatch between field and bp_filter: " - "field.shape[1]=%d , " % (field.shape[1] + 1) + "field.shape[1]=%d , " + % (field.shape[1] + 1) + "bp_filter['weights_2d'].shape[2]" "=%d" % bp_filter["weights_2d"].shape[2] ) diff --git a/pysteps/cascade/interface.py b/pysteps/cascade/interface.py index f7adeb17e..828edf0ff 100644 --- a/pysteps/cascade/interface.py +++ b/pysteps/cascade/interface.py @@ -10,7 +10,7 @@ get_method """ -from pysteps.cascade import decomposition, bandpass_filters +from pysteps.cascade import bandpass_filters, decomposition _cascade_methods = dict() _cascade_methods["fft"] = (decomposition.decomposition_fft, decomposition.recompose_fft) diff --git a/pysteps/extrapolation/interface.py b/pysteps/extrapolation/interface.py index 538489f00..73e851a60 100644 --- a/pysteps/extrapolation/interface.py +++ b/pysteps/extrapolation/interface.py @@ -34,7 +34,8 @@ """ import numpy as np -from pysteps.extrapolation import semilagrangian, eulerian_persistence + +from pysteps.extrapolation import eulerian_persistence, semilagrangian def _do_nothing(precip, velocity, timesteps, outval=np.nan, **kwargs): diff --git a/pysteps/feature/blob.py b/pysteps/feature/blob.py index a51717416..ed0234a09 100644 --- a/pysteps/feature/blob.py +++ b/pysteps/feature/blob.py @@ -11,11 +11,10 @@ """ import numpy as np +from scipy.ndimage import gaussian_laplace from pysteps.exceptions import MissingOptionalDependency -from scipy.ndimage import gaussian_laplace - try: from skimage import feature diff --git a/pysteps/feature/interface.py b/pysteps/feature/interface.py index bdf7ee58f..ff1b43b59 100644 --- a/pysteps/feature/interface.py +++ b/pysteps/feature/interface.py @@ -29,9 +29,7 @@ get_method """ -from pysteps.feature import blob -from pysteps.feature import tstorm -from pysteps.feature import shitomasi +from pysteps.feature import blob, shitomasi, tstorm _detection_methods = dict() _detection_methods["blob"] = blob.detection diff --git a/pysteps/feature/tstorm.py b/pysteps/feature/tstorm.py index b3e67f26c..12be7dcc1 100644 --- a/pysteps/feature/tstorm.py +++ b/pysteps/feature/tstorm.py @@ -116,11 +116,11 @@ def detection( """ if not SKIMAGE_IMPORTED: raise MissingOptionalDependency( - "skimage is required for thunderstorm DATing " "but it is not installed" + "skimage is required for thunderstorm DATing but it is not installed" ) if not PANDAS_IMPORTED: raise MissingOptionalDependency( - "pandas is required for thunderstorm DATing " "but it is not installed" + "pandas is required for thunderstorm DATing but it is not installed" ) filt_image = np.zeros(input_image.shape) filt_image[input_image >= minref] = input_image[input_image >= minref] diff --git a/pysteps/io/__init__.py b/pysteps/io/__init__.py index ce89b29fc..785b64956 100644 --- a/pysteps/io/__init__.py +++ b/pysteps/io/__init__.py @@ -4,9 +4,9 @@ forecasts into files. """ -from .interface import get_method, discover_importers, importers_info from .archive import * from .exporters import * from .importers import * +from .interface import discover_importers, get_method, importers_info from .nowcast_importers import * from .readers import * diff --git a/pysteps/io/archive.py b/pysteps/io/archive.py index b50802d26..e4f098bb0 100644 --- a/pysteps/io/archive.py +++ b/pysteps/io/archive.py @@ -11,9 +11,9 @@ find_by_date """ -from datetime import datetime, timedelta import fnmatch import os +from datetime import datetime, timedelta def find_by_date( diff --git a/pysteps/io/exporters.py b/pysteps/io/exporters.py index 7be0b9fd8..b222371bd 100644 --- a/pysteps/io/exporters.py +++ b/pysteps/io/exporters.py @@ -195,7 +195,7 @@ def initialize_forecast_exporter_geotiff( if not GDAL_IMPORTED: raise MissingOptionalDependency( - "gdal package is required for GeoTIFF " "exporters but it is not installed" + "gdal package is required for GeoTIFF exporters but it is not installed" ) if incremental == "member": @@ -456,13 +456,12 @@ def initialize_forecast_exporter_netcdf( if not NETCDF4_IMPORTED: raise MissingOptionalDependency( - "netCDF4 package is required for netcdf " - "exporters but it is not installed" + "netCDF4 package is required for netcdf exporters but it is not installed" ) if not PYPROJ_IMPORTED: raise MissingOptionalDependency( - "pyproj package is required for netcdf " "exporters but it is not installed" + "pyproj package is required for netcdf exporters but it is not installed" ) if incremental not in [None, "timestep", "member"]: @@ -698,8 +697,7 @@ def export_forecast_dataset(field, exporter): if exporter["method"] == "netcdf" and not NETCDF4_IMPORTED: raise MissingOptionalDependency( - "netCDF4 package is required for netcdf " - "exporters but it is not installed" + "netCDF4 package is required for netcdf exporters but it is not installed" ) if exporter["incremental"] is None: diff --git a/pysteps/io/interface.py b/pysteps/io/interface.py index cb57da284..f4f833dc4 100644 --- a/pysteps/io/interface.py +++ b/pysteps/io/interface.py @@ -14,10 +14,10 @@ """ from importlib.metadata import entry_points - -from pysteps.io import importers, exporters, interface from pprint import pprint +from pysteps.io import exporters, importers, interface + _importer_methods = dict( bom_rf3=importers.import_bom_rf3, dwd_hdf5=importers.import_dwd_hdf5, diff --git a/pysteps/io/nowcast_importers.py b/pysteps/io/nowcast_importers.py index cc14c7cd9..13006a7d8 100755 --- a/pysteps/io/nowcast_importers.py +++ b/pysteps/io/nowcast_importers.py @@ -69,9 +69,9 @@ """ import numpy as np +import xarray as xr from pysteps.exceptions import MissingOptionalDependency -import xarray as xr try: import netCDF4 diff --git a/pysteps/motion/farneback.py b/pysteps/motion/farneback.py index 9a6f19c0e..e4b9337df 100644 --- a/pysteps/motion/farneback.py +++ b/pysteps/motion/farneback.py @@ -19,11 +19,12 @@ farneback """ +import time + import numpy as np +import scipy.ndimage as sndi import xarray as xr from numpy.ma.core import MaskedArray -import scipy.ndimage as sndi -import time from pysteps.decorators import check_input_frames from pysteps.exceptions import MissingOptionalDependency diff --git a/pysteps/motion/interface.py b/pysteps/motion/interface.py index 61a8c029c..89e3e686d 100644 --- a/pysteps/motion/interface.py +++ b/pysteps/motion/interface.py @@ -28,10 +28,10 @@ from pysteps.motion.constant import constant from pysteps.motion.darts import DARTS +from pysteps.motion.farneback import farneback from pysteps.motion.lucaskanade import dense_lucaskanade from pysteps.motion.proesmans import proesmans from pysteps.motion.vet import vet -from pysteps.motion.farneback import farneback _methods = dict() _methods["constant"] = constant diff --git a/pysteps/noise/__init__.py b/pysteps/noise/__init__.py index 7be00590d..87499f359 100644 --- a/pysteps/noise/__init__.py +++ b/pysteps/noise/__init__.py @@ -4,5 +4,5 @@ velocity fields. """ +from . import fftgenerators, motion, utils from .interface import get_method -from . import utils, motion, fftgenerators diff --git a/pysteps/noise/interface.py b/pysteps/noise/interface.py index 01d2dbae3..59ee9cc3c 100644 --- a/pysteps/noise/interface.py +++ b/pysteps/noise/interface.py @@ -12,14 +12,14 @@ """ from pysteps.noise.fftgenerators import ( - initialize_param_2d_fft_filter, generate_noise_2d_fft_filter, - initialize_nonparam_2d_fft_filter, - initialize_nonparam_2d_ssft_filter, generate_noise_2d_ssft_filter, + initialize_nonparam_2d_fft_filter, initialize_nonparam_2d_nested_filter, + initialize_nonparam_2d_ssft_filter, + initialize_param_2d_fft_filter, ) -from pysteps.noise.motion import initialize_bps, generate_bps +from pysteps.noise.motion import generate_bps, initialize_bps _noise_methods = dict() diff --git a/pysteps/nowcasts/extrapolation.py b/pysteps/nowcasts/extrapolation.py index 6e45092d2..74f495bf1 100644 --- a/pysteps/nowcasts/extrapolation.py +++ b/pysteps/nowcasts/extrapolation.py @@ -113,7 +113,7 @@ def forecast( def _check_inputs(precip, velocity, timesteps): if precip.ndim != 2: - raise ValueError("The input precipitation must be a " "two-dimensional array") + raise ValueError("The input precipitation must be a two-dimensional array") if velocity.ndim != 3: raise ValueError("Input velocity must be a three-dimensional array") if precip.shape != velocity.shape[1:3]: diff --git a/pysteps/nowcasts/linda.py b/pysteps/nowcasts/linda.py index f7f14135c..245fa1f9c 100644 --- a/pysteps/nowcasts/linda.py +++ b/pysteps/nowcasts/linda.py @@ -40,8 +40,8 @@ import time import warnings -from pysteps.xarray_helpers import convert_output_to_xarray_dataset from pysteps.utils.check_norain import check_norain +from pysteps.xarray_helpers import convert_output_to_xarray_dataset try: import dask @@ -447,8 +447,8 @@ def _compute_ellipse_bbox(phi, sigma1, sigma2, cutoff): def _compute_inverse_acf_mapping(target_dist, target_dist_params, n_intervals=10): """Compute the inverse ACF mapping between two distributions.""" - phi = ( - lambda x1, x2, rho: 1.0 + phi = lambda x1, x2, rho: ( + 1.0 / (2 * np.pi * np.sqrt(1 - rho**2)) * np.exp(-(x1**2 + x2**2 - 2 * rho * x1 * x2) / (2 * (1 - rho**2))) ) @@ -463,10 +463,8 @@ def _compute_inverse_acf_mapping(target_dist, target_dist_params, n_intervals=10 int_range = (-6, 6) for i, rho_1_ in enumerate(rho_1): - f = ( - lambda x1, x2: (cdf_trans(x1) - mu) - * (cdf_trans(x2) - mu) - * phi(x1, x2, rho_1_) + f = lambda x1, x2: ( + (cdf_trans(x1) - mu) * (cdf_trans(x2) - mu) * phi(x1, x2, rho_1_) ) opts = {"epsabs": 1e-8, "epsrel": 1e-8, "limit": 1} rho_2[i] = nquad(f, (int_range, int_range), opts=opts)[0] / (sigma * sigma) diff --git a/pysteps/nowcasts/sprog.py b/pysteps/nowcasts/sprog.py index e3476d79d..470a49c58 100644 --- a/pysteps/nowcasts/sprog.py +++ b/pysteps/nowcasts/sprog.py @@ -20,8 +20,8 @@ from pysteps.nowcasts.utils import compute_percentile_mask, nowcast_main_loop from pysteps.postprocessing import probmatching from pysteps.timeseries import autoregression, correlation -from pysteps.xarray_helpers import convert_output_to_xarray_dataset from pysteps.utils.check_norain import check_norain +from pysteps.xarray_helpers import convert_output_to_xarray_dataset try: import dask diff --git a/pysteps/nowcasts/sseps.py b/pysteps/nowcasts/sseps.py index 066b71286..20f687767 100644 --- a/pysteps/nowcasts/sseps.py +++ b/pysteps/nowcasts/sseps.py @@ -801,7 +801,7 @@ def worker(j): precip_l[ idxm.item(0) : idxm.item(1), idxn.item(0) : idxn.item(1) - ] += (precip_l_ * mask_l) + ] += precip_l_ * mask_l precip_l_ = None ind = M_s > 0 @@ -874,9 +874,9 @@ def worker(j): [t_diff_prev], **extrap_kwargs_, ) - precip_forecast_ep[0][ - precip_forecast_ep[0] < precip_thr - ] = precip_min + precip_forecast_ep[0][precip_forecast_ep[0] < precip_thr] = ( + precip_min + ) precip_forecast_out.append(precip_forecast_ep[0]) t_prev[j] = t_sub diff --git a/pysteps/postprocessing/__init__.py b/pysteps/postprocessing/__init__.py index d11c33cab..5ae4b357f 100644 --- a/pysteps/postprocessing/__init__.py +++ b/pysteps/postprocessing/__init__.py @@ -3,5 +3,5 @@ from . import ensemblestats from .diagnostics import * -from .interface import * from .ensemblestats import * +from .interface import * diff --git a/pysteps/postprocessing/interface.py b/pysteps/postprocessing/interface.py index 10a7dc2c6..b2a47cf7a 100644 --- a/pysteps/postprocessing/interface.py +++ b/pysteps/postprocessing/interface.py @@ -18,11 +18,11 @@ """ import importlib +import warnings from importlib.metadata import entry_points +from pprint import pprint from pysteps.postprocessing import diagnostics, ensemblestats -from pprint import pprint -import warnings _diagnostics_methods = dict() diff --git a/pysteps/scripts/fit_vel_pert_params.py b/pysteps/scripts/fit_vel_pert_params.py index 06e9fcac4..b895f0616 100644 --- a/pysteps/scripts/fit_vel_pert_params.py +++ b/pysteps/scripts/fit_vel_pert_params.py @@ -5,8 +5,9 @@ import argparse import pickle -from matplotlib import pyplot + import numpy as np +from matplotlib import pyplot from scipy.optimize import curve_fit description = ( diff --git a/pysteps/scripts/run_vel_pert_analysis.py b/pysteps/scripts/run_vel_pert_analysis.py index 7990b6298..90548fb53 100644 --- a/pysteps/scripts/run_vel_pert_analysis.py +++ b/pysteps/scripts/run_vel_pert_analysis.py @@ -4,12 +4,13 @@ For a description of the method, see :cite:`BPS2006`.""" import argparse -from datetime import datetime, timedelta import pickle +from datetime import datetime, timedelta + import numpy as np from scipy import linalg as la -from pysteps import io, motion -from pysteps import rcparams + +from pysteps import io, motion, rcparams from pysteps.utils import transformation # TODO: Don't hard-code these. diff --git a/pysteps/tests/test_archive.py b/pysteps/tests/test_archive.py index 56c33e8f2..2b4d882ab 100644 --- a/pysteps/tests/test_archive.py +++ b/pysteps/tests/test_archive.py @@ -1,8 +1,9 @@ # -*- coding: utf-8 -*- -import pytest from datetime import datetime +import pytest + from pysteps.io.archive import _generate_path test_argvalues = [ diff --git a/pysteps/tests/test_blending_clim.py b/pysteps/tests/test_blending_clim.py index 6508a959f..d6eeaa5a9 100644 --- a/pysteps/tests/test_blending_clim.py +++ b/pysteps/tests/test_blending_clim.py @@ -1,16 +1,16 @@ # -*- coding: utf-8 -*- -from datetime import datetime, timedelta -from os.path import join, exists import pickle import random +from datetime import datetime, timedelta +from os.path import exists, join import numpy as np -from numpy.testing import assert_array_equal import pytest +from numpy.testing import assert_array_equal -from pysteps.blending.clim import save_skill, calc_clim_skill +from pysteps.blending.clim import calc_clim_skill, save_skill random.seed(12356) n_cascade_levels = 7 diff --git a/pysteps/tests/test_blending_linear_blending.py b/pysteps/tests/test_blending_linear_blending.py index c0450212e..360f67d7a 100644 --- a/pysteps/tests/test_blending_linear_blending.py +++ b/pysteps/tests/test_blending_linear_blending.py @@ -67,21 +67,21 @@ def test_linear_blending( # entirely constant # Assert that the control time step is in the range of the forecasted time steps - assert controltime <= ( - n_timesteps * timestep - ), "Control time needs to be within reach of forecasts, controltime = {} and n_timesteps = {}".format( - controltime, n_timesteps + assert controltime <= (n_timesteps * timestep), ( + "Control time needs to be within reach of forecasts, controltime = {} and n_timesteps = {}".format( + controltime, n_timesteps + ) ) # Assert that the start time of the blending comes before the end time of the blending - assert ( - start_blending < end_blending - ), "Start time of blending needs to be smaller than end time of blending" + assert start_blending < end_blending, ( + "Start time of blending needs to be smaller than end time of blending" + ) # Assert that the control time is a multiple of the time step - assert ( - not controltime % timestep - ), "Control time needs to be a multiple of the time step" + assert not controltime % timestep, ( + "Control time needs to be a multiple of the time step" + ) # Initialise dummy NWP data if n_models == 0: @@ -184,16 +184,20 @@ def test_linear_blending( n_timesteps, 200, 200, - ), "The shape of the blended array does not have the expected value. The shape is {}".format( - r_blended.shape + ), ( + "The shape of the blended array does not have the expected value. The shape is {}".format( + r_blended.shape + ) ) else: assert r_blended.shape == ( n_timesteps, 200, 200, - ), "The shape of the blended array does not have the expected value. The shape is {}".format( - r_blended.shape + ), ( + "The shape of the blended array does not have the expected value. The shape is {}".format( + r_blended.shape + ) ) # Assert that the blended field at the control time step is equal to @@ -286,12 +290,14 @@ def test_linear_blending_ar_nowcast_3d_precip(): n_timesteps, 32, 32, - ), "The shape of the blended array does not have the expected value. The shape is {}".format( - r_blended.shape + ), ( + "The shape of the blended array does not have the expected value. The shape is {}".format( + r_blended.shape + ) + ) + assert np.all(np.isfinite(r_blended)), ( + "The blended array contains non-finite values" ) - assert np.all( - np.isfinite(r_blended) - ), "The blended array contains non-finite values" def test_linear_blending_timesteps_nowcast_clamp(): @@ -368,12 +374,14 @@ def test_linear_blending_timesteps_nowcast_clamp(): n_timesteps, 32, 32, - ), "The shape of the blended array does not have the expected value. The shape is {}".format( - r_blended.shape + ), ( + "The shape of the blended array does not have the expected value. The shape is {}".format( + r_blended.shape + ) + ) + assert np.all(np.isfinite(r_blended)), ( + "The blended array contains non-finite values after filling with NWP data" ) - assert np.all( - np.isfinite(r_blended) - ), "The blended array contains non-finite values after filling with NWP data" ranked_salience_values = [ @@ -401,6 +409,8 @@ def test_salient_weight( assert ws.shape == ( 200, 200, - ), "The shape of the ranked salience array does not have the expected value. The shape is {}".format( - ws.shape + ), ( + "The shape of the ranked salience array does not have the expected value. The shape is {}".format( + ws.shape + ) ) diff --git a/pysteps/tests/test_blending_pca_ens_kalman_filter.py b/pysteps/tests/test_blending_pca_ens_kalman_filter.py index 06a9d29d5..657a762fe 100644 --- a/pysteps/tests/test_blending_pca_ens_kalman_filter.py +++ b/pysteps/tests/test_blending_pca_ens_kalman_filter.py @@ -226,12 +226,12 @@ def test_pca_enkf_combination( ~np.isfinite(model_dataset[model_precip_var].values) ] = model_dataset[model_precip_var].attrs["zerovalue"] - assert ( - np.any(~np.isfinite(radar_dataset[radar_precip_var].values)) == False - ), "There are still infinite values in the input radar data" - assert ( - np.any(~np.isfinite(model_dataset[model_precip_var].values)) == False - ), "There are still infinite values in the NWP data" + assert np.any(~np.isfinite(radar_dataset[radar_precip_var].values)) == False, ( + "There are still infinite values in the input radar data" + ) + assert np.any(~np.isfinite(model_dataset[model_precip_var].values)) == False, ( + "There are still infinite values in the NWP data" + ) # Initialize radar velocity oflow_method = motion.get_method("LK") @@ -300,25 +300,25 @@ def test_pca_enkf_combination( combined_forecast = combined_forecast_dataset[radar_precip_var].values assert combined_forecast.ndim == 4, "Wrong amount of dimensions in forecast output" - assert ( - combined_forecast.shape[0] == n_ens_members - ), "Wrong amount of output ensemble members in forecast output" - assert ( - combined_forecast.shape[1] == forecast_length // temporal_res_radar + 1 - ), "Wrong amount of output time steps in forecast output" + assert combined_forecast.shape[0] == n_ens_members, ( + "Wrong amount of output ensemble members in forecast output" + ) + assert combined_forecast.shape[1] == forecast_length // temporal_res_radar + 1, ( + "Wrong amount of output time steps in forecast output" + ) # Transform the data back into mm/h combined_forecast_mmh_dataset = converter(combined_forecast_dataset) combined_forecast = combined_forecast_mmh_dataset[radar_precip_var].values - assert ( - combined_forecast.ndim == 4 - ), "Wrong amount of dimensions in converted forecast output" - assert ( - combined_forecast.shape[0] == n_ens_members - ), "Wrong amount of output ensemble members in converted forecast output" - assert ( - combined_forecast.shape[1] == forecast_length // temporal_res_radar + 1 - ), "Wrong amount of output time steps in converted forecast output" + assert combined_forecast.ndim == 4, ( + "Wrong amount of dimensions in converted forecast output" + ) + assert combined_forecast.shape[0] == n_ens_members, ( + "Wrong amount of output ensemble members in converted forecast output" + ) + assert combined_forecast.shape[1] == forecast_length // temporal_res_radar + 1, ( + "Wrong amount of output time steps in converted forecast output" + ) return diff --git a/pysteps/tests/test_blending_skill_scores.py b/pysteps/tests/test_blending_skill_scores.py index cec22be9a..e0d77a278 100644 --- a/pysteps/tests/test_blending_skill_scores.py +++ b/pysteps/tests/test_blending_skill_scores.py @@ -5,10 +5,10 @@ from numpy.testing import assert_array_almost_equal from pysteps.blending.skill_scores import ( - spatial_correlation, - lt_dependent_cor_nwp, - lt_dependent_cor_extrapolation, clim_regr_values, + lt_dependent_cor_extrapolation, + lt_dependent_cor_nwp, + spatial_correlation, ) # Set the climatological correlations values @@ -261,12 +261,12 @@ def test_blending_skill_scores( # Check if the field has the same number of cascade levels as the model # field and as the given n_cascade_levels - assert ( - correlations_t0.shape[0] == mod.shape[0] - ), "Number of cascade levels should be the same as in the model field" - assert ( - correlations_t0.shape[0] == n_cascade_levels - ), "Number of cascade levels should be the same as n_cascade_levels" + assert correlations_t0.shape[0] == mod.shape[0], ( + "Number of cascade levels should be the same as in the model field" + ) + assert correlations_t0.shape[0] == n_cascade_levels, ( + "Number of cascade levels should be the same as n_cascade_levels" + ) # Check if the returned values are as expected assert_array_almost_equal( @@ -285,9 +285,9 @@ def test_blending_skill_scores( n_model=n_model, skill_kwargs=skill_kwargs, ) - assert ( - correlations_clim.shape[0] == n_cascade_levels - ), "Number of cascade levels should be the same as n_cascade_levels" + assert correlations_clim.shape[0] == n_cascade_levels, ( + "Number of cascade levels should be the same as n_cascade_levels" + ) assert_array_almost_equal( correlations_clim, clim_cor_values, @@ -305,12 +305,12 @@ def test_blending_skill_scores( correlations_nwp_lt = lt_dependent_cor_nwp( lt=lt, correlations=correlations_t0, outdir_path="./tmp/" ) - assert ( - correlations_nwp_lt.shape[0] == mod.shape[0] - ), "Number of cascade levels should be the same as in the model field" - assert ( - correlations_nwp_lt.shape[0] == n_cascade_levels - ), "Number of cascade levels should be the same as n_cascade_levels" + assert correlations_nwp_lt.shape[0] == mod.shape[0], ( + "Number of cascade levels should be the same as in the model field" + ) + assert correlations_nwp_lt.shape[0] == n_cascade_levels, ( + "Number of cascade levels should be the same as n_cascade_levels" + ) assert_array_almost_equal( correlations_nwp_lt, expected_cor_nwp_lt, @@ -325,12 +325,12 @@ def test_blending_skill_scores( ) print(correlations_nowcast_lt) - assert ( - correlations_nowcast_lt.shape[0] == mod.shape[0] - ), "Number of cascade levels should be the same as in the model field" - assert ( - correlations_nowcast_lt.shape[0] == n_cascade_levels - ), "Number of cascade levels should be the same as n_cascade_levels" + assert correlations_nowcast_lt.shape[0] == mod.shape[0], ( + "Number of cascade levels should be the same as in the model field" + ) + assert correlations_nowcast_lt.shape[0] == n_cascade_levels, ( + "Number of cascade levels should be the same as n_cascade_levels" + ) assert_array_almost_equal( correlations_nowcast_lt, expected_cor_nowcast_lt, diff --git a/pysteps/tests/test_blending_steps.py b/pysteps/tests/test_blending_steps.py index 6f6390acd..940568748 100644 --- a/pysteps/tests/test_blending_steps.py +++ b/pysteps/tests/test_blending_steps.py @@ -98,27 +98,27 @@ def run_and_assert_forecast( precip_forecast = precip_forecast_dataset[precip_var_forecast].values assert precip_forecast.ndim == 4, "Wrong amount of dimensions in forecast output" - assert ( - precip_forecast.shape[0] == expected_n_ens_members - ), "Wrong amount of output ensemble members in forecast output" - assert ( - precip_forecast.shape[1] == n_timesteps - ), "Wrong amount of output time steps in forecast output" + assert precip_forecast.shape[0] == expected_n_ens_members, ( + "Wrong amount of output ensemble members in forecast output" + ) + assert precip_forecast.shape[1] == n_timesteps, ( + "Wrong amount of output time steps in forecast output" + ) # Transform the data back into mm/h precip_forecast_dataset = converter(precip_forecast_dataset) precip_var_forecast = precip_forecast_dataset.attrs["precip_var"] precip_forecast = precip_forecast_dataset[precip_var_forecast].values - assert ( - precip_forecast.ndim == 4 - ), "Wrong amount of dimensions in converted forecast output" - assert ( - precip_forecast.shape[0] == expected_n_ens_members - ), "Wrong amount of output ensemble members in converted forecast output" - assert ( - precip_forecast.shape[1] == n_timesteps - ), "Wrong amount of output time steps in converted forecast output" + assert precip_forecast.ndim == 4, ( + "Wrong amount of dimensions in converted forecast output" + ) + assert precip_forecast.shape[0] == expected_n_ens_members, ( + "Wrong amount of output ensemble members in converted forecast output" + ) + assert precip_forecast.shape[1] == n_timesteps, ( + "Wrong amount of output time steps in converted forecast output" + ) steps_arg_names = ( @@ -358,12 +358,12 @@ def test_steps_blending( ~np.isfinite(model_dataset[model_precip_var].values) ] = model_dataset[model_precip_var].attrs["zerovalue"] - assert ( - np.any(~np.isfinite(radar_dataset[radar_precip_var].values)) == False - ), "There are still infinite values in the input radar data" - assert ( - np.any(~np.isfinite(model_dataset[radar_precip_var].values)) == False - ), "There are still infinite values in the NWP data" + assert np.any(~np.isfinite(radar_dataset[radar_precip_var].values)) == False, ( + "There are still infinite values in the input radar data" + ) + assert np.any(~np.isfinite(model_dataset[radar_precip_var].values)) == False, ( + "There are still infinite values in the NWP data" + ) ### # Decompose the R_NWP data @@ -686,9 +686,9 @@ def variance_shares(weights_list, has_noise): external_radar, external_nwp = variance_shares(external_weights, has_noise=False) assert normal_nwp.size > 0, "No full-blend weights were captured" - assert ( - normal_nwp.shape == external_nwp.shape - ), "The two paths produced a different number/shape of blend weights" + assert normal_nwp.shape == external_nwp.shape, ( + "The two paths produced a different number/shape of blend weights" + ) # The NWP (and thus radar-side) variance share must be identical between # the standard and external-nowcast paths at every blend call and scale. @@ -737,9 +737,9 @@ def test_steps_blending_external_nowcast_with_noise_weight_distribution(): ) assert len(normal_weights) > 0, "No full-blend weights were captured" - assert len(normal_weights) == len( - external_weights - ), "The two paths produced a different number of blend weights" + assert len(normal_weights) == len(external_weights), ( + "The two paths produced a different number of blend weights" + ) # Both paths use the full three-component weights, so they must match # exactly (extrapolation, NWP and noise weights alike). diff --git a/pysteps/tests/test_blending_utils.py b/pysteps/tests/test_blending_utils.py index 15312c36c..ae6b3b2a7 100644 --- a/pysteps/tests/test_blending_utils.py +++ b/pysteps/tests/test_blending_utils.py @@ -1,12 +1,13 @@ # -*- coding: utf-8 -*- -from datetime import datetime, timezone import os +from datetime import datetime, timezone import numpy as np import pytest -from numpy.testing import assert_array_almost_equal import xarray as xr +from numpy.testing import assert_array_almost_equal + import pysteps from pysteps.blending.utils import ( blend_cascades, diff --git a/pysteps/tests/test_cascade.py b/pysteps/tests/test_cascade.py index d9f92b737..bbf954062 100644 --- a/pysteps/tests/test_cascade.py +++ b/pysteps/tests/test_cascade.py @@ -8,8 +8,7 @@ import pysteps from pysteps import nowcasts -from pysteps.cascade.bandpass_filters import filter_gaussian -from pysteps.cascade.bandpass_filters import filter_uniform +from pysteps.cascade.bandpass_filters import filter_gaussian, filter_uniform from pysteps.cascade.decomposition import decomposition_fft, recompose_fft from pysteps.tests.helpers import smart_assert diff --git a/pysteps/tests/test_datasets.py b/pysteps/tests/test_datasets.py index 93eefaaaa..f35cb57b8 100644 --- a/pysteps/tests/test_datasets.py +++ b/pysteps/tests/test_datasets.py @@ -7,8 +7,8 @@ import pysteps from pysteps.datasets import ( - download_pysteps_data, create_default_pystepsrc, + download_pysteps_data, load_dataset, ) from pysteps.exceptions import DirectoryNotEmpty diff --git a/pysteps/tests/test_downscaling_rainfarm.py b/pysteps/tests/test_downscaling_rainfarm.py index aeb6deba5..74bd7a7db 100644 --- a/pysteps/tests/test_downscaling_rainfarm.py +++ b/pysteps/tests/test_downscaling_rainfarm.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- -import pytest import numpy as np +import pytest + from pysteps import downscaling from pysteps.tests.helpers import get_precipitation_fields -from pysteps.utils import aggregate_fields_space, square_domain, aggregate_fields +from pysteps.utils import aggregate_fields, aggregate_fields_space, square_domain @pytest.fixture(scope="module") diff --git a/pysteps/tests/test_exporters.py b/pysteps/tests/test_exporters.py index b5e4a0890..12afb132a 100644 --- a/pysteps/tests/test_exporters.py +++ b/pysteps/tests/test_exporters.py @@ -5,8 +5,8 @@ from datetime import datetime import numpy as np -import xarray as xr import pytest +import xarray as xr from numpy.testing import assert_array_almost_equal from pysteps.io import import_netcdf_pysteps @@ -56,9 +56,7 @@ def test_get_geotiff_filename(): file_name = _get_geotiff_filename( "test/path", start_date, n_timesteps, timestep, timestep_index ) - expected = ( - f"test/path_201909082022_" f"{(timestep_index + 1) * timestep:03d}.tif" - ) + expected = f"test/path_201909082022_{(timestep_index + 1) * timestep:03d}.tif" assert expected == file_name diff --git a/pysteps/tests/test_feature.py b/pysteps/tests/test_feature.py index 084744b29..60ea8d539 100644 --- a/pysteps/tests/test_feature.py +++ b/pysteps/tests/test_feature.py @@ -1,5 +1,6 @@ -import pytest import numpy as np +import pytest + from pysteps import feature from pysteps.tests.helpers import get_precipitation_fields diff --git a/pysteps/tests/test_feature_tstorm.py b/pysteps/tests/test_feature_tstorm.py index 129206ced..51e0711a6 100644 --- a/pysteps/tests/test_feature_tstorm.py +++ b/pysteps/tests/test_feature_tstorm.py @@ -2,8 +2,8 @@ import pytest from pysteps.feature.tstorm import detection -from pysteps.utils import to_reflectivity from pysteps.tests.helpers import get_precipitation_fields +from pysteps.utils import to_reflectivity try: from pandas import DataFrame diff --git a/pysteps/tests/test_interfaces.py b/pysteps/tests/test_interfaces.py index 1f1c2ef6d..b5c088140 100644 --- a/pysteps/tests/test_interfaces.py +++ b/pysteps/tests/test_interfaces.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- from functools import partial + import numpy import pytest @@ -42,7 +43,7 @@ def test_nowcasts_interface(): def test_cascade_interface(): """Test the cascade module interface.""" - from pysteps.cascade import decomposition, bandpass_filters + from pysteps.cascade import bandpass_filters, decomposition method_getter = pysteps.cascade.interface.get_method @@ -60,9 +61,7 @@ def test_extrapolation_interface(): """Test the extrapolation module interface.""" from pysteps import extrapolation - from pysteps.extrapolation import semilagrangian - - from pysteps.extrapolation import eulerian_persistence + from pysteps.extrapolation import eulerian_persistence, semilagrangian from pysteps.extrapolation.interface import _do_nothing as do_nothing method_getter = extrapolation.interface.get_method @@ -95,20 +94,21 @@ def test_extrapolation_interface(): def test_io_interface(): """Test the io module interface.""" - from pysteps.io import import_bom_rf3 - from pysteps.io import import_fmi_geotiff - from pysteps.io import import_fmi_pgm - from pysteps.io import import_knmi_hdf5 - from pysteps.io import import_mch_gif - from pysteps.io import import_mch_hdf5 - from pysteps.io import import_mch_metranet - from pysteps.io import import_mrms_grib - from pysteps.io import import_opera_hdf5 - from pysteps.io import import_saf_crri - - from pysteps.io import initialize_forecast_exporter_geotiff - from pysteps.io import initialize_forecast_exporter_kineros - from pysteps.io import initialize_forecast_exporter_netcdf + from pysteps.io import ( + import_bom_rf3, + import_fmi_geotiff, + import_fmi_pgm, + import_knmi_hdf5, + import_mch_gif, + import_mch_hdf5, + import_mch_metranet, + import_mrms_grib, + import_opera_hdf5, + import_saf_crri, + initialize_forecast_exporter_geotiff, + initialize_forecast_exporter_kineros, + initialize_forecast_exporter_netcdf, + ) # Test importers valid_names_func_pair = [ @@ -157,7 +157,7 @@ def test_postprocessing_interface(): """Test the postprocessing module interface.""" # ensemblestats pre-installed methods - from pysteps.postprocessing import mean, excprob, banddepth + from pysteps.postprocessing import banddepth, excprob, mean # Test ensemblestats valid_names_func_pair = [ @@ -247,15 +247,14 @@ def test_noise_interface(): """Test the noise module interface.""" from pysteps.noise.fftgenerators import ( - initialize_param_2d_fft_filter, generate_noise_2d_fft_filter, - initialize_nonparam_2d_fft_filter, - initialize_nonparam_2d_ssft_filter, generate_noise_2d_ssft_filter, + initialize_nonparam_2d_fft_filter, initialize_nonparam_2d_nested_filter, + initialize_nonparam_2d_ssft_filter, + initialize_param_2d_fft_filter, ) - - from pysteps.noise.motion import initialize_bps, generate_bps + from pysteps.noise.motion import generate_bps, initialize_bps method_getter = pysteps.noise.interface.get_method @@ -287,8 +286,8 @@ def test_nowcasts_interface(): lagrangian_probability, linda, sprog, - steps, sseps, + steps, ) method_getter = pysteps.nowcasts.interface.get_method @@ -313,16 +312,18 @@ def test_nowcasts_interface(): def test_utils_interface(): """Test utils module interface.""" - from pysteps.utils import arrays - from pysteps.utils import cleansing - from pysteps.utils import conversion - from pysteps.utils import dimension - from pysteps.utils import images - from pysteps.utils import interpolate - from pysteps.utils import reprojection - from pysteps.utils import spectral - from pysteps.utils import tapering - from pysteps.utils import transformation + from pysteps.utils import ( + arrays, + cleansing, + conversion, + dimension, + images, + interpolate, + reprojection, + spectral, + tapering, + transformation, + ) method_getter = pysteps.utils.interface.get_method @@ -378,9 +379,7 @@ def test_downscaling_interface(): def test_feature_interface(): """Test the feature detection module interface.""" - from pysteps.feature import blob - from pysteps.feature import tstorm - from pysteps.feature import shitomasi + from pysteps.feature import blob, shitomasi, tstorm method_getter = pysteps.feature.interface.get_method @@ -397,8 +396,7 @@ def test_feature_interface(): def test_tracking_interface(): """Test the feature tracking module interface.""" - from pysteps.tracking import lucaskanade - from pysteps.tracking import tdating + from pysteps.tracking import lucaskanade, tdating method_getter = pysteps.tracking.interface.get_method diff --git a/pysteps/tests/test_io_opera_hdf5.py b/pysteps/tests/test_io_opera_hdf5.py index 3a93c6ba0..23da55109 100644 --- a/pysteps/tests/test_io_opera_hdf5.py +++ b/pysteps/tests/test_io_opera_hdf5.py @@ -77,7 +77,7 @@ def test_io_import_opera_hdf5_nimbus_rain_accum_shape(): test_odyssey_attrs = [ (precip_odyssey.attrs["projection"], expected_proj, None), (float(precip_odyssey.lon.isel(x=0, y=-1).values), -10.4268122372, 1e-10), - (float(precip_odyssey.lat.isel(x=0, y=-1).values), 31.7575305202, 1e-10), + (float(precip_odyssey.lat.isel(x=0, y=-1).values), 31.7575305202, 1e-6), (float(precip_odyssey.lon.isel(x=-1, y=0).values), 57.7778944303, 1e-10), (float(precip_odyssey.lat.isel(x=-1, y=0).values), 67.6204665961, 1e-10), (precip_odyssey.x.isel(x=0).values, 999.999583891, 1e-6), @@ -107,7 +107,7 @@ def test_io_import_opera_hdf5_odyssey_dataset_attrs(variable, expected, toleranc test_cirrus_attrs = [ (precip_cirrus.attrs["projection"], expected_proj, None), (float(precip_cirrus.lon.isel(x=0, y=-1).values), -10.4306947565, 1e-10), - (float(precip_cirrus.lat.isel(x=0, y=-1).values), 31.7518730246, 1e-10), + (float(precip_cirrus.lat.isel(x=0, y=-1).values), 31.7518730246, 1e-6), (float(precip_cirrus.lon.isel(x=-1, y=0).values), 57.7949292793, 1e-10), (float(precip_cirrus.lat.isel(x=-1, y=0).values), 67.6207527344, 1e-10), (precip_cirrus.x.isel(x=0).values, 499.99972864, 1e-6), @@ -138,7 +138,7 @@ def test_io_import_opera_hdf5_cirrus_dataset_attrs(variable, expected, tolerance test_nimbus_rain_rate_attrs = [ (precip_nimbus_rain_rate.attrs["projection"], expected_proj, None), (float(precip_nimbus_rain_rate.lon.isel(x=0, y=-1).values), -10.4268354001, 1e-10), - (float(precip_nimbus_rain_rate.lat.isel(x=0, y=-1).values), 31.7575151548, 1e-10), + (float(precip_nimbus_rain_rate.lat.isel(x=0, y=-1).values), 31.7575151548, 1e-6), (float(precip_nimbus_rain_rate.lon.isel(x=-1, y=0).values), 57.7778328845, 1e-10), (float(precip_nimbus_rain_rate.lat.isel(x=-1, y=0).values), 67.6204748496, 1e-10), (precip_nimbus_rain_rate.x.isel(x=0).values, 997.46972871, 1e-6), @@ -169,7 +169,7 @@ def test_io_import_opera_hdf5_nimbus_rain_rate_dataset_attrs( test_nimbus_rain_accum_attrs = [ (precip_nimbus_rain_accum.attrs["projection"], expected_proj, None), (float(precip_nimbus_rain_accum.lon.isel(x=0, y=-1).values), -10.4268354001, 1e-10), - (float(precip_nimbus_rain_accum.lat.isel(x=0, y=-1).values), 31.7575151548, 1e-10), + (float(precip_nimbus_rain_accum.lat.isel(x=0, y=-1).values), 31.7575151548, 1e-6), (float(precip_nimbus_rain_accum.lon.isel(x=-1, y=0).values), 57.7778328845, 1e-10), (float(precip_nimbus_rain_accum.lat.isel(x=-1, y=0).values), 67.6204748496, 1e-10), (precip_nimbus_rain_accum.x.isel(x=0).values, 997.46972871, 1e-6), diff --git a/pysteps/tests/test_io_readers.py b/pysteps/tests/test_io_readers.py index 9417e931e..b9e9337f4 100644 --- a/pysteps/tests/test_io_readers.py +++ b/pysteps/tests/test_io_readers.py @@ -1,8 +1,8 @@ from datetime import datetime import numpy as np - import xarray as xr + from pysteps.tests.helpers import get_precipitation_fields diff --git a/pysteps/tests/test_io_saf_crri.py b/pysteps/tests/test_io_saf_crri.py index b35f5f066..a338a530c 100644 --- a/pysteps/tests/test_io_saf_crri.py +++ b/pysteps/tests/test_io_saf_crri.py @@ -52,8 +52,7 @@ def test_io_import_saf_crri_extent(extent, expected_extent, expected_shape, tole expected_proj = ( - "+proj=geos +a=6378137.000000 +b=6356752.300000 " - "+lon_0=0.000000 +h=35785863.000000" + "+proj=geos +a=6378137.000000 +b=6356752.300000 +lon_0=0.000000 +h=35785863.000000" ) test_geodata_crri = [ diff --git a/pysteps/tests/test_motion_farneback.py b/pysteps/tests/test_motion_farneback.py index 148c2d59c..cadea6f54 100644 --- a/pysteps/tests/test_motion_farneback.py +++ b/pysteps/tests/test_motion_farneback.py @@ -1,9 +1,9 @@ -import pytest import numpy as np +import pytest import xarray as xr -from pysteps.motion import farneback from pysteps.exceptions import MissingOptionalDependency +from pysteps.motion import farneback from pysteps.tests.helpers import get_precipitation_fields fb_arg_names = ( diff --git a/pysteps/tests/test_noise_motion.py b/pysteps/tests/test_noise_motion.py index 4c870298b..e9a2f275c 100644 --- a/pysteps/tests/test_noise_motion.py +++ b/pysteps/tests/test_noise_motion.py @@ -4,10 +4,12 @@ import pytest from numpy.testing import assert_array_almost_equal -from pysteps.noise.motion import generate_bps -from pysteps.noise.motion import get_default_params_bps_par -from pysteps.noise.motion import get_default_params_bps_perp -from pysteps.noise.motion import initialize_bps +from pysteps.noise.motion import ( + generate_bps, + get_default_params_bps_par, + get_default_params_bps_perp, + initialize_bps, +) def test_noise_motion_get_default_params_bps_par(): diff --git a/pysteps/tests/test_nowcasts_linda.py b/pysteps/tests/test_nowcasts_linda.py index 9d011ce6b..f5efb3656 100644 --- a/pysteps/tests/test_nowcasts_linda.py +++ b/pysteps/tests/test_nowcasts_linda.py @@ -270,9 +270,9 @@ def callback(array): assert precip_netcdf.shape[2:] == field_shape, "Wrong field shape" # assert that the saved output is the same as the original output - assert np.allclose( - precip_netcdf, precip_output, equal_nan=True - ), "Wrong output values" + assert np.allclose(precip_netcdf, precip_output, equal_nan=True), ( + "Wrong output values" + ) # assert that leadtimes and timestamps are as expected td = timedelta(minutes=timestep) diff --git a/pysteps/tests/test_nowcasts_steps.py b/pysteps/tests/test_nowcasts_steps.py index 1baa6db12..8f8bb454a 100644 --- a/pysteps/tests/test_nowcasts_steps.py +++ b/pysteps/tests/test_nowcasts_steps.py @@ -201,9 +201,9 @@ def callback(array): assert precip_netcdf.shape[2:] == field_shape, "Wrong field shape" # assert that the saved output is the same as the original output - assert np.allclose( - precip_netcdf, precip_output, equal_nan=True - ), "Wrong output values" + assert np.allclose(precip_netcdf, precip_output, equal_nan=True), ( + "Wrong output values" + ) # assert that leadtimes and timestamps are as expected td = timedelta(minutes=timestep) @@ -229,26 +229,26 @@ def run_and_assert_forecast( precip_forecast = dataset_forecast[precip_var].values assert precip_forecast.ndim == 4, "Wrong amount of dimensions in forecast output" - assert ( - precip_forecast.shape[0] == expected_n_ens_members - ), "Wrong amount of output ensemble members in forecast output" - assert ( - precip_forecast.shape[1] == n_timesteps - ), "Wrong amount of output time steps in forecast output" + assert precip_forecast.shape[0] == expected_n_ens_members, ( + "Wrong amount of output ensemble members in forecast output" + ) + assert precip_forecast.shape[1] == n_timesteps, ( + "Wrong amount of output time steps in forecast output" + ) # Transform the data back into mm/h dataset_forecast = converter(dataset_forecast) precip_forecast = dataset_forecast[precip_var].values - assert ( - precip_forecast.ndim == 4 - ), "Wrong amount of dimensions in converted forecast output" - assert ( - precip_forecast.shape[0] == expected_n_ens_members - ), "Wrong amount of output ensemble members in converted forecast output" - assert ( - precip_forecast.shape[1] == n_timesteps - ), "Wrong amount of output time steps in converted forecast output" + assert precip_forecast.ndim == 4, ( + "Wrong amount of dimensions in converted forecast output" + ) + assert precip_forecast.shape[0] == expected_n_ens_members, ( + "Wrong amount of output ensemble members in converted forecast output" + ) + assert precip_forecast.shape[1] == n_timesteps, ( + "Wrong amount of output time steps in converted forecast output" + ) @pytest.mark.parametrize(steps_arg_names, steps_arg_values) @@ -353,9 +353,9 @@ def test_steps_nowcast( metadata["zerovalue"] ) - assert ( - np.any(~np.isfinite(dataset_input[precip_var].values)) == False - ), "There are still infinite values in the input radar data" + assert np.any(~np.isfinite(dataset_input[precip_var].values)) == False, ( + "There are still infinite values in the input radar data" + ) ### # Determine the velocity fields diff --git a/pysteps/tests/test_paramsrc.py b/pysteps/tests/test_paramsrc.py index f47afa917..5c61f4232 100644 --- a/pysteps/tests/test_paramsrc.py +++ b/pysteps/tests/test_paramsrc.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- import os - from tempfile import NamedTemporaryFile import pysteps diff --git a/pysteps/tests/test_plugins_support.py b/pysteps/tests/test_plugins_support.py index df3d098f4..914c8fa38 100644 --- a/pysteps/tests/test_plugins_support.py +++ b/pysteps/tests/test_plugins_support.py @@ -6,17 +6,19 @@ """ import os -import pytest import subprocess import sys import tempfile +import pytest + __ = pytest.importorskip("cookiecutter") from cookiecutter.main import cookiecutter PLUGIN_TEMPLATE_URL = "https://github.com/pysteps/cookiecutter-pysteps-plugin" from contextlib import contextmanager + from pysteps import io, postprocessing diff --git a/pysteps/tests/test_postprocessing_ensemblestats.py b/pysteps/tests/test_postprocessing_ensemblestats.py index 8e266cfe7..fc81173e5 100644 --- a/pysteps/tests/test_postprocessing_ensemblestats.py +++ b/pysteps/tests/test_postprocessing_ensemblestats.py @@ -4,7 +4,7 @@ import pytest from numpy.testing import assert_array_almost_equal -from pysteps.postprocessing.ensemblestats import excprob, mean, banddepth +from pysteps.postprocessing.ensemblestats import banddepth, excprob, mean # CREATE DATASETS TO TEST diff --git a/pysteps/tests/test_utils_pca.py b/pysteps/tests/test_utils_pca.py index 4b1fcb09d..07902f606 100644 --- a/pysteps/tests/test_utils_pca.py +++ b/pysteps/tests/test_utils_pca.py @@ -1,7 +1,8 @@ # -*- coding: utf-8 -*- -import pytest import numpy as np +import pytest + from pysteps.utils import pca pca_arg_values = ( diff --git a/pysteps/tests/test_utils_reprojection.py b/pysteps/tests/test_utils_reprojection.py index c5f1fa032..113f8a565 100644 --- a/pysteps/tests/test_utils_reprojection.py +++ b/pysteps/tests/test_utils_reprojection.py @@ -4,8 +4,8 @@ import pytest import xarray as xr -from pysteps.utils import reprojection as rpj from pysteps.tests.helpers import get_precipitation_fields +from pysteps.utils import reprojection as rpj def build_precip_dataset( @@ -151,15 +151,15 @@ def test_utils_reproject_grids(precip_dataset, nwp_dataset): nwp_dataarray = nwp_dataset[nwp_dataset.attrs["precip_var"]] precip_dataarray = precip_dataset[precip_dataset.attrs["precip_var"]] # The tests - assert ( - nwp_dataset_reproj_dataarray.shape[0] == nwp_dataarray.shape[0] - ), "Time dimension has not the same length as source" - assert ( - nwp_dataset_reproj_dataarray.shape[1] == precip_dataarray.shape[1] - ), "y dimension has not the same length as radar composite" - assert ( - nwp_dataset_reproj_dataarray.shape[2] == precip_dataarray.shape[2] - ), "x dimension has not the same length as radar composite" + assert nwp_dataset_reproj_dataarray.shape[0] == nwp_dataarray.shape[0], ( + "Time dimension has not the same length as source" + ) + assert nwp_dataset_reproj_dataarray.shape[1] == precip_dataarray.shape[1], ( + "y dimension has not the same length as radar composite" + ) + assert nwp_dataset_reproj_dataarray.shape[2] == precip_dataarray.shape[2], ( + "x dimension has not the same length as radar composite" + ) assert float(nwp_dataset_reproj_dataarray.x.isel(x=0).values) == float( precip_dataarray.x.isel(x=0).values diff --git a/pysteps/tests/test_utils_spectral.py b/pysteps/tests/test_utils_spectral.py index 82eb4e40f..62d676927 100644 --- a/pysteps/tests/test_utils_spectral.py +++ b/pysteps/tests/test_utils_spectral.py @@ -1,5 +1,6 @@ import numpy as np import pytest + from pysteps.utils import spectral _rapsd_input_fields = [ diff --git a/pysteps/timeseries/autoregression.py b/pysteps/timeseries/autoregression.py index 7ce75e81f..bd0f3c734 100644 --- a/pysteps/timeseries/autoregression.py +++ b/pysteps/timeseries/autoregression.py @@ -23,9 +23,9 @@ """ import numpy as np -from scipy.special import binom from scipy import linalg as la from scipy import ndimage +from scipy.special import binom def adjust_lag2_corrcoef1(gamma_1, gamma_2): @@ -217,7 +217,7 @@ def estimate_ar_params_ols( if check_stationarity: if not test_ar_stationarity(phi): raise RuntimeError( - "Error in estimate_ar_params_yw: " "nonstationary AR(p) process" + "Error in estimate_ar_params_yw: nonstationary AR(p) process" ) if d == 1: @@ -452,7 +452,7 @@ def estimate_ar_params_yw(gamma, d=0, check_stationarity=True): if check_stationarity: if not test_ar_stationarity(phi): raise RuntimeError( - "Error in estimate_ar_params_yw: " "nonstationary AR(p) process" + "Error in estimate_ar_params_yw: nonstationary AR(p) process" ) # Estimate PHI_{j,0} as phi_pert. See Pulkkinen et al. (2019), eq. 6. @@ -660,7 +660,7 @@ def estimate_var_params_ols( if np.any(np.abs(r) > 0.999): raise RuntimeError( - "Error in estimate_var_params_ols: " "nonstationary VAR(p) process" + "Error in estimate_var_params_ols: nonstationary VAR(p) process" ) if d == 1: @@ -925,7 +925,7 @@ def estimate_var_params_yw(gamma, d=0, check_stationarity=True): if check_stationarity: if not test_var_stationarity(phi): raise RuntimeError( - "Error in estimate_var_params_yw: " "nonstationary VAR(p) process" + "Error in estimate_var_params_yw: nonstationary VAR(p) process" ) if d == 1: diff --git a/pysteps/timeseries/correlation.py b/pysteps/timeseries/correlation.py index 4fa3fe8ef..2cbde4f92 100644 --- a/pysteps/timeseries/correlation.py +++ b/pysteps/timeseries/correlation.py @@ -15,6 +15,7 @@ import numpy as np from scipy import ndimage + from pysteps.utils import spectral diff --git a/pysteps/tracking/interface.py b/pysteps/tracking/interface.py index 1359bd851..8c57bbbff 100644 --- a/pysteps/tracking/interface.py +++ b/pysteps/tracking/interface.py @@ -12,8 +12,7 @@ get_method """ -from pysteps.tracking import lucaskanade -from pysteps.tracking import tdating +from pysteps.tracking import lucaskanade, tdating _tracking_methods = dict() _tracking_methods["lucaskanade"] = lucaskanade.track_features diff --git a/pysteps/utils/__init__.py b/pysteps/utils/__init__.py index a1a37c393..f60af9b9f 100644 --- a/pysteps/utils/__init__.py +++ b/pysteps/utils/__init__.py @@ -4,10 +4,10 @@ from .cleansing import * from .conversion import * from .dimension import * +from .fft import * from .images import * from .interface import get_method from .interpolate import * -from .fft import * from .pca import * from .reprojection import * from .spectral import * diff --git a/pysteps/utils/check_norain.py b/pysteps/utils/check_norain.py index 109c81968..25833c4bf 100644 --- a/pysteps/utils/check_norain.py +++ b/pysteps/utils/check_norain.py @@ -150,6 +150,6 @@ def check_previous_radar_obs(precip, ar_order, check_norain_kwargs=None): if precip.shape[0] - 1 < ar_order: # Give a warning print( - f"[WARNING] Radar input only with {precip.shape[0]} non-zero time steps and ar_order set to {precip.shape[0]-1}." + f"[WARNING] Radar input only with {precip.shape[0]} non-zero time steps and ar_order set to {precip.shape[0] - 1}." ) return precip, min(ar_order, precip.shape[0] - 1) diff --git a/pysteps/utils/cleansing.py b/pysteps/utils/cleansing.py index 7c4276c57..ecd192f7a 100644 --- a/pysteps/utils/cleansing.py +++ b/pysteps/utils/cleansing.py @@ -1,249 +1,249 @@ -# -*- coding: utf-8 -*- -""" -pysteps.utils.cleansing -======================= - -Data cleansing routines for pysteps. - -.. autosummary:: - :toctree: ../generated/ - - decluster - detect_outliers -""" - -import warnings - -import numpy as np -import scipy.spatial - - -def decluster(coord, input_array, scale, min_samples=1, verbose=False): - """ - Decluster a set of sparse data points by aggregating, that is, taking - the median value of all values lying within a certain distance (i.e., a - cluster). - - Parameters - ---------- - coord: array_like - Array of shape (n, d) containing the coordinates of the input data into - a space of *d* dimensions. - input_array: array_like - Array of shape (n) or (n, m), where *n* is the number of samples and - *m* the number of variables. - All values in ``input_array`` are required to have finite values. - scale: float or array_like - The ``scale`` parameter in the same units of ``coord``. - It can be a scalar or an array_like of shape (d). - Data points within the declustering ``scale`` are aggregated. - min_samples: int, optional - The minimum number of samples for computing the median within a given - cluster. - verbose: bool, optional - Print out information. - - Returns - ------- - out: tuple of ndarrays - A two-element tuple (``out_coord``, ``output_array``) containing the - declustered coordinates (l, d) and input array (l, m), where *l* is - the new number of samples with *l* <= *n*. - """ - - coord = np.copy(coord) - input_array = np.copy(input_array) - - # check inputs - if np.any(~np.isfinite(input_array)): - raise ValueError("input_array contains non-finite values") - - if input_array.ndim == 1: - nvar = 1 - input_array = input_array[:, None] - elif input_array.ndim == 2: - nvar = input_array.shape[1] - else: - raise ValueError( - "input_array must have 1 (n) or 2 dimensions (n, m), but it has %i" - % input_array.ndim - ) - - if coord.ndim != 2: - raise ValueError( - "coord must have 2 dimensions (n, d), but it has %i" % coord.ndim - ) - if coord.shape[0] != input_array.shape[0]: - raise ValueError( - "the number of samples in the input_array does not match the " - + "number of coordinates %i!=%i" % (input_array.shape[0], coord.shape[0]) - ) - - if np.isscalar(scale): - scale = float(scale) - else: - scale = np.copy(scale) - if scale.ndim != 1: - raise ValueError( - "scale must have 1 dimension (d), but it has %i" % scale.ndim - ) - if scale.shape[0] != coord.shape[1]: - raise ValueError( - "scale must have %i elements, but it has %i" - % (coord.shape[1], scale.shape[0]) - ) - scale = scale[None, :] - - # reduce original coordinates - coord_ = np.floor(coord / scale) - - # keep only unique pairs of the reduced coordinates - ucoord_ = np.unique(coord_, axis=0) - - # loop through these unique values and average data points which belong to - # the same cluster - dinput = np.empty(shape=(0, nvar)) - dcoord = np.empty(shape=(0, coord.shape[1])) - for i in range(ucoord_.shape[0]): - idx = np.all(coord_ == ucoord_[i, :], axis=1) - npoints = np.sum(idx) - if npoints >= min_samples: - dinput = np.append( - dinput, np.median(input_array[idx, :], axis=0)[None, :], axis=0 - ) - dcoord = np.append( - dcoord, np.median(coord[idx, :], axis=0)[None, :], axis=0 - ) - - if verbose: - print("--- %i samples left after declustering ---" % dinput.shape[0]) - - return dcoord, dinput - - -def detect_outliers(input_array, thr, coord=None, k=None, verbose=False): - """ - Detect outliers in a (multivariate and georeferenced) dataset. - - Assume a (multivariate) Gaussian distribution and detect outliers based on - the number of standard deviations from the mean. - - If spatial information is provided through coordinates, the outlier - detection can be localized by considering only the k-nearest neighbours - when computing the local mean and standard deviation. - - Parameters - ---------- - input_array: array_like - Array of shape (n) or (n, m), where *n* is the number of samples and - *m* the number of variables. If *m* > 1, the Mahalanobis distance - is used. - All values in ``input_array`` are required to have finite values. - thr: float - The number of standard deviations from the mean used to define an outlier. - coord: array_like or None, optional - Array of shape (n, d) containing the coordinates of the input data into - a space of *d* dimensions. - Passing ``coord`` requires that ``k`` is not None. - k: int or None, optional - The number of nearest neighbours used to localize the outlier - detection. If set to None (the default), it employs all the data points (global - detection). Setting ``k`` requires that ``coord`` is not None. - verbose: bool, optional - Print out information. - - Returns - ------- - out: array_like - A 1-D boolean array of shape (n) with True values indicating the outliers - detected in ``input_array``. - """ - - input_array = np.copy(input_array) - - if np.any(~np.isfinite(input_array)): - raise ValueError("input_array contains non-finite values") - - if input_array.ndim == 1: - nsamples = input_array.size - nvar = 1 - elif input_array.ndim == 2: - nsamples = input_array.shape[0] - nvar = input_array.shape[1] - else: - raise ValueError( - f"input_array must have 1 (n) or 2 dimensions (n, m), " - f"but it has {input_array.ndim}" - ) - - if nsamples < 2: - return np.zeros(nsamples, dtype=bool) - - if coord is not None and k is not None: - coord = np.copy(coord) - if coord.ndim == 1: - coord = coord[:, None] - - elif coord.ndim > 2: - raise ValueError( - "coord must have 2 dimensions (n, d)," f"but it has {coord.ndim}" - ) - - if coord.shape[0] != nsamples: - raise ValueError( - "the number of samples in input_array does not match the " - f"number of coordinates {nsamples}!={coord.shape[0]}" - ) - - k = np.min((nsamples, k + 1)) - - # global - - if k is None or coord is None: - if nvar == 1: - # univariate - zdata = np.abs(input_array - np.mean(input_array)) / np.std(input_array) - outliers = zdata > thr - else: - # multivariate (mahalanobis distance) - zdata = input_array - np.mean(input_array, axis=0) - V = np.cov(zdata.T) - try: - VI = np.linalg.inv(V) - MD = np.sqrt(np.dot(np.dot(zdata, VI), zdata.T).diagonal()) - except np.linalg.LinAlgError as err: - warnings.warn(f"{err} during outlier detection") - MD = np.zeros(nsamples) - outliers = MD > thr - - # local - else: - tree = scipy.spatial.cKDTree(coord) - __, inds = tree.query(coord, k=k) - outliers = np.empty(shape=0, dtype=bool) - for i in range(inds.shape[0]): - if nvar == 1: - # univariate - thisdata = input_array[i] - neighbours = input_array[inds[i, 1:]] - thiszdata = np.abs(thisdata - np.mean(neighbours)) / np.std(neighbours) - outliers = np.append(outliers, thiszdata > thr) - else: - # multivariate (mahalanobis distance) - thisdata = input_array[i, :] - neighbours = input_array[inds[i, 1:], :].copy() - thiszdata = thisdata - np.mean(neighbours, axis=0) - neighbours = neighbours - np.mean(neighbours, axis=0) - V = np.cov(neighbours.T) - try: - VI = np.linalg.inv(V) - MD = np.sqrt(np.dot(np.dot(thiszdata, VI), thiszdata.T)) - except np.linalg.LinAlgError as err: - warnings.warn(f"{err} during outlier detection") - MD = 0 - outliers = np.append(outliers, MD > thr) - - if verbose: - print(f"--- {np.sum(outliers)} outliers detected ---") - - return outliers +# -*- coding: utf-8 -*- +""" +pysteps.utils.cleansing +======================= + +Data cleansing routines for pysteps. + +.. autosummary:: + :toctree: ../generated/ + + decluster + detect_outliers +""" + +import warnings + +import numpy as np +import scipy.spatial + + +def decluster(coord, input_array, scale, min_samples=1, verbose=False): + """ + Decluster a set of sparse data points by aggregating, that is, taking + the median value of all values lying within a certain distance (i.e., a + cluster). + + Parameters + ---------- + coord: array_like + Array of shape (n, d) containing the coordinates of the input data into + a space of *d* dimensions. + input_array: array_like + Array of shape (n) or (n, m), where *n* is the number of samples and + *m* the number of variables. + All values in ``input_array`` are required to have finite values. + scale: float or array_like + The ``scale`` parameter in the same units of ``coord``. + It can be a scalar or an array_like of shape (d). + Data points within the declustering ``scale`` are aggregated. + min_samples: int, optional + The minimum number of samples for computing the median within a given + cluster. + verbose: bool, optional + Print out information. + + Returns + ------- + out: tuple of ndarrays + A two-element tuple (``out_coord``, ``output_array``) containing the + declustered coordinates (l, d) and input array (l, m), where *l* is + the new number of samples with *l* <= *n*. + """ + + coord = np.copy(coord) + input_array = np.copy(input_array) + + # check inputs + if np.any(~np.isfinite(input_array)): + raise ValueError("input_array contains non-finite values") + + if input_array.ndim == 1: + nvar = 1 + input_array = input_array[:, None] + elif input_array.ndim == 2: + nvar = input_array.shape[1] + else: + raise ValueError( + "input_array must have 1 (n) or 2 dimensions (n, m), but it has %i" + % input_array.ndim + ) + + if coord.ndim != 2: + raise ValueError( + "coord must have 2 dimensions (n, d), but it has %i" % coord.ndim + ) + if coord.shape[0] != input_array.shape[0]: + raise ValueError( + "the number of samples in the input_array does not match the " + + "number of coordinates %i!=%i" % (input_array.shape[0], coord.shape[0]) + ) + + if np.isscalar(scale): + scale = float(scale) + else: + scale = np.copy(scale) + if scale.ndim != 1: + raise ValueError( + "scale must have 1 dimension (d), but it has %i" % scale.ndim + ) + if scale.shape[0] != coord.shape[1]: + raise ValueError( + "scale must have %i elements, but it has %i" + % (coord.shape[1], scale.shape[0]) + ) + scale = scale[None, :] + + # reduce original coordinates + coord_ = np.floor(coord / scale) + + # keep only unique pairs of the reduced coordinates + ucoord_ = np.unique(coord_, axis=0) + + # loop through these unique values and average data points which belong to + # the same cluster + dinput = np.empty(shape=(0, nvar)) + dcoord = np.empty(shape=(0, coord.shape[1])) + for i in range(ucoord_.shape[0]): + idx = np.all(coord_ == ucoord_[i, :], axis=1) + npoints = np.sum(idx) + if npoints >= min_samples: + dinput = np.append( + dinput, np.median(input_array[idx, :], axis=0)[None, :], axis=0 + ) + dcoord = np.append( + dcoord, np.median(coord[idx, :], axis=0)[None, :], axis=0 + ) + + if verbose: + print("--- %i samples left after declustering ---" % dinput.shape[0]) + + return dcoord, dinput + + +def detect_outliers(input_array, thr, coord=None, k=None, verbose=False): + """ + Detect outliers in a (multivariate and georeferenced) dataset. + + Assume a (multivariate) Gaussian distribution and detect outliers based on + the number of standard deviations from the mean. + + If spatial information is provided through coordinates, the outlier + detection can be localized by considering only the k-nearest neighbours + when computing the local mean and standard deviation. + + Parameters + ---------- + input_array: array_like + Array of shape (n) or (n, m), where *n* is the number of samples and + *m* the number of variables. If *m* > 1, the Mahalanobis distance + is used. + All values in ``input_array`` are required to have finite values. + thr: float + The number of standard deviations from the mean used to define an outlier. + coord: array_like or None, optional + Array of shape (n, d) containing the coordinates of the input data into + a space of *d* dimensions. + Passing ``coord`` requires that ``k`` is not None. + k: int or None, optional + The number of nearest neighbours used to localize the outlier + detection. If set to None (the default), it employs all the data points (global + detection). Setting ``k`` requires that ``coord`` is not None. + verbose: bool, optional + Print out information. + + Returns + ------- + out: array_like + A 1-D boolean array of shape (n) with True values indicating the outliers + detected in ``input_array``. + """ + + input_array = np.copy(input_array) + + if np.any(~np.isfinite(input_array)): + raise ValueError("input_array contains non-finite values") + + if input_array.ndim == 1: + nsamples = input_array.size + nvar = 1 + elif input_array.ndim == 2: + nsamples = input_array.shape[0] + nvar = input_array.shape[1] + else: + raise ValueError( + f"input_array must have 1 (n) or 2 dimensions (n, m), " + f"but it has {input_array.ndim}" + ) + + if nsamples < 2: + return np.zeros(nsamples, dtype=bool) + + if coord is not None and k is not None: + coord = np.copy(coord) + if coord.ndim == 1: + coord = coord[:, None] + + elif coord.ndim > 2: + raise ValueError( + f"coord must have 2 dimensions (n, d),but it has {coord.ndim}" + ) + + if coord.shape[0] != nsamples: + raise ValueError( + "the number of samples in input_array does not match the " + f"number of coordinates {nsamples}!={coord.shape[0]}" + ) + + k = np.min((nsamples, k + 1)) + + # global + + if k is None or coord is None: + if nvar == 1: + # univariate + zdata = np.abs(input_array - np.mean(input_array)) / np.std(input_array) + outliers = zdata > thr + else: + # multivariate (mahalanobis distance) + zdata = input_array - np.mean(input_array, axis=0) + V = np.cov(zdata.T) + try: + VI = np.linalg.inv(V) + MD = np.sqrt(np.dot(np.dot(zdata, VI), zdata.T).diagonal()) + except np.linalg.LinAlgError as err: + warnings.warn(f"{err} during outlier detection") + MD = np.zeros(nsamples) + outliers = MD > thr + + # local + else: + tree = scipy.spatial.cKDTree(coord) + __, inds = tree.query(coord, k=k) + outliers = np.empty(shape=0, dtype=bool) + for i in range(inds.shape[0]): + if nvar == 1: + # univariate + thisdata = input_array[i] + neighbours = input_array[inds[i, 1:]] + thiszdata = np.abs(thisdata - np.mean(neighbours)) / np.std(neighbours) + outliers = np.append(outliers, thiszdata > thr) + else: + # multivariate (mahalanobis distance) + thisdata = input_array[i, :] + neighbours = input_array[inds[i, 1:], :].copy() + thiszdata = thisdata - np.mean(neighbours, axis=0) + neighbours = neighbours - np.mean(neighbours, axis=0) + V = np.cov(neighbours.T) + try: + VI = np.linalg.inv(V) + MD = np.sqrt(np.dot(np.dot(thiszdata, VI), thiszdata.T)) + except np.linalg.LinAlgError as err: + warnings.warn(f"{err} during outlier detection") + MD = 0 + outliers = np.append(outliers, MD > thr) + + if verbose: + print(f"--- {np.sum(outliers)} outliers detected ---") + + return outliers diff --git a/pysteps/utils/conversion.py b/pysteps/utils/conversion.py index efec2a728..48eeb35fd 100644 --- a/pysteps/utils/conversion.py +++ b/pysteps/utils/conversion.py @@ -1,321 +1,321 @@ -# -*- coding: utf-8 -*- -""" -pysteps.utils.conversion -======================== - -Methods for converting physical units. - -.. autosummary:: - :toctree: ../generated/ - - to_rainrate - to_raindepth - to_reflectivity -""" - -import warnings - -import xarray as xr - -from pysteps.xarray_helpers import cf_parameters_from_unit - -from . import transformation - -# TODO: This should not be done. Instead fix the code so that it doesn't -# produce the warnings. -# to deactivate warnings for comparison operators with NaNs -warnings.filterwarnings("ignore", category=RuntimeWarning) - - -def _change_unit(dataset: xr.Dataset, precip_var: str, new_unit: str) -> xr.Dataset: - new_var, new_attrs = cf_parameters_from_unit(new_unit) - dataset = dataset.rename_vars({precip_var: new_var}) - dataset.attrs["precip_var"] = new_var - - dataset[new_var].attrs = { - **dataset[new_var].attrs, - **new_attrs, - } - - return dataset - - -def to_rainrate(dataset: xr.Dataset, zr_a=None, zr_b=None): - """ - Convert to rain rate [mm/h]. - - Parameters - ---------- - dataset: xarray.Dataset - Dataset to be (back-)transformed as described in the documentation of - :py:mod:`pysteps.io.importers`. - - Additionally, in case of conversion to/from reflectivity units, the - zr_a and zr_b attributes are also required, - but only if zr_a = zr_b = None. - If missing, it defaults to Marshall–Palmer relation, - that is, zr_a = 200.0 and zr_b = 1.6. - zr_a, zr_b: float, optional - The a and b coefficients of the Z-R relationship (Z = a*R^b). - - Returns - ------- - dataset: xarray.Dataset - Dataset containing the converted units. - """ - - dataset = dataset.copy(deep=True) - precip_var = dataset.attrs["precip_var"] - metadata = dataset[precip_var].attrs - - if "transform" in metadata: - if metadata["transform"] == "dB": - dataset = transformation.dB_transform(dataset, inverse=True) - - elif metadata["transform"] in ["BoxCox", "log"]: - dataset = transformation.boxcox_transform(dataset, inverse=True) - - elif metadata["transform"] == "NQT": - dataset = transformation.NQ_transform(dataset, inverse=True) - - elif metadata["transform"] == "sqrt": - dataset = transformation.sqrt_transform(dataset, inverse=True) - - else: - raise ValueError(f'Unknown transformation {metadata["transform"]}') - - precip_var = dataset.attrs["precip_var"] - metadata = dataset[precip_var].attrs - precip_data = dataset[precip_var].values - - if metadata["units"] == "mm/h": - pass - - elif metadata["units"] == "mm": - threshold = metadata["threshold"] # convert the threshold, too - zerovalue = metadata["zerovalue"] # convert the zerovalue, too - - precip_data = precip_data / float(metadata["accutime"]) * 60.0 - threshold = threshold / float(metadata["accutime"]) * 60.0 - zerovalue = zerovalue / float(metadata["accutime"]) * 60.0 - - metadata["threshold"] = threshold - metadata["zerovalue"] = zerovalue - - elif metadata["units"] == "dBZ": - threshold = metadata["threshold"] # convert the threshold, too - zerovalue = metadata["zerovalue"] # convert the zerovalue, too - - # Z to R - if zr_a is None: - zr_a = metadata.get("zr_a", 200.0) # default to Marshall–Palmer - if zr_b is None: - zr_b = metadata.get("zr_b", 1.6) # default to Marshall–Palmer - precip_data = (precip_data / zr_a) ** (1.0 / zr_b) - threshold = (threshold / zr_a) ** (1.0 / zr_b) - zerovalue = (zerovalue / zr_a) ** (1.0 / zr_b) - - metadata["zr_a"] = zr_a - metadata["zr_b"] = zr_b - metadata["threshold"] = threshold - metadata["zerovalue"] = zerovalue - - else: - raise ValueError( - f'Cannot convert unit {metadata["units"]} and transform {metadata["transform"]} to mm/h' - ) - - dataset[precip_var].data[:] = precip_data - dataset = _change_unit(dataset, precip_var, "mm/h") - return dataset - - -def to_raindepth(dataset: xr.Dataset, zr_a=None, zr_b=None): - """ - Convert to rain depth [mm]. - - Parameters - ---------- - dataset: xarray.Dataset - Dataset to be (back-)transformed as described in the documentation of - :py:mod:`pysteps.io.importers`. - - Additionally, in case of conversion to/from reflectivity units, the - zr_a and zr_b attributes are also required, - but only if zr_a = zr_b = None. - If missing, it defaults to Marshall–Palmer relation, that is, - zr_a = 200.0 and zr_b = 1.6. - zr_a, zr_b: float, optional - The a and b coefficients of the Z-R relationship (Z = a*R^b). - - Returns - ------- - dataset: xarray.Dataset - Dataset containing the converted units. - """ - - dataset = dataset.copy(deep=True) - precip_var = dataset.attrs["precip_var"] - metadata = dataset[precip_var].attrs - - if "transform" in metadata: - if metadata["transform"] == "dB": - dataset = transformation.dB_transform(dataset, inverse=True) - - elif metadata["transform"] in ["BoxCox", "log"]: - dataset = transformation.boxcox_transform(dataset, inverse=True) - - elif metadata["transform"] == "NQT": - dataset = transformation.NQ_transform(dataset, inverse=True) - - elif metadata["transform"] == "sqrt": - dataset = transformation.sqrt_transform(dataset, inverse=True) - - else: - raise ValueError(f'Unknown transformation {metadata["transform"]}') - - precip_var = dataset.attrs["precip_var"] - metadata = dataset[precip_var].attrs - precip_data = dataset[precip_var].values - - if metadata["units"] == "mm" and "transform" not in metadata: - pass - - elif metadata["units"] == "mm/h": - threshold = metadata["threshold"] # convert the threshold, too - zerovalue = metadata["zerovalue"] # convert the zerovalue, too - - precip_data = precip_data / 60.0 * metadata["accutime"] - threshold = threshold / 60.0 * metadata["accutime"] - zerovalue = zerovalue / 60.0 * metadata["accutime"] - - metadata["threshold"] = threshold - metadata["zerovalue"] = zerovalue - - elif metadata["units"] == "dBZ": - threshold = metadata["threshold"] # convert the threshold, too - zerovalue = metadata["zerovalue"] # convert the zerovalue, too - - # Z to R - if zr_a is None: - zr_a = metadata.get("zr_a", 200.0) # Default to Marshall–Palmer - if zr_b is None: - zr_b = metadata.get("zr_b", 1.6) # Default to Marshall–Palmer - precip_data = (precip_data / zr_a) ** (1.0 / zr_b) / 60.0 * metadata["accutime"] - threshold = (threshold / zr_a) ** (1.0 / zr_b) / 60.0 * metadata["accutime"] - zerovalue = (zerovalue / zr_a) ** (1.0 / zr_b) / 60.0 * metadata["accutime"] - - metadata["zr_a"] = zr_a - metadata["zr_b"] = zr_b - metadata["threshold"] = threshold - metadata["zerovalue"] = zerovalue - - else: - raise ValueError( - f'Cannot convert unit {metadata["units"]} and transform {metadata["transform"]} to mm' - ) - - dataset[precip_var].data[:] = precip_data - dataset = _change_unit(dataset, precip_var, "mm") - return dataset - - -def to_reflectivity(dataset: xr.Dataset, zr_a=None, zr_b=None): - """ - Convert to reflectivity [dBZ]. - - Parameters - ---------- - dataset: xarray.Dataset - Dataset to be (back-)transformed as described in the documentation of - :py:mod:`pysteps.io.importers`. - - Additionally, in case of conversion to/from reflectivity units, the - zr_a and zr_b attributes are also required, - but only if zr_a = zr_b = None. - If missing, it defaults to Marshall–Palmer relation, that is, - zr_a = 200.0 and zr_b = 1.6. - zr_a, zr_b: float, optional - The a and b coefficients of the Z-R relationship (Z = a*R^b). - - Returns - ------- - dataset: xarray.Dataset - Dataset containing the converted units. - """ - - dataset = dataset.copy(deep=True) - precip_var = dataset.attrs["precip_var"] - metadata = dataset[precip_var].attrs - - if "transform" in metadata: - if metadata["transform"] == "dB": - dataset = transformation.dB_transform(dataset, inverse=True) - - elif metadata["transform"] in ["BoxCox", "log"]: - dataset = transformation.boxcox_transform(dataset, inverse=True) - - elif metadata["transform"] == "NQT": - dataset = transformation.NQ_transform(dataset, inverse=True) - - elif metadata["transform"] == "sqrt": - dataset = transformation.sqrt_transform(dataset, inverse=True) - - else: - raise ValueError(f'Unknown transformation {metadata["transform"]}') - - precip_var = dataset.attrs["precip_var"] - metadata = dataset[precip_var].attrs - precip_data = dataset[precip_var].values - - if metadata["units"] == "mm/h": - # Z to R - if zr_a is None: - zr_a = metadata.get("zr_a", 200.0) # Default to Marshall–Palmer - if zr_b is None: - zr_b = metadata.get("zr_b", 1.6) # Default to Marshall–Palmer - - precip_data = zr_a * precip_data**zr_b - metadata["threshold"] = zr_a * metadata["threshold"] ** zr_b - metadata["zerovalue"] = zr_a * metadata["zerovalue"] ** zr_b - metadata["zr_a"] = zr_a - metadata["zr_b"] = zr_b - - elif metadata["units"] == "mm": - # depth to rate - dataset = to_rainrate(dataset) - - precip_var = dataset.attrs["precip_var"] - metadata = dataset[precip_var].attrs - precip_data = dataset[precip_var].values - - # Z to R - if zr_a is None: - zr_a = metadata.get("zr_a", 200.0) # Default to Marshall-Palmer - if zr_b is None: - zr_b = metadata.get("zr_b", 1.6) # Default to Marshall-Palmer - precip_data = zr_a * precip_data**zr_b - metadata["threshold"] = zr_a * metadata["threshold"] ** zr_b - metadata["zerovalue"] = zr_a * metadata["zerovalue"] ** zr_b - metadata["zr_a"] = zr_a - metadata["zr_b"] = zr_b - - elif metadata["units"] == "dBZ": - pass - - else: - raise ValueError( - f'Cannot convert unit {metadata["units"]} and transform {metadata["transform"]} to dBZ' - ) - - dataset[precip_var].data[:] = precip_data - # Z to dBZ - dataset = transformation.dB_transform(dataset) - - precip_var = dataset.attrs["precip_var"] - metadata = dataset[precip_var].attrs - precip_data = dataset[precip_var].values - - dataset[precip_var].data[:] = precip_data - dataset = _change_unit(dataset, precip_var, "dBZ") - return dataset +# -*- coding: utf-8 -*- +""" +pysteps.utils.conversion +======================== + +Methods for converting physical units. + +.. autosummary:: + :toctree: ../generated/ + + to_rainrate + to_raindepth + to_reflectivity +""" + +import warnings + +import xarray as xr + +from pysteps.xarray_helpers import cf_parameters_from_unit + +from . import transformation + +# TODO: This should not be done. Instead fix the code so that it doesn't +# produce the warnings. +# to deactivate warnings for comparison operators with NaNs +warnings.filterwarnings("ignore", category=RuntimeWarning) + + +def _change_unit(dataset: xr.Dataset, precip_var: str, new_unit: str) -> xr.Dataset: + new_var, new_attrs = cf_parameters_from_unit(new_unit) + dataset = dataset.rename_vars({precip_var: new_var}) + dataset.attrs["precip_var"] = new_var + + dataset[new_var].attrs = { + **dataset[new_var].attrs, + **new_attrs, + } + + return dataset + + +def to_rainrate(dataset: xr.Dataset, zr_a=None, zr_b=None): + """ + Convert to rain rate [mm/h]. + + Parameters + ---------- + dataset: xarray.Dataset + Dataset to be (back-)transformed as described in the documentation of + :py:mod:`pysteps.io.importers`. + + Additionally, in case of conversion to/from reflectivity units, the + zr_a and zr_b attributes are also required, + but only if zr_a = zr_b = None. + If missing, it defaults to Marshall–Palmer relation, + that is, zr_a = 200.0 and zr_b = 1.6. + zr_a, zr_b: float, optional + The a and b coefficients of the Z-R relationship (Z = a*R^b). + + Returns + ------- + dataset: xarray.Dataset + Dataset containing the converted units. + """ + + dataset = dataset.copy(deep=True) + precip_var = dataset.attrs["precip_var"] + metadata = dataset[precip_var].attrs + + if "transform" in metadata: + if metadata["transform"] == "dB": + dataset = transformation.dB_transform(dataset, inverse=True) + + elif metadata["transform"] in ["BoxCox", "log"]: + dataset = transformation.boxcox_transform(dataset, inverse=True) + + elif metadata["transform"] == "NQT": + dataset = transformation.NQ_transform(dataset, inverse=True) + + elif metadata["transform"] == "sqrt": + dataset = transformation.sqrt_transform(dataset, inverse=True) + + else: + raise ValueError(f"Unknown transformation {metadata['transform']}") + + precip_var = dataset.attrs["precip_var"] + metadata = dataset[precip_var].attrs + precip_data = dataset[precip_var].values + + if metadata["units"] == "mm/h": + pass + + elif metadata["units"] == "mm": + threshold = metadata["threshold"] # convert the threshold, too + zerovalue = metadata["zerovalue"] # convert the zerovalue, too + + precip_data = precip_data / float(metadata["accutime"]) * 60.0 + threshold = threshold / float(metadata["accutime"]) * 60.0 + zerovalue = zerovalue / float(metadata["accutime"]) * 60.0 + + metadata["threshold"] = threshold + metadata["zerovalue"] = zerovalue + + elif metadata["units"] == "dBZ": + threshold = metadata["threshold"] # convert the threshold, too + zerovalue = metadata["zerovalue"] # convert the zerovalue, too + + # Z to R + if zr_a is None: + zr_a = metadata.get("zr_a", 200.0) # default to Marshall–Palmer + if zr_b is None: + zr_b = metadata.get("zr_b", 1.6) # default to Marshall–Palmer + precip_data = (precip_data / zr_a) ** (1.0 / zr_b) + threshold = (threshold / zr_a) ** (1.0 / zr_b) + zerovalue = (zerovalue / zr_a) ** (1.0 / zr_b) + + metadata["zr_a"] = zr_a + metadata["zr_b"] = zr_b + metadata["threshold"] = threshold + metadata["zerovalue"] = zerovalue + + else: + raise ValueError( + f"Cannot convert unit {metadata['units']} and transform {metadata['transform']} to mm/h" + ) + + dataset[precip_var].data[:] = precip_data + dataset = _change_unit(dataset, precip_var, "mm/h") + return dataset + + +def to_raindepth(dataset: xr.Dataset, zr_a=None, zr_b=None): + """ + Convert to rain depth [mm]. + + Parameters + ---------- + dataset: xarray.Dataset + Dataset to be (back-)transformed as described in the documentation of + :py:mod:`pysteps.io.importers`. + + Additionally, in case of conversion to/from reflectivity units, the + zr_a and zr_b attributes are also required, + but only if zr_a = zr_b = None. + If missing, it defaults to Marshall–Palmer relation, that is, + zr_a = 200.0 and zr_b = 1.6. + zr_a, zr_b: float, optional + The a and b coefficients of the Z-R relationship (Z = a*R^b). + + Returns + ------- + dataset: xarray.Dataset + Dataset containing the converted units. + """ + + dataset = dataset.copy(deep=True) + precip_var = dataset.attrs["precip_var"] + metadata = dataset[precip_var].attrs + + if "transform" in metadata: + if metadata["transform"] == "dB": + dataset = transformation.dB_transform(dataset, inverse=True) + + elif metadata["transform"] in ["BoxCox", "log"]: + dataset = transformation.boxcox_transform(dataset, inverse=True) + + elif metadata["transform"] == "NQT": + dataset = transformation.NQ_transform(dataset, inverse=True) + + elif metadata["transform"] == "sqrt": + dataset = transformation.sqrt_transform(dataset, inverse=True) + + else: + raise ValueError(f"Unknown transformation {metadata['transform']}") + + precip_var = dataset.attrs["precip_var"] + metadata = dataset[precip_var].attrs + precip_data = dataset[precip_var].values + + if metadata["units"] == "mm" and "transform" not in metadata: + pass + + elif metadata["units"] == "mm/h": + threshold = metadata["threshold"] # convert the threshold, too + zerovalue = metadata["zerovalue"] # convert the zerovalue, too + + precip_data = precip_data / 60.0 * metadata["accutime"] + threshold = threshold / 60.0 * metadata["accutime"] + zerovalue = zerovalue / 60.0 * metadata["accutime"] + + metadata["threshold"] = threshold + metadata["zerovalue"] = zerovalue + + elif metadata["units"] == "dBZ": + threshold = metadata["threshold"] # convert the threshold, too + zerovalue = metadata["zerovalue"] # convert the zerovalue, too + + # Z to R + if zr_a is None: + zr_a = metadata.get("zr_a", 200.0) # Default to Marshall–Palmer + if zr_b is None: + zr_b = metadata.get("zr_b", 1.6) # Default to Marshall–Palmer + precip_data = (precip_data / zr_a) ** (1.0 / zr_b) / 60.0 * metadata["accutime"] + threshold = (threshold / zr_a) ** (1.0 / zr_b) / 60.0 * metadata["accutime"] + zerovalue = (zerovalue / zr_a) ** (1.0 / zr_b) / 60.0 * metadata["accutime"] + + metadata["zr_a"] = zr_a + metadata["zr_b"] = zr_b + metadata["threshold"] = threshold + metadata["zerovalue"] = zerovalue + + else: + raise ValueError( + f"Cannot convert unit {metadata['units']} and transform {metadata['transform']} to mm" + ) + + dataset[precip_var].data[:] = precip_data + dataset = _change_unit(dataset, precip_var, "mm") + return dataset + + +def to_reflectivity(dataset: xr.Dataset, zr_a=None, zr_b=None): + """ + Convert to reflectivity [dBZ]. + + Parameters + ---------- + dataset: xarray.Dataset + Dataset to be (back-)transformed as described in the documentation of + :py:mod:`pysteps.io.importers`. + + Additionally, in case of conversion to/from reflectivity units, the + zr_a and zr_b attributes are also required, + but only if zr_a = zr_b = None. + If missing, it defaults to Marshall–Palmer relation, that is, + zr_a = 200.0 and zr_b = 1.6. + zr_a, zr_b: float, optional + The a and b coefficients of the Z-R relationship (Z = a*R^b). + + Returns + ------- + dataset: xarray.Dataset + Dataset containing the converted units. + """ + + dataset = dataset.copy(deep=True) + precip_var = dataset.attrs["precip_var"] + metadata = dataset[precip_var].attrs + + if "transform" in metadata: + if metadata["transform"] == "dB": + dataset = transformation.dB_transform(dataset, inverse=True) + + elif metadata["transform"] in ["BoxCox", "log"]: + dataset = transformation.boxcox_transform(dataset, inverse=True) + + elif metadata["transform"] == "NQT": + dataset = transformation.NQ_transform(dataset, inverse=True) + + elif metadata["transform"] == "sqrt": + dataset = transformation.sqrt_transform(dataset, inverse=True) + + else: + raise ValueError(f"Unknown transformation {metadata['transform']}") + + precip_var = dataset.attrs["precip_var"] + metadata = dataset[precip_var].attrs + precip_data = dataset[precip_var].values + + if metadata["units"] == "mm/h": + # Z to R + if zr_a is None: + zr_a = metadata.get("zr_a", 200.0) # Default to Marshall–Palmer + if zr_b is None: + zr_b = metadata.get("zr_b", 1.6) # Default to Marshall–Palmer + + precip_data = zr_a * precip_data**zr_b + metadata["threshold"] = zr_a * metadata["threshold"] ** zr_b + metadata["zerovalue"] = zr_a * metadata["zerovalue"] ** zr_b + metadata["zr_a"] = zr_a + metadata["zr_b"] = zr_b + + elif metadata["units"] == "mm": + # depth to rate + dataset = to_rainrate(dataset) + + precip_var = dataset.attrs["precip_var"] + metadata = dataset[precip_var].attrs + precip_data = dataset[precip_var].values + + # Z to R + if zr_a is None: + zr_a = metadata.get("zr_a", 200.0) # Default to Marshall-Palmer + if zr_b is None: + zr_b = metadata.get("zr_b", 1.6) # Default to Marshall-Palmer + precip_data = zr_a * precip_data**zr_b + metadata["threshold"] = zr_a * metadata["threshold"] ** zr_b + metadata["zerovalue"] = zr_a * metadata["zerovalue"] ** zr_b + metadata["zr_a"] = zr_a + metadata["zr_b"] = zr_b + + elif metadata["units"] == "dBZ": + pass + + else: + raise ValueError( + f"Cannot convert unit {metadata['units']} and transform {metadata['transform']} to dBZ" + ) + + dataset[precip_var].data[:] = precip_data + # Z to dBZ + dataset = transformation.dB_transform(dataset) + + precip_var = dataset.attrs["precip_var"] + metadata = dataset[precip_var].attrs + precip_data = dataset[precip_var].values + + dataset[precip_var].data[:] = precip_data + dataset = _change_unit(dataset, precip_var, "dBZ") + return dataset diff --git a/pysteps/utils/fft.py b/pysteps/utils/fft.py index 1c277d78f..686b92509 100644 --- a/pysteps/utils/fft.py +++ b/pysteps/utils/fft.py @@ -13,9 +13,10 @@ get_pyfftw """ -from pysteps.exceptions import MissingOptionalDependency from types import SimpleNamespace +from pysteps.exceptions import MissingOptionalDependency + def get_numpy(shape, fftn_shape=None, **kwargs): import numpy.fft as numpy_fft @@ -60,8 +61,8 @@ def get_scipy(shape, fftn_shape=None, **kwargs): def get_pyfftw(shape, fftn_shape=None, n_threads=1, **kwargs): try: - import pyfftw.interfaces.numpy_fft as pyfftw_fft import pyfftw + import pyfftw.interfaces.numpy_fft as pyfftw_fft pyfftw.interfaces.cache.enable() except ImportError: diff --git a/pysteps/utils/images.py b/pysteps/utils/images.py index 6f2376e2f..98ecaac28 100644 --- a/pysteps/utils/images.py +++ b/pysteps/utils/images.py @@ -1,86 +1,86 @@ -# -*- coding: utf-8 -*- -""" -pysteps.utils.images -==================== - -Image processing routines for pysteps. - -.. autosummary:: - :toctree: ../generated/ - - morph_opening -""" - -import numpy as np -from numpy.ma.core import MaskedArray - -from pysteps.exceptions import MissingOptionalDependency - -try: - import cv2 - - CV2_IMPORTED = True -except ImportError: - CV2_IMPORTED = False - - -def morph_opening(input_image, thr, n): - """ - Filter out small scale noise on the image by applying a binary - morphological opening, that is, erosion followed by dilation. - - .. _MaskedArray:\ - https://docs.scipy.org/doc/numpy/reference/maskedarray.baseclass.html#numpy.ma.MaskedArray - - .. _ndarray:\ - https://docs.scipy.org/doc/numpy/reference/generated/numpy.ndarray.html - - Parameters - ---------- - input_image: ndarray_ or MaskedArray_ - Array of shape (m, n) containing the input image. - thr: float - The threshold used to convert the image into a binary image. - n: int - The structuring element size [pixels]. - - Returns - ------- - input_image: ndarray_ or MaskedArray_ - Array of shape (m,n) containing the filtered image. - """ - if not CV2_IMPORTED: - raise MissingOptionalDependency( - "opencv package is required for the morphologyEx " - "routine but it is not installed" - ) - - input_image = input_image.copy() - - # Check if a MaskedArray is used. If not, mask the ndarray - to_ndarray = False - if not isinstance(input_image, MaskedArray): - to_ndarray = True - input_image = np.ma.masked_invalid(input_image) - - np.ma.set_fill_value(input_image, input_image.min()) - - # Convert to binary image - field_bin = np.ndarray.astype(input_image.filled() > thr, "uint8") - - # Build a structuring element of size n - kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (n, n)) - - # Apply morphological opening (i.e. erosion then dilation) - field_bin_out = cv2.morphologyEx(field_bin, cv2.MORPH_OPEN, kernel) - - # Build mask to be applied on the original image - mask = (field_bin - field_bin_out) > 0 - - # Filter out small isolated pixels based on mask - input_image[mask] = np.nanmin(input_image) - - if to_ndarray: - input_image = np.array(input_image) - - return input_image +# -*- coding: utf-8 -*- +""" +pysteps.utils.images +==================== + +Image processing routines for pysteps. + +.. autosummary:: + :toctree: ../generated/ + + morph_opening +""" + +import numpy as np +from numpy.ma.core import MaskedArray + +from pysteps.exceptions import MissingOptionalDependency + +try: + import cv2 + + CV2_IMPORTED = True +except ImportError: + CV2_IMPORTED = False + + +def morph_opening(input_image, thr, n): + """ + Filter out small scale noise on the image by applying a binary + morphological opening, that is, erosion followed by dilation. + + .. _MaskedArray:\ + https://docs.scipy.org/doc/numpy/reference/maskedarray.baseclass.html#numpy.ma.MaskedArray + + .. _ndarray:\ + https://docs.scipy.org/doc/numpy/reference/generated/numpy.ndarray.html + + Parameters + ---------- + input_image: ndarray_ or MaskedArray_ + Array of shape (m, n) containing the input image. + thr: float + The threshold used to convert the image into a binary image. + n: int + The structuring element size [pixels]. + + Returns + ------- + input_image: ndarray_ or MaskedArray_ + Array of shape (m,n) containing the filtered image. + """ + if not CV2_IMPORTED: + raise MissingOptionalDependency( + "opencv package is required for the morphologyEx " + "routine but it is not installed" + ) + + input_image = input_image.copy() + + # Check if a MaskedArray is used. If not, mask the ndarray + to_ndarray = False + if not isinstance(input_image, MaskedArray): + to_ndarray = True + input_image = np.ma.masked_invalid(input_image) + + np.ma.set_fill_value(input_image, input_image.min()) + + # Convert to binary image + field_bin = np.ndarray.astype(input_image.filled() > thr, "uint8") + + # Build a structuring element of size n + kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (n, n)) + + # Apply morphological opening (i.e. erosion then dilation) + field_bin_out = cv2.morphologyEx(field_bin, cv2.MORPH_OPEN, kernel) + + # Build mask to be applied on the original image + mask = (field_bin - field_bin_out) > 0 + + # Filter out small isolated pixels based on mask + input_image[mask] = np.nanmin(input_image) + + if to_ndarray: + input_image = np.array(input_image) + + return input_image diff --git a/pysteps/utils/interpolate.py b/pysteps/utils/interpolate.py index 8cb5d9b1a..a1beb0e51 100644 --- a/pysteps/utils/interpolate.py +++ b/pysteps/utils/interpolate.py @@ -1,182 +1,181 @@ -# -*- coding: utf-8 -*- -""" -pysteps.utils.interpolate -========================= - -Interpolation routines for pysteps. - -.. autosummary:: - :toctree: ../generated/ - - idwinterp2d - rbfinterp2d -""" - -import warnings - -import numpy as np -from scipy.interpolate import Rbf -from scipy.spatial import cKDTree -from scipy.spatial.distance import cdist - - -from pysteps.decorators import memoize, prepare_interpolator - - -@prepare_interpolator() -def idwinterp2d( - xy_coord, values, xgrid, ygrid, power=0.5, k=20, dist_offset=0.5, **kwargs -): - """ - Inverse distance weighting interpolation of a sparse (multivariate) array. - - .. _ndarray:\ - https://docs.scipy.org/doc/numpy/reference/generated/numpy.ndarray.html - - Parameters - ---------- - xy_coord: ndarray_ - Array of shape (n, 2) containing the coordinates of the data points in - a 2-dimensional space. - values: ndarray_ - Array of shape (n) or (n, m) containing the values of the data points, - where *n* is the number of data points and *m* the number of co-located - variables. All elements in ``values`` are required to be finite. - xgrid, ygrid: ndarray_ - 1-D arrays representing the coordinates of the 2-D output grid. - power: positive float, optional - The power parameter used to compute the distance weights as - ``weight = distance ** (-power)``. - k: positive int or None, optional - The number of nearest neighbours used for each target location. - If set to None, it interpolates using all the data points at once. - dist_offset: float, optional - A small, positive constant that is added to distances to avoid zero - values. It has units of pixels. - - Other Parameters - ---------------- - {extra_kwargs_doc} - - Returns - ------- - output_array: ndarray_ - The interpolated field(s) having shape (``ygrid.size``, ``xgrid.size``) - or (*m*, ``ygrid.size``, ``xgrid.size``). - """ - if values.ndim == 1: - nvar = 1 - values = values[:, None] - - elif values.ndim == 2: - nvar = values.shape[1] - - npoints = values.shape[0] - - # generate the target grid - xgridv, ygridv = np.meshgrid(xgrid, ygrid) - gridv = np.column_stack((xgridv.ravel(), ygridv.ravel())) - - if k is not None: - k = int(np.min((k, npoints))) - tree = _cKDTree_cached(xy_coord, hkey=kwargs.get("hkey", None)) - dist, inds = tree.query(gridv, k=k) - if dist.ndim == 1: - dist = dist[..., None] - inds = inds[..., None] - else: - # use all points - dist = cdist(xy_coord, gridv, "euclidean").transpose() - inds = np.arange(npoints)[None, :] * np.ones((gridv.shape[0], npoints)).astype( - int - ) - - # convert geographical distances to number of pixels - x_res = np.gradient(xgrid) - y_res = np.gradient(ygrid) - mean_res = np.mean(np.abs([x_res.mean(), y_res.mean()])) - dist /= mean_res - - # compute distance-based weights - dist += dist_offset # avoid zero distances - weights = 1 / np.power(dist, power) - weights = weights / np.sum(weights, axis=1, keepdims=True) - - # interpolate - output_array = np.sum( - values[inds, :] * weights[..., None], - axis=1, - ) - - # reshape to final grid size - output_array = output_array.reshape(ygrid.size, xgrid.size, nvar) - - return np.moveaxis(output_array, -1, 0).squeeze() - - -@prepare_interpolator() -def rbfinterp2d(xy_coord, values, xgrid, ygrid, **kwargs): - """ - Radial basis function interpolation of a sparse (multivariate) array. - - .. _ndarray:\ - https://docs.scipy.org/doc/numpy/reference/generated/numpy.ndarray.html - .. _`scipy.interpolate.Rbf`:\ - https://docs.scipy.org/doc/scipy/reference/generated/scipy.interpolate.Rbf.html - - This method wraps the `scipy.interpolate.Rbf`_ class. - - Parameters - ---------- - xy_coord: ndarray_ - Array of shape (n, 2) containing the coordinates of the data points in - a 2-dimensional space. - values: ndarray_ - Array of shape (n) or (n, m) containing the values of the data points, - where *n* is the number of data points and *m* the number of co-located - variables. All values in ``values`` are required to be finite. - xgrid, ygrid: ndarray_ - 1-D arrays representing the coordinates of the 2-D output grid. - - Other Parameters - ---------------- - Any of the parameters from the original `scipy.interpolate.Rbf`_ class. - {extra_kwargs_doc} - - Returns - ------- - output_array: ndarray_ - The interpolated field(s) having shape (``ygrid.size``, ``xgrid.size``) - or (*m*, ``ygrid.size``, ``xgrid.size``). - """ - deprecated_args = ["rbfunction", "k"] - deprecated_args = [arg for arg in deprecated_args if arg in list(kwargs.keys())] - if deprecated_args: - warnings.warn( - "rbfinterp2d: The following keyword arguments are deprecated:\n" - + str(deprecated_args), - DeprecationWarning, - ) - - if values.ndim == 1: - kwargs["mode"] = "1-D" - else: - kwargs["mode"] = "N-D" - - xgridv, ygridv = np.meshgrid(xgrid, ygrid) - rbfi = _Rbf_cached(*np.split(xy_coord, xy_coord.shape[1], 1), values, **kwargs) - output_array = rbfi(xgridv, ygridv) - - return np.moveaxis(output_array, -1, 0).squeeze() - - -@memoize() -def _cKDTree_cached(*args, **kwargs): - """Add LRU cache to cKDTree class.""" - return cKDTree(*args) - - -@memoize() -def _Rbf_cached(*args, **kwargs): - """Add LRU cache to Rbf class.""" - return Rbf(*args, **kwargs) +# -*- coding: utf-8 -*- +""" +pysteps.utils.interpolate +========================= + +Interpolation routines for pysteps. + +.. autosummary:: + :toctree: ../generated/ + + idwinterp2d + rbfinterp2d +""" + +import warnings + +import numpy as np +from scipy.interpolate import Rbf +from scipy.spatial import cKDTree +from scipy.spatial.distance import cdist + +from pysteps.decorators import memoize, prepare_interpolator + + +@prepare_interpolator() +def idwinterp2d( + xy_coord, values, xgrid, ygrid, power=0.5, k=20, dist_offset=0.5, **kwargs +): + """ + Inverse distance weighting interpolation of a sparse (multivariate) array. + + .. _ndarray:\ + https://docs.scipy.org/doc/numpy/reference/generated/numpy.ndarray.html + + Parameters + ---------- + xy_coord: ndarray_ + Array of shape (n, 2) containing the coordinates of the data points in + a 2-dimensional space. + values: ndarray_ + Array of shape (n) or (n, m) containing the values of the data points, + where *n* is the number of data points and *m* the number of co-located + variables. All elements in ``values`` are required to be finite. + xgrid, ygrid: ndarray_ + 1-D arrays representing the coordinates of the 2-D output grid. + power: positive float, optional + The power parameter used to compute the distance weights as + ``weight = distance ** (-power)``. + k: positive int or None, optional + The number of nearest neighbours used for each target location. + If set to None, it interpolates using all the data points at once. + dist_offset: float, optional + A small, positive constant that is added to distances to avoid zero + values. It has units of pixels. + + Other Parameters + ---------------- + {extra_kwargs_doc} + + Returns + ------- + output_array: ndarray_ + The interpolated field(s) having shape (``ygrid.size``, ``xgrid.size``) + or (*m*, ``ygrid.size``, ``xgrid.size``). + """ + if values.ndim == 1: + nvar = 1 + values = values[:, None] + + elif values.ndim == 2: + nvar = values.shape[1] + + npoints = values.shape[0] + + # generate the target grid + xgridv, ygridv = np.meshgrid(xgrid, ygrid) + gridv = np.column_stack((xgridv.ravel(), ygridv.ravel())) + + if k is not None: + k = int(np.min((k, npoints))) + tree = _cKDTree_cached(xy_coord, hkey=kwargs.get("hkey", None)) + dist, inds = tree.query(gridv, k=k) + if dist.ndim == 1: + dist = dist[..., None] + inds = inds[..., None] + else: + # use all points + dist = cdist(xy_coord, gridv, "euclidean").transpose() + inds = np.arange(npoints)[None, :] * np.ones((gridv.shape[0], npoints)).astype( + int + ) + + # convert geographical distances to number of pixels + x_res = np.gradient(xgrid) + y_res = np.gradient(ygrid) + mean_res = np.mean(np.abs([x_res.mean(), y_res.mean()])) + dist /= mean_res + + # compute distance-based weights + dist += dist_offset # avoid zero distances + weights = 1 / np.power(dist, power) + weights = weights / np.sum(weights, axis=1, keepdims=True) + + # interpolate + output_array = np.sum( + values[inds, :] * weights[..., None], + axis=1, + ) + + # reshape to final grid size + output_array = output_array.reshape(ygrid.size, xgrid.size, nvar) + + return np.moveaxis(output_array, -1, 0).squeeze() + + +@prepare_interpolator() +def rbfinterp2d(xy_coord, values, xgrid, ygrid, **kwargs): + """ + Radial basis function interpolation of a sparse (multivariate) array. + + .. _ndarray:\ + https://docs.scipy.org/doc/numpy/reference/generated/numpy.ndarray.html + .. _`scipy.interpolate.Rbf`:\ + https://docs.scipy.org/doc/scipy/reference/generated/scipy.interpolate.Rbf.html + + This method wraps the `scipy.interpolate.Rbf`_ class. + + Parameters + ---------- + xy_coord: ndarray_ + Array of shape (n, 2) containing the coordinates of the data points in + a 2-dimensional space. + values: ndarray_ + Array of shape (n) or (n, m) containing the values of the data points, + where *n* is the number of data points and *m* the number of co-located + variables. All values in ``values`` are required to be finite. + xgrid, ygrid: ndarray_ + 1-D arrays representing the coordinates of the 2-D output grid. + + Other Parameters + ---------------- + Any of the parameters from the original `scipy.interpolate.Rbf`_ class. + {extra_kwargs_doc} + + Returns + ------- + output_array: ndarray_ + The interpolated field(s) having shape (``ygrid.size``, ``xgrid.size``) + or (*m*, ``ygrid.size``, ``xgrid.size``). + """ + deprecated_args = ["rbfunction", "k"] + deprecated_args = [arg for arg in deprecated_args if arg in list(kwargs.keys())] + if deprecated_args: + warnings.warn( + "rbfinterp2d: The following keyword arguments are deprecated:\n" + + str(deprecated_args), + DeprecationWarning, + ) + + if values.ndim == 1: + kwargs["mode"] = "1-D" + else: + kwargs["mode"] = "N-D" + + xgridv, ygridv = np.meshgrid(xgrid, ygrid) + rbfi = _Rbf_cached(*np.split(xy_coord, xy_coord.shape[1], 1), values, **kwargs) + output_array = rbfi(xgridv, ygridv) + + return np.moveaxis(output_array, -1, 0).squeeze() + + +@memoize() +def _cKDTree_cached(*args, **kwargs): + """Add LRU cache to cKDTree class.""" + return cKDTree(*args) + + +@memoize() +def _Rbf_cached(*args, **kwargs): + """Add LRU cache to Rbf class.""" + return Rbf(*args, **kwargs) diff --git a/pysteps/utils/pca.py b/pysteps/utils/pca.py index dcd288e45..683fe2031 100644 --- a/pysteps/utils/pca.py +++ b/pysteps/utils/pca.py @@ -12,6 +12,7 @@ """ import numpy as np + from pysteps.exceptions import MissingOptionalDependency try: diff --git a/pysteps/utils/spectral.py b/pysteps/utils/spectral.py index a304b4abc..865691934 100644 --- a/pysteps/utils/spectral.py +++ b/pysteps/utils/spectral.py @@ -16,6 +16,7 @@ """ import numpy as np + from . import arrays diff --git a/pysteps/utils/transformation.py b/pysteps/utils/transformation.py index 49b4e4fe8..e898a721e 100644 --- a/pysteps/utils/transformation.py +++ b/pysteps/utils/transformation.py @@ -1,350 +1,350 @@ -# -*- coding: utf-8 -*- -""" -pysteps.utils.transformation -============================ - -Methods for transforming data values. - -.. autosummary:: - :toctree: ../generated/ - - boxcox_transform - dB_transform - NQ_transform - sqrt_transform -""" - -import warnings - -import numpy as np -import scipy.stats as scipy_stats -import xarray as xr -from scipy.interpolate import interp1d - -warnings.filterwarnings( - "ignore", category=RuntimeWarning -) # To deactivate warnings for comparison operators with NaNs - - -def boxcox_transform( - dataset: xr.Dataset, Lambda=None, threshold=None, zerovalue=None, inverse=False -) -> xr.Dataset: - """ - The one-parameter Box-Cox transformation. - - The Box-Cox transform is a well-known power transformation introduced by - Box and Cox (1964). In its one-parameter version, the Box-Cox transform - takes the form T(x) = ln(x) for Lambda = 0, - or T(x) = (x**Lambda - 1)/Lambda otherwise. - - Default parameters will produce a log transform (i.e. Lambda=0). - - Parameters - ---------- - dataset: xarray.Dataset - Dataset to be transformed as described in the documentation of - :py:mod:`pysteps.io.importers`. - Lambda: float, optional - Parameter Lambda of the Box-Cox transformation. - It is 0 by default, which produces the log transformation. - - Choose Lambda < 1 for positively skewed data, Lambda > 1 for negatively - skewed data. - threshold: float, optional - The value that is used for thresholding with the same units as in the dataset. - If None, the threshold contained in metadata is used. - If no threshold is found in the metadata, - a value of 0.1 is used as default. - zerovalue: float, optional - The value to be assigned to no rain pixels as defined by the threshold. - It is equal to the threshold - 1 by default. - inverse: bool, optional - If set to True, it performs the inverse transform. False by default. - - Returns - ------- - dataset: xarray.Dataset - Dataset containing the (back-)transformed units. - - References - ---------- - Box, G. E. and Cox, D. R. (1964), An Analysis of Transformations. Journal - of the Royal Statistical Society: Series B (Methodological), 26: 211-243. - doi:10.1111/j.2517-6161.1964.tb00553.x - """ - - dataset = dataset.copy(deep=True) - precip_var = dataset.attrs["precip_var"] - metadata = dataset[precip_var].attrs - precip_data = dataset[precip_var].values - - if not inverse: - if "transform" in metadata and metadata["transform"] == "BoxCox": - return dataset - - if Lambda is None: - Lambda = metadata.get("BoxCox_lambda", 0.0) - - if threshold is None: - threshold = metadata.get("threshold", 0.1) - - zeros = precip_data < threshold - - # Apply Box-Cox transform - if Lambda == 0.0: - precip_data[~zeros] = np.log(precip_data[~zeros]) - threshold = np.log(threshold) - - else: - precip_data[~zeros] = (precip_data[~zeros] ** Lambda - 1) / Lambda - threshold = (threshold**Lambda - 1) / Lambda - - # Set value for zeros - if zerovalue is None: - zerovalue = threshold - 1 # TODO: set to a more meaningful value - precip_data[zeros] = zerovalue - - metadata["transform"] = "BoxCox" - metadata["BoxCox_lambda"] = Lambda - metadata["zerovalue"] = zerovalue - metadata["threshold"] = threshold - - elif inverse: - if metadata["transform"] not in ["BoxCox", "log"]: - return precip_data, metadata - - if Lambda is None: - Lambda = metadata.pop("BoxCox_lambda", 0.0) - if threshold is None: - threshold = metadata.get("threshold", -10.0) - if zerovalue is None: - zerovalue = 0.0 - - # Apply inverse Box-Cox transform - if Lambda == 0.0: - precip_data = np.exp(precip_data) - threshold = np.exp(threshold) - - else: - precip_data = np.exp(np.log(Lambda * precip_data + 1) / Lambda) - threshold = np.exp(np.log(Lambda * threshold + 1) / Lambda) - - precip_data[precip_data < threshold] = zerovalue - - del metadata["transform"] - metadata["zerovalue"] = zerovalue - metadata["threshold"] = threshold - - dataset[precip_var].data[:] = precip_data - - return dataset - - -def dB_transform( - dataset: xr.Dataset, threshold=None, zerovalue=None, inverse=False -) -> xr.Dataset: - """Methods to transform precipitation intensities to/from dB units. - - Parameters - ---------- - dataset: xarray.Dataset - Dataset to be (back-)transformed as described in the documentation of - :py:mod:`pysteps.io.importers`. - threshold: float, optional - Optional value that is used for thresholding with the same units as in the dataset. - If None, the threshold contained in metadata is used. - If no threshold is found in the metadata, - a value of 0.1 is used as default. - zerovalue: float, optional - The value to be assigned to no rain pixels as defined by the threshold. - It is equal to the threshold - 1 by default. - inverse: bool, optional - If set to True, it performs the inverse transform. False by default. - - Returns - ------- - dataset: xarray.Dataset - Dataset containing the (back-)transformed units. - """ - - dataset = dataset.copy(deep=True) - precip_var = dataset.attrs["precip_var"] - metadata = dataset[precip_var].attrs - precip_data = dataset[precip_var].values - - # to dB units - if not inverse: - if "transform" in metadata and metadata["transform"] == "dB": - return dataset - - if threshold is None: - threshold = metadata.get("threshold", 0.1) - - zeros = precip_data < threshold - - # Convert to dB - precip_data[~zeros] = 10.0 * np.log10(precip_data[~zeros]) - threshold = 10.0 * np.log10(threshold) - - # Set value for zeros - if zerovalue is None: - zerovalue = threshold - 5 # TODO: set to a more meaningful value - precip_data[zeros] = zerovalue - - metadata["transform"] = "dB" - metadata["zerovalue"] = zerovalue - metadata["threshold"] = threshold - - # from dB units - elif inverse: - if metadata["transform"] != "dB": - return dataset - - if threshold is None: - threshold = metadata.get("threshold", -10.0) - if zerovalue is None: - zerovalue = 0.0 - - precip_data = 10.0 ** (precip_data / 10.0) - threshold = 10.0 ** (threshold / 10.0) - precip_data[precip_data < threshold] = zerovalue - - del metadata["transform"] - metadata["threshold"] = threshold - metadata["zerovalue"] = zerovalue - - dataset[precip_var].data[:] = precip_data - - return dataset - - -def NQ_transform(dataset: xr.Dataset, inverse: bool = False, **kwargs) -> xr.Dataset: - """ - The normal quantile transformation as in Bogner et al (2012). - Zero rain vales are set to zero in norm space. - - Parameters - ---------- - dataset: xarray.Dataset - Dataset to be transformed as described in the documentation of - :py:mod:`pysteps.io.importers`. - inverse: bool, optional - If set to True, it performs the inverse transform. False by default. - - Other Parameters - ---------------- - a: float, optional - The offset fraction to be used for plotting positions; - typically in (0,1). - The default is 0., that is, it spaces the points evenly in the uniform - distribution. - - Returns - ------- - dataset: xarray.Dataset - Dataset containing the (back-)transformed units. - - References - ---------- - Bogner, K., Pappenberger, F., and Cloke, H. L.: Technical Note: The normal - quantile transformation and its application in a flood forecasting system, - Hydrol. Earth Syst. Sci., 16, 1085-1094, - https://doi.org/10.5194/hess-16-1085-2012, 2012. - """ - - # defaults - a = kwargs.get("a", 0.0) - - dataset = dataset.copy(deep=True) - precip_var = dataset.attrs["precip_var"] - metadata = dataset[precip_var].attrs - precip_data = dataset[precip_var].values - - shape0 = precip_data.shape - precip_data = precip_data.ravel().astype(float) - idxNan = np.isnan(precip_data) - precip_data_ = precip_data[~idxNan] - - if not inverse: - # Plotting positions - # https://en.wikipedia.org/wiki/Q%E2%80%93Q_plot#Plotting_position - n = precip_data_.size - Rpp = ((np.arange(n) + 1 - a) / (n + 1 - 2 * a)).reshape(precip_data_.shape) - - # NQ transform - Rqn = scipy_stats.norm.ppf(Rpp) - precip_data__ = np.interp( - precip_data_, precip_data_[np.argsort(precip_data_)], Rqn - ) - - # set zero rain to 0 in norm space - precip_data__[precip_data[~idxNan] == metadata["zerovalue"]] = 0 - - # build inverse transform - metadata["inqt"] = interp1d( - Rqn, - precip_data_[np.argsort(precip_data_)], - bounds_error=False, - fill_value=(precip_data_.min(), precip_data_.max()), - ) - - metadata["transform"] = "NQT" - metadata["zerovalue"] = 0 - metadata["threshold"] = precip_data__[precip_data__ > 0].min() - - else: - f = metadata.pop("inqt") - precip_data__ = f(precip_data_) - del metadata["transform"] - metadata["zerovalue"] = precip_data__.min() - metadata["threshold"] = precip_data__[precip_data__ > precip_data__.min()].min() - - precip_data[~idxNan] = precip_data__ - - dataset[precip_var].data[:] = precip_data.reshape(shape0) - - return dataset - - -def sqrt_transform(dataset: xr.Dataset, inverse: bool = False, **kwargs) -> xr.Dataset: - """ - Square-root transform. - - Parameters - ---------- - dataset: xarray.Dataset - Dataset to be transformed as described in the documentation of - :py:mod:`pysteps.io.importers`. - inverse: bool, optional - If set to True, it performs the inverse transform. False by default. - - Returns - ------- - dataset: xarray.Dataset - Dataset containing the (back-)transformed units. - - """ - - dataset = dataset.copy(deep=True) - precip_var = dataset.attrs["precip_var"] - metadata = dataset[precip_var].attrs - precip_data = dataset[precip_var].values - - if not inverse: - # sqrt transform - precip_data = np.sqrt(precip_data) - - metadata["transform"] = "sqrt" - metadata["zerovalue"] = np.sqrt(metadata["zerovalue"]) - metadata["threshold"] = np.sqrt(metadata["threshold"]) - else: - # inverse sqrt transform - precip_data = precip_data**2 - - del metadata["transform"] - metadata["zerovalue"] = metadata["zerovalue"] ** 2 - metadata["threshold"] = metadata["threshold"] ** 2 - - dataset[precip_var].data[:] = precip_data - - return dataset +# -*- coding: utf-8 -*- +""" +pysteps.utils.transformation +============================ + +Methods for transforming data values. + +.. autosummary:: + :toctree: ../generated/ + + boxcox_transform + dB_transform + NQ_transform + sqrt_transform +""" + +import warnings + +import numpy as np +import scipy.stats as scipy_stats +import xarray as xr +from scipy.interpolate import interp1d + +warnings.filterwarnings( + "ignore", category=RuntimeWarning +) # To deactivate warnings for comparison operators with NaNs + + +def boxcox_transform( + dataset: xr.Dataset, Lambda=None, threshold=None, zerovalue=None, inverse=False +) -> xr.Dataset: + """ + The one-parameter Box-Cox transformation. + + The Box-Cox transform is a well-known power transformation introduced by + Box and Cox (1964). In its one-parameter version, the Box-Cox transform + takes the form T(x) = ln(x) for Lambda = 0, + or T(x) = (x**Lambda - 1)/Lambda otherwise. + + Default parameters will produce a log transform (i.e. Lambda=0). + + Parameters + ---------- + dataset: xarray.Dataset + Dataset to be transformed as described in the documentation of + :py:mod:`pysteps.io.importers`. + Lambda: float, optional + Parameter Lambda of the Box-Cox transformation. + It is 0 by default, which produces the log transformation. + + Choose Lambda < 1 for positively skewed data, Lambda > 1 for negatively + skewed data. + threshold: float, optional + The value that is used for thresholding with the same units as in the dataset. + If None, the threshold contained in metadata is used. + If no threshold is found in the metadata, + a value of 0.1 is used as default. + zerovalue: float, optional + The value to be assigned to no rain pixels as defined by the threshold. + It is equal to the threshold - 1 by default. + inverse: bool, optional + If set to True, it performs the inverse transform. False by default. + + Returns + ------- + dataset: xarray.Dataset + Dataset containing the (back-)transformed units. + + References + ---------- + Box, G. E. and Cox, D. R. (1964), An Analysis of Transformations. Journal + of the Royal Statistical Society: Series B (Methodological), 26: 211-243. + doi:10.1111/j.2517-6161.1964.tb00553.x + """ + + dataset = dataset.copy(deep=True) + precip_var = dataset.attrs["precip_var"] + metadata = dataset[precip_var].attrs + precip_data = dataset[precip_var].values + + if not inverse: + if "transform" in metadata and metadata["transform"] == "BoxCox": + return dataset + + if Lambda is None: + Lambda = metadata.get("BoxCox_lambda", 0.0) + + if threshold is None: + threshold = metadata.get("threshold", 0.1) + + zeros = precip_data < threshold + + # Apply Box-Cox transform + if Lambda == 0.0: + precip_data[~zeros] = np.log(precip_data[~zeros]) + threshold = np.log(threshold) + + else: + precip_data[~zeros] = (precip_data[~zeros] ** Lambda - 1) / Lambda + threshold = (threshold**Lambda - 1) / Lambda + + # Set value for zeros + if zerovalue is None: + zerovalue = threshold - 1 # TODO: set to a more meaningful value + precip_data[zeros] = zerovalue + + metadata["transform"] = "BoxCox" + metadata["BoxCox_lambda"] = Lambda + metadata["zerovalue"] = zerovalue + metadata["threshold"] = threshold + + elif inverse: + if metadata["transform"] not in ["BoxCox", "log"]: + return precip_data, metadata + + if Lambda is None: + Lambda = metadata.pop("BoxCox_lambda", 0.0) + if threshold is None: + threshold = metadata.get("threshold", -10.0) + if zerovalue is None: + zerovalue = 0.0 + + # Apply inverse Box-Cox transform + if Lambda == 0.0: + precip_data = np.exp(precip_data) + threshold = np.exp(threshold) + + else: + precip_data = np.exp(np.log(Lambda * precip_data + 1) / Lambda) + threshold = np.exp(np.log(Lambda * threshold + 1) / Lambda) + + precip_data[precip_data < threshold] = zerovalue + + del metadata["transform"] + metadata["zerovalue"] = zerovalue + metadata["threshold"] = threshold + + dataset[precip_var].data[:] = precip_data + + return dataset + + +def dB_transform( + dataset: xr.Dataset, threshold=None, zerovalue=None, inverse=False +) -> xr.Dataset: + """Methods to transform precipitation intensities to/from dB units. + + Parameters + ---------- + dataset: xarray.Dataset + Dataset to be (back-)transformed as described in the documentation of + :py:mod:`pysteps.io.importers`. + threshold: float, optional + Optional value that is used for thresholding with the same units as in the dataset. + If None, the threshold contained in metadata is used. + If no threshold is found in the metadata, + a value of 0.1 is used as default. + zerovalue: float, optional + The value to be assigned to no rain pixels as defined by the threshold. + It is equal to the threshold - 1 by default. + inverse: bool, optional + If set to True, it performs the inverse transform. False by default. + + Returns + ------- + dataset: xarray.Dataset + Dataset containing the (back-)transformed units. + """ + + dataset = dataset.copy(deep=True) + precip_var = dataset.attrs["precip_var"] + metadata = dataset[precip_var].attrs + precip_data = dataset[precip_var].values + + # to dB units + if not inverse: + if "transform" in metadata and metadata["transform"] == "dB": + return dataset + + if threshold is None: + threshold = metadata.get("threshold", 0.1) + + zeros = precip_data < threshold + + # Convert to dB + precip_data[~zeros] = 10.0 * np.log10(precip_data[~zeros]) + threshold = 10.0 * np.log10(threshold) + + # Set value for zeros + if zerovalue is None: + zerovalue = threshold - 5 # TODO: set to a more meaningful value + precip_data[zeros] = zerovalue + + metadata["transform"] = "dB" + metadata["zerovalue"] = zerovalue + metadata["threshold"] = threshold + + # from dB units + elif inverse: + if metadata["transform"] != "dB": + return dataset + + if threshold is None: + threshold = metadata.get("threshold", -10.0) + if zerovalue is None: + zerovalue = 0.0 + + precip_data = 10.0 ** (precip_data / 10.0) + threshold = 10.0 ** (threshold / 10.0) + precip_data[precip_data < threshold] = zerovalue + + del metadata["transform"] + metadata["threshold"] = threshold + metadata["zerovalue"] = zerovalue + + dataset[precip_var].data[:] = precip_data + + return dataset + + +def NQ_transform(dataset: xr.Dataset, inverse: bool = False, **kwargs) -> xr.Dataset: + """ + The normal quantile transformation as in Bogner et al (2012). + Zero rain vales are set to zero in norm space. + + Parameters + ---------- + dataset: xarray.Dataset + Dataset to be transformed as described in the documentation of + :py:mod:`pysteps.io.importers`. + inverse: bool, optional + If set to True, it performs the inverse transform. False by default. + + Other Parameters + ---------------- + a: float, optional + The offset fraction to be used for plotting positions; + typically in (0,1). + The default is 0., that is, it spaces the points evenly in the uniform + distribution. + + Returns + ------- + dataset: xarray.Dataset + Dataset containing the (back-)transformed units. + + References + ---------- + Bogner, K., Pappenberger, F., and Cloke, H. L.: Technical Note: The normal + quantile transformation and its application in a flood forecasting system, + Hydrol. Earth Syst. Sci., 16, 1085-1094, + https://doi.org/10.5194/hess-16-1085-2012, 2012. + """ + + # defaults + a = kwargs.get("a", 0.0) + + dataset = dataset.copy(deep=True) + precip_var = dataset.attrs["precip_var"] + metadata = dataset[precip_var].attrs + precip_data = dataset[precip_var].values + + shape0 = precip_data.shape + precip_data = precip_data.ravel().astype(float) + idxNan = np.isnan(precip_data) + precip_data_ = precip_data[~idxNan] + + if not inverse: + # Plotting positions + # https://en.wikipedia.org/wiki/Q%E2%80%93Q_plot#Plotting_position + n = precip_data_.size + Rpp = ((np.arange(n) + 1 - a) / (n + 1 - 2 * a)).reshape(precip_data_.shape) + + # NQ transform + Rqn = scipy_stats.norm.ppf(Rpp) + precip_data__ = np.interp( + precip_data_, precip_data_[np.argsort(precip_data_)], Rqn + ) + + # set zero rain to 0 in norm space + precip_data__[precip_data[~idxNan] == metadata["zerovalue"]] = 0 + + # build inverse transform + metadata["inqt"] = interp1d( + Rqn, + precip_data_[np.argsort(precip_data_)], + bounds_error=False, + fill_value=(precip_data_.min(), precip_data_.max()), + ) + + metadata["transform"] = "NQT" + metadata["zerovalue"] = 0 + metadata["threshold"] = precip_data__[precip_data__ > 0].min() + + else: + f = metadata.pop("inqt") + precip_data__ = f(precip_data_) + del metadata["transform"] + metadata["zerovalue"] = precip_data__.min() + metadata["threshold"] = precip_data__[precip_data__ > precip_data__.min()].min() + + precip_data[~idxNan] = precip_data__ + + dataset[precip_var].data[:] = precip_data.reshape(shape0) + + return dataset + + +def sqrt_transform(dataset: xr.Dataset, inverse: bool = False, **kwargs) -> xr.Dataset: + """ + Square-root transform. + + Parameters + ---------- + dataset: xarray.Dataset + Dataset to be transformed as described in the documentation of + :py:mod:`pysteps.io.importers`. + inverse: bool, optional + If set to True, it performs the inverse transform. False by default. + + Returns + ------- + dataset: xarray.Dataset + Dataset containing the (back-)transformed units. + + """ + + dataset = dataset.copy(deep=True) + precip_var = dataset.attrs["precip_var"] + metadata = dataset[precip_var].attrs + precip_data = dataset[precip_var].values + + if not inverse: + # sqrt transform + precip_data = np.sqrt(precip_data) + + metadata["transform"] = "sqrt" + metadata["zerovalue"] = np.sqrt(metadata["zerovalue"]) + metadata["threshold"] = np.sqrt(metadata["threshold"]) + else: + # inverse sqrt transform + precip_data = precip_data**2 + + del metadata["transform"] + metadata["zerovalue"] = metadata["zerovalue"] ** 2 + metadata["threshold"] = metadata["threshold"] ** 2 + + dataset[precip_var].data[:] = precip_data + + return dataset diff --git a/pysteps/verification/__init__.py b/pysteps/verification/__init__.py index 2a434e550..1d506ea36 100644 --- a/pysteps/verification/__init__.py +++ b/pysteps/verification/__init__.py @@ -1,11 +1,11 @@ # -- coding: utf-8 -- """Methods for verification of deterministic, probabilistic and ensemble forecasts.""" -from .interface import get_method from .detcatscores import * from .detcontscores import * from .ensscores import * +from .interface import get_method from .plots import * from .probscores import * -from .spatialscores import * from .salscores import * +from .spatialscores import * diff --git a/pysteps/verification/detcatscores.py b/pysteps/verification/detcatscores.py index 83a8d1512..9e12b7e48 100644 --- a/pysteps/verification/detcatscores.py +++ b/pysteps/verification/detcatscores.py @@ -17,6 +17,7 @@ """ import collections + import numpy as np diff --git a/pysteps/verification/detcontscores.py b/pysteps/verification/detcontscores.py index 25d989016..cdfdad5a3 100644 --- a/pysteps/verification/detcontscores.py +++ b/pysteps/verification/detcontscores.py @@ -16,6 +16,7 @@ """ import collections + import numpy as np from scipy.stats import spearmanr diff --git a/pysteps/verification/ensscores.py b/pysteps/verification/ensscores.py index df6b00088..dbb9791d1 100644 --- a/pysteps/verification/ensscores.py +++ b/pysteps/verification/ensscores.py @@ -17,6 +17,7 @@ """ import numpy as np + from .interface import get_method diff --git a/pysteps/verification/interface.py b/pysteps/verification/interface.py index 6e325f6f4..b64a75ccb 100644 --- a/pysteps/verification/interface.py +++ b/pysteps/verification/interface.py @@ -166,8 +166,8 @@ def get_method(name, type="deterministic"): if type == "deterministic": from .detcatscores import det_cat_fct from .detcontscores import det_cont_fct - from .spatialscores import binary_mse, fss from .salscores import sal + from .spatialscores import binary_mse, fss # categorical if name in [ @@ -236,7 +236,7 @@ def f(fct, obs, **kwargs): raise ValueError("unknown ensemble method %s" % name) elif type == "probabilistic": - from .probscores import CRPS, reldiag, ROC_curve + from .probscores import CRPS, ROC_curve, reldiag if name == "crps": return CRPS diff --git a/pysteps/verification/lifetime.py b/pysteps/verification/lifetime.py index 4c5dcb03f..482ec0163 100644 --- a/pysteps/verification/lifetime.py +++ b/pysteps/verification/lifetime.py @@ -1,157 +1,158 @@ -# -- coding: utf-8 -- -""" -pysteps.verification.lifetime -============================= - -Estimation of precipitation lifetime from -a decaying verification score function -(e.g. autocorrelation function). - -.. autosummary:: - :toctree: ../generated/ - - lifetime - lifetime_init - lifetime_accum - lifetime_compute -""" - -from math import exp -import numpy as np -from scipy.integrate import simpson - - -def lifetime(X_s, X_t, rule="1/e"): - """ - Compute the average lifetime by integrating the correlation function - as a function of lead time. When not using the 1/e rule, the correlation - function must be long enough to converge to 0, otherwise the lifetime is - underestimated. The correlation function can be either empirical or - theoretical, e.g. derived using the function 'ar_acf' - in timeseries/autoregression.py. - - Parameters - ---------- - X_s: array-like - Array with the correlation function. - Works also with other decaying scores that are defined - in the range [0,1]=[min_skill,max_skill]. - X_t: array-like - Array with the forecast lead times in the desired unit, - e.g. [min, hour]. - rule: str {'1/e', 'trapz', 'simpson'}, optional - Name of the method to integrate the correlation curve. \n - '1/e' uses the 1/e rule and assumes an exponential decay. It linearly - interpolates the time when the correlation goes below the value 1/e. - When all values are > 1/e it returns the max lead time. - When all values are < 1/e it returns the min lead time. \n - 'trapz' uses the trapezoidal rule for integration.\n - 'simpson' uses the Simpson's rule for integration. - - Returns - ------- - lf: float - Estimated lifetime with same units of X_t. - """ - X_s = X_s.copy() - X_t = X_t.copy() - life = lifetime_init(rule) - lifetime_accum(life, X_s, X_t) - return lifetime_compute(life) - - -def lifetime_init(rule="1/e"): - """ - Initialize a lifetime object. - - Parameters - ---------- - rule: str {'1/e', 'trapz', 'simpson'}, optional - Name of the method to integrate the correlation curve. \n - '1/e' uses the 1/e rule and assumes an exponential decay. It linearly - interpolates the time when the correlation goes below the value 1/e. - When all values are > 1/e it returns the max lead time. - When all values are < 1/e it returns the min lead time.\n - 'trapz' uses the trapezoidal rule for integration.\n - 'simpson' uses the Simpson's rule for integration. - - Returns - ------- - out: dict - The lifetime object. - """ - list_rules = ["trapz", "simpson", "1/e"] - if rule not in list_rules: - raise ValueError( - "Unknown rule %s for integration.\n" % rule - + "The available methods are: " - + str(list_rules) - ) - - lifetime = {} - lifetime["lifetime_sum"] = 0.0 - lifetime["n"] = 0.0 - lifetime["rule"] = rule - return lifetime - - -def lifetime_accum(lifetime, X_s, X_t): - """ - Compute the lifetime by integrating the correlation function - and accumulate the result into the given lifetime object. - - Parameters - ---------- - X_s: array-like - Array with the correlation function. - Works also with other decaying scores that are defined - in the range [0,1]=[min_skill,max_skill]. - X_t: array-like - Array with the forecast lead times in the desired unit, - e.g. [min, hour]. - """ - if lifetime["rule"] == "trapz": - lf = np.trapz(X_s, x=X_t) - elif lifetime["rule"] == "simpson": - lf = simpson(X_s, x=X_t) - elif lifetime["rule"] == "1/e": - euler_number = 1.0 / exp(1.0) - X_s_ = np.array(X_s) - - is_euler_reached = np.sum(X_s_ <= euler_number) > 0 - if is_euler_reached: - idx_b = np.argmax(X_s_ <= euler_number) - if idx_b > 0: - idx_a = idx_b - 1 - fraction_score = ( - (euler_number - X_s[idx_b]) - * (X_t[idx_a] - X_t[idx_b]) - / (X_s[idx_a] - X_s[idx_b]) - ) - lf = X_t[idx_b] + fraction_score - else: - # if all values are below the 1/e value, return min lead time - lf = np.min(X_t) - else: - # if all values are above the 1/e value, return max lead time - lf = np.max(X_t) - - lifetime["lifetime_sum"] += lf - lifetime["n"] += 1 - - -def lifetime_compute(lifetime): - """ - Compute the average value from the lifetime object. - - Parameters - ---------- - lifetime: dict - A lifetime object created with lifetime_init. - - Returns - ------- - out: float - The computed lifetime. - """ - return 1.0 * lifetime["lifetime_sum"] / lifetime["n"] +# -- coding: utf-8 -- +""" +pysteps.verification.lifetime +============================= + +Estimation of precipitation lifetime from +a decaying verification score function +(e.g. autocorrelation function). + +.. autosummary:: + :toctree: ../generated/ + + lifetime + lifetime_init + lifetime_accum + lifetime_compute +""" + +from math import exp + +import numpy as np +from scipy.integrate import simpson + + +def lifetime(X_s, X_t, rule="1/e"): + """ + Compute the average lifetime by integrating the correlation function + as a function of lead time. When not using the 1/e rule, the correlation + function must be long enough to converge to 0, otherwise the lifetime is + underestimated. The correlation function can be either empirical or + theoretical, e.g. derived using the function 'ar_acf' + in timeseries/autoregression.py. + + Parameters + ---------- + X_s: array-like + Array with the correlation function. + Works also with other decaying scores that are defined + in the range [0,1]=[min_skill,max_skill]. + X_t: array-like + Array with the forecast lead times in the desired unit, + e.g. [min, hour]. + rule: str {'1/e', 'trapz', 'simpson'}, optional + Name of the method to integrate the correlation curve. \n + '1/e' uses the 1/e rule and assumes an exponential decay. It linearly + interpolates the time when the correlation goes below the value 1/e. + When all values are > 1/e it returns the max lead time. + When all values are < 1/e it returns the min lead time. \n + 'trapz' uses the trapezoidal rule for integration.\n + 'simpson' uses the Simpson's rule for integration. + + Returns + ------- + lf: float + Estimated lifetime with same units of X_t. + """ + X_s = X_s.copy() + X_t = X_t.copy() + life = lifetime_init(rule) + lifetime_accum(life, X_s, X_t) + return lifetime_compute(life) + + +def lifetime_init(rule="1/e"): + """ + Initialize a lifetime object. + + Parameters + ---------- + rule: str {'1/e', 'trapz', 'simpson'}, optional + Name of the method to integrate the correlation curve. \n + '1/e' uses the 1/e rule and assumes an exponential decay. It linearly + interpolates the time when the correlation goes below the value 1/e. + When all values are > 1/e it returns the max lead time. + When all values are < 1/e it returns the min lead time.\n + 'trapz' uses the trapezoidal rule for integration.\n + 'simpson' uses the Simpson's rule for integration. + + Returns + ------- + out: dict + The lifetime object. + """ + list_rules = ["trapz", "simpson", "1/e"] + if rule not in list_rules: + raise ValueError( + "Unknown rule %s for integration.\n" % rule + + "The available methods are: " + + str(list_rules) + ) + + lifetime = {} + lifetime["lifetime_sum"] = 0.0 + lifetime["n"] = 0.0 + lifetime["rule"] = rule + return lifetime + + +def lifetime_accum(lifetime, X_s, X_t): + """ + Compute the lifetime by integrating the correlation function + and accumulate the result into the given lifetime object. + + Parameters + ---------- + X_s: array-like + Array with the correlation function. + Works also with other decaying scores that are defined + in the range [0,1]=[min_skill,max_skill]. + X_t: array-like + Array with the forecast lead times in the desired unit, + e.g. [min, hour]. + """ + if lifetime["rule"] == "trapz": + lf = np.trapz(X_s, x=X_t) + elif lifetime["rule"] == "simpson": + lf = simpson(X_s, x=X_t) + elif lifetime["rule"] == "1/e": + euler_number = 1.0 / exp(1.0) + X_s_ = np.array(X_s) + + is_euler_reached = np.sum(X_s_ <= euler_number) > 0 + if is_euler_reached: + idx_b = np.argmax(X_s_ <= euler_number) + if idx_b > 0: + idx_a = idx_b - 1 + fraction_score = ( + (euler_number - X_s[idx_b]) + * (X_t[idx_a] - X_t[idx_b]) + / (X_s[idx_a] - X_s[idx_b]) + ) + lf = X_t[idx_b] + fraction_score + else: + # if all values are below the 1/e value, return min lead time + lf = np.min(X_t) + else: + # if all values are above the 1/e value, return max lead time + lf = np.max(X_t) + + lifetime["lifetime_sum"] += lf + lifetime["n"] += 1 + + +def lifetime_compute(lifetime): + """ + Compute the average value from the lifetime object. + + Parameters + ---------- + lifetime: dict + A lifetime object created with lifetime_init. + + Returns + ------- + out: float + The computed lifetime. + """ + return 1.0 * lifetime["lifetime_sum"] / lifetime["n"] diff --git a/pysteps/verification/plots.py b/pysteps/verification/plots.py index 21974b013..3f27dcfd0 100755 --- a/pysteps/verification/plots.py +++ b/pysteps/verification/plots.py @@ -14,10 +14,11 @@ plot_ROC """ -from matplotlib import cm import matplotlib.pylab as plt -from mpl_toolkits.axes_grid1.inset_locator import inset_axes import numpy as np +from matplotlib import cm +from mpl_toolkits.axes_grid1.inset_locator import inset_axes + from pysteps.verification import ensscores, probscores, spatialscores diff --git a/pysteps/verification/salscores.py b/pysteps/verification/salscores.py index f2d97b77c..8b2743d04 100644 --- a/pysteps/verification/salscores.py +++ b/pysteps/verification/salscores.py @@ -14,7 +14,7 @@ sal_location """ -from math import sqrt, hypot +from math import hypot, sqrt import numpy as np from scipy.ndimage import center_of_mass diff --git a/pysteps/verification/spatialscores.py b/pysteps/verification/spatialscores.py index 41e02c93e..a4d505dab 100644 --- a/pysteps/verification/spatialscores.py +++ b/pysteps/verification/spatialscores.py @@ -26,6 +26,7 @@ """ import collections + import numpy as np from scipy.ndimage import uniform_filter diff --git a/pysteps/visualization/__init__.py b/pysteps/visualization/__init__.py index c76f94c15..fd884d17f 100644 --- a/pysteps/visualization/__init__.py +++ b/pysteps/visualization/__init__.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- """Methods for plotting precipitation and motion fields.""" +from .animations import * from .motionfields import * from .precipfields import * -from .animations import * from .spectral import * from .thunderstorms import * diff --git a/pysteps/visualization/animations.py b/pysteps/visualization/animations.py index ef8d7583c..3e1ae304a 100644 --- a/pysteps/visualization/animations.py +++ b/pysteps/visualization/animations.py @@ -1,332 +1,332 @@ -# -*- coding: utf-8 -*- -""" -pysteps.visualization.animations -================================ - -Functions to produce animations for pysteps. - -.. autosummary:: - :toctree: ../generated/ - - animate -""" - -import os -import warnings - -import matplotlib.pylab as plt -import pysteps as st - -PRECIP_VALID_TYPES = ("ensemble", "mean", "prob") -MOTION_VALID_METHODS = ("quiver", "streamplot") - - -def animate( - precip_obs, - precip_fct=None, - timestamps_obs=None, - timestep_min=None, - motion_field=None, - ptype="ensemble", - motion_plot="quiver", - geodata=None, - title=None, - prob_thr=None, - display_animation=True, - nloops=1, - time_wait=0.2, - savefig=False, - fig_dpi=100, - fig_format="png", - path_outputs="", - precip_kwargs=None, - motion_kwargs=None, - map_kwargs=None, -): - """ - Function to animate observations and forecasts in pysteps. - - It also allows to export the individual frames as figures, which - is useful for constructing animated GIFs or similar. - - .. _Axes: https://matplotlib.org/api/axes_api.html#matplotlib.axes.Axes - - Parameters - ---------- - precip_obs: array-like - Three-dimensional array containing the time series of observed - precipitation fields. - precip_fct: array-like, optional - The three or four-dimensional (for ensembles) array - containing the time series of forecasted precipitation field. - timestamps_obs: list of datetimes, optional - List of datetime objects corresponding to the time stamps of - the fields in precip_obs. - timestep_min: float, optional - The time resolution in minutes of the forecast. - motion_field: array-like, optional - Three-dimensional array containing the u and v components of - the motion field. - motion_plot: string, optional - The method to plot the motion field. See plot methods in - :py:mod:`pysteps.visualization.motionfields`. - geodata: dictionary or None, optional - Dictionary containing geographical information about - the field. - If geodata is not None, it must contain the following key-value pairs: - - .. tabularcolumns:: |p{1.5cm}|L| - - +----------------+----------------------------------------------------+ - | Key | Value | - +================+====================================================+ - | projection | PROJ.4-compatible projection definition | - +----------------+----------------------------------------------------+ - | x1 | x-coordinate of the lower-left corner of the data | - | | raster | - +----------------+----------------------------------------------------+ - | y1 | y-coordinate of the lower-left corner of the data | - | | raster | - +----------------+----------------------------------------------------+ - | x2 | x-coordinate of the upper-right corner of the data | - | | raster | - +----------------+----------------------------------------------------+ - | y2 | y-coordinate of the upper-right corner of the data | - | | raster | - +----------------+----------------------------------------------------+ - | yorigin | a string specifying the location of the first | - | | element in the data raster w.r.t. y-axis: | - | | 'upper' = upper border, 'lower' = lower border | - +----------------+----------------------------------------------------+ - - title: str or None, optional - If not None, print the string as title on top of the plot. - ptype: {'ensemble', 'mean', 'prob'}, str, optional - Type of the plot to animate. 'ensemble' = ensemble members, - 'mean' = ensemble mean, 'prob' = exceedance probability - (using threshold defined in prob_thrs). - prob_thr: float, optional - Intensity threshold for the exceedance probability maps. Applicable - if ptype = 'prob'. - display_animation: bool, optional - If set to True, display the animation (set to False if only - interested in saving the animation frames). - nloops: int, optional - The number of loops in the animation. - time_wait: float, optional - The time in seconds between one frame and the next. Applicable - if display_animation is True. - savefig: bool, optional - If set to True, save the individual frames into path_outputs. - fig_dpi: float, optional - The resolution in dots per inch. Applicable if savefig is True. - fig_format: str, optional - Filename extension. Applicable if savefig is True. - path_outputs: string, optional - Path to folder where to save the frames. Applicable if savefig is True. - precip_kwargs: dict, optional - Optional parameters that are supplied to - :py:func:`pysteps.visualization.precipfields.plot_precip_field`. - motion_kwargs: dict, optional - Optional parameters that are supplied to - :py:func:`pysteps.visualization.motionfields.quiver` or - :py:func:`pysteps.visualization.motionfields.streamplot`. - map_kwargs: dict, optional - Optional parameters that need to be passed to - :py:func:`pysteps.visualization.basemaps.plot_geography`. - - Returns - ------- - None - """ - - if precip_kwargs is None: - precip_kwargs = {} - - if motion_kwargs is None: - motion_kwargs = {} - - if map_kwargs is None: - map_kwargs = {} - - if precip_fct is not None: - if len(precip_fct.shape) == 3: - precip_fct = precip_fct[None, ...] - n_lead_times = precip_fct.shape[1] - n_members = precip_fct.shape[0] - else: - n_lead_times = 0 - n_members = 1 - - if title is not None and isinstance(title, str): - title_first_line = title + "\n" - else: - title_first_line = "" - - if motion_plot not in MOTION_VALID_METHODS: - raise ValueError( - f"Invalid motion plot method '{motion_plot}'." - f"Supported: {str(MOTION_VALID_METHODS)}" - ) - - if ptype not in PRECIP_VALID_TYPES: - raise ValueError( - f"Invalid precipitation type '{ptype}'." - f"Supported: {str(PRECIP_VALID_TYPES)}" - ) - - if timestamps_obs is not None: - if len(timestamps_obs) != precip_obs.shape[0]: - raise ValueError( - f"The number of timestamps does not match the size of precip_obs: " - f"{len(timestamps_obs)} != {precip_obs.shape[0]}" - ) - if precip_fct is not None: - reftime_str = timestamps_obs[-1].strftime("%Y%m%d%H%M") - else: - reftime_str = timestamps_obs[0].strftime("%Y%m%d%H%M") - else: - reftime_str = None - - if ptype == "prob" and prob_thr is None: - raise ValueError("ptype 'prob' needs a prob_thr value") - - if ptype != "ensemble": - n_members = 1 - - n_obs = precip_obs.shape[0] - - loop = 0 - while loop < nloops: - for n in range(n_members): - for i in range(n_obs + n_lead_times): - plt.clf() - - # Observations - if i < n_obs and (display_animation or n == 0): - title = title_first_line + "Analysis" - if timestamps_obs is not None: - title += ( - f" valid for {timestamps_obs[i].strftime('%Y-%m-%d %H:%M')}" - ) - - plt.clf() - if ptype == "prob": - prob_field = st.postprocessing.ensemblestats.excprob( - precip_obs[None, i, ...], prob_thr - ) - ax = st.plt.plot_precip_field( - prob_field, - ptype="prob", - geodata=geodata, - probthr=prob_thr, - title=title, - map_kwargs=map_kwargs, - **precip_kwargs, - ) - else: - ax = st.plt.plot_precip_field( - precip_obs[i, :, :], - geodata=geodata, - title=title, - map_kwargs=map_kwargs, - **precip_kwargs, - ) - - if motion_field is not None: - if motion_plot == "quiver": - st.plt.quiver( - motion_field, ax=ax, geodata=geodata, **motion_kwargs - ) - elif motion_plot == "streamplot": - st.plt.streamplot( - motion_field, ax=ax, geodata=geodata, **motion_kwargs - ) - - if savefig & (loop == 0): - figtags = [reftime_str, ptype, f"f{i:02d}"] - figname = "_".join([tag for tag in figtags if tag]) - filename = os.path.join(path_outputs, f"{figname}.{fig_format}") - plt.savefig(filename, bbox_inches="tight", dpi=fig_dpi) - print("saved: ", filename) - - # Forecasts - elif i >= n_obs and precip_fct is not None: - title = title_first_line + "Forecast" - if timestamps_obs is not None: - title += f" valid for {timestamps_obs[-1].strftime('%Y-%m-%d %H:%M')}" - if timestep_min is not None: - title += " +%02d min" % ((1 + i - n_obs) * timestep_min) - else: - title += " +%02d" % (1 + i - n_obs) - - plt.clf() - if ptype == "prob": - prob_field = st.postprocessing.ensemblestats.excprob( - precip_fct[:, i - n_obs, :, :], prob_thr - ) - ax = st.plt.plot_precip_field( - prob_field, - ptype="prob", - geodata=geodata, - probthr=prob_thr, - title=title, - map_kwargs=map_kwargs, - **precip_kwargs, - ) - elif ptype == "mean": - ens_mean = st.postprocessing.ensemblestats.mean( - precip_fct[:, i - n_obs, :, :] - ) - ax = st.plt.plot_precip_field( - ens_mean, - geodata=geodata, - title=title, - map_kwargs=map_kwargs, - **precip_kwargs, - ) - else: - ax = st.plt.plot_precip_field( - precip_fct[n, i - n_obs, ...], - geodata=geodata, - title=title, - map_kwargs=map_kwargs, - **precip_kwargs, - ) - - if motion_field is not None: - if motion_plot == "quiver": - st.plt.quiver( - motion_field, ax=ax, geodata=geodata, **motion_kwargs - ) - elif motion_plot == "streamplot": - st.plt.streamplot( - motion_field, ax=ax, geodata=geodata, **motion_kwargs - ) - - if ptype == "ensemble" and n_members > 1: - plt.text( - 0.01, - 0.99, - "m %02d" % (n + 1), - transform=ax.transAxes, - ha="left", - va="top", - ) - - if savefig & (loop == 0): - figtags = [reftime_str, ptype, f"f{i:02d}", f"m{n + 1:02d}"] - figname = "_".join([tag for tag in figtags if tag]) - filename = os.path.join(path_outputs, f"{figname}.{fig_format}") - plt.savefig(filename, bbox_inches="tight", dpi=fig_dpi) - print("saved: ", filename) - - if display_animation: - plt.pause(time_wait) - - if display_animation: - plt.pause(2 * time_wait) - - loop += 1 - - plt.close() +# -*- coding: utf-8 -*- +""" +pysteps.visualization.animations +================================ + +Functions to produce animations for pysteps. + +.. autosummary:: + :toctree: ../generated/ + + animate +""" + +import os +import warnings + +import matplotlib.pylab as plt + +import pysteps as st + +PRECIP_VALID_TYPES = ("ensemble", "mean", "prob") +MOTION_VALID_METHODS = ("quiver", "streamplot") + + +def animate( + precip_obs, + precip_fct=None, + timestamps_obs=None, + timestep_min=None, + motion_field=None, + ptype="ensemble", + motion_plot="quiver", + geodata=None, + title=None, + prob_thr=None, + display_animation=True, + nloops=1, + time_wait=0.2, + savefig=False, + fig_dpi=100, + fig_format="png", + path_outputs="", + precip_kwargs=None, + motion_kwargs=None, + map_kwargs=None, +): + """ + Function to animate observations and forecasts in pysteps. + + It also allows to export the individual frames as figures, which + is useful for constructing animated GIFs or similar. + + .. _Axes: https://matplotlib.org/api/axes_api.html#matplotlib.axes.Axes + + Parameters + ---------- + precip_obs: array-like + Three-dimensional array containing the time series of observed + precipitation fields. + precip_fct: array-like, optional + The three or four-dimensional (for ensembles) array + containing the time series of forecasted precipitation field. + timestamps_obs: list of datetimes, optional + List of datetime objects corresponding to the time stamps of + the fields in precip_obs. + timestep_min: float, optional + The time resolution in minutes of the forecast. + motion_field: array-like, optional + Three-dimensional array containing the u and v components of + the motion field. + motion_plot: string, optional + The method to plot the motion field. See plot methods in + :py:mod:`pysteps.visualization.motionfields`. + geodata: dictionary or None, optional + Dictionary containing geographical information about + the field. + If geodata is not None, it must contain the following key-value pairs: + + .. tabularcolumns:: |p{1.5cm}|L| + + +----------------+----------------------------------------------------+ + | Key | Value | + +================+====================================================+ + | projection | PROJ.4-compatible projection definition | + +----------------+----------------------------------------------------+ + | x1 | x-coordinate of the lower-left corner of the data | + | | raster | + +----------------+----------------------------------------------------+ + | y1 | y-coordinate of the lower-left corner of the data | + | | raster | + +----------------+----------------------------------------------------+ + | x2 | x-coordinate of the upper-right corner of the data | + | | raster | + +----------------+----------------------------------------------------+ + | y2 | y-coordinate of the upper-right corner of the data | + | | raster | + +----------------+----------------------------------------------------+ + | yorigin | a string specifying the location of the first | + | | element in the data raster w.r.t. y-axis: | + | | 'upper' = upper border, 'lower' = lower border | + +----------------+----------------------------------------------------+ + + title: str or None, optional + If not None, print the string as title on top of the plot. + ptype: {'ensemble', 'mean', 'prob'}, str, optional + Type of the plot to animate. 'ensemble' = ensemble members, + 'mean' = ensemble mean, 'prob' = exceedance probability + (using threshold defined in prob_thrs). + prob_thr: float, optional + Intensity threshold for the exceedance probability maps. Applicable + if ptype = 'prob'. + display_animation: bool, optional + If set to True, display the animation (set to False if only + interested in saving the animation frames). + nloops: int, optional + The number of loops in the animation. + time_wait: float, optional + The time in seconds between one frame and the next. Applicable + if display_animation is True. + savefig: bool, optional + If set to True, save the individual frames into path_outputs. + fig_dpi: float, optional + The resolution in dots per inch. Applicable if savefig is True. + fig_format: str, optional + Filename extension. Applicable if savefig is True. + path_outputs: string, optional + Path to folder where to save the frames. Applicable if savefig is True. + precip_kwargs: dict, optional + Optional parameters that are supplied to + :py:func:`pysteps.visualization.precipfields.plot_precip_field`. + motion_kwargs: dict, optional + Optional parameters that are supplied to + :py:func:`pysteps.visualization.motionfields.quiver` or + :py:func:`pysteps.visualization.motionfields.streamplot`. + map_kwargs: dict, optional + Optional parameters that need to be passed to + :py:func:`pysteps.visualization.basemaps.plot_geography`. + + Returns + ------- + None + """ + + if precip_kwargs is None: + precip_kwargs = {} + + if motion_kwargs is None: + motion_kwargs = {} + + if map_kwargs is None: + map_kwargs = {} + + if precip_fct is not None: + if len(precip_fct.shape) == 3: + precip_fct = precip_fct[None, ...] + n_lead_times = precip_fct.shape[1] + n_members = precip_fct.shape[0] + else: + n_lead_times = 0 + n_members = 1 + + if title is not None and isinstance(title, str): + title_first_line = title + "\n" + else: + title_first_line = "" + + if motion_plot not in MOTION_VALID_METHODS: + raise ValueError( + f"Invalid motion plot method '{motion_plot}'." + f"Supported: {str(MOTION_VALID_METHODS)}" + ) + + if ptype not in PRECIP_VALID_TYPES: + raise ValueError( + f"Invalid precipitation type '{ptype}'.Supported: {str(PRECIP_VALID_TYPES)}" + ) + + if timestamps_obs is not None: + if len(timestamps_obs) != precip_obs.shape[0]: + raise ValueError( + f"The number of timestamps does not match the size of precip_obs: " + f"{len(timestamps_obs)} != {precip_obs.shape[0]}" + ) + if precip_fct is not None: + reftime_str = timestamps_obs[-1].strftime("%Y%m%d%H%M") + else: + reftime_str = timestamps_obs[0].strftime("%Y%m%d%H%M") + else: + reftime_str = None + + if ptype == "prob" and prob_thr is None: + raise ValueError("ptype 'prob' needs a prob_thr value") + + if ptype != "ensemble": + n_members = 1 + + n_obs = precip_obs.shape[0] + + loop = 0 + while loop < nloops: + for n in range(n_members): + for i in range(n_obs + n_lead_times): + plt.clf() + + # Observations + if i < n_obs and (display_animation or n == 0): + title = title_first_line + "Analysis" + if timestamps_obs is not None: + title += ( + f" valid for {timestamps_obs[i].strftime('%Y-%m-%d %H:%M')}" + ) + + plt.clf() + if ptype == "prob": + prob_field = st.postprocessing.ensemblestats.excprob( + precip_obs[None, i, ...], prob_thr + ) + ax = st.plt.plot_precip_field( + prob_field, + ptype="prob", + geodata=geodata, + probthr=prob_thr, + title=title, + map_kwargs=map_kwargs, + **precip_kwargs, + ) + else: + ax = st.plt.plot_precip_field( + precip_obs[i, :, :], + geodata=geodata, + title=title, + map_kwargs=map_kwargs, + **precip_kwargs, + ) + + if motion_field is not None: + if motion_plot == "quiver": + st.plt.quiver( + motion_field, ax=ax, geodata=geodata, **motion_kwargs + ) + elif motion_plot == "streamplot": + st.plt.streamplot( + motion_field, ax=ax, geodata=geodata, **motion_kwargs + ) + + if savefig & (loop == 0): + figtags = [reftime_str, ptype, f"f{i:02d}"] + figname = "_".join([tag for tag in figtags if tag]) + filename = os.path.join(path_outputs, f"{figname}.{fig_format}") + plt.savefig(filename, bbox_inches="tight", dpi=fig_dpi) + print("saved: ", filename) + + # Forecasts + elif i >= n_obs and precip_fct is not None: + title = title_first_line + "Forecast" + if timestamps_obs is not None: + title += f" valid for {timestamps_obs[-1].strftime('%Y-%m-%d %H:%M')}" + if timestep_min is not None: + title += " +%02d min" % ((1 + i - n_obs) * timestep_min) + else: + title += " +%02d" % (1 + i - n_obs) + + plt.clf() + if ptype == "prob": + prob_field = st.postprocessing.ensemblestats.excprob( + precip_fct[:, i - n_obs, :, :], prob_thr + ) + ax = st.plt.plot_precip_field( + prob_field, + ptype="prob", + geodata=geodata, + probthr=prob_thr, + title=title, + map_kwargs=map_kwargs, + **precip_kwargs, + ) + elif ptype == "mean": + ens_mean = st.postprocessing.ensemblestats.mean( + precip_fct[:, i - n_obs, :, :] + ) + ax = st.plt.plot_precip_field( + ens_mean, + geodata=geodata, + title=title, + map_kwargs=map_kwargs, + **precip_kwargs, + ) + else: + ax = st.plt.plot_precip_field( + precip_fct[n, i - n_obs, ...], + geodata=geodata, + title=title, + map_kwargs=map_kwargs, + **precip_kwargs, + ) + + if motion_field is not None: + if motion_plot == "quiver": + st.plt.quiver( + motion_field, ax=ax, geodata=geodata, **motion_kwargs + ) + elif motion_plot == "streamplot": + st.plt.streamplot( + motion_field, ax=ax, geodata=geodata, **motion_kwargs + ) + + if ptype == "ensemble" and n_members > 1: + plt.text( + 0.01, + 0.99, + "m %02d" % (n + 1), + transform=ax.transAxes, + ha="left", + va="top", + ) + + if savefig & (loop == 0): + figtags = [reftime_str, ptype, f"f{i:02d}", f"m{n + 1:02d}"] + figname = "_".join([tag for tag in figtags if tag]) + filename = os.path.join(path_outputs, f"{figname}.{fig_format}") + plt.savefig(filename, bbox_inches="tight", dpi=fig_dpi) + print("saved: ", filename) + + if display_animation: + plt.pause(time_wait) + + if display_animation: + plt.pause(2 * time_wait) + + loop += 1 + + plt.close() diff --git a/pysteps/visualization/basemaps.py b/pysteps/visualization/basemaps.py index 27b5ed3d4..fa1ced72c 100644 --- a/pysteps/visualization/basemaps.py +++ b/pysteps/visualization/basemaps.py @@ -1,299 +1,301 @@ -# -*- coding: utf-8 -*- -""" -pysteps.visualization.basemaps -============================== - -Methods for plotting geographical maps using Cartopy. - -.. autosummary:: - :toctree: ../generated/ - - plot_geography - plot_map_cartopy -""" - -from matplotlib import gridspec -import matplotlib.pylab as plt -import numpy as np -import warnings -from pysteps.exceptions import MissingOptionalDependency - -try: - import cartopy.crs as ccrs - import cartopy.feature as cfeature - from cartopy.mpl.geoaxes import GeoAxesSubplot - - CARTOPY_IMPORTED = True -except ImportError: - CARTOPY_IMPORTED = False -try: - import pyproj - - PYPROJ_IMPORTED = True -except ImportError: - PYPROJ_IMPORTED = False - -from . import utils - -VALID_BASEMAPS = ("cartopy",) - - -######################### -# Basemap features zorder -# - ocean: 0 -# - land: 0 -# - lakes: 0 -# - rivers_lake_centerlines: 0 -# - coastline: 15 -# - cultural: 15 -# - reefs: 15 -# - minor_islands: 15 - - -def plot_geography( - proj4str, - extent, - lw=0.5, - drawlonlatlines=False, - drawlonlatlabels=True, - plot_map="cartopy", - scale="50m", - subplot=None, - **kwargs, -): - """ - Plot geographical map in a chosen projection using cartopy. - - .. _SubplotSpec: https://matplotlib.org/api/_as_gen/matplotlib.gridspec.SubplotSpec.html - - Parameters - ---------- - proj4str: str - The PROJ.4-compatible projection string. - extent: scalars (left, right, bottom, top) - The bounding box in proj4str coordinates. - lw: float, optional` - Linewidth of the map (administrative boundaries and coastlines). - drawlonlatlines: bool, optional - If set to True, draw longitude and latitude lines. - drawlonlatlabels: bool, optional - If set to True, draw longitude and latitude labels. Valid only if - 'drawlonlatlines' is True. - plot_map: {'cartopy', None}, optional - The type of basemap, either 'cartopy' or None. If None, the figure - axis is returned without any basemap drawn. Default `'cartopy'`. - scale: {'10m', '50m', '110m'}, optional - The scale (resolution). Applicable if 'plot_map' is 'cartopy'. - The available options are '10m', '50m', and '110m'. Default ``'50m'``. - subplot: tuple of int (nrows, ncols, index) or SubplotSpec_ instance, optional - The subplot where to plot the basemap. - By the default, the basemap will replace the current axis. - - Returns - ------- - ax: fig Axes - Cartopy axes. - """ - - if len(kwargs) > 0: - warnings.warn( - "plot_geography: The following keywords are ignored:\n" - + str(kwargs) - + "\nIn version 1.5, passing unsupported arguments will raise an error.", - DeprecationWarning, - ) - - if plot_map is None: - return plt.gca() - - if plot_map not in VALID_BASEMAPS: - raise ValueError( - f"unsupported plot_map method {plot_map}. Supported basemaps: " - f"{VALID_BASEMAPS}" - ) - - if plot_map == "cartopy" and not CARTOPY_IMPORTED: - warnings.warn( - "The cartopy package is required to plot the geographical map but it is " - "not installed. Ignoring the geographic information." - ) - return plt.gca() - - if not PYPROJ_IMPORTED: - warnings.warn( - "the pyproj package is required to plot the geographical map " - "but it is not installed" - ) - return plt.gca() - - crs = utils.proj4_to_cartopy(proj4str) - - ax = plot_map_cartopy( - crs, - extent, - scale, - drawlonlatlines=drawlonlatlines, - drawlonlatlabels=drawlonlatlabels, - lw=lw, - subplot=subplot, - ) - - return ax - - -def plot_map_cartopy( - crs, - extent, - cartopy_scale, - drawlonlatlines=False, - drawlonlatlabels=True, - lw=0.5, - subplot=None, -): - """ - Plot coastlines, countries, rivers and meridians/parallels using cartopy. - - .. _SubplotSpec: https://matplotlib.org/api/_as_gen/matplotlib.gridspec.SubplotSpec.html - - Parameters - ---------- - crs: object - Instance of a crs class defined in cartopy.crs. - It can be created using utils.proj4_to_cartopy. - extent: scalars (left, right, bottom, top) - The coordinates of the bounding box. - drawlonlatlines: bool - Whether to plot longitudes and latitudes. - drawlonlatlabels: bool, optional - If set to True, draw longitude and latitude labels. Valid only if - 'drawlonlatlines' is True. - cartopy_scale: {'10m', '50m', '110m'} - The scale (resolution) of the map. The available options are '10m', - '50m', and '110m'. - lw: float - Line width. - subplot: tuple of int (nrows, ncols, index) or SubplotSpec_ instance, optional - The subplot where to place the basemap. - By the default, the basemap will replace the current axis. - - Returns - ------- - ax: axes - Cartopy axes. Compatible with matplotlib. - """ - if not CARTOPY_IMPORTED: - raise MissingOptionalDependency( - "the cartopy package is required to plot the geographical map" - " but it is not installed" - ) - - if subplot is None: - ax = plt.gca() - else: - if isinstance(subplot, gridspec.SubplotSpec): - ax = plt.subplot(subplot, projection=crs) - else: - ax = plt.subplot(*subplot, projection=crs) - - if not isinstance(ax, GeoAxesSubplot): - ax = plt.subplot(ax.get_subplotspec(), projection=crs) - # cax.clear() - ax.set_axis_off() - - ax.add_feature( - cfeature.NaturalEarthFeature( - "physical", - "ocean", - scale="50m" if cartopy_scale == "10m" else cartopy_scale, - edgecolor="none", - facecolor=np.array([0.59375, 0.71484375, 0.8828125]), - ), - zorder=0, - ) - ax.add_feature( - cfeature.NaturalEarthFeature( - "physical", - "land", - scale=cartopy_scale, - edgecolor="none", - facecolor=np.array([0.9375, 0.9375, 0.859375]), - ), - zorder=0, - ) - ax.add_feature( - cfeature.NaturalEarthFeature( - "physical", - "coastline", - scale=cartopy_scale, - edgecolor="black", - facecolor="none", - linewidth=lw, - ), - zorder=15, - ) - ax.add_feature( - cfeature.NaturalEarthFeature( - "physical", - "lakes", - scale=cartopy_scale, - edgecolor="none", - facecolor=np.array([0.59375, 0.71484375, 0.8828125]), - ), - zorder=0, - ) - ax.add_feature( - cfeature.NaturalEarthFeature( - "physical", - "rivers_lake_centerlines", - scale=cartopy_scale, - edgecolor=np.array([0.59375, 0.71484375, 0.8828125]), - facecolor="none", - ), - zorder=0, - ) - ax.add_feature( - cfeature.NaturalEarthFeature( - "cultural", - "admin_0_boundary_lines_land", - scale=cartopy_scale, - edgecolor="black", - facecolor="none", - linewidth=lw, - ), - zorder=15, - ) - if cartopy_scale in ["10m", "50m"]: - ax.add_feature( - cfeature.NaturalEarthFeature( - "physical", - "reefs", - scale="10m", - edgecolor="black", - facecolor="none", - linewidth=lw, - ), - zorder=15, - ) - ax.add_feature( - cfeature.NaturalEarthFeature( - "physical", - "minor_islands", - scale="10m", - edgecolor="black", - facecolor="none", - linewidth=lw, - ), - zorder=15, - ) - - if drawlonlatlines: - grid_lines = ax.gridlines( - crs=ccrs.PlateCarree(), draw_labels=drawlonlatlabels, dms=True - ) - grid_lines.top_labels = grid_lines.right_labels = False - grid_lines.y_inline = grid_lines.x_inline = False - grid_lines.rotate_labels = False - - ax.set_extent(extent, crs) - - return ax +# -*- coding: utf-8 -*- +""" +pysteps.visualization.basemaps +============================== + +Methods for plotting geographical maps using Cartopy. + +.. autosummary:: + :toctree: ../generated/ + + plot_geography + plot_map_cartopy +""" + +import warnings + +import matplotlib.pylab as plt +import numpy as np +from matplotlib import gridspec + +from pysteps.exceptions import MissingOptionalDependency + +try: + import cartopy.crs as ccrs + import cartopy.feature as cfeature + from cartopy.mpl.geoaxes import GeoAxesSubplot + + CARTOPY_IMPORTED = True +except ImportError: + CARTOPY_IMPORTED = False +try: + import pyproj + + PYPROJ_IMPORTED = True +except ImportError: + PYPROJ_IMPORTED = False + +from . import utils + +VALID_BASEMAPS = ("cartopy",) + + +######################### +# Basemap features zorder +# - ocean: 0 +# - land: 0 +# - lakes: 0 +# - rivers_lake_centerlines: 0 +# - coastline: 15 +# - cultural: 15 +# - reefs: 15 +# - minor_islands: 15 + + +def plot_geography( + proj4str, + extent, + lw=0.5, + drawlonlatlines=False, + drawlonlatlabels=True, + plot_map="cartopy", + scale="50m", + subplot=None, + **kwargs, +): + """ + Plot geographical map in a chosen projection using cartopy. + + .. _SubplotSpec: https://matplotlib.org/api/_as_gen/matplotlib.gridspec.SubplotSpec.html + + Parameters + ---------- + proj4str: str + The PROJ.4-compatible projection string. + extent: scalars (left, right, bottom, top) + The bounding box in proj4str coordinates. + lw: float, optional` + Linewidth of the map (administrative boundaries and coastlines). + drawlonlatlines: bool, optional + If set to True, draw longitude and latitude lines. + drawlonlatlabels: bool, optional + If set to True, draw longitude and latitude labels. Valid only if + 'drawlonlatlines' is True. + plot_map: {'cartopy', None}, optional + The type of basemap, either 'cartopy' or None. If None, the figure + axis is returned without any basemap drawn. Default `'cartopy'`. + scale: {'10m', '50m', '110m'}, optional + The scale (resolution). Applicable if 'plot_map' is 'cartopy'. + The available options are '10m', '50m', and '110m'. Default ``'50m'``. + subplot: tuple of int (nrows, ncols, index) or SubplotSpec_ instance, optional + The subplot where to plot the basemap. + By the default, the basemap will replace the current axis. + + Returns + ------- + ax: fig Axes + Cartopy axes. + """ + + if len(kwargs) > 0: + warnings.warn( + "plot_geography: The following keywords are ignored:\n" + + str(kwargs) + + "\nIn version 1.5, passing unsupported arguments will raise an error.", + DeprecationWarning, + ) + + if plot_map is None: + return plt.gca() + + if plot_map not in VALID_BASEMAPS: + raise ValueError( + f"unsupported plot_map method {plot_map}. Supported basemaps: " + f"{VALID_BASEMAPS}" + ) + + if plot_map == "cartopy" and not CARTOPY_IMPORTED: + warnings.warn( + "The cartopy package is required to plot the geographical map but it is " + "not installed. Ignoring the geographic information." + ) + return plt.gca() + + if not PYPROJ_IMPORTED: + warnings.warn( + "the pyproj package is required to plot the geographical map " + "but it is not installed" + ) + return plt.gca() + + crs = utils.proj4_to_cartopy(proj4str) + + ax = plot_map_cartopy( + crs, + extent, + scale, + drawlonlatlines=drawlonlatlines, + drawlonlatlabels=drawlonlatlabels, + lw=lw, + subplot=subplot, + ) + + return ax + + +def plot_map_cartopy( + crs, + extent, + cartopy_scale, + drawlonlatlines=False, + drawlonlatlabels=True, + lw=0.5, + subplot=None, +): + """ + Plot coastlines, countries, rivers and meridians/parallels using cartopy. + + .. _SubplotSpec: https://matplotlib.org/api/_as_gen/matplotlib.gridspec.SubplotSpec.html + + Parameters + ---------- + crs: object + Instance of a crs class defined in cartopy.crs. + It can be created using utils.proj4_to_cartopy. + extent: scalars (left, right, bottom, top) + The coordinates of the bounding box. + drawlonlatlines: bool + Whether to plot longitudes and latitudes. + drawlonlatlabels: bool, optional + If set to True, draw longitude and latitude labels. Valid only if + 'drawlonlatlines' is True. + cartopy_scale: {'10m', '50m', '110m'} + The scale (resolution) of the map. The available options are '10m', + '50m', and '110m'. + lw: float + Line width. + subplot: tuple of int (nrows, ncols, index) or SubplotSpec_ instance, optional + The subplot where to place the basemap. + By the default, the basemap will replace the current axis. + + Returns + ------- + ax: axes + Cartopy axes. Compatible with matplotlib. + """ + if not CARTOPY_IMPORTED: + raise MissingOptionalDependency( + "the cartopy package is required to plot the geographical map" + " but it is not installed" + ) + + if subplot is None: + ax = plt.gca() + else: + if isinstance(subplot, gridspec.SubplotSpec): + ax = plt.subplot(subplot, projection=crs) + else: + ax = plt.subplot(*subplot, projection=crs) + + if not isinstance(ax, GeoAxesSubplot): + ax = plt.subplot(ax.get_subplotspec(), projection=crs) + # cax.clear() + ax.set_axis_off() + + ax.add_feature( + cfeature.NaturalEarthFeature( + "physical", + "ocean", + scale="50m" if cartopy_scale == "10m" else cartopy_scale, + edgecolor="none", + facecolor=np.array([0.59375, 0.71484375, 0.8828125]), + ), + zorder=0, + ) + ax.add_feature( + cfeature.NaturalEarthFeature( + "physical", + "land", + scale=cartopy_scale, + edgecolor="none", + facecolor=np.array([0.9375, 0.9375, 0.859375]), + ), + zorder=0, + ) + ax.add_feature( + cfeature.NaturalEarthFeature( + "physical", + "coastline", + scale=cartopy_scale, + edgecolor="black", + facecolor="none", + linewidth=lw, + ), + zorder=15, + ) + ax.add_feature( + cfeature.NaturalEarthFeature( + "physical", + "lakes", + scale=cartopy_scale, + edgecolor="none", + facecolor=np.array([0.59375, 0.71484375, 0.8828125]), + ), + zorder=0, + ) + ax.add_feature( + cfeature.NaturalEarthFeature( + "physical", + "rivers_lake_centerlines", + scale=cartopy_scale, + edgecolor=np.array([0.59375, 0.71484375, 0.8828125]), + facecolor="none", + ), + zorder=0, + ) + ax.add_feature( + cfeature.NaturalEarthFeature( + "cultural", + "admin_0_boundary_lines_land", + scale=cartopy_scale, + edgecolor="black", + facecolor="none", + linewidth=lw, + ), + zorder=15, + ) + if cartopy_scale in ["10m", "50m"]: + ax.add_feature( + cfeature.NaturalEarthFeature( + "physical", + "reefs", + scale="10m", + edgecolor="black", + facecolor="none", + linewidth=lw, + ), + zorder=15, + ) + ax.add_feature( + cfeature.NaturalEarthFeature( + "physical", + "minor_islands", + scale="10m", + edgecolor="black", + facecolor="none", + linewidth=lw, + ), + zorder=15, + ) + + if drawlonlatlines: + grid_lines = ax.gridlines( + crs=ccrs.PlateCarree(), draw_labels=drawlonlatlabels, dms=True + ) + grid_lines.top_labels = grid_lines.right_labels = False + grid_lines.y_inline = grid_lines.x_inline = False + grid_lines.rotate_labels = False + + ax.set_extent(extent, crs) + + return ax diff --git a/pysteps/visualization/precipfields.py b/pysteps/visualization/precipfields.py index 69bf384ba..759811971 100644 --- a/pysteps/visualization/precipfields.py +++ b/pysteps/visualization/precipfields.py @@ -17,9 +17,9 @@ import matplotlib.pylab as plt import numpy as np -from matplotlib import pyplot, colors +from matplotlib import colors, pyplot -from pysteps.visualization.utils import get_geogrid, get_basemap_axis +from pysteps.visualization.utils import get_basemap_axis, get_geogrid PRECIP_VALID_TYPES = ("intensity", "depth", "prob") PRECIP_VALID_UNITS = ("mm/h", "mm", "dBZ") @@ -139,14 +139,12 @@ def plot_precip_field( if ptype not in PRECIP_VALID_TYPES: raise ValueError( - f"Invalid precipitation type '{ptype}'." - f"Supported: {str(PRECIP_VALID_TYPES)}" + f"Invalid precipitation type '{ptype}'.Supported: {str(PRECIP_VALID_TYPES)}" ) if units not in PRECIP_VALID_UNITS: raise ValueError( - f"Invalid precipitation units '{units}." - f"Supported: {str(PRECIP_VALID_UNITS)}" + f"Invalid precipitation units '{units}.Supported: {str(PRECIP_VALID_UNITS)}" ) if ptype == "prob" and colorbar and probthr is None: diff --git a/pysteps/visualization/spectral.py b/pysteps/visualization/spectral.py index aac16248a..37a9e02c1 100644 --- a/pysteps/visualization/spectral.py +++ b/pysteps/visualization/spectral.py @@ -1,100 +1,100 @@ -# -*- coding: utf-8 -*- -""" -pysteps.visualization.spectral -============================== - -Methods for plotting Fourier spectra. - -.. autosummary:: - :toctree: ../generated/ - - plot_spectrum1d -""" - -import matplotlib.pylab as plt -import numpy as np - - -def plot_spectrum1d( - fft_freq, - fft_power, - x_units=None, - y_units=None, - wavelength_ticks=None, - color="k", - lw=1.0, - label=None, - ax=None, - **kwargs, -): - """ - Function to plot in log-log a radially averaged Fourier spectrum. - - Parameters - ---------- - fft_freq: array-like - 1d array containing the Fourier frequencies computed with the function - :py:func:`pysteps.utils.spectral.rapsd`. - fft_power: array-like - 1d array containing the radially averaged Fourier power spectrum - computed with the function :py:func:`pysteps.utils.spectral.rapsd`. - x_units: str, optional - Units of the X variable (distance, e.g. "km"). - y_units: str, optional - Units of the Y variable (amplitude, e.g. "dBR"). - wavelength_ticks: array-like, optional - List of wavelengths where to show xticklabels. - color: str, optional - Line color. - lw: float, optional - Line width. - label: str, optional - Label (for legend). - ax: Axes, optional - Plot axes. - - Returns - ------- - ax: Axes - Plot axes - """ - # Check input dimensions - n_freq = len(fft_freq) - n_pow = len(fft_power) - if n_freq != n_pow: - raise ValueError( - f"Dimensions of the 1d input arrays must be equal. {n_freq} vs {n_pow}" - ) - - if ax is None: - ax = plt.subplot(111) - - # Plot spectrum in log-log scale - ax.plot( - 10 * np.log10(fft_freq), - 10 * np.log10(fft_power), - color=color, - linewidth=lw, - label=label, - **kwargs, - ) - - # X-axis - if wavelength_ticks is not None: - wavelength_ticks = np.array(wavelength_ticks) - freq_ticks = 1 / wavelength_ticks - ax.set_xticks(10 * np.log10(freq_ticks)) - ax.set_xticklabels(wavelength_ticks) - if x_units is not None: - ax.set_xlabel(f"Wavelength [{x_units}]") - else: - if x_units is not None: - ax.set_xlabel(f"Frequency [1/{x_units}]") - - # Y-axis - if y_units is not None: - # { -> {{ with f-strings - power_units = rf"$10log_{{ 10 }}(\frac{{ {y_units}^2 }}{{ {x_units} }})$" - ax.set_ylabel(f"Power {power_units}") - - return ax +# -*- coding: utf-8 -*- +""" +pysteps.visualization.spectral +============================== + +Methods for plotting Fourier spectra. + +.. autosummary:: + :toctree: ../generated/ + + plot_spectrum1d +""" + +import matplotlib.pylab as plt +import numpy as np + + +def plot_spectrum1d( + fft_freq, + fft_power, + x_units=None, + y_units=None, + wavelength_ticks=None, + color="k", + lw=1.0, + label=None, + ax=None, + **kwargs, +): + """ + Function to plot in log-log a radially averaged Fourier spectrum. + + Parameters + ---------- + fft_freq: array-like + 1d array containing the Fourier frequencies computed with the function + :py:func:`pysteps.utils.spectral.rapsd`. + fft_power: array-like + 1d array containing the radially averaged Fourier power spectrum + computed with the function :py:func:`pysteps.utils.spectral.rapsd`. + x_units: str, optional + Units of the X variable (distance, e.g. "km"). + y_units: str, optional + Units of the Y variable (amplitude, e.g. "dBR"). + wavelength_ticks: array-like, optional + List of wavelengths where to show xticklabels. + color: str, optional + Line color. + lw: float, optional + Line width. + label: str, optional + Label (for legend). + ax: Axes, optional + Plot axes. + + Returns + ------- + ax: Axes + Plot axes + """ + # Check input dimensions + n_freq = len(fft_freq) + n_pow = len(fft_power) + if n_freq != n_pow: + raise ValueError( + f"Dimensions of the 1d input arrays must be equal. {n_freq} vs {n_pow}" + ) + + if ax is None: + ax = plt.subplot(111) + + # Plot spectrum in log-log scale + ax.plot( + 10 * np.log10(fft_freq), + 10 * np.log10(fft_power), + color=color, + linewidth=lw, + label=label, + **kwargs, + ) + + # X-axis + if wavelength_ticks is not None: + wavelength_ticks = np.array(wavelength_ticks) + freq_ticks = 1 / wavelength_ticks + ax.set_xticks(10 * np.log10(freq_ticks)) + ax.set_xticklabels(wavelength_ticks) + if x_units is not None: + ax.set_xlabel(f"Wavelength [{x_units}]") + else: + if x_units is not None: + ax.set_xlabel(f"Frequency [1/{x_units}]") + + # Y-axis + if y_units is not None: + # { -> {{ with f-strings + power_units = rf"$10log_{{ 10 }}(\frac{{ {y_units}^2 }}{{ {x_units} }})$" + ax.set_ylabel(f"Power {power_units}") + + return ax diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index b5075ad35..000000000 --- a/requirements.txt +++ /dev/null @@ -1,10 +0,0 @@ -numpy -opencv-python -pillow -pyproj -scipy -matplotlib -jsmin -jsonschema -netCDF4 -xarray diff --git a/requirements_dev.txt b/requirements_dev.txt deleted file mode 100644 index 25e043b3f..000000000 --- a/requirements_dev.txt +++ /dev/null @@ -1,31 +0,0 @@ -# Base dependencies -python>=3.10 -numpy -opencv-python -pillow -pyproj -scipy -matplotlib -jsmin -jsonschema -netCDF4 -xarray - -# Optional dependencies -dask -pyfftw -cartopy>=0.18 -h5py -scikit-image -scikit-learn -pandas -rasterio -pygrib -pywavelets -geotiff -gdal==3.4.1; "22.04" in platform_version and "Ubuntu" in platform_version -cookiecutter - -# Testing -pytest - diff --git a/setup.py b/setup.py index 666ca2536..3e9d3e80b 100644 --- a/setup.py +++ b/setup.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# Some things are not yet in pyproject.toml. They're here. import sys @@ -60,46 +60,6 @@ external_modules = cythonize(extensions, force=True, language_level=3) -requirements = [ - "numpy", - "jsmin", - "scipy", - "matplotlib", - "jsonschema", -] - setup( - name="pysteps", - version="1.21.5", - author="PySteps developers", - packages=find_packages(), - license="LICENSE", - include_package_data=True, - description="Python framework for short-term ensemble prediction systems", - long_description=open("README.rst").read(), - long_description_content_type="text/x-rst", - url="https://pysteps.github.io/", - project_urls={ - "Source": "https://github.com/pySTEPS/pysteps", - "Issues": "https://github.com/pySTEPS/pysteps/issues", - "CI": "https://github.com/pySTEPS/pysteps/actions", - "Changelog": "https://github.com/pySTEPS/pysteps/releases", - "Documentation": "https://pysteps.readthedocs.io", - }, - classifiers=[ - "Development Status :: 5 - Production/Stable", - "Intended Audience :: Science/Research", - "Topic :: Scientific/Engineering", - "Topic :: Scientific/Engineering :: Atmospheric Science", - "Topic :: Scientific/Engineering :: Hydrology", - "License :: OSI Approved :: BSD License", - "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", - "Programming Language :: Python :: 3.13", - "Operating System :: OS Independent", - ], ext_modules=external_modules, - setup_requires=requirements, - install_requires=requirements, ) diff --git a/tox.ini b/tox.ini deleted file mode 100644 index 86a2e4b1d..000000000 --- a/tox.ini +++ /dev/null @@ -1,131 +0,0 @@ -# Tox configuration file for pysteps projects -# Need conda, tox and tox-conda installed to run -# -# In conda run: -# > conda install -c conda-forge tox tox-conda -# -# Alternatively, you can install them using pip: -# > pip install tox tox-conda -# -# Then, to run the tests, from the repo’s root run: -# -# > tox # Run pytests -# > tox -e install # Test package installation -# > tox -e black # Test for black formatting warnings - -[tox] -envlist = py37, py38, py39 - -[testenv] -description = Run the pysteps's test suite -deps = - -r{toxinidir}/requirements.txt - cython - dask - toolz - pillow - pyfftw - h5py - PyWavelets - scikit-learn - gitpython - pytest - pytest-cov - codecov -conda_deps = - netCDF4 - pyproj - cartopy - pygrib - rasterio -conda_channels = conda-forge - -setenv = - PYSTEPS_DATA_PATH = {toxworkdir}/pysteps-data - PYSTEPSRC = {toxworkdir}/pysteps-data/pystepsrc - PACKAGE_ROOT = {toxinidir} - PROJ_LIB={envdir}/share/proj -commands = - python {toxinidir}/ci/fetch_pysteps_data.py - pytest --pyargs pysteps --cov=pysteps -ra --disable-warnings - -[test_no_cov] -commands = - python {toxinidir}/ci/fetch_pysteps_data.py - pytest --pyargs pysteps --disable-warnings - -[testenv:install] -description = Test the installation of the package in a clean environment and run minimal tests -deps = pytest -conda_deps = -changedir = {homedir} -commands = - pip install -U {toxinidir}/ - python -c "import pysteps" - - # Test the pysteps plugin support - pip install cookiecutter - cookiecutter -f --no-input https://github.com/pySTEPS/cookiecutter-pysteps-plugin -o {temp_dir}/ - # NB: this should match the default name for a cookiecutter-generated plugin! - pip install {temp_dir}/pysteps-importer-institution-name - python {toxinidir}/ci/test_plugin_support.py - # Check the compiled modules - python -c "from pysteps import motion" - python -c "from pysteps.motion import vet" - python -c "from pysteps.motion import proesmans" - - -[testenv:install_full] -description = Test the installation of the package in an environment with all the dependencies -changedir = {homedir} -commands = - {[testenv:install]commands} - {[test_no_cov]commands} - -[testenv:pypi] -description = Test the installation of the package from the PyPI in a clean environment -deps = pytest -conda_deps = -changedir = {homedir} -commands = - pip install --no-cache-dir pysteps - python -c "import pysteps" - {[test_no_cov]commands} - -[testenv:pypi_test] -description = Test the installation of the package from the test-PyPI in a clean environment -deps = pytest -conda_deps = -changedir = {homedir} -commands = - pip install --no-cache-dir --index-url https://test.pypi.org/simple/ --extra-index-url=https://pypi.org/simple/ pysteps - python -c "import pysteps" - {[test_no_cov]commands} - -[testenv:pypi_test_full] -description = Test the installation of the package from the test-PyPI in an environment with all the dependencies -changedir = {homedir} -commands = {[testenv:pypi_test]commands} - - -[testenv:docs] -description = Build the html documentation using sphinx -usedevelop = True -deps = - -r{toxinidir}/requirements.txt - -r{toxinidir}/doc/requirements.txt - cython -conda_channels = - conda-forge - default -changedir = doc -setenv = - PYSTEPS_DATA_PATH = {toxworkdir}/pysteps-data - PYSTEPSRC = {toxworkdir}/pysteps-data/pystepsrc -commands = - python {toxinidir}/ci/fetch_pysteps_data.py - sphinx-build -b html source _build - -[testenv:black] -deps = black -commands = black --check pysteps diff --git a/uv.lock b/uv.lock new file mode 100644 index 000000000..f0d16ac40 --- /dev/null +++ b/uv.lock @@ -0,0 +1,2411 @@ +version = 1 +revision = 3 +requires-python = ">=3.11.0, <3.14.0" +resolution-markers = [ + "python_full_version >= '3.12' and sys_platform == 'win32'", + "python_full_version >= '3.12' and sys_platform == 'emscripten'", + "python_full_version >= '3.12' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version < '3.12' and sys_platform == 'win32'", + "python_full_version < '3.12' and sys_platform == 'emscripten'", + "python_full_version < '3.12' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] + +[[package]] +name = "affine" +version = "3.0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/63/e9/4a4480601992a529c5d0f406605f70ca59aeaef4a6f5ba8905cfde217d0b/affine-3.0.1.tar.gz", hash = "sha256:e1b3c38c5d4d3ef5024a182a6d1bf1e0c51ab221825781c741aeb4d0c079a7e2", size = 20981, upload-time = "2026-08-28T18:38:14.452Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3d/87/e62f55c956b583380e7d2a71705dfd431ee32dd1689d50491ba0c610fc11/affine-3.0.1-py3-none-any.whl", hash = "sha256:cda3b303325e7bf2bf34817e68753a0d1c4cacbdd451fe67c4878dc2ecbaa540", size = 10887, upload-time = "2026-08-28T18:38:12.837Z" }, +] + +[[package]] +name = "arrow" +version = "1.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "python-dateutil" }, + { name = "tzdata" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b9/33/032cdc44182491aa708d06a68b62434140d8c50820a087fac7af37703357/arrow-1.4.0.tar.gz", hash = "sha256:ed0cc050e98001b8779e84d461b0098c4ac597e88704a655582b21d116e526d7", size = 152931, upload-time = "2025-10-18T17:46:46.761Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ed/c9/d7977eaacb9df673210491da99e6a247e93df98c715fc43fd136ce1d3d33/arrow-1.4.0-py3-none-any.whl", hash = "sha256:749f0769958ebdc79c173ff0b0670d59051a535fa26e8eba02953dc19eb43205", size = 68797, upload-time = "2025-10-18T17:46:45.663Z" }, +] + +[[package]] +name = "asciitree" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f0/0f/c9686d87934dbbd59d9dee7fbcd98d0e9c8f44e0c5d4965a5bd2f4385865/asciitree-1.0.0.tar.gz", hash = "sha256:0c396808c13cab27b60cbe402b99ef37fa681c6b59d337a008719efa201cdd07", size = 4940, upload-time = "2026-10-25T17:50:11.174Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3b/6a/6683d280e053ab42276234ba73e648ce0db8dce40adc5d409e7e27aaa037/asciitree-1.0.0-py3-none-any.whl", hash = "sha256:4ec3e9dbf093e4b52577325071c11c669ad6fb180a78f168962d35b535d3f430", size = 5255, upload-time = "2026-10-25T17:50:10.284Z" }, +] + +[[package]] +name = "attrs" +version = "26.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9a/8e/82a0fe20a541c03148528be8cac2408564a6c9a0cc7e9171802bc1d26985/attrs-26.1.0.tar.gz", hash = "sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32", size = 952055, upload-time = "2026-03-19T14:22:25.026Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548, upload-time = "2026-03-19T14:22:23.645Z" }, +] + +[[package]] +name = "bandit" +version = "1.9.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "pyyaml" }, + { name = "rich" }, + { name = "stevedore" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/aa/c3/0cb80dfe0f3076e5da7e4c5ad8e57bac6ac357ff4a6406205501cade4965/bandit-1.9.4.tar.gz", hash = "sha256:b589e5de2afe70bd4d53fa0c1da6199f4085af666fde00e8a034f152a52cd628", size = 4242677, upload-time = "2026-02-25T06:44:15.503Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/05/a4/a26d5b25671d27e03afb5401a0be5899d94ff8fab6a698b1ac5be3ec29ef/bandit-1.9.4-py3-none-any.whl", hash = "sha256:f89ffa663767f5a0585ea075f01020207e966a9c0f2b9ef56a57c7963a3f6f8e", size = 134741, upload-time = "2026-02-25T06:44:13.694Z" }, +] + +[[package]] +name = "binaryornot" +version = "0.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/86/72/4755b85101f37707c71526a301c1203e413c715a0016ecb592de3d2dcfff/binaryornot-0.6.0.tar.gz", hash = "sha256:cc8d57cfa71d74ff8c28a7726734d53a851d02fad9e3a5581fb807f989f702f0", size = 478718, upload-time = "2026-03-08T16:26:28.804Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cd/0c/31cfaa6b56fe23488ecb993bc9fc526c0d84d89607decdf2a10776426c2e/binaryornot-0.6.0-py3-none-any.whl", hash = "sha256:900adfd5e1b821255ba7e63139b0396b14c88b9286e74e03b6f51e0200331337", size = 14185, upload-time = "2026-03-08T16:26:27.466Z" }, +] + +[[package]] +name = "cartopy" +version = "0.26.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "matplotlib" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "numpy", version = "2.5.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "packaging" }, + { name = "pyproj", version = "3.7.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "pyproj", version = "3.8.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "pyshp" }, + { name = "shapely" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a3/a2/ae348fc400e73fd5b589919ab5f13aece4feb31cd5cc78ab54773018563b/cartopy-0.26.0.tar.gz", hash = "sha256:b87a35835f30333760cc46194bd90b6a4958d12f488f50131d4c4c0962ad9c3c", size = 8579492, upload-time = "2026-09-17T02:57:14.863Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/89/6b/7a1a47a2310495517d155bab1c98e640e73b156a81901d7df37d24d1187b/cartopy-0.26.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9297180160470eed63829a94367fe286a83d5ca3ae7c6111a918a9d80c83ed8e", size = 8788673, upload-time = "2026-09-17T02:54:36.764Z" }, + { url = "https://files.pythonhosted.org/packages/97/50/368864c14a9e8cc2b51159158ae44f2a74866053be39060c3b1e8b5f6ed6/cartopy-0.26.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:32fbcf0bb3160a50ccfd94af9c13cdf11ffef84f4b0c711951238e45915ff585", size = 8787362, upload-time = "2026-09-17T02:54:39.41Z" }, + { url = "https://files.pythonhosted.org/packages/1a/1f/cf48b6a0e662b444f87fc57e4ae1a3b9c805b07d30df4fbfa2cd9ea39056/cartopy-0.26.0-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cf5ee30387b469d75821c80191f437e46fcafdc969957a9c716aa798f358b811", size = 9511447, upload-time = "2026-09-17T02:54:41.991Z" }, + { url = "https://files.pythonhosted.org/packages/6f/8c/cb0296135606d6e4d9c8b03b881936d0ded26ccdcce8a8140b26fc5645fb/cartopy-0.26.0-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1dc7a6bb2742e69b971951bc80f7da9ec4120bd174f8cc336b9d836502118d17", size = 9518919, upload-time = "2026-09-17T02:54:44.452Z" }, + { url = "https://files.pythonhosted.org/packages/54/95/255d6c300b15f63965405860497554f344e30803f384fd818533f853d608/cartopy-0.26.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b455122169a8c5ad7ce1625d3af7fd3c34886aded57951d746ae58f06504050e", size = 10482638, upload-time = "2026-09-17T02:54:46.959Z" }, + { url = "https://files.pythonhosted.org/packages/66/57/521b60702a884bb74a0064c40e04985468eddb4c961f5ebe3ff5d81214fc/cartopy-0.26.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3f9b613fecfe386a4c4edfcde2d7ee43ffc8f2bee89fcfaae83d7b6adf873205", size = 10558119, upload-time = "2026-09-17T02:54:49.439Z" }, + { url = "https://files.pythonhosted.org/packages/40/5a/6baac596aa92a8e555b1203efc878c13a0f7f53f8440452cbd101a1038d8/cartopy-0.26.0-cp311-cp311-win32.whl", hash = "sha256:706f3540576f820415d2cff18dee7bf7c7391bda8ffbd471e32e97a823efc1de", size = 8944394, upload-time = "2026-09-17T02:54:52.238Z" }, + { url = "https://files.pythonhosted.org/packages/58/ea/13cad9d491b107d796c4bffc98bd3b3c931d927118794dc9584d69107abe/cartopy-0.26.0-cp311-cp311-win_amd64.whl", hash = "sha256:538c3eac09f9750caf6a9c934afa9d7cfa2f317e1a9bd1b580df249175911a9e", size = 8962155, upload-time = "2026-09-17T02:54:54.58Z" }, + { url = "https://files.pythonhosted.org/packages/4a/9b/3a32fa86d694188801ed2635672742a74b73538ee807edf9bb05d8ec471f/cartopy-0.26.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:663f00c5350c85d547ef219fdd8672c15e36622132a9745915118bf71ccd30d4", size = 8791036, upload-time = "2026-09-17T02:54:57.097Z" }, + { url = "https://files.pythonhosted.org/packages/b8/85/3d3145e4858f1f980be896ef8d0e19b00302895499e46ce9c9821b9c728d/cartopy-0.26.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6331dc2efd85c76ad55c3cb458940038f7a4224d46fc339ee5dc81b251908755", size = 8787966, upload-time = "2026-09-17T02:54:59.819Z" }, + { url = "https://files.pythonhosted.org/packages/e4/11/eddff579989027c23dbfaacb00b79159266f63e0c42ce4982c5a773f2f56/cartopy-0.26.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0e4364f5c17f2be28bce4714a60a75603b10d67fe7890d479ef3aaf1e69118a5", size = 9492979, upload-time = "2026-09-17T02:55:02.599Z" }, + { url = "https://files.pythonhosted.org/packages/a0/19/b4dfb2690b55262e6ec3e578731ee4b7df1e668a41a8879b4f7d71d9a0df/cartopy-0.26.0-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c176bc6a9b9e96cdafed63daea2a3d6368f9887e7acf7b3bcc9d5e0bc0914b9", size = 9510776, upload-time = "2026-09-17T02:55:05.346Z" }, + { url = "https://files.pythonhosted.org/packages/52/84/d40cf7c53da9db42e217de3dd0b1a6795267f14cc12de1dd277441f50b24/cartopy-0.26.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:24686f0b5ebff20e0691933f2a9c0155efd24192324966a7a81d6c1903015711", size = 10459518, upload-time = "2026-09-17T02:55:08.122Z" }, + { url = "https://files.pythonhosted.org/packages/a3/dc/bd53e2dce590efd5f47e285ab2b1303256e6f60780e43e4f7bd3e1b5fec5/cartopy-0.26.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c6543662275a760acd7b8bfcb7b6d531229dffee6fd4b891d2bb11c8f53ff8ea", size = 10537231, upload-time = "2026-09-17T02:55:11.245Z" }, + { url = "https://files.pythonhosted.org/packages/85/c7/5adfeb3dcdf59e22200a97a852e03fbb4c63a7a1bce6eded66947a086e6c/cartopy-0.26.0-cp312-cp312-win32.whl", hash = "sha256:cf0925eeb9fcaea3db97cb2b62e07df84ddc4f66e4fd9c38da470c279e502f0d", size = 8945441, upload-time = "2026-09-17T02:55:14.695Z" }, + { url = "https://files.pythonhosted.org/packages/2e/06/e92639aed965e7bec43518208e3caccd65d3e3f9f7ae36abd18db15b5fc8/cartopy-0.26.0-cp312-cp312-win_amd64.whl", hash = "sha256:0ffd0c16788fd79fb6ddffe77cff01e6a00632962cb2f13b3750d72384271280", size = 8961674, upload-time = "2026-09-17T02:55:17.96Z" }, + { url = "https://files.pythonhosted.org/packages/0d/1d/b3b2125e20cb37f0bdfd501bc05c93c4c43fc915883979fa3fd94c7b3591/cartopy-0.26.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9cbcc7b585cd715befb143ba19213fe7770c302fc50097edcb6025ad2b129beb", size = 8790561, upload-time = "2026-09-17T02:55:20.926Z" }, + { url = "https://files.pythonhosted.org/packages/7b/33/7f2f6e1ae1db2c4d688f9d1bd54778e1677e2f46a341286b74287b331895/cartopy-0.26.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9841e51d4c1a3258365ad3ae0549d06451ee7bd48dd19657d8daa172d581a897", size = 8787570, upload-time = "2026-09-17T02:55:23.671Z" }, + { url = "https://files.pythonhosted.org/packages/ac/60/343f57b4d0c422a3c87308de766716f0c190b2dc35d5fef262331711bbb2/cartopy-0.26.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e81ede9ba9f8dcd7ef7372ff89811c7652967ae14fdd4ced15bd5d765db198d8", size = 9486503, upload-time = "2026-09-17T02:55:26.489Z" }, + { url = "https://files.pythonhosted.org/packages/a2/41/47cc1137a8d02802d131ad0e16f3e52cd4518dab7cae5194af224fe9b9d2/cartopy-0.26.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0326ef7f3833690998f1256ea181a60e2bac6dcc9896c5233c17204521db90fb", size = 9505266, upload-time = "2026-09-17T02:55:29.174Z" }, + { url = "https://files.pythonhosted.org/packages/2b/ae/1cd78770eda2fb4448acbe1b95e1b629dc1bb3fae6d7fabbef8fb5eebea2/cartopy-0.26.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3ff2753716d9f2ff8e4c5c3021663628f05de56937a359ba9c24369a8893644c", size = 10455294, upload-time = "2026-09-17T02:55:32.138Z" }, + { url = "https://files.pythonhosted.org/packages/7f/a5/4d7c666dcc08e24cf647a88b7dc8fa7346b7bdc9454ec50a903954a149ef/cartopy-0.26.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:30691afee99e1ef457f6fe2b0c134e7e96b2d605e57f48c49b265d0aea2fd449", size = 10529735, upload-time = "2026-09-17T02:55:35.153Z" }, + { url = "https://files.pythonhosted.org/packages/26/11/4d45793c2e767d7be45cbb513f72b451f19e40c6263da652bc5ee02e05cd/cartopy-0.26.0-cp313-cp313-win32.whl", hash = "sha256:0677c3f6437716730dee29b6ac5efdde4a0d24ee1624ad349783d1c5e31b55f1", size = 8945259, upload-time = "2026-09-17T02:55:38.203Z" }, + { url = "https://files.pythonhosted.org/packages/53/0a/825999b94a62670a7b2eca37fcaa7781bf3eadcbb60cef657071facfc25e/cartopy-0.26.0-cp313-cp313-win_amd64.whl", hash = "sha256:0f8f81b45844272dd289f79de707d6deb1975e5e0b27a64dff955519e73dc1d5", size = 8961735, upload-time = "2026-09-17T02:55:41.107Z" }, +] + +[[package]] +name = "certifi" +version = "2026.10.23" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fd/11/b524cbd9bbdaca58608e4cf4053fd463417549e1959fa8d3aa18319f9555/certifi-2026.10.23.tar.gz", hash = "sha256:a9895e6e2758b371e0545e321a3a8fd1fcfd9829ada8824a936dfb16aaaf36cd", size = 136225, upload-time = "2026-10-23T11:36:16.478Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/32/d1/a3652fe2c19134d0d15922177821d863cbd7e842d53a21bbe7e8e3a1b5ac/certifi-2026.10.23-py3-none-any.whl", hash = "sha256:e9b54de76e530bb6cf0ccd036787103acb58fd42ba930ea8bf948ddbaa2f61d1", size = 135082, upload-time = "2026-10-23T11:36:14.763Z" }, +] + +[[package]] +name = "cfgv" +version = "3.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4e/b5/721b8799b04bf9afe054a3899c6cf4e880fcf8563cc71c15610242490a0c/cfgv-3.5.0.tar.gz", hash = "sha256:d5b1034354820651caa73ede66a6294d6e95c1b00acc5e9b098e917404669132", size = 7334, upload-time = "2025-11-19T20:55:51.612Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl", hash = "sha256:a8dc6b26ad22ff227d2634a65cb388215ce6cc96bbcc5cfde7641ae87e8dacc0", size = 7445, upload-time = "2025-11-19T20:55:50.744Z" }, +] + +[[package]] +name = "cftime" +version = "1.6.6.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "numpy", version = "2.5.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4f/bb/64c8afb966ca9482ef7961b606bfe8615f7948e7ea70e737159f9a940064/cftime-1.6.6.1.tar.gz", hash = "sha256:3eff428a229169c2632c093b554e36dd5277dacc0f7aae8ad73ce6a93304d58d", size = 381552, upload-time = "2026-10-14T18:50:47.589Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dd/28/0239c934ddcf555ba718c1b971aa8e241a241f80c92bead219eefd76b82c/cftime-1.6.6.1-cp311-abi3-macosx_10_9_x86_64.whl", hash = "sha256:dd42f26a5ec493ac6ffe83eabc173625d2954cc6993de150ef60bab7599dc10e", size = 549727, upload-time = "2026-10-14T18:50:18.032Z" }, + { url = "https://files.pythonhosted.org/packages/e3/98/2906f59bb8fb11dd47133fcf40d1d01c2e7a00f5f00701a80cfb6cc1f498/cftime-1.6.6.1-cp311-abi3-macosx_11_0_arm64.whl", hash = "sha256:6afab9967fe9635eb16569cd670619a7beed09bbea7982f9c6f61001d06c62a1", size = 543813, upload-time = "2026-10-14T18:50:19.916Z" }, + { url = "https://files.pythonhosted.org/packages/a1/f5/84eb544579f55452df65a47bcc86147deaddf0e568725987bc69a1bc4cf6/cftime-1.6.6.1-cp311-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:92566dd8c3213b824f8b2e86904efa7f0d8db6a51017cc5ad365b1d20b579617", size = 1673835, upload-time = "2026-10-14T18:50:21.438Z" }, + { url = "https://files.pythonhosted.org/packages/dd/29/563ff54069998e89cd74e410e862017131690e4f595a06820172335a9c1b/cftime-1.6.6.1-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2d1e68e14537e16db8d3a48c95aa617556247d3f8e6e3a864eca302427dc1e4c", size = 1642106, upload-time = "2026-10-14T18:50:23.441Z" }, + { url = "https://files.pythonhosted.org/packages/a8/3a/ad6e96204d85ea6abf3721393ec2e830e025854b51402cfc081a670b61f5/cftime-1.6.6.1-cp311-abi3-win_amd64.whl", hash = "sha256:e4ed505118cbffec8ca6b59636f283d55df1675a3f092bc2d276f917a04af68a", size = 512994, upload-time = "2026-10-14T18:50:25.098Z" }, + { url = "https://files.pythonhosted.org/packages/c4/c5/a5eb6e8b11d8b603af51827c9afb42598d0eda1a2cbd5c0190e31f701348/cftime-1.6.6.1-cp311-abi3-win_arm64.whl", hash = "sha256:f0cf93b58005e8dd012d2c7c10428d405b1afb19384d12de66782fad46df2b39", size = 499638, upload-time = "2026-10-14T18:50:26.658Z" }, +] + +[[package]] +name = "charset-normalizer" +version = "3.5.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/33/1c/f41d4e74c28ab327ff3acd36053f7ea506c55872d7a90b0fa71aa3ab0c89/charset_normalizer-3.5.2.tar.gz", hash = "sha256:39de2a259fc954455c57274dc94c79d5842774e1247a016aff30bc0efed0f4ef", size = 172659, upload-time = "2026-09-30T04:39:23.398Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/22/67/6a0b94a7960d5e1b5eacd2fb529f3fccc47db4644f7f0a7cfdcfc3be578a/charset_normalizer-3.5.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3d21b8b13c7592db2ac5e544a6d83187b995257472b0c9e8351b6d507ae37ed6", size = 370642, upload-time = "2026-09-30T04:35:06.91Z" }, + { url = "https://files.pythonhosted.org/packages/fb/94/01009e13b94041599004edf32e56e382c24e570f60f79bab8efe45cfe1eb/charset_normalizer-3.5.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d760fe2a4d7c3b226cb9026d6a842868d52a7901bd98420e1baf14e80da85cf5", size = 259280, upload-time = "2026-09-30T04:35:08.448Z" }, + { url = "https://files.pythonhosted.org/packages/66/85/3b5358f60a13210f0b67d3755c168ef758701b021e655d88d4da28554467/charset_normalizer-3.5.2-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c9790464842f85f437dbbb54417eda1e0e6bfc52dd8d22d6fd1c994b73b2dc74", size = 245335, upload-time = "2026-09-30T04:35:10.104Z" }, + { url = "https://files.pythonhosted.org/packages/74/75/77c1c479b09ecd751d1e767b251ea5c14d4d50ff757bf404afab2692f600/charset_normalizer-3.5.2-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4685902cf26edf013ed7a3da0f426ebba7a00ebb9541386d835afbf002c11cab", size = 291707, upload-time = "2026-09-30T04:35:11.575Z" }, + { url = "https://files.pythonhosted.org/packages/0b/0d/363f78cacb70f58f15f4b083961bbd9d292f335d3f5c66fc4f1cfe69cb90/charset_normalizer-3.5.2-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4495c5002a7b28557e7e222e77e0b661183e432b7d6d2e788101e3f240e05b8c", size = 286747, upload-time = "2026-09-30T04:35:13.022Z" }, + { url = "https://files.pythonhosted.org/packages/e4/ed/cf505d3011ffceb12c2067a7a5d3cfe92b875d4d44bb0ff0d69375e2c184/charset_normalizer-3.5.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:211d5a3eb6af8f513b8d4ca19a8c1b7accab1b5f0d3175f9826b03c1a920dc1f", size = 269972, upload-time = "2026-09-30T04:35:14.606Z" }, + { url = "https://files.pythonhosted.org/packages/15/d8/f0a93a431d170e7ca681d4f6650fee3de934d18560e474e7267eb4b0f987/charset_normalizer-3.5.2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ef4fcbf3327382cd4c9f540babd61248208af7b93eec4de397b4d5f58a09e288", size = 269339, upload-time = "2026-09-30T04:35:16.087Z" }, + { url = "https://files.pythonhosted.org/packages/86/bd/9b2bd1c5b7af02462c9752d33994834ff972a96b4c483eefde9e594488e2/charset_normalizer-3.5.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bd16aabe4a02a297c23417aa17ac6299dbd8c49f673bcd645b4929b11f5a4400", size = 261683, upload-time = "2026-09-30T04:35:17.488Z" }, + { url = "https://files.pythonhosted.org/packages/76/a5/cac540ab0fd61f3fec88ad3dbb64509e71424593d73cfdfff5ab3e4db279/charset_normalizer-3.5.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:fb9e68df06293761f9fe66ade60a9bc6d0f5e42b8acf2939a9158af86ab0e5bd", size = 248143, upload-time = "2026-09-30T04:35:18.849Z" }, + { url = "https://files.pythonhosted.org/packages/71/7a/ff467301deef2089fad87f72df9e000a26a78fec7acbb18e1999371b8369/charset_normalizer-3.5.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:59f63901b0031c3136cf64704dcb21de0bbae62ce2c9529bc39d27665463de37", size = 292358, upload-time = "2026-09-30T04:35:20.326Z" }, + { url = "https://files.pythonhosted.org/packages/ad/77/22d7e785d1e210afc2e2f58600dd1799d17a35665faf84383f002826c5f8/charset_normalizer-3.5.2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:304d5463e65a35d7bb0850550e0780395395f6fcf452f04db7d5ca7cecc425ac", size = 268766, upload-time = "2026-09-30T04:35:21.72Z" }, + { url = "https://files.pythonhosted.org/packages/ae/91/e8e946267f1c2d9e2bd651726e2fbd2addf02c4d36cea5069e32ca9d7bb5/charset_normalizer-3.5.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:9cf9b1a857e25c4baceeb3624e92a56df3668f398c4acba74e174d81fb4d1d3a", size = 287432, upload-time = "2026-09-30T04:35:23.273Z" }, + { url = "https://files.pythonhosted.org/packages/4e/88/7561d8a88d555e7df6623abe7c0070b4baf47549b9408783a2ae0a1a6cf7/charset_normalizer-3.5.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:114e4d0c92d618409ed82a99e22b5c5e768fe995f2973f78265f4524f49d4640", size = 272470, upload-time = "2026-09-30T04:35:24.655Z" }, + { url = "https://files.pythonhosted.org/packages/35/7e/578c702301ec036f01455f30744a08d2b42f6ab35b9b2d4bf8cae0ef2a80/charset_normalizer-3.5.2-cp311-cp311-win32.whl", hash = "sha256:2625388c6c754520c37abaf3b41eb34d1cc4a373f457898f08606c8e362b891d", size = 186688, upload-time = "2026-09-30T04:35:26.225Z" }, + { url = "https://files.pythonhosted.org/packages/e8/fc/fdf8cf52ff21cd5bf158f20978991cf985325842f74283eb6df26c8a39d8/charset_normalizer-3.5.2-cp311-cp311-win_amd64.whl", hash = "sha256:87e50a3e7cb90af586b6c5faf23e302a970415ac73bd7bd90a515a04b427ef96", size = 214932, upload-time = "2026-09-30T04:35:27.796Z" }, + { url = "https://files.pythonhosted.org/packages/97/66/3e45a506d8110b632541faf9a9470185aa9878f1ed44020f31346c1c5e5b/charset_normalizer-3.5.2-cp311-cp311-win_arm64.whl", hash = "sha256:254eb48b9fa5ee9898a3c445825a1f340fe53712a098904b39b0bddba8ea3cb1", size = 202828, upload-time = "2026-09-30T04:35:29.259Z" }, + { url = "https://files.pythonhosted.org/packages/e7/c8/693809898870237d82785a03f3b2b58fe4c9f14669f84a7d4e623c92a59e/charset_normalizer-3.5.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ed2a239c0ea213acc1908150a3037257083c7c083128f1a4cec2ec4b97dca491", size = 367780, upload-time = "2026-09-30T04:35:30.888Z" }, + { url = "https://files.pythonhosted.org/packages/c9/87/2fea8c13dc24b3ca9c6f803a5b2dfdeae73eb4f9e12c7885ed908ff0433c/charset_normalizer-3.5.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b91363207bd9dc966a691e959bb47f64b30f7ac4b072be9968b366982f7db77c", size = 246730, upload-time = "2026-09-30T04:35:32.286Z" }, + { url = "https://files.pythonhosted.org/packages/a8/9e/09efac30b937722f46d3110ba30b875b24b2e3a266ed746cc4e376a94d80/charset_normalizer-3.5.2-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:38a873987f3be698494da8b2e3085e29da02da7b633dce73e79c699a113d7bf0", size = 237707, upload-time = "2026-09-30T04:35:33.709Z" }, + { url = "https://files.pythonhosted.org/packages/9e/18/70d76670b13686237863a379928d60bd10e021f17d243ab3d7014c4a5f4e/charset_normalizer-3.5.2-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:355ad8011081dec5412240c087a9a0c9d4d5039f3ed11a3f13e18c2b29b56c51", size = 273050, upload-time = "2026-09-30T04:35:35.138Z" }, + { url = "https://files.pythonhosted.org/packages/54/e2/77a8b09d5adc013ed07b95b01b8b8fa5441c4e810e83ee7e4aae2fa4d91a/charset_normalizer-3.5.2-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ee21e28f0430bd6dc9086c6e525d5e818a44a5ad19720c8a0ef766792f3eb5e5", size = 270345, upload-time = "2026-09-30T04:35:36.502Z" }, + { url = "https://files.pythonhosted.org/packages/7f/c5/38806a25ab5e65fc178f39affeda20858efafede2fce1ffc2556cfc9fe73/charset_normalizer-3.5.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3d31298449090ab8d47b7b1b2a555ff73cac7ed438a08b7ac160980c7ebed649", size = 257601, upload-time = "2026-09-30T04:35:37.919Z" }, + { url = "https://files.pythonhosted.org/packages/ae/8d/213565184708fdb263ae55e2c04ee1ff748129dd65d48ed0e3502da9c85a/charset_normalizer-3.5.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5cde776b7cc66e4f6c99612cea4aa7269aa65863f7a15841b2c264f103822f4e", size = 252222, upload-time = "2026-09-30T04:35:39.544Z" }, + { url = "https://files.pythonhosted.org/packages/7e/24/76d2cefc25472531e4c5c7dfff68865eb1c39b78482f0fdc15b46f047830/charset_normalizer-3.5.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ae4f5fea5b8b8ccff88238cc8569303e5ee95efae67fa62922a311397a71f346", size = 248482, upload-time = "2026-09-30T04:35:41.088Z" }, + { url = "https://files.pythonhosted.org/packages/7d/dc/65a801b66ab4c197e22c433ab25e7ac24324ac6f45a2269aca42cce309bf/charset_normalizer-3.5.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:f7d486c83842422badd511868fd8a9a20e9407ace71564b6af47ce7e60a336c1", size = 241206, upload-time = "2026-09-30T04:35:42.59Z" }, + { url = "https://files.pythonhosted.org/packages/a7/95/ca9b5eabde673002c6f1e7ada1b223916fe18f6d661da7aabd4d643718f1/charset_normalizer-3.5.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:11a4d68a6ecda3292cb1e50239e111543ba5d709bb62a6b4ea1afcfa729d8875", size = 273190, upload-time = "2026-09-30T04:35:44.347Z" }, + { url = "https://files.pythonhosted.org/packages/2d/8b/803b4d2a3f6e1740f63f1e87b04d14b42f3d4fdfe6ed7d4db2d34102b14f/charset_normalizer-3.5.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:d6734d2ef8a50fbf8445c139477da401f50d62a0606bf00e20ec6d87773fefb1", size = 253527, upload-time = "2026-09-30T04:35:45.915Z" }, + { url = "https://files.pythonhosted.org/packages/a9/55/93c0e5dbd085ae0471346026abbe7e0db9ea2d6fea74e51f0b5a46f233a7/charset_normalizer-3.5.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:a815775b6c38d4e0ff7bcffbeba67feded90202bb6a226b8dd35f1c855217413", size = 271285, upload-time = "2026-09-30T04:35:47.49Z" }, + { url = "https://files.pythonhosted.org/packages/95/69/0dbd0e0b9b16cfa816cdfcb3e2e3854a1f680dc07fb1245ea125e7448060/charset_normalizer-3.5.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:23851fb4e1b85ed3f6c2a27b777cdfe2e19fb5b38429a8faf38c7542b7665869", size = 260010, upload-time = "2026-09-30T04:35:48.996Z" }, + { url = "https://files.pythonhosted.org/packages/58/9d/e7b88e7b1bf403590c3b573277b5e1e488c68c7a6fbacca310a2c324e90c/charset_normalizer-3.5.2-cp312-cp312-win32.whl", hash = "sha256:db19d07e2e0129e974a0e65d0064fc222a446cd5122c2fd4184d2af9fc734a9e", size = 184125, upload-time = "2026-09-30T04:35:50.777Z" }, + { url = "https://files.pythonhosted.org/packages/eb/e6/e6e083884cbcfd49c64865af05027fe7011be7b2d9179524f099a1b611f3/charset_normalizer-3.5.2-cp312-cp312-win_amd64.whl", hash = "sha256:780fbe7cab297b81dad9fb8dc5eb003c0468ffb0d9e5f65068c53a34661a96bc", size = 207486, upload-time = "2026-09-30T04:35:52.194Z" }, + { url = "https://files.pythonhosted.org/packages/c4/e3/017aea0911ada7405a825c7d937eb3a13009664e2f5b38e8c4bbf2abf894/charset_normalizer-3.5.2-cp312-cp312-win_arm64.whl", hash = "sha256:e2af3aad578aa6bd1384bcf4750fc285e5a9de53f40b7d41e5a0bf748edeb2b3", size = 196734, upload-time = "2026-09-30T04:35:53.636Z" }, + { url = "https://files.pythonhosted.org/packages/c5/34/68292d68512768591aaff07c59bb53ee31341c87759433a859c4641a50c2/charset_normalizer-3.5.2-cp313-cp313-android_24_arm64_v8a.whl", hash = "sha256:ed905975ab14056a2e5eb1c376cb2e1ebc5396baf84163939c518556fccde9f5", size = 225946, upload-time = "2026-09-30T04:35:55.313Z" }, + { url = "https://files.pythonhosted.org/packages/e3/80/bee0b01b90ccd5322ae1d0abb33fab1bd95b7c2eadaf02aeccf22e04ee83/charset_normalizer-3.5.2-cp313-cp313-android_24_x86_64.whl", hash = "sha256:a66c3bc5ab1f0ff2164fc9965ddd611ff0802173f4b9d24554c563f6ab7e1d6e", size = 238619, upload-time = "2026-09-30T04:35:56.863Z" }, + { url = "https://files.pythonhosted.org/packages/78/6e/60ce52a85a7fd631ae8482ae6d74521014ca2f255892679484dc04d7ef56/charset_normalizer-3.5.2-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:d2374b62878abb00cd8309b32af6c0b715cd02dec0ca74ef12e5069bdc64144a", size = 206701, upload-time = "2026-09-30T04:35:58.639Z" }, + { url = "https://files.pythonhosted.org/packages/36/8c/71aafad23f971afc84c2b295bc0c560739ce1dac558aad9fec22e39f3639/charset_normalizer-3.5.2-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:d376bbd28b3a8999db1a103b3b388aee6f1ddeb3e51bc2172993efdcd86e064d", size = 210506, upload-time = "2026-09-30T04:36:00.147Z" }, + { url = "https://files.pythonhosted.org/packages/91/da/3c5a7798c046df7d2d68ad653cf5b6c5a8bfee225055a843c6f2f42aac1a/charset_normalizer-3.5.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6045373d5a89a5ec71afde535db987ca28e76dfa276c2d4c818265b375d4b055", size = 366626, upload-time = "2026-09-30T04:36:01.77Z" }, + { url = "https://files.pythonhosted.org/packages/e1/16/710ac3de2ee354e2bd1a9c94efe45a2d27b5c6ad39b2d6a905be2c094b6c/charset_normalizer-3.5.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:849df64e889b2e17230d58410a03dba311a65b163508fd33679b2b737d4b7858", size = 244624, upload-time = "2026-09-30T04:36:03.389Z" }, + { url = "https://files.pythonhosted.org/packages/d6/39/45c7439f5b63d24f7d5b2a1d760f34af7628782d7144b4cc8ded45c2d4bc/charset_normalizer-3.5.2-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:15c44f7edfd477b06f517a5cc317fc1707edb9de2c865f43d4b6513907473234", size = 235731, upload-time = "2026-09-30T04:36:04.987Z" }, + { url = "https://files.pythonhosted.org/packages/4d/34/38f3154785ce92e9f56eb226f4d35bdfae6b008480dd055f58837a89c810/charset_normalizer-3.5.2-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a89012d6d5476ee112d20d998570ed58df2260a852afb1758809cd6900411d21", size = 270352, upload-time = "2026-09-30T04:36:06.412Z" }, + { url = "https://files.pythonhosted.org/packages/04/f3/859f74e7babc977705026b30593b3be04049632a522fb7000f83c033d747/charset_normalizer-3.5.2-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0c951d5e6dd9c2ff60609476752bee49da4206adde960ebc247766937f72e718", size = 267393, upload-time = "2026-09-30T04:36:07.865Z" }, + { url = "https://files.pythonhosted.org/packages/4b/85/41d27f234b82e47c167a5f6c0f62501dc0c640585ff4aba79e08a390336a/charset_normalizer-3.5.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7218e8f32b0956cfcd048fd42d9d5779809745ca1d86113ca56f66e7ae1549c4", size = 254660, upload-time = "2026-09-30T04:36:09.248Z" }, + { url = "https://files.pythonhosted.org/packages/58/ca/5d1a997587febe5b26d8daffe363b5c1a091cece19828eec6502fd09c5ef/charset_normalizer-3.5.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a19a731138fc27d5682277d3b9df22855cea1239bce7fcec5f78f42ef2d1f3c3", size = 249996, upload-time = "2026-09-30T04:36:10.73Z" }, + { url = "https://files.pythonhosted.org/packages/b3/1f/d1e78246f7ed60c8c8d606b4ac27f66ce49cc3e95f24893ccbeba9f77302/charset_normalizer-3.5.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:62603db9a7caa0802eaa28c1c46fecd7b3a263a774069c24c3c28c302448721c", size = 247028, upload-time = "2026-09-30T04:36:12.294Z" }, + { url = "https://files.pythonhosted.org/packages/8e/37/eba316edd4f0c4d3a5d945924c4eeeae59abac4056aa815d8a4268f863a2/charset_normalizer-3.5.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:b6856554c4f44d79fc2307d5768854310a8f0096e501c75637542c82292b0429", size = 240562, upload-time = "2026-09-30T04:36:13.887Z" }, + { url = "https://files.pythonhosted.org/packages/c8/8e/aaa037d40ca9ef045977f1a661048b1aa33f223adfce3452fe9be9f79d14/charset_normalizer-3.5.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:1bc0baf5ef96b6ede57d47f4b8fe4d9d84019c3bfcbeb20a41edc6a6ee341f1f", size = 271699, upload-time = "2026-09-30T04:36:15.41Z" }, + { url = "https://files.pythonhosted.org/packages/26/19/1c1c9f75974adf523b87f34b8a2adc5a435cd65916812bcbd0dfa45f9a29/charset_normalizer-3.5.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:56bc200a365efb37383b7852e4cc5898d3b2da5987289b543956cf8cad71018a", size = 252030, upload-time = "2026-09-30T04:36:16.839Z" }, + { url = "https://files.pythonhosted.org/packages/bc/90/0660ef18e18df0a4d2a1a0edff7dfbba42d4e50ef2425557a5bb7051f77b/charset_normalizer-3.5.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:2c9ad19a6cfcd5ea5c0d41161d22f9df1dcc277e9bef2751391334546a314c00", size = 268565, upload-time = "2026-09-30T04:36:18.468Z" }, + { url = "https://files.pythonhosted.org/packages/79/ba/57adc269824e8658f1a0f97a9e514c247445a9632b3419b97e0ba37f16dc/charset_normalizer-3.5.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e243bd13217235fc7290c621941c3f5cc8b66e4872495be821d7436ba2fb838d", size = 257627, upload-time = "2026-09-30T04:36:19.938Z" }, + { url = "https://files.pythonhosted.org/packages/9a/85/33abd4315c052d3d4f54c92b1ee49bfbc0dc7115a981e462a793b6d2ab87/charset_normalizer-3.5.2-cp313-cp313-pyemscripten_2025_0_wasm32.whl", hash = "sha256:a090bb2c68df85450502e3e20d665e3a5af9c65a84d6508ed477badd49166fd3", size = 143407, upload-time = "2026-09-30T04:36:21.376Z" }, + { url = "https://files.pythonhosted.org/packages/4f/de/6435e18d1aaa5d910b896d551411c96af1f42a0c56c29afc2016c61ccc2e/charset_normalizer-3.5.2-cp313-cp313-win32.whl", hash = "sha256:2b7b3bbfb4fe8ef40600792d762fbaa9057559f9d3fad209525b7a22b99e91fd", size = 183207, upload-time = "2026-09-30T04:36:22.776Z" }, + { url = "https://files.pythonhosted.org/packages/9c/76/b8ec57f4e9ee3253541abf95e4a462c0175fe8032dcd070f1f2421240942/charset_normalizer-3.5.2-cp313-cp313-win_amd64.whl", hash = "sha256:78456a747de8dc58360ffa581f30a002baf5aa28cb262536545e91f113ed7639", size = 206489, upload-time = "2026-09-30T04:36:24.306Z" }, + { url = "https://files.pythonhosted.org/packages/3e/60/c647c6ae47480221e875ea5d743ff94946f7416e3c69415ab772928e8d32/charset_normalizer-3.5.2-cp313-cp313-win_arm64.whl", hash = "sha256:11912e4bb14baae7c5d8791aa55ba0a3a03ec6729073307b0f57270abaa713d3", size = 195501, upload-time = "2026-09-30T04:36:25.846Z" }, + { url = "https://files.pythonhosted.org/packages/8c/ab/176fbfd5b64939c55d652366aa5b9ef1d767af207a3aa6ebeb0d226c484d/charset_normalizer-3.5.2-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:4275811936e2f06feff5e598fb42a1b7ae852da8e39605211892b56b81a34efd", size = 331815, upload-time = "2026-09-30T04:38:26.216Z" }, + { url = "https://files.pythonhosted.org/packages/7e/84/371eac6b30bdbcbf2d632a1a01809103459216fcaae61b8b8d922c1bfb8a/charset_normalizer-3.5.2-cp37-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:1c50fe28bbc2ced33386f298650d91218076c05420e6cbd790b913adc41659e7", size = 253276, upload-time = "2026-09-30T04:38:28.032Z" }, + { url = "https://files.pythonhosted.org/packages/43/6f/c4fbae58febff71709c51bc7e18fdfa55341dc382704740f9f0cbf03817b/charset_normalizer-3.5.2-cp37-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d19fbd981a488e22cd04883659ca6b08f50b5974f9fd7c95655ef6a043e5893f", size = 241239, upload-time = "2026-09-30T04:38:29.732Z" }, + { url = "https://files.pythonhosted.org/packages/61/71/458c3f42164a07d0c5210798e9e704b39e540a6793b05aba67f3a35243a9/charset_normalizer-3.5.2-cp37-abi3-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0fed1d06615f022ee3b13caf5e8b180cfea32bb2c5aded8a9d44277afc040f93", size = 231121, upload-time = "2026-09-30T04:38:31.462Z" }, + { url = "https://files.pythonhosted.org/packages/09/54/ab9e89367076f6331bb6c65c4bf14a5361fa5191cb6561bf534f18504e1b/charset_normalizer-3.5.2-cp37-abi3-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:838dcc90063569a0448120554591a1d6c4a4ffe11babf048908793154ab86ade", size = 260350, upload-time = "2026-09-30T04:38:33.239Z" }, + { url = "https://files.pythonhosted.org/packages/7c/c1/061431ecc688d9d76602502cb57cc01e691e682c18f1beb45f9673b5bbd2/charset_normalizer-3.5.2-cp37-abi3-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2ce45c6627b22c47e390bc91a41c3d13032192e699fa0bea96e9671b373d69b0", size = 255430, upload-time = "2026-09-30T04:38:34.865Z" }, + { url = "https://files.pythonhosted.org/packages/8d/1f/20c8949f0676f7ab811abdeb7f4d7f1cbc6e61ff20bef08b44edeb092bc8/charset_normalizer-3.5.2-cp37-abi3-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0774bf9bf620249fee3e0b8b9fd3065de213be30f3aa94ce2494b3b638949e26", size = 250612, upload-time = "2026-09-30T04:38:36.649Z" }, + { url = "https://files.pythonhosted.org/packages/2b/9e/46f2fa4c431fc98c4ae76a8cb5bdca54e0341e3cfc3fcfd8e82740250818/charset_normalizer-3.5.2-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1db38f4c5496827c1a501846d64d14c3b80c7e6714e406cd7dc36a9899fa1011", size = 242083, upload-time = "2026-09-30T04:38:38.26Z" }, + { url = "https://files.pythonhosted.org/packages/bd/39/559be29a0c0f086e0bba6922babd38916cc5e0b58ced4de13ee01ea05508/charset_normalizer-3.5.2-cp37-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:304d8e4d493af723536393eee0c689eb7813f4a474c8b479dee63f1fdd98f621", size = 232738, upload-time = "2026-09-30T04:38:39.81Z" }, + { url = "https://files.pythonhosted.org/packages/ff/6c/387b0e4f756a282831c1d9fc6aeb6c51ca4507ca202767c8de15ce9b12e2/charset_normalizer-3.5.2-cp37-abi3-musllinux_1_2_ppc64le.whl", hash = "sha256:9b7f416ff0978e2f2249330527f0ad6fa02f4932e6199692d3b52da2048c19e4", size = 260703, upload-time = "2026-09-30T04:38:41.346Z" }, + { url = "https://files.pythonhosted.org/packages/96/92/1fdf015f09ef449f50d3ac4b67c90887c9c318b727daa95cc4f866e6521d/charset_normalizer-3.5.2-cp37-abi3-musllinux_1_2_riscv64.whl", hash = "sha256:01077390b03f7988f11d700a2194e69b119741a86b1a638b1db88891e3eced8e", size = 247622, upload-time = "2026-09-30T04:38:42.937Z" }, + { url = "https://files.pythonhosted.org/packages/dc/3c/8e7b8a5671ad5d433669fb2a76f1a0164df2d9b1718b0206bc2a16d840cc/charset_normalizer-3.5.2-cp37-abi3-musllinux_1_2_s390x.whl", hash = "sha256:7e841fb9010836c992c9f12fcbd43a831de93a5f726fc1ccd8ca1d0268c5014c", size = 257500, upload-time = "2026-09-30T04:38:44.604Z" }, + { url = "https://files.pythonhosted.org/packages/b4/f0/45b579df5cabc1d5d53ea1cc35e8437d3ca768c0acccc7041517cb6fbb32/charset_normalizer-3.5.2-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:9cae88599c7219005d879f98e5ed53341e9a122af585e1091200358a3003d2a0", size = 255100, upload-time = "2026-09-30T04:38:46.289Z" }, + { url = "https://files.pythonhosted.org/packages/31/68/fdec18a343f5fb3f310588dd478b09ac4799e0b187dbade3a8cd776f03ef/charset_normalizer-3.5.2-cp37-abi3-win32.whl", hash = "sha256:01b0c0d2262a9e28e8484a278c7e1b5d650e3ac8cf2683d2967e25899f208bdf", size = 174499, upload-time = "2026-09-30T04:38:47.999Z" }, + { url = "https://files.pythonhosted.org/packages/9d/8a/b618149cc5207943a0242068d7a27897f56a62947b5a039085f2a22029f8/charset_normalizer-3.5.2-cp37-abi3-win_amd64.whl", hash = "sha256:9f56f72050826f63dcee7a7f55b0a77168cb3bfc553fd405e7f8f9ece75a4036", size = 200092, upload-time = "2026-09-30T04:38:49.707Z" }, + { url = "https://files.pythonhosted.org/packages/03/cf/4c66866fa9e2b1c78e3c911516d1de497a677b7ac60f1eceda74ce777ca3/charset_normalizer-3.5.2-cp37-abi3-win_arm64.whl", hash = "sha256:40ab6bffa02ae10a0581e6c198be7d2d8ca5c2a0c64e4ed3465d766df457573e", size = 294363, upload-time = "2026-09-30T04:38:51.312Z" }, + { url = "https://files.pythonhosted.org/packages/fc/ad/d07d7862a62ffa6d79d68074d14823243dd235a77c45262acbf6adeb28bf/charset_normalizer-3.5.2-py3-none-any.whl", hash = "sha256:b6b751274acb69d77b3323d6b7dbaa3c7fdfc1eb829b7eb61d262f32e1af9685", size = 68872, upload-time = "2026-09-30T04:39:21.828Z" }, +] + +[[package]] +name = "click" +version = "8.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c7/0e/7fa0ef50764b67090eca4114772a2abf8b6148198475e54c660b97caeee6/click-8.5.0.tar.gz", hash = "sha256:ba0d2089de75ea0310e2dde03160e6ca10009947fb95a182f9b54021bb272e34", size = 382235, upload-time = "2026-08-26T13:33:14.56Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/58/50/6c0d534c5f134586a8e1ba4e330569e32f057e33372ae556463212fb4cd3/click-8.5.0-py3-none-any.whl", hash = "sha256:255bc9599cf7748b4b1a446ccc735421bd08a2ae529a8b88597d3de5664ee360", size = 125251, upload-time = "2026-08-26T13:33:12.928Z" }, +] + +[[package]] +name = "click-plugins" +version = "1.1.1.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c3/a4/34847b59150da33690a36da3681d6bbc2ec14ee9a846bc30a6746e5984e4/click_plugins-1.1.1.2.tar.gz", hash = "sha256:d7af3984a99d243c131aa1a828331e7630f4a88a9741fd05c927b204bcf92261", size = 8343, upload-time = "2025-06-25T00:47:37.555Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3d/9a/2abecb28ae875e39c8cad711eb1186d8d14eab564705325e77e4e6ab9ae5/click_plugins-1.1.1.2-py2.py3-none-any.whl", hash = "sha256:008d65743833ffc1f5417bf0e78e8d2c23aab04d9745ba817bd3e71b0feb6aa6", size = 11051, upload-time = "2025-06-25T00:47:36.731Z" }, +] + +[[package]] +name = "cligj" +version = "0.7.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ea/0d/837dbd5d8430fd0f01ed72c4cfb2f548180f4c68c635df84ce87956cff32/cligj-0.7.2.tar.gz", hash = "sha256:a4bc13d623356b373c2c27c53dbd9c68cae5d526270bfa71f6c6fa69669c6b27", size = 9803, upload-time = "2021-05-28T21:23:27.935Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/73/86/43fa9f15c5b9fb6e82620428827cd3c284aa933431405d1bcf5231ae3d3e/cligj-0.7.2-py3-none-any.whl", hash = "sha256:c1ca117dbce1fe20a5809dc96f01e1c2840f6dcc939b3ddbb1111bf330ba82df", size = 7069, upload-time = "2021-05-28T21:23:26.877Z" }, +] + +[[package]] +name = "cloudpickle" +version = "3.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/27/fb/576f067976d320f5f0114a8d9fa1215425441bb35627b1993e5afd8111e5/cloudpickle-3.1.2.tar.gz", hash = "sha256:7fda9eb655c9c230dab534f1983763de5835249750e85fbcef43aaa30a9a2414", size = 22330, upload-time = "2025-11-03T09:25:26.604Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl", hash = "sha256:9acb47f6afd73f60dc1df93bb801b472f05ff42fa6c84167d25cb206be1fbf4a", size = 22228, upload-time = "2025-11-03T09:25:25.534Z" }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "contourpy" +version = "1.3.3" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.12' and sys_platform == 'win32'", + "python_full_version < '3.12' and sys_platform == 'emscripten'", + "python_full_version < '3.12' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +dependencies = [ + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" } }, +] +sdist = { url = "https://files.pythonhosted.org/packages/58/01/1253e6698a07380cd31a736d248a3f2a50a7c88779a1813da27503cadc2a/contourpy-1.3.3.tar.gz", hash = "sha256:083e12155b210502d0bca491432bb04d56dc3432f95a979b429f2848c3dbe880", size = 13466174, upload-time = "2025-07-26T12:03:12.549Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/91/2e/c4390a31919d8a78b90e8ecf87cd4b4c4f05a5b48d05ec17db8e5404c6f4/contourpy-1.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:709a48ef9a690e1343202916450bc48b9e51c049b089c7f79a267b46cffcdaa1", size = 288773, upload-time = "2025-07-26T12:01:02.277Z" }, + { url = "https://files.pythonhosted.org/packages/0d/44/c4b0b6095fef4dc9c420e041799591e3b63e9619e3044f7f4f6c21c0ab24/contourpy-1.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:23416f38bfd74d5d28ab8429cc4d63fa67d5068bd711a85edb1c3fb0c3e2f381", size = 270149, upload-time = "2025-07-26T12:01:04.072Z" }, + { url = "https://files.pythonhosted.org/packages/30/2e/dd4ced42fefac8470661d7cb7e264808425e6c5d56d175291e93890cce09/contourpy-1.3.3-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:929ddf8c4c7f348e4c0a5a3a714b5c8542ffaa8c22954862a46ca1813b667ee7", size = 329222, upload-time = "2025-07-26T12:01:05.688Z" }, + { url = "https://files.pythonhosted.org/packages/f2/74/cc6ec2548e3d276c71389ea4802a774b7aa3558223b7bade3f25787fafc2/contourpy-1.3.3-cp311-cp311-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9e999574eddae35f1312c2b4b717b7885d4edd6cb46700e04f7f02db454e67c1", size = 377234, upload-time = "2025-07-26T12:01:07.054Z" }, + { url = "https://files.pythonhosted.org/packages/03/b3/64ef723029f917410f75c09da54254c5f9ea90ef89b143ccadb09df14c15/contourpy-1.3.3-cp311-cp311-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0bf67e0e3f482cb69779dd3061b534eb35ac9b17f163d851e2a547d56dba0a3a", size = 380555, upload-time = "2025-07-26T12:01:08.801Z" }, + { url = "https://files.pythonhosted.org/packages/5f/4b/6157f24ca425b89fe2eb7e7be642375711ab671135be21e6faa100f7448c/contourpy-1.3.3-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:51e79c1f7470158e838808d4a996fa9bac72c498e93d8ebe5119bc1e6becb0db", size = 355238, upload-time = "2025-07-26T12:01:10.319Z" }, + { url = "https://files.pythonhosted.org/packages/98/56/f914f0dd678480708a04cfd2206e7c382533249bc5001eb9f58aa693e200/contourpy-1.3.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:598c3aaece21c503615fd59c92a3598b428b2f01bfb4b8ca9c4edeecc2438620", size = 1326218, upload-time = "2025-07-26T12:01:12.659Z" }, + { url = "https://files.pythonhosted.org/packages/fb/d7/4a972334a0c971acd5172389671113ae82aa7527073980c38d5868ff1161/contourpy-1.3.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:322ab1c99b008dad206d406bb61d014cf0174df491ae9d9d0fac6a6fda4f977f", size = 1392867, upload-time = "2025-07-26T12:01:15.533Z" }, + { url = "https://files.pythonhosted.org/packages/75/3e/f2cc6cd56dc8cff46b1a56232eabc6feea52720083ea71ab15523daab796/contourpy-1.3.3-cp311-cp311-win32.whl", hash = "sha256:fd907ae12cd483cd83e414b12941c632a969171bf90fc937d0c9f268a31cafff", size = 183677, upload-time = "2025-07-26T12:01:17.088Z" }, + { url = "https://files.pythonhosted.org/packages/98/4b/9bd370b004b5c9d8045c6c33cf65bae018b27aca550a3f657cdc99acdbd8/contourpy-1.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:3519428f6be58431c56581f1694ba8e50626f2dd550af225f82fb5f5814d2a42", size = 225234, upload-time = "2025-07-26T12:01:18.256Z" }, + { url = "https://files.pythonhosted.org/packages/d9/b6/71771e02c2e004450c12b1120a5f488cad2e4d5b590b1af8bad060360fe4/contourpy-1.3.3-cp311-cp311-win_arm64.whl", hash = "sha256:15ff10bfada4bf92ec8b31c62bf7c1834c244019b4a33095a68000d7075df470", size = 193123, upload-time = "2025-07-26T12:01:19.848Z" }, + { url = "https://files.pythonhosted.org/packages/be/45/adfee365d9ea3d853550b2e735f9d66366701c65db7855cd07621732ccfc/contourpy-1.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b08a32ea2f8e42cf1d4be3169a98dd4be32bafe4f22b6c4cb4ba810fa9e5d2cb", size = 293419, upload-time = "2025-07-26T12:01:21.16Z" }, + { url = "https://files.pythonhosted.org/packages/53/3e/405b59cfa13021a56bba395a6b3aca8cec012b45bf177b0eaf7a202cde2c/contourpy-1.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:556dba8fb6f5d8742f2923fe9457dbdd51e1049c4a43fd3986a0b14a1d815fc6", size = 273979, upload-time = "2025-07-26T12:01:22.448Z" }, + { url = "https://files.pythonhosted.org/packages/d4/1c/a12359b9b2ca3a845e8f7f9ac08bdf776114eb931392fcad91743e2ea17b/contourpy-1.3.3-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92d9abc807cf7d0e047b95ca5d957cf4792fcd04e920ca70d48add15c1a90ea7", size = 332653, upload-time = "2025-07-26T12:01:24.155Z" }, + { url = "https://files.pythonhosted.org/packages/63/12/897aeebfb475b7748ea67b61e045accdfcf0d971f8a588b67108ed7f5512/contourpy-1.3.3-cp312-cp312-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b2e8faa0ed68cb29af51edd8e24798bb661eac3bd9f65420c1887b6ca89987c8", size = 379536, upload-time = "2025-07-26T12:01:25.91Z" }, + { url = "https://files.pythonhosted.org/packages/43/8a/a8c584b82deb248930ce069e71576fc09bd7174bbd35183b7943fb1064fd/contourpy-1.3.3-cp312-cp312-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:626d60935cf668e70a5ce6ff184fd713e9683fb458898e4249b63be9e28286ea", size = 384397, upload-time = "2025-07-26T12:01:27.152Z" }, + { url = "https://files.pythonhosted.org/packages/cc/8f/ec6289987824b29529d0dfda0d74a07cec60e54b9c92f3c9da4c0ac732de/contourpy-1.3.3-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4d00e655fcef08aba35ec9610536bfe90267d7ab5ba944f7032549c55a146da1", size = 362601, upload-time = "2025-07-26T12:01:28.808Z" }, + { url = "https://files.pythonhosted.org/packages/05/0a/a3fe3be3ee2dceb3e615ebb4df97ae6f3828aa915d3e10549ce016302bd1/contourpy-1.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:451e71b5a7d597379ef572de31eeb909a87246974d960049a9848c3bc6c41bf7", size = 1331288, upload-time = "2025-07-26T12:01:31.198Z" }, + { url = "https://files.pythonhosted.org/packages/33/1d/acad9bd4e97f13f3e2b18a3977fe1b4a37ecf3d38d815333980c6c72e963/contourpy-1.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:459c1f020cd59fcfe6650180678a9993932d80d44ccde1fa1868977438f0b411", size = 1403386, upload-time = "2025-07-26T12:01:33.947Z" }, + { url = "https://files.pythonhosted.org/packages/cf/8f/5847f44a7fddf859704217a99a23a4f6417b10e5ab1256a179264561540e/contourpy-1.3.3-cp312-cp312-win32.whl", hash = "sha256:023b44101dfe49d7d53932be418477dba359649246075c996866106da069af69", size = 185018, upload-time = "2025-07-26T12:01:35.64Z" }, + { url = "https://files.pythonhosted.org/packages/19/e8/6026ed58a64563186a9ee3f29f41261fd1828f527dd93d33b60feca63352/contourpy-1.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:8153b8bfc11e1e4d75bcb0bff1db232f9e10b274e0929de9d608027e0d34ff8b", size = 226567, upload-time = "2025-07-26T12:01:36.804Z" }, + { url = "https://files.pythonhosted.org/packages/d1/e2/f05240d2c39a1ed228d8328a78b6f44cd695f7ef47beb3e684cf93604f86/contourpy-1.3.3-cp312-cp312-win_arm64.whl", hash = "sha256:07ce5ed73ecdc4a03ffe3e1b3e3c1166db35ae7584be76f65dbbe28a7791b0cc", size = 193655, upload-time = "2025-07-26T12:01:37.999Z" }, + { url = "https://files.pythonhosted.org/packages/68/35/0167aad910bbdb9599272bd96d01a9ec6852f36b9455cf2ca67bd4cc2d23/contourpy-1.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:177fb367556747a686509d6fef71d221a4b198a3905fe824430e5ea0fda54eb5", size = 293257, upload-time = "2025-07-26T12:01:39.367Z" }, + { url = "https://files.pythonhosted.org/packages/96/e4/7adcd9c8362745b2210728f209bfbcf7d91ba868a2c5f40d8b58f54c509b/contourpy-1.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d002b6f00d73d69333dac9d0b8d5e84d9724ff9ef044fd63c5986e62b7c9e1b1", size = 274034, upload-time = "2025-07-26T12:01:40.645Z" }, + { url = "https://files.pythonhosted.org/packages/73/23/90e31ceeed1de63058a02cb04b12f2de4b40e3bef5e082a7c18d9c8ae281/contourpy-1.3.3-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:348ac1f5d4f1d66d3322420f01d42e43122f43616e0f194fc1c9f5d830c5b286", size = 334672, upload-time = "2025-07-26T12:01:41.942Z" }, + { url = "https://files.pythonhosted.org/packages/ed/93/b43d8acbe67392e659e1d984700e79eb67e2acb2bd7f62012b583a7f1b55/contourpy-1.3.3-cp313-cp313-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:655456777ff65c2c548b7c454af9c6f33f16c8884f11083244b5819cc214f1b5", size = 381234, upload-time = "2025-07-26T12:01:43.499Z" }, + { url = "https://files.pythonhosted.org/packages/46/3b/bec82a3ea06f66711520f75a40c8fc0b113b2a75edb36aa633eb11c4f50f/contourpy-1.3.3-cp313-cp313-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:644a6853d15b2512d67881586bd03f462c7ab755db95f16f14d7e238f2852c67", size = 385169, upload-time = "2025-07-26T12:01:45.219Z" }, + { url = "https://files.pythonhosted.org/packages/4b/32/e0f13a1c5b0f8572d0ec6ae2f6c677b7991fafd95da523159c19eff0696a/contourpy-1.3.3-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4debd64f124ca62069f313a9cb86656ff087786016d76927ae2cf37846b006c9", size = 362859, upload-time = "2025-07-26T12:01:46.519Z" }, + { url = "https://files.pythonhosted.org/packages/33/71/e2a7945b7de4e58af42d708a219f3b2f4cff7386e6b6ab0a0fa0033c49a9/contourpy-1.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a15459b0f4615b00bbd1e91f1b9e19b7e63aea7483d03d804186f278c0af2659", size = 1332062, upload-time = "2025-07-26T12:01:48.964Z" }, + { url = "https://files.pythonhosted.org/packages/12/fc/4e87ac754220ccc0e807284f88e943d6d43b43843614f0a8afa469801db0/contourpy-1.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ca0fdcd73925568ca027e0b17ab07aad764be4706d0a925b89227e447d9737b7", size = 1403932, upload-time = "2025-07-26T12:01:51.979Z" }, + { url = "https://files.pythonhosted.org/packages/a6/2e/adc197a37443f934594112222ac1aa7dc9a98faf9c3842884df9a9d8751d/contourpy-1.3.3-cp313-cp313-win32.whl", hash = "sha256:b20c7c9a3bf701366556e1b1984ed2d0cedf999903c51311417cf5f591d8c78d", size = 185024, upload-time = "2025-07-26T12:01:53.245Z" }, + { url = "https://files.pythonhosted.org/packages/18/0b/0098c214843213759692cc638fce7de5c289200a830e5035d1791d7a2338/contourpy-1.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:1cadd8b8969f060ba45ed7c1b714fe69185812ab43bd6b86a9123fe8f99c3263", size = 226578, upload-time = "2025-07-26T12:01:54.422Z" }, + { url = "https://files.pythonhosted.org/packages/8a/9a/2f6024a0c5995243cd63afdeb3651c984f0d2bc727fd98066d40e141ad73/contourpy-1.3.3-cp313-cp313-win_arm64.whl", hash = "sha256:fd914713266421b7536de2bfa8181aa8c699432b6763a0ea64195ebe28bff6a9", size = 193524, upload-time = "2025-07-26T12:01:55.73Z" }, + { url = "https://files.pythonhosted.org/packages/c0/b3/f8a1a86bd3298513f500e5b1f5fd92b69896449f6cab6a146a5d52715479/contourpy-1.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:88df9880d507169449d434c293467418b9f6cbe82edd19284aa0409e7fdb933d", size = 306730, upload-time = "2025-07-26T12:01:57.051Z" }, + { url = "https://files.pythonhosted.org/packages/3f/11/4780db94ae62fc0c2053909b65dc3246bd7cecfc4f8a20d957ad43aa4ad8/contourpy-1.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:d06bb1f751ba5d417047db62bca3c8fde202b8c11fb50742ab3ab962c81e8216", size = 287897, upload-time = "2025-07-26T12:01:58.663Z" }, + { url = "https://files.pythonhosted.org/packages/ae/15/e59f5f3ffdd6f3d4daa3e47114c53daabcb18574a26c21f03dc9e4e42ff0/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e4e6b05a45525357e382909a4c1600444e2a45b4795163d3b22669285591c1ae", size = 326751, upload-time = "2025-07-26T12:02:00.343Z" }, + { url = "https://files.pythonhosted.org/packages/0f/81/03b45cfad088e4770b1dcf72ea78d3802d04200009fb364d18a493857210/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ab3074b48c4e2cf1a960e6bbeb7f04566bf36b1861d5c9d4d8ac04b82e38ba20", size = 375486, upload-time = "2025-07-26T12:02:02.128Z" }, + { url = "https://files.pythonhosted.org/packages/0c/ba/49923366492ffbdd4486e970d421b289a670ae8cf539c1ea9a09822b371a/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c3d53c796f8647d6deb1abe867daeb66dcc8a97e8455efa729516b997b8ed99", size = 388106, upload-time = "2025-07-26T12:02:03.615Z" }, + { url = "https://files.pythonhosted.org/packages/9f/52/5b00ea89525f8f143651f9f03a0df371d3cbd2fccd21ca9b768c7a6500c2/contourpy-1.3.3-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50ed930df7289ff2a8d7afeb9603f8289e5704755c7e5c3bbd929c90c817164b", size = 352548, upload-time = "2025-07-26T12:02:05.165Z" }, + { url = "https://files.pythonhosted.org/packages/32/1d/a209ec1a3a3452d490f6b14dd92e72280c99ae3d1e73da74f8277d4ee08f/contourpy-1.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4feffb6537d64b84877da813a5c30f1422ea5739566abf0bd18065ac040e120a", size = 1322297, upload-time = "2025-07-26T12:02:07.379Z" }, + { url = "https://files.pythonhosted.org/packages/bc/9e/46f0e8ebdd884ca0e8877e46a3f4e633f6c9c8c4f3f6e72be3fe075994aa/contourpy-1.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2b7e9480ffe2b0cd2e787e4df64270e3a0440d9db8dc823312e2c940c167df7e", size = 1391023, upload-time = "2025-07-26T12:02:10.171Z" }, + { url = "https://files.pythonhosted.org/packages/b9/70/f308384a3ae9cd2209e0849f33c913f658d3326900d0ff5d378d6a1422d2/contourpy-1.3.3-cp313-cp313t-win32.whl", hash = "sha256:283edd842a01e3dcd435b1c5116798d661378d83d36d337b8dde1d16a5fc9ba3", size = 196157, upload-time = "2025-07-26T12:02:11.488Z" }, + { url = "https://files.pythonhosted.org/packages/b2/dd/880f890a6663b84d9e34a6f88cded89d78f0091e0045a284427cb6b18521/contourpy-1.3.3-cp313-cp313t-win_amd64.whl", hash = "sha256:87acf5963fc2b34825e5b6b048f40e3635dd547f590b04d2ab317c2619ef7ae8", size = 240570, upload-time = "2025-07-26T12:02:12.754Z" }, + { url = "https://files.pythonhosted.org/packages/80/99/2adc7d8ffead633234817ef8e9a87115c8a11927a94478f6bb3d3f4d4f7d/contourpy-1.3.3-cp313-cp313t-win_arm64.whl", hash = "sha256:3c30273eb2a55024ff31ba7d052dde990d7d8e5450f4bbb6e913558b3d6c2301", size = 199713, upload-time = "2025-07-26T12:02:14.4Z" }, + { url = "https://files.pythonhosted.org/packages/a5/29/8dcfe16f0107943fa92388c23f6e05cff0ba58058c4c95b00280d4c75a14/contourpy-1.3.3-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:cd5dfcaeb10f7b7f9dc8941717c6c2ade08f587be2226222c12b25f0483ed497", size = 278809, upload-time = "2025-07-26T12:02:52.74Z" }, + { url = "https://files.pythonhosted.org/packages/85/a9/8b37ef4f7dafeb335daee3c8254645ef5725be4d9c6aa70b50ec46ef2f7e/contourpy-1.3.3-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:0c1fc238306b35f246d61a1d416a627348b5cf0648648a031e14bb8705fcdfe8", size = 261593, upload-time = "2025-07-26T12:02:54.037Z" }, + { url = "https://files.pythonhosted.org/packages/0a/59/ebfb8c677c75605cc27f7122c90313fd2f375ff3c8d19a1694bda74aaa63/contourpy-1.3.3-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:70f9aad7de812d6541d29d2bbf8feb22ff7e1c299523db288004e3157ff4674e", size = 302202, upload-time = "2025-07-26T12:02:55.947Z" }, + { url = "https://files.pythonhosted.org/packages/3c/37/21972a15834d90bfbfb009b9d004779bd5a07a0ec0234e5ba8f64d5736f4/contourpy-1.3.3-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5ed3657edf08512fc3fe81b510e35c2012fbd3081d2e26160f27ca28affec989", size = 329207, upload-time = "2025-07-26T12:02:57.468Z" }, + { url = "https://files.pythonhosted.org/packages/0c/58/bd257695f39d05594ca4ad60df5bcb7e32247f9951fd09a9b8edb82d1daa/contourpy-1.3.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:3d1a3799d62d45c18bafd41c5fa05120b96a28079f2393af559b843d1a966a77", size = 225315, upload-time = "2025-07-26T12:02:58.801Z" }, +] + +[[package]] +name = "contourpy" +version = "1.4.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.12' and sys_platform == 'win32'", + "python_full_version >= '3.12' and sys_platform == 'emscripten'", + "python_full_version >= '3.12' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +dependencies = [ + { name = "numpy", version = "2.5.4", source = { registry = "https://pypi.org/simple" } }, +] +sdist = { url = "https://files.pythonhosted.org/packages/83/5a/a55177dd22553a277388e8a1b3220e92de91bacb28356cdc73caa240121d/contourpy-1.4.0.tar.gz", hash = "sha256:20156f5a1ac4f8ce02656e39a61e82164a3d359796dc8026f75b062783d500e1", size = 13323726, upload-time = "2026-09-11T19:05:05.808Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/97/ba/01bde9753bdbba04a6da9d2bff3881f021bc708f654655e95fae26d3b3a3/contourpy-1.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:186ba929df36d61b6127da2e89cd1357e4cb79aca647381ab1a6feb0b152877b", size = 297302, upload-time = "2026-09-11T19:02:45.103Z" }, + { url = "https://files.pythonhosted.org/packages/dd/f5/c5bf67522d49a2222f3154fe46879451d26f0e35ece41770758a64ea78cb/contourpy-1.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c76f3a5318164db7d9401132fc1b5364b784c613c93fa506e3b5e6d1bf353ec8", size = 284921, upload-time = "2026-09-11T19:02:48.049Z" }, + { url = "https://files.pythonhosted.org/packages/0f/cc/cb989599eec12fda312e127eb8e04a8b21a7a6c94bf7ff1bc68273334a42/contourpy-1.4.0-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:43c3ccbb32c6294b183dcc8e8c46dacf5ecef809497e3d48a5be298eef185ad0", size = 356332, upload-time = "2026-09-11T19:02:51.408Z" }, + { url = "https://files.pythonhosted.org/packages/47/fb/6f620d7602507817b0b4c21dd790ed68688f1f4ae9c29aacf21f08db5cf1/contourpy-1.4.0-cp312-cp312-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:86d05cec773c9507a3950122e0e40ce77c23c75ceeb2fc189514e71893cbb34b", size = 405878, upload-time = "2026-09-11T19:02:54.093Z" }, + { url = "https://files.pythonhosted.org/packages/32/4e/693c6d6bdece679f0953775eef3a1b66d56be55478d936e7deb1f3004422/contourpy-1.4.0-cp312-cp312-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2deb580178ca19437a84bd77e4bc2cd91a8ccad212413a83c273900868b5978c", size = 408296, upload-time = "2026-09-11T19:02:56.528Z" }, + { url = "https://files.pythonhosted.org/packages/78/f9/b6831508960d559581c448532ba4df312217072975486dbf2b24fcc5b76f/contourpy-1.4.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:875f42444c9cf48d56f724f2637e60d0f73b3b12c9041e1484580a233edf9591", size = 383243, upload-time = "2026-09-11T19:02:58.638Z" }, + { url = "https://files.pythonhosted.org/packages/f2/21/52903825a0ae7bb625e8bd30a09816d4c3c5d6174a469c10a616166cf780/contourpy-1.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f863c6100bf926cf47d13f3cd75f9bb8ebd98aaab230eeb4468b91f98f39a6b3", size = 1357306, upload-time = "2026-09-11T19:03:01.036Z" }, + { url = "https://files.pythonhosted.org/packages/d8/79/d68b1ce8539e4071518fed9523f558398f34dcd078b8927b109c72dad2ef/contourpy-1.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3863ef2e2b13fe93f8c0ebb08ee400cb07153b8b0e91c5acb26e4537f283634f", size = 1427243, upload-time = "2026-09-11T19:03:03.866Z" }, + { url = "https://files.pythonhosted.org/packages/ac/f0/b75a10e9d0616b97a30de277b6dfe83f1879880854c7330337309530eb48/contourpy-1.4.0-cp312-cp312-win32.whl", hash = "sha256:5450f091ac1be0be3ad3a2a3b3f23b5e443e78c670ced4fd347d626f92a28fd2", size = 347345, upload-time = "2026-09-11T19:03:06.192Z" }, + { url = "https://files.pythonhosted.org/packages/50/a9/dab08786bb4d77ef9a046b3c1be923be4e73c5d07d91f5a54e8ea9b09e41/contourpy-1.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:6e697d94e69f499ff6bebb899cae97a58d5d14f0e1fe9568b43a0248d2f9af8c", size = 234083, upload-time = "2026-09-11T19:03:08.122Z" }, + { url = "https://files.pythonhosted.org/packages/47/b9/3ba509755a970dbde5948e7142ac21f266be72f38017cc4087a05fdceee1/contourpy-1.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:0c7a4c2716a4e98342221954416a836cca77c996c14ddbf22b5f01d5d93ca09c", size = 559521, upload-time = "2026-09-11T19:03:10.047Z" }, + { url = "https://files.pythonhosted.org/packages/b7/8b/62a6eacea08ea0b6ab0159a7783df7ed421ff9c1ff58fef7c31f58fa5e52/contourpy-1.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e21d1d4a9db5b3a793649c7ab0ddb4697f2818929eec871a7b9c45fce7b2a1ea", size = 297330, upload-time = "2026-09-11T19:03:11.809Z" }, + { url = "https://files.pythonhosted.org/packages/3f/2f/103cdc5b7077cd569989d3ddb99ee3d4e96ee31bde8df2a6e426b2dc6195/contourpy-1.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:196759a3e4db60e1546167e361090ebb6d199aa3aafc9e34ea17a5ac3b23e814", size = 285037, upload-time = "2026-09-11T19:03:13.781Z" }, + { url = "https://files.pythonhosted.org/packages/bf/25/97001d1fdf940e6f5adfc592d4f5899fff95daf7dceead3988fe09987d2d/contourpy-1.4.0-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c0e07c691f3b3321913ed9b8161c50ea5f77fadd006f52f5e755359b0dcbfc5", size = 356675, upload-time = "2026-09-11T19:03:15.374Z" }, + { url = "https://files.pythonhosted.org/packages/a6/5e/9a7241a89e2f74402aefa2569e1349ad2750ff77823c04c788eaaedb7fdd/contourpy-1.4.0-cp313-cp313-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7f861af508fca2384eef392bc1d8377cfa349b2b854cb11b45d89c75780e2561", size = 406990, upload-time = "2026-09-11T19:03:17.44Z" }, + { url = "https://files.pythonhosted.org/packages/36/81/3a4fbbf6a806eada5453a6d16f6c7d96b9f4b3e57a3127368fb545986b8a/contourpy-1.4.0-cp313-cp313-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a03b32c2e7eda8b17757c022162c13c86f5c3d6f937dddf9ba3c3ff7b513953b", size = 407271, upload-time = "2026-09-11T19:03:19.523Z" }, + { url = "https://files.pythonhosted.org/packages/68/77/8f93ecbde1a4d11dfd460cd19a0c159f5487352e751fa87f5d78cc9db08e/contourpy-1.4.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f1219a8898523cba821085f2da8a1b962cc696325763f09d7f93538ba43b2d70", size = 384707, upload-time = "2026-09-11T19:03:21.208Z" }, + { url = "https://files.pythonhosted.org/packages/05/07/e5cdf94d68961ee464de6b4fedd3d2f666584f59ccd124bb1290597122ab/contourpy-1.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:20da2f86bfaed60dbba729fe738c8241b1dd22b6cf7e8cbbf30df290bd04ba28", size = 1356741, upload-time = "2026-09-11T19:03:23.621Z" }, + { url = "https://files.pythonhosted.org/packages/fd/8a/2301d07accb257ac08daeb631875fada06fedd019c20ff7b00ee07e9d211/contourpy-1.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6ced1670fafee703b8277ab1645072a226e74f167166eae146fb743916119b67", size = 1426793, upload-time = "2026-09-11T19:03:25.955Z" }, + { url = "https://files.pythonhosted.org/packages/7d/5d/a62648f06259d15c139b14eb8cf3c58fe53cb970a55f2bb77d1070e80a41/contourpy-1.4.0-cp313-cp313-pyemscripten_2025_0_wasm32.whl", hash = "sha256:abd0f7e51ecc52bf8c95713bd80b1c17c516a71fa391c54475308a4377903c2b", size = 117961, upload-time = "2026-09-11T19:03:27.713Z" }, + { url = "https://files.pythonhosted.org/packages/71/31/c5b7771ba6982f319ec90c0cf5c425b36891adfeee2d9f667f90b2d0c166/contourpy-1.4.0-cp313-cp313-win32.whl", hash = "sha256:510f7d93d94cf6ebf4e2cd0640bea16ab8affac7547230175b6745b596ce0599", size = 347438, upload-time = "2026-09-11T19:03:29.829Z" }, + { url = "https://files.pythonhosted.org/packages/68/c1/a59df0754bf83a6f3e153cc19d550a5b01f607e389b1c4d57f436f9139e0/contourpy-1.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:6507a016976a75a15aee47809a34605de6244ccc056e6164bf9fb14b27eecad4", size = 234084, upload-time = "2026-09-11T19:03:31.657Z" }, + { url = "https://files.pythonhosted.org/packages/41/48/97224f5decb1efa5a610f912195deb45321f73ec9b9dac485cd6fe2d5a6a/contourpy-1.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:423bd5f4b3f11d54a8c597234e513382e3454bb106b8ec7ae32ba4ed63f8d99b", size = 559559, upload-time = "2026-09-11T19:03:33.528Z" }, +] + +[[package]] +name = "cookiecutter" +version = "2.7.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "arrow" }, + { name = "binaryornot" }, + { name = "click" }, + { name = "jinja2" }, + { name = "python-slugify" }, + { name = "pyyaml" }, + { name = "requests" }, + { name = "rich" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/92/03/f4c96d8fd4f5e8af0210bf896eb63927f35d3014a8e8f3bf9d2c43ad3332/cookiecutter-2.7.1.tar.gz", hash = "sha256:ca7bb7bc8c6ff441fbf53921b5537668000e38d56e28d763a1b73975c66c6138", size = 142854, upload-time = "2026-03-04T04:06:02.786Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/14/a9/8c855c14b401dc67d20739345295af5afce5e930a69600ab20f6cfa50b5c/cookiecutter-2.7.1-py3-none-any.whl", hash = "sha256:cee50defc1eaa7ad0071ee9b9893b746c1b3201b66bf4d3686d0f127c8ed6cf9", size = 41317, upload-time = "2026-03-04T04:06:01.221Z" }, +] + +[[package]] +name = "coverage" +version = "7.17.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/02/9b/639ff6f5b3158999fab6f03cb7597526ad57e15cad0c6cfa9b64bac874dc/coverage-7.17.1.tar.gz", hash = "sha256:5c5c77f95a0e83e6b79eff948d251c9911cfd61d67f5059159fc25b7bc4aec27", size = 995565, upload-time = "2026-11-03T12:38:47.608Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f8/28/bc914aa69f2dd27cfcebedc62acf472fca357389ba80295a8fa693d6ea59/coverage-7.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e7429e44cf754fec4baf895307d7b3b906140df82f5c8637777e9cf4227afd87", size = 227529, upload-time = "2026-11-03T12:36:03.283Z" }, + { url = "https://files.pythonhosted.org/packages/97/46/60f46d3219278e53455a4fdb4b755e7c47a0be5b14b89f2c827bf28bc8eb/coverage-7.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:08d773b49e0001ef360a4ee7cf8e7a2101a52007fcc5b46ef56ad1032bd403b7", size = 227947, upload-time = "2026-11-03T12:36:06.739Z" }, + { url = "https://files.pythonhosted.org/packages/27/18/791ca5cf2f4b38c1e3e421d80992a67be4f0c09b5add22ff1e3c164899ea/coverage-7.17.1-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:fa52a8600a7cd6f219b41254bb6a04e000da4a79bb993d3c5b21a625f93425f8", size = 259977, upload-time = "2026-11-03T12:36:08.067Z" }, + { url = "https://files.pythonhosted.org/packages/b8/ed/952fea2330a020cd2bb4315aee09d75716c20f2920d0c3b952e7bf28c992/coverage-7.17.1-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c33792ae97208a5a978ac63b16e70d344946ed1a0db3abb1ea64f1f7b110b5b5", size = 262198, upload-time = "2026-11-03T12:36:09.452Z" }, + { url = "https://files.pythonhosted.org/packages/b5/e6/761f6eefeee17b634b104004214cccd08b0350295ad5d0fd8bec447c5224/coverage-7.17.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:23a49c5949e96b5eae1cf16aa3f37ab6fbcf1a15602cdab9c7c31cf2ebd49307", size = 264294, upload-time = "2026-11-03T12:36:10.725Z" }, + { url = "https://files.pythonhosted.org/packages/ae/dc/4ebdbb17f038dc1e1b8677514af2e832705085327dbe4ca5760914aad83a/coverage-7.17.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:adc720c43f820c91e3b89158ab6b6d162532fd925b0d06ad32baf2bdf130f41c", size = 266304, upload-time = "2026-11-03T12:36:12.32Z" }, + { url = "https://files.pythonhosted.org/packages/8f/34/7597613c900c361370d2a8a7345939072aa8c751fe54dc374ee55fed0bb4/coverage-7.17.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f96ac45a2adb46bc59c2c2c3dc4303501397a906a7ed953832d2f03712a82774", size = 260091, upload-time = "2026-11-03T12:36:13.759Z" }, + { url = "https://files.pythonhosted.org/packages/3d/36/b6b0dc241df22b3f61ef2fd26675fc14309bc3642a6f9c9d9cfb5f689383/coverage-7.17.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f75d95a0fbbfa4095c307027e9a9286063f12c4f4b9f5df46023f9011175b32e", size = 261993, upload-time = "2026-11-03T12:36:15.072Z" }, + { url = "https://files.pythonhosted.org/packages/f6/c9/c8d973fe07fd67378d49a45085c7fde6f2da5a01329541fb26cc83afd0a1/coverage-7.17.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:06ee57242ba8b5da8a38289f4d70c767529e089c5e5f5492dec1851e3c0981a2", size = 260006, upload-time = "2026-11-03T12:36:16.487Z" }, + { url = "https://files.pythonhosted.org/packages/a0/b0/1f321be70939ffc74ede6b1387f5b727397870cb867382873fa6b23c047d/coverage-7.17.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:eb3e0fa955355da7cca2b4b55d93a4c59b60862e5563f9fac506e35e245cd302", size = 263932, upload-time = "2026-11-03T12:36:17.855Z" }, + { url = "https://files.pythonhosted.org/packages/a7/d5/5cd76e78fceab21289e344768660638b2373aebb4155c6fa76007a2e25b9/coverage-7.17.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:2dbdfb11dd06d10829e13724b4858e61925bbb16c5b396f7d9ccc250bcee916d", size = 259534, upload-time = "2026-11-03T12:36:19.331Z" }, + { url = "https://files.pythonhosted.org/packages/e9/52/265f3b4eef6b51f7cb5ac12f356e186388861530719437b29a8d76f5c218/coverage-7.17.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:23faef6b1b30335aff610b70f4203ae19b19deb6abd637fa9736f01da4d5588c", size = 260733, upload-time = "2026-11-03T12:36:20.643Z" }, + { url = "https://files.pythonhosted.org/packages/92/b9/fe77b52f2ded65934e0d9a559ca8385400fd54401988004dfe4242af9606/coverage-7.17.1-cp311-cp311-win32.whl", hash = "sha256:110ecef18899cbe49d7a904bb45e3c5f13a7d9c2114ba39daa9a0f3ca9eaf41b", size = 229397, upload-time = "2026-11-03T12:36:22.021Z" }, + { url = "https://files.pythonhosted.org/packages/57/f6/6b856eba8d5bcdb521c9cc41b4c4edf88a4ac61aa04e0e463f67b38872ee/coverage-7.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:c4935efed225aa96f39b5ed7db08baa96996efa85c5a9d3ae2bde3ff8487631b", size = 230001, upload-time = "2026-11-03T12:36:23.372Z" }, + { url = "https://files.pythonhosted.org/packages/77/d8/11cd7ebb5b80f5570705a10f9f3ddd11e6a0b8ff9d6580f46707145dd458/coverage-7.17.1-cp311-cp311-win_arm64.whl", hash = "sha256:6c7182c03dcea5e457553f38ee0005dda781bc26c734d10659365775d5a02fe7", size = 229910, upload-time = "2026-11-03T12:36:24.679Z" }, + { url = "https://files.pythonhosted.org/packages/64/37/0e501535cfd4c71555ac26fc951cd926d6200f20ba5146cb34de429f5e36/coverage-7.17.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7ae17b0127df7298fffec6866d45969620ca70267cf8961c5102a872abfd0198", size = 227626, upload-time = "2026-11-03T12:36:26.151Z" }, + { url = "https://files.pythonhosted.org/packages/84/f8/619e48ba82da0ff2fea7e377e427d5a1d4e918dd90613240145576aa76c9/coverage-7.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b8a8f0b1d662828e7cfd52ba6d4e8d79496d4711057c33daaf29f50414d5f8c6", size = 227859, upload-time = "2026-11-03T12:36:27.659Z" }, + { url = "https://files.pythonhosted.org/packages/45/9c/93a83fdb0120538192d38b0fe77deb6036eae357a803a1e62b9bb7d1cf57/coverage-7.17.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:710a02c4a2f7de5f08c2ecd26fced31099032b8c3fcf2f602e4cef5190008baf", size = 260153, upload-time = "2026-11-03T12:36:29.012Z" }, + { url = "https://files.pythonhosted.org/packages/35/be/2704ac4e51ef9075255314cf547e44f43cd4bde96c9efb7da826ce39ce57/coverage-7.17.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:622f0d4869db8fdfa3a7ee2c6b0207b17abe59b462617599c8c3def27f482d78", size = 263480, upload-time = "2026-11-03T12:36:30.308Z" }, + { url = "https://files.pythonhosted.org/packages/ef/83/64edd51a572196239890825cdfefce0c38f634de439d3d840bbe33e09271/coverage-7.17.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d8ba77c22882009f4f6599ccc88a9170b9c6e69f4464c2ef235e9ca24b62f68", size = 264016, upload-time = "2026-11-03T12:36:31.667Z" }, + { url = "https://files.pythonhosted.org/packages/71/95/c9998e7e718e86f85142a0cbf517d100ba2bb4c068f814245f96a253772c/coverage-7.17.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8ff5288d8b03519e05697cc7065e75c55a10ce4d7581108978b549516c7edaab", size = 266294, upload-time = "2026-11-03T12:36:33.03Z" }, + { url = "https://files.pythonhosted.org/packages/46/b0/73c6276d5c75239b725207e60b19ed23a4d8c564a92232890d7ba9e70bef/coverage-7.17.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6c461a379dbacd1b92c3800f529025e3b85a41a90b97e49d3b004ec690a13847", size = 260386, upload-time = "2026-11-03T12:36:34.463Z" }, + { url = "https://files.pythonhosted.org/packages/3b/57/9374f6513341d07c1a02bdbd37ee7e98fbc8d963da9e71dc4b580ef86994/coverage-7.17.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bf29880edc2dbe589f3bc9fc7ea9a4c81cdce118a69feb52bf4dc420b475ed9b", size = 261901, upload-time = "2026-11-03T12:36:36.001Z" }, + { url = "https://files.pythonhosted.org/packages/ea/ca/50a4fab6983deb19d0ef8941c64e69e09d9428523c16e38262a0a28d379e/coverage-7.17.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:d416449f86fa1af6a9c1031fbd27ad7c1d30f57945bb52cd28250266c46ae549", size = 260066, upload-time = "2026-11-03T12:36:37.426Z" }, + { url = "https://files.pythonhosted.org/packages/66/db/4c5e12596e68084a59a108712f2a89e5cedf30fc3f2fd8b2b38e62d300a5/coverage-7.17.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a7858779514aa04242f7fd7b4a92dd395d08fe791ba33624187d6c68f96245c6", size = 264114, upload-time = "2026-11-03T12:36:38.937Z" }, + { url = "https://files.pythonhosted.org/packages/bc/9b/2c8bc32551e15080c894d7f7d8c13589c91980923a11a96b51ca7c538eee/coverage-7.17.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:b367f16bf32939fafa8f26669fdb59ab21ee601527ad716aab7cd178ed3bd28f", size = 259566, upload-time = "2026-11-03T12:36:40.514Z" }, + { url = "https://files.pythonhosted.org/packages/f3/54/9cbf5e95496e2f8d8684eea3e7f1f4911375d01c73ccb51b067385c0d77c/coverage-7.17.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a20b2945e275fe76ed2ed6be6103aa4d8e923b8f2678f1f3dc31c6ec8849c8a1", size = 262053, upload-time = "2026-11-03T12:36:41.975Z" }, + { url = "https://files.pythonhosted.org/packages/d1/3e/ef5728364207026031aac8f068e68f15bf71c1a84046efbca4623617cd2a/coverage-7.17.1-cp312-cp312-win32.whl", hash = "sha256:6c89aa3497bd593e1bfd9f57ebdecc5544387b5e94e6d4c6cb7597565eb551e6", size = 229457, upload-time = "2026-11-03T12:36:43.227Z" }, + { url = "https://files.pythonhosted.org/packages/e9/a9/8830de67aa3b16f94e6cc456dde07db95d0ea8001cf8224340cdb6d47cb4/coverage-7.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:6e6c9759a42cf946ca427dbbd772305516401ff6d280b93cc52e7b11fcedfb2a", size = 230037, upload-time = "2026-11-03T12:36:44.547Z" }, + { url = "https://files.pythonhosted.org/packages/6b/00/a932037e8a27705d8f1aded46edf879d8f2592392dcd025349fa72d9b5a8/coverage-7.17.1-cp312-cp312-win_arm64.whl", hash = "sha256:1c06c40a9fb03ab43ace3ee27a11e61ebf233161fb9ebabfb2b51f78cf1602e0", size = 229843, upload-time = "2026-11-03T12:36:45.821Z" }, + { url = "https://files.pythonhosted.org/packages/04/e0/ff26f5a74cfcfe131bebe09649a9c9dc84a8fe8fed5cbed0117c57f7efeb/coverage-7.17.1-cp313-cp313-android_24_arm64_v8a.whl", hash = "sha256:9b8d68b2330cda90e1972e78c93c6fd572453c5b4e29594aa98550694efb0466", size = 228775, upload-time = "2026-11-03T12:36:47.054Z" }, + { url = "https://files.pythonhosted.org/packages/df/af/b83c4a7d7ed81458298c137aeefbfb7a088d36fff6fb60a0f8375e731990/coverage-7.17.1-cp313-cp313-android_24_x86_64.whl", hash = "sha256:b975d0bd867eefeff37ba28aee6ee08a0987ca3ce6bfb5e0d3ddc1b40cf3e52e", size = 228845, upload-time = "2026-11-03T12:36:48.362Z" }, + { url = "https://files.pythonhosted.org/packages/52/07/3156d72bb538d05aa4cf51d90a86cc705e9cb570d8a0eeef80880cb21d09/coverage-7.17.1-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:60002a0ea7f5e5f64006aba9f37aa29a4b1e52756bc0fbbc2471188b2732e4ed", size = 227091, upload-time = "2026-11-03T12:36:49.687Z" }, + { url = "https://files.pythonhosted.org/packages/cb/50/03128c382b303e02a48b1f9658bda6412288fa1d83b478696a08e410c0f6/coverage-7.17.1-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:80b613bd68caa146d090023a18dfc504eb958ff525e31e6f2368b363502bb1ea", size = 227396, upload-time = "2026-11-03T12:36:51.162Z" }, + { url = "https://files.pythonhosted.org/packages/90/83/b617213e11609d6d5d64afc701e40384bbfb39c9999531ef747b6d1259cb/coverage-7.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8c2e67f95c8de089ba0ff11c7b7ee83ec0890d418e8d0185e863367f5250a087", size = 227516, upload-time = "2026-11-03T12:36:52.897Z" }, + { url = "https://files.pythonhosted.org/packages/44/98/71e36bd7bc121a33c5c78a1f687587802b2806299feb4b9f2fcf21616007/coverage-7.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:56f81ec13a8790a0f2ea30a14d2f39dec2374115eb88cb25cc2c0226f72f8b0e", size = 227781, upload-time = "2026-11-03T12:36:54.144Z" }, + { url = "https://files.pythonhosted.org/packages/04/e8/28f4c350e124242a757b86cd19d71d807eda8b202126c39fb57eb1495f7b/coverage-7.17.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:b7fb16b5c4fa1a37d69204ec5f7b8b187d2cfc54a2c084e2750ceee443748ac0", size = 258947, upload-time = "2026-11-03T12:36:55.461Z" }, + { url = "https://files.pythonhosted.org/packages/64/94/f99d4cbd1b187ac1029cadf293a934f8a2300908d8055b5c9de11577a875/coverage-7.17.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b05c06318fd46706c9055a1acc6f4aa0d3a63b211bf3b6114d3c1feab76e37f2", size = 261858, upload-time = "2026-11-03T12:36:56.804Z" }, + { url = "https://files.pythonhosted.org/packages/58/9c/b91da5d4790ac926e6bede81a1bac0b8e216d2574f5c332c7494d23b88de/coverage-7.17.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8f3bbb5f9c074e97f7082987ead736036aa522cb39b996b8ed9c5aa8eee008be", size = 262791, upload-time = "2026-11-03T12:36:58.072Z" }, + { url = "https://files.pythonhosted.org/packages/4b/15/2527c7e46bcc7f69e792b7a0eba1e70d9effb5ae29f29d704487954975ba/coverage-7.17.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6eb807f5eb02f64eb19f39b63015de046b312b847e3919b639a0ca19ae9a10eb", size = 264857, upload-time = "2026-11-03T12:36:59.79Z" }, + { url = "https://files.pythonhosted.org/packages/b8/83/6cf7a9cc1a4188227dc74e9293dd006e6e76b97e24747452f3b17b300f30/coverage-7.17.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0bf74f2960a3d0f39067bddd658db1331a17828c93102922864699401df82dc7", size = 258994, upload-time = "2026-11-03T12:37:01.07Z" }, + { url = "https://files.pythonhosted.org/packages/98/0d/1f027bbe75df8b9553e87ad49889a288409e25bad00032391678b612c5ab/coverage-7.17.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3da4fe4cd099239c10691ff6deb9fd291e5438ad77c2583cf235a5b9aa9250ae", size = 260950, upload-time = "2026-11-03T12:37:02.553Z" }, + { url = "https://files.pythonhosted.org/packages/a4/25/5f02f89f262a126a90fda3061ea5beb0721202d9b8469b4a50adb85b2e2a/coverage-7.17.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d2c2973dcbcd883ec94305f513b7c05be6359679f25ad7134adf0cc512cf405e", size = 258871, upload-time = "2026-11-03T12:37:03.896Z" }, + { url = "https://files.pythonhosted.org/packages/50/2e/61f046a3ecddb2fc6e44350de857840e01fffbe83246302aa2382e8b9ac5/coverage-7.17.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:deea769f5199ea3e8053d5274e737e84f4a9cfbb1e69330066101825b098aac6", size = 262967, upload-time = "2026-11-03T12:37:05.191Z" }, + { url = "https://files.pythonhosted.org/packages/33/41/a45075de5bb61a646882912b7fc21cd594a83fe9be0ee6c77ffafa3a2ba7/coverage-7.17.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:7fdea302c956e1e85526a294ce64495b0029edc451b4655e2a9a85af6029fa24", size = 258721, upload-time = "2026-11-03T12:37:06.44Z" }, + { url = "https://files.pythonhosted.org/packages/89/ac/a58424f93baf37f1f36a9ad60d622d84482e26b017157e101ad87e965e28/coverage-7.17.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:32141eb9f9d228da33d45baf789736bc3c00a31ea9150aa7f48bbc08cfe2849f", size = 260799, upload-time = "2026-11-03T12:37:08.058Z" }, + { url = "https://files.pythonhosted.org/packages/27/10/9c424a67004ebeff99a03030115c4f8562eefabf0fa2ae889289efa85aa9/coverage-7.17.1-cp313-cp313-win32.whl", hash = "sha256:fc7b1ee12d7376ea85f6d09a272da3011c879e605d2f354d4a1c21c890cb571c", size = 229400, upload-time = "2026-11-03T12:37:09.337Z" }, + { url = "https://files.pythonhosted.org/packages/9e/0c/1b92a40cf24fa09e1bc2e7f9120a312e0b4a792b5f907852cbf64b75ab98/coverage-7.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:26d0fa82cd76c888058c182b8ebb6326578f89f01cc1e576dde76bba8e446261", size = 230128, upload-time = "2026-11-03T12:37:10.69Z" }, + { url = "https://files.pythonhosted.org/packages/c3/af/67fa4bc27bdd15514810ac43c1f1285ca46e9b7af29859655948574f419a/coverage-7.17.1-cp313-cp313-win_arm64.whl", hash = "sha256:ddaca2c745275aede7b7511618aba9e1e17728f772edd1d2016529def73df01d", size = 229853, upload-time = "2026-11-03T12:37:12.173Z" }, + { url = "https://files.pythonhosted.org/packages/18/61/7ad3e5f92557c73044d3dfaf88cc65fb7b1ad589c744e8629af8e6370224/coverage-7.17.1-py3-none-any.whl", hash = "sha256:fda1023828e4a9f4a40ef06783e005f3630f805f839edc679c87f597f33e041f", size = 219074, upload-time = "2026-11-03T12:38:46.229Z" }, +] + +[[package]] +name = "cycler" +version = "0.12.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a9/95/a3dbbb5028f35eafb79008e7522a75244477d2838f38cbb722248dabc2a8/cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c", size = 7615, upload-time = "2023-10-07T05:32:18.335Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", size = 8321, upload-time = "2023-10-07T05:32:16.783Z" }, +] + +[[package]] +name = "dask" +version = "2026.8.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "cloudpickle" }, + { name = "fsspec" }, + { name = "importlib-metadata", marker = "python_full_version < '3.12'" }, + { name = "packaging" }, + { name = "partd" }, + { name = "pyyaml" }, + { name = "toolz" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/33/a7/6b3c7ac32b642fbbe0821111654e0bd8cfbe88f68560bcf23cc78ab35c71/dask-2026.8.0.tar.gz", hash = "sha256:8a94c37b5de6d869343340dc26c3c3acca7ec48a3abdabe00ea3abb1125884d5", size = 11561752, upload-time = "2026-08-24T19:21:25.906Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f8/3a/4fc99e788bcfa1b3b3f21abf57da45898d807d007e7f6fd1c7300904eb70/dask-2026.8.0-py3-none-any.whl", hash = "sha256:ccc0c83a189b0398602435189771d28dad7b5773b6089bb8dce14ae732dd782c", size = 1492182, upload-time = "2026-08-24T19:21:23.997Z" }, +] + +[[package]] +name = "distlib" +version = "0.4.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/68/bc/d36966de7304047a1001a7559fbf266c464bb5d8cc85464536ef8c9bcc32/distlib-0.4.4.tar.gz", hash = "sha256:ca4175b4ac7f71223cc30ed7248c45d2b38c2439d8ee6e498ab67ed63cf3508d", size = 621304, upload-time = "2026-10-16T13:56:34.203Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c1/2b/238f02a4927fa2e27aa995371d240a468b52e42eae44a678434aff736b38/distlib-0.4.4-py2.py3-none-any.whl", hash = "sha256:cf09d55b1e85a89576b036aeb71550bfd68320f3f21761ca329eadadad918638", size = 472099, upload-time = "2026-10-16T13:56:32.053Z" }, +] + +[[package]] +name = "fasteners" +version = "0.20" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2d/18/7881a99ba5244bfc82f06017316ffe93217dbbbcfa52b887caa1d4f2a6d3/fasteners-0.20.tar.gz", hash = "sha256:55dce8792a41b56f727ba6e123fcaee77fd87e638a6863cec00007bfea84c8d8", size = 25087, upload-time = "2025-08-11T10:19:37.785Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/51/ac/e5d886f892666d2d1e5cb8c1a41146e1d79ae8896477b1153a21711d3b44/fasteners-0.20-py3-none-any.whl", hash = "sha256:9422c40d1e350e4259f509fb2e608d6bc43c0136f79a00db1b49046029d0b3b7", size = 18702, upload-time = "2025-08-11T10:19:35.716Z" }, +] + +[[package]] +name = "filelock" +version = "4.2.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e0/14/73688a22e91cfcfc6837a011ffa572e2ba3c348765f7bd482d8436fa9b6f/filelock-4.2.7.tar.gz", hash = "sha256:31f6f7e3a8b6f0bd89114d30c5d5218ea1cfb63e5c4aa695b7559314a209bc7f", size = 661450, upload-time = "2026-11-02T16:09:15.119Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1d/1a/1fcf0d3886bad15b92459a0e529dd86a1ec4d9ef30e789df48044fd3698c/filelock-4.2.7-py3-none-any.whl", hash = "sha256:d08972bbca2b8767ee552aff877b091dd111ffb1d22de7de885766ea7d719d5e", size = 164345, upload-time = "2026-11-02T16:09:13.588Z" }, +] + +[[package]] +name = "fonttools" +version = "4.68.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0c/b5/672312ae80ce8b8eb9779c56052a24c8f36652aa7293a1a420553bcf03e5/fonttools-4.68.0.tar.gz", hash = "sha256:73294e233070f71e2b3653b6bd8564af676d57f26d075761fb06d1ab2e4a7a5d", size = 3848583, upload-time = "2026-10-27T16:30:26.451Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/21/51/7c725115425be01aa0fac87eb38f8e8c8c04de80d49a99b08a74898b25ee/fonttools-4.68.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:362443ab355701d6fd26b10d022cd6c8456e1e248f80103c44cbe5ef0228f87f", size = 3135538, upload-time = "2026-10-27T16:28:02.169Z" }, + { url = "https://files.pythonhosted.org/packages/2e/d5/0a04cbde3e3e997d7bda216f77566bea2eb79018e4ca2adb13ce3c9a7206/fonttools-4.68.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:664dfa7a4869b3c827dfc2c38dbbb7a58d53e97933d1bd51ee6729792ee73d50", size = 2632853, upload-time = "2026-10-27T16:28:04.962Z" }, + { url = "https://files.pythonhosted.org/packages/d8/8c/aa4c7b38f4814d6bcb3378276bff802c9610a1227f1d24965a9f23f0a48c/fonttools-4.68.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:53f2387d7e3816763f30e08d14263be18e28ead53d4dc20a82f9280d0c99d88a", size = 5530571, upload-time = "2026-10-27T16:28:06.952Z" }, + { url = "https://files.pythonhosted.org/packages/00/01/e0f5b3d648ddd2eae35c4bcddcc2cffc059279e9d2cdb325bed10458f606/fonttools-4.68.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4b9c89b5876f1a74b1341632dfaf4c4c2be565d76c060445df401ebe7a4ceefa", size = 5493829, upload-time = "2026-10-27T16:28:09.127Z" }, + { url = "https://files.pythonhosted.org/packages/ea/bb/9c5568659275c86e6eebb060d98e8ea77c56fb78ee77ea5654a057f72bde/fonttools-4.68.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:054a4d5de7e1f973ab9789e9ae3f20238774783ca9bf7ee0359aebae4de9400c", size = 5496373, upload-time = "2026-10-27T16:28:11.378Z" }, + { url = "https://files.pythonhosted.org/packages/b0/32/0a35ffd35bfc1b0a754563a45500e87d651e0f336cf006a25e0bc460ba2f/fonttools-4.68.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b5a015b322275d3d745b06d5e69cf74ef4b1ce2dae02afbe4826cb186d8527a3", size = 5634196, upload-time = "2026-10-27T16:28:13.783Z" }, + { url = "https://files.pythonhosted.org/packages/93/76/3454e20f07dbe4b308b7bc99117292109bb7775a1b595ead768110144553/fonttools-4.68.0-cp311-cp311-win32.whl", hash = "sha256:bd5b047ed8f16b83d0344fef053668712ab9c88e2e46d8612ec41df83c5222e1", size = 1624232, upload-time = "2026-10-27T16:28:16.001Z" }, + { url = "https://files.pythonhosted.org/packages/46/87/4ba592099172764aa56e66919b0c3ddaf944c775433e806e36e165062095/fonttools-4.68.0-cp311-cp311-win_amd64.whl", hash = "sha256:35425bba603cf9d72c9f3c173ea6c98632164f3bdfb1ce1a3dcc928979c6ec6d", size = 1681821, upload-time = "2026-10-27T16:28:17.757Z" }, + { url = "https://files.pythonhosted.org/packages/e1/54/004f12709b21a56bb944fdca6d165b0d90cf9366143675dbc7bb46784ddd/fonttools-4.68.0-cp311-cp311-win_arm64.whl", hash = "sha256:04365b897c9ee29a048b6d4db74c75c38b70aa6d1074726fbdd7ea1a23aa8ed2", size = 1649151, upload-time = "2026-10-27T16:28:19.502Z" }, + { url = "https://files.pythonhosted.org/packages/15/63/f332b2099599b4911786a6f6fd69552fdd5ae6a45cfaffb0c881092f36c8/fonttools-4.68.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:f265c85432c398d86f26add143423924a4747879b2dce001a3ffe0a5fed30638", size = 3143556, upload-time = "2026-10-27T16:28:21.812Z" }, + { url = "https://files.pythonhosted.org/packages/c8/97/68816bb56843e06631ff11517e4fb35fc62d76aec6e29f11df095e7f33c5/fonttools-4.68.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d225096687fc3bac0ebe3357e95b2dd8b0a49d191f016db0771d537ccdd6f47e", size = 2633689, upload-time = "2026-10-27T16:28:23.54Z" }, + { url = "https://files.pythonhosted.org/packages/21/94/b125c7a6d154d17f4f699150ef3abebd9d8ad0f7eddce9dad649a9e5c1d8/fonttools-4.68.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b41e71b2657c9843b2327b4e3c116db1757dc8cd05d978cd47cb5061272d4997", size = 5462605, upload-time = "2026-10-27T16:28:25.774Z" }, + { url = "https://files.pythonhosted.org/packages/7f/58/fe13db71e3f5a020c6875f349806e84f7550f3f24cb3cdbabdd693968b1d/fonttools-4.68.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3edce69ecdefa73ab1041901ca22eefed6ebb93a77bc77e889d940479f25bf7d", size = 5443448, upload-time = "2026-10-27T16:28:27.898Z" }, + { url = "https://files.pythonhosted.org/packages/f1/7f/48236f8c626007670586dd6b0d61ed80323a6b17ead763c881d717525cea/fonttools-4.68.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d8d5674de2b614575f0751671727056af042ac1d61beacb59a298d74282fa492", size = 5399992, upload-time = "2026-10-27T16:28:29.99Z" }, + { url = "https://files.pythonhosted.org/packages/cb/0d/09e9a433430e277e454eace1c4d02c3691a33d5257bd234702a427a9d090/fonttools-4.68.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7ab8097e7e191784974c5608eb95fb31e1243fe3054cb66778ac82558e9b36f8", size = 5563739, upload-time = "2026-10-27T16:28:32.691Z" }, + { url = "https://files.pythonhosted.org/packages/ac/54/6708f4c6742de2d5bf4280d8bde22fb8aef5120328bdc19ca34a9d2e8e8b/fonttools-4.68.0-cp312-cp312-win32.whl", hash = "sha256:1695cd2e1f9768fe3f14d369ce6ef1b3e8561069cc9e47059571985d1ea42614", size = 2481614, upload-time = "2026-10-27T16:28:35.122Z" }, + { url = "https://files.pythonhosted.org/packages/1c/3e/ef786310a3774ae7aacaa288ca6d1e1dfd03c24214137478e4b630b76af0/fonttools-4.68.0-cp312-cp312-win_amd64.whl", hash = "sha256:fedc183bdbc3fdbfbdca26e0e04566d089cbb404712509a4e824608e9304cc77", size = 2533508, upload-time = "2026-10-27T16:28:37.085Z" }, + { url = "https://files.pythonhosted.org/packages/06/11/2cc4272ddd485c448c0363be9beb0a24d3f429f1cc8c5aafa716c508e899/fonttools-4.68.0-cp312-cp312-win_arm64.whl", hash = "sha256:95628bbc5ea81d5761268f4c4f1ba709af510b970f363498ac801c0c1fd7a4a8", size = 2502049, upload-time = "2026-10-27T16:28:39.355Z" }, + { url = "https://files.pythonhosted.org/packages/05/ae/e2d75b7f582c1cc018ab94b8bbfbad7ab501b340debfa256f48125cae3eb/fonttools-4.68.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f15de48b82a816c72f358e772792801bd3788a156caa76e1806cb0aa8148cd61", size = 3141370, upload-time = "2026-10-27T16:28:41.721Z" }, + { url = "https://files.pythonhosted.org/packages/fe/80/73d5297441b42d42b64d91cd4fffdff1541fac6171b0aca0a167c26d1c33/fonttools-4.68.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:86d319a1feedfc75c8ff582d947b21fa1854e2829cb8cf9428784d750a05f1d9", size = 2633412, upload-time = "2026-10-27T16:28:43.612Z" }, + { url = "https://files.pythonhosted.org/packages/36/29/16501aa69a586b8e8f810d7be19584aa026a0bca46c598ac516cfdd2a31a/fonttools-4.68.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:04a4868dcfcc23f00bb033147d5afc1a118a992842644c3eb9da55467ab4fd74", size = 5435968, upload-time = "2026-10-27T16:28:45.651Z" }, + { url = "https://files.pythonhosted.org/packages/79/c6/4b44abbffa01f85d6d0f0b9325a712019b4e2e6208f999ac6846d8da1346/fonttools-4.68.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:08851f7d2b983412f490b73a7c56f17cbda1d4d85d2ad3f857564bf15b5d9475", size = 5418193, upload-time = "2026-10-27T16:28:49.238Z" }, + { url = "https://files.pythonhosted.org/packages/98/0d/15cffdf13e156bb75ee421eee319fa8c5fb91a48a9a3e84685bf786df5be/fonttools-4.68.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b6f7c5fabf5b1d9ca79f204664f2c3e2f8218e50aebc43e85e02df0b2d9cc313", size = 5378957, upload-time = "2026-10-27T16:28:51.889Z" }, + { url = "https://files.pythonhosted.org/packages/9f/18/174052f12611c1088b61eeaa4019a8f1aedbcb1e985c33ef8fbe0c1ba674/fonttools-4.68.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e3ecdb0b248cd0bf43100a8b3cb40f7b45d52ea4be8616edea5dea8fefe90229", size = 5536796, upload-time = "2026-10-27T16:28:54.445Z" }, + { url = "https://files.pythonhosted.org/packages/35/d6/892213f45f63565f3f7eca3bce484a6f52ab7f7eda61851f93b1be829d5e/fonttools-4.68.0-cp313-cp313-win32.whl", hash = "sha256:4da99b83ae17f531838bc750abf56164ff37e1543e8e9b2b5403548f50497ce0", size = 2479972, upload-time = "2026-10-27T16:28:56.749Z" }, + { url = "https://files.pythonhosted.org/packages/61/2b/6c6f578176edeec83c5e02b7e745c1c8029f8cec4e32b4898d2c573bb881/fonttools-4.68.0-cp313-cp313-win_amd64.whl", hash = "sha256:6ed796b220654b4d37ad20af73419ec7969b669dd678cab88afb602c5683a331", size = 2531658, upload-time = "2026-10-27T16:28:59.013Z" }, + { url = "https://files.pythonhosted.org/packages/3c/b9/7cc71534bc954346451003bf5c9c117af835aea681a13a2847222ccf19b2/fonttools-4.68.0-cp313-cp313-win_arm64.whl", hash = "sha256:cd4b0d00523cae1e5e1eaed1d2b85ef342cec000f58d0738e12b064d72a7bbc2", size = 2499695, upload-time = "2026-10-27T16:29:01.511Z" }, + { url = "https://files.pythonhosted.org/packages/35/74/4ea331ecc65ba05a58b1e4e8ccd604c8a15565f876dfbf93fc2a2ea2e22b/fonttools-4.68.0-py3-none-any.whl", hash = "sha256:d76f8fdcb6936f5e1045a0c9dd721a4bf11112e60d1e243bfa1d39f06a8b3104", size = 1248357, upload-time = "2026-10-27T16:30:24.389Z" }, +] + +[[package]] +name = "fsspec" +version = "2026.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/77/cd/9be253869fc42e764de7f3dedd6969af7d44ff9c3375214a3442a6f3fc08/fsspec-2026.9.0.tar.gz", hash = "sha256:0f08147951c8cb31d844c3547d631053b127863b60be04cf06e121333ee0e2fe", size = 333545, upload-time = "2026-09-18T17:50:42.825Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6c/c0/a98505f18594f1bce828bb159cec0fcf9860562f1a2c85913409fc8f3d9e/fsspec-2026.9.0-py3-none-any.whl", hash = "sha256:8dd6e646e99ea382bd85f97a45e6b526a442d79423a7dc673f1e2756d05fcb5f", size = 221738, upload-time = "2026-09-18T17:50:41.341Z" }, +] + +[[package]] +name = "geotiff" +version = "0.2.10" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "numpy", version = "2.5.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "pyproj", version = "3.7.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "pyproj", version = "3.8.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "tifffile", version = "2026.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "tifffile", version = "2026.10.15", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "zarr" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ba/6e/00d51cd7ade2fd0e960722f090cd3bad0dbbc7206baf2871448a903e40f3/geotiff-0.2.10.tar.gz", hash = "sha256:36da356d3e2f6e3719bad32212283fe99049373391c7f576c2d6022642ad88d6", size = 2242539, upload-time = "2023-08-23T00:47:27.791Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/42/45/1f3fd283de696f421f99dc04d23251dfd10204c098684c3267935118b1cc/geotiff-0.2.10-py3-none-any.whl", hash = "sha256:1c238b1115884a82b0e8b462384828b1bfe2819194f9f16384f517d07ab12e68", size = 19900, upload-time = "2023-08-23T00:47:20.415Z" }, +] + +[[package]] +name = "h5py" +version = "3.16.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "numpy", version = "2.5.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/db/33/acd0ce6863b6c0d7735007df01815403f5589a21ff8c2e1ee2587a38f548/h5py-3.16.0.tar.gz", hash = "sha256:a0dbaad796840ccaa67a4c144a0d0c8080073c34c76d5a6941d6818678ef2738", size = 446526, upload-time = "2026-03-06T13:49:08.07Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ba/95/a825894f3e45cbac7554c4e97314ce886b233a20033787eda755ca8fecc7/h5py-3.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:719439d14b83f74eeb080e9650a6c7aa6d0d9ea0ca7f804347b05fac6fbf18af", size = 3721663, upload-time = "2026-03-06T13:47:49.599Z" }, + { url = "https://files.pythonhosted.org/packages/bf/3b/38ff88b347c3e346cda1d3fc1b65a7aa75d40632228d8b8a5d7b58508c24/h5py-3.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c3f0a0e136f2e95dd0b67146abb6668af4f1a69c81ef8651a2d316e8e01de447", size = 3087630, upload-time = "2026-03-06T13:47:51.249Z" }, + { url = "https://files.pythonhosted.org/packages/98/a8/2594cef906aee761601eff842c7dc598bea2b394a3e1c00966832b8eeb7c/h5py-3.16.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:a6fbc5367d4046801f9b7db9191b31895f22f1c6df1f9987d667854cac493538", size = 4823472, upload-time = "2026-03-06T13:47:53.085Z" }, + { url = "https://files.pythonhosted.org/packages/52/a0/c1f604538ff6db22a0690be2dc44ab59178e115f63c917794e529356ab23/h5py-3.16.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:fb1720028d99040792bb2fb31facb8da44a6f29df7697e0b84f0d79aff2e9bd3", size = 5027150, upload-time = "2026-03-06T13:47:55.043Z" }, + { url = "https://files.pythonhosted.org/packages/2e/fd/301739083c2fc4fd89950f9bcfce75d6e14b40b0ca3d40e48a8993d1722c/h5py-3.16.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:314b6054fe0b1051c2b0cb2df5cbdab15622fb05e80f202e3b6a5eee0d6fe365", size = 4814544, upload-time = "2026-03-06T13:47:56.893Z" }, + { url = "https://files.pythonhosted.org/packages/4c/42/2193ed41ccee78baba8fcc0cff2c925b8b9ee3793305b23e1f22c20bf4c7/h5py-3.16.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ffbab2fedd6581f6aa31cf1639ca2cb86e02779de525667892ebf4cc9fd26434", size = 5034013, upload-time = "2026-03-06T13:47:59.01Z" }, + { url = "https://files.pythonhosted.org/packages/f7/20/e6c0ff62ca2ad1a396a34f4380bafccaaf8791ff8fccf3d995a1fc12d417/h5py-3.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:17d1f1630f92ad74494a9a7392ab25982ce2b469fc62da6074c0ce48366a2999", size = 3191673, upload-time = "2026-03-06T13:48:00.626Z" }, + { url = "https://files.pythonhosted.org/packages/f2/48/239cbe352ac4f2b8243a8e620fa1a2034635f633731493a7ff1ed71e8658/h5py-3.16.0-cp311-cp311-win_arm64.whl", hash = "sha256:85b9c49dd58dc44cf70af944784e2c2038b6f799665d0dcbbc812a26e0faa859", size = 2673834, upload-time = "2026-03-06T13:48:02.579Z" }, + { url = "https://files.pythonhosted.org/packages/c8/c0/5d4119dba94093bbafede500d3defd2f5eab7897732998c04b54021e530b/h5py-3.16.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c5313566f4643121a78503a473f0fb1e6dcc541d5115c44f05e037609c565c4d", size = 3685604, upload-time = "2026-03-06T13:48:04.198Z" }, + { url = "https://files.pythonhosted.org/packages/b0/42/c84efcc1d4caebafb1ecd8be4643f39c85c47a80fe254d92b8b43b1eadaf/h5py-3.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:42b012933a83e1a558c673176676a10ce2fd3759976a0fedee1e672d1e04fc9d", size = 3061940, upload-time = "2026-03-06T13:48:05.783Z" }, + { url = "https://files.pythonhosted.org/packages/89/84/06281c82d4d1686fde1ac6b0f307c50918f1c0151062445ab3b6fa5a921d/h5py-3.16.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:ff24039e2573297787c3063df64b60aab0591980ac898329a08b0320e0cf2527", size = 5198852, upload-time = "2026-03-06T13:48:07.482Z" }, + { url = "https://files.pythonhosted.org/packages/9e/e9/1a19e42cd43cc1365e127db6aae85e1c671da1d9a5d746f4d34a50edb577/h5py-3.16.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:dfc21898ff025f1e8e67e194965a95a8d4754f452f83454538f98f8a3fcb207e", size = 5405250, upload-time = "2026-03-06T13:48:09.628Z" }, + { url = "https://files.pythonhosted.org/packages/b7/8e/9790c1655eabeb85b92b1ecab7d7e62a2069e53baefd58c98f0909c7a948/h5py-3.16.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:698dd69291272642ffda44a0ecd6cd3bda5faf9621452d255f57ce91487b9794", size = 5190108, upload-time = "2026-03-06T13:48:11.26Z" }, + { url = "https://files.pythonhosted.org/packages/51/d7/ab693274f1bd7e8c5f9fdd6c7003a88d59bedeaf8752716a55f532924fbb/h5py-3.16.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2b2c02b0a160faed5fb33f1ba8a264a37ee240b22e049ecc827345d0d9043074", size = 5419216, upload-time = "2026-03-06T13:48:13.322Z" }, + { url = "https://files.pythonhosted.org/packages/03/c1/0976b235cf29ead553e22f2fb6385a8252b533715e00d0ae52ed7b900582/h5py-3.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:96b422019a1c8975c2d5dadcf61d4ba6f01c31f92bbde6e4649607885fe502d6", size = 3182868, upload-time = "2026-03-06T13:48:15.759Z" }, + { url = "https://files.pythonhosted.org/packages/14/d9/866b7e570b39070f92d47b0ff1800f0f8239b6f9e45f02363d7112336c1f/h5py-3.16.0-cp312-cp312-win_arm64.whl", hash = "sha256:39c2838fb1e8d97bcf1755e60ad1f3dd76a7b2a475928dc321672752678b96db", size = 2653286, upload-time = "2026-03-06T13:48:17.279Z" }, + { url = "https://files.pythonhosted.org/packages/0f/9e/6142ebfda0cb6e9349c091eae73c2e01a770b7659255248d637bec54a88b/h5py-3.16.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:370a845f432c2c9619db8eed334d1e610c6015796122b0e57aa46312c22617d9", size = 3671808, upload-time = "2026-03-06T13:48:19.737Z" }, + { url = "https://files.pythonhosted.org/packages/b0/65/5e088a45d0f43cd814bc5bec521c051d42005a472e804b1a36c48dada09b/h5py-3.16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:42108e93326c50c2810025aade9eac9d6827524cdccc7d4b75a546e5ab308edb", size = 3045837, upload-time = "2026-03-06T13:48:21.854Z" }, + { url = "https://files.pythonhosted.org/packages/da/1e/6172269e18cc5a484e2913ced33339aad588e02ba407fafd00d369e22ef3/h5py-3.16.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:099f2525c9dcf28de366970a5fb34879aab20491589fa89ce2863a84218bb524", size = 5193860, upload-time = "2026-03-06T13:48:24.071Z" }, + { url = "https://files.pythonhosted.org/packages/bd/98/ef2b6fe2903e377cbe870c3b2800d62552f1e3dbe81ce49e1923c53d1c5c/h5py-3.16.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:9300ad32dea9dfc5171f94d5f6948e159ed93e4701280b0f508773b3f582f402", size = 5400417, upload-time = "2026-03-06T13:48:25.728Z" }, + { url = "https://files.pythonhosted.org/packages/bc/81/5b62d760039eed64348c98129d17061fdfc7839fc9c04eaaad6dee1004e4/h5py-3.16.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:171038f23bccddfc23f344cadabdfc9917ff554db6a0d417180d2747fe4c75a7", size = 5185214, upload-time = "2026-03-06T13:48:27.436Z" }, + { url = "https://files.pythonhosted.org/packages/28/c4/532123bcd9080e250696779c927f2cb906c8bf3447df98f5ceb8dcded539/h5py-3.16.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7e420b539fb6023a259a1b14d4c9f6df8cf50d7268f48e161169987a57b737ff", size = 5414598, upload-time = "2026-03-06T13:48:29.49Z" }, + { url = "https://files.pythonhosted.org/packages/c3/d9/a27997f84341fc0dfcdd1fe4179b6ba6c32a7aa880fdb8c514d4dad6fba3/h5py-3.16.0-cp313-cp313-win_amd64.whl", hash = "sha256:18f2bbcd545e6991412253b98727374c356d67caa920e68dc79eab36bf5fedad", size = 3175509, upload-time = "2026-03-06T13:48:31.131Z" }, + { url = "https://files.pythonhosted.org/packages/a5/23/bb8647521d4fd770c30a76cfc6cb6a2f5495868904054e92f2394c5a78ff/h5py-3.16.0-cp313-cp313-win_arm64.whl", hash = "sha256:656f00e4d903199a1d58df06b711cf3ca632b874b4207b7dbec86185b5c8c7d4", size = 2647362, upload-time = "2026-03-06T13:48:33.411Z" }, +] + +[[package]] +name = "identify" +version = "2.6.20" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/53/35/d70c0006c7cee65999ea94a6273e60b2094f600a3d8b71b04318253fc643/identify-2.6.20.tar.gz", hash = "sha256:ad729860a923858d26917c2f4fb0a1d83d27a75b1e090c06440c573f048f3285", size = 99681, upload-time = "2026-09-26T20:29:07.187Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fa/70/fffc9613501877c0a10a1ef73a165e6ded3e53f6e7502227fef56d973933/identify-2.6.20-py2.py3-none-any.whl", hash = "sha256:6a16b69b93187244e0548cbfd25b3e4a6f9a7a2ad784625c3bec2b8d27b81aaa", size = 99506, upload-time = "2026-09-26T20:29:06.054Z" }, +] + +[[package]] +name = "idna" +version = "3.20" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f5/08/8eea9d4b8302028f3abb2c0813953f7aec26d33b7a8960ed760e65ff29fa/idna-3.20.tar.gz", hash = "sha256:a7db850025b95ded1eae8a46181a1a6c56c92c96f0e2b005d9ff8dc0210cab44", size = 216463, upload-time = "2026-09-17T14:11:04.752Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/58/a2/bb081bab032533a855d44de1d56f8e8426114ff1ba5d1f07a438a0a654f8/idna-3.20-py3-none-any.whl", hash = "sha256:ab7ae7122974553370f0bdb919e1a960b2cd1bc1ef0276416d896db81c14582c", size = 69583, upload-time = "2026-09-17T14:11:03.168Z" }, +] + +[[package]] +name = "imageio" +version = "2.38.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "numpy", version = "2.5.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "pillow" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f3/cd/69e4ac55b6dafdd2b5f32075236841a3945dea7323d0232d80f28c37cea8/imageio-2.38.1.tar.gz", hash = "sha256:6769f1f01c4dd46448307863a787c9a22fa4dbe11c0c88f525c6e410fdfd7983", size = 403667, upload-time = "2026-10-08T14:35:32.961Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7d/8a/3b7f62b9df56460959f1b9bef094248d2a4d7f393c5b0d2da8efcdf0e40e/imageio-2.38.1-py3-none-any.whl", hash = "sha256:36d23eb7423d2fb63f637098758edb3d2df3687f7125e0a5ce8596572022dcf9", size = 328389, upload-time = "2026-10-08T14:35:31.075Z" }, +] + +[[package]] +name = "importlib-metadata" +version = "10.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "zipp" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/98/f6/4eda8458eb00ec03e715c376c6b23b3def69687924c68d36d1c4003d3bc4/importlib_metadata-10.1.0.tar.gz", hash = "sha256:82b04047fa09792c1a8cfeddfe08bec79e4b13c29c8469b0d3949860f61017e8", size = 67761, upload-time = "2026-10-24T00:01:44.856Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/65/b6/495438ad95e0102aff8f6786b5474202f38cd5d4700d03a72b77977dec10/importlib_metadata-10.1.0-py3-none-any.whl", hash = "sha256:cf45ce66a929c64e994185a809c04840d0501db17e663810d993d0f4829265c7", size = 32221, upload-time = "2026-10-24T00:01:43.507Z" }, +] + +[[package]] +name = "iniconfig" +version = "2.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/01/e1/2069291243c926a2ff1cd706c7f3eeb9b62144bf60f77c9fb9ff2fb26bd3/iniconfig-2.3.1.tar.gz", hash = "sha256:67f4b9c50da0dedf52af349e7749a80a9057a5031199791b906c3bb3ae878960", size = 21209, upload-time = "2026-10-06T22:48:38.076Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/56/43/4ca9e49d27a1fcf6bece6f6aec0ea46bb9112489b93d4b688fb415457bdb/iniconfig-2.3.1-py3-none-any.whl", hash = "sha256:9121e2c1fdb355232495be3194c8dfe87ccc2d5dee45947b78e68f499790d7a7", size = 7552, upload-time = "2026-10-06T22:48:36.959Z" }, +] + +[[package]] +name = "jinja2" +version = "3.1.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, +] + +[[package]] +name = "joblib" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cloudpickle" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d5/1d/537ab090f302b838943a1b56497dd53059b9a9b46a074936470173a2e207/joblib-1.6.0.tar.gz", hash = "sha256:2ccc96785b12046c08fd6d55839c12857831b54a3c1673ffadd2f04bfc4eda03", size = 327903, upload-time = "2026-08-31T09:39:04.122Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/53/84099323c2ec4be98d935f63c033ac4151ee83836ca1050ede3b3aadf155/joblib-1.6.0-py3-none-any.whl", hash = "sha256:3dbbf9f6e4b592a2357b854608e980fe6390d131d7a82f011a377ef2ebef7aba", size = 306115, upload-time = "2026-08-31T09:39:02.298Z" }, +] + +[[package]] +name = "jsmin" +version = "3.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5e/73/e01e4c5e11ad0494f4407a3f623ad4d87714909f50b17a06ed121034ff6e/jsmin-3.0.1.tar.gz", hash = "sha256:c0959a121ef94542e807a674142606f7e90214a2b3d1eb17300244bbb5cc2bfc", size = 13925, upload-time = "2022-01-16T20:35:59.13Z" } + +[[package]] +name = "jsonschema" +version = "4.26.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "jsonschema-specifications" }, + { name = "referencing" }, + { name = "rpds-py" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b3/fc/e067678238fa451312d4c62bf6e6cf5ec56375422aee02f9cb5f909b3047/jsonschema-4.26.0.tar.gz", hash = "sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326", size = 366583, upload-time = "2026-01-07T13:41:07.246Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl", hash = "sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce", size = 90630, upload-time = "2026-01-07T13:41:05.306Z" }, +] + +[[package]] +name = "jsonschema-specifications" +version = "2025.9.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "referencing" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855, upload-time = "2025-09-08T01:34:59.186Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437, upload-time = "2025-09-08T01:34:57.871Z" }, +] + +[[package]] +name = "kiwisolver" +version = "1.5.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/07/bd78e6a8fae171ea041ef5bba3ed21a003522fa088834b069b1909981f30/kiwisolver-1.5.1.tar.gz", hash = "sha256:f1303ef2eec81262a4b708c3e858afe58d7c75ad91c1c05266eda7673369859a", size = 104395, upload-time = "2026-08-28T10:28:27.153Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/94/7b/2de6908edc668427c149af5f93112e931f87e1fa4cab80bac32c5844dccc/kiwisolver-1.5.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b3d78f7bb2b9d9a30345be1474b9aaa8685430b54afb51ba3639b5c6c11e9ed6", size = 123364, upload-time = "2026-08-28T10:25:04.359Z" }, + { url = "https://files.pythonhosted.org/packages/8a/24/e70914415c77c97be7e22c80a0740869cb7428768cc380fdcdf6703e7084/kiwisolver-1.5.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5025e36fb4fb275cef0a4e30dbb11cb4ae61d1c83deb90189cb5d7e4cafd6b55", size = 66558, upload-time = "2026-08-28T10:25:05.506Z" }, + { url = "https://files.pythonhosted.org/packages/e8/2b/8b08b11833db4d475b8ef1f36174f8d8a7abd31bedd7e794be78e8814b48/kiwisolver-1.5.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dc1a26b8e53395a01c2c611e58602fa47461f136fba7cd5542e6db6d64be1839", size = 64071, upload-time = "2026-08-28T10:25:06.7Z" }, + { url = "https://files.pythonhosted.org/packages/89/00/05c2d0369ac322d22d5c05f84b5c4a6856fa6207fbae42869108a28f0383/kiwisolver-1.5.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:95a02752aa032eef4aed01cda6d9b687c669bd0396bf4519eef8bba22a286720", size = 1438206, upload-time = "2026-08-28T10:25:08.254Z" }, + { url = "https://files.pythonhosted.org/packages/c0/05/c941a139f27438c1910d630fdc3ccfdab7c8407c72052299ead12ece086e/kiwisolver-1.5.1-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:719a35fa1156db3640555f95ebb94f60a444e64d1c69626b0edef5df78eba225", size = 1248975, upload-time = "2026-08-28T10:25:10.053Z" }, + { url = "https://files.pythonhosted.org/packages/58/a1/2669ee5512e39b9d4de25faacaedf788c957f93730c5f7c63993ec4f5933/kiwisolver-1.5.1-cp311-cp311-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:febcce10f2bcdbb80b4ea919238a6a4ac13dbc4c7cadbe8d5d75c3682f8b5404", size = 1266301, upload-time = "2026-08-28T10:25:11.754Z" }, + { url = "https://files.pythonhosted.org/packages/28/b8/353f52f2c7f861a9e90cd2e8f90f85b3ad03060835f823e08298d094c463/kiwisolver-1.5.1-cp311-cp311-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:1d852545c4d0e35a72728d072cbaa59e2fa7dd84bdf01e068d670dd0ceb58eb6", size = 1319708, upload-time = "2026-08-28T10:25:13.559Z" }, + { url = "https://files.pythonhosted.org/packages/21/0e/14b83200eadc2c1d63b76bac01c1813bf072aecf567429f303e00b70258e/kiwisolver-1.5.1-cp311-cp311-manylinux_2_39_riscv64.whl", hash = "sha256:2e10ae1bba1899188b33557c10d73affcc12033edd18adddb57d209039976a4c", size = 971720, upload-time = "2026-08-28T10:25:14.934Z" }, + { url = "https://files.pythonhosted.org/packages/86/91/9d43d84d23b1cbff72a142d387ead1ea03db0cba8ff86ed5335addad3cc9/kiwisolver-1.5.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b69602970994a2ed8bbfa78c2f0394a7435226c6040489702d9f0a0ad0c07052", size = 2200119, upload-time = "2026-08-28T10:25:16.636Z" }, + { url = "https://files.pythonhosted.org/packages/ff/7c/f2bd9616f27ffb5e17cecc0baa5d0bbcee7e55aeddc0ccc871d69e2fc3ee/kiwisolver-1.5.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:d50de98e8d807dc31822fff96f50293163a62418eb65487a21b42713d72ed0b7", size = 2295005, upload-time = "2026-08-28T10:25:18.374Z" }, + { url = "https://files.pythonhosted.org/packages/ba/17/ee671b72bf8f46a08379d4392c65582541759a542428197562f2898294ad/kiwisolver-1.5.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:3221f78211074f561c44ca42eac0619828171bec15a2c4cf6f7747d07df76e8e", size = 1960982, upload-time = "2026-08-28T10:25:19.893Z" }, + { url = "https://files.pythonhosted.org/packages/2e/f7/0e26b4c05bee3bdb0f048dfa305e4fe701999ea17b51e9c616ef91035bbe/kiwisolver-1.5.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0ba9527afc80ae3d7814ed98b6572d02bf85eaf48065678342c5f0c6dab7a8c7", size = 2464918, upload-time = "2026-08-28T10:25:21.65Z" }, + { url = "https://files.pythonhosted.org/packages/ae/62/6eb431133d30ce656ac1e5ff72fac70dd34d54c3984f4011b9ac8bf77d54/kiwisolver-1.5.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e12dfea7f5fc2a34a9080efbf79c4c44eb380ec5b9c6fea09407e08f0d1e941d", size = 2270967, upload-time = "2026-08-28T10:25:23.643Z" }, + { url = "https://files.pythonhosted.org/packages/bf/9f/6f9e489c188200e6fb3193935501894811e8c97577c8ffe9033589bf3521/kiwisolver-1.5.1-cp311-cp311-win_amd64.whl", hash = "sha256:1a7587dc335f2c0f5bd577fd0540bd16c66006bdb60f759a1059f025e6c4f071", size = 70744, upload-time = "2026-08-28T10:25:25.061Z" }, + { url = "https://files.pythonhosted.org/packages/c6/6d/dfc430d1d43957061599adea3f08ea982bb6f4ab601a8c974bedcf2ba850/kiwisolver-1.5.1-cp311-cp311-win_arm64.whl", hash = "sha256:e4e4523d6f336708d732516e6cfca7796cf3d96c9474eb5aecf6165f2f1fefc3", size = 68404, upload-time = "2026-08-28T10:25:26.186Z" }, + { url = "https://files.pythonhosted.org/packages/6b/9b/65b302742389c6f96f2956bef5decf26011309feb2fc5d79613af18adea4/kiwisolver-1.5.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:63fb7294b768f444eb4b068965f2662f28c2fd4161e23bd60fcf3ff27b74c046", size = 123876, upload-time = "2026-08-28T10:25:27.44Z" }, + { url = "https://files.pythonhosted.org/packages/71/74/c21f339956f6f691b2ed7e31d5f3ae767304df6c460192739fc830853051/kiwisolver-1.5.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0ebdef3eae5336568147c39a55be6a2036ffde53faa9ca2d978989ae7c2da12c", size = 66487, upload-time = "2026-08-28T10:25:28.728Z" }, + { url = "https://files.pythonhosted.org/packages/84/e5/bdb34e21523e01dceda064d63713f3bdec91388af24fba1eca7ea5e85864/kiwisolver-1.5.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1798e83840c3f627246104c4d8a9639c60fa068adf9ce92b61791781fa8a68c1", size = 64660, upload-time = "2026-08-28T10:25:30.071Z" }, + { url = "https://files.pythonhosted.org/packages/fc/f4/dadfec469313c7f428efa7e84b4aba9732f813c13ea7131a24b7b008ef57/kiwisolver-1.5.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:34633ecf50d16187ab8e5528b7a2530f2feb4e23f300db4672538b51cfc5cd38", size = 1477929, upload-time = "2026-08-28T10:25:31.495Z" }, + { url = "https://files.pythonhosted.org/packages/6f/35/09c58daac34e6f6ea5c6dee0094b422118e5a7c265586008a95fd135ac5f/kiwisolver-1.5.1-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d27c2123977cb9269c30a49ba45f03a4323017ef693e19db4ec9dbe1299a3002", size = 1278499, upload-time = "2026-08-28T10:25:33.375Z" }, + { url = "https://files.pythonhosted.org/packages/19/32/739765e24fbad29d13f83e546ea4abc215a78cea9d677ca09025b027724d/kiwisolver-1.5.1-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6a797a1cefc8b9c93170db580337e1fe3d011ad18b1299943231279406342048", size = 1296677, upload-time = "2026-08-28T10:25:35.059Z" }, + { url = "https://files.pythonhosted.org/packages/df/32/03304d1010e2cc45e5b3b52cef7e43fed3a2a5cd6c87a89b4a88e1d85b5d/kiwisolver-1.5.1-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2551cf9917af48ee7c4b29cc82320489508cf96fd26a51f6fc124de661cd44c7", size = 1346037, upload-time = "2026-08-28T10:25:36.705Z" }, + { url = "https://files.pythonhosted.org/packages/3e/57/4c49377bfd274450dd72ecaa13eaac32ea804a03363e4d1db0c5aa999ceb/kiwisolver-1.5.1-cp312-cp312-manylinux_2_39_riscv64.whl", hash = "sha256:38f6e0deb4d0a4615efe0c4efc5990b06ae450ab50a0b321c0b078b6d238c083", size = 988248, upload-time = "2026-08-28T10:25:38.299Z" }, + { url = "https://files.pythonhosted.org/packages/cb/c3/38df144a08b6c5d75ca4504e5cc3141bb3bfef64c04f4ef48204f42711b6/kiwisolver-1.5.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bfd1de989b3330420e29de39352f5c049905c9e3ee67233a50d550e3d652c148", size = 2228722, upload-time = "2026-08-28T10:25:40.038Z" }, + { url = "https://files.pythonhosted.org/packages/e7/11/3221838a89cd64d9b386353e000cd8a296069a20fbe3584507fdfd5bebae/kiwisolver-1.5.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1209042a623ddfda5497e4066c7b77651dde8e1d3a9dd97599dc7e97f3b9b78c", size = 2325216, upload-time = "2026-08-28T10:25:41.699Z" }, + { url = "https://files.pythonhosted.org/packages/83/d4/075c219230697bb5db910d37262b9bacf880f92b4811a02ab81ed073a253/kiwisolver-1.5.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:26e8268480be5061d509e29669d59103c067a26377a56491630ece11762e3858", size = 1977689, upload-time = "2026-08-28T10:25:43.559Z" }, + { url = "https://files.pythonhosted.org/packages/bb/08/1d219c3c2dd960983d0d4da623d916e9de6385df2b0bab3d1af0e9b8fccc/kiwisolver-1.5.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:d79308fa689fac89cbcfbd4dbfc80b5f95c54c5a7fd4d194be221f9d33d026e6", size = 2491443, upload-time = "2026-08-28T10:25:45.242Z" }, + { url = "https://files.pythonhosted.org/packages/ba/d3/024208ec1079d273f1047468d1bdffbf38bb75b7b268090fd3a0301b9d9a/kiwisolver-1.5.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b03af77d77e50edba2030fd5f7c352ff209314b09030a3cba7c14edf9a09a444", size = 2295200, upload-time = "2026-08-28T10:25:46.984Z" }, + { url = "https://files.pythonhosted.org/packages/6e/7c/7b210498f9f92e1cd7855f260fa69ef056881087b199ee20c208f0e4189a/kiwisolver-1.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:06a6917674de9e0fe3f66f5430787f59a9f2ddb64af9b714eaec547e29ef5c19", size = 70748, upload-time = "2026-08-28T10:25:48.444Z" }, + { url = "https://files.pythonhosted.org/packages/94/61/ef0daa157c8bb23672f7423e0d14c39db1dc6ef8ed47e6bc54c9c1bef3bf/kiwisolver-1.5.1-cp312-cp312-win_arm64.whl", hash = "sha256:ad8b9671348d7c8716715652ae11f85ed0eb99e265a2df2ca490577d69860b2c", size = 68324, upload-time = "2026-08-28T10:25:49.81Z" }, + { url = "https://files.pythonhosted.org/packages/08/c1/88018321d976f53c421e379c43bc6993e70ce0c8a3ec5edc4bfe102257f6/kiwisolver-1.5.1-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:b6ae6a0328f0bc035741820fdeecdcd67bf4694eee03972e843663107122f450", size = 62272, upload-time = "2026-08-28T10:25:51.02Z" }, + { url = "https://files.pythonhosted.org/packages/85/d2/712bc17ea4f1d216034928069d612defbc6c95a471c55a7203a39faecb1a/kiwisolver-1.5.1-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:886fc26012f0e8b5f69d1cfe6d711f6b11f194621539bf8e6bb1c25c5dc82724", size = 64481, upload-time = "2026-08-28T10:25:52.22Z" }, + { url = "https://files.pythonhosted.org/packages/1b/e6/6c5380d676f43b6d918033962ea5e72360ca69e5a404154bc496b598ffdb/kiwisolver-1.5.1-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:aefe930d113798330e9462f7874542977869c0613cba3262e2de3a8d5dee8f3a", size = 66260, upload-time = "2026-08-28T10:25:53.387Z" }, + { url = "https://files.pythonhosted.org/packages/92/6a/7087f5822cc8bb272679641404b1966a42504dac9ee74e2b33840475a0aa/kiwisolver-1.5.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a5ca5aebae78a0bc13c1943af4af615d4966c5b650b05d5aa83b50e427196fee", size = 123876, upload-time = "2026-08-28T10:25:54.644Z" }, + { url = "https://files.pythonhosted.org/packages/5c/4b/9f385087ca09ee5ab9c09c6832561a7d2f7c78d3e5661d511e669f70e439/kiwisolver-1.5.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1ed0f5e49d0ceff8b72190824d9e59c062fbbc02c231b853112c78474b3f5ec2", size = 66487, upload-time = "2026-08-28T10:25:55.899Z" }, + { url = "https://files.pythonhosted.org/packages/47/8b/40d33ffd2f378094ed462e9a9a0907e59d4de9845e65a59561272da350d4/kiwisolver-1.5.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:77a4c8187a5948d7f8795adb765a3c7b553d07d86d88e43038fc32fc1fb9a3f3", size = 64673, upload-time = "2026-08-28T10:25:57.048Z" }, + { url = "https://files.pythonhosted.org/packages/ab/43/86aacc027959108b4c66eeae8b73cedb057dfa6eb3a335d05ad65197081c/kiwisolver-1.5.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:74ad5c3dad54a4641b4c28cd15ded70899d04459c6c7aeacafea716be97cce6d", size = 1477992, upload-time = "2026-08-28T10:25:58.479Z" }, + { url = "https://files.pythonhosted.org/packages/ed/40/b1d0369048c79733a32c8abb0f2718532e6630641368e33a81384246e844/kiwisolver-1.5.1-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:21e46b23a2da695c364124817bc01d970effd5483147f8d66a6a7167e3f6b851", size = 1278821, upload-time = "2026-08-28T10:26:00.121Z" }, + { url = "https://files.pythonhosted.org/packages/61/a1/fa71c1792272ff9461432715ae60ffb7e11a4d7ac3bf68961b9cab6c60cf/kiwisolver-1.5.1-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:75d9b1cf8258462dbdc1eeda718c96ea7f079324c09067f6daabfcf37712b7fe", size = 1296805, upload-time = "2026-08-28T10:26:01.868Z" }, + { url = "https://files.pythonhosted.org/packages/a3/49/3f0bd94af8e06ecc47eb834b195a06f05d48711ceb2352c56d6835160f0e/kiwisolver-1.5.1-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8fca690b00c4c48f6c2a547b0160ed511357093a4e4c9b47e0fadf3128066d89", size = 1346109, upload-time = "2026-08-28T10:26:03.59Z" }, + { url = "https://files.pythonhosted.org/packages/a4/44/3afe6ef9cf06d61220953a8963e94eca978491be1d9547cb01d82a1efa08/kiwisolver-1.5.1-cp313-cp313-manylinux_2_39_riscv64.whl", hash = "sha256:876bbfd276473d3daffe30e8c975df4ed9429967b41a6cb362dbb5155b6f13ad", size = 988252, upload-time = "2026-08-28T10:26:05.306Z" }, + { url = "https://files.pythonhosted.org/packages/e4/30/a12bd7a7285a211e1747c3eec77b8c614dbfcc1dad942f7611a1a6921ae5/kiwisolver-1.5.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f942903fde7363d1d879057ec5de01310efda2597161784d752fa9953a01a71a", size = 2228846, upload-time = "2026-08-28T10:26:07.312Z" }, + { url = "https://files.pythonhosted.org/packages/a1/6b/233e2958abf0dab7b18d07e52f286e02e519d7651bfbbe97af9347564109/kiwisolver-1.5.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c90d3022d8a94778939cda8638c6c8da8fa757b8958dad7ec868ce29c87681b8", size = 2325583, upload-time = "2026-08-28T10:26:09.093Z" }, + { url = "https://files.pythonhosted.org/packages/42/8e/7673060a27b01405b580058510adef34687069d229800239f5e44682d4d0/kiwisolver-1.5.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:8a34616dc2521cc8dc1d7d081734da63539f021ac0450ce950908340c6e7aa2f", size = 1978221, upload-time = "2026-08-28T10:26:11.127Z" }, + { url = "https://files.pythonhosted.org/packages/3f/7b/1b882fc1a8b4a0bb8084e7d1d85004116d08c92c0705d31f2928dec607f2/kiwisolver-1.5.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:8bf4df63592c2a66b4f8edc5df2544998c288aa02f96ce0acd880cd1de8c8127", size = 2491819, upload-time = "2026-08-28T10:26:13.348Z" }, + { url = "https://files.pythonhosted.org/packages/39/9c/426deb49e62c5f69464b64bbeca064d3b758a7506b8913d986ef34f4619c/kiwisolver-1.5.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d09037ca068d784ebc4aec290ef952ca27ac15dd9c0b5801a88c6e1096b83e6b", size = 2295520, upload-time = "2026-08-28T10:26:15.042Z" }, + { url = "https://files.pythonhosted.org/packages/f5/22/deabbb3ad6d918d74b7831b2d8ae7151b09d21c87974e5ee8a456f58c94c/kiwisolver-1.5.1-cp313-cp313-win_amd64.whl", hash = "sha256:dc23390afe9f4ef9ac3bcc72a03a56eebbde03f4c571a32cb38f859cff9a6524", size = 70758, upload-time = "2026-08-28T10:26:16.504Z" }, + { url = "https://files.pythonhosted.org/packages/b9/71/02fa5c2fd92068bb8952847e70ee6c5cb280e7febe11653d17812acc53dd/kiwisolver-1.5.1-cp313-cp313-win_arm64.whl", hash = "sha256:186884a58486651e3c217b6acea0a53eaa9498fdd472057c46f2f0fb5c25aad5", size = 68329, upload-time = "2026-08-28T10:26:17.658Z" }, + { url = "https://files.pythonhosted.org/packages/a9/c4/1407df7512a5b36cc79840e01710dc575733c461b13ab866cae77eaf87f3/kiwisolver-1.5.1-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:482676e5bd48d70ac99d9fc78863469845421e01184fa83f1f9366dc49f7e974", size = 134002, upload-time = "2026-08-28T10:28:08.881Z" }, + { url = "https://files.pythonhosted.org/packages/16/45/c37a21ad5c0ab581a93c55ad544721aaa1f0ae94edb29c6a678a23d013e6/kiwisolver-1.5.1-graalpy312-graalpy250_312_native-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:072bdb15a3c19a5b5dbc8f8fb1f4e1884bf4f3507eeb4cc6334401274d37a5c0", size = 194292, upload-time = "2026-08-28T10:28:11.06Z" }, + { url = "https://files.pythonhosted.org/packages/a6/c1/69f00d627949580e43d57af0aa465df46868d7c29801c137a55374101294/kiwisolver-1.5.1-graalpy312-graalpy250_312_native-win_amd64.whl", hash = "sha256:a5a00665d1a0e26763a7338d7e911d4598fbc1d50dd0d6b7919b7dc6c5d6569f", size = 73362, upload-time = "2026-08-28T10:28:12.449Z" }, + { url = "https://files.pythonhosted.org/packages/b5/1d/59ba570b1774e95e97fde3a0981b2e22118a7a495f73bf74cedc538566a0/kiwisolver-1.5.1-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:416ba7ff9f233b7036689bb5a3783537e838ad483f63558d2a800f75afe738b1", size = 59450, upload-time = "2026-08-28T10:28:20.383Z" }, + { url = "https://files.pythonhosted.org/packages/22/98/a6849f04dc18b5400e8b98affa2cd8fd86ed583085f036e57b32e571f4fa/kiwisolver-1.5.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:8af9b142ad719ae3a911ebf616bc4b78b32bbab84d6a40d3ad2f129670509957", size = 57400, upload-time = "2026-08-28T10:28:21.632Z" }, + { url = "https://files.pythonhosted.org/packages/f4/ce/a7dc71353dd06a4cbe02222773f52d4a28c81e5a452a75797f8ed113dc99/kiwisolver-1.5.1-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5daa1f19e097050b9c4d9a78fcc9263cb96c9dfae08037ddc1b7c4ad1889f2a2", size = 79891, upload-time = "2026-08-28T10:28:22.936Z" }, + { url = "https://files.pythonhosted.org/packages/10/b1/d61c61a84ff85d1a36a99df2c152b59ffedb1d356c598902aba44abcdb60/kiwisolver-1.5.1-pp311-pypy311_pp73-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cdaeeb6c350106df6bf9d873395973e5f066a9713200b72cd64f55d0a3eafab6", size = 77605, upload-time = "2026-08-28T10:28:24.322Z" }, + { url = "https://files.pythonhosted.org/packages/d3/52/5aef56f21a460a6e43ab3cdfc7697d59d7b87deb0ec97a0f7b91aa4a521b/kiwisolver-1.5.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:17851e5dad4484be0cbccbde3b15331deae036de9aebd45eed964487802b172f", size = 98465, upload-time = "2026-08-28T10:28:25.696Z" }, +] + +[[package]] +name = "lazy-loader" +version = "0.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/19/8c/0f2ff2a8b7513e68871a74740c17a504b0488c679b379e6445ebb7bd78dc/lazy_loader-0.6.tar.gz", hash = "sha256:2f4b7824d6401958639008a0cae20c776b61dff619accd6890693e2de8260167", size = 18438, upload-time = "2026-09-21T21:31:32.982Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/23/93/4e45e37f26c820216704b36b8e67cc55c490a7abd287a78a26a2c0e21386/lazy_loader-0.6-py3-none-any.whl", hash = "sha256:77253be3391b06124a0e16105bd663b6c54470af1a9ca8e1cf026f38d58ed056", size = 8791, upload-time = "2026-09-21T21:31:31.632Z" }, +] + +[[package]] +name = "locket" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2f/83/97b29fe05cb6ae28d2dbd30b81e2e402a3eed5f460c26e9eaa5895ceacf5/locket-1.0.0.tar.gz", hash = "sha256:5c0d4c052a8bbbf750e056a8e65ccd309086f4f0f18a2eac306a8dfa4112a632", size = 4350, upload-time = "2022-04-20T22:04:44.312Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/db/bc/83e112abc66cd466c6b83f99118035867cecd41802f8d044638aa78a106e/locket-1.0.0-py2.py3-none-any.whl", hash = "sha256:b6c819a722f7b6bd955b80781788e4a66a55628b858d347536b7e81325a3a5e3", size = 4398, upload-time = "2022-04-20T22:04:42.23Z" }, +] + +[[package]] +name = "markdown-it-py" +version = "4.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mdurl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/ff/7841249c247aa650a76b9ee4bbaeae59370dc8bfd2f6c01f3630c35eb134/markdown_it_py-4.2.0.tar.gz", hash = "sha256:04a21681d6fbb623de53f6f364d352309d4094dd4194040a10fd51833e418d49", size = 82454, upload-time = "2026-05-07T12:08:28.36Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/81/4da04ced5a082363ecfa159c010d200ecbd959ae410c10c0264a38cac0f5/markdown_it_py-4.2.0-py3-none-any.whl", hash = "sha256:9f7ebbcd14fe59494226453aed97c1070d83f8d24b6fc3a3bcf9a38092641c4a", size = 91687, upload-time = "2026-05-07T12:08:27.182Z" }, +] + +[[package]] +name = "markupsafe" +version = "3.0.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/38/9b/e422a865e1d5d57d0e509b4e0bf1c1a70a7f6382c29a5aa428df994c8bc8/markupsafe-3.0.4.tar.gz", hash = "sha256:2e9ad7dd851bf45fab9f75cbff4cb493fee9979e8d8c7c9c3ee119022518edd6", size = 153777, upload-time = "2026-10-02T23:07:22.29Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/32/55/18dbb4778b30ada5ce071608503cc3edc9e14e13d868c17a6d178fc30f7a/markupsafe-3.0.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9e25feb9e330b63edb0278a0acdf85e50d0cb0fbf49c3084abbe4e24ae195346", size = 11520, upload-time = "2026-10-02T23:04:36.299Z" }, + { url = "https://files.pythonhosted.org/packages/6c/14/0b05f79b4733e264a18d08fe08fa1df7347630ff32a6cb82180d9dccec55/markupsafe-3.0.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7d3391b2188d18737cb2fa147028b1096236eaa7e156446c650a489fa2cadc91", size = 12063, upload-time = "2026-10-02T23:04:37.291Z" }, + { url = "https://files.pythonhosted.org/packages/ca/3a/63ba10b6c1463216b3e4df669a9f0e5a3b0c3071557d2e8229e3968c79fb/markupsafe-3.0.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:849dd2bb0e5e4ab2b71c7191726a4a8d5aa8a610daa584728cbee0b710ddc4ef", size = 24321, upload-time = "2026-10-02T23:04:38.262Z" }, + { url = "https://files.pythonhosted.org/packages/1a/2e/5f015261b76ad633d187ef6f388b413aedd64a8773c4df59e530a0be5525/markupsafe-3.0.4-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:befb4158af32106b9a93db8d6d1d1cbbd418c0d5aca0cabb7b1780abf0c89169", size = 28845, upload-time = "2026-10-02T23:04:39.295Z" }, + { url = "https://files.pythonhosted.org/packages/33/cf/26e594b26be40c2f1fec63ccf8a8b99d0335a5b2cbe84835c7d82a994375/markupsafe-3.0.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:71f88e749ea29f67f21f3b36433c1dc54c7729ed2a6d9e2da2e0d9e0d7b224eb", size = 24701, upload-time = "2026-10-02T23:04:40.348Z" }, + { url = "https://files.pythonhosted.org/packages/81/a5/a513b76c139a3915b43404324e55c0b7979ae4f0d39eb6f075b0282e90a8/markupsafe-3.0.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6da83a088f8ef93b2d483a8232a4dbf4d69d3d8496b568a03c56becac43e1808", size = 22961, upload-time = "2026-10-02T23:04:41.372Z" }, + { url = "https://files.pythonhosted.org/packages/46/cf/4c66192c100b4542bcbe392ae06696b670f66927be3ac38a213234778ff9/markupsafe-3.0.4-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8f0fac8b13d14bb06c68195f849371924ae53dd7b1c00fed24650f704383b692", size = 21920, upload-time = "2026-10-02T23:04:42.429Z" }, + { url = "https://files.pythonhosted.org/packages/cb/17/ac3662678bfbad649893117ada2ba44dc30bf56884e84f13154a792b10f1/markupsafe-3.0.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4a7cdc2a420ca01058182da4253329764d4bfa055564d1eced90e6ba1e8b1d3d", size = 23726, upload-time = "2026-10-02T23:04:43.531Z" }, + { url = "https://files.pythonhosted.org/packages/f7/af/fe47cee339180a69ebca3c57fb3483d0f5cbd1e8337d1871fb1d9c1aebee/markupsafe-3.0.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:83b3944fea42a8400edf92fd1770fb8d0d4f7de651353bd2d8525a92dba69a21", size = 22539, upload-time = "2026-10-02T23:04:44.618Z" }, + { url = "https://files.pythonhosted.org/packages/12/32/d55440ba140442800e02d799c9cb5ab597bf6ebdb1177b5ea39a11f797bd/markupsafe-3.0.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:8138eb83940ec7299024d92d4dee45f601b9e6c5ffde9d25f4e35e326203c707", size = 23963, upload-time = "2026-10-02T23:04:45.656Z" }, + { url = "https://files.pythonhosted.org/packages/50/9d/9c86042cb364c2ad4c971e6d1247929effd25f714cd7ee11b05e6316445b/markupsafe-3.0.4-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:811d02d5122171c1941357efd8f9bf4ffe907b7f0a1a4e729a880e4be3f46e3e", size = 21506, upload-time = "2026-10-02T23:04:46.671Z" }, + { url = "https://files.pythonhosted.org/packages/75/ef/5b824f03ba40c3b3652b6272d083d2fc4fcdd440de519a3a39ba2c3e7262/markupsafe-3.0.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:50b5bedc9ed8a94fc8857a42ef4f84a81ea88f8d4f05dc8705fb23ee6d8dcca7", size = 22955, upload-time = "2026-10-02T23:04:47.698Z" }, + { url = "https://files.pythonhosted.org/packages/1e/e8/44cfcb5ea40e5e43cec7793ef90704ed0c475280839076c4345757eb8e59/markupsafe-3.0.4-cp311-cp311-win32.whl", hash = "sha256:2e5a7cd7fdd14fcb1ae5d7d8bf23d24fbd1daefd1fbca2580132e1ea75f098b5", size = 14094, upload-time = "2026-10-02T23:04:48.788Z" }, + { url = "https://files.pythonhosted.org/packages/91/89/f2b509f7bf79352e40117824c1070dbeafd4df67031d3fa98165a3134228/markupsafe-3.0.4-cp311-cp311-win_amd64.whl", hash = "sha256:fdb4ca07ab75ffadab4a8b135ad59cdbb3156b99310f3d565370da74a15d6bd3", size = 14328, upload-time = "2026-10-02T23:04:49.801Z" }, + { url = "https://files.pythonhosted.org/packages/2b/5a/ccf22672a0f64dc682306e288f0dabcb06c7a201f3bb6e6cbf86d9e8ad03/markupsafe-3.0.4-cp311-cp311-win_arm64.whl", hash = "sha256:569d65055d367e3dcdf30c3f41119467b73d9ee9faf332bdf40402644f5ac08e", size = 14188, upload-time = "2026-10-02T23:04:50.847Z" }, + { url = "https://files.pythonhosted.org/packages/81/09/4c59d56b8461ae8eb0d8ba34bb25b7e618547044679d58a82ef9b2479fc1/markupsafe-3.0.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:61631e08084be9e21a8967ec3139c7616ed7c5e9368e05c86d1b39562c8a57b6", size = 11658, upload-time = "2026-10-02T23:04:51.876Z" }, + { url = "https://files.pythonhosted.org/packages/a2/f0/d6613774d86fbf6d145751d43c59875e47a6f9f17daee0aef173bd36d90e/markupsafe-3.0.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0930db9bdc62d22944e10b066448bb65dc9abe9112880c7cab8da54db4284d5f", size = 12050, upload-time = "2026-10-02T23:04:52.931Z" }, + { url = "https://files.pythonhosted.org/packages/0d/f2/8f18e0b806eb13c1f8d07d917a720831ead54253a6dec011fbc78098a6f8/markupsafe-3.0.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6a45c3d514f2436064db00d7fc8778d888f0236ebfed649b53d13a59e69ad51b", size = 24363, upload-time = "2026-10-02T23:04:53.895Z" }, + { url = "https://files.pythonhosted.org/packages/60/ce/fa07dbe8a5675558fa36dea033e19995bc783de2dec5f540ccb9030b06aa/markupsafe-3.0.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:1e1451fab512d1bcc3dc26988ec1edb0b82c2db909132872cd9356070a6b63df", size = 28525, upload-time = "2026-10-02T23:04:54.905Z" }, + { url = "https://files.pythonhosted.org/packages/85/40/be87c01f3868ec217f8a2015089d71c22c8c5a75324822e5ed1cdd87210d/markupsafe-3.0.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:bd3ce56ae2cbae3ba82b683bc425cd7e48d2ed8b10f3e818186b6f5646d9271c", size = 24733, upload-time = "2026-10-02T23:04:56.229Z" }, + { url = "https://files.pythonhosted.org/packages/4f/a7/aeedb5140afa41fc74c225e9184ab96723a6e873b6ee1c9fede7283456d8/markupsafe-3.0.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8e124f974786f831d6043728e38296969d3579db8896fe004682f5758e613581", size = 22985, upload-time = "2026-10-02T23:04:57.521Z" }, + { url = "https://files.pythonhosted.org/packages/c3/fc/e91352bb08c6a59da3ef0909d457bf95a5f5908fbf151b30a06d9dbcfbb4/markupsafe-3.0.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c02e8f18bdedba082cef725942ac823b9b60656db07f7e265cb31618dfd00d77", size = 22001, upload-time = "2026-10-02T23:04:58.597Z" }, + { url = "https://files.pythonhosted.org/packages/5d/f8/bffee5e7d2a3deb59748a797650a48af7e672025cf641a79344a771ad106/markupsafe-3.0.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9f098115c247e11d138ab83a28fa0323c77015007ea2df73ba5fd714dfefd67c", size = 23793, upload-time = "2026-10-02T23:04:59.686Z" }, + { url = "https://files.pythonhosted.org/packages/ed/59/b853d6628ecb4d658e1d637224846d5e9bb4adf4f8df97f3be9f29dce2ec/markupsafe-3.0.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:d5f93ebbeb8032d47e349328ec8662d973d9b05a70b3c35df1f91fe419b84749", size = 22640, upload-time = "2026-10-02T23:05:00.768Z" }, + { url = "https://files.pythonhosted.org/packages/09/b2/1506df394f0f075797c418d0301498f49e43be194e3ffcb49e6fe6ccf022/markupsafe-3.0.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:64511c54db4e4987aef4c41923235927428729e8174c5dba488429be70a998ed", size = 24074, upload-time = "2026-10-02T23:05:01.813Z" }, + { url = "https://files.pythonhosted.org/packages/c7/81/5ed69cda630ac69ef60d06c09ba5a7f84ff66a2e28cf986fd5614ab3c6e6/markupsafe-3.0.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:e1a622f13970d81f95d0c72f9dc090dce9085fccfa4c9f2174377ee32bd15786", size = 21563, upload-time = "2026-10-02T23:05:03.239Z" }, + { url = "https://files.pythonhosted.org/packages/0c/fe/fb1e79be0fea60aa32602ebefc9c35a82bb42b4df157285ab7dfec12341a/markupsafe-3.0.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c9a7f43c0b202b334cc9184af09bb8f21d3a209e038efaf106936fb69e6b026e", size = 23048, upload-time = "2026-10-02T23:05:04.479Z" }, + { url = "https://files.pythonhosted.org/packages/c8/52/7632a53360671a9b750cdbabaf9cdd89f18b42248b8e4cb42c0b0296e459/markupsafe-3.0.4-cp312-cp312-win32.whl", hash = "sha256:f0ec3b750b59375eab5b0fb2b9254810c00a3375be6d789899f1055a1d556237", size = 14108, upload-time = "2026-10-02T23:05:05.513Z" }, + { url = "https://files.pythonhosted.org/packages/3f/bf/62495e180b7000aaf30000fff849e933f74264638057176cf46852500adc/markupsafe-3.0.4-cp312-cp312-win_amd64.whl", hash = "sha256:11935df9bf455ed0c04eb87bcd720f02b1fe5e02128a9430f23aed6f93336fc7", size = 14303, upload-time = "2026-10-02T23:05:06.538Z" }, + { url = "https://files.pythonhosted.org/packages/c5/8e/4c24208776a65878d656996945aacfbfe010d3720d1a98fc0eb8491fc03b/markupsafe-3.0.4-cp312-cp312-win_arm64.whl", hash = "sha256:a4bbd2d87dd233b9fc5812160c3d0ffbe42edc22a26ce0469f58479ede633fe9", size = 14174, upload-time = "2026-10-02T23:05:07.617Z" }, + { url = "https://files.pythonhosted.org/packages/6d/18/4bc5ba32499e87bb2b0ef5b3a9bb9c00a131fa961ddf0be548cb550f548b/markupsafe-3.0.4-cp313-cp313-android_24_arm64_v8a.whl", hash = "sha256:de8b364c423ef0a4bad9069657d617f9a5d2b2062457a89b1fa16ee199c399c1", size = 12786, upload-time = "2026-10-02T23:05:08.709Z" }, + { url = "https://files.pythonhosted.org/packages/4e/6f/17f0c099bf25f3e31e63cc19244d9f6af861a9a4ab778c203997903cfdd0/markupsafe-3.0.4-cp313-cp313-android_24_x86_64.whl", hash = "sha256:34bdde374c5932765d7dc685c4a1d191a3207852d67e8e0a9eb6ea85156181f1", size = 12510, upload-time = "2026-10-02T23:05:09.93Z" }, + { url = "https://files.pythonhosted.org/packages/11/af/1a141081b905036ee904ec4bd945e1f70b4e1b32d33c4e59e8cf1d58b247/markupsafe-3.0.4-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:6bd9e1788e15bfcf6a9082de42e30387e7b85d211ab21e57a939bb8cfaaf8d96", size = 11583, upload-time = "2026-10-02T23:05:10.884Z" }, + { url = "https://files.pythonhosted.org/packages/e7/0a/a89385ae590232622a03e091805cff12f24fabe6c11e0e8bae096cece81c/markupsafe-3.0.4-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:5066b244f576f91afc8ee3ba029a89f99d39c79b1853fe9d39bea9f0afbec148", size = 11801, upload-time = "2026-10-02T23:05:11.913Z" }, + { url = "https://files.pythonhosted.org/packages/ed/85/ea548dc013962eb73653124bc595635fbf9e0fa41d1f181a967ccb784dfb/markupsafe-3.0.4-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:7a83aa6e4805df46fed18e989d3d16f86ef60cb50bbc8d9ce3a6be89165fbf6e", size = 11443, upload-time = "2026-10-02T23:05:12.887Z" }, + { url = "https://files.pythonhosted.org/packages/cc/72/15f2e5ec9cf2eb00d5cdfe968d94e4156a7bd7303832c3f3b2c403a36839/markupsafe-3.0.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2d1b7d9308288661f56672b1b157d75fc536714d3638487bbea17b6318a78248", size = 11665, upload-time = "2026-10-02T23:05:13.829Z" }, + { url = "https://files.pythonhosted.org/packages/ca/e0/4030bea613677e333c8a2c901fd405055f657f9d06acba5b7357984b6ef7/markupsafe-3.0.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:73e77980c7207854f00fc4e71fb1626868d5740ab4012623d55c7a99ad122a72", size = 12061, upload-time = "2026-10-02T23:05:14.807Z" }, + { url = "https://files.pythonhosted.org/packages/f3/a5/28b76a7449eb702966b88bef599e2360b411fbb3afeee8fe560939be06ec/markupsafe-3.0.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7018d4af1cd272e847aa5917983ab5e83e4f6579f9dbfecd4a79c0ca80b144c2", size = 24395, upload-time = "2026-10-02T23:05:15.909Z" }, + { url = "https://files.pythonhosted.org/packages/07/6c/21232811afc3a063b5e934b1ae2efda52f46154ec382f585149c020e61fe/markupsafe-3.0.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c90d5b3d4e944e065a301d741b3c1d784f6bd1f503aa68b4967e32b2ba313d85", size = 27183, upload-time = "2026-10-02T23:05:16.976Z" }, + { url = "https://files.pythonhosted.org/packages/14/38/6ccdfa5b59049cb36fb80cbc80aee9cf1fc9bb77d1335ad435f2070b08cf/markupsafe-3.0.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:18a801868a884f216e784d7d14db2a4077143ce7610440aee2ce8f734e7cfcde", size = 24772, upload-time = "2026-10-02T23:05:18.209Z" }, + { url = "https://files.pythonhosted.org/packages/63/e0/cec6865dfe88cb48fedd4b20aed6af5158e41092adcbf3e028bcc6ec2108/markupsafe-3.0.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:434139499bb20b502ed3baa1f169e618f924a97e7a777fea1a49446d80106cf6", size = 23017, upload-time = "2026-10-02T23:05:19.286Z" }, + { url = "https://files.pythonhosted.org/packages/ee/76/6ed4940bb7648a9aac457c14f870cfdd5105f139a0fb1f29cd61fafa47d1/markupsafe-3.0.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9e227f3dbe6bde7491cf0a9965d00b88c6b1a4a95d11480ddf88bb96d397c19f", size = 22036, upload-time = "2026-10-02T23:05:20.352Z" }, + { url = "https://files.pythonhosted.org/packages/a1/4f/ed476226d4fe46a09090a36025bf319296810028df55eb12f1253b540f3a/markupsafe-3.0.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b8cd1f918b26fd7b1832ece557cc18f2d8747309ff8b3f0ef9d4250c5ad67a39", size = 23786, upload-time = "2026-10-02T23:05:21.576Z" }, + { url = "https://files.pythonhosted.org/packages/9a/35/66ff30450e35ef5fba9ebc930c9411747e537fd9447b65e44f5007e2b84d/markupsafe-3.0.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:a5fcffb37e602b0b3c1638a97746b9b96125caa9bcf6fa41d337a9261de231ee", size = 22672, upload-time = "2026-10-02T23:05:22.922Z" }, + { url = "https://files.pythonhosted.org/packages/32/0b/72f45ce4b4efcbca4b80cf1b06703eff0be8d37e82abb78f66c85a7ead1e/markupsafe-3.0.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:5989cb26b2e1efc6a42216a9f6b5ee495ce5ace2e5b352a9af489976b32d1ee2", size = 24107, upload-time = "2026-10-02T23:05:24.175Z" }, + { url = "https://files.pythonhosted.org/packages/d2/03/71776e5fdcba04614b384cc102e8a4198208579d896fd1394cb7cb9aa900/markupsafe-3.0.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:add96447a86d205ab616665d53b2950ee81083757f56e6ea833c8b2917646b46", size = 21615, upload-time = "2026-10-02T23:05:25.215Z" }, + { url = "https://files.pythonhosted.org/packages/ab/5f/801ce02a02e7aee0f784b1ec7843026178f6adeb9c93ac67eb1992a9a84d/markupsafe-3.0.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2628d3a8cb648ecebb3c5d6b0a1052d400e4d8b7ac0fb786be8d285b50040d17", size = 23076, upload-time = "2026-10-02T23:05:26.423Z" }, + { url = "https://files.pythonhosted.org/packages/4a/85/c43776625428f3bb4a61e8633940400e3efe6409e3c6f5bff26de5e45618/markupsafe-3.0.4-cp313-cp313-win32.whl", hash = "sha256:672d207103e6b16ca098611b0f9efad6bc00afd47c03d6ef62186495ca677dc0", size = 14118, upload-time = "2026-10-02T23:05:27.716Z" }, + { url = "https://files.pythonhosted.org/packages/6f/36/163da64de88a13db79214ef75fa041be7fa13bdb42261cf5b7484de14bfb/markupsafe-3.0.4-cp313-cp313-win_amd64.whl", hash = "sha256:1f1f9477e174582b0a1b583d60b66e1f2cf5d3fe12cee985e4aedf44766600e5", size = 14313, upload-time = "2026-10-02T23:05:28.749Z" }, + { url = "https://files.pythonhosted.org/packages/9f/a8/9b662783ffaa1149221432a923cee562f78b9cbbb8baa3df9b3753e63e1e/markupsafe-3.0.4-cp313-cp313-win_arm64.whl", hash = "sha256:06de8ef6331f6e822c28d577dc8bf43fe398800477c49498f38fc38b67ff33fc", size = 14189, upload-time = "2026-10-02T23:05:29.917Z" }, +] + +[[package]] +name = "matplotlib" +version = "3.11.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "contourpy", version = "1.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "contourpy", version = "1.4.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "cycler" }, + { name = "fonttools" }, + { name = "kiwisolver" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "numpy", version = "2.5.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "packaging" }, + { name = "pillow" }, + { name = "pyparsing" }, + { name = "python-dateutil" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e7/c8/9aa712a0afb882649424dd8de8ad9aa6235e796e84c6052e8f6dc1598d0d/matplotlib-3.11.2.tar.gz", hash = "sha256:cec596316640f2b394b8f0daa0ea61a8eae82d017b620b9f202befb972a59ea4", size = 32660610, upload-time = "2026-09-11T19:05:31.214Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c0/a9/a7fc5a63c427aeb9a4b99d6a0449f891be0952d3aee2b85229099f7b9f54/matplotlib-3.11.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:5e1e923a3fc3326b99ec0a6ff1ab1338ac6c6cc62ad9d8a9c944197c7f8c6221", size = 9461046, upload-time = "2026-09-11T19:02:59.277Z" }, + { url = "https://files.pythonhosted.org/packages/0b/d7/59d8ee63e4dee845d953515c50d67e6b8cefa953b453c6b803f197ae556e/matplotlib-3.11.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c27e577ece613ea12a0790e00b4eb80d901c59a3e16cad474f31d8b1529690b9", size = 9290872, upload-time = "2026-09-11T19:03:02.295Z" }, + { url = "https://files.pythonhosted.org/packages/2e/a4/1ca765c63a60e3096ae2ad132eb25f6f7eab0377418cd0472808befd9665/matplotlib-3.11.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:07d9b9fa60cd4c393692f50d0bb03123242ddf61c99bb0e95e75feb354e7c1a8", size = 9854405, upload-time = "2026-09-11T19:03:04.796Z" }, + { url = "https://files.pythonhosted.org/packages/d7/3d/f28ca971a4ad0ed83ebdd60230b09d99de8b91aba47964fac3a81eb2e2ba/matplotlib-3.11.2-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:30ec15d7eefee71de16b7c689b42ba49715a4644650b76a6c7c70d79daf24e91", size = 10660507, upload-time = "2026-09-11T19:03:07.402Z" }, + { url = "https://files.pythonhosted.org/packages/f7/0c/a743556bc64a9b57d2246b14d91b713078adfd2df4199f542df1861737a9/matplotlib-3.11.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:116cdb0eb0eb5644fc98eb2975d4b4dd4ad35e5c8e6b851c22e6976f371f7ab5", size = 10801141, upload-time = "2026-09-11T19:03:11.357Z" }, + { url = "https://files.pythonhosted.org/packages/66/54/91fe3fdfa29100b4668cbf9721577a10acde6e009694618230e4e7a97527/matplotlib-3.11.2-cp311-cp311-win_amd64.whl", hash = "sha256:a24d5fd36e4f0e742c3851dcd20810e56a95633a342e4bf6cb591c678e8fe61f", size = 9329982, upload-time = "2026-09-11T19:03:14.25Z" }, + { url = "https://files.pythonhosted.org/packages/49/c1/13b70871c98d7e93f0d2ed71f226b8d6f873f1372124046068a6b4902fb5/matplotlib-3.11.2-cp311-cp311-win_arm64.whl", hash = "sha256:57b9ea60a835937c2012861923cbb91f47db8565775d326d1c42fc926aa10351", size = 9023542, upload-time = "2026-09-11T19:03:16.878Z" }, + { url = "https://files.pythonhosted.org/packages/cb/ce/1bfcc4873b121597791ad74032943b123218c0613af0b97e8dd05e916fb1/matplotlib-3.11.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ef752769cd962f39ea0b6ffc82d1ea43a0012c5a6157c7a075212fa509cfcff2", size = 9476466, upload-time = "2026-09-11T19:03:19.45Z" }, + { url = "https://files.pythonhosted.org/packages/a6/c4/7f5f3601ee69baf072c0c7d3ce60c03e0618621c5a56c34a62a460e29d11/matplotlib-3.11.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ef31985c4dedb5f1424e1aec6849a47dd37689cb7fa3c20b1b82187f26806261", size = 9305583, upload-time = "2026-09-11T19:03:22.39Z" }, + { url = "https://files.pythonhosted.org/packages/b8/90/2b3fd67ee273163faeda6d514be70b5596eaa0fd77b60ffc294ad0b34f5f/matplotlib-3.11.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:df4f7784aca81a94f254c0a2767d592ee25f407e488f5fa7203e51093fb6ca27", size = 9860353, upload-time = "2026-09-11T19:03:25.049Z" }, + { url = "https://files.pythonhosted.org/packages/f4/84/32549e7a462dc311aed2ab62e5d2538028840b5c53a8be0195c789937c3d/matplotlib-3.11.2-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b9a7ad579856284135e401ecc918c5f8a017ee30539298862a109f51b971710", size = 10670348, upload-time = "2026-09-11T19:03:28.108Z" }, + { url = "https://files.pythonhosted.org/packages/84/39/02e21b74f7439bd643d717ea846006d46e309e6d29b632b57751265311d6/matplotlib-3.11.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3aa4b8516fd26659e4363abbf317c703d9116496c5db2e9d0609a2866dd39dd2", size = 10810580, upload-time = "2026-09-11T19:03:31.402Z" }, + { url = "https://files.pythonhosted.org/packages/04/93/0d239bde12b308262265c2d98909b2f0ecad2b5deeae96241642e822e9c7/matplotlib-3.11.2-cp312-cp312-win_amd64.whl", hash = "sha256:c5c1c68ee401fc98271263410f0e5ce88285abacf7627132914e8adf3d70ff43", size = 9349409, upload-time = "2026-09-11T19:03:34.195Z" }, + { url = "https://files.pythonhosted.org/packages/49/a8/06baf901c02246c8a222b655cc4540ef9c15e1549a04e07927f9cde716c3/matplotlib-3.11.2-cp312-cp312-win_arm64.whl", hash = "sha256:643ff850d8e0f5b8319337f87ed3cb59506afb3df3cc48de777d85871233be7b", size = 9028084, upload-time = "2026-09-11T19:03:37.032Z" }, + { url = "https://files.pythonhosted.org/packages/bf/ce/fc58855a3d17f6cb8a87b6f9ccd100f65432a3a7ffed57309cbf14c78987/matplotlib-3.11.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7d43ff8cebb50840648cb6429b2228621dbd709010f3117b3740abb20abf21c0", size = 9476987, upload-time = "2026-09-11T19:03:40.17Z" }, + { url = "https://files.pythonhosted.org/packages/a9/29/2a47b700545aca050e0ed73c89ba21e08407e11f5f42a567421b220de6c1/matplotlib-3.11.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2a8285cea8ef4d92aa041d1c33788bcae82248503300f93f1ca2136b9049452f", size = 9306050, upload-time = "2026-09-11T19:03:43.103Z" }, + { url = "https://files.pythonhosted.org/packages/a9/df/e7c10292f348cf55178a5b3c0092a38ea68891a3945f199ae8b7b8ac9dc1/matplotlib-3.11.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1944895967f87c84c9b4bad29a707b31f5b36df4a3a2339ea1f8ea3ce5105539", size = 9861060, upload-time = "2026-09-11T19:03:46.044Z" }, + { url = "https://files.pythonhosted.org/packages/e4/b9/b978de6d43d47f1d4d8e653fac32c16d8d2e5f46bbbb822e26d7cb1adece/matplotlib-3.11.2-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:af2661f6ac6bbd1d081996f54fdd9385715c625ace8cec0869055c0cfbf38981", size = 10670885, upload-time = "2026-09-11T19:03:48.942Z" }, + { url = "https://files.pythonhosted.org/packages/a3/80/d72f61bb2631ac15d17fada1f17e0f99fecba69d8376d34f17cf93b7eddd/matplotlib-3.11.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7e5a90f8a707ebb6004a713b1cff091a40cc5df4c7c1cb165e5a505ebc11c292", size = 10811222, upload-time = "2026-09-11T19:03:51.665Z" }, + { url = "https://files.pythonhosted.org/packages/94/2e/6d1d33f242c705b5c6c6fb3f0fc141add51cb1abb62912e59de0835d28c4/matplotlib-3.11.2-cp313-cp313-win_amd64.whl", hash = "sha256:bc067c462a86f0e57bf52fc6e058d90171a5420007dac00c46e90153050c69a7", size = 9349539, upload-time = "2026-09-11T19:03:54.534Z" }, + { url = "https://files.pythonhosted.org/packages/bb/bb/c0aa86a7f4c0e62f1c3c0711e361f6f23f87eb9018732a5e11994bd1b7ec/matplotlib-3.11.2-cp313-cp313-win_arm64.whl", hash = "sha256:3e8576f7c47e02fd4f21f44171302d2d1d58d4471d46da3d71fe8899d19539d9", size = 9028295, upload-time = "2026-09-11T19:03:57.261Z" }, + { url = "https://files.pythonhosted.org/packages/0f/d9/a1218a31fa2d8082eb893548990ad09a2005ebda1b2b89dad88a8308e51a/matplotlib-3.11.2-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:854df8d7dfe9fdffcbaa6f39e44a6c24b409cb4d7561fbc09213b157d833f6a6", size = 9457702, upload-time = "2026-09-11T19:05:22.086Z" }, + { url = "https://files.pythonhosted.org/packages/68/d7/a9fde05d87c3c853f233ec20c5b5c3a1c86035e1ed4bfa397cd54d46ec90/matplotlib-3.11.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:eac4b07d4e3743b172451e122ea964f72f152879d4f9adcf3f3d33e518f12ead", size = 9287753, upload-time = "2026-09-11T19:05:24.677Z" }, + { url = "https://files.pythonhosted.org/packages/ec/79/ea2ae4eab84f011c1dc8145864f78e5a63adda8efb95557ef36cc38e6da2/matplotlib-3.11.2-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7ef7a53b66780e5d942923724f08577fbc5be1f7322da0f0f3f9dcaa45dd803d", size = 9848259, upload-time = "2026-09-11T19:05:27.455Z" }, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, +] + +[[package]] +name = "narwhals" +version = "2.28.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/67/43/e11d75a7fb635c6f63dd6e176cb3e050ec2fee35f2f2267b332a6ad351ec/narwhals-2.28.0.tar.gz", hash = "sha256:2d9860c80d6610820826e94c6e42ce4b1d92d89cdd479586e652a8d211dab345", size = 754977, upload-time = "2026-10-27T12:19:33.777Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cf/82/8ef9c7e0f50493307a49c09cfa7f00c5a778b6d1921f7b2dabe1d3a5809e/narwhals-2.28.0-py3-none-any.whl", hash = "sha256:1f69d6385ec30e2fa6e1fdeb37c562ecabd0f713f6f3e3b58a668c5c9df9567f", size = 490591, upload-time = "2026-10-27T12:19:31.649Z" }, +] + +[[package]] +name = "netcdf4" +version = "1.7.5.1.post1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "cftime" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "numpy", version = "2.5.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "packaging" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/84/a6/816e4887fc132ce62b055cd2aecc47f0273e6bcf870cda250ed1323d93be/netcdf4-1.7.5.1.post1.tar.gz", hash = "sha256:9823076013fd54e7984d3ae8f431671fd199b15eeb267f91b8c4b057fd8324ab", size = 849840, upload-time = "2026-10-23T16:47:18.246Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f7/85/4bf2ff1dc7ab58e777e44f6a25d196f3104b916b8836e4f749fe9e77b158/netcdf4-1.7.5.1.post1-cp311-abi3-macosx_15_0_arm64.whl", hash = "sha256:e241c3e8c3af6a2e04a989bf80db31c580e4692ea6fc8f41d998ef1b2010fbcc", size = 10346992, upload-time = "2026-10-23T16:46:32.361Z" }, + { url = "https://files.pythonhosted.org/packages/95/cd/22874a17787224f6b806224091ae898607762db8f2028ae3cee95bfde498/netcdf4-1.7.5.1.post1-cp311-abi3-macosx_15_0_x86_64.whl", hash = "sha256:3fc1e1820b889b78f5777e18f828e1373ac497798a1e207d4723f21dac5271ed", size = 11089349, upload-time = "2026-10-23T16:46:34.821Z" }, + { url = "https://files.pythonhosted.org/packages/34/b6/ffb022b635490f884cc32b30e3f280fb36a41f35d424213d88a5beddec4d/netcdf4-1.7.5.1.post1-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:544316a1f712aa9f0a29a57ba835c9a8e640f9a5724b37d5cdf8a919e54427df", size = 10695626, upload-time = "2026-10-23T16:46:37.148Z" }, + { url = "https://files.pythonhosted.org/packages/4b/eb/dfbce2a6028198d65d238370f42ced033a506d61804fcb3b882e08f2d930/netcdf4-1.7.5.1.post1-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:fde13b1fc71bf2b0f684a272fe279fa7d01ee45e6098b4711840a4ab6ae42130", size = 10552948, upload-time = "2026-10-23T16:46:39.139Z" }, + { url = "https://files.pythonhosted.org/packages/48/da/1e49d4f77d21ed70d6f4cfdb53822f9877eb9a7ba4601085bfeedd268069/netcdf4-1.7.5.1.post1-cp311-cp311-win_amd64.whl", hash = "sha256:5ed5cc38606a7588afbca1f855a606cc6afe13eb7d56fa311aadff0ecfdb911d", size = 22467001, upload-time = "2026-10-23T16:46:41.429Z" }, + { url = "https://files.pythonhosted.org/packages/2f/5e/cf0894ae69b4f3406e24d35b2e324e7ac4038fc8da4e59a1611021439cef/netcdf4-1.7.5.1.post1-cp311-cp311-win_arm64.whl", hash = "sha256:d72e3b9a04df78fa27f5ccf8e77adf01345697cbcac4c1111ee6d80c9e7d8a25", size = 2833700, upload-time = "2026-10-23T16:46:43.71Z" }, + { url = "https://files.pythonhosted.org/packages/f4/f2/6906e029e47331c0d2c8ed03b0c72826f394e3561352e9c6dcb27f576e0c/netcdf4-1.7.5.1.post1-cp312-cp312-win_amd64.whl", hash = "sha256:e7a3e88fcad3bd795bd38f772309ae922473287de39412877213939c0a2ae8f5", size = 22443609, upload-time = "2026-10-23T16:46:45.31Z" }, + { url = "https://files.pythonhosted.org/packages/31/b1/572a42ea45ad873477c3d8e3ed8666aa0444e424d2d54408651f5eddd26e/netcdf4-1.7.5.1.post1-cp312-cp312-win_arm64.whl", hash = "sha256:ef36ce6a0ffffa1ec92deaf1fca7fcf49f1b2b14655b08a4acf39afa588a7aad", size = 2813763, upload-time = "2026-10-23T16:46:47.175Z" }, + { url = "https://files.pythonhosted.org/packages/75/2c/145c4e8d5c2870930d4d9d7faebc4bc8d4a9a7036534113b57528d78982e/netcdf4-1.7.5.1.post1-cp313-cp313-win_amd64.whl", hash = "sha256:2d8cff389ca9d49f83f3faebc2cf1abfebf2a023d8a168ee0dc8234de4504371", size = 22443432, upload-time = "2026-10-23T16:46:48.92Z" }, + { url = "https://files.pythonhosted.org/packages/a6/0f/3237cb3dc72c966a5322282b3ac059ee52dc783a69aa1a9601d532388789/netcdf4-1.7.5.1.post1-cp313-cp313-win_arm64.whl", hash = "sha256:2faeb065a16cd60841e7c3930526222f5f8345c3abeed25de34565ee73fe48a4", size = 2813718, upload-time = "2026-10-23T16:46:50.82Z" }, +] + +[[package]] +name = "networkx" +version = "3.6.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.12' and sys_platform == 'win32'", + "python_full_version < '3.12' and sys_platform == 'emscripten'", + "python_full_version < '3.12' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +sdist = { url = "https://files.pythonhosted.org/packages/6a/51/63fe664f3908c97be9d2e4f1158eb633317598cfa6e1fc14af5383f17512/networkx-3.6.1.tar.gz", hash = "sha256:26b7c357accc0c8cde558ad486283728b65b6a95d85ee1cd66bafab4c8168509", size = 2517025, upload-time = "2025-12-08T17:02:39.908Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/c9/b2622292ea83fbb4ec318f5b9ab867d0a28ab43c5717bb85b0a5f6b3b0a4/networkx-3.6.1-py3-none-any.whl", hash = "sha256:d47fbf302e7d9cbbb9e2555a0d267983d2aa476bac30e90dfbe5669bd57f3762", size = 2068504, upload-time = "2025-12-08T17:02:38.159Z" }, +] + +[[package]] +name = "networkx" +version = "3.7" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.12' and sys_platform == 'win32'", + "python_full_version >= '3.12' and sys_platform == 'emscripten'", + "python_full_version >= '3.12' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +sdist = { url = "https://files.pythonhosted.org/packages/dc/76/3af777226b63a5e64a6b36b1ec5855c14e2b94a37096d4760e595fc43511/networkx-3.7.tar.gz", hash = "sha256:fd77a511bd90f39f3d016351345b52cf5319b813bdca01de3f755d3cca62e96a", size = 1866482, upload-time = "2026-09-21T16:45:16.974Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/cd/fe58041e9011f307c490e3e17dd48cc516448f7c698a3f2d9d9d65d7e6a8/networkx-3.7-py3-none-any.whl", hash = "sha256:e3fd2c13a7814cee3746340d8d7f8598a67f16a58bf47fb7f8793fab6efca1b0", size = 2142205, upload-time = "2026-09-21T16:45:14.609Z" }, +] + +[[package]] +name = "nodeenv" +version = "1.12.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e6/99/d27cfc9518f49e4cbc25c683ec7cf8704a9d31311341c291c0e78ddf791b/nodeenv-1.12.0.tar.gz", hash = "sha256:4ea7f88339310afebc3253f2c7a91b94bacf97d73a36a5c108962abdfaca501e", size = 131292, upload-time = "2026-10-31T19:03:18.847Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2d/6a/8a9ac7d5e2f7cf53c459bfef90ebc17a2f8cd54b665af96799d4c534840e/nodeenv-1.12.0-py3-none-any.whl", hash = "sha256:80644d65c10cc2be12e3e4b54b3367ff288c7ad24ae75c2dcc65454fc7449d4d", size = 39442, upload-time = "2026-10-31T19:03:17.427Z" }, +] + +[[package]] +name = "numcodecs" +version = "0.16.6" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.12' and sys_platform == 'win32'", + "python_full_version < '3.12' and sys_platform == 'emscripten'", + "python_full_version < '3.12' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +dependencies = [ + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" } }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f9/03/c82563fffd81245a5b0239be8ff7743b30fe35d044080261458ae3c6aee5/numcodecs-0.16.6.tar.gz", hash = "sha256:5e446928af7c40b4dc8f51e960b4c9fb14f617846577d2fcf35ae2f92738f00f", size = 6288989, upload-time = "2026-10-20T19:42:43.925Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e1/14/a962963a85357410969621a1a56ed026c70dfc3836949eec415bae7f7ecf/numcodecs-0.16.6-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:0916f0852500dd68fe2698682d490edaf08fc673b9bc992183a4e81aad25e322", size = 1634935, upload-time = "2026-10-20T19:42:11.706Z" }, + { url = "https://files.pythonhosted.org/packages/dc/ba/e5bd5a0812d320e370d153243775476378d12eface499e9b4a2bde9bc207/numcodecs-0.16.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:73df0384f20ecd662fea6da2a08933ea8885d74e3c4e5916819292c5c38c0dca", size = 1179199, upload-time = "2026-10-20T19:42:13.251Z" }, + { url = "https://files.pythonhosted.org/packages/06/87/3e2bf32adab8aa9337cfd450b789b0f8de4b24d0cba76748e109a628effe/numcodecs-0.16.6-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30f1533255c32aea337ef2c0b8cf14772f1d490cfb023ce4cdb80deedea4bd2", size = 8743497, upload-time = "2026-10-20T19:42:14.847Z" }, + { url = "https://files.pythonhosted.org/packages/47/31/ff9f1bac85fa145710d1744423222d67a8d23c2bad332c4f04664732c291/numcodecs-0.16.6-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b271f5d7ebe079b0c2914cad256153d92b3a271de534c3d4220692e82daf870d", size = 9349451, upload-time = "2026-10-20T19:42:16.684Z" }, + { url = "https://files.pythonhosted.org/packages/81/ed/b1b224c3aabc24121fe247e341bab6f65c75e225936d37fbe9fb56edaec6/numcodecs-0.16.6-cp311-cp311-win_amd64.whl", hash = "sha256:e3e18c7dfe96c8c28411dc45a723c872c85ea4a6fcd9c214ed55e9bc9eec6ba6", size = 844468, upload-time = "2026-10-20T19:42:18.96Z" }, + { url = "https://files.pythonhosted.org/packages/53/02/26a65f7639089b378b79713340d59aa7f691f21ff9e1e44918da8dc36833/numcodecs-0.16.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3ff7c380b40e855ae8caf6c19c368f1c43bf75dcac876445286320bd9fce7753", size = 1666738, upload-time = "2026-10-20T19:42:20.352Z" }, + { url = "https://files.pythonhosted.org/packages/10/c9/1ca1e577f54851126dd1652ec8352ffddef30b9b5eb4ffbade149b12eb6e/numcodecs-0.16.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:524278af1f81cdf4d47b4300650458096822bbdf073c8b3417fa10147e1f73d9", size = 1174596, upload-time = "2026-10-20T19:42:21.968Z" }, + { url = "https://files.pythonhosted.org/packages/a0/31/96be58c5acc480ba389b8c37f66d688e86e9e694aad8a275a455c9a1b296/numcodecs-0.16.6-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:58a02c60f9e4497f7a2c9653f58e8884fd2b7bf5b86ea205f965e363d495ecfd", size = 8710129, upload-time = "2026-10-20T19:42:23.403Z" }, + { url = "https://files.pythonhosted.org/packages/e0/13/01081ae906ae7574289818b08f076ab6663d220f0283b46837962a50ce0f/numcodecs-0.16.6-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:609fd6f5ab1dd27fcde21d3e2bbf7e05846d5d5fdd23cf34e02375278a44252f", size = 9346139, upload-time = "2026-10-20T19:42:25.563Z" }, + { url = "https://files.pythonhosted.org/packages/de/d1/5639161aa9524b609d9ce9088bd691ec228a027b141ffc5a88faebc686d2/numcodecs-0.16.6-cp312-cp312-win_amd64.whl", hash = "sha256:c9492c7d79674aac222cb11aba7d48de8b95a2dabadeab501ffe61c124aee664", size = 841956, upload-time = "2026-10-20T19:42:27.243Z" }, + { url = "https://files.pythonhosted.org/packages/c4/2a/829aada6ab1df933a7ebe2693ce6c243a4c4bbbe3040dca175e39005ee31/numcodecs-0.16.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e3b113eecf5509b116982ab7f30a8c861b158045f0702234322bad7bfdeab728", size = 1665051, upload-time = "2026-10-20T19:42:28.451Z" }, + { url = "https://files.pythonhosted.org/packages/8e/87/48fa19e087df8a1a1bd6157c345bef0cdc1133c0fe927219fd43b21cc8f2/numcodecs-0.16.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e7e41920099df1aec14d6112e3e1ab028b2451b9ed92e33163510667bfa2e384", size = 1172068, upload-time = "2026-10-20T19:42:29.77Z" }, + { url = "https://files.pythonhosted.org/packages/af/16/670283fc6aeccace1ed426fe223e73e4dba42ae714efad70e75f004942ad/numcodecs-0.16.6-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ea78370626cdf1436a9d1fa0984a4f7a227a0dc643abbbc2d1558e6d4afafeb", size = 8659446, upload-time = "2026-10-20T19:42:31.073Z" }, + { url = "https://files.pythonhosted.org/packages/fe/4e/0088e3a9a8e1c466a8c8500aa90f613f9aa418b79d303008f6b2e8a641ba/numcodecs-0.16.6-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a063fbac0403e8609a649c5fd716d05c8ac3f2fed0aa19d03c0d0ab2194a9527", size = 9301961, upload-time = "2026-10-20T19:42:32.904Z" }, + { url = "https://files.pythonhosted.org/packages/9e/73/875f008f7e4d8ac5e65a90222e27c14ea4e062192d5452a15e7d266beeb5/numcodecs-0.16.6-cp313-cp313-win_amd64.whl", hash = "sha256:40ed58a808c8a629391be8918ea251a884715deaf077d1618327429adf06c2f4", size = 838867, upload-time = "2026-10-20T19:42:34.603Z" }, +] + +[[package]] +name = "numcodecs" +version = "0.17.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.12' and sys_platform == 'win32'", + "python_full_version >= '3.12' and sys_platform == 'emscripten'", + "python_full_version >= '3.12' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +dependencies = [ + { name = "numpy", version = "2.5.4", source = { registry = "https://pypi.org/simple" } }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a6/f7/7fd3a5066fd07a1541b7937f3f9db73552f48b28a819470c5c00a0c3e472/numcodecs-0.17.1.tar.gz", hash = "sha256:5069dc01b0087c6135cbb3b043707aaf927c992fcd325248d15d907c51dd3747", size = 6290108, upload-time = "2026-10-20T19:37:58.903Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/83/42/3e48ae17b5810f4972140344d86d951c6620ff618901e4e84cb007c5e5e6/numcodecs-0.17.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7cc861d98827e92f93d3ff5b796ec6ee6785efadd44d721496b49e2609365457", size = 1172442, upload-time = "2026-10-20T19:37:28.792Z" }, + { url = "https://files.pythonhosted.org/packages/95/e0/338a68d002397fa896581ff3c8af6ca8c6cba259cda4da2496084b1b1785/numcodecs-0.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d639064acc8f38184e763a5bfe6b5376361c3a79583c67a86a57424629a247c8", size = 979779, upload-time = "2026-10-20T19:37:30.651Z" }, + { url = "https://files.pythonhosted.org/packages/73/72/ca2c75596fe84eba628510595d0b32395205ebf450870f07311adb9cd3e9/numcodecs-0.17.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bfa4d91bacec65232d02402efb82ad08e8c49298e5358d03fe205691ffcca397", size = 1385001, upload-time = "2026-10-20T19:37:32.092Z" }, + { url = "https://files.pythonhosted.org/packages/76/c7/e6b131d0240345bc523873b9f16b22aaca8319abdf0acef4247ccffb3cea/numcodecs-0.17.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8e3c08f6f4074eb92b3312efbf293afbbae5697d6b9d009435671a3a18e22ba5", size = 1437783, upload-time = "2026-10-20T19:37:33.861Z" }, + { url = "https://files.pythonhosted.org/packages/8c/80/1fa323d38f42002bc3beb18ec3c5d1eecc8c3c703f497a9f0ea6ddf8e512/numcodecs-0.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:f65d08e20e16430dfb2a21e6a1298b09f98a0ca08a2a9e7aec25a81e4b98b7e5", size = 1497112, upload-time = "2026-10-20T19:37:35.912Z" }, + { url = "https://files.pythonhosted.org/packages/80/01/325b1b78c4602f808e4733e9f88ae4b3f15813c98c9a44d46d799e543bb9/numcodecs-0.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6ca56ab612dbdfffa69c24ad510eaf3cdd794b40dd47144f96f7e0c890fd712c", size = 1171680, upload-time = "2026-10-20T19:37:37.23Z" }, + { url = "https://files.pythonhosted.org/packages/fe/3c/b5350ec12911abfcc69f87113e00ee01beecda4d8550fa081e6a840a73be/numcodecs-0.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0eca3c1c135890911971f6f31e66c9dbfdb28af780c8a53aeadb160478553cd8", size = 976932, upload-time = "2026-10-20T19:37:38.588Z" }, + { url = "https://files.pythonhosted.org/packages/61/3e/59c7ba952edf8e822d76b6b7933c93e7eb5edb11e273162a338c4bcf6985/numcodecs-0.17.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fed686b607d9c1afde8cd0b17d808e29d9a7c8d4ff2c12478456198f57b21e91", size = 1380146, upload-time = "2026-10-20T19:37:39.895Z" }, + { url = "https://files.pythonhosted.org/packages/fb/66/d7ed1f3cf2acdd7066c79e5fbb4a71df275860df4e1951d4209c8a4bf446/numcodecs-0.17.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3a67976c03dd62e1b6a5e938c61f643673a6a9a37e17800eca99d1a7ea3c89e2", size = 1431354, upload-time = "2026-10-20T19:37:41.298Z" }, + { url = "https://files.pythonhosted.org/packages/26/80/ab38a26d5633da6c9217bfb04cc38025371d1e785fd6484aed56dd9a6f37/numcodecs-0.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:a5cc666eba758ae20798c6bb1ce406d68db60e3fe0d65aa67e00d130aa1d00f0", size = 1493578, upload-time = "2026-10-20T19:37:42.997Z" }, +] + +[[package]] +name = "numpy" +version = "2.4.6" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.12' and sys_platform == 'win32'", + "python_full_version < '3.12' and sys_platform == 'emscripten'", + "python_full_version < '3.12' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +sdist = { url = "https://files.pythonhosted.org/packages/d0/ad/fed0499ce6a338d2a03ebae59cd15093910c8875328855781952abf6c2fe/numpy-2.4.6.tar.gz", hash = "sha256:f3a3570c4a2a16746ac2c31a7c7c7b0c186b95ce902e33db6f28094ed7387dda", size = 20735807, upload-time = "2026-05-18T23:37:14.07Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/49/ec46835a70be8fa6446c495126ac84fdb28cb2558e1620ffb87a10c8b64c/numpy-2.4.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0280e0356c0829a18d9de1cb7eee50ec22ca639878d7240307ca0943d73cd2c4", size = 16969194, upload-time = "2026-05-18T23:33:13.503Z" }, + { url = "https://files.pythonhosted.org/packages/0e/0d/f5957185c0ee2f3e12f78715aa9e3b353fd83633316c8532b38faa37e3f6/numpy-2.4.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:110f8b71aacb688ec69062bb7f6938a0f8acb01b7c1c4beb453c65b6d234584d", size = 14964111, upload-time = "2026-05-18T23:33:17.795Z" }, + { url = "https://files.pythonhosted.org/packages/ad/40/40a40ee0ddf7ceb782c49af278894b686e586d65d8c1889c8b5da01a3d7d/numpy-2.4.6-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:4cfe66903cc32a9921a6733d96b19bb6abf310397581bbad89c228f5abaf0ee8", size = 5469159, upload-time = "2026-05-18T23:33:20.654Z" }, + { url = "https://files.pythonhosted.org/packages/63/13/f9a8046535cb21deae82f8d03de9617e08882d274fad2539630761888228/numpy-2.4.6-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:8155154c7c691289fe18f510b5d4657c68c67989f293f0535a91360392ff6538", size = 6798936, upload-time = "2026-05-18T23:33:22.987Z" }, + { url = "https://files.pythonhosted.org/packages/33/a8/6fa8c1a345a8c85dbb21932c447bee07c30a2c2a3f31e369c0a84b300147/numpy-2.4.6-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ab0a9c4ffb1a6d95ef519fe4247dba8eb6b18ad93999f76b7f657039acabd47", size = 15966692, upload-time = "2026-05-18T23:33:26.62Z" }, + { url = "https://files.pythonhosted.org/packages/02/03/74fe2a4cb3817d94d86402f2506554130a2f01414e299b5a843e5a8a957f/numpy-2.4.6-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:89cd468399cfd2504718f0ba50e410dca55a170b61a02ad92bb18c8a65186e93", size = 16918164, upload-time = "2026-05-18T23:33:29.955Z" }, + { url = "https://files.pythonhosted.org/packages/c5/80/3615be3313f7e7696609bc194b9f0101da809df79e859bdb84e0cd043f46/numpy-2.4.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c2d37ab77531417474168eb79d6d80b14f821a966818505d03013d0833edb7a8", size = 17322877, upload-time = "2026-05-18T23:33:34.724Z" }, + { url = "https://files.pythonhosted.org/packages/ca/ac/a691e0fe2675e370d0e08ff905adc49a1c8830e8cae03efe4477e92cd55d/numpy-2.4.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f407cb6b8e9d6d8c626bc73c945db1706035af8fd632295547bf1c9e46d092d6", size = 18651487, upload-time = "2026-05-18T23:33:38.217Z" }, + { url = "https://files.pythonhosted.org/packages/15/a7/9bc1cd626d7bf6869bfedf27b91b6ab5dd607758bf8e959d6fa80c6a59cb/numpy-2.4.6-cp311-cp311-win32.whl", hash = "sha256:ddea102b48f9e339f3948bf22040944184627a30fdf7f858667673b9c5f033c8", size = 6233945, upload-time = "2026-05-18T23:33:41.331Z" }, + { url = "https://files.pythonhosted.org/packages/c5/31/7fc6239c12bce7e931463251cca4426c465e1876ba3cc785402ef4dd8f4e/numpy-2.4.6-cp311-cp311-win_amd64.whl", hash = "sha256:1e254a00cdf42b1e4d5b3d68d33af63268d41340d8885df2ab6470f2e1500147", size = 12608406, upload-time = "2026-05-18T23:33:44.131Z" }, + { url = "https://files.pythonhosted.org/packages/27/83/140f85a466595a16382996a1bf06b2b54bcd597488921b0c9daaeeda72af/numpy-2.4.6-cp311-cp311-win_arm64.whl", hash = "sha256:ed9749eef4cbd126da3dc1d6bcb3a57f5eb7ac6a6484146bdbf743f552dfc577", size = 10479528, upload-time = "2026-05-18T23:33:50.725Z" }, + { url = "https://files.pythonhosted.org/packages/95/2a/3d7b5ac8aac24feaf9ad7ed58f45b0bbc06d37e4338ae84c9f2298b570f9/numpy-2.4.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:001fbb8e08d942dd57599e781f2472269ee7f2755fae407b4f67b2f0b17da3f1", size = 16689119, upload-time = "2026-05-18T23:33:54.065Z" }, + { url = "https://files.pythonhosted.org/packages/ea/12/92c4c131527599e8288d6918e888d88726f84d805d784b771f32408aeaef/numpy-2.4.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ebfb099f8dcf083deef3ac1ca4c1503f387cf76296fcb3816b66f5ecb5f54fdb", size = 14699246, upload-time = "2026-05-18T23:33:57.621Z" }, + { url = "https://files.pythonhosted.org/packages/ad/fe/c0a6b7b2ca128a8fb228575147073b660656734b8ebe4d76c8fd748dcc79/numpy-2.4.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:3213d622a0283a39a93d188f3cf72b26862df52fbb4ca3697f51705016523d41", size = 5204410, upload-time = "2026-05-18T23:34:00.302Z" }, + { url = "https://files.pythonhosted.org/packages/f3/d4/9770d14ba719432bb90a421bfd443872ed0f70f7264b64bec12ea363d5fd/numpy-2.4.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:357cc07a6d7b0b182ff02249616a03742827ebb1277546b5c7cd7f7620a45698", size = 6551240, upload-time = "2026-05-18T23:34:02.852Z" }, + { url = "https://files.pythonhosted.org/packages/c9/c6/50a46a6205feba2343f1d6d17438107c5dc491ed1c736e6ea68689fd906b/numpy-2.4.6-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f9fb9157b4ce2971008323afe46053787b526ef624fea915b261468a8421a0f", size = 15671012, upload-time = "2026-05-18T23:34:05.485Z" }, + { url = "https://files.pythonhosted.org/packages/99/60/14115e6364fa676c5397c2ad3004e527e9aa487abf5d0706ec81bbd08529/numpy-2.4.6-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:90f9849678c75fe7afa2d348ac842c168b0a4d3d61919687216dfc547976d853", size = 16645538, upload-time = "2026-05-18T23:34:09.265Z" }, + { url = "https://files.pythonhosted.org/packages/ae/c5/693cbe59e57db94d2231fa519ca3978dc9e19da5a8f088588f5c6e947ff2/numpy-2.4.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c1a2af6c6ef86344a6b0db6b97834208bf598db514f2b155042439b62605601a", size = 17020706, upload-time = "2026-05-18T23:34:13.053Z" }, + { url = "https://files.pythonhosted.org/packages/ef/fc/85b7c4eff9b4966ade25c2273cf7e7012e92366c032058653934b37de044/numpy-2.4.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e5805d5a22fd19c8ccff10a9561f9df94436b0545619ea579db2d3c35294bce2", size = 18368541, upload-time = "2026-05-18T23:34:17.024Z" }, + { url = "https://files.pythonhosted.org/packages/f6/81/e1b27545deedce7f4a0b348618c6b62d74e36a4dc9ccd42f3eb2f85eee32/numpy-2.4.6-cp312-cp312-win32.whl", hash = "sha256:e3eeb0aabd6bd5ce64faae67e9935203a6991b4bc2a485a767fbafb2c5125f45", size = 5962825, upload-time = "2026-05-18T23:34:20.3Z" }, + { url = "https://files.pythonhosted.org/packages/ab/ca/feab00bd44aa5fe1ad2c18f08b4d3bb92e26484b0b1d1443897809ed528c/numpy-2.4.6-cp312-cp312-win_amd64.whl", hash = "sha256:d8e8286dd7cea7895157318d1b91cdacac64c479f3cbc8dce548331728484751", size = 12321687, upload-time = "2026-05-18T23:34:23.095Z" }, + { url = "https://files.pythonhosted.org/packages/63/cf/5a6d34850a39d1093558564f77ee8e8e0bee5061151b8f05a55711001ec7/numpy-2.4.6-cp312-cp312-win_arm64.whl", hash = "sha256:4081eb135ac24158bd51cdfbef16f1c64df7063b1143f24731387137c092bec8", size = 10221482, upload-time = "2026-05-18T23:34:25.876Z" }, + { url = "https://files.pythonhosted.org/packages/fb/82/bdab26d7438c6791ca31b7c024ca37c1eab8b726ba236129005cd4a06e45/numpy-2.4.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:511dbaf848decaaaf4b4ca48032619fb3138710c4bf7da7617765edad1ef96b0", size = 16684648, upload-time = "2026-05-18T23:34:29.41Z" }, + { url = "https://files.pythonhosted.org/packages/1b/30/a80189bcc7f5e4258b3fbc3968d909d1756f54d023299ecc39ad6fdb9ef8/numpy-2.4.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bf162abab1c1a736333192707cef898e735a5ca00f38f27eeedf44b39d9e85eb", size = 14693902, upload-time = "2026-05-18T23:34:33.013Z" }, + { url = "https://files.pythonhosted.org/packages/97/12/70b5d0d7c15e1ebb8a6a84a8caa1d19e181d84fb58bb6d70aca29099dec1/numpy-2.4.6-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:043191bfa8eab18c776647b62723ac9dddece59743b13f49b2016094129c2b3f", size = 5198992, upload-time = "2026-05-18T23:34:36.132Z" }, + { url = "https://files.pythonhosted.org/packages/ba/8c/ebd2a8f8a83541f8d38cc5667e8c2b69cecfd30da6e45693e8158857d44b/numpy-2.4.6-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:6180d8b35af935aed8ece3a85e0a43f87393ae0ac87c8d2c8bd2c993f7270ef3", size = 6546944, upload-time = "2026-05-18T23:34:38.484Z" }, + { url = "https://files.pythonhosted.org/packages/bb/c5/7b863a97a91671a0338f4253bd3b5a3d3852f0692dae91711c9f4a10e787/numpy-2.4.6-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:72fbe16c6fac95aedf5937fa873445cec2110be35d8a4e9433d7501fd98dae6b", size = 15669392, upload-time = "2026-05-18T23:34:41.257Z" }, + { url = "https://files.pythonhosted.org/packages/a5/9d/3584b9984ca4c047aea75214ce1a4c4c73d849bd71b604264b7f5653f8a8/numpy-2.4.6-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a7830bab239b79cda9c08c2da014761cafb48da6150e1da17ac06283f43b6089", size = 16633220, upload-time = "2026-05-18T23:34:45.075Z" }, + { url = "https://files.pythonhosted.org/packages/05/ae/7c67fba23bd98caec7c99261f3a16072ade14813486b0282cb29846de832/numpy-2.4.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ef4aea96ce4d3b074422cb4f2f64e216bf9e213004bb58ecfdf50ea02ea8eb9a", size = 17020800, upload-time = "2026-05-18T23:34:49.065Z" }, + { url = "https://files.pythonhosted.org/packages/d9/5d/3b6725cb31d983c5e66916f5d36f6d7e5521129e4c4404d64f918292a5b6/numpy-2.4.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:dfa20cc6ca228e6b155b11da03825975ce66aea520985dbbddf0f2a5a495c605", size = 18357600, upload-time = "2026-05-18T23:34:52.709Z" }, + { url = "https://files.pythonhosted.org/packages/f7/da/2ccc6c2fe8898dee01d90c75c5f5f914a23daf99e3e0f59516a08760c8b5/numpy-2.4.6-cp313-cp313-win32.whl", hash = "sha256:56b39e5e0622a09a25bf5baf62f4bcf0cb8a41ae6e2819cf49bbc5a74c083f91", size = 5961134, upload-time = "2026-05-18T23:34:55.618Z" }, + { url = "https://files.pythonhosted.org/packages/b5/cd/9cc4dc876fb065d5c220aae4d5e14826b2715331bb7618ce1fb07a679d99/numpy-2.4.6-cp313-cp313-win_amd64.whl", hash = "sha256:c4fc99836233ea196540b17ab0983aff60ed07941751930f5f4d05bc3b3b7359", size = 12318598, upload-time = "2026-05-18T23:34:58.928Z" }, + { url = "https://files.pythonhosted.org/packages/39/1e/c0bcba1f8694116485fe28fd1be698c278fcda4141c5b0e53a2aed8b12a8/numpy-2.4.6-cp313-cp313-win_arm64.whl", hash = "sha256:a7c711e21628b52034bb5ab8d1bce291f752fcc5e92accc615778acee1ff4778", size = 10222272, upload-time = "2026-05-18T23:35:02.167Z" }, + { url = "https://files.pythonhosted.org/packages/63/6d/cc5619247c8f4204e507f5883528372e4ac4bb189e579fb859a12e480b1f/numpy-2.4.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:112b06a867b235ef466ed3508ddf0238050df9c727cafb5301ac385b899189a1", size = 14821197, upload-time = "2026-05-18T23:35:05.468Z" }, + { url = "https://files.pythonhosted.org/packages/00/58/f1c39161c87d9e9bed660f1ed4bafc0e403d5ec9650b6dd77aead07d489b/numpy-2.4.6-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:eaf7fa2de5c0be8ae6ff8e9bea2ccd725e980541244521d8d4b5f3354a27babe", size = 5326287, upload-time = "2026-05-18T23:35:08.693Z" }, + { url = "https://files.pythonhosted.org/packages/af/57/3917ab0fd97f271a8694513581b8a36c655f111c446852c302f04ccdb6fc/numpy-2.4.6-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:7265a2f3d436e54ef9f2b52b5c937e6be778781bd97a590319d7348f1c1ca997", size = 6646763, upload-time = "2026-05-18T23:35:11.459Z" }, + { url = "https://files.pythonhosted.org/packages/eb/0f/037e64c494b67581ae18193d770adef354c41f3f2c8ebf865602d949bf8f/numpy-2.4.6-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f74a575920ab21fe304421a3fc28793d82e299cae9eccb37084e9fc7f3617c20", size = 15728070, upload-time = "2026-05-18T23:35:14.79Z" }, + { url = "https://files.pythonhosted.org/packages/21/a6/5d2bae9c9542eb4df16dc9c46dc79c186e9bad53805dfa5399a6023c6db0/numpy-2.4.6-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ede83e07a75dd06bc501566c1eca2afc0d61677c1472ac9ad93fdee6e638a48d", size = 16681752, upload-time = "2026-05-18T23:35:18.836Z" }, + { url = "https://files.pythonhosted.org/packages/92/14/23d1dfb410ae362cd59ce53e936b1513d545eb40db3949ced632e19a459e/numpy-2.4.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:68bb27509ac1b9a3443094260f6326150663b06abe40b73a2f81160623da5b67", size = 17086024, upload-time = "2026-05-18T23:35:22.52Z" }, + { url = "https://files.pythonhosted.org/packages/4b/6e/23595a2c642cdf3bc567877064bdd7f91c8b0038a4453cf2daf7248eafe9/numpy-2.4.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a0df0043bdb289bde1f62da130d20df23d58b45429f752bc7a8fc5325a225ecd", size = 18403398, upload-time = "2026-05-18T23:35:26.398Z" }, + { url = "https://files.pythonhosted.org/packages/8a/90/0ac3bc947217e66dec77e7cbc6a1979d1af70b6461b82f620d3bccd5e4c8/numpy-2.4.6-cp313-cp313t-win32.whl", hash = "sha256:29a287e0cf63ff528da061de6b9f64a4618da591ca1046aafc54062e40ca7eab", size = 6084971, upload-time = "2026-05-18T23:35:29.387Z" }, + { url = "https://files.pythonhosted.org/packages/77/71/5673e351671a1d2bd6063b91b44f70c0affea7d1516fa7a6572941ba4aa1/numpy-2.4.6-cp313-cp313t-win_amd64.whl", hash = "sha256:25c692919ac5a01f170a3bfcd62d745b24fd095c353d50812637d6fcab442e75", size = 12458532, upload-time = "2026-05-18T23:35:32.175Z" }, + { url = "https://files.pythonhosted.org/packages/3f/88/19d3503c5046e688f049274b27a3ef3d771152fa80d3ba3d01a3dff61abe/numpy-2.4.6-cp313-cp313t-win_arm64.whl", hash = "sha256:1e978ec1e8bd0e0e4de6bb75de9d30cbb74db6b6a2bb727618613703ca0167dd", size = 10291881, upload-time = "2026-05-18T23:35:35.465Z" }, + { url = "https://files.pythonhosted.org/packages/de/12/b422cc84439adc0d00de605bf4a308890ae5c26f2c71fbd73e5d08fbb0dd/numpy-2.4.6-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:55cced7c52e981362f708ad635198e97a752dfba412cc03c23bbf3bd8d5cd662", size = 16847511, upload-time = "2026-05-18T23:36:50.673Z" }, + { url = "https://files.pythonhosted.org/packages/44/53/f481bef68011740f8849418d82db07230e825013f31f4eef5ba5b805316a/numpy-2.4.6-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d6da64deb6b8ed903e7560180a92f2d804ee1ba5eeb849ac2748b8c1aba1f6d7", size = 14889064, upload-time = "2026-05-18T23:36:53.879Z" }, + { url = "https://files.pythonhosted.org/packages/7f/57/42ed575c10ced8af951d426bc4e1f8aff16fd851db33f067036215a7f860/numpy-2.4.6-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:68a5124b13fa6cc2086764a20005d30bc0548146f7f5322f02fce212ca14317f", size = 5394157, upload-time = "2026-05-18T23:36:57.194Z" }, + { url = "https://files.pythonhosted.org/packages/6a/ef/f66cc724fcc36c1e364c67f51ae9146090b8b584f27d58b97fdae3edd737/numpy-2.4.6-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:948424b06129ce883307e8cff868c31396d8dc7630a59c61d70d98dbe70f222c", size = 6708728, upload-time = "2026-05-18T23:36:59.575Z" }, + { url = "https://files.pythonhosted.org/packages/1a/9c/c531f2293b91265d8b48e9b329f54fdd7ffae73cb4134ea10cca4237e9cc/numpy-2.4.6-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5dbbdb29840ca3d91ee0fece42fc29278886d908280bfec0a5846c6f901a3eb0", size = 15798374, upload-time = "2026-05-18T23:37:02.674Z" }, + { url = "https://files.pythonhosted.org/packages/1a/b0/413077f6b1153ed3cba361401c6783bbad6114804a000cc22eb71c13e190/numpy-2.4.6-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8ad03c0965fb3c692200e74d458ca28c1dbb4ce96f9a479a8aa041ad5fabca02", size = 16747286, upload-time = "2026-05-18T23:37:06.327Z" }, + { url = "https://files.pythonhosted.org/packages/15/ce/e5ec180bc41812edcd8daeb8639d205622c0e8c02259d8ab25a0201b3c2a/numpy-2.4.6-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:2803abfebfc990042cd494d8ce2d5f82e9d847af6d35ec486923aa19dbad5e73", size = 12504263, upload-time = "2026-05-18T23:37:09.715Z" }, +] + +[[package]] +name = "numpy" +version = "2.5.4" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.12' and sys_platform == 'win32'", + "python_full_version >= '3.12' and sys_platform == 'emscripten'", + "python_full_version >= '3.12' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +sdist = { url = "https://files.pythonhosted.org/packages/95/b0/c7453d0b6e2073c3264468b106ee1563750cecc910965e67357e3698c83e/numpy-2.5.4.tar.gz", hash = "sha256:9a94cf751c9ad8ebaa835bcd3d40dacf8534ad086b88c38029b65123c7999d2a", size = 20866315, upload-time = "2026-10-10T20:05:31.422Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d0/97/ba2074e92b7befea137e77ea8471e768bbd87c339b7e8c9f5a931949f977/numpy-2.5.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c6342f54c67093cae5c0227eb0eb772fdb79f2a2c37a6eb278b9909ee06aa356", size = 17001609, upload-time = "2026-10-10T20:02:40.843Z" }, + { url = "https://files.pythonhosted.org/packages/ff/a9/bac826765e971d8e16e2064e9ac7525fd69b40ac17c905033a7f5442023f/numpy-2.5.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b11e8fda06a7d69f15ebf542660b74466c2e51094800c1fb794f47ad4faeef17", size = 12015718, upload-time = "2026-10-10T20:02:43.45Z" }, + { url = "https://files.pythonhosted.org/packages/31/2f/5ea3570fcb8ccd0882bea99436a513b2c85dad8f774a2057849130a8fb99/numpy-2.5.4-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:9cb18a327b49c5c337f972b03682f6a49855525faaf3c0d3e9c96cd0fd8880a8", size = 5451717, upload-time = "2026-10-10T20:02:46.169Z" }, + { url = "https://files.pythonhosted.org/packages/34/f2/b4fc1bafca03868220b5eaf729d2f21ebd7d7b151c0f9e144fe212bbca35/numpy-2.5.4-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:aec3fc4b32ff82421274f5d205c559c51c840c8df66a78efd7f3612dd005a26a", size = 6789926, upload-time = "2026-10-10T20:02:48.139Z" }, + { url = "https://files.pythonhosted.org/packages/dc/96/8319e2457ae4333c62c815c7006b869a4f60985c1e01024c2f8c6c040fe5/numpy-2.5.4-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fe4d21ab149f15e4e6043dfb0de87e6e5f34ac176cde83060e9802981fca2ac2", size = 15695312, upload-time = "2026-10-10T20:02:50.115Z" }, + { url = "https://files.pythonhosted.org/packages/43/a3/c799c62e19c337e6d3770b08e475887fb30ce8477d3c09efca6b2f0228a6/numpy-2.5.4-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fbde6962867ee75b48b0ee29b2b9372ec5d617799dbaf38e82dc0596f2f7738a", size = 16727283, upload-time = "2026-10-10T20:02:53.186Z" }, + { url = "https://files.pythonhosted.org/packages/39/6b/3604e53fb00314d0dc1b94ec9125a1484f649c0a17480b1f0f0c7a9d6250/numpy-2.5.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:381a7a3d2e65e64c0ec302795ab9dc12bb1e73f150904699c153716177eebdaf", size = 17047890, upload-time = "2026-10-10T20:02:56.038Z" }, + { url = "https://files.pythonhosted.org/packages/4a/7a/e8b58a5289a0d464c52885de47c35a935cdd70c03a4c3ab94a5126416dd0/numpy-2.5.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b89d0aaae2fe498c648f4c4795c084db535af5bd98ef942b2a3681fb74ce8645", size = 18485839, upload-time = "2026-10-10T20:02:59.018Z" }, + { url = "https://files.pythonhosted.org/packages/6f/c9/47094f597015009f310b8c900def59065ef1ff5a6fe7b51fc65ec58ec2c6/numpy-2.5.4-cp312-cp312-win32.whl", hash = "sha256:9968ab7e49b93ac6e1c3b2239732183152c9150f16308d30b66a372cffe3483c", size = 6138936, upload-time = "2026-10-10T20:03:01.626Z" }, + { url = "https://files.pythonhosted.org/packages/12/33/fefe62073dc8acfd0f2b9ed7c003af2f50aa61555e113e6db02b8f79f145/numpy-2.5.4-cp312-cp312-win_amd64.whl", hash = "sha256:a7b1b6353e36a7e50de2973a38d705c88ee93adcf120673cee7f45a4a3fa223a", size = 12573091, upload-time = "2026-10-10T20:03:04.349Z" }, + { url = "https://files.pythonhosted.org/packages/1a/07/161270b0c2eec56e4c905f6d6d22e1b836887b2cb189d3f5820aa588e9dd/numpy-2.5.4-cp312-cp312-win_arm64.whl", hash = "sha256:aa1cce2ff3f8d953de38b76bf44602caeb69f101430208f64a10067f7cb4b1d3", size = 10521630, upload-time = "2026-10-10T20:03:06.767Z" }, + { url = "https://files.pythonhosted.org/packages/67/14/1c3ee0118a8fce08565a5d8482631608426a33af10a01077fada5dc7c119/numpy-2.5.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2377da2dd3ba2c1200956acbab2a358c83b8e1f8531191672d1cd6ad83250d53", size = 16997729, upload-time = "2026-10-10T20:03:09.291Z" }, + { url = "https://files.pythonhosted.org/packages/83/8c/b0ea9477fb1f0d4484bbc5cba21678cc9969704d8d7f3f158d1db35f8e14/numpy-2.5.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7415db95818b39ec475a5eea54d9e3b6bc83e3912158e46da3438cdce399804d", size = 12009826, upload-time = "2026-10-10T20:03:11.946Z" }, + { url = "https://files.pythonhosted.org/packages/e2/84/6a3d75b3ba3dfe84ac0053450753d1e6d250a8bf80f66474cc46d1fb643f/numpy-2.5.4-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:6d6a71b9d9a97c03633aa12565ef2825ffa036cc1d99cfd50dacf0f128af4fe2", size = 5445803, upload-time = "2026-10-10T20:03:14.329Z" }, + { url = "https://files.pythonhosted.org/packages/61/18/bb993f267ca20b376e07092a16793a5b31ed3138751e9ba480011a14d742/numpy-2.5.4-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:d8200f16437b289a5bb927c6e184eccc3e8389bc0070fea4cd5b9e13c1757959", size = 6786220, upload-time = "2026-10-10T20:03:16.602Z" }, + { url = "https://files.pythonhosted.org/packages/db/b6/135bb0953b61dc21c6cafa14b424ae666944e4899cf140e00c2b322a1a45/numpy-2.5.4-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c2e71b04c6cad90026e544501bbe0ab9290fa8a4d845e7e8c0d124fb429c988", size = 15689178, upload-time = "2026-10-10T20:03:18.721Z" }, + { url = "https://files.pythonhosted.org/packages/da/24/3bd070f3269dc609d8f26b2643f62ef91bb415841c0b294805aaf7fe06da/numpy-2.5.4-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6ffa07666f8da0eef81d149934a626d0d95fbd6838432a33e66245423a9062c0", size = 16718044, upload-time = "2026-10-10T20:03:21.386Z" }, + { url = "https://files.pythonhosted.org/packages/c7/8e/9d15bd356b0a019c965312b1a3c6a727cac4cae5bc40045fbc12ce4cff9c/numpy-2.5.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2fa3328f784fc8277fc48026f6cad516f5c561c5d8e2e39b3c9e0c8f23223b34", size = 17048364, upload-time = "2026-10-10T20:03:24.468Z" }, + { url = "https://files.pythonhosted.org/packages/dc/fe/9d5b560db964f15871885f2250795d15945f8699e17ef90c0c2ff4c875b2/numpy-2.5.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b86966fbe4ad7de710422175572bcdc75fdedadfb54bc6fab7deabccddd7780b", size = 18474904, upload-time = "2026-10-10T20:03:27.895Z" }, + { url = "https://files.pythonhosted.org/packages/e9/98/d27552990f1bd611ef3e7466adadc78312ea2df63b83aad47fdc3d3ca8df/numpy-2.5.4-cp313-cp313-win32.whl", hash = "sha256:5258bc06526964be5face2fc6f756857a3f24f21ec3e72ca131337a75b165d6c", size = 6134537, upload-time = "2026-10-10T20:03:30.511Z" }, + { url = "https://files.pythonhosted.org/packages/90/8c/140a40398a66b4471211be1affdb6ed24c486d581bd28d07b7f2fcb69540/numpy-2.5.4-cp313-cp313-win_amd64.whl", hash = "sha256:8b4d2fd2d34e5f8c9235ee787de5631a37a28402b15cb80814df973d2be54129", size = 12566113, upload-time = "2026-10-10T20:03:32.612Z" }, + { url = "https://files.pythonhosted.org/packages/34/52/01d205e5e8ccb27b2b0b141e801f22b830198c979111b0fa44771438d9a9/numpy-2.5.4-cp313-cp313-win_arm64.whl", hash = "sha256:bc39ac66a7a9a3fbd6134fda43136b60ffde99c8f4501e64e0d2b24da137babf", size = 10519523, upload-time = "2026-10-10T20:03:35.163Z" }, +] + +[[package]] +name = "opencv-python" +version = "5.0.0.93" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "numpy", version = "2.5.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/79/4c/a438d23e09ce2033c09f7b784ad2fbdb0adf529e434101ed28f142226f98/opencv_python-5.0.0.93.tar.gz", hash = "sha256:66aac3e5b5faa48d4025816592f3af19e4bfc2c68dec067bae2dbb4ca10aa9e2", size = 81802749, upload-time = "2026-07-02T06:59:53.815Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9c/75/76f6ade78f6102c61034f828e2a22616708df2c9504bc8d6af9dd8f73dc5/opencv_python-5.0.0.93-cp37-abi3-macosx_13_0_arm64.whl", hash = "sha256:198a75138241810206a17c829dbcc40a7cb1841cda538ca86cbbfc6c7d95f898", size = 48322443, upload-time = "2026-07-02T05:50:25.466Z" }, + { url = "https://files.pythonhosted.org/packages/15/8c/bc1bda6aae69a32e9d84fc34153ba104cd25226861eb4aea33b2cea4860d/opencv_python-5.0.0.93-cp37-abi3-macosx_14_0_x86_64.whl", hash = "sha256:6bbc32f59e1b1a7db7b39c81f63d00625f041d333037fd8702f6da52cc39108b", size = 34782755, upload-time = "2026-07-02T05:51:30.556Z" }, + { url = "https://files.pythonhosted.org/packages/f4/8a/b04776ec45d2dea08a1b176f1829201db3515d4ed16c35f8fcc9fa7beb16/opencv_python-5.0.0.93-cp37-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e2b4272e736836f66c2d176e43ab8101f3a00d45654916399f52e150c58981ac", size = 50614064, upload-time = "2026-07-02T06:53:22.604Z" }, + { url = "https://files.pythonhosted.org/packages/95/54/eb47866b94f2b5b42dde17644b78055ef1ee05aae59962c7290e55270803/opencv_python-5.0.0.93-cp37-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f8b6d0a212253dd26ad338c812f1f23ca118fdf05a9c8c6b9444f161aa8c5881", size = 71064711, upload-time = "2026-07-02T06:54:13.148Z" }, + { url = "https://files.pythonhosted.org/packages/93/da/962579f1e703cbf8c5422fd1f576467dcb3b5b0b0b81c1471c979764353a/opencv_python-5.0.0.93-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:08d5d91d967b58d6db86073b2ad3eaef88ca4ebdfd45c9059bf59f5ded0c7ad2", size = 49798576, upload-time = "2026-07-02T06:54:33.781Z" }, + { url = "https://files.pythonhosted.org/packages/cf/4c/c73f828fdbcd37eaf21d08fa852544a3ca7c2dbb3ea76873d64f2ea413d1/opencv_python-5.0.0.93-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:c8de2dec111122a02e8beb28e16c31904992dfd6186560b142a92c71403c1039", size = 73783032, upload-time = "2026-07-02T06:55:03.415Z" }, + { url = "https://files.pythonhosted.org/packages/e2/4b/edaf83b996ca5a1a3d8ccad485706b9c6d4742b13b9c4586bf1c1e7d9423/opencv_python-5.0.0.93-cp37-abi3-win32.whl", hash = "sha256:4b4b1a34c79bf8d3738e3cfe9a9e67b51a79663f6b692cbdad8c31f570da4157", size = 35564734, upload-time = "2026-07-02T05:49:57.704Z" }, + { url = "https://files.pythonhosted.org/packages/21/f0/9fa6e85cb10c8eb36a0222d27e50fe381b86ce49a55446bf39f491727564/opencv_python-5.0.0.93-cp37-abi3-win_amd64.whl", hash = "sha256:f90ba04b8f73bc5c3814037699739f0156f597338a98f05956c684e7c3ca10d2", size = 44000345, upload-time = "2026-07-02T05:49:54.971Z" }, +] + +[[package]] +name = "packaging" +version = "26.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/fa/3944b40b07da9ce895c0e6303a5ab7d53da063554f534556b134a54d6093/packaging-26.3.tar.gz", hash = "sha256:94edc256424af38762eb31306eed28beb9f0efc50a8837492c9d6fd6004aed79", size = 313412, upload-time = "2026-08-04T18:15:28.737Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/63/34/ba1c580383c9eada3711951fef0795c80b829a078d72188184bcab9dd527/packaging-26.3-py3-none-any.whl", hash = "sha256:d7193f7c8e4e93f444fde0262bf90af30e16fa0ad0ad44cb553c87339b23cd1c", size = 129956, upload-time = "2026-08-04T18:15:27.159Z" }, +] + +[[package]] +name = "pandas" +version = "3.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "numpy", version = "2.5.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "python-dateutil" }, + { name = "tzdata", marker = "sys_platform == 'emscripten' or sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/55/b1/a10c0d1a7e3ce44eb42635771004aa503e3b9ddde0bd39c524dda65f7eff/pandas-3.1.0.tar.gz", hash = "sha256:ecb9b2d8a817b1e1ba1fdb90c0abe9eab31b4fdf2562b7a56b86ac66ace17e49", size = 5933421, upload-time = "2026-10-22T11:57:03.087Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/36/f2/d8d89ee886df6d959d7b9f483a55a0e5a1d5b034f08c32f4343a524af802/pandas-3.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d53387f8bbff4e7aa567619d4034ffdb1836db1e768a482851b904bbeeed8ef9", size = 11595013, upload-time = "2026-10-22T11:54:48.33Z" }, + { url = "https://files.pythonhosted.org/packages/ec/73/e5243378792b79d3d08f9a6782a0e23ae65cc8c6d2f476b9d2825946b071/pandas-3.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0d2ca3b380f0f6cc788780430eb23762619730c11d53989f3982ec96835bfb9e", size = 11159002, upload-time = "2026-10-22T11:54:51.011Z" }, + { url = "https://files.pythonhosted.org/packages/bf/4a/1064a6ec84ae047379a5969f6b331bd8d57013f08b30976564780fb0c9bd/pandas-3.1.0-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:388fad0c683ad061baabe23a76f43bf43c1d2c2fd46572b687082cfa195b7275", size = 11758276, upload-time = "2026-10-22T11:54:53.367Z" }, + { url = "https://files.pythonhosted.org/packages/14/41/2d264868728f7cb2add91a605bea5f2aabd3da41889ee97746a586ec34ee/pandas-3.1.0-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0a28602573f50453b57802d282f9f8cb7663aa10201127ecbbe95ed4a41c64ce", size = 12330371, upload-time = "2026-10-22T11:54:55.654Z" }, + { url = "https://files.pythonhosted.org/packages/7e/04/790ac6114ceb45575d7d7a909126210f829978f18f4f10ea8c6c847f0118/pandas-3.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e6f83e44846f9386e3593965bac017a54a5b5c611ea7b836ceab7f465bb4cb24", size = 12777723, upload-time = "2026-10-22T11:54:58.08Z" }, + { url = "https://files.pythonhosted.org/packages/56/b1/0513aaaac23bc1400b831881448665565a1b9b327d3eb7c5aca2b23c0b84/pandas-3.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ebbc0e652844e52e06d1525e5084ec1609158c086c8646aa6ccb34802102e533", size = 13411658, upload-time = "2026-10-22T11:55:00.372Z" }, + { url = "https://files.pythonhosted.org/packages/f5/1a/e33f6637445dd4b7323aa7bdaf4cc15c383805b4a2e99f958b7e32296e58/pandas-3.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:a7704e3d7367cfae71d3f8c95ee81e178c7a90bef8d6eac30df9815010722259", size = 10977787, upload-time = "2026-10-22T11:55:02.967Z" }, + { url = "https://files.pythonhosted.org/packages/2e/f7/86bd686a20a5052e15581fdc7f220098426b1982604fb545e78bdd5b5b2e/pandas-3.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:d3e660af30a6683fa57a5a2969555df117e873eaaeb418ad3b2e59249405ee70", size = 10305708, upload-time = "2026-10-22T11:55:05.046Z" }, + { url = "https://files.pythonhosted.org/packages/e9/12/b94c28870f351e9650c724e6daedf661455b348a9dca6d717c42c30fa216/pandas-3.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f4772320b7735d96dbbef437129f840dec4175f8a18a2391bd1222ed8943825c", size = 11587014, upload-time = "2026-10-22T11:55:07.208Z" }, + { url = "https://files.pythonhosted.org/packages/1b/4b/af8ca15261db52ab07f5d0c2d4c1bc28d822ea4c2a79756bc2606c012e7c/pandas-3.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cc5e4e12d287ea77bbf4c4d4bf47ed152d82a990d3a6794dee7c350f887532b5", size = 11167712, upload-time = "2026-10-22T11:55:09.523Z" }, + { url = "https://files.pythonhosted.org/packages/49/c4/cd5127751e2bef3d10f07e0d856e120916a3df0df6450f759ddb48c78b1c/pandas-3.1.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9b4e696ff70ed06e986feeec09356c491ce878d0df69a36c8e591b6a38afa29b", size = 11476964, upload-time = "2026-10-22T11:55:11.889Z" }, + { url = "https://files.pythonhosted.org/packages/c5/a6/dc89cdd95e12093bc813e9b814a09f1a056be3e1304edaef9bf9dbee108a/pandas-3.1.0-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9433b5dc2f26ef9d9b56daaf4d41d078bf4372c5d88c42ed5b397a6eea80de16", size = 12019688, upload-time = "2026-10-22T11:55:14.246Z" }, + { url = "https://files.pythonhosted.org/packages/36/87/da14aa671da792612f15847451e731d5dc0f06f6747b76b15f197ffcce13/pandas-3.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3cba20fd269808e224d6212273008173978033a1dee0f3f217bfdbd58775a72c", size = 12490149, upload-time = "2026-10-22T11:55:16.675Z" }, + { url = "https://files.pythonhosted.org/packages/5e/a0/1ce996c7354ad37983955571058ba0dde585663163ab067275197183868f/pandas-3.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:743554ed96755ad1963397ccadbe9965d92db8777ca2ae8c1fe58bd9549d7f28", size = 13090624, upload-time = "2026-10-22T11:55:18.854Z" }, + { url = "https://files.pythonhosted.org/packages/92/72/d235f82b51a22478938a3583b68e8dc05811937543adf410f34bab76004a/pandas-3.1.0-cp312-cp312-pyemscripten_2024_0_wasm32.whl", hash = "sha256:ac2f833557874a79fc0b5fac4c86e402ea6498beba8753dc29ca23d08481689a", size = 8091628, upload-time = "2026-10-22T11:55:21.285Z" }, + { url = "https://files.pythonhosted.org/packages/10/72/06aefff8fe5efc5e1ca8bfb2695b648a435358f6dc224d4e40159314181a/pandas-3.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:b8c75dbb27c7d170b25c13dd35515da4bb92d40fe8bb3bd2c69a95de2218889b", size = 10748836, upload-time = "2026-10-22T11:55:23.47Z" }, + { url = "https://files.pythonhosted.org/packages/f6/b6/4c86f659f5b14790d14e17253becc93c4e3d1511b73f0ba2184f06b164f8/pandas-3.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:73b6cea22090b7123c78ee662a5108ff36616b0bec4fe47026f86fb04a49bce7", size = 10128898, upload-time = "2026-10-22T11:55:25.995Z" }, + { url = "https://files.pythonhosted.org/packages/01/54/eb85f8b2ce121c21286842cf81abaa670ec3570173c512298f91128927a5/pandas-3.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:849d5019dead65944875ce21fbf71a2ca18e982164cd7367623cbda8567c3122", size = 11609893, upload-time = "2026-10-22T11:55:28.592Z" }, + { url = "https://files.pythonhosted.org/packages/9b/f2/b327e814327b7e7e61b5c5cceafd5c0c20f0d38e94f99ebf2dd6c1cec85b/pandas-3.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c1e084760f3b275eed5863407d025b933d25f3247515fcc84bf664405e8bb3d2", size = 11182834, upload-time = "2026-10-22T11:55:30.912Z" }, + { url = "https://files.pythonhosted.org/packages/4e/6d/2b918a45ad31d85f898567c948f11d0789ffe7fbadd6f487565e99d179b6/pandas-3.1.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c1009f5b6819b81319cf177b4f01a2823ed54c81db8bd11cdf2375f42d064c02", size = 11421478, upload-time = "2026-10-22T11:55:33.257Z" }, + { url = "https://files.pythonhosted.org/packages/4b/6f/b63e1d43eef5746014ae3f4786c72e0020e61b6b373a2abb6ebf1be3df49/pandas-3.1.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de6d0fc96e8551b83f400bf212611a0b3a2b389594d20a2540dacc61b21e7bce", size = 11991052, upload-time = "2026-10-22T11:55:35.556Z" }, + { url = "https://files.pythonhosted.org/packages/fd/43/2cf85145d822957a3f8959fd0a850e6bf687dac2823c612b3912b8888dae/pandas-3.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5611344f9f0ed51619ac7acdbf72b86f5043801a66e3f8730c8971257082e8ad", size = 12455560, upload-time = "2026-10-22T11:55:37.656Z" }, + { url = "https://files.pythonhosted.org/packages/7f/72/0cb19cea2e6afbb0854cbde0ed1345b61600e0de84eb7b73583fde3e3b55/pandas-3.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:39c67ed9e56ad3ac0727dc2ae6a1eef766988c762d5ce2e2a162613d81c8990b", size = 13045764, upload-time = "2026-10-22T11:55:39.969Z" }, + { url = "https://files.pythonhosted.org/packages/e3/5c/ea5472e8444f5871427f3a6818725684fcdce74223ca1b22aa7ca345d490/pandas-3.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:07807035ad66de0d833d68f9bee05801dbf08b88d56a233b0e9fd189372c3261", size = 10729719, upload-time = "2026-10-22T11:55:42.008Z" }, + { url = "https://files.pythonhosted.org/packages/45/f7/8cdd350c67bc62865d1e30cd7add43dd631420223487ba42dfcfd0d0e052/pandas-3.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:54fed4a50b8321ff8d413b46b7e31a023cecefd06c7210c02aa980a9836428b2", size = 10116651, upload-time = "2026-10-22T11:55:44.367Z" }, +] + +[[package]] +name = "partd" +version = "1.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "locket" }, + { name = "toolz" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b2/3a/3f06f34820a31257ddcabdfafc2672c5816be79c7e353b02c1f318daa7d4/partd-1.4.2.tar.gz", hash = "sha256:d022c33afbdc8405c226621b015e8067888173d85f7f5ecebb3cafed9a20f02c", size = 21029, upload-time = "2024-05-06T19:51:41.945Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/71/e7/40fb618334dcdf7c5a316c0e7343c5cd82d3d866edc100d98e29bc945ecd/partd-1.4.2-py3-none-any.whl", hash = "sha256:978e4ac767ec4ba5b86c6eaa52e5a2a3bc748a2ca839e8cc798f1cc6ce6efb0f", size = 18905, upload-time = "2024-05-06T19:51:39.271Z" }, +] + +[[package]] +name = "pillow" +version = "13.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ce/a4/9cc30690a394a8305e8be4c9036aa16d0dfddadd09079860b582f562496d/pillow-13.0.0.tar.gz", hash = "sha256:cdcd1ba258178f7356d74170c9a81ba4eab6a5d55748bc402a5a2a6fe6be3510", size = 47058631, upload-time = "2026-10-16T22:18:09.718Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6c/d7/af6af1b8cc55e07cb0489d5b0950bf5a9bb1461eeba825700b6d8d0e26ea/pillow-13.0.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:6f38f3f885a09cafc93fd667b1173a92f379e43785d635e7983c893f0da319fb", size = 5592324, upload-time = "2026-10-16T22:15:24.672Z" }, + { url = "https://files.pythonhosted.org/packages/0b/d2/2c12cb4fbb5d95f38d1001ff3d95fafcb830077018cba40e56a8f932dfbc/pillow-13.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:37290fbef0c73e2d51d594eb78fded7dc7170d1c3ad0a890269e6fd8c8c4e061", size = 4856298, upload-time = "2026-10-16T22:15:27.669Z" }, + { url = "https://files.pythonhosted.org/packages/5c/ee/229b27472483a4801ea94990815e710391836afdf987b4b722b62bd75db6/pillow-13.0.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7c88c10dd432f3b39ccdafc9ed81d0c21b8fd634502574da76d1bfa4fe2c76d0", size = 6517304, upload-time = "2026-10-16T22:15:29.43Z" }, + { url = "https://files.pythonhosted.org/packages/36/46/f509b74787f40ce0627501089d630b2c741b63b244c1f40b640b3b01e181/pillow-13.0.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a625df9a2a5597fa5e5266e01aefc82604bb85d291e3547981e863560d75da6c", size = 7140667, upload-time = "2026-10-16T22:15:31.368Z" }, + { url = "https://files.pythonhosted.org/packages/aa/a4/5a398b8ec2c37145397d6461c0b551c23ec03072aa64f357aed25e582b00/pillow-13.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:10b5019883c98f1dd819ca48ffc6f33a91cee04948fe637c605f4345c7cafd6f", size = 6610583, upload-time = "2026-10-16T22:15:34.025Z" }, + { url = "https://files.pythonhosted.org/packages/4f/7b/f2f1109b230fd4e2d57ab2305de31adc37ea9b4ff8719436c623cb484415/pillow-13.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f391dc92a015c478745192fe4f2e120c2af2aba18d9fe0616341cbf537599806", size = 7218241, upload-time = "2026-10-16T22:15:36.214Z" }, + { url = "https://files.pythonhosted.org/packages/ab/d3/069edb9083b267746baf59756282cacef904d3475c5288c7216fb5195a98/pillow-13.0.0-cp311-cp311-win32.whl", hash = "sha256:6a0920cb55df5a4d3f6862964908078c0bd59332bdd1bcd20d14e8c70ef27cc5", size = 6515278, upload-time = "2026-10-16T22:15:38.129Z" }, + { url = "https://files.pythonhosted.org/packages/7d/f6/6e2d96413e4c9dbec12ea584d9e646d602877d4cce3ea669099f959deac6/pillow-13.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:d7710eb676e9bb68965cb045be0d333b4c0d7f9dfbaa7fbf121385b1588683d3", size = 7278471, upload-time = "2026-10-16T22:15:40.286Z" }, + { url = "https://files.pythonhosted.org/packages/70/e7/baff21f8cb60496c8c250609501b88919eeedb4088d3da106f6c62f3f3c0/pillow-13.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:12fbb851cf323cc19aac3eea1df5ad17b57e91e9fe3cfd06100dba7bb8208c13", size = 2698981, upload-time = "2026-10-16T22:15:42.637Z" }, + { url = "https://files.pythonhosted.org/packages/7a/b8/58d79332229482eb0d27c82973850808c19d37e5bdc14443d3417bd373f5/pillow-13.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:67c8d44ad89fa7eb88de5d6ef6199b1e03dd24974f85a67639fc8badb801fe79", size = 5568643, upload-time = "2026-10-16T22:15:44.474Z" }, + { url = "https://files.pythonhosted.org/packages/39/4b/f2ed53d83aa44ac19599ac0a54897f867fafb95f406980d1e24083f8cbe3/pillow-13.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc481f742ccda20e9ed760b8f978dff50f2a4e9bc253ac167fb0a05b0783ef52", size = 4852329, upload-time = "2026-10-16T22:15:46.284Z" }, + { url = "https://files.pythonhosted.org/packages/3b/bb/9e30e8ed18c20800ee327a12107d5249ad8dc444867b3adfcc21654e3c71/pillow-13.0.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8bf295fe459343bc6093c8e5e0df60bfa189e0fcb775a22e2c2488fdeccc6c0d", size = 6519309, upload-time = "2026-10-16T22:15:48.14Z" }, + { url = "https://files.pythonhosted.org/packages/e6/99/fdf65cc539e266ab93546c261bdf98411fe165c6e4e13f9dbf597097192b/pillow-13.0.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:42d63be3af4f9b951b199b0a9d23365d37ab14687e6d77b74940432c8515c4ca", size = 7144542, upload-time = "2026-10-16T22:15:50.093Z" }, + { url = "https://files.pythonhosted.org/packages/26/9b/f38b7c4d8f599537e2d1a6d546cec9beef7536ff8f5c49a3f9c78179d8ac/pillow-13.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4bd024d567eca931b668978d672dba914d4b8e2628e2c3073a2ad178de8a2620", size = 6618042, upload-time = "2026-10-16T22:15:52.054Z" }, + { url = "https://files.pythonhosted.org/packages/a8/7c/45ee28531f89ee4582967d8a17f7a26d71ca64cd902b93c269590cd60a00/pillow-13.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:23edf4ec4b6f4f240e8df6e4985e9ee2ced86474632433e55fb1e2548744bf40", size = 7228137, upload-time = "2026-10-16T22:15:54.009Z" }, + { url = "https://files.pythonhosted.org/packages/01/bc/9c2a969cb1b70201d04fa17fd5aedf02a732870b8da86bceaaa30b0ca385/pillow-13.0.0-cp312-cp312-win32.whl", hash = "sha256:02464da86f561f410e13b061ec37c3068a9cb557a095494157742d07b3c24a27", size = 6515423, upload-time = "2026-10-16T22:15:56.25Z" }, + { url = "https://files.pythonhosted.org/packages/ff/cd/73a79ecdc822076002eb24b5e1e4a530b0bb5310fcb6d239abbec877110b/pillow-13.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:ff3fe9eb621fbb9db4c4be33ab946f6bac5f98ab50ca8d3260503653656c961b", size = 7272166, upload-time = "2026-10-16T22:15:58.597Z" }, + { url = "https://files.pythonhosted.org/packages/4a/a3/be5a1236efd085b3984e4305da8f179ae9dab80c991d2ad78f2bd5c4c843/pillow-13.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:c3bc8ee0271387e0b1e3ad0ff893645990a656e79bf9c42f4f734a3642b11d51", size = 2698943, upload-time = "2026-10-16T22:16:00.637Z" }, + { url = "https://files.pythonhosted.org/packages/82/e2/9132d38df320e8099e2642d9fa538f18a7dfebf68a875a31725dc2a115aa/pillow-13.0.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:e98b4f91f99b24b1da72e10621ba181f7281922384a7403094debb4199de6c00", size = 4190620, upload-time = "2026-10-16T22:16:02.527Z" }, + { url = "https://files.pythonhosted.org/packages/cb/be/436dac0996876f65ef1916ffb711c5ef4af2ebc57414b6f16642e07f6b47/pillow-13.0.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:34fb3db1149c879343861831728c243c432d4a5afc8f1878ca7612ef8de2a033", size = 4290286, upload-time = "2026-10-16T22:16:04.861Z" }, + { url = "https://files.pythonhosted.org/packages/60/16/7c1007162e4169ea0d5bec47e2e0b2174f56c50cf5b8c02a1dc23e41037e/pillow-13.0.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:2d50500a4a3bc6469adcdb319cf05d688b08ca3bf0f22a05339f3b25b433a599", size = 3723169, upload-time = "2026-10-16T22:16:06.956Z" }, + { url = "https://files.pythonhosted.org/packages/08/bc/578ab2eaa04705a8978bd9be0fe15c3d43a8fc95097c4f7e39540e79e4fa/pillow-13.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ef5fe6b7db3f501c56000ba8fe7c7e1a35ca1d45824fbeb95a67daa85cff4dbe", size = 5568469, upload-time = "2026-10-16T22:16:08.664Z" }, + { url = "https://files.pythonhosted.org/packages/98/c8/ccede3dcc4f274b80fb49199eed1ef34ba05566c8b71976b9d1f111da2e3/pillow-13.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f11a3c1358bb3c68012a7b1f41b223c3db9f7bfde90b481df028cfe1c3363313", size = 4852187, upload-time = "2026-10-16T22:16:10.693Z" }, + { url = "https://files.pythonhosted.org/packages/fb/3c/cd0770e96d20bfd990db024bcbd43897ff3e88da1f4782cf90ea231b1d4f/pillow-13.0.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:19b66ca0a4d25cc0200b381ce824c7ea090db61427315e73cfb05dbf1b35c369", size = 6515908, upload-time = "2026-10-16T22:16:12.896Z" }, + { url = "https://files.pythonhosted.org/packages/ef/fe/f5cdaa2d1c4086e757430f368c271a58ee29cfe7378e7ae7b9d964015c25/pillow-13.0.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5f56499c1a74d2b862beb0c33b8016f99d095858455df9d21583bbbc1c15b582", size = 7142544, upload-time = "2026-10-16T22:16:14.844Z" }, + { url = "https://files.pythonhosted.org/packages/fb/b5/a54f6d44adbcd23737c8c12953f42be107e4cca64e247c0ca3a5b563061c/pillow-13.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a2a14cd73d5e6c927f644b0110ee81f59c4ef506259ec9183904747f2caacc81", size = 6613097, upload-time = "2026-10-16T22:16:16.893Z" }, + { url = "https://files.pythonhosted.org/packages/e6/b4/9e6c1207c21b2e91db0d15c16d30f3701ce5add1bd4da0e787761e66cb6e/pillow-13.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:08b826a9bf1265c76de04a42061d61ea6b109418b7826f28ff1ecdfaa26c5502", size = 7224846, upload-time = "2026-10-16T22:16:19.195Z" }, + { url = "https://files.pythonhosted.org/packages/78/9f/d3c3cb0518f3dd1eee0ef501535f53d15597a53c5c4118d178086143397a/pillow-13.0.0-cp313-cp313-win32.whl", hash = "sha256:12b0fa15879fe5a9d9eebe66e4e2d74128a773b1a4448010c9c714f45e04b9d8", size = 6516013, upload-time = "2026-10-16T22:16:21.389Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ed/1b16fb66a37e1b9e41370b93b2c614c7da761a5b2a41dc333f7af4639962/pillow-13.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:9dda0788a9932776a75d8bcc020076edc308ce2508b51f6739a45e3158b3fc42", size = 7284777, upload-time = "2026-10-16T22:16:23.393Z" }, + { url = "https://files.pythonhosted.org/packages/87/7d/8d02eba9f7ecc87f8e90f731e032cc4773802f2a843763afd0c66a535ff4/pillow-13.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:40a266371ece581e5de13b6635ef78d7487824af80561b6ca27694a88c19bc0f", size = 2698826, upload-time = "2026-10-16T22:16:25.382Z" }, + { url = "https://files.pythonhosted.org/packages/68/c0/fb7b1caf25ed5ac5d46116f09b8b0706b9559833c65c667cecb1a53153f2/pillow-13.0.0-pp311-pypy311_pp80-macosx_10_15_x86_64.whl", hash = "sha256:405be6058e206a86fedce2f715ecc2343f94072d7925251632dffa2c05f70dca", size = 5521630, upload-time = "2026-10-16T22:17:50.008Z" }, + { url = "https://files.pythonhosted.org/packages/1a/e6/4d11d5b1137f4fc080605cce410cfaae4f303ad400bdec75d27d3083fa29/pillow-13.0.0-pp311-pypy311_pp80-macosx_11_0_arm64.whl", hash = "sha256:b84be41387fc00db28c13c02e30d42b02d8d8f6a4a741a1aee6012ca7f1f6039", size = 4806023, upload-time = "2026-10-16T22:17:51.932Z" }, + { url = "https://files.pythonhosted.org/packages/f1/45/0923202a658e8ff6aafd72c1a4395ebadcdebaa182c437d8595d3dd3db32/pillow-13.0.0-pp311-pypy311_pp80-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a192360d512305fe92d3053e4f7627bbad1f7708f1f4ad38cccd9336acabe9ad", size = 5464831, upload-time = "2026-10-16T22:17:53.742Z" }, + { url = "https://files.pythonhosted.org/packages/07/52/d3e38f0cad1dadea2a658b9a7d6ba8a04048f5ae14f011b806e5c3bd6bcb/pillow-13.0.0-pp311-pypy311_pp80-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:83b37abd87b590acd8f47ca8a61f70893322b11834d36e07c2298dacad4d14d2", size = 6043133, upload-time = "2026-10-16T22:17:55.625Z" }, + { url = "https://files.pythonhosted.org/packages/c4/f9/2ed7685980c1d64f8131a7d87a19c369424747577152622b73ddeff163b7/pillow-13.0.0-pp311-pypy311_pp80-win_amd64.whl", hash = "sha256:73ee25cf8cf8b75d5a5aeed7cb6588f5e5dbfbb0fde51707b630bbb23897645c", size = 7365208, upload-time = "2026-10-16T22:17:57.509Z" }, + { url = "https://files.pythonhosted.org/packages/60/da/f6d644e631c5b370c5d0b899f3ccadd2780aab070b29a110527cf4e1fd2c/pillow-13.0.0-pp312-pypy312_pp80-macosx_10_15_x86_64.whl", hash = "sha256:8619685cad8efc4c38c66d3b36aad854523297c43d748b3b6f0cc03c90ba3463", size = 5523188, upload-time = "2026-10-16T22:17:59.334Z" }, + { url = "https://files.pythonhosted.org/packages/e4/b0/2594f202d743a35d4fe80682bfd96f277e5ca7aecb27cf64ef5bfc1d000b/pillow-13.0.0-pp312-pypy312_pp80-macosx_11_0_arm64.whl", hash = "sha256:7e91e97a929232cdb828889ee6ad0e48d7f40f4ec38818f4e4a8968b2b1af22e", size = 4806808, upload-time = "2026-10-16T22:18:01.188Z" }, + { url = "https://files.pythonhosted.org/packages/8e/ed/d504165ef79af6fafe25c76726cf30f544cc236002f7ed054f1d51adc4ff/pillow-13.0.0-pp312-pypy312_pp80-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:93ff9c9c3663ce23aaa5d0b6b6abac55359e2a0c06c7e30f3771829af816be63", size = 5464358, upload-time = "2026-10-16T22:18:02.779Z" }, + { url = "https://files.pythonhosted.org/packages/7b/fe/a10aba5503ca13e9e009b8bd0a1a4ce5584bdce246245fd6f3a83701369c/pillow-13.0.0-pp312-pypy312_pp80-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e65dd6e75de67b2362a4fa3e16aa627595f65054dc4ec73d64680c84d592fa61", size = 6043514, upload-time = "2026-10-16T22:18:04.538Z" }, + { url = "https://files.pythonhosted.org/packages/c0/37/66b12dc476041fe523bca29c9d58fbf1ea92a4eaf73acfb9e993948f1191/pillow-13.0.0-pp312-pypy312_pp80-win_amd64.whl", hash = "sha256:567f20f1c75897d7663830da6ea8af719e03d712b1d9ee7f813319f9ece42e5a", size = 7366030, upload-time = "2026-10-16T22:18:06.638Z" }, +] + +[[package]] +name = "pip" +version = "26.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/53/c7/f8d9e2e0e7c1ed8a22a10eeb3b5bf7860bc07d8446c7a978a882c5c8214b/pip-26.3.tar.gz", hash = "sha256:6c36ae7da8695be67e3cd9f7d87919667ced53f57ce2ede75d89b5912ed2bc4c", size = 1934348, upload-time = "2026-11-02T20:34:27.738Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/07/25/ee8c38ae72d66b844ce8eb9711ba905dba21ca1a8a0c960057c41cd23337/pip-26.3-py3-none-any.whl", hash = "sha256:f3b60b20ffaadd4415ed7957212a38ac63fa4c5fa375bd0f4a797777940fad13", size = 1950951, upload-time = "2026-11-02T20:34:25.728Z" }, +] + +[[package]] +name = "platformdirs" +version = "4.14.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9a/6e/886dc7c9d21e2d95aceb696f1e31271742033aeef3661f6e9c8921493592/platformdirs-4.14.3.tar.gz", hash = "sha256:021add1ab113483edbcd4694f5fbb2d688b893ea30d35e1155bb371cf4be0be8", size = 71124, upload-time = "2026-11-01T16:27:32.928Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/93/41/f0aa003909ee77a9e7e55f8659abb635ca23ab0d022a449bbb18da87a1ef/platformdirs-4.14.3-py3-none-any.whl", hash = "sha256:cba2d05fe5e18b26b1fa6f376b8e6234c012abe1f8696c486fface7d0054e931", size = 35276, upload-time = "2026-11-01T16:27:31.498Z" }, +] + +[[package]] +name = "pluggy" +version = "1.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bf/db/7fc19e6f2dc92a966727031389fc2e08b558f0f25eb7403c1119ad4713cd/pluggy-1.7.0.tar.gz", hash = "sha256:d1eaa46ebb595891b860ab086b4d09c8588af65ebd4361b8e8f4bb8920b90ba8", size = 123304, upload-time = "2026-10-15T09:50:58.343Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/40/9e/2b38731e0fc536806f16490e1a12d7f0dc2a1235aa8cc07bcc75416a7daa/pluggy-1.7.0-py3-none-any.whl", hash = "sha256:7dd7b0d8832ba3cb632c306926ded123429211b83641b35dc5c41ad2d34f9bec", size = 27082, upload-time = "2026-10-15T09:50:56.808Z" }, +] + +[[package]] +name = "pre-commit" +version = "4.7.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cfgv" }, + { name = "identify" }, + { name = "nodeenv" }, + { name = "pyyaml" }, + { name = "virtualenv" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3a/c3/6965c2690ddce885ec75121572d065a5b94c5a5d8d3e657927ecda9fd715/pre_commit-4.7.0.tar.gz", hash = "sha256:1f3deee12b914c8fa184bd2b5953391c2e4f36763e403d4a23dc9983fa7c6e86", size = 198120, upload-time = "2026-10-12T20:45:40.32Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/eb/83/ce0a196e4f7b20b11ce3aa1fc72fb973f64b7628d51c1b8aebad37fb7f81/pre_commit-4.7.0-py2.py3-none-any.whl", hash = "sha256:2e229038ad3656081b70c27913157e4b636ef306f643f024a8a8e3f0f7b2f877", size = 226261, upload-time = "2026-10-12T20:45:38.858Z" }, +] + +[[package]] +name = "pyfftw" +version = "0.15.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "numpy", version = "2.5.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f2/2d/e38439b7f937e8bf91a9ff2b8d9713d0d8e64e980fc00e8d1945b8a5b74b/pyfftw-0.15.1.tar.gz", hash = "sha256:bbcde6d40d165e1cbaf12dde062ebfebe9e43394cac8c166e699ba2c9a4b0461", size = 192838, upload-time = "2025-10-22T19:58:56.683Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/20/c0/4f1a586eed5eba86c27d6fab4ebed44bbfeedd5209e835c19c6406f96a41/pyfftw-0.15.1-cp311-cp311-macosx_13_0_x86_64.whl", hash = "sha256:fb5283238be3c797b7b91cbb1d549787b9d0495a7d48b828832a3c94fa557f48", size = 3297881, upload-time = "2025-10-22T19:58:20.945Z" }, + { url = "https://files.pythonhosted.org/packages/b2/c1/02eaaf7a2f7ca26d7b56b6195617979b44b9213f3dcc1a514663a3bbc264/pyfftw-0.15.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:f4b42864e95f128a676e27a94025cce074d807a81d48ed423c65aa7403a7729a", size = 1682075, upload-time = "2025-10-22T19:58:22.236Z" }, + { url = "https://files.pythonhosted.org/packages/c6/23/e3692116f6e903d1d9705d1f948f94ee133512a0b388d05ea0f8adcb0c07/pyfftw-0.15.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2e728dbe262182089a6606b7feb5c67bc55b8b385b0c1b60f71d6a891b5f142a", size = 2557646, upload-time = "2025-10-22T19:58:23.345Z" }, + { url = "https://files.pythonhosted.org/packages/7a/49/affa553caa93928d2f7d814f26939487d37d4068a0ba4d8238c6ba3000b1/pyfftw-0.15.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:294eecf05577b0198e6d041376e016254c4b54040b8a7a330d9d6321b8b30472", size = 3158494, upload-time = "2025-10-22T19:58:24.63Z" }, + { url = "https://files.pythonhosted.org/packages/88/92/bde713b499fa2062f30f396603bbf09acd0c9c974249638202c931433498/pyfftw-0.15.1-cp311-cp311-win32.whl", hash = "sha256:ed42033aa729520c66500027157525981613a139c2fd4671dd7d52d58cb4b820", size = 2226331, upload-time = "2025-10-22T19:58:26.345Z" }, + { url = "https://files.pythonhosted.org/packages/25/f4/e0583a68e1114b108354207ce0680b898a4b37c48bd3a8cfc1fd0ec1a476/pyfftw-0.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:e32a30c34f27c24ee602b89b22d9cd453570bf851e7d8f98506185cb5e99ccfc", size = 2634855, upload-time = "2025-10-22T19:58:27.839Z" }, + { url = "https://files.pythonhosted.org/packages/4f/76/5673561606f45c60ab3da497deead308e2c4b59c2b705334a01b880f0421/pyfftw-0.15.1-cp312-cp312-macosx_13_0_x86_64.whl", hash = "sha256:074926b5fa6a2193771cb4dc5beaa52ea1d629dc40da363d7de7918df5f3e951", size = 3315083, upload-time = "2025-10-22T19:58:28.981Z" }, + { url = "https://files.pythonhosted.org/packages/2d/fb/5042ad5c4ddc2db89844fc853b6358d36497e44f9d32f3aef9c3fa393182/pyfftw-0.15.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:2156d7bc72a2e2a1c747767909b1a7ba2bcfde14a51fca3171d7c1f8de6c2f02", size = 1698218, upload-time = "2025-10-22T19:58:30.536Z" }, + { url = "https://files.pythonhosted.org/packages/df/1c/7b76808da67a3775480f64e93acc0a623aad7682ba83428311de05ebc67a/pyfftw-0.15.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:313340683d5e99b0a36e45b8c2bd92cd86dfbb18917a62e89890927a66bed9e9", size = 2562434, upload-time = "2025-10-22T19:58:31.531Z" }, + { url = "https://files.pythonhosted.org/packages/fb/43/43fbc63ec6790ce6346e6441e82b54356334641d9d6f099f1ed75d302f2a/pyfftw-0.15.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:900d79c7fa1b27b58ec4240f7cb6d512492a9db9bc8cbd18076ed28c84a63b62", size = 3167162, upload-time = "2025-10-22T19:58:32.636Z" }, + { url = "https://files.pythonhosted.org/packages/e4/e1/82d4967ef4ccd8c17e58a9a34fa283436de98c32c08aac4191ef56bf9989/pyfftw-0.15.1-cp312-cp312-win32.whl", hash = "sha256:9c94a6d251f2ceb9d6bb86964c43f9eb9cbd8612a60f41b10c8a64e816f6a2ed", size = 2222298, upload-time = "2025-10-22T19:58:33.795Z" }, + { url = "https://files.pythonhosted.org/packages/ab/c0/3f4513921fd3cb40986bc6fe4a33e87c5aef671edff483db6f7c4a5f8309/pyfftw-0.15.1-cp312-cp312-win_amd64.whl", hash = "sha256:74fe153d6bec682cd85b5601df09cb84ce6e3cc901172a3ce86da7544e457e4c", size = 2633571, upload-time = "2025-10-22T19:58:35.185Z" }, + { url = "https://files.pythonhosted.org/packages/c2/f5/5dca09b863e71db438069eb1990526a4409deebc190228177dec3b7cc636/pyfftw-0.15.1-cp313-cp313-macosx_13_0_x86_64.whl", hash = "sha256:a16c4eb45ff5277f8bcb979d43a6a0d7f2e7405dcad984dc45a30064ed487da9", size = 3315212, upload-time = "2025-10-22T19:58:36.273Z" }, + { url = "https://files.pythonhosted.org/packages/56/c1/1ff9aa930f98c77bbff9cae122e496f1fd7201abac309f1dbcc9fbe5c7ee/pyfftw-0.15.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:2b4bad0184546e3129eeda9d07541eca71232f6e431d57c734930d25f06386e8", size = 1698270, upload-time = "2025-10-22T19:58:37.684Z" }, + { url = "https://files.pythonhosted.org/packages/9a/3f/e94d5773429dc076ec4b47d029603de1728a5037024ed3dbcc853cfa72e9/pyfftw-0.15.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1716378d1d102527917b872a5baf15e2e3de52d5200ccc22128816cd71c33148", size = 2559574, upload-time = "2025-10-22T19:58:38.662Z" }, + { url = "https://files.pythonhosted.org/packages/cb/64/5f773d61cca91a1a741e2403d7191610513d0cca3fc73a41a6941a31fe89/pyfftw-0.15.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:16209de9a244ab7b3337e8ce8d528420ffd05881e7d19e5be21d2546a7e5b2c1", size = 3166906, upload-time = "2025-10-22T19:58:39.986Z" }, + { url = "https://files.pythonhosted.org/packages/c4/8a/dda252557803fde20be41396425a85faf6680eaeed3996e4b765745e9206/pyfftw-0.15.1-cp313-cp313-win32.whl", hash = "sha256:db2a65c59d7a707c55a8f0f38be3916b907429ceed316c6875201b202e22ac99", size = 2222209, upload-time = "2025-10-22T19:58:41.237Z" }, + { url = "https://files.pythonhosted.org/packages/20/6b/9236a2fb77b01b00b957304d53694de2bf034caac89c1d35ab7fa3421fd9/pyfftw-0.15.1-cp313-cp313-win_amd64.whl", hash = "sha256:dcac51a5a4b8c6600bb2c7edb86d9739860a61bb0b076e20fbf0340919da307d", size = 2633673, upload-time = "2025-10-22T19:58:42.709Z" }, + { url = "https://files.pythonhosted.org/packages/88/45/a34cd015d16002e748de0e218d24ecc2f30eb9ad073f1c7941a51a796a2e/pyfftw-0.15.1-pp311-pypy311_pp73-macosx_13_0_x86_64.whl", hash = "sha256:41a25b721e79378a3ea29d21d2e6cf25e43c1f44bc0bbf554beccdcdb2b8064e", size = 3278795, upload-time = "2025-10-22T19:58:52.212Z" }, + { url = "https://files.pythonhosted.org/packages/15/27/0e6195c9c5eeef73a7499772621057aff5ffd04ebb52a71d49cc049a3dad/pyfftw-0.15.1-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5e957f5caa7ccc032d17831ce336cb61b93f516ed7da25ea6c9e1086ed2e323f", size = 2570541, upload-time = "2025-10-22T19:58:53.357Z" }, + { url = "https://files.pythonhosted.org/packages/2e/a9/f1d00acce882645198576a94c4b7fff5465f0bb6c4b7bb1005314527c2d0/pyfftw-0.15.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:763cd58e858133493db371ed3a63e9e41463cd40965e2375a079947c2d96805f", size = 2625398, upload-time = "2025-10-22T19:58:54.893Z" }, +] + +[[package]] +name = "pygments" +version = "2.21.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/49/2e/ced460408999b33da6b31b0021b0f37d329e202d4169aeb164493778f25b/pygments-2.21.0.tar.gz", hash = "sha256:610ca751c9bc2492b38eb9a38a7fbc93edbbb2d7182edaf34e66ae493dee5c8c", size = 5005329, upload-time = "2026-08-17T08:02:48.824Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/71/46/17f022dd3e953bf20a04a028a21ec746d942f8d2af30fa0f124fa0e6a684/pygments-2.21.0-py3-none-any.whl", hash = "sha256:2363c69b61c4a97c838da3b130dcd6468f4848992b21a82f2a63ec34377137d9", size = 1250147, upload-time = "2026-08-17T08:02:44.912Z" }, +] + +[[package]] +name = "pygrib" +version = "2.1.8" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "numpy", version = "2.5.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "packaging" }, + { name = "pyproj", version = "3.7.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "pyproj", version = "3.8.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/dc/e9/29ebe5a47274afcfb37b17be9097b24cf2ff5640672cb06db12ff9a46346/pygrib-2.1.8.tar.gz", hash = "sha256:7ad941571c02b9afa685cb92fb85fb5ed2cd0e36c4ec40cb74d7782dc7975400", size = 21864193, upload-time = "2025-10-26T18:06:02.849Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/36/59/1b0d11c95b21df7092844cab2a7d799090d7e3853d100dce1c2dbd289b08/pygrib-2.1.8-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:916906610809bfd58b066dd42f4585125ae393f1b95794ac0d84bd4b4d953f7a", size = 20163338, upload-time = "2025-10-26T18:04:25.606Z" }, + { url = "https://files.pythonhosted.org/packages/9a/e8/1a7431df26112dad8e2989342effb16ea24469734307504b885e35726a36/pygrib-2.1.8-cp311-cp311-macosx_15_0_x86_64.whl", hash = "sha256:64741d7fca01af894929428d01d7e85ef2e95c4a6b4163862c6fe83489763ad5", size = 20277734, upload-time = "2025-10-26T18:04:33.499Z" }, + { url = "https://files.pythonhosted.org/packages/a7/1f/ec6b8f44a13034d82b0998ffd9c6b4b06515e29fbb046ea6f3da30237bc5/pygrib-2.1.8-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:baf604dc82a6413f03c88e5c1a2722c3d3bccb0e74a9ba36592ebabac9ee785d", size = 17726322, upload-time = "2025-10-26T18:04:40.248Z" }, + { url = "https://files.pythonhosted.org/packages/cf/06/7a420b8e5f7b8d3385e11d4f4d0d01827f01578ec49733c785826f94dd25/pygrib-2.1.8-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:93ebc99c4b998fd463c6ee75fb64d69f366768cf757b0db27552c740696df0b4", size = 17794642, upload-time = "2025-10-26T18:04:47.798Z" }, + { url = "https://files.pythonhosted.org/packages/5e/15/25e2a0399065c0c637001d5c4ef8b885ca2bd402a4d961c49c28de85b4a1/pygrib-2.1.8-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:7dc0c267609395e9a5135f6617c8e689c10055303d4f745d9b591b6180377f8a", size = 20200529, upload-time = "2025-10-26T18:04:54.287Z" }, + { url = "https://files.pythonhosted.org/packages/96/44/c95bed59300722a4d135d5692a382f3347bc1db0cbd6945436975ed64487/pygrib-2.1.8-cp312-cp312-macosx_15_0_x86_64.whl", hash = "sha256:18abee3b3708346c2ef4f77db23edeb6142fb35b0b596815d7614550388aca76", size = 20310657, upload-time = "2025-10-26T18:05:00.461Z" }, + { url = "https://files.pythonhosted.org/packages/f4/eb/ca92e6960f513e9ecaba17ba28ea15767a88e133fe0f4fd1ff083d185700/pygrib-2.1.8-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d3ab748efdc97eb2a4b0ab752e2ee4c1d4bc2b24b82eb02640917b7f7a188d5a", size = 17776009, upload-time = "2025-10-26T18:05:05.498Z" }, + { url = "https://files.pythonhosted.org/packages/0f/fd/ce5f152d3a5ac223b5cfb59df79907193d8333ec97b9eb00d4fff5303e2d/pygrib-2.1.8-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:12bb2dade46f8cc755e67216e867f8959ac0170afef2f78913930f68c5a97e82", size = 17843263, upload-time = "2025-10-26T18:05:10.796Z" }, + { url = "https://files.pythonhosted.org/packages/db/29/9bf1e08c3bea6321969b6ca5feacbddd052e519044c167b34417f4c7d2fa/pygrib-2.1.8-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:3693da30a0b1e997c8bda7a7a4a2dfc3c5adadb5ecb54b4c10c95caa4aeb443c", size = 20200423, upload-time = "2025-10-26T18:05:15.988Z" }, + { url = "https://files.pythonhosted.org/packages/02/3d/59b5340cca7bf4074da4e9b2954572a626c5a3eb8bde0414190b10a6a55a/pygrib-2.1.8-cp313-cp313-macosx_15_0_x86_64.whl", hash = "sha256:35e93d3a5df20ba13b59cc1631e395ec37f15a66b6aafd7edd95cfc163e234d4", size = 20310527, upload-time = "2025-10-26T18:05:21.451Z" }, + { url = "https://files.pythonhosted.org/packages/45/f4/67095c134991d44a5361e6f07e38f162839715e7940b0a399b7e7b860e00/pygrib-2.1.8-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f16d805abe78b3fd1de29a2f1d26849df85c24b095975af7fb5685f330594b7f", size = 17775832, upload-time = "2025-10-26T18:05:27.201Z" }, + { url = "https://files.pythonhosted.org/packages/de/e5/e20c1cb440d80716dd12522bcd617e2b60d160465b6da15ed3c827478d7f/pygrib-2.1.8-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2509ecb335bd49f584d5671f4b6fb6c7e42bd8e5db3c260c4af10a97d7b6a0c8", size = 17843058, upload-time = "2025-10-26T18:05:33.418Z" }, +] + +[[package]] +name = "pyparsing" +version = "3.3.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e4/11/b213bebff182584360cb8d17c72c1677fec5c5c228de439e63bcf8ab1c8f/pyparsing-3.3.3.tar.gz", hash = "sha256:928ae7e20211f3b6f3915a72f06a0cfd29ab9d24279dd6346b6b1a7146397d36", size = 1050487, upload-time = "2026-09-20T20:59:05.609Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/38/bb/d215ee7c73b61497b28a5503f9f53523f294fcc936762b7caf90e0c1c2b5/pyparsing-3.3.3-py3-none-any.whl", hash = "sha256:ece8c00a69cf01b45d0b1dedabb469c90d8caf996d4fda40f147627a122849a4", size = 126420, upload-time = "2026-09-20T20:59:04.025Z" }, +] + +[[package]] +name = "pyproj" +version = "3.7.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.12' and sys_platform == 'win32'", + "python_full_version < '3.12' and sys_platform == 'emscripten'", + "python_full_version < '3.12' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +dependencies = [ + { name = "certifi" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/04/90/67bd7260b4ea9b8b20b4f58afef6c223ecb3abf368eb4ec5bc2cdef81b49/pyproj-3.7.2.tar.gz", hash = "sha256:39a0cf1ecc7e282d1d30f36594ebd55c9fae1fda8a2622cee5d100430628f88c", size = 226279, upload-time = "2025-08-14T12:05:42.18Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a6/bd/f205552cd1713b08f93b09e39a3ec99edef0b3ebbbca67b486fdf1abe2de/pyproj-3.7.2-cp311-cp311-macosx_13_0_x86_64.whl", hash = "sha256:2514d61f24c4e0bb9913e2c51487ecdaeca5f8748d8313c933693416ca41d4d5", size = 6227022, upload-time = "2025-08-14T12:03:51.474Z" }, + { url = "https://files.pythonhosted.org/packages/75/4c/9a937e659b8b418ab573c6d340d27e68716928953273e0837e7922fcac34/pyproj-3.7.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:8693ca3892d82e70de077701ee76dd13d7bca4ae1c9d1e739d72004df015923a", size = 4625810, upload-time = "2025-08-14T12:03:53.808Z" }, + { url = "https://files.pythonhosted.org/packages/c0/7d/a9f41e814dc4d1dc54e95b2ccaf0b3ebe3eb18b1740df05fe334724c3d89/pyproj-3.7.2-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:5e26484d80fea56273ed1555abaea161e9661d81a6c07815d54b8e883d4ceb25", size = 9638694, upload-time = "2025-08-14T12:03:55.669Z" }, + { url = "https://files.pythonhosted.org/packages/ad/ab/9bdb4a6216b712a1f9aab1c0fcbee5d3726f34a366f29c3e8c08a78d6b70/pyproj-3.7.2-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:281cb92847814e8018010c48b4069ff858a30236638631c1a91dd7bfa68f8a8a", size = 9493977, upload-time = "2025-08-14T12:03:57.937Z" }, + { url = "https://files.pythonhosted.org/packages/c9/db/2db75b1b6190f1137b1c4e8ef6a22e1c338e46320f6329bfac819143e063/pyproj-3.7.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9c8577f0b7bb09118ec2e57e3babdc977127dd66326d6c5d755c76b063e6d9dc", size = 10841151, upload-time = "2025-08-14T12:04:00.271Z" }, + { url = "https://files.pythonhosted.org/packages/89/f7/989643394ba23a286e9b7b3f09981496172f9e0d4512457ffea7dc47ffc7/pyproj-3.7.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a23f59904fac3a5e7364b3aa44d288234af267ca041adb2c2b14a903cd5d3ac5", size = 10751585, upload-time = "2025-08-14T12:04:02.228Z" }, + { url = "https://files.pythonhosted.org/packages/53/6d/ad928fe975a6c14a093c92e6a319ca18f479f3336bb353a740bdba335681/pyproj-3.7.2-cp311-cp311-win32.whl", hash = "sha256:f2af4ed34b2cf3e031a2d85b067a3ecbd38df073c567e04b52fa7a0202afde8a", size = 5908533, upload-time = "2025-08-14T12:04:04.821Z" }, + { url = "https://files.pythonhosted.org/packages/79/e0/b95584605cec9ed50b7ebaf7975d1c4ddeec5a86b7a20554ed8b60042bd7/pyproj-3.7.2-cp311-cp311-win_amd64.whl", hash = "sha256:0b7cb633565129677b2a183c4d807c727d1c736fcb0568a12299383056e67433", size = 6320742, upload-time = "2025-08-14T12:04:06.357Z" }, + { url = "https://files.pythonhosted.org/packages/b7/4d/536e8f93bca808175c2d0a5ac9fdf69b960d8ab6b14f25030dccb07464d7/pyproj-3.7.2-cp311-cp311-win_arm64.whl", hash = "sha256:38b08d85e3a38e455625b80e9eb9f78027c8e2649a21dec4df1f9c3525460c71", size = 6245772, upload-time = "2025-08-14T12:04:08.365Z" }, + { url = "https://files.pythonhosted.org/packages/8d/ab/9893ea9fb066be70ed9074ae543914a618c131ed8dff2da1e08b3a4df4db/pyproj-3.7.2-cp312-cp312-macosx_13_0_x86_64.whl", hash = "sha256:0a9bb26a6356fb5b033433a6d1b4542158fb71e3c51de49b4c318a1dff3aeaab", size = 6219832, upload-time = "2025-08-14T12:04:10.264Z" }, + { url = "https://files.pythonhosted.org/packages/53/78/4c64199146eed7184eb0e85bedec60a4aa8853b6ffe1ab1f3a8b962e70a0/pyproj-3.7.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:567caa03021178861fad27fabde87500ec6d2ee173dd32f3e2d9871e40eebd68", size = 4620650, upload-time = "2025-08-14T12:04:11.978Z" }, + { url = "https://files.pythonhosted.org/packages/b6/ac/14a78d17943898a93ef4f8c6a9d4169911c994e3161e54a7cedeba9d8dde/pyproj-3.7.2-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:c203101d1dc3c038a56cff0447acc515dd29d6e14811406ac539c21eed422b2a", size = 9667087, upload-time = "2025-08-14T12:04:13.964Z" }, + { url = "https://files.pythonhosted.org/packages/b8/be/212882c450bba74fc8d7d35cbd57e4af84792f0a56194819d98106b075af/pyproj-3.7.2-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:1edc34266c0c23ced85f95a1ee8b47c9035eae6aca5b6b340327250e8e281630", size = 9552797, upload-time = "2025-08-14T12:04:16.624Z" }, + { url = "https://files.pythonhosted.org/packages/ba/c0/c0f25c87b5d2a8686341c53c1792a222a480d6c9caf60311fec12c99ec26/pyproj-3.7.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:aa9f26c21bc0e2dc3d224cb1eb4020cf23e76af179a7c66fea49b828611e4260", size = 10837036, upload-time = "2025-08-14T12:04:18.733Z" }, + { url = "https://files.pythonhosted.org/packages/5d/37/5cbd6772addde2090c91113332623a86e8c7d583eccb2ad02ea634c4a89f/pyproj-3.7.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f9428b318530625cb389b9ddc9c51251e172808a4af79b82809376daaeabe5e9", size = 10775952, upload-time = "2025-08-14T12:04:20.709Z" }, + { url = "https://files.pythonhosted.org/packages/69/a1/dc250e3cf83eb4b3b9a2cf86fdb5e25288bd40037ae449695550f9e96b2f/pyproj-3.7.2-cp312-cp312-win32.whl", hash = "sha256:b3d99ed57d319da042f175f4554fc7038aa4bcecc4ac89e217e350346b742c9d", size = 5898872, upload-time = "2025-08-14T12:04:22.485Z" }, + { url = "https://files.pythonhosted.org/packages/4a/a6/6fe724b72b70f2b00152d77282e14964d60ab092ec225e67c196c9b463e5/pyproj-3.7.2-cp312-cp312-win_amd64.whl", hash = "sha256:11614a054cd86a2ed968a657d00987a86eeb91fdcbd9ad3310478685dc14a128", size = 6312176, upload-time = "2025-08-14T12:04:24.736Z" }, + { url = "https://files.pythonhosted.org/packages/5d/68/915cc32c02a91e76d02c8f55d5a138d6ef9e47a0d96d259df98f4842e558/pyproj-3.7.2-cp312-cp312-win_arm64.whl", hash = "sha256:509a146d1398bafe4f53273398c3bb0b4732535065fa995270e52a9d3676bca3", size = 6233452, upload-time = "2025-08-14T12:04:27.287Z" }, + { url = "https://files.pythonhosted.org/packages/be/14/faf1b90d267cea68d7e70662e7f88cefdb1bc890bd596c74b959e0517a72/pyproj-3.7.2-cp313-cp313-macosx_13_0_x86_64.whl", hash = "sha256:19466e529b1b15eeefdf8ff26b06fa745856c044f2f77bf0edbae94078c1dfa1", size = 6214580, upload-time = "2025-08-14T12:04:28.804Z" }, + { url = "https://files.pythonhosted.org/packages/35/48/da9a45b184d375f62667f62eba0ca68569b0bd980a0bb7ffcc1d50440520/pyproj-3.7.2-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:c79b9b84c4a626c5dc324c0d666be0bfcebd99f7538d66e8898c2444221b3da7", size = 4615388, upload-time = "2025-08-14T12:04:30.553Z" }, + { url = "https://files.pythonhosted.org/packages/5e/e7/d2b459a4a64bca328b712c1b544e109df88e5c800f7c143cfbc404d39bfb/pyproj-3.7.2-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:ceecf374cacca317bc09e165db38ac548ee3cad07c3609442bd70311c59c21aa", size = 9628455, upload-time = "2025-08-14T12:04:32.435Z" }, + { url = "https://files.pythonhosted.org/packages/f8/85/c2b1706e51942de19076eff082f8495e57d5151364e78b5bef4af4a1d94a/pyproj-3.7.2-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:5141a538ffdbe4bfd157421828bb2e07123a90a7a2d6f30fa1462abcfb5ce681", size = 9514269, upload-time = "2025-08-14T12:04:34.599Z" }, + { url = "https://files.pythonhosted.org/packages/34/38/07a9b89ae7467872f9a476883a5bad9e4f4d1219d31060f0f2b282276cbe/pyproj-3.7.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f000841e98ea99acbb7b8ca168d67773b0191de95187228a16110245c5d954d5", size = 10808437, upload-time = "2025-08-14T12:04:36.485Z" }, + { url = "https://files.pythonhosted.org/packages/12/56/fda1daeabbd39dec5b07f67233d09f31facb762587b498e6fc4572be9837/pyproj-3.7.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8115faf2597f281a42ab608ceac346b4eb1383d3b45ab474fd37341c4bf82a67", size = 10745540, upload-time = "2025-08-14T12:04:38.568Z" }, + { url = "https://files.pythonhosted.org/packages/0d/90/c793182cbba65a39a11db2ac6b479fe76c59e6509ae75e5744c344a0da9d/pyproj-3.7.2-cp313-cp313-win32.whl", hash = "sha256:f18c0579dd6be00b970cb1a6719197fceecc407515bab37da0066f0184aafdf3", size = 5896506, upload-time = "2025-08-14T12:04:41.059Z" }, + { url = "https://files.pythonhosted.org/packages/be/0f/747974129cf0d800906f81cd25efd098c96509026e454d4b66868779ab04/pyproj-3.7.2-cp313-cp313-win_amd64.whl", hash = "sha256:bb41c29d5f60854b1075853fe80c58950b398d4ebb404eb532536ac8d2834ed7", size = 6310195, upload-time = "2025-08-14T12:04:42.974Z" }, + { url = "https://files.pythonhosted.org/packages/82/64/fc7598a53172c4931ec6edf5228280663063150625d3f6423b4c20f9daff/pyproj-3.7.2-cp313-cp313-win_arm64.whl", hash = "sha256:2b617d573be4118c11cd96b8891a0b7f65778fa7733ed8ecdb297a447d439100", size = 6230748, upload-time = "2025-08-14T12:04:44.491Z" }, + { url = "https://files.pythonhosted.org/packages/aa/f0/611dd5cddb0d277f94b7af12981f56e1441bf8d22695065d4f0df5218498/pyproj-3.7.2-cp313-cp313t-macosx_13_0_x86_64.whl", hash = "sha256:d27b48f0e81beeaa2b4d60c516c3a1cfbb0c7ff6ef71256d8e9c07792f735279", size = 6241729, upload-time = "2025-08-14T12:04:46.274Z" }, + { url = "https://files.pythonhosted.org/packages/15/93/40bd4a6c523ff9965e480870611aed7eda5aa2c6128c6537345a2b77b542/pyproj-3.7.2-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:55a3610d75023c7b1c6e583e48ef8f62918e85a2ae81300569d9f104d6684bb6", size = 4652497, upload-time = "2025-08-14T12:04:48.203Z" }, + { url = "https://files.pythonhosted.org/packages/1b/ae/7150ead53c117880b35e0d37960d3138fe640a235feb9605cb9386f50bb0/pyproj-3.7.2-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:8d7349182fa622696787cc9e195508d2a41a64765da9b8a6bee846702b9e6220", size = 9942610, upload-time = "2025-08-14T12:04:49.652Z" }, + { url = "https://files.pythonhosted.org/packages/d8/17/7a4a7eafecf2b46ab64e5c08176c20ceb5844b503eaa551bf12ccac77322/pyproj-3.7.2-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:d230b186eb876ed4f29a7c5ee310144c3a0e44e89e55f65fb3607e13f6db337c", size = 9692390, upload-time = "2025-08-14T12:04:51.731Z" }, + { url = "https://files.pythonhosted.org/packages/c3/55/ae18f040f6410f0ea547a21ada7ef3e26e6c82befa125b303b02759c0e9d/pyproj-3.7.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:237499c7862c578d0369e2b8ac56eec550e391a025ff70e2af8417139dabb41c", size = 11047596, upload-time = "2025-08-14T12:04:53.748Z" }, + { url = "https://files.pythonhosted.org/packages/e6/2e/d3fff4d2909473f26ae799f9dda04caa322c417a51ff3b25763f7d03b233/pyproj-3.7.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8c225f5978abd506fd9a78eaaf794435e823c9156091cabaab5374efb29d7f69", size = 10896975, upload-time = "2025-08-14T12:04:55.875Z" }, + { url = "https://files.pythonhosted.org/packages/f2/bc/8fc7d3963d87057b7b51ebe68c1e7c51c23129eee5072ba6b86558544a46/pyproj-3.7.2-cp313-cp313t-win32.whl", hash = "sha256:2da731876d27639ff9d2d81c151f6ab90a1546455fabd93368e753047be344a2", size = 5953057, upload-time = "2025-08-14T12:04:58.466Z" }, + { url = "https://files.pythonhosted.org/packages/cc/27/ea9809966cc47d2d51e6d5ae631ea895f7c7c7b9b3c29718f900a8f7d197/pyproj-3.7.2-cp313-cp313t-win_amd64.whl", hash = "sha256:f54d91ae18dd23b6c0ab48126d446820e725419da10617d86a1b69ada6d881d3", size = 6375414, upload-time = "2025-08-14T12:04:59.861Z" }, + { url = "https://files.pythonhosted.org/packages/5b/f8/1ef0129fba9a555c658e22af68989f35e7ba7b9136f25758809efec0cd6e/pyproj-3.7.2-cp313-cp313t-win_arm64.whl", hash = "sha256:fc52ba896cfc3214dc9f9ca3c0677a623e8fdd096b257c14a31e719d21ff3fdd", size = 6262501, upload-time = "2025-08-14T12:05:01.39Z" }, +] + +[[package]] +name = "pyproj" +version = "3.8.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.12' and sys_platform == 'win32'", + "python_full_version >= '3.12' and sys_platform == 'emscripten'", + "python_full_version >= '3.12' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +dependencies = [ + { name = "certifi" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c8/29/6598570c90cbfc84ddefc3ccac4aa412bf51a527d72c74cc4fe64a5e6f24/pyproj-3.8.0.tar.gz", hash = "sha256:efa59725bba68bf97fa808b61302df32934acdceb6a5c92a8dd0e71dc266a876", size = 242018, upload-time = "2026-09-05T20:05:09.353Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bb/c0/e2cdf9555f4feb6a17717de08527b0358ecccca7892ac78b17f5a04903cb/pyproj-3.8.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:8606ccbc110ce2e8cbe4c9ba6c3f24cd73f603795ac0fce1ceb8fc27a9bb786e", size = 4990712, upload-time = "2026-09-05T20:03:10.928Z" }, + { url = "https://files.pythonhosted.org/packages/4e/99/4ec34c75e06d18bd4b8e08189ef8a76170bab339a9bee7d6eaeed075d444/pyproj-3.8.0-cp312-cp312-macosx_15_0_x86_64.whl", hash = "sha256:2d4c49e27d404a95d244196fdadb6b0b0ba3c3cbb43a8d2a357fc579a36b7835", size = 6098819, upload-time = "2026-09-05T20:03:13.066Z" }, + { url = "https://files.pythonhosted.org/packages/28/c3/a819a14ff040ad441f09589fff28cca88c82cb8537818316c798f2ddafb2/pyproj-3.8.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:d8e00ccd9195d9dbb968d490ae9e51c3a59378665506c05e36bfdb6493a38a69", size = 10120591, upload-time = "2026-09-05T20:03:15.214Z" }, + { url = "https://files.pythonhosted.org/packages/5f/ed/035354de8fbc1c1350f5b7f66361995f187d89675622eb63016bf7261dd5/pyproj-3.8.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:6b34cec6bdd66b721980c003cc2a0fce0cab7949444ad037fc2a7ecb6f3b996c", size = 10037552, upload-time = "2026-09-05T20:03:17.43Z" }, + { url = "https://files.pythonhosted.org/packages/76/62/04809135418fb84402acf2a1d37beaeb770d66d78061962cbf0d459a0b07/pyproj-3.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:859caa91bd9a1c614bac7de110f3a62f715e6c5a4fe9140c3fd6a0c5034e390d", size = 11498786, upload-time = "2026-09-05T20:03:20.178Z" }, + { url = "https://files.pythonhosted.org/packages/55/ce/7e29ca3df78c59b413ab20d2d2dfb1ef36329f051597992eaa65e4306ac0/pyproj-3.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e6e21972a28e65fdec4b794b6298206516853e03fc9ac40c14271c5dce6b0d32", size = 11497184, upload-time = "2026-09-05T20:03:22.591Z" }, + { url = "https://files.pythonhosted.org/packages/9a/8c/010481b3629eb70f8c8c433d706207b1ec91fdb76fc10cf4ec1d92f51bdd/pyproj-3.8.0-cp312-cp312-win32.whl", hash = "sha256:7914e83760284e4d8d3b6b2a6845c21270a07f214bfd89344d45f9ab63e1b4b5", size = 6295859, upload-time = "2026-09-05T20:03:24.738Z" }, + { url = "https://files.pythonhosted.org/packages/b7/d7/2c861a429581577a441e03020d27274812d7969748f5951d972f6e34597f/pyproj-3.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:0556ce011e1530aea2084a12e39dc9c6ccd25a16c1db5457f39bf83161e7c301", size = 6714045, upload-time = "2026-09-05T20:03:26.863Z" }, + { url = "https://files.pythonhosted.org/packages/09/bb/2d1ff197922cfe106f204041503fb8039d2cee9c34c2a61a0b65f4121b4c/pyproj-3.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:a792112106471f97c3f74b51639068388c9ad0605cc1ca100f11fd0bf47a004e", size = 6663720, upload-time = "2026-09-05T20:03:28.625Z" }, + { url = "https://files.pythonhosted.org/packages/3a/6c/50e8846bda4502d2967c78a106e3565f0e3008965066e65a90cfa295c673/pyproj-3.8.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:d7bd22f1d4f058db72b5f09d0fcc9a2346178ccf965139ca483edaa5c3a7f2d3", size = 4987064, upload-time = "2026-09-05T20:03:30.544Z" }, + { url = "https://files.pythonhosted.org/packages/56/71/108a8a1fe4dfd6d0bfea14835d834d2a10a89c82b1807084f34f480c5599/pyproj-3.8.0-cp313-cp313-macosx_15_0_x86_64.whl", hash = "sha256:c90bf55c42d3d5475958196bf7331b9aa87e1505af49570f126739ad7e808c1e", size = 6096593, upload-time = "2026-09-05T20:03:32.128Z" }, + { url = "https://files.pythonhosted.org/packages/6d/c4/e9213bb303205912bce7d0681c39da654f9e0a9585cc227b1ee142b5156c/pyproj-3.8.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:8e01abceec40fd8326637cc207a4da089a3c3f61e64001cbd86951c746c54085", size = 10051120, upload-time = "2026-09-05T20:03:34.006Z" }, + { url = "https://files.pythonhosted.org/packages/c9/80/3cdcc2e6942eec2774c8c27655705329e0d76b1eea849136d27dd75aea38/pyproj-3.8.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:01a1601da9c6ad247a12d304f96f9e0b4ddd00b307636341c136442a70c5e218", size = 9974666, upload-time = "2026-09-05T20:03:36.226Z" }, + { url = "https://files.pythonhosted.org/packages/99/c4/f890986aa51e846de464e5054d46ea5443c7cf6fa677631b0e2aa0659dc4/pyproj-3.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0efefc85d3f262d4e5b43d0ffc4ea30e89881ed21feb281b1d1b1294423411ac", size = 11437839, upload-time = "2026-09-05T20:03:38.752Z" }, + { url = "https://files.pythonhosted.org/packages/c6/1e/e720a2d83424181be89ea5201c1f38c1ea8c73fdfae54e549bb44a14ace1/pyproj-3.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9d7f3526031ba810922b15eeab446667f02af4e78de49141e7f0d4a3c7e10ce1", size = 11436240, upload-time = "2026-09-05T20:03:41.128Z" }, + { url = "https://files.pythonhosted.org/packages/9b/0a/8cf66c2a355e2af80ce1dd41386ef2c9f6986c16893b5251e437a03cc5e3/pyproj-3.8.0-cp313-cp313-win32.whl", hash = "sha256:efe9f067215397d719df759083dda09b7012de99439003b12dff5109b339771d", size = 6293886, upload-time = "2026-09-05T20:03:43.392Z" }, + { url = "https://files.pythonhosted.org/packages/b7/70/c5477f4bcc1e1dfeb53ba082f8102467a5675f66ffc21fce1f2564c5ce5d/pyproj-3.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:d7b542e249eb593c1af737b7124648868383b69744ab6a1a0a2ffd0113c997f4", size = 6710283, upload-time = "2026-09-05T20:03:45.185Z" }, + { url = "https://files.pythonhosted.org/packages/99/c5/986fc93c7569e82f21dc2e68cf3603843a57a9837e40f48651a69b4bd27f/pyproj-3.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:b761da280804bb02574c3d950d5e56c47e2aec782d8a3e6714c9c10645cfd020", size = 6661749, upload-time = "2026-09-05T20:03:46.976Z" }, +] + +[[package]] +name = "pyshp" +version = "3.1.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/68/6e/f6f241fcb57f09c856ae78249d70cf31c576840218226ba7d88eda5ceab8/pyshp-3.1.6.tar.gz", hash = "sha256:4c4b5a4138815c93defd6ebe50bae885b7e58984f431cdb7bb5d00718ac71daf", size = 2227945, upload-time = "2026-07-25T16:53:12.324Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ac/72/1f79dab76eb7651b5e3622a46b18a7f93f57c3a1317b712986c58810895e/pyshp-3.1.6-py3-none-any.whl", hash = "sha256:be8cd9436dcbde477c63ea789f7dcdea3af19f17ea84f2744bfc12efe9145d1e", size = 73744, upload-time = "2026-07-25T16:53:10.889Z" }, +] + +[[package]] +name = "pysteps" +version = "2.0.0" +source = { editable = "." } +dependencies = [ + { name = "jsmin" }, + { name = "jsonschema" }, + { name = "matplotlib" }, + { name = "netcdf4" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "numpy", version = "2.5.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "opencv-python" }, + { name = "pandas" }, + { name = "pillow" }, + { name = "pyproj", version = "3.7.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "pyproj", version = "3.8.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "scipy", version = "1.17.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "scipy", version = "1.18.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "xarray" }, +] + +[package.optional-dependencies] +gdal = [ + { name = "pip" }, + { name = "setuptools" }, + { name = "wheel" }, +] +optional = [ + { name = "cartopy" }, + { name = "cookiecutter" }, + { name = "dask" }, + { name = "geotiff" }, + { name = "h5py" }, + { name = "pandas" }, + { name = "pyfftw" }, + { name = "pywavelets", version = "1.9.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "pywavelets", version = "1.10.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "rasterio", version = "1.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "rasterio", version = "1.5.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "scikit-image" }, + { name = "scikit-learn" }, +] +pygrib = [ + { name = "pygrib" }, +] + +[package.dev-dependencies] +dev = [ + { name = "bandit" }, + { name = "pre-commit" }, + { name = "pytest" }, + { name = "pytest-cov" }, + { name = "ruff" }, +] + +[package.metadata] +requires-dist = [ + { name = "cartopy", marker = "extra == 'optional'", specifier = ">=0.18" }, + { name = "cookiecutter", marker = "extra == 'optional'" }, + { name = "dask", marker = "extra == 'optional'" }, + { name = "geotiff", marker = "extra == 'optional'" }, + { name = "h5py", marker = "extra == 'optional'" }, + { name = "jsmin" }, + { name = "jsonschema" }, + { name = "matplotlib" }, + { name = "netcdf4" }, + { name = "numpy" }, + { name = "opencv-python" }, + { name = "pandas" }, + { name = "pandas", marker = "extra == 'optional'" }, + { name = "pillow" }, + { name = "pip", marker = "extra == 'gdal'" }, + { name = "pyfftw", marker = "extra == 'optional'" }, + { name = "pygrib", marker = "extra == 'pygrib'" }, + { name = "pyproj" }, + { name = "pywavelets", marker = "extra == 'optional'" }, + { name = "rasterio", marker = "extra == 'optional'" }, + { name = "scikit-image", marker = "extra == 'optional'" }, + { name = "scikit-learn", marker = "extra == 'optional'" }, + { name = "scipy" }, + { name = "setuptools", marker = "extra == 'gdal'", specifier = ">=67" }, + { name = "wheel", marker = "extra == 'gdal'" }, + { name = "xarray" }, +] +provides-extras = ["optional", "pygrib", "gdal"] + +[package.metadata.requires-dev] +dev = [ + { name = "bandit" }, + { name = "pre-commit" }, + { name = "pytest" }, + { name = "pytest-cov" }, + { name = "ruff" }, +] + +[[package]] +name = "pytest" +version = "9.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/79/42/bb4641652e58dedeac2ec51abc1f8cae4fee4ec63899a0999016229a0f2a/pytest-9.2.0.tar.gz", hash = "sha256:97c4f3b1f3c7414aa36465e09b8220f1d03627993bd51b1282e193753c80557b", size = 1797555, upload-time = "2026-10-27T21:30:20.728Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/51/39/1f5d8c8877a0fa327db959c19341d4be61660de1ab6b7391f9eb805bcd67/pytest-9.2.0-py3-none-any.whl", hash = "sha256:07acdcf88ec174f907dc01af70bf52d49b014bbab03faeeade31c96b18c2e588", size = 411776, upload-time = "2026-10-27T21:30:19.131Z" }, +] + +[[package]] +name = "pytest-cov" +version = "7.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "coverage" }, + { name = "pluggy" }, + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/51/a849f96e117386044471c8ec2bd6cfebacda285da9525c9106aeb28da671/pytest_cov-7.1.0.tar.gz", hash = "sha256:30674f2b5f6351aa09702a9c8c364f6a01c27aae0c1366ae8016160d1efc56b2", size = 55592, upload-time = "2026-03-21T20:11:16.284Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9d/7a/d968e294073affff457b041c2be9868a40c1c71f4a35fcc1e45e5493067b/pytest_cov-7.1.0-py3-none-any.whl", hash = "sha256:a0461110b7865f9a271aa1b51e516c9a95de9d696734a2f71e3e78f46e1d4678", size = 22876, upload-time = "2026-03-21T20:11:14.438Z" }, +] + +[[package]] +name = "python-dateutil" +version = "4.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "tzdata" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/18/39/b3927f3616f6223832f9b760e4208e8a7c7e1dea2b4d9ad7d32a5d214c3a/python_dateutil-4.0.0.tar.gz", hash = "sha256:d7295ff14fdc0f92f662100bfb6c551d160ca090f2939f1ec5b9794265c03f06", size = 250419, upload-time = "2026-11-01T07:02:42.426Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2f/a9/52b89563d154433d79b51ed1eb4842bf7334cb2696891a7fe135618853d7/python_dateutil-4.0.0-py3-none-any.whl", hash = "sha256:a5e246f79eb80475e691bf93862cead98decd8a498cf53ba0516fcdc0f50dc1c", size = 92853, upload-time = "2026-11-01T07:02:40.797Z" }, +] + +[[package]] +name = "python-discovery" +version = "1.9.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "filelock" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ea/87/77b17c6d289a551d682826e228e3efa3199305a080541d991cb7b268a527/python_discovery-1.9.2.tar.gz", hash = "sha256:de397b9c4b09a5fb8bb4c51db3c0da71fb81d2283718d74fd1efaabffc32afbc", size = 140950, upload-time = "2026-11-03T05:04:25.117Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/39/5b/d61e08d34eeb74eb88431243e3849551f0161de08659d55312955afd2a36/python_discovery-1.9.2-py3-none-any.whl", hash = "sha256:65857e689ba72aeb5517e68b7025430ed60372b78d037245e0bed3d41ae7ba65", size = 44201, upload-time = "2026-11-03T05:04:23.777Z" }, +] + +[[package]] +name = "python-slugify" +version = "9.1.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "text-unidecode" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bd/e8/26b1af09d728d604dc16427a39f53985b22a170dbd61addac3f48db73f03/python_slugify-9.1.3.tar.gz", hash = "sha256:90e997f2e0987239ce95e12f700086eb18e1d1d3ee22624fbbdbd095afca42b6", size = 66093, upload-time = "2026-10-07T01:06:20.101Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/47/67/5598c98725332a6a4084c7b622d5a1ebe7d6745c1d2f434c9e4e89e04cba/python_slugify-9.1.3-py3-none-any.whl", hash = "sha256:9aced0670e54c5603e2335c0fa9a4011ad4bc41ecb3c46e8097294c46fd60ed4", size = 16032, upload-time = "2026-10-07T01:06:18.891Z" }, +] + +[[package]] +name = "pywavelets" +version = "1.9.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.12' and sys_platform == 'win32'", + "python_full_version < '3.12' and sys_platform == 'emscripten'", + "python_full_version < '3.12' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +dependencies = [ + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" } }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5a/75/50581633d199812205ea8cdd0f6d52f12a624886b74bf1486335b67f01ff/pywavelets-1.9.0.tar.gz", hash = "sha256:148d12203377772bea452a59211d98649c8ee4a05eff019a9021853a36babdc8", size = 3938340, upload-time = "2025-08-04T16:20:04.978Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bd/8b/ca700d0c174c3a4eec1fbb603f04374d1fed84255c2a9f487cfaa749c865/pywavelets-1.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:54662cce4d56f0d6beaa6ebd34b2960f3aa4a43c83c9098a24729e9dc20a4be2", size = 4323640, upload-time = "2025-08-04T16:18:51.683Z" }, + { url = "https://files.pythonhosted.org/packages/b5/f3/0fa57b6407ea9c4452b0bc182141256b9481b479ffbfc9d7fdb73afe193b/pywavelets-1.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0d8ed4b4d1eab9347e8fe0c5b45008ce5a67225ce5b05766b8b1fa923a5f8b34", size = 4294938, upload-time = "2025-08-04T16:18:53.818Z" }, + { url = "https://files.pythonhosted.org/packages/ea/95/a998313c8459a57e488ff2b18e24be9e836aedda3aa3a1673197deeaa59a/pywavelets-1.9.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:862be65481fdfecfd84c6b0ca132ba571c12697a082068921bca5b5e039f1371", size = 4472829, upload-time = "2025-08-04T16:18:55.508Z" }, + { url = "https://files.pythonhosted.org/packages/d8/8c/f316a153f7f89d2753df8a7371d15d0faab87e709fe02715dbc297c79385/pywavelets-1.9.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d76b7fa8fc500b09201d689b4f15bf5887e30ffbe2e1f338eb8470590eb4521a", size = 4524936, upload-time = "2025-08-04T16:18:57.146Z" }, + { url = "https://files.pythonhosted.org/packages/24/f7/89fdc1caef4b384a341a8e149253e23f36c1702bbb986a26123348624854/pywavelets-1.9.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:aa859d0b686a697c87a47e29319aebe44125f114a4f8c7e444832b921f52de5a", size = 4481475, upload-time = "2025-08-04T16:18:58.725Z" }, + { url = "https://files.pythonhosted.org/packages/82/53/b733fbfb71853e4a5c430da56e325a763562d65241dd785f0fadb67aed6a/pywavelets-1.9.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:20e97b84a263003e2c7348bcf72beba96edda1a6169f072dc4e4d4ee3a6c7368", size = 4527994, upload-time = "2025-08-04T16:18:59.917Z" }, + { url = "https://files.pythonhosted.org/packages/ed/15/5f6a6e9fdad8341e42642ed622a5f3033da4ea9d426cc3e574ae418b4726/pywavelets-1.9.0-cp311-cp311-win32.whl", hash = "sha256:f8330cdbfa506000e63e79525716df888998a76414c5cd6ecd9a7e371191fb05", size = 4136109, upload-time = "2025-08-04T16:19:01.511Z" }, + { url = "https://files.pythonhosted.org/packages/fd/33/62dbb4aea86ec9d79b283127c42cc896f4d4ff265a9aeb1337a7836dd550/pywavelets-1.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:ed10959a17df294ef55948dcc76367d59ec7b6aad67e38dd4e313d2fe3ad47b2", size = 4228321, upload-time = "2025-08-04T16:19:03.164Z" }, + { url = "https://files.pythonhosted.org/packages/5c/37/3fda13fb2518fdd306528382d6b18c116ceafefff0a7dccd28f1034f4dd2/pywavelets-1.9.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:30baa0788317d3c938560c83fe4fc43817342d06e6c9662a440f73ba3fb25c9b", size = 4320835, upload-time = "2025-08-04T16:19:04.855Z" }, + { url = "https://files.pythonhosted.org/packages/36/65/a5549325daafc3eae4b52de076798839eaf529a07218f8fb18cccefe76a1/pywavelets-1.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:df7436a728339696a7aa955c020ae65c85b0d9d2b5ff5b4cf4551f5d4c50f2c7", size = 4290469, upload-time = "2025-08-04T16:19:06.178Z" }, + { url = "https://files.pythonhosted.org/packages/05/85/901bb756d37dfa56baa26ef4a3577aecfe9c55f50f51366fede322f8c91d/pywavelets-1.9.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:07b26526db2476974581274c43a9c2447c917418c6bd03c8d305ad2a5cd9fac3", size = 4437717, upload-time = "2025-08-04T16:19:07.514Z" }, + { url = "https://files.pythonhosted.org/packages/0f/34/0f54dd9c288941294898877008bcb5c07012340cc9c5db9cff1bd185d449/pywavelets-1.9.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:573b650805d2f3c981a0e5ae95191c781a722022c37a0f6eba3fa7eae8e0ee17", size = 4483843, upload-time = "2025-08-04T16:19:08.857Z" }, + { url = "https://files.pythonhosted.org/packages/48/1f/cff6bb4ea64ff508d8cac3fe113c0aa95310a7446d9efa6829027cc2afdf/pywavelets-1.9.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3747ec804492436de6e99a7b6130480e53406d047e87dc7095ab40078a515a23", size = 4442236, upload-time = "2025-08-04T16:19:11.061Z" }, + { url = "https://files.pythonhosted.org/packages/ce/53/a3846eeefe0fb7ca63ae045f038457aa274989a15af793c1b824138caf98/pywavelets-1.9.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5163665686219c3f43fd5bbfef2391e87146813961dad0f86c62d4aed561f547", size = 4488077, upload-time = "2025-08-04T16:19:12.333Z" }, + { url = "https://files.pythonhosted.org/packages/f7/98/44852d2fe94455b72dece2db23562145179d63186a1c971125279a1c381f/pywavelets-1.9.0-cp312-cp312-win32.whl", hash = "sha256:80b8ab99f5326a3e724f71f23ba8b0a5b03e333fa79f66e965ea7bed21d42a2f", size = 4134094, upload-time = "2025-08-04T16:19:13.564Z" }, + { url = "https://files.pythonhosted.org/packages/2c/a7/0d9ee3fe454d606e0f5c8e3aebf99d2ecddbfb681826a29397729538c8f1/pywavelets-1.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:92bfb8a117b8c8d3b72f2757a85395346fcbf37f50598880879ae72bd8e1c4b9", size = 4213900, upload-time = "2025-08-04T16:19:14.939Z" }, + { url = "https://files.pythonhosted.org/packages/db/a7/dec4e450675d62946ad975f5b4d924437df42d2fae46e91dfddda2de0f5a/pywavelets-1.9.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:74f8455c143818e4b026fc67b27fd82f38e522701b94b8a6d1aaf3a45fcc1a25", size = 4316201, upload-time = "2025-08-04T16:19:16.259Z" }, + { url = "https://files.pythonhosted.org/packages/aa/0c/b54b86596c0df68027e48c09210e907e628435003e77048384a2dd6767e3/pywavelets-1.9.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c50320fe0a4a23ddd8835b3dc9b53b09ee05c7cc6c56b81d0916f04fc1649070", size = 4286838, upload-time = "2025-08-04T16:19:17.92Z" }, + { url = "https://files.pythonhosted.org/packages/5a/9c/333969c3baad8af2e7999e83addcb7bb1d1fd48e2d812fb27e2e89582cb1/pywavelets-1.9.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d6e059265223ed659e5214ab52a84883c88ddf3decbf08d7ec6abb8e4c5ed7be", size = 4430753, upload-time = "2025-08-04T16:19:19.529Z" }, + { url = "https://files.pythonhosted.org/packages/e5/1b/a24c6ff03b026b826ad7b9267bd63cd34ce026795a0302f8a5403840b8e7/pywavelets-1.9.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ae10ed46c139c7ddb8b1249cfe0989f8ccb610d93f2899507b1b1573a0e424b5", size = 4491315, upload-time = "2025-08-04T16:19:20.717Z" }, + { url = "https://files.pythonhosted.org/packages/d7/c7/e3fbb502fca3469e51ced4f1e1326364c338be91edc5db5a8ddd26b303fa/pywavelets-1.9.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c8f8b1cc2df012401cb837ee6fa2f59607c7b4fe0ff409d9a4f6906daf40dc86", size = 4437654, upload-time = "2025-08-04T16:19:22.359Z" }, + { url = "https://files.pythonhosted.org/packages/92/44/c9b25084048d9324881a19b88e0969a4141bcfdc1d218f1b4b680b7af1c1/pywavelets-1.9.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:db43969c7a8fbb17693ecfd14f21616edc3b29f0e47a49b32fa4127c01312a67", size = 4496435, upload-time = "2025-08-04T16:19:23.842Z" }, + { url = "https://files.pythonhosted.org/packages/cd/b6/b27ec18c72b1dee3314e297af39c5f8136d43cc130dd93cb6c178ca820e5/pywavelets-1.9.0-cp313-cp313-win32.whl", hash = "sha256:9e7d60819d87dcd6c68a2d1bc1d37deb1f4d96607799ab6a25633ea484dcda41", size = 4132709, upload-time = "2025-08-04T16:19:25.415Z" }, + { url = "https://files.pythonhosted.org/packages/0a/87/78ef3f9fb36cdb16ee82371d22c3a7c89eeb79ec8c9daef6222060da6c79/pywavelets-1.9.0-cp313-cp313-win_amd64.whl", hash = "sha256:0d70da9d7858c869e24dc254f16a61dc09d8a224cad85a10c393b2eccddeb126", size = 4213377, upload-time = "2025-08-04T16:19:26.875Z" }, + { url = "https://files.pythonhosted.org/packages/8b/cd/ca0d9db0ff29e3843f6af60c2f5eb588794e05ca8eeb872a595867b1f3f5/pywavelets-1.9.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:4dc85f44c38d76a184a1aa2cb038f802c3740428c9bb877525f4be83a223b134", size = 4354336, upload-time = "2025-08-04T16:19:28.745Z" }, + { url = "https://files.pythonhosted.org/packages/82/d6/70afefcc1139f37d02018a3b1dba3b8fc87601bb7707d9616b7f7a76e269/pywavelets-1.9.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:7acf6f950c6deaecd210fbff44421f234a8ca81eb6f4da945228e498361afa9d", size = 4335721, upload-time = "2025-08-04T16:19:30.371Z" }, + { url = "https://files.pythonhosted.org/packages/cd/3a/713f731b9ed6df0c36269c8fb62be8bb28eb343b9e26b13d6abda37bce38/pywavelets-1.9.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:144d4fc15c98da56654d0dca2d391b812b8d04127b194a37ad4a497f8e887141", size = 4418702, upload-time = "2025-08-04T16:19:31.743Z" }, + { url = "https://files.pythonhosted.org/packages/44/e8/f801eb4b5f7a316ba20054948c5d6b27b879c77fab2674942e779974bd86/pywavelets-1.9.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1aa3729585408a979d655736f74b995b511c86b9be1544f95d4a3142f8f4b8b5", size = 4470023, upload-time = "2025-08-04T16:19:32.963Z" }, + { url = "https://files.pythonhosted.org/packages/e9/cc/44b002cb16f2a392f2082308dd470b3f033fa4925d3efa7c46f790ce895a/pywavelets-1.9.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:e0e24ad6b8eb399c49606dd1fcdcbf9749ad7f6d638be3fe6f59c1f3098821e2", size = 4426498, upload-time = "2025-08-04T16:19:34.151Z" }, + { url = "https://files.pythonhosted.org/packages/91/fe/2b70276ede7878c5fe8356ca07574db5da63e222ce39a463e84bfad135e8/pywavelets-1.9.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:3830e6657236b53a3aae20c735cccead942bb97c54bbca9e7d07bae01645fe9c", size = 4477528, upload-time = "2025-08-04T16:19:35.932Z" }, + { url = "https://files.pythonhosted.org/packages/e7/ed/d58b540c15e36508cfeded7b0d39493e811b0dce18d9d4e6787fb2e89685/pywavelets-1.9.0-cp313-cp313t-win32.whl", hash = "sha256:81bb65facfbd7b50dec50450516e72cdc51376ecfdd46f2e945bb89d39bfb783", size = 4186493, upload-time = "2025-08-04T16:19:37.198Z" }, + { url = "https://files.pythonhosted.org/packages/84/b2/12a849650d618a86bbe4d8876c7e20a7afe59a8cad6f49c57eca9af26dfa/pywavelets-1.9.0-cp313-cp313t-win_amd64.whl", hash = "sha256:47d52cf35e2afded8cfe1133663f6f67106a3220b77645476ae660ad34922cb4", size = 4274821, upload-time = "2025-08-04T16:19:38.436Z" }, +] + +[[package]] +name = "pywavelets" +version = "1.10.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.12' and sys_platform == 'win32'", + "python_full_version >= '3.12' and sys_platform == 'emscripten'", + "python_full_version >= '3.12' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +dependencies = [ + { name = "numpy", version = "2.5.4", source = { registry = "https://pypi.org/simple" } }, +] +sdist = { url = "https://files.pythonhosted.org/packages/25/84/a9e80e33eb114e78bd2f1829a90d72d8d4d660109c721a8aa2f0d58d7592/pywavelets-1.10.0.tar.gz", hash = "sha256:f3cb8640225a8f3fdcbc3f6f3bd112c78927c9407cbfa65a5ff303990a6a3e32", size = 3948163, upload-time = "2026-09-07T17:15:15.637Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4b/0c/bba2a675907453876c47025c15d25eeb9a3b40359fbdcee2752f6fe567e1/pywavelets-1.10.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5378e24d8e0bc7a5a13e5b3ed69e5ae3e2404889dd77ec2b66f7567ce6022f00", size = 4327195, upload-time = "2026-09-07T17:13:40.915Z" }, + { url = "https://files.pythonhosted.org/packages/b6/b3/5af6d72915bc5fb4bf162a6db1b8b78670727c10e7eb9e6b07dca76aa587/pywavelets-1.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ba9e246d5f6e03ce051f07d448d1ba89691ea4815cf8df4f4a5831832a43c0b0", size = 4286953, upload-time = "2026-09-07T17:13:42.766Z" }, + { url = "https://files.pythonhosted.org/packages/42/61/8c7a9337973571511ee5beb6fa11bcc54b7d728273334e271feaf38477ec/pywavelets-1.10.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b08149aaf7446280a7bb515a3731e87e16b39d4dbb98af11243a278c36d12c8f", size = 4411264, upload-time = "2026-09-07T17:13:44.271Z" }, + { url = "https://files.pythonhosted.org/packages/c3/93/e0c4cc749feda5984af84e510179dc9c37c89a5af62d2ce20c7d2414b85e/pywavelets-1.10.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:262d11406c57a5ffba1fa531d8ecbb270c85208612e9d1f4fc98897ccf536722", size = 4466610, upload-time = "2026-09-07T17:13:46.417Z" }, + { url = "https://files.pythonhosted.org/packages/e2/4f/e9f7fbbc6a0be1f629fda74706c95fdf7f3026f8e6bdea1a4a73202b14de/pywavelets-1.10.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bbccca7722a31ea740c48d8ae1a0edc653e37ef554e516d122e2a19522b38893", size = 4417881, upload-time = "2026-09-07T17:13:48.236Z" }, + { url = "https://files.pythonhosted.org/packages/a0/19/dd6ed2b8114ea058cdce9346b5024d1028df9f163a0456b95ec8514ae851/pywavelets-1.10.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:82e134f287918dde43f9d56ee8aea9174faab509feaea835127e1685365660fd", size = 4472602, upload-time = "2026-09-07T17:13:49.783Z" }, + { url = "https://files.pythonhosted.org/packages/f1/5f/9a57ad0f2a4a15ec5c2dce859f0b502ed360f6da6ccd0f3b97cb2fc38a58/pywavelets-1.10.0-cp312-cp312-win32.whl", hash = "sha256:9cfb974fc316fbca735761474c92cd1d6be2d98bc72c37ddc7ac7a2ae83e221b", size = 4177188, upload-time = "2026-09-07T17:13:51.604Z" }, + { url = "https://files.pythonhosted.org/packages/5b/4c/6490c47aefdc3d210db3e071a9592ca453d01c55a1338682da970792bfad/pywavelets-1.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:5be6bdee86f8088eb15becaefabfecaa6336cd6103c58f46d2466d127b4f08cc", size = 4281235, upload-time = "2026-09-07T17:13:53.234Z" }, + { url = "https://files.pythonhosted.org/packages/a5/63/bae289a235152e54c757049da0cb6e73ca996ae131d3382ddf4608a028d1/pywavelets-1.10.0-cp312-cp312-win_arm64.whl", hash = "sha256:fab763c551e33c1bdb567688d08bf33d8d79410f2cc0cf0f964c567bf2495639", size = 4175595, upload-time = "2026-09-07T17:13:54.763Z" }, + { url = "https://files.pythonhosted.org/packages/07/dd/6db37a5c4d3e8c0ae7316384fa0cd52f8575c2e15d71a12d320f9dc4b2c3/pywavelets-1.10.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:761af8d319c9c104969b1a92e0e9c41aad34ca98f229cf87060f202cd9e633eb", size = 4329364, upload-time = "2026-09-07T17:13:56.581Z" }, + { url = "https://files.pythonhosted.org/packages/cf/0f/3b2762f936510c0344ce8b97438720ee9f1879a83316410ba475bdeea463/pywavelets-1.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4bfeaada62a12664626b21cfc41e5d0538b3c238dde22e78ea4769fac4bf6160", size = 4287304, upload-time = "2026-09-07T17:13:58.4Z" }, + { url = "https://files.pythonhosted.org/packages/6c/15/dc9a391a7bcf191f6f175492018fb0d3cce3e42784bd759c39e3f422ec4d/pywavelets-1.10.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0699c4c095f4a3121fcedfdb2696d8b173bd7bd47346e2ab7f49f4fc21e71263", size = 4406919, upload-time = "2026-09-07T17:14:00.266Z" }, + { url = "https://files.pythonhosted.org/packages/57/b7/4b0bec531c157f5c933df20964a95a793fbd8c933fcc5eddaa0ae9ad35ce/pywavelets-1.10.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d27de9fd9d76d2dcd2debd12f8ac236697cec82345dbada5a0646821cae8ea85", size = 4463469, upload-time = "2026-09-07T17:14:01.94Z" }, + { url = "https://files.pythonhosted.org/packages/a4/7d/a7a54a2fc2aae1c097b03604f4dcb488014134c544d36c7ae78e4b16f58a/pywavelets-1.10.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:86fe1cd2cd42e33f21f92a249077988767eb209bd98af07ce4eba431bb8205f2", size = 4413686, upload-time = "2026-09-07T17:14:03.788Z" }, + { url = "https://files.pythonhosted.org/packages/4c/ef/2decd0c6bbb9b508f985dac9208257cf82c5ffa70721bbdd47be4d7d7b16/pywavelets-1.10.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:45f4ea69941424e4268955f29c34ecc9bf26eeef2df3105689e404075693d149", size = 4468051, upload-time = "2026-09-07T17:14:05.446Z" }, + { url = "https://files.pythonhosted.org/packages/3f/64/f7c0fd52d21e2a0a04116a86a94b0178d1e861550a5d7079b1828215a7bb/pywavelets-1.10.0-cp313-cp313-win32.whl", hash = "sha256:d2c09ee7c5f975b1e176a73d4df105fa6bcb547517bf5e54a6cc304d5e0d2d09", size = 4176584, upload-time = "2026-09-07T17:14:07.365Z" }, + { url = "https://files.pythonhosted.org/packages/73/89/bc45f5be9b4141aa8a9b4dec2340c32301e6f0e968fc348ca59973ab3f7a/pywavelets-1.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:47179b616e3afc53bfdc1274437af9b541bb1ddf2404929599affdfbd21e51c4", size = 4280183, upload-time = "2026-09-07T17:14:09.188Z" }, + { url = "https://files.pythonhosted.org/packages/a1/59/0fcd739c2a289f741e3ed43832f436a046c751c23a8c8d1b1f41903172f5/pywavelets-1.10.0-cp313-cp313-win_arm64.whl", hash = "sha256:74003c55544b4c090f340cae72e279148fbc5e7994ff85c61dcd9b1feda90311", size = 4174810, upload-time = "2026-09-07T17:14:10.765Z" }, +] + +[[package]] +name = "pyyaml" +version = "6.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6d/16/a95b6757765b7b031c9374925bb718d55e0a9ba8a1b6a12d25962ea44347/pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e", size = 185826, upload-time = "2025-09-25T21:31:58.655Z" }, + { url = "https://files.pythonhosted.org/packages/16/19/13de8e4377ed53079ee996e1ab0a9c33ec2faf808a4647b7b4c0d46dd239/pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824", size = 175577, upload-time = "2025-09-25T21:32:00.088Z" }, + { url = "https://files.pythonhosted.org/packages/0c/62/d2eb46264d4b157dae1275b573017abec435397aa59cbcdab6fc978a8af4/pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c", size = 775556, upload-time = "2025-09-25T21:32:01.31Z" }, + { url = "https://files.pythonhosted.org/packages/10/cb/16c3f2cf3266edd25aaa00d6c4350381c8b012ed6f5276675b9eba8d9ff4/pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00", size = 882114, upload-time = "2025-09-25T21:32:03.376Z" }, + { url = "https://files.pythonhosted.org/packages/71/60/917329f640924b18ff085ab889a11c763e0b573da888e8404ff486657602/pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d", size = 806638, upload-time = "2025-09-25T21:32:04.553Z" }, + { url = "https://files.pythonhosted.org/packages/dd/6f/529b0f316a9fd167281a6c3826b5583e6192dba792dd55e3203d3f8e655a/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a", size = 767463, upload-time = "2025-09-25T21:32:06.152Z" }, + { url = "https://files.pythonhosted.org/packages/f2/6a/b627b4e0c1dd03718543519ffb2f1deea4a1e6d42fbab8021936a4d22589/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4", size = 794986, upload-time = "2025-09-25T21:32:07.367Z" }, + { url = "https://files.pythonhosted.org/packages/45/91/47a6e1c42d9ee337c4839208f30d9f09caa9f720ec7582917b264defc875/pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b", size = 142543, upload-time = "2025-09-25T21:32:08.95Z" }, + { url = "https://files.pythonhosted.org/packages/da/e3/ea007450a105ae919a72393cb06f122f288ef60bba2dc64b26e2646fa315/pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf", size = 158763, upload-time = "2025-09-25T21:32:09.96Z" }, + { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" }, + { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" }, + { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" }, + { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" }, + { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" }, + { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" }, + { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" }, + { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" }, + { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" }, + { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" }, + { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" }, + { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" }, + { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" }, + { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" }, + { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" }, + { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" }, + { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" }, + { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" }, + { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" }, + { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" }, +] + +[[package]] +name = "rasterio" +version = "1.4.4" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.12' and sys_platform == 'win32'", + "python_full_version < '3.12' and sys_platform == 'emscripten'", + "python_full_version < '3.12' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +dependencies = [ + { name = "affine" }, + { name = "attrs" }, + { name = "certifi" }, + { name = "click" }, + { name = "click-plugins" }, + { name = "cligj" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" } }, + { name = "pyparsing" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ec/fa/fce8dc9f09e5bc6520b6fc1b4ecfa510af9ca06eb42ad7bdff9c9b8989d0/rasterio-1.4.4.tar.gz", hash = "sha256:c95424e2c7f009b8f7df1095d645c52895cd332c0c2e1b4c2e073ea28b930320", size = 445004, upload-time = "2025-12-12T18:01:08.971Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c6/0d/d3859e49ab94464de2623fec82c6798d8d7c8bea2473cd2696fc5e09f717/rasterio-1.4.4-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:b8eea428b5f0c78a963f6003a19b60777df83a0aba8c28231d65431e32ac160e", size = 21144125, upload-time = "2025-12-12T17:58:59.511Z" }, + { url = "https://files.pythonhosted.org/packages/aa/3c/97ba4b146309cdc0e36f289b02ac69465b026a21afc828e4e4e1dc39466a/rasterio-1.4.4-cp311-cp311-macosx_15_0_x86_64.whl", hash = "sha256:1cc0ea5aa0d22f5f349aa221674481de689b7b3a99607ce6bb58a29e5be54d17", size = 25746406, upload-time = "2025-12-12T17:59:02.902Z" }, + { url = "https://files.pythonhosted.org/packages/ce/33/75f81bd837ac2336b24456fdb249597a4b9af2a212b7151f64d09022be36/rasterio-1.4.4-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:7eb25b23666b29dadfc49a59206cead62c99190584b61771bba0e95f7da06801", size = 34587242, upload-time = "2025-12-12T17:59:05.848Z" }, + { url = "https://files.pythonhosted.org/packages/f9/77/3869a426f6e752dde13f3868cdf16253ca0214f92107db79c1583c9aa07b/rasterio-1.4.4-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:e24b7b8c2df801dde2a1dffb44c58902bd76b5cab740dc11de4ff9963992a71a", size = 35881871, upload-time = "2025-12-12T17:59:09.779Z" }, + { url = "https://files.pythonhosted.org/packages/66/d0/3818859ddbd3750d0ef5a6580a3272e81764286d943c689dd41e49b8b786/rasterio-1.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:0718630f607be2f5742d8e4b34b434746fd788a192d77eefc9bb924399fea802", size = 25716477, upload-time = "2025-12-12T17:59:13.519Z" }, + { url = "https://files.pythonhosted.org/packages/4b/02/039eb4970c93aaef4c9eb1ee159abad18e6e7f932c2eed575c95f78d94f6/rasterio-1.4.4-cp311-cp311-win_arm64.whl", hash = "sha256:0308ff4762ae9eb40a991f12d758626b59af4376b13675480391dd7295d17bbf", size = 24075993, upload-time = "2025-12-12T17:59:16.407Z" }, + { url = "https://files.pythonhosted.org/packages/4c/fc/63d89ddfcb4643730553683ee322566b9b15fe56d026e4c21c4f4f5d9d26/rasterio-1.4.4-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:f3c4f0cbd188f893011f2a0a6dc2852b3892799b3a0d79eddf92f2b115ec7ed7", size = 21120715, upload-time = "2025-12-12T17:59:19.35Z" }, + { url = "https://files.pythonhosted.org/packages/43/70/2c003f76a23dbb078fdee35c8e2ec490d2ad8982f4dc956ba08b56027b87/rasterio-1.4.4-cp312-cp312-macosx_15_0_x86_64.whl", hash = "sha256:6fce26090b9f509eab337228420145947c491a13628965410f25bc3e6e05cf75", size = 25732944, upload-time = "2025-12-12T17:59:22.533Z" }, + { url = "https://files.pythonhosted.org/packages/f6/cc/4a8e92362c0ff496dd1007c3dcba66e9ededf1a45eca8ad1db302b071c49/rasterio-1.4.4-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:c1c722da390dc264aeccdc0dc200ca37923875d910ca4cd5bec0fec351bb818e", size = 34295209, upload-time = "2025-12-12T17:59:26.035Z" }, + { url = "https://files.pythonhosted.org/packages/e6/6d/717d2dec47fbefad33ca0d27bd5f0d543b1d1bc9fcab5ef82a13adaaf38d/rasterio-1.4.4-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:98b6dfb8282b2a54b9d75c3dc8d2520a69bbc66916c7d43de8e0bbf6e0240ca1", size = 35661866, upload-time = "2025-12-12T17:59:29.928Z" }, + { url = "https://files.pythonhosted.org/packages/ed/60/ae3351fba2726ec0976974ce2eb030c159edd3363b8771e832b8db571c24/rasterio-1.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:9513f4c7a6d93b45098f8dff2421fa9516604e3bfbf35aa144484a88d36a321f", size = 25682853, upload-time = "2025-12-12T17:59:35.869Z" }, + { url = "https://files.pythonhosted.org/packages/38/ee/35387296bbacfc5cbbb4273228b1b959793d3ce38b0402a07f11a248420b/rasterio-1.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:60b49a482e0f12f12ce9d2cc3090add02f89f3d422e85f2cffaa9207adb83c04", size = 24043249, upload-time = "2025-12-12T17:59:39.915Z" }, + { url = "https://files.pythonhosted.org/packages/c1/fe/e3e37041c49956f4f4cbe473c3fe290aaba96ed20e9c07da304e0cad2015/rasterio-1.4.4-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:df26c96aa81ffbd0b33189680859211eadf9950123c21579f84de73bb0f91d81", size = 21107336, upload-time = "2025-12-12T17:59:43.585Z" }, + { url = "https://files.pythonhosted.org/packages/f3/02/c217fdcc8e80a4b7d1b1bc4529d78f98452816e9add53ff8742049a77ae7/rasterio-1.4.4-cp313-cp313-macosx_15_0_x86_64.whl", hash = "sha256:b3af0ecc922a80f3755516629f7948e37bade9077b5f5c12a3869a5e7f01619b", size = 25719929, upload-time = "2025-12-12T17:59:47.64Z" }, + { url = "https://files.pythonhosted.org/packages/c0/d0/7f177f37bc9595d809dabb0073abd0c42358469f6b10875192b46331c652/rasterio-1.4.4-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:7ce3b0f9a22e95a27790087908753973644d7c3877d495ec9bd6e04a25233ca4", size = 34198845, upload-time = "2025-12-12T17:59:52.405Z" }, + { url = "https://files.pythonhosted.org/packages/7b/84/66c0d9cca2a09074ec2ce6fffa87709ca51b0d197ae742d835e841bac660/rasterio-1.4.4-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:c072450caa96428b1218b030500bb908fd6f09bc013a88969ff81a124b6a112a", size = 35576074, upload-time = "2025-12-12T17:59:56.392Z" }, + { url = "https://files.pythonhosted.org/packages/32/68/f7df5478458ace2fa50be43e9fab1a39957a0e71afaa3e6147ec289e0fc8/rasterio-1.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:16ee92ef10c0ba89f45f9c2b40fca9f971f357385f04ee9b716fb09cbd9ce20c", size = 25680573, upload-time = "2025-12-12T18:00:00.45Z" }, + { url = "https://files.pythonhosted.org/packages/34/e5/1bdaccb658430dfd391ad4a63d206546f36639d7e4130bf31f125c6525b4/rasterio-1.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:65c10afe64b5e488185aaff0b659e08eda22c89285b54a3e433b80e6c6621770", size = 24040367, upload-time = "2025-12-12T18:00:04.443Z" }, + { url = "https://files.pythonhosted.org/packages/32/76/54643a7d1d650fd7f1acea9093c298603e4c01bba6f90be2254310b48507/rasterio-1.4.4-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:18c2c1130e789dc2771d0aa5ec4b56d5b8a0097c648ccb94882d5ff3ab55c928", size = 21247203, upload-time = "2025-12-12T18:00:07.547Z" }, + { url = "https://files.pythonhosted.org/packages/76/ef/434b4849ccd6a3e03a0b1ac37c963c1771564945745613d15c5d96ce768d/rasterio-1.4.4-cp313-cp313t-macosx_15_0_x86_64.whl", hash = "sha256:2d1654b7ffa6f3dde42c5fd27159ae45148c11e352de26f12fe7313a3236aeed", size = 25822050, upload-time = "2025-12-12T18:00:11.081Z" }, + { url = "https://files.pythonhosted.org/packages/2d/fa/fe9a478aa0cde246da58baeb0df3248c7ca174e4d9c9b27e81b504e40a76/rasterio-1.4.4-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:c4022cbddb659856e120603b12233cec8913ae760fff220657ce888c3c6b9f9d", size = 34833783, upload-time = "2025-12-12T18:00:14.525Z" }, + { url = "https://files.pythonhosted.org/packages/04/cd/ed4716590dbcd4b8ae633417d758564e510bee4d6aaac5050a0f6d5179c5/rasterio-1.4.4-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:96b88880551a07b7a3b50439483cefbd9af91a09e19ff2b736815994e5671314", size = 35738114, upload-time = "2025-12-12T18:00:17.96Z" }, + { url = "https://files.pythonhosted.org/packages/7e/29/da7050d11ba1d041e0333ac14768e6e9ca1aa2b9fa8416f317d2650ed276/rasterio-1.4.4-cp313-cp313t-win_amd64.whl", hash = "sha256:def75d486d0ab8f306f918a913c425ed57159495518c54efe8e18d5164d37d90", size = 25896835, upload-time = "2025-12-12T18:00:21.411Z" }, + { url = "https://files.pythonhosted.org/packages/88/80/304dbe5434c4aa8dfaf90480c16d770161796a6a61fa88e72e8a402153df/rasterio-1.4.4-cp313-cp313t-win_arm64.whl", hash = "sha256:770b7e86f6c565e6f9cf30f6fa4479a5a2bab4e10ff44fe7acfd518ca4a71d1b", size = 24128074, upload-time = "2025-12-12T18:00:24.653Z" }, +] + +[[package]] +name = "rasterio" +version = "1.5.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.12' and sys_platform == 'win32'", + "python_full_version >= '3.12' and sys_platform == 'emscripten'", + "python_full_version >= '3.12' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +dependencies = [ + { name = "affine" }, + { name = "attrs" }, + { name = "certifi" }, + { name = "click" }, + { name = "numpy", version = "2.5.4", source = { registry = "https://pypi.org/simple" } }, + { name = "pyparsing" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/51/90/bd0a124e164f5fe776084c9731b43ab136b31281a18608e617cdb5f2be70/rasterio-1.5.2.tar.gz", hash = "sha256:e65a15b7bd22ce8f8ce8159856669dc9fafabf66cde6156e8f8e71d55abcd515", size = 458586, upload-time = "2026-09-30T15:57:14.889Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/74/db/9937f3e5c779a62c4c1c961d62384220e66f224526bdb57d440dfe34fb34/rasterio-1.5.2-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:89821de2f1d9e9f637f9bc0c466a2a6499b2a96db68909e0c21ff7ce6eb5a63e", size = 23461311, upload-time = "2026-09-30T15:55:32.22Z" }, + { url = "https://files.pythonhosted.org/packages/38/d0/a2473a5b6997d58b5c433ace8f31549821012256fd2cf4d7ff9f0743ce42/rasterio-1.5.2-cp312-cp312-macosx_15_0_x86_64.whl", hash = "sha256:078e0486cfd15af4cee62842af71d6fb9e0f2bdab624c14527d929acfde6fe47", size = 25076022, upload-time = "2026-09-30T15:55:35.044Z" }, + { url = "https://files.pythonhosted.org/packages/cc/d8/948607e5f14971026ed2cc0fae1f54582748a8c03ccba0127bde4fd0ada6/rasterio-1.5.2-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:0459e4d999ed219d8dff48b71523d3643c33d5ce2ec6a793477dc09592f9e84b", size = 37721851, upload-time = "2026-09-30T15:55:37.731Z" }, + { url = "https://files.pythonhosted.org/packages/06/c5/860f1c58249b5229722bcde42b4c9c88766450107977f57d283579a7934b/rasterio-1.5.2-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:8d0f9c1ba8975fe2980bbef313310982eeea0558f8c8f4989aed5fc6fbeac5c0", size = 39205905, upload-time = "2026-09-30T15:55:40.791Z" }, + { url = "https://files.pythonhosted.org/packages/d4/b7/91643e597cd59c7c74fa6ae1b5f48a6baf04a8b457434e80059fca8c392d/rasterio-1.5.2-cp312-cp312-win_amd64.whl", hash = "sha256:508d8ca45893fea9785128b6206e0347300d015a7dc453822f9d25a376aa3754", size = 30703173, upload-time = "2026-09-30T15:55:43.484Z" }, + { url = "https://files.pythonhosted.org/packages/3e/76/e2ceccbc5fe63db14100780c72a4d3bd5622bc1f807f95a22a096807ea6d/rasterio-1.5.2-cp312-cp312-win_arm64.whl", hash = "sha256:c148628357f43a54d7b26e9ef52ed0be3cc9d3e33456cff6a72ddd8347633287", size = 29568812, upload-time = "2026-09-30T15:55:46.116Z" }, + { url = "https://files.pythonhosted.org/packages/3d/09/6364633f9716019abb748e1f3f8166f108b905d850b73445dd8bd05fb811/rasterio-1.5.2-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:de9db8f891c63e6a1d8deb7d4c8fe703795245ad3b2572d35e0ec76b39495f29", size = 23461761, upload-time = "2026-09-30T15:55:48.982Z" }, + { url = "https://files.pythonhosted.org/packages/d8/dd/5dc8460b5e090bf931e1c2e69e8662946eac46b8d426eab7625ec9015b34/rasterio-1.5.2-cp313-cp313-macosx_15_0_x86_64.whl", hash = "sha256:19b8849ac84c6c26208314c7e516062b8aaabc1aa45f06c7edf22d5b098a7f84", size = 25080627, upload-time = "2026-09-30T15:55:51.441Z" }, + { url = "https://files.pythonhosted.org/packages/3c/6b/f8cc1a79b926bd3e10766ad4718082836b6ad433ac72c05c8ed2ac09d382/rasterio-1.5.2-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:f85cec5d23e7cd8d22a4b4edba11f63a94008c396a03433b8fb260140c00cb90", size = 37640915, upload-time = "2026-09-30T15:55:53.966Z" }, + { url = "https://files.pythonhosted.org/packages/d1/ef/681c3b3a97c9e38035b5f8f36115958568d8be18352fa2c9952c9e88f4a8/rasterio-1.5.2-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:be2d2a825d545e6c6e8b2aa0d67c963e9ffc44ce3cecbab4ffe95dc87c0fc0de", size = 39113469, upload-time = "2026-09-30T15:55:57.024Z" }, + { url = "https://files.pythonhosted.org/packages/07/e1/bbe71985a0a76403f5189a6c653dc94fe36ddd4a02cc0e3a55d6436e06c2/rasterio-1.5.2-cp313-cp313-win_amd64.whl", hash = "sha256:edbf60e95cb26604b7b884a7edf64a778a0f5ab64aed6f0b7dc9c1664967ae0c", size = 30696523, upload-time = "2026-09-30T15:55:59.565Z" }, + { url = "https://files.pythonhosted.org/packages/c6/ec/09bd48f32f6c6aeea00f9aa664ff1e38ac918223c0bfe378117b9baf62e3/rasterio-1.5.2-cp313-cp313-win_arm64.whl", hash = "sha256:eba030745bd573df0dbecc19ed6a22f6b2037e7b1785170f84115a7c58bea72e", size = 29565599, upload-time = "2026-09-30T15:56:02.251Z" }, +] + +[[package]] +name = "referencing" +version = "0.37.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "rpds-py" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/22/f5/df4e9027acead3ecc63e50fe1e36aca1523e1719559c499951bb4b53188f/referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8", size = 78036, upload-time = "2025-10-13T15:30:48.871Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231", size = 26766, upload-time = "2025-10-13T15:30:47.625Z" }, +] + +[[package]] +name = "requests" +version = "2.34.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ac/c3/e2a2b89f2d3e2179abd6d00ebd70bff6273f37fb3e0cc209f48b39d00cbf/requests-2.34.2.tar.gz", hash = "sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed", size = 142856, upload-time = "2026-05-14T19:25:27.735Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl", hash = "sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0", size = 73075, upload-time = "2026-05-14T19:25:26.443Z" }, +] + +[[package]] +name = "rich" +version = "15.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown-it-py" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c0/8f/0722ca900cc807c13a6a0c696dacf35430f72e0ec571c4275d2371fca3e9/rich-15.0.0.tar.gz", hash = "sha256:edd07a4824c6b40189fb7ac9bc4c52536e9780fbbfbddf6f1e2502c31b068c36", size = 230680, upload-time = "2026-04-12T08:24:00.75Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/82/3b/64d4899d73f91ba49a8c18a8ff3f0ea8f1c1d75481760df8c68ef5235bf5/rich-15.0.0-py3-none-any.whl", hash = "sha256:33bd4ef74232fb73fe9279a257718407f169c09b78a87ad3d296f548e27de0bb", size = 310654, upload-time = "2026-04-12T08:24:02.83Z" }, +] + +[[package]] +name = "rpds-py" +version = "2026.9.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/42/68/3bd46b8a5e01d3c2ebdf9c5e9497912e3fe0cde02bac21a7130ca866e403/rpds_py-2026.9.1.tar.gz", hash = "sha256:4793ef7f78268b124b73fa933440f01d258bbae01de9fa53e9080c9ab0425a12", size = 63948, upload-time = "2026-10-04T16:32:36.469Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f5/6d/ddb8dfcf41892ba9c672d27988592c038d30db61708230313831b6780a1b/rpds_py-2026.9.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:2711d29b653b3bce48a63d18b9c6b53274669e6d6c4094dddeb4d9a0e45128b2", size = 344947, upload-time = "2026-10-04T16:28:55.207Z" }, + { url = "https://files.pythonhosted.org/packages/9e/b2/632c3d034961149eff606e3e357547620a9ab37b849ae5815f67838753fd/rpds_py-2026.9.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3231c4c0e521dafa5be0c9f114ee2c2ad46650836f2d72caa86801950c3e7044", size = 346770, upload-time = "2026-10-04T16:28:56.884Z" }, + { url = "https://files.pythonhosted.org/packages/6d/45/4df346410bb873d03ec60c3b891d1fceb8cabde1971d29844bbb625cc296/rpds_py-2026.9.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e01b3c878c8641913e688edd1b3f08658c6783d29cf6b826bd3c0d1ae7a1ffaa", size = 377795, upload-time = "2026-10-04T16:28:58.193Z" }, + { url = "https://files.pythonhosted.org/packages/ba/b8/a70f302207e4f29f873c01a986fcac5def49b9c8224328cef1ad7da6b7a9/rpds_py-2026.9.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3e524c7874ac72884d28e16dd5b8d839fd09e0fe76b020d3fbca23212a7b8c52", size = 383137, upload-time = "2026-10-04T16:28:59.411Z" }, + { url = "https://files.pythonhosted.org/packages/13/e3/3aa28a48a01169d0e20728bf0e5ebbea3ae2774285b40853ae49b3ef332a/rpds_py-2026.9.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:761fdae6728ceb99ab182fad2f0cc1e262f610834dc891aea1d1a2a2e634776f", size = 496789, upload-time = "2026-10-04T16:29:01.011Z" }, + { url = "https://files.pythonhosted.org/packages/3e/a7/414e8d088602d66ad6f47c65bf07fe5090992a8574cdd4571c1a83b647ed/rpds_py-2026.9.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6b723eb406dec5bc9ec516c73ab9c3239a3284e017f7eb89ee2b3258bd504fb7", size = 395929, upload-time = "2026-10-04T16:29:02.465Z" }, + { url = "https://files.pythonhosted.org/packages/2d/5c/aac3b960fd55d37b49f77c0eebbe043eaf36762ebd48e7b46951e59c1c05/rpds_py-2026.9.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:136a1c3fe4402b7008bc81cb62ee538481795b61a7e83df88dff3b3f02b726ff", size = 375426, upload-time = "2026-10-04T16:29:03.944Z" }, + { url = "https://files.pythonhosted.org/packages/d3/80/77987a41c40be7202a78d9aed4a650bfb05792237cfd3b9e22638d990fe5/rpds_py-2026.9.1-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:839dde845559254f34885267c6878f60d61d5205180226d976fe488d45fa128e", size = 381497, upload-time = "2026-10-04T16:29:05.308Z" }, + { url = "https://files.pythonhosted.org/packages/b8/d0/640b02eb1ecd71a913c1178b6235287b31006266bca9a26862e80e497b54/rpds_py-2026.9.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:56cd8b3f77d7b6812f533b662186a1f28316931166ddc00fb893b1b0db7e9888", size = 405740, upload-time = "2026-10-04T16:29:06.578Z" }, + { url = "https://files.pythonhosted.org/packages/3e/8c/d034195d45d215625a23f2ae85dbbdb86a49a93a51272061c97c80b9d316/rpds_py-2026.9.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:074a4d198bc34d9a8ea425114fc3ded6d11ec01f6a314a8db67454a5152d8834", size = 555850, upload-time = "2026-10-04T16:29:08.115Z" }, + { url = "https://files.pythonhosted.org/packages/de/f8/532f1addfc86207bc3cf23749638bb0d457bce891595012ed8a08d0543b8/rpds_py-2026.9.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6beb738155fe8ab8091afdfa5a3226b21c2b1593f1e50ebb90eb25b44dbc0391", size = 618661, upload-time = "2026-10-04T16:29:09.554Z" }, + { url = "https://files.pythonhosted.org/packages/50/5b/b0f2c9833a36a5b1f6ac5cb0e635fabacf713266f46ceefba2102da0bf51/rpds_py-2026.9.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:57492a550a1d88d29d003247e5f78dd8cf04a701fac0e4c8db8745a6d2504e0a", size = 583530, upload-time = "2026-10-04T16:29:11.072Z" }, + { url = "https://files.pythonhosted.org/packages/bf/20/464ed4b98d3c8c6a334f3b5e24d3886de04b05658a95fc40a37e0e6e4775/rpds_py-2026.9.1-cp311-cp311-win32.whl", hash = "sha256:d95a354e02393eada6d7351184671aced9d4cce109dabf927cb7aa99624352a1", size = 208125, upload-time = "2026-10-04T16:29:12.408Z" }, + { url = "https://files.pythonhosted.org/packages/df/c9/384a9b62f8cd89c8e3e609b8c9052c140e8c01d807261e0517c904b8bf29/rpds_py-2026.9.1-cp311-cp311-win_amd64.whl", hash = "sha256:ce4d4f52e2a4324396caddbd45a97d8d7be5f42edd25d2355282a9c34f9b2f7f", size = 226045, upload-time = "2026-10-04T16:29:13.751Z" }, + { url = "https://files.pythonhosted.org/packages/4f/3b/0a691f0dcb2301fa9c736346da2d0d4991778373283301c5c12b8f952f3d/rpds_py-2026.9.1-cp311-cp311-win_arm64.whl", hash = "sha256:fdcd198979b4ecffcc1beba366a7fbcf4eb41243691a82fe52ceb0b902f09c12", size = 222636, upload-time = "2026-10-04T16:29:14.989Z" }, + { url = "https://files.pythonhosted.org/packages/5d/34/a828586ea3329fbb50895b50e9cf98ca3d924a9f41a0b26d443bff1b3794/rpds_py-2026.9.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:50906f5aea24b5a865cbd0a589698288631d9f3a54c3a937c83aefa95a0d14af", size = 346822, upload-time = "2026-10-04T16:29:16.258Z" }, + { url = "https://files.pythonhosted.org/packages/90/81/ac6a0d064982251856ce009c9e1dd51a34110b3c055aa7d1aad18b4899a3/rpds_py-2026.9.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e21c1429e205828ea886a2293a4a2c8e01f4c25d9893ca330e97a6cf73f52e7b", size = 341380, upload-time = "2026-10-04T16:29:17.706Z" }, + { url = "https://files.pythonhosted.org/packages/42/ff/bf7d54f362748fd6a49277b9d6531c394074110fedf50ad791ec59133fbb/rpds_py-2026.9.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2693b2728bbcc48d09a981a356954b0c47c53ff25b545856f28a889ea619f69a", size = 373053, upload-time = "2026-10-04T16:29:19.063Z" }, + { url = "https://files.pythonhosted.org/packages/60/de/74b0ccdbbd28687b9b5fdb34c1cabed88352182facaced9d6f5486b8b9ed/rpds_py-2026.9.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8601470267d938bcb7f3ab1a336100af51a4fd5b6ed030ef52461bb3ef5e7e07", size = 377742, upload-time = "2026-10-04T16:29:20.543Z" }, + { url = "https://files.pythonhosted.org/packages/c2/6d/b979775a3057b2a5c26d75ecbff60a20a24ef081db6dd76836fca3f20247/rpds_py-2026.9.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3890a6aa36e6baa53d5258a2a25d3ef8b37ad165a6ab27a892d7c3e3a432cd69", size = 487289, upload-time = "2026-10-04T16:29:21.958Z" }, + { url = "https://files.pythonhosted.org/packages/1d/5d/7c34734ce3ece943d9d6ee0122a74a21bbc75b47acb5b7053c83f6efb1ed/rpds_py-2026.9.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6b5b393eda5ea42cca1c1a6665f2a4882b4fd5d1777e41ce0545a107fb008c9d", size = 390710, upload-time = "2026-10-04T16:29:23.378Z" }, + { url = "https://files.pythonhosted.org/packages/1d/6d/b26eb1e75395925b3a142ed351cbed2ec8212f5c9d937aee3df32c701baa/rpds_py-2026.9.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:addeda51556dac7c1a2f14cda62db8b621cd12afba3091d03a96c72932387eab", size = 371261, upload-time = "2026-10-04T16:29:25.1Z" }, + { url = "https://files.pythonhosted.org/packages/7c/98/b2fdfe10301a9e27af21e634337fbba7baac0bc17fe44619a17c26bba2cb/rpds_py-2026.9.1-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:d9edf30457d74eebfd76b045535e36f1cd89062566a128a0db2145ca042d787e", size = 376075, upload-time = "2026-10-04T16:29:26.748Z" }, + { url = "https://files.pythonhosted.org/packages/c5/b1/c4b8d954e49c3c69cf8063c0c9e0919c99f3d2cecc46311606a1cbe835dc/rpds_py-2026.9.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:815d26356930846a40c7bc1366e7b1b0320ab8a063e66c11298a208bed0fd237", size = 399358, upload-time = "2026-10-04T16:29:28.282Z" }, + { url = "https://files.pythonhosted.org/packages/b3/59/9559a7293c97dff0cbb293efffbd36644103883ecc27741af8ef90c84ca8/rpds_py-2026.9.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3b5a6f40f0a1486b4b36c888123afc67acdbd9f33235927acf5ff295429a0ba3", size = 550267, upload-time = "2026-10-04T16:29:29.851Z" }, + { url = "https://files.pythonhosted.org/packages/6c/9d/6dc60e49511de4c8b00e74f9c1d43aed4d27f712cdb1ade92f2c199cbf64/rpds_py-2026.9.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b5b8b0753718d258fd454283fbd57e14545d3b40583fa672e27cb4f987626bcc", size = 614018, upload-time = "2026-10-04T16:29:31.54Z" }, + { url = "https://files.pythonhosted.org/packages/fb/bc/92a4ecf27301b886232a413360f6b348a81d55ffef654f2ebda5970ebc3d/rpds_py-2026.9.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:46d80bc76b51a6c24f9944368c28d38b8bcbcea1da4f2f8d3ebc31a67e8c6ec6", size = 578509, upload-time = "2026-10-04T16:29:33.01Z" }, + { url = "https://files.pythonhosted.org/packages/08/53/efb97f2e6589b7ab8394385a73870e0b2ab2a195281c117b21fd6d7d4855/rpds_py-2026.9.1-cp312-cp312-win32.whl", hash = "sha256:befc2d6a953e563f8a7bfd87a42c22ebf8a3e980dcb7b6a4d17b70b0e914e8a3", size = 205401, upload-time = "2026-10-04T16:29:34.451Z" }, + { url = "https://files.pythonhosted.org/packages/1c/84/1700cc748d0eaa747486e4e82882d2575224e6e9a3148df583058850c629/rpds_py-2026.9.1-cp312-cp312-win_amd64.whl", hash = "sha256:5ce8943f79c2210f7abcc28e86367b03b28d95027fd01c46d2472373ae70c86f", size = 222923, upload-time = "2026-10-04T16:29:35.709Z" }, + { url = "https://files.pythonhosted.org/packages/c3/89/0302215373c2f8b4408b0fdfee955368cc378a98ff59508d47a7e0dc2dbf/rpds_py-2026.9.1-cp312-cp312-win_arm64.whl", hash = "sha256:501909f2e4a1e2dee528ef766fe3c469060ebc17e54a8383d404ba07a81a6f02", size = 218415, upload-time = "2026-10-04T16:29:37.263Z" }, + { url = "https://files.pythonhosted.org/packages/83/ea/ee88fd9e756ff93fb6b1182a47ec09504a242620e33ce1d20679efefe841/rpds_py-2026.9.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:a36b70596407634ca82d4b989a3729074a008537a0522e4c8046a67c729103e9", size = 346229, upload-time = "2026-10-04T16:29:38.82Z" }, + { url = "https://files.pythonhosted.org/packages/57/71/a097d6552f837500fc36e6b23d09cfb9890c3cc47531f9ca64e149799615/rpds_py-2026.9.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:eba5d173f7d5708b22a93815017a4611873ed54db9f268077c0dd1ed99cfc858", size = 340514, upload-time = "2026-10-04T16:29:40.405Z" }, + { url = "https://files.pythonhosted.org/packages/bd/b7/497e85768bf4e0d8ddbaa096a4cac31d1509251dee2728a8490aa367e0b5/rpds_py-2026.9.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:457866b85daf5034296666168b84a69e0b2e89dc4f1af102b46f6448a60b9063", size = 372611, upload-time = "2026-10-04T16:29:41.778Z" }, + { url = "https://files.pythonhosted.org/packages/52/4b/74ab4108916250b6e198e0d3af05bc6835eb046315f22f7a0ceb49667c5a/rpds_py-2026.9.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a3a52a3ba86436ab3aef510fbe21512abc2ddd1993005dfe50514bd2284ef025", size = 377712, upload-time = "2026-10-04T16:29:43.242Z" }, + { url = "https://files.pythonhosted.org/packages/0c/8e/067e77d9d7b3cc793c9d909b7e97e7aadbbb1fb094093b6876902cc96d38/rpds_py-2026.9.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d7841166b7fa64c9c56404617ae4341448847482d45933b13135d26c130519e5", size = 488106, upload-time = "2026-10-04T16:29:44.692Z" }, + { url = "https://files.pythonhosted.org/packages/3c/b4/c5aae6c2dde269bf955f6b7d35065c655a57e47750d9668052ad67e74dda/rpds_py-2026.9.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:926bdd3e3b5998ddf70cc64bc8cf57209571f9044542913afb673799fec77dd0", size = 390795, upload-time = "2026-10-04T16:29:46.129Z" }, + { url = "https://files.pythonhosted.org/packages/a0/36/76fab39973ee11e7f9f357c55138197bb01c86f6502cb76487e3b4f42db0/rpds_py-2026.9.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7868b85224291c6cb6759f9b5adb9745f486d226f62b16a614dd5a2a5ab2b35b", size = 370794, upload-time = "2026-10-04T16:29:47.603Z" }, + { url = "https://files.pythonhosted.org/packages/3d/fe/cd2a80e6d7b871937a60e935c5d507aa390d143f4ff3636f640b9733d5df/rpds_py-2026.9.1-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:3cd182d7291d29b92c521a0069d9c01ba6193628a9a105531d11b40a6d731a33", size = 375673, upload-time = "2026-10-04T16:29:49.223Z" }, + { url = "https://files.pythonhosted.org/packages/6c/18/7464a9953724e55a3b3206062fa0ffdeaa519584c6aabf65d3956d94f131/rpds_py-2026.9.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e6ea1cda8d8c688278430e4268a42f5e5da3bdd74578dfadc0820c3f1766ce83", size = 398758, upload-time = "2026-10-04T16:29:50.601Z" }, + { url = "https://files.pythonhosted.org/packages/c0/86/1534b436700fd49ff411063b7c4d7e938adfabf90895b6cf1622d5a7d1f6/rpds_py-2026.9.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5943980471829f6de242a20b109de3111ba6b77e3af0ffc587028ac854b05e6c", size = 550370, upload-time = "2026-10-04T16:29:52.002Z" }, + { url = "https://files.pythonhosted.org/packages/57/1c/e1fa82a8a01e3c5820f3ba98a8b2673f642128eb368fa88871b01dd2c909/rpds_py-2026.9.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:76d3af9732d2dab69f28179b40ba2d87e2f1d5824b4a694780aa787d685e8f36", size = 613106, upload-time = "2026-10-04T16:29:53.655Z" }, + { url = "https://files.pythonhosted.org/packages/29/55/b20b8c4c3dde8755bfcd5b08492a02d0cd2e26929aedf6199ca2a377d42a/rpds_py-2026.9.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:78326f4cb4427a56ba4996c0762b63be45f06b85f086526420d2b3a66e40f84d", size = 578235, upload-time = "2026-10-04T16:29:55.157Z" }, + { url = "https://files.pythonhosted.org/packages/55/42/df3f7bbc3f7ab37a8a9db8d6c2ff2c985422899f1f7926afbf7ec3c0b8b4/rpds_py-2026.9.1-cp313-cp313-win32.whl", hash = "sha256:172e47169583f46ce118cbec68e6795d0da0f4606b488b6434f8276bca0a058c", size = 205293, upload-time = "2026-10-04T16:29:56.669Z" }, + { url = "https://files.pythonhosted.org/packages/31/9c/ba5a9569d719bfdd6ce863df4133ac6a1658cf1b07cc3534c31db729fbbc/rpds_py-2026.9.1-cp313-cp313-win_amd64.whl", hash = "sha256:3e93b2cd69a9830be33e03945cd7cda940a0a8bfcfbff41d6144f0cb0d3d8bd9", size = 222409, upload-time = "2026-10-04T16:29:58.049Z" }, + { url = "https://files.pythonhosted.org/packages/35/72/f28ca566f6c23c35bbf7445f65eb0364577b25a026305995e24f78b83d94/rpds_py-2026.9.1-cp313-cp313-win_arm64.whl", hash = "sha256:d151e148117294133bf8af7eeace085e7e87432db15ab6adf640330298a47f6f", size = 217681, upload-time = "2026-10-04T16:29:59.449Z" }, + { url = "https://files.pythonhosted.org/packages/f3/ad/f0eba548e297987964ef8088403db875b333963bf8189f3f563b6936b20e/rpds_py-2026.9.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c0d2cb595a420b34d5086db0add011e26e2c09d6a024afbac4228bf8f863a30", size = 384112, upload-time = "2026-10-04T16:31:42.643Z" }, + { url = "https://files.pythonhosted.org/packages/33/89/891fa4d81f1eae7fe262ecd0aba2c51de94a116c7f3ccfb034fa75f4c537/rpds_py-2026.9.1-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f3d6ed6a98cfd19155996605474982cc470d7601746a6439078f1a5a3fa8b050", size = 389662, upload-time = "2026-10-04T16:31:44.642Z" }, + { url = "https://files.pythonhosted.org/packages/4f/46/912cf623b2263f9c9c2d04e8508fc38dbe89983faeba5fb36ca4558ae550/rpds_py-2026.9.1-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8171b44a054e5c67fd748ada04187f1250bf35b95f85e52ab64bcf3331a923bb", size = 504637, upload-time = "2026-10-04T16:31:46.667Z" }, + { url = "https://files.pythonhosted.org/packages/9b/5c/7f3cfa7c5f01765c9f47c8398210ed0c1eb686c0e99c223cb1c6776d3fb9/rpds_py-2026.9.1-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fda1d96e542c37b6c804547dbf489c129fe7c97183a76a5ec275909ba1a063df", size = 402607, upload-time = "2026-10-04T16:31:49.068Z" }, + { url = "https://files.pythonhosted.org/packages/f0/76/2e915d4115ee453c33e6af17bc1df7e22ddc52974e178eeeb92a78c09983/rpds_py-2026.9.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:270bdcdaac5d5b6f73c5e22e7e135c7f2a50e789f71d9e241d5be8d90026e19a", size = 382762, upload-time = "2026-10-04T16:31:51.24Z" }, + { url = "https://files.pythonhosted.org/packages/0d/e5/844382925d7b84a4074ad79fc2b9d05eb6231ea243b34f247e68e519b606/rpds_py-2026.9.1-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:84a6ecc0c940169190d2c23bd969debd48c94dbc855acd60188a68d71d421608", size = 389563, upload-time = "2026-10-04T16:31:53.549Z" }, + { url = "https://files.pythonhosted.org/packages/81/82/d6e3951602dc2e24790ef9d299aa24bf5e2493df5043f2c6da00480cc447/rpds_py-2026.9.1-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ab4b2fda7c2b542f7f9d886cc6a838c5079d2b76f72e6081411faba11adde2c9", size = 416842, upload-time = "2026-10-04T16:31:55.76Z" }, + { url = "https://files.pythonhosted.org/packages/ea/1c/8d248e4ea671d525ab7daa0703ad81cc2175076fa0ccd7ef088facbd2b01/rpds_py-2026.9.1-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:44b32a7c4f0da3d28af31c259e38ddcff096f855e205ed0671d02fcf44f1ea1c", size = 562434, upload-time = "2026-10-04T16:31:57.874Z" }, + { url = "https://files.pythonhosted.org/packages/ad/38/e7ac0db898120d15dea25e3661b1242a40cb3b23d1f4db86c15a01fe0ba1/rpds_py-2026.9.1-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:a3dbc5ed9514908d5046107d7b1346bde71eea61de6e0e4919c19354f97e769f", size = 627909, upload-time = "2026-10-04T16:31:59.977Z" }, + { url = "https://files.pythonhosted.org/packages/03/c8/37e555514932cb1ee36c75349187aa882a98dee5d5654c761699ed8a0d27/rpds_py-2026.9.1-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:6eae33003518fd4cb4f83a218d5371469dd3001aa3b87128c005b07762f7fe5e", size = 593658, upload-time = "2026-10-04T16:32:02.14Z" }, + { url = "https://files.pythonhosted.org/packages/12/5c/5f69a91add94a8a33acea68a2b034a464946a654ff7296af9d957ef0d6a5/rpds_py-2026.9.1-pp311-pypy311_pp80-macosx_10_12_x86_64.whl", hash = "sha256:0483515261947e4e8b8e1375bf7463e7eb6ccfb3d86e7b554d90cd5285f20f32", size = 351769, upload-time = "2026-10-04T16:32:04.675Z" }, + { url = "https://files.pythonhosted.org/packages/41/22/35ea8bf3b2682b90ab687c74e4828418ff12298a5b0b0a0c033f61c86a30/rpds_py-2026.9.1-pp311-pypy311_pp80-macosx_11_0_arm64.whl", hash = "sha256:4cfaf02209061880210819934de2f4f6aa83dc04dafe6770276acc240a56da31", size = 351719, upload-time = "2026-10-04T16:32:06.834Z" }, + { url = "https://files.pythonhosted.org/packages/a3/90/a9ba81408369d34c1aca73319c15adc91ed45c1d21e9e8ee4832ffb7fe0b/rpds_py-2026.9.1-pp312-pypy312_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6cdc537c8633d7fd92a82e2e0d2ab74320a3f63d5e59fb9cf08711e08fe151c4", size = 381109, upload-time = "2026-10-04T16:32:08.994Z" }, + { url = "https://files.pythonhosted.org/packages/b3/44/5192a0bed94cec86bd2a5e1cc5a1bb8f7eec3aec907eacd2deb13e87e326/rpds_py-2026.9.1-pp312-pypy312_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:10e208f2425d973938afcd56e28a7c4be32e27b6a60b5d381f49fb9d8acf9759", size = 388222, upload-time = "2026-10-04T16:32:11.321Z" }, + { url = "https://files.pythonhosted.org/packages/92/cd/5356549711448f18b52f7a11ffbe90f1228774996fbf3c6cd1b18d22abaf/rpds_py-2026.9.1-pp312-pypy312_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6c0dbbcc19735fe5f8b0a54c07659d154a9e69f47e15d0a6ab7299215daf62cb", size = 500384, upload-time = "2026-10-04T16:32:13.478Z" }, + { url = "https://files.pythonhosted.org/packages/7a/88/ddda9d28adfe33119c75b2e733d4ba7e326f41d7e1b1093951771768ca48/rpds_py-2026.9.1-pp312-pypy312_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:684fd492fff4fead00587544e059be2bbcb6f93454f21fa2a91b66fc7508be82", size = 401661, upload-time = "2026-10-04T16:32:15.653Z" }, + { url = "https://files.pythonhosted.org/packages/32/c3/bb59ba16a6b4a57995d15b8c04c00f28df3b938c4dcdde48fbe0f73edb0c/rpds_py-2026.9.1-pp312-pypy312_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1028417bb44037eb3069c1009bd7b7277212876cda22fbe565b0bca9fab6d2c", size = 380748, upload-time = "2026-10-04T16:32:17.727Z" }, + { url = "https://files.pythonhosted.org/packages/f5/b8/0580faa1c5a32ddc160130dd7ae54d2d007272a89a40a6b5db3e50f9f585/rpds_py-2026.9.1-pp312-pypy312_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:492e5e428cbe126221611f47e068f01660352feec4ad18bc0f5ea9b2ae88fb14", size = 385731, upload-time = "2026-10-04T16:32:19.855Z" }, + { url = "https://files.pythonhosted.org/packages/bf/70/f73564642bbe3322c7eeef2c2f258cf040b71c41a430b532e59d04a1b838/rpds_py-2026.9.1-pp312-pypy312_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:88b5268892fde430d5531f95bc560b6efbbd67c929662c586afd729a96e7461c", size = 412131, upload-time = "2026-10-04T16:32:22.285Z" }, + { url = "https://files.pythonhosted.org/packages/8c/5d/17fff2e1f8f68721a2afb5cb48f7e442c751bb6b4883157c5abaf618cd08/rpds_py-2026.9.1-pp312-pypy312_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:01445c8d194aa032a08e944f16567672da1c62dbdbefd8b6d0693032e290cf68", size = 558193, upload-time = "2026-10-04T16:32:24.4Z" }, + { url = "https://files.pythonhosted.org/packages/2a/d0/869ab6fb08531f97aae4a780b4d61b4190d8aef48fe054ee28bcf7114466/rpds_py-2026.9.1-pp312-pypy312_pp73-musllinux_1_2_i686.whl", hash = "sha256:eef6a03b0b6d08d0835ccfa8ec8d1bc70525e3801387567137b50c557695e6da", size = 622448, upload-time = "2026-10-04T16:32:26.844Z" }, + { url = "https://files.pythonhosted.org/packages/ba/a5/9672e532fe02cd3b92c78cfd8743abca939d96853006c63835009a1ecb6f/rpds_py-2026.9.1-pp312-pypy312_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:6b9bf3135b4ad5981df9a73d71a35272d650a2985ae9c2746357b24d59de2448", size = 591126, upload-time = "2026-10-04T16:32:29.274Z" }, + { url = "https://files.pythonhosted.org/packages/80/ef/3a9f8e4279c7920af561deed4cb7ebebed2794a8f608cf404d5eef14a105/rpds_py-2026.9.1-pp312-pypy312_pp80-macosx_10_12_x86_64.whl", hash = "sha256:56c6952a9b15047466d0c2347c446a761d4527f89976156341e68f0ce5cc08b0", size = 357108, upload-time = "2026-10-04T16:32:31.641Z" }, + { url = "https://files.pythonhosted.org/packages/b7/55/4b2fa381a583760aea5c92841e4e928a358e1c6511b129219e9c2826a226/rpds_py-2026.9.1-pp312-pypy312_pp80-macosx_11_0_arm64.whl", hash = "sha256:b242c27c8f836305a4a72df9cdd564386ac57b807bd252a063223331c9316b37", size = 346877, upload-time = "2026-10-04T16:32:34.061Z" }, +] + +[[package]] +name = "ruff" +version = "0.17.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/59/73/81f140c4edd9cc323de5478e6bbd3bbcfae9a82692df1a258de8cec6a6d2/ruff-0.17.3.tar.gz", hash = "sha256:a57049c8addb3662073859bb55a7ad35aa54f8a7c9ed268911d1333c5aadaaa9", size = 5339444, upload-time = "2026-10-27T00:34:05.914Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/8c/083365310c4e79c842f25bf5a842facbd9b0c0ec336f96dffa5074fe723d/ruff-0.17.3-py3-none-linux_armv6l.whl", hash = "sha256:ab86b84cd9d05e56d329742c0cabdcbe2d1d56c98f4a04aab9b5b66196febfb4", size = 10216522, upload-time = "2026-10-27T00:33:29.822Z" }, + { url = "https://files.pythonhosted.org/packages/72/26/4b2edf31cfebe9605d8f6ed8f1d6f376d3a53d4c9a123e62661c64c440f0/ruff-0.17.3-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:e407b4a6852853a9a2cdce96963e94ad9775578bb44e7561a0f2829f3cc04b11", size = 10217786, upload-time = "2026-10-27T00:33:33.949Z" }, + { url = "https://files.pythonhosted.org/packages/a2/7e/537aba4b7f54397a374d2f6a6fc93e9c8015e18553f31a4a7b7dbced48b5/ruff-0.17.3-py3-none-macosx_11_0_arm64.whl", hash = "sha256:b75cc113b3aca388933d8296b667b1ac401a80317ea3074c9b0a66541199da29", size = 9440362, upload-time = "2026-10-27T00:33:35.784Z" }, + { url = "https://files.pythonhosted.org/packages/3d/66/283e39734f66c8a5ebaa93c22eb7b7515779a0b1d5a5c0ad20e05c0edb41/ruff-0.17.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ab5fa7ed5ce18554ea69d850ad162cbaa0c85fc47c08d83a569f806681c9a6ad", size = 10094281, upload-time = "2026-10-27T00:33:38.216Z" }, + { url = "https://files.pythonhosted.org/packages/21/4c/1ffc768497bd025a32c72152771e40c0b99bddc5cc6e4f50c7949a395346/ruff-0.17.3-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c673c170e27834d1262e3a9c1e97bdc9b9c3b180c63d89173ce69747232474c1", size = 10022543, upload-time = "2026-10-27T00:33:40.181Z" }, + { url = "https://files.pythonhosted.org/packages/73/c3/f90db547301080fcb82c92abf914c8bd57b89911d9b65a6c4cc6b52d0635/ruff-0.17.3-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6a754c0341bc9ca151b7b335988b3263921c08859f84ca219b70cee825571b6f", size = 10683921, upload-time = "2026-10-27T00:33:42.182Z" }, + { url = "https://files.pythonhosted.org/packages/e5/98/92d4a8a26c6c96cf1a5b34dd59036d8a71e52d650aab065c58867e964e63/ruff-0.17.3-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4590b9d86c0b1543ebb09094254e191cd43fe8edaf330baa035aab03da251532", size = 11373408, upload-time = "2026-10-27T00:33:44.19Z" }, + { url = "https://files.pythonhosted.org/packages/43/02/1f24d2e3064c9753926deeb901afa57777b37ee46799b09ddd7aee6e5145/ruff-0.17.3-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bbde18beccd98c29a227c63c99022e3443b99258e08326df6425dfe0443705ef", size = 10977469, upload-time = "2026-10-27T00:33:46.179Z" }, + { url = "https://files.pythonhosted.org/packages/f0/20/fecedea2209f574ab92033b344e01a7f0c2dc76c6d6d19a14c9f8141173f/ruff-0.17.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7fa4b0f931cdbcb8a52044f370a3eb429fb42eb88807b0e800fcaa98dd42b24e", size = 10577491, upload-time = "2026-10-27T00:33:48.233Z" }, + { url = "https://files.pythonhosted.org/packages/33/36/b93fdf0bc6f84f0cd487aadfb0354deb40013669ea35ac3d26855de1e959/ruff-0.17.3-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:39405f341deec934b4289634324ad695f72df2970f9121582872eaf5b722f0f5", size = 10608748, upload-time = "2026-10-27T00:33:50.284Z" }, + { url = "https://files.pythonhosted.org/packages/5d/b7/7d3f5f14145daead1efb0b9a8289ed1daa100f419d60774375951723ce00/ruff-0.17.3-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:68074094f55ca3d29ab19491f640282f7a84061c4c7977aa5094b968ca457e9a", size = 10075057, upload-time = "2026-10-27T00:33:52.074Z" }, + { url = "https://files.pythonhosted.org/packages/b0/ed/93ff7a5909ee56e2bd4b982deae9e0efb2724417874a90c9e41ad66c6dd7/ruff-0.17.3-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:27bb571ac78fef178fe5118d2827df95c2aae74a48fff2b15f6acd03af5ff5bb", size = 10022431, upload-time = "2026-10-27T00:33:54.061Z" }, + { url = "https://files.pythonhosted.org/packages/9b/07/a83b9eeec8c1c3c0e4eda15f06c3a91001bfd2687ea2f07b750719bf9712/ruff-0.17.3-py3-none-musllinux_1_2_i686.whl", hash = "sha256:029c37cbd3de48ca37aa12117bead2d065f15da17d66bf84e0d4274013705db2", size = 10394903, upload-time = "2026-10-27T00:33:56.158Z" }, + { url = "https://files.pythonhosted.org/packages/af/b2/05c39857c2526fcbebe46ad93a8dc97424656dfef14d3ef933abe1ffd331/ruff-0.17.3-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:692fdb46d0fd9c71e380947dc5fc72f2c3f881073bc1f739e4579376231affbe", size = 10799780, upload-time = "2026-10-27T00:33:58.204Z" }, + { url = "https://files.pythonhosted.org/packages/ec/d9/47171a16739eb7a353936b31c20cf54340531e68c9af7d72fd7a784e235d/ruff-0.17.3-py3-none-win32.whl", hash = "sha256:46539cb031b01fd29ae0a02142483c46640b233ee12bb1c1b690bffa1555d5ca", size = 9908481, upload-time = "2026-10-27T00:34:00.087Z" }, + { url = "https://files.pythonhosted.org/packages/d0/a0/f08c1631bfd81faa6a90eaf70c37c786c11bed2046302b56bb506c25e1a0/ruff-0.17.3-py3-none-win_amd64.whl", hash = "sha256:56b8ffb3f3b3cbccb8b76b71b38261b7ed982ef5d20f5b447b1b61a7f4949b24", size = 10341333, upload-time = "2026-10-27T00:34:02.099Z" }, + { url = "https://files.pythonhosted.org/packages/99/d5/4ad4ed31413c301430918e70d3d38ae6f097af0766cb9ab8c9d502b2a4d6/ruff-0.17.3-py3-none-win_arm64.whl", hash = "sha256:81fce74bcd6be0e9da7c67c7553fd0f1f12964a2052cd728ef917b937a5ca1d9", size = 9854332, upload-time = "2026-10-27T00:34:03.847Z" }, +] + +[[package]] +name = "scikit-image" +version = "0.26.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "imageio" }, + { name = "lazy-loader" }, + { name = "networkx", version = "3.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "networkx", version = "3.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "numpy", version = "2.5.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "packaging" }, + { name = "pillow" }, + { name = "scipy", version = "1.17.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "scipy", version = "1.18.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "tifffile", version = "2026.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "tifffile", version = "2026.10.15", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a1/b4/2528bb43c67d48053a7a649a9666432dc307d66ba02e3a6d5c40f46655df/scikit_image-0.26.0.tar.gz", hash = "sha256:f5f970ab04efad85c24714321fcc91613fcb64ef2a892a13167df2f3e59199fa", size = 22729739, upload-time = "2025-12-20T17:12:21.824Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/16/8a407688b607f86f81f8c649bf0d68a2a6d67375f18c2d660aba20f5b648/scikit_image-0.26.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b1ede33a0fb3731457eaf53af6361e73dd510f449dac437ab54573b26788baf0", size = 12355510, upload-time = "2025-12-20T17:10:31.628Z" }, + { url = "https://files.pythonhosted.org/packages/6b/f9/7efc088ececb6f6868fd4475e16cfafc11f242ce9ab5fc3557d78b5da0d4/scikit_image-0.26.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7af7aa331c6846bd03fa28b164c18d0c3fd419dbb888fb05e958ac4257a78fdd", size = 12056334, upload-time = "2025-12-20T17:10:34.559Z" }, + { url = "https://files.pythonhosted.org/packages/9f/1e/bc7fb91fb5ff65ef42346c8b7ee8b09b04eabf89235ab7dbfdfd96cbd1ea/scikit_image-0.26.0-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9ea6207d9e9d21c3f464efe733121c0504e494dbdc7728649ff3e23c3c5a4953", size = 13297768, upload-time = "2025-12-20T17:10:37.733Z" }, + { url = "https://files.pythonhosted.org/packages/a5/2a/e71c1a7d90e70da67b88ccc609bd6ae54798d5847369b15d3a8052232f9d/scikit_image-0.26.0-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:74aa5518ccea28121f57a95374581d3b979839adc25bb03f289b1bc9b99c58af", size = 13711217, upload-time = "2025-12-20T17:10:40.935Z" }, + { url = "https://files.pythonhosted.org/packages/d4/59/9637ee12c23726266b91296791465218973ce1ad3e4c56fc81e4d8e7d6e1/scikit_image-0.26.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d5c244656de905e195a904e36dbc18585e06ecf67d90f0482cbde63d7f9ad59d", size = 14337782, upload-time = "2025-12-20T17:10:43.452Z" }, + { url = "https://files.pythonhosted.org/packages/e7/5c/a3e1e0860f9294663f540c117e4bf83d55e5b47c281d475cc06227e88411/scikit_image-0.26.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:21a818ee6ca2f2131b9e04d8eb7637b5c18773ebe7b399ad23dcc5afaa226d2d", size = 14805997, upload-time = "2025-12-20T17:10:45.93Z" }, + { url = "https://files.pythonhosted.org/packages/d3/c6/2eeacf173da041a9e388975f54e5c49df750757fcfc3ee293cdbbae1ea0a/scikit_image-0.26.0-cp311-cp311-win_amd64.whl", hash = "sha256:9490360c8d3f9a7e85c8de87daf7c0c66507960cf4947bb9610d1751928721c7", size = 11878486, upload-time = "2025-12-20T17:10:48.246Z" }, + { url = "https://files.pythonhosted.org/packages/c3/a4/a852c4949b9058d585e762a66bf7e9a2cd3be4795cd940413dfbfbb0ce79/scikit_image-0.26.0-cp311-cp311-win_arm64.whl", hash = "sha256:0baa0108d2d027f34d748e84e592b78acc23e965a5de0e4bb03cf371de5c0581", size = 11346518, upload-time = "2025-12-20T17:10:50.575Z" }, + { url = "https://files.pythonhosted.org/packages/99/e8/e13757982264b33a1621628f86b587e9a73a13f5256dad49b19ba7dc9083/scikit_image-0.26.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d454b93a6fa770ac5ae2d33570f8e7a321bb80d29511ce4b6b78058ebe176e8c", size = 12376452, upload-time = "2025-12-20T17:10:52.796Z" }, + { url = "https://files.pythonhosted.org/packages/e3/be/f8dd17d0510f9911f9f17ba301f7455328bf13dae416560126d428de9568/scikit_image-0.26.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3409e89d66eff5734cd2b672d1c48d2759360057e714e1d92a11df82c87cba37", size = 12061567, upload-time = "2025-12-20T17:10:55.207Z" }, + { url = "https://files.pythonhosted.org/packages/b3/2b/c70120a6880579fb42b91567ad79feb4772f7be72e8d52fec403a3dde0c6/scikit_image-0.26.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4c717490cec9e276afb0438dd165b7c3072d6c416709cc0f9f5a4c1070d23a44", size = 13084214, upload-time = "2025-12-20T17:10:57.468Z" }, + { url = "https://files.pythonhosted.org/packages/f4/a2/70401a107d6d7466d64b466927e6b96fcefa99d57494b972608e2f8be50f/scikit_image-0.26.0-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7df650e79031634ac90b11e64a9eedaf5a5e06fcd09bcd03a34be01745744466", size = 13561683, upload-time = "2025-12-20T17:10:59.49Z" }, + { url = "https://files.pythonhosted.org/packages/13/a5/48bdfd92794c5002d664e0910a349d0a1504671ef5ad358150f21643c79a/scikit_image-0.26.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:cefd85033e66d4ea35b525bb0937d7f42d4cdcfed2d1888e1570d5ce450d3932", size = 14112147, upload-time = "2025-12-20T17:11:02.083Z" }, + { url = "https://files.pythonhosted.org/packages/ee/b5/ac71694da92f5def5953ca99f18a10fe98eac2dd0a34079389b70b4d0394/scikit_image-0.26.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3f5bf622d7c0435884e1e141ebbe4b2804e16b2dd23ae4c6183e2ea99233be70", size = 14661625, upload-time = "2025-12-20T17:11:04.528Z" }, + { url = "https://files.pythonhosted.org/packages/23/4d/a3cc1e96f080e253dad2251bfae7587cf2b7912bcd76fd43fd366ff35a87/scikit_image-0.26.0-cp312-cp312-win_amd64.whl", hash = "sha256:abed017474593cd3056ae0fe948d07d0747b27a085e92df5474f4955dd65aec0", size = 11911059, upload-time = "2025-12-20T17:11:06.61Z" }, + { url = "https://files.pythonhosted.org/packages/35/8a/d1b8055f584acc937478abf4550d122936f420352422a1a625eef2c605d8/scikit_image-0.26.0-cp312-cp312-win_arm64.whl", hash = "sha256:4d57e39ef67a95d26860c8caf9b14b8fb130f83b34c6656a77f191fa6d1d04d8", size = 11348740, upload-time = "2025-12-20T17:11:09.118Z" }, + { url = "https://files.pythonhosted.org/packages/4f/48/02357ffb2cca35640f33f2cfe054a4d6d5d7a229b88880a64f1e45c11f4e/scikit_image-0.26.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a2e852eccf41d2d322b8e60144e124802873a92b8d43a6f96331aa42888491c7", size = 12346329, upload-time = "2025-12-20T17:11:11.599Z" }, + { url = "https://files.pythonhosted.org/packages/67/b9/b792c577cea2c1e94cda83b135a656924fc57c428e8a6d302cd69aac1b60/scikit_image-0.26.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:98329aab3bc87db352b9887f64ce8cdb8e75f7c2daa19927f2e121b797b678d5", size = 12031726, upload-time = "2025-12-20T17:11:13.871Z" }, + { url = "https://files.pythonhosted.org/packages/07/a9/9564250dfd65cb20404a611016db52afc6268b2b371cd19c7538ea47580f/scikit_image-0.26.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:915bb3ba66455cf8adac00dc8fdf18a4cd29656aec7ddd38cb4dda90289a6f21", size = 13094910, upload-time = "2025-12-20T17:11:16.2Z" }, + { url = "https://files.pythonhosted.org/packages/a3/b8/0d8eeb5a9fd7d34ba84f8a55753a0a3e2b5b51b2a5a0ade648a8db4a62f7/scikit_image-0.26.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b36ab5e778bf50af5ff386c3ac508027dc3aaeccf2161bdf96bde6848f44d21b", size = 13660939, upload-time = "2025-12-20T17:11:18.464Z" }, + { url = "https://files.pythonhosted.org/packages/2f/d6/91d8973584d4793d4c1a847d388e34ef1218d835eeddecfc9108d735b467/scikit_image-0.26.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:09bad6a5d5949c7896c8347424c4cca899f1d11668030e5548813ab9c2865dcb", size = 14138938, upload-time = "2025-12-20T17:11:20.919Z" }, + { url = "https://files.pythonhosted.org/packages/39/9a/7e15d8dc10d6bbf212195fb39bdeb7f226c46dd53f9c63c312e111e2e175/scikit_image-0.26.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:aeb14db1ed09ad4bee4ceb9e635547a8d5f3549be67fc6c768c7f923e027e6cd", size = 14752243, upload-time = "2025-12-20T17:11:23.347Z" }, + { url = "https://files.pythonhosted.org/packages/8f/58/2b11b933097bc427e42b4a8b15f7de8f24f2bac1fd2779d2aea1431b2c31/scikit_image-0.26.0-cp313-cp313-win_amd64.whl", hash = "sha256:ac529eb9dbd5954f9aaa2e3fe9a3fd9661bfe24e134c688587d811a0233127f1", size = 11906770, upload-time = "2025-12-20T17:11:25.297Z" }, + { url = "https://files.pythonhosted.org/packages/ad/ec/96941474a18a04b69b6f6562a5bd79bd68049fa3728d3b350976eccb8b93/scikit_image-0.26.0-cp313-cp313-win_arm64.whl", hash = "sha256:a2d211bc355f59725efdcae699b93b30348a19416cc9e017f7b2fb599faf7219", size = 11342506, upload-time = "2025-12-20T17:11:27.399Z" }, + { url = "https://files.pythonhosted.org/packages/03/e5/c1a9962b0cf1952f42d32b4a2e48eed520320dbc4d2ff0b981c6fa508b6b/scikit_image-0.26.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:9eefb4adad066da408a7601c4c24b07af3b472d90e08c3e7483d4e9e829d8c49", size = 12663278, upload-time = "2025-12-20T17:11:29.358Z" }, + { url = "https://files.pythonhosted.org/packages/ae/97/c1a276a59ce8e4e24482d65c1a3940d69c6b3873279193b7ebd04e5ee56b/scikit_image-0.26.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:6caec76e16c970c528d15d1c757363334d5cb3069f9cea93d2bead31820511f3", size = 12405142, upload-time = "2025-12-20T17:11:31.282Z" }, + { url = "https://files.pythonhosted.org/packages/d4/4a/f1cbd1357caef6c7993f7efd514d6e53d8fd6f7fe01c4714d51614c53289/scikit_image-0.26.0-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a07200fe09b9d99fcdab959859fe0f7db8df6333d6204344425d476850ce3604", size = 12942086, upload-time = "2025-12-20T17:11:33.683Z" }, + { url = "https://files.pythonhosted.org/packages/5b/6f/74d9fb87c5655bd64cf00b0c44dc3d6206d9002e5f6ba1c9aeb13236f6bf/scikit_image-0.26.0-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:92242351bccf391fc5df2d1529d15470019496d2498d615beb68da85fe7fdf37", size = 13265667, upload-time = "2025-12-20T17:11:36.11Z" }, + { url = "https://files.pythonhosted.org/packages/a7/73/faddc2413ae98d863f6fa2e3e14da4467dd38e788e1c23346cf1a2b06b97/scikit_image-0.26.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:52c496f75a7e45844d951557f13c08c81487c6a1da2e3c9c8a39fcde958e02cc", size = 14001966, upload-time = "2025-12-20T17:11:38.55Z" }, + { url = "https://files.pythonhosted.org/packages/02/94/9f46966fa042b5d57c8cd641045372b4e0df0047dd400e77ea9952674110/scikit_image-0.26.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:20ef4a155e2e78b8ab973998e04d8a361d49d719e65412405f4dadd9155a61d9", size = 14359526, upload-time = "2025-12-20T17:11:41.087Z" }, + { url = "https://files.pythonhosted.org/packages/5d/b4/2840fe38f10057f40b1c9f8fb98a187a370936bf144a4ac23452c5ef1baf/scikit_image-0.26.0-cp313-cp313t-win_amd64.whl", hash = "sha256:c9087cf7d0e7f33ab5c46d2068d86d785e70b05400a891f73a13400f1e1faf6a", size = 12287629, upload-time = "2025-12-20T17:11:43.11Z" }, + { url = "https://files.pythonhosted.org/packages/22/ba/73b6ca70796e71f83ab222690e35a79612f0117e5aaf167151b7d46f5f2c/scikit_image-0.26.0-cp313-cp313t-win_arm64.whl", hash = "sha256:27d58bc8b2acd351f972c6508c1b557cfed80299826080a4d803dd29c51b707e", size = 11647755, upload-time = "2025-12-20T17:11:45.279Z" }, +] + +[[package]] +name = "scikit-learn" +version = "1.9.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "joblib" }, + { name = "narwhals" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "numpy", version = "2.5.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "scipy", version = "1.17.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "scipy", version = "1.18.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "threadpoolctl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d2/eb/eaf5e07fcc0da7149b0e084f24e54edd7441b9a89ce7e034032ae97fe3a0/scikit_learn-1.9.1.tar.gz", hash = "sha256:629cada3e33e2b9bf376cdc7614a47a4140b8aedc1d836579e359736fbd82977", size = 7786908, upload-time = "2026-09-10T18:34:04.679Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/79/67/1904bf57ab9ea77b5ba3899178bcb35ff8d814fc38ef6d1283bcddeaf04a/scikit_learn-1.9.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:326c188f92084bf58664229f4578eeab6176313b37cd5dfc85abd92b94130c58", size = 8823771, upload-time = "2026-09-10T18:32:29.891Z" }, + { url = "https://files.pythonhosted.org/packages/23/31/dc50d8780ac647bed9417f10a8b15ae3cf990ad7edb4f4b468f35030f8c5/scikit_learn-1.9.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:b48b2b5b41d9c5fbafef5f37b042f61110df3318ad2a45baf57287ea5b9ba5a2", size = 8290892, upload-time = "2026-09-10T18:32:32.441Z" }, + { url = "https://files.pythonhosted.org/packages/a1/aa/a7a61517eaaa8acbf6061d255a625034e4afa277caff47e829574cc21d1c/scikit_learn-1.9.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4298fcc01b3d8fa9768d36894e99cce0747b3b2dd73bfd80779e393769d0afab", size = 9026402, upload-time = "2026-09-10T18:32:34.482Z" }, + { url = "https://files.pythonhosted.org/packages/69/46/f8b95a7a16fd13b458401d5e85a63d88ecb3679f3649812d5fdbea4c8357/scikit_learn-1.9.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:52a0703bbc07ad27f560fa63fa68e4c54dd735bfbbf65b4dd3c225dc7547b6df", size = 9315422, upload-time = "2026-09-10T18:32:36.581Z" }, + { url = "https://files.pythonhosted.org/packages/29/8a/8d1e888fe3d0dd4e2a1f3356dad703eed569aaf673e222b6f647005ad30f/scikit_learn-1.9.1-cp311-cp311-win_amd64.whl", hash = "sha256:220fa18152852a5ce29c49e1eaba9d44ec44631cd2e5cf65f5a40eafa5ab3412", size = 8329877, upload-time = "2026-09-10T18:32:38.729Z" }, + { url = "https://files.pythonhosted.org/packages/97/44/8f43f8de3e025c505efbd88b9264008f39490fbaf15a5ca8dbd8dae0cb74/scikit_learn-1.9.1-cp311-cp311-win_arm64.whl", hash = "sha256:8218cb8938d3031e0d23842838425490c229ecf3e99f666776e09d12ed902352", size = 7954229, upload-time = "2026-09-10T18:32:40.661Z" }, + { url = "https://files.pythonhosted.org/packages/df/a7/25f0a43d2fde306e8ef45f45121192f687b79beaf4bae8c21607c46c5e63/scikit_learn-1.9.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0c0f8b5d09b44101cea2767f300680bada1ea27f976fe4b48b83950a4f55a49a", size = 8775209, upload-time = "2026-09-10T18:32:42.804Z" }, + { url = "https://files.pythonhosted.org/packages/60/ea/57e57539ce175d774fc291ed091b0a6d756854b92cd92554c6bb4d0ae498/scikit_learn-1.9.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:8c14ce41d561f7749f990b41d6703fe02c4669fbc485e598e069e0a1967b488e", size = 8295541, upload-time = "2026-09-10T18:32:45.069Z" }, + { url = "https://files.pythonhosted.org/packages/78/2b/5721a174406bfba49bce20ae997b3b64cf355c3f623a2638284ab6a82156/scikit_learn-1.9.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e4c20a6c017d820faa7ac8c783e3d0c6a9a2e297bf9f55332ca17cdf7fd4d04d", size = 8871592, upload-time = "2026-09-10T18:32:46.999Z" }, + { url = "https://files.pythonhosted.org/packages/8e/57/a50162f3d29feb979ab6347c6debda506dfb525bcff3c50dd17606651c7e/scikit_learn-1.9.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e5d7b18a5b9dca241a74695f3275fa4c895a9dadc72b3d8df5fa9d1083c9b83e", size = 9166043, upload-time = "2026-09-10T18:32:49.343Z" }, + { url = "https://files.pythonhosted.org/packages/72/8d/27c054166bac671770d1ea0ef7716134fe8119c0df224a58c89fd735a61c/scikit_learn-1.9.1-cp312-cp312-win_amd64.whl", hash = "sha256:4b59abb30618121cc46b45972d6bf53a7128b4df4cd346c6ca6f4d5f9031e49c", size = 8262238, upload-time = "2026-09-10T18:32:51.679Z" }, + { url = "https://files.pythonhosted.org/packages/9d/d6/493086006ea0c68ad62c40a8dece1961b61bf503f45400f133d47f56e5be/scikit_learn-1.9.1-cp312-cp312-win_arm64.whl", hash = "sha256:d5945a2908be62350e2978344e62b56c1552c2ca4f844ebf6277c94944d647dd", size = 7902102, upload-time = "2026-09-10T18:32:53.67Z" }, + { url = "https://files.pythonhosted.org/packages/bb/8d/b60d5e7354ff0ff5cc9400e60273696589d87a30b8b2235886a76d80d062/scikit_learn-1.9.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2c2b312fd8c02951a364fa120ea08c1cec10d863466bf1701b013152d7537835", size = 8739300, upload-time = "2026-09-10T18:32:55.483Z" }, + { url = "https://files.pythonhosted.org/packages/2f/81/3c6392c03665d2899457a76e535a9a6f597dddddf3220fd2e1d790da88c5/scikit_learn-1.9.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:61cd968ab831a76d0ecbaf0347ab2270268716da28f94fd022497e3d6f205f13", size = 8262364, upload-time = "2026-09-10T18:32:57.966Z" }, + { url = "https://files.pythonhosted.org/packages/0f/35/a15b8653499692879821301d48059376d6e68e8b65cd0f22d19b6ee83cd9/scikit_learn-1.9.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5990f9c69e431bfaddcde1a6d7c5355243e026bc9b9e560c13893b90dab53fb4", size = 8823698, upload-time = "2026-09-10T18:33:00.632Z" }, + { url = "https://files.pythonhosted.org/packages/23/e5/688703d357e5393f708d98eb189fd415ae69e39f6de03c6bd4005aef6118/scikit_learn-1.9.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:55e79d6e9b0923f1a978179822bd43d7f5543f45e970a00fe861f43486380aba", size = 9121732, upload-time = "2026-09-10T18:33:02.825Z" }, + { url = "https://files.pythonhosted.org/packages/96/45/a10add34c08184d373be9384660c75758128ca881ed27b503b6f6a742478/scikit_learn-1.9.1-cp313-cp313-win_amd64.whl", hash = "sha256:2070f271e5375dc42c6bb93b461ab1c0aa5841d4009267e0cfd95a39dca94a43", size = 8237244, upload-time = "2026-09-10T18:33:05.26Z" }, + { url = "https://files.pythonhosted.org/packages/9e/08/7a89bcdadd1fff0d464d01056417b646c9abcbc54f7297a0a1203bba5ebb/scikit_learn-1.9.1-cp313-cp313-win_arm64.whl", hash = "sha256:613f0a783ca05aa844a4e1ac42d48425058f2c52be73f40f8cd98b7cd111acd6", size = 7875398, upload-time = "2026-09-10T18:33:07.506Z" }, +] + +[[package]] +name = "scipy" +version = "1.17.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.12' and sys_platform == 'win32'", + "python_full_version < '3.12' and sys_platform == 'emscripten'", + "python_full_version < '3.12' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +dependencies = [ + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" } }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7a/97/5a3609c4f8d58b039179648e62dd220f89864f56f7357f5d4f45c29eb2cc/scipy-1.17.1.tar.gz", hash = "sha256:95d8e012d8cb8816c226aef832200b1d45109ed4464303e997c5b13122b297c0", size = 30573822, upload-time = "2026-02-23T00:26:24.851Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/75/b4ce781849931fef6fd529afa6b63711d5a733065722d0c3e2724af9e40a/scipy-1.17.1-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:1f95b894f13729334fb990162e911c9e5dc1ab390c58aa6cbecb389c5b5e28ec", size = 31613675, upload-time = "2026-02-23T00:16:00.13Z" }, + { url = "https://files.pythonhosted.org/packages/f7/58/bccc2861b305abdd1b8663d6130c0b3d7cc22e8d86663edbc8401bfd40d4/scipy-1.17.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:e18f12c6b0bc5a592ed23d3f7b891f68fd7f8241d69b7883769eb5d5dfb52696", size = 28162057, upload-time = "2026-02-23T00:16:09.456Z" }, + { url = "https://files.pythonhosted.org/packages/6d/ee/18146b7757ed4976276b9c9819108adbc73c5aad636e5353e20746b73069/scipy-1.17.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:a3472cfbca0a54177d0faa68f697d8ba4c80bbdc19908c3465556d9f7efce9ee", size = 20334032, upload-time = "2026-02-23T00:16:17.358Z" }, + { url = "https://files.pythonhosted.org/packages/ec/e6/cef1cf3557f0c54954198554a10016b6a03b2ec9e22a4e1df734936bd99c/scipy-1.17.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:766e0dc5a616d026a3a1cffa379af959671729083882f50307e18175797b3dfd", size = 22709533, upload-time = "2026-02-23T00:16:25.791Z" }, + { url = "https://files.pythonhosted.org/packages/4d/60/8804678875fc59362b0fb759ab3ecce1f09c10a735680318ac30da8cd76b/scipy-1.17.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:744b2bf3640d907b79f3fd7874efe432d1cf171ee721243e350f55234b4cec4c", size = 33062057, upload-time = "2026-02-23T00:16:36.931Z" }, + { url = "https://files.pythonhosted.org/packages/09/7d/af933f0f6e0767995b4e2d705a0665e454d1c19402aa7e895de3951ebb04/scipy-1.17.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43af8d1f3bea642559019edfe64e9b11192a8978efbd1539d7bc2aaa23d92de4", size = 35349300, upload-time = "2026-02-23T00:16:49.108Z" }, + { url = "https://files.pythonhosted.org/packages/b4/3d/7ccbbdcbb54c8fdc20d3b6930137c782a163fa626f0aef920349873421ba/scipy-1.17.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cd96a1898c0a47be4520327e01f874acfd61fb48a9420f8aa9f6483412ffa444", size = 35127333, upload-time = "2026-02-23T00:17:01.293Z" }, + { url = "https://files.pythonhosted.org/packages/e8/19/f926cb11c42b15ba08e3a71e376d816ac08614f769b4f47e06c3580c836a/scipy-1.17.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4eb6c25dd62ee8d5edf68a8e1c171dd71c292fdae95d8aeb3dd7d7de4c364082", size = 37741314, upload-time = "2026-02-23T00:17:12.576Z" }, + { url = "https://files.pythonhosted.org/packages/95/da/0d1df507cf574b3f224ccc3d45244c9a1d732c81dcb26b1e8a766ae271a8/scipy-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:d30e57c72013c2a4fe441c2fcb8e77b14e152ad48b5464858e07e2ad9fbfceff", size = 36607512, upload-time = "2026-02-23T00:17:23.424Z" }, + { url = "https://files.pythonhosted.org/packages/68/7f/bdd79ceaad24b671543ffe0ef61ed8e659440eb683b66f033454dcee90eb/scipy-1.17.1-cp311-cp311-win_arm64.whl", hash = "sha256:9ecb4efb1cd6e8c4afea0daa91a87fbddbce1b99d2895d151596716c0b2e859d", size = 24599248, upload-time = "2026-02-23T00:17:34.561Z" }, + { url = "https://files.pythonhosted.org/packages/35/48/b992b488d6f299dbe3f11a20b24d3dda3d46f1a635ede1c46b5b17a7b163/scipy-1.17.1-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:35c3a56d2ef83efc372eaec584314bd0ef2e2f0d2adb21c55e6ad5b344c0dcb8", size = 31610954, upload-time = "2026-02-23T00:17:49.855Z" }, + { url = "https://files.pythonhosted.org/packages/b2/02/cf107b01494c19dc100f1d0b7ac3cc08666e96ba2d64db7626066cee895e/scipy-1.17.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:fcb310ddb270a06114bb64bbe53c94926b943f5b7f0842194d585c65eb4edd76", size = 28172662, upload-time = "2026-02-23T00:18:01.64Z" }, + { url = "https://files.pythonhosted.org/packages/cf/a9/599c28631bad314d219cf9ffd40e985b24d603fc8a2f4ccc5ae8419a535b/scipy-1.17.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:cc90d2e9c7e5c7f1a482c9875007c095c3194b1cfedca3c2f3291cdc2bc7c086", size = 20344366, upload-time = "2026-02-23T00:18:12.015Z" }, + { url = "https://files.pythonhosted.org/packages/35/f5/906eda513271c8deb5af284e5ef0206d17a96239af79f9fa0aebfe0e36b4/scipy-1.17.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:c80be5ede8f3f8eded4eff73cc99a25c388ce98e555b17d31da05287015ffa5b", size = 22704017, upload-time = "2026-02-23T00:18:21.502Z" }, + { url = "https://files.pythonhosted.org/packages/da/34/16f10e3042d2f1d6b66e0428308ab52224b6a23049cb2f5c1756f713815f/scipy-1.17.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e19ebea31758fac5893a2ac360fedd00116cbb7628e650842a6691ba7ca28a21", size = 32927842, upload-time = "2026-02-23T00:18:35.367Z" }, + { url = "https://files.pythonhosted.org/packages/01/8e/1e35281b8ab6d5d72ebe9911edcdffa3f36b04ed9d51dec6dd140396e220/scipy-1.17.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:02ae3b274fde71c5e92ac4d54bc06c42d80e399fec704383dcd99b301df37458", size = 35235890, upload-time = "2026-02-23T00:18:49.188Z" }, + { url = "https://files.pythonhosted.org/packages/c5/5c/9d7f4c88bea6e0d5a4f1bc0506a53a00e9fcb198de372bfe4d3652cef482/scipy-1.17.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8a604bae87c6195d8b1045eddece0514d041604b14f2727bbc2b3020172045eb", size = 35003557, upload-time = "2026-02-23T00:18:54.74Z" }, + { url = "https://files.pythonhosted.org/packages/65/94/7698add8f276dbab7a9de9fb6b0e02fc13ee61d51c7c3f85ac28b65e1239/scipy-1.17.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f590cd684941912d10becc07325a3eeb77886fe981415660d9265c4c418d0bea", size = 37625856, upload-time = "2026-02-23T00:19:00.307Z" }, + { url = "https://files.pythonhosted.org/packages/a2/84/dc08d77fbf3d87d3ee27f6a0c6dcce1de5829a64f2eae85a0ecc1f0daa73/scipy-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:41b71f4a3a4cab9d366cd9065b288efc4d4f3c0b37a91a8e0947fb5bd7f31d87", size = 36549682, upload-time = "2026-02-23T00:19:07.67Z" }, + { url = "https://files.pythonhosted.org/packages/bc/98/fe9ae9ffb3b54b62559f52dedaebe204b408db8109a8c66fdd04869e6424/scipy-1.17.1-cp312-cp312-win_arm64.whl", hash = "sha256:f4115102802df98b2b0db3cce5cb9b92572633a1197c77b7553e5203f284a5b3", size = 24547340, upload-time = "2026-02-23T00:19:12.024Z" }, + { url = "https://files.pythonhosted.org/packages/76/27/07ee1b57b65e92645f219b37148a7e7928b82e2b5dbeccecb4dff7c64f0b/scipy-1.17.1-cp313-cp313-macosx_10_14_x86_64.whl", hash = "sha256:5e3c5c011904115f88a39308379c17f91546f77c1667cea98739fe0fccea804c", size = 31590199, upload-time = "2026-02-23T00:19:17.192Z" }, + { url = "https://files.pythonhosted.org/packages/ec/ae/db19f8ab842e9b724bf5dbb7db29302a91f1e55bc4d04b1025d6d605a2c5/scipy-1.17.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:6fac755ca3d2c3edcb22f479fceaa241704111414831ddd3bc6056e18516892f", size = 28154001, upload-time = "2026-02-23T00:19:22.241Z" }, + { url = "https://files.pythonhosted.org/packages/5b/58/3ce96251560107b381cbd6e8413c483bbb1228a6b919fa8652b0d4090e7f/scipy-1.17.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:7ff200bf9d24f2e4d5dc6ee8c3ac64d739d3a89e2326ba68aaf6c4a2b838fd7d", size = 20325719, upload-time = "2026-02-23T00:19:26.329Z" }, + { url = "https://files.pythonhosted.org/packages/b2/83/15087d945e0e4d48ce2377498abf5ad171ae013232ae31d06f336e64c999/scipy-1.17.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:4b400bdc6f79fa02a4d86640310dde87a21fba0c979efff5248908c6f15fad1b", size = 22683595, upload-time = "2026-02-23T00:19:30.304Z" }, + { url = "https://files.pythonhosted.org/packages/b4/e0/e58fbde4a1a594c8be8114eb4aac1a55bcd6587047efc18a61eb1f5c0d30/scipy-1.17.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2b64ca7d4aee0102a97f3ba22124052b4bd2152522355073580bf4845e2550b6", size = 32896429, upload-time = "2026-02-23T00:19:35.536Z" }, + { url = "https://files.pythonhosted.org/packages/f5/5f/f17563f28ff03c7b6799c50d01d5d856a1d55f2676f537ca8d28c7f627cd/scipy-1.17.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:581b2264fc0aa555f3f435a5944da7504ea3a065d7029ad60e7c3d1ae09c5464", size = 35203952, upload-time = "2026-02-23T00:19:42.259Z" }, + { url = "https://files.pythonhosted.org/packages/8d/a5/9afd17de24f657fdfe4df9a3f1ea049b39aef7c06000c13db1530d81ccca/scipy-1.17.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:beeda3d4ae615106d7094f7e7cef6218392e4465cc95d25f900bebabfded0950", size = 34979063, upload-time = "2026-02-23T00:19:47.547Z" }, + { url = "https://files.pythonhosted.org/packages/8b/13/88b1d2384b424bf7c924f2038c1c409f8d88bb2a8d49d097861dd64a57b2/scipy-1.17.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6609bc224e9568f65064cfa72edc0f24ee6655b47575954ec6339534b2798369", size = 37598449, upload-time = "2026-02-23T00:19:53.238Z" }, + { url = "https://files.pythonhosted.org/packages/35/e5/d6d0e51fc888f692a35134336866341c08655d92614f492c6860dc45bb2c/scipy-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:37425bc9175607b0268f493d79a292c39f9d001a357bebb6b88fdfaff13f6448", size = 36510943, upload-time = "2026-02-23T00:20:50.89Z" }, + { url = "https://files.pythonhosted.org/packages/2a/fd/3be73c564e2a01e690e19cc618811540ba5354c67c8680dce3281123fb79/scipy-1.17.1-cp313-cp313-win_arm64.whl", hash = "sha256:5cf36e801231b6a2059bf354720274b7558746f3b1a4efb43fcf557ccd484a87", size = 24545621, upload-time = "2026-02-23T00:20:55.871Z" }, + { url = "https://files.pythonhosted.org/packages/6f/6b/17787db8b8114933a66f9dcc479a8272e4b4da75fe03b0c282f7b0ade8cd/scipy-1.17.1-cp313-cp313t-macosx_10_14_x86_64.whl", hash = "sha256:d59c30000a16d8edc7e64152e30220bfbd724c9bbb08368c054e24c651314f0a", size = 31936708, upload-time = "2026-02-23T00:19:58.694Z" }, + { url = "https://files.pythonhosted.org/packages/38/2e/524405c2b6392765ab1e2b722a41d5da33dc5c7b7278184a8ad29b6cb206/scipy-1.17.1-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:010f4333c96c9bb1a4516269e33cb5917b08ef2166d5556ca2fd9f082a9e6ea0", size = 28570135, upload-time = "2026-02-23T00:20:03.934Z" }, + { url = "https://files.pythonhosted.org/packages/fd/c3/5bd7199f4ea8556c0c8e39f04ccb014ac37d1468e6cfa6a95c6b3562b76e/scipy-1.17.1-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:2ceb2d3e01c5f1d83c4189737a42d9cb2fc38a6eeed225e7515eef71ad301dce", size = 20741977, upload-time = "2026-02-23T00:20:07.935Z" }, + { url = "https://files.pythonhosted.org/packages/d9/b8/8ccd9b766ad14c78386599708eb745f6b44f08400a5fd0ade7cf89b6fc93/scipy-1.17.1-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:844e165636711ef41f80b4103ed234181646b98a53c8f05da12ca5ca289134f6", size = 23029601, upload-time = "2026-02-23T00:20:12.161Z" }, + { url = "https://files.pythonhosted.org/packages/6d/a0/3cb6f4d2fb3e17428ad2880333cac878909ad1a89f678527b5328b93c1d4/scipy-1.17.1-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:158dd96d2207e21c966063e1635b1063cd7787b627b6f07305315dd73d9c679e", size = 33019667, upload-time = "2026-02-23T00:20:17.208Z" }, + { url = "https://files.pythonhosted.org/packages/f3/c3/2d834a5ac7bf3a0c806ad1508efc02dda3c8c61472a56132d7894c312dea/scipy-1.17.1-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:74cbb80d93260fe2ffa334efa24cb8f2f0f622a9b9febf8b483c0b865bfb3475", size = 35264159, upload-time = "2026-02-23T00:20:23.087Z" }, + { url = "https://files.pythonhosted.org/packages/4d/77/d3ed4becfdbd217c52062fafe35a72388d1bd82c2d0ba5ca19d6fcc93e11/scipy-1.17.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:dbc12c9f3d185f5c737d801da555fb74b3dcfa1a50b66a1a93e09190f41fab50", size = 35102771, upload-time = "2026-02-23T00:20:28.636Z" }, + { url = "https://files.pythonhosted.org/packages/bd/12/d19da97efde68ca1ee5538bb261d5d2c062f0c055575128f11a2730e3ac1/scipy-1.17.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:94055a11dfebe37c656e70317e1996dc197e1a15bbcc351bcdd4610e128fe1ca", size = 37665910, upload-time = "2026-02-23T00:20:34.743Z" }, + { url = "https://files.pythonhosted.org/packages/06/1c/1172a88d507a4baaf72c5a09bb6c018fe2ae0ab622e5830b703a46cc9e44/scipy-1.17.1-cp313-cp313t-win_amd64.whl", hash = "sha256:e30bdeaa5deed6bc27b4cc490823cd0347d7dae09119b8803ae576ea0ce52e4c", size = 36562980, upload-time = "2026-02-23T00:20:40.575Z" }, + { url = "https://files.pythonhosted.org/packages/70/b0/eb757336e5a76dfa7911f63252e3b7d1de00935d7705cf772db5b45ec238/scipy-1.17.1-cp313-cp313t-win_arm64.whl", hash = "sha256:a720477885a9d2411f94a93d16f9d89bad0f28ca23c3f8daa521e2dcc3f44d49", size = 24856543, upload-time = "2026-02-23T00:20:45.313Z" }, +] + +[[package]] +name = "scipy" +version = "1.18.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.12' and sys_platform == 'win32'", + "python_full_version >= '3.12' and sys_platform == 'emscripten'", + "python_full_version >= '3.12' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +dependencies = [ + { name = "numpy", version = "2.5.4", source = { registry = "https://pypi.org/simple" } }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7e/74/66de6258867beb2ef08f35f9f2ac017a52cacd5081714d239ff1a442d458/scipy-1.18.1.tar.gz", hash = "sha256:52c4b7422442aba924d03ad4019852b08a92e64ea187b933135687bfe2747307", size = 30781235, upload-time = "2026-08-21T23:28:50.599Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/f7/240c110c08693826b4513a52f5717d62ec7c7af72f2920821247c03b17b3/scipy-1.18.1-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:457fd7a2a8edeb044ab6ffbc0aa03ff6cd18491356e5e0c834d76ce621b916d1", size = 31111061, upload-time = "2026-08-21T23:23:44.522Z" }, + { url = "https://files.pythonhosted.org/packages/05/4a/78c6285577c375e7cf27277ea8ee6961224327f1e1a0c44af5f17f23635c/scipy-1.18.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:e708533e8b2ae2497d65346538a7dcc92814410b25b81432eac66de0f2af8265", size = 28733332, upload-time = "2026-08-21T23:23:50.015Z" }, + { url = "https://files.pythonhosted.org/packages/a5/f6/a5b82f8abbe14d134691b8b903696f701d25a081353a29dc655c364d9e62/scipy-1.18.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:7bbf207c4453ce1ad2e00b17313852b33310b83090c2311bdaf97f93c0380d12", size = 20475078, upload-time = "2026-08-21T23:23:54.138Z" }, + { url = "https://files.pythonhosted.org/packages/23/22/0858a0bbd6b3e825ceb8cd9baf9eaf3b2f2b1d77727eb6be40500bcdc92f/scipy-1.18.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:78c0665edead396b1abb4897c41a5c1d9bf090c8a637a4c20a61678e0a264e66", size = 23108904, upload-time = "2026-08-21T23:23:57.824Z" }, + { url = "https://files.pythonhosted.org/packages/75/9a/2e71719f31eaefe0e3a1706c4a1ded94e664bfd95ffca2b219a671faee01/scipy-1.18.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3c085faa2cfa879c5141df483f836f4d691045a078224a670fa570fa01612d89", size = 34025113, upload-time = "2026-08-21T23:24:02.209Z" }, + { url = "https://files.pythonhosted.org/packages/df/64/ff35eb9e54894cf471ff4716abd3c81eb0a0626869217ce3e6ba4ccf17d7/scipy-1.18.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f55fa87b6c612ecd6b058f167c53231b1d14e412efe361d3d6e38b3631c73218", size = 35344199, upload-time = "2026-08-21T23:24:07.844Z" }, + { url = "https://files.pythonhosted.org/packages/d3/af/c5538be1792f7034c12c7db6ee67cace58253c7b87b122d68253eaf5de89/scipy-1.18.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c35d74ce0e193ff740c2f2be2ac913ddc232fe6c1ff40b26cfecb9c670c63314", size = 35639587, upload-time = "2026-08-21T23:24:13.05Z" }, + { url = "https://files.pythonhosted.org/packages/91/4c/075e4f66471bac101141ac739e9e135549be1bae584571bd03a530c056e1/scipy-1.18.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d2924a03db38dc2e848bca2fe9f077dafb891480b91a00a0963a8cf86dfc31c1", size = 37480330, upload-time = "2026-08-21T23:24:19.608Z" }, + { url = "https://files.pythonhosted.org/packages/39/e7/979fd14e75008623df31ba70d6bb144700f68feadcea042021c06a05bf82/scipy-1.18.1-cp312-cp312-win_amd64.whl", hash = "sha256:5e4d44984abc0020154ea81b247adeddcc3ac5527b975ff798bd1ba0adc513c2", size = 36658278, upload-time = "2026-08-21T23:24:25.463Z" }, + { url = "https://files.pythonhosted.org/packages/c7/0b/e1525354ff9d7d5feb6d1b31af6d14072e5c91e9607b421fa1ec889660b3/scipy-1.18.1-cp312-cp312-win_arm64.whl", hash = "sha256:d65d448389b8436493abcf629cc94ad0cf32aecaf06e1acca1de53cc795f2f12", size = 24400588, upload-time = "2026-08-21T23:24:30.579Z" }, + { url = "https://files.pythonhosted.org/packages/b6/55/4540ee0f9c42a9ad7109d0d1a8cc70de54c3572b01c6693a2b1c70e90ceb/scipy-1.18.1-cp313-cp313-macosx_10_15_x86_64.whl", hash = "sha256:3ab3523da44749156e1f68b464dc56af11ae4cbc5c739a49d05f32b982eca9f3", size = 31089958, upload-time = "2026-08-21T23:24:35.8Z" }, + { url = "https://files.pythonhosted.org/packages/2a/f5/769f36d14922b8071a43e95d24d18b6bdafad10d7f5cf647867e1ac052bc/scipy-1.18.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:e6fb6a55cc0ba97b59a1f288fb86dc6fce8bdfc0fffcbfd015e3a954bf2a2d93", size = 28715106, upload-time = "2026-08-21T23:24:40.775Z" }, + { url = "https://files.pythonhosted.org/packages/9a/d7/21d890274f75ea37a8209d5519e72da3da90302e3b9fb8397a0918386a62/scipy-1.18.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:ea324d9dd34c38bfb9bec8ca4d1b407db97dbb74029f566b8e322b1b6fe56fe6", size = 20456846, upload-time = "2026-08-21T23:24:45.066Z" }, + { url = "https://files.pythonhosted.org/packages/ec/01/798430ecea2e78ec7c02663d5f71c007bb6abeca931080debd40d7fa55ea/scipy-1.18.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:75b00eb8fb802090aa903f4ea1c7f5a584779f967361e68b7e98e531cc2d7174", size = 23087986, upload-time = "2026-08-21T23:24:49.539Z" }, + { url = "https://files.pythonhosted.org/packages/e6/5f/4634e9d35c68496e4e34cb6946eafab044458e6cedab42b40b6588e475b6/scipy-1.18.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d416b16cccfd70fbf62400e84d0bb2f4e6af519a45557f1692c749b37f14b315", size = 33998146, upload-time = "2026-08-21T23:24:54.714Z" }, + { url = "https://files.pythonhosted.org/packages/41/48/6450ed9243315322bbc19ac57b9b70d66a20bf1d38d124c96bc4bf6af9ea/scipy-1.18.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fdaf5ea890a6183d0565f51a61799d67081bd5b1cf03c5f4b3fd3732108625c9", size = 35312578, upload-time = "2026-08-21T23:25:00.44Z" }, + { url = "https://files.pythonhosted.org/packages/00/bd/bf5a4be6a3525676499f6dff307991739ff6fdcad1481b1aeb6745339f58/scipy-1.18.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c825cef2f49e46753726a7181a8e199804a912b29519ada542c6ebc654951899", size = 35612621, upload-time = "2026-08-21T23:25:06.144Z" }, + { url = "https://files.pythonhosted.org/packages/bd/4e/3c45c33e00a77996c4b1cb707929f833ba7b1d522ee29f882512c330676d/scipy-1.18.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e3b417bf8c2c7c16e8f58ad91db17783ec911ac16e7b50eb6eab6e809b4f5b07", size = 37457323, upload-time = "2026-08-21T23:25:12.483Z" }, + { url = "https://files.pythonhosted.org/packages/93/0e/e0348fbc0dbab65c114cf78957e7dfeb49f8e8b556b4d930cc12ff195e18/scipy-1.18.1-cp313-cp313-win_amd64.whl", hash = "sha256:559ed65f60c1af5a03f3912605a1b5114f522c7c32fb23c3376ae8f03219fe28", size = 36622841, upload-time = "2026-08-21T23:25:18.722Z" }, + { url = "https://files.pythonhosted.org/packages/50/a8/6a77f5f267c555108f0a864b6db714363dab567a8266422a79a385f9232b/scipy-1.18.1-cp313-cp313-win_arm64.whl", hash = "sha256:cd479fc04dd9401e3b4f49e76518768ef99c4f517a98c284eb091fd725719adf", size = 24399315, upload-time = "2026-08-21T23:25:23.458Z" }, +] + +[[package]] +name = "setuptools" +version = "84.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/70/b3/c19426f783614d625699a4b247faf628ea9ab1db228674491767b417f9e1/setuptools-84.2.1.tar.gz", hash = "sha256:3ff8b3b5492a0aa66960f4419365a86b84e013070d4313e4d4a6894f5d64d6bc", size = 1230289, upload-time = "2026-10-20T19:01:37.742Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/55/6dc6d6bb953bdfc52519a0059592c52b6e3b8ad51cf8256dffceb55b0610/setuptools-84.2.1-py3-none-any.whl", hash = "sha256:ca140b45d5325a9478f880e351484f62fef15f336462e449ca0c03c873d589c2", size = 876263, upload-time = "2026-10-20T19:01:35.678Z" }, +] + +[[package]] +name = "shapely" +version = "2.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "numpy", version = "2.5.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f3/ab/924b6e202f796d270a3041a230151f7908db5ea48c74effe6f8023e9bd05/shapely-2.2.0.tar.gz", hash = "sha256:e8865e553d874a1ec4a032057ea81fca9def37b188cd8fb550af3b3480b3f88c", size = 380326, upload-time = "2026-10-07T09:18:01.001Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a5/43/11bfb01afed47a77540dc4075c7c9ad1ba6323a698fcdb01795df62f55b2/shapely-2.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:596b7994ceafa526b6e0522ca29fbc41d19f86459161d6efe1f251d0acd49f3f", size = 1786155, upload-time = "2026-10-07T09:15:56.158Z" }, + { url = "https://files.pythonhosted.org/packages/55/23/e3502c9312e0cd0dcee9e382ad90d0d3039144015b22de87084be20fffec/shapely-2.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7c0b262116bb75b86751440b42e19673911bc0a8f0d5ce723ce294c3d6e4d5c0", size = 1582454, upload-time = "2026-10-07T09:15:58.042Z" }, + { url = "https://files.pythonhosted.org/packages/11/97/dd0ce3bcc03b317298dec0f66e8b404e50cdb126dcb0a27f1c4801371ba5/shapely-2.2.0-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7765e0e5d51d63eae0a911861cbda87165a01677bc9bce6ed20d06858ccde99f", size = 2184352, upload-time = "2026-10-07T09:15:59.87Z" }, + { url = "https://files.pythonhosted.org/packages/dc/20/56289b91914b62a0849de3c1efe5f0cfd5b5d7ba168568567900087615be/shapely-2.2.0-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5d61088e2ef71dafad0dd4fae8a521cc1f20da4a89d3096bab5b3260b39b3052", size = 2307874, upload-time = "2026-10-07T09:16:01.748Z" }, + { url = "https://files.pythonhosted.org/packages/a4/a8/95a81840efdfc49902cdfceb91e0972563489acda5e92d93bce7fdabea5d/shapely-2.2.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0edec813c81effaf4e20c18b1aa86827925ce27c0315621f2a1a080e22e0de5e", size = 3251822, upload-time = "2026-10-07T09:16:04.238Z" }, + { url = "https://files.pythonhosted.org/packages/ab/f1/23aa57dd931a361d7a77512a32fbaedd2eb62ede201192c2ff391bf1d28a/shapely-2.2.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:8d6ffe94710f37535a47161120cd5f7f0f0d9bb800c2fddebbd089cb7f1b3453", size = 3397542, upload-time = "2026-10-07T09:16:06.52Z" }, + { url = "https://files.pythonhosted.org/packages/9a/8b/b812edc45ab4096d4dd3696e614fbe44207c916da16f26be3fe2305ed4e4/shapely-2.2.0-cp311-cp311-win32.whl", hash = "sha256:ce858295be3947143a3f44f145fa6dbacd5dcc5c4103801d42cd3be4a2034614", size = 1618880, upload-time = "2026-10-07T09:16:08.56Z" }, + { url = "https://files.pythonhosted.org/packages/1f/bc/1b4246004806869c1d420c0a53e97244c9b538a7175e10f5e30012bbeab7/shapely-2.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:806d399418b23eee7241736d572ad1e0b784782f9241d7c8e2cfceb00787831d", size = 1795293, upload-time = "2026-10-07T09:16:10.332Z" }, + { url = "https://files.pythonhosted.org/packages/e7/8b/aa11e4f696fbcda4e06f1a49c95fabe82f28a93265d1b9d0cc2c596f03df/shapely-2.2.0-cp311-cp311-win_arm64.whl", hash = "sha256:5b740c9a197e5feb30bdc6e64a5eb3ca2a7324d11498844136dfc317daac6a99", size = 1880736, upload-time = "2026-10-07T09:16:11.949Z" }, + { url = "https://files.pythonhosted.org/packages/63/af/ac371511dbf0f0a172544a647246f746ceb2b5d12b3e1238224bec3a3796/shapely-2.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:626fe4c0d32860a98e75ecffabf5a62254c6168eac96b633ad313cd62a38bb2b", size = 1785072, upload-time = "2026-10-07T09:16:13.707Z" }, + { url = "https://files.pythonhosted.org/packages/02/96/5c48977168f32de067bfafce7f584dd04becf152152bf088636cc034828e/shapely-2.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c36ccbff5c3374c349c370bfdac22c7676b268b4a707c98e9031f498965aa02d", size = 1584868, upload-time = "2026-10-07T09:16:15.795Z" }, + { url = "https://files.pythonhosted.org/packages/ae/34/b90723043091161f636fde302e850583dcebd610e798f9edd6e3245f5a2a/shapely-2.2.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a9a380624cdd7a7e661bf15a4d1625082766f07ccd2540cb0a9e0df1ad4f6c11", size = 2176960, upload-time = "2026-10-07T09:16:17.965Z" }, + { url = "https://files.pythonhosted.org/packages/ad/87/6842e4c996914a47b6bfd3ef14a543e67e993f86a9ea5679c34efc9314ab/shapely-2.2.0-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:650a5f4d8a8e3c96982079d8c99b6ddbe6602bbd1e34c75c2b95dbc0d28ac997", size = 2300970, upload-time = "2026-10-07T09:16:20.191Z" }, + { url = "https://files.pythonhosted.org/packages/54/ea/06295d871f0befc3eafa96b7bb87a31e848f042d03ba80c5938b12eb68dd/shapely-2.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a851e077f0f02a3383923e02eca5447a29ddbf234e39593b91c8b7ac75218133", size = 3244534, upload-time = "2026-10-07T09:16:22.36Z" }, + { url = "https://files.pythonhosted.org/packages/8b/2a/ab017941b2014f29b8fca233e3f5a75e5fe109a1f27d316ba85d95a515ff/shapely-2.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dc5faa593948aa64d9afae48331b80f43f7aacc68425d99064a4d6772f53f1ad", size = 3391336, upload-time = "2026-10-07T09:16:24.226Z" }, + { url = "https://files.pythonhosted.org/packages/63/ee/4ccaa854f3b7ecd9181920b913c2d2f4b15053331457b7f615139a9774cc/shapely-2.2.0-cp312-cp312-win32.whl", hash = "sha256:da47a0cc9e630b4dff0db46e8972b29d2d27f337425ce9d4c77fd046ce48eabd", size = 1617174, upload-time = "2026-10-07T09:16:26.277Z" }, + { url = "https://files.pythonhosted.org/packages/f6/26/ba9192f0a72c830aa8cc1c61e207ada57cc54b7de81668b1836f575ee717/shapely-2.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:90895df6542ae039fc6557dec6194e3509e883fbd6f5788e3c3e7a38fe46b257", size = 1791280, upload-time = "2026-10-07T09:16:27.94Z" }, + { url = "https://files.pythonhosted.org/packages/8e/92/4e4f93d7b7db9af2a77126c6c96af2c0c422635e2276f5abb533f67b42df/shapely-2.2.0-cp312-cp312-win_arm64.whl", hash = "sha256:7cf5b3a801b9b4febf774efde2e31280e647388deae8452693d8e6420b3a1ff2", size = 1879393, upload-time = "2026-10-07T09:16:29.684Z" }, + { url = "https://files.pythonhosted.org/packages/28/b6/9ba2a62ab6e831b911a248f0752f0f4120be7637a33ab33d8649ed4ede4d/shapely-2.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c037369c35510f51100dd6d386ee3203bac32f164d53e27ca12c3cea5bb643b1", size = 1785102, upload-time = "2026-10-07T09:16:31.662Z" }, + { url = "https://files.pythonhosted.org/packages/e9/8a/d7c11c2d1beef99a4df4183b255ea2d8669f3bcf7d049bb17fe56bf7cd72/shapely-2.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d75957716368f919c63016dae1977a0d007e15f06861cd178701edb91b08d2b0", size = 1584542, upload-time = "2026-10-07T09:16:33.413Z" }, + { url = "https://files.pythonhosted.org/packages/f0/bd/21ed8bfd340455ede2df0d25d896acf4e4bab2ed9b582bb67389e97b2250/shapely-2.2.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4ed79beb8d4b6cc7c67780fd381feed25848a5f9b8a2385ac5711eccd115647a", size = 2175369, upload-time = "2026-10-07T09:16:35.507Z" }, + { url = "https://files.pythonhosted.org/packages/5d/df/d67d5c56efddf9b8c2e6913c917c8eca78fdd9e7c6fb73b541dd56b1ce1d/shapely-2.2.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f340e7f99aaee3df5acd6b247cddf723051a7c93d1e1ef09025b80d84e4c0ded", size = 2299018, upload-time = "2026-10-07T09:16:37.246Z" }, + { url = "https://files.pythonhosted.org/packages/58/dd/6e2b5ac83edb4afb540925092feee393a15970216e711a8b211f5abe4478/shapely-2.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:17434cb9819c9974c3331333a3b878fa5bf8f85dd69cc3fb7ff5d260f6fbc102", size = 3243663, upload-time = "2026-10-07T09:16:39.093Z" }, + { url = "https://files.pythonhosted.org/packages/0c/dc/7c0461549c212b0d663f99383fe846eb082f4b06cd1fba3bb786b9927d22/shapely-2.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b2338ac40e6652c8bfb857936ea9be9a16f43a362c6f67eb3bad741b05fd5683", size = 3388136, upload-time = "2026-10-07T09:16:41.287Z" }, + { url = "https://files.pythonhosted.org/packages/1c/58/ac8f7de528c125ab41a001523ada72e95e2d5f746917487e723b25e1c5c4/shapely-2.2.0-cp313-cp313-win32.whl", hash = "sha256:40871d7135cd723f965d200181aa28418e9ec029fd85bdd010488259d1c01906", size = 1616372, upload-time = "2026-10-07T09:16:43.094Z" }, + { url = "https://files.pythonhosted.org/packages/25/ed/7fcd625c9796e61d815ca9545d4e44a16f075f83869c1532206de88f23f1/shapely-2.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:1eaa2cb64cdedaf65d6bc86f2819c9cd7d6d68f969aa3ebfdc93743ab581f437", size = 1790191, upload-time = "2026-10-07T09:16:44.852Z" }, + { url = "https://files.pythonhosted.org/packages/23/c9/947fcd5665e1945dd54f6e8890bc6dd04613dd169a5fbb4ba7497754b3a8/shapely-2.2.0-cp313-cp313-win_arm64.whl", hash = "sha256:f79b3b34ad2d067207f21f821489c720b14ce40f3bfda931987a193165f80133", size = 1878351, upload-time = "2026-10-07T09:16:46.656Z" }, +] + +[[package]] +name = "stevedore" +version = "5.9.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/db/a1/3b8ed9c1fc3aa6eebb57732d924ddaa0500ecc3b638d0454816320994383/stevedore-5.9.1.tar.gz", hash = "sha256:e97a2667923efda926e8713fde6a73616df68210a3cbc6f02b48967b676fd8bf", size = 518111, upload-time = "2026-08-20T15:25:14.754Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c5/97/bba6e7ec2f5498b9dcb7b1b6400086b80ae5a8ebaff4b25e8c8add75f439/stevedore-5.9.1-py3-none-any.whl", hash = "sha256:5c8ff3a9f336cc1a06ac0f597bc79d11a2f950bfd32e290ca56b5a301fafafbf", size = 54931, upload-time = "2026-08-20T15:25:13.602Z" }, +] + +[[package]] +name = "text-unidecode" +version = "1.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ab/e2/e9a00f0ccb71718418230718b3d900e71a5d16e701a3dae079a21e9cd8f8/text-unidecode-1.3.tar.gz", hash = "sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93", size = 76885, upload-time = "2019-08-30T21:36:45.405Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a6/a5/c0b6468d3824fe3fde30dbb5e1f687b291608f9473681bbf7dabbf5a87d7/text_unidecode-1.3-py2.py3-none-any.whl", hash = "sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8", size = 78154, upload-time = "2019-08-30T21:37:03.543Z" }, +] + +[[package]] +name = "threadpoolctl" +version = "3.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/00/dc/6c58154c1c65f758ea979e7139cb76993a9cfc662d14e9be3c4a667cfb77/threadpoolctl-3.7.0.tar.gz", hash = "sha256:61348cfb77d53b9242e0017029244b559b810c142ced65b4e21eeca1843959a7", size = 31961, upload-time = "2026-09-15T15:46:20.263Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/3f/f88a53f60a472b46f4023f56d204dd7de33d34c5d2acbfa0d70a674e639e/threadpoolctl-3.7.0-py3-none-any.whl", hash = "sha256:cd8b60b5641b45c67bbf73c64c843235fc2d8a480c87389f52f5dbee893b86be", size = 26362, upload-time = "2026-09-15T15:46:19.168Z" }, +] + +[[package]] +name = "tifffile" +version = "2026.3.3" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.12' and sys_platform == 'win32'", + "python_full_version < '3.12' and sys_platform == 'emscripten'", + "python_full_version < '3.12' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +dependencies = [ + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" } }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c5/cb/2f6d79c7576e22c116352a801f4c3c8ace5957e9aced862012430b62e14f/tifffile-2026.3.3.tar.gz", hash = "sha256:d9a1266bed6f2ee1dd0abde2018a38b4f8b2935cb843df381d70ac4eac5458b7", size = 388745, upload-time = "2026-03-03T19:14:38.134Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1a/e4/e804505f87627cd8cdae9c010c47c4485fd8c1ce31a7dd0ab7fcc4707377/tifffile-2026.3.3-py3-none-any.whl", hash = "sha256:e8be15c94273113d31ecb7aa3a39822189dd11c4967e3cc88c178f1ad2fd1170", size = 243960, upload-time = "2026-03-03T19:14:35.808Z" }, +] + +[[package]] +name = "tifffile" +version = "2026.10.15" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.12' and sys_platform == 'win32'", + "python_full_version >= '3.12' and sys_platform == 'emscripten'", + "python_full_version >= '3.12' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +dependencies = [ + { name = "numpy", version = "2.5.4", source = { registry = "https://pypi.org/simple" } }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fd/87/a982ca5b892579397c2b155275cc0119b51feb1701eaefa18acb0245f0a4/tifffile-2026.10.15.tar.gz", hash = "sha256:a5ee666dbd6d62e059830a3450825a2521a981b3f45c07a80ab009484aef56f0", size = 454150, upload-time = "2026-10-16T00:32:10.511Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5b/bc/9e7664e2d30d4e82ada1b29dbb249d2dcac1ed1114cbbf11764f670d8be7/tifffile-2026.10.15-py3-none-any.whl", hash = "sha256:3dcf6c56731a45c1f85c81e90dad922b0a380a2d639e77f71207e010f8ef0f7b", size = 276988, upload-time = "2026-10-16T00:32:09.158Z" }, +] + +[[package]] +name = "toolz" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/31/6f/ae20c212a07aa2d156c787383d8088a5e045ee39628661edb190c97e1659/toolz-1.2.0.tar.gz", hash = "sha256:9667a038e9d6ecba37995e26cb2f59ec6420b6ad8dd9677de59db9b956b08490", size = 55442, upload-time = "2026-10-07T04:16:25.639Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/db/17/4c8beb6c8c4176c6bf143bfd7e1e4dd6719b00ced90738c7ac471b71c1df/toolz-1.2.0-py3-none-any.whl", hash = "sha256:890f820b1cb8152785aaf9386d8707770110809035800985ca65cb24ce1120ef", size = 60789, upload-time = "2026-10-07T04:16:24.173Z" }, +] + +[[package]] +name = "typing-extensions" +version = "4.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/cc/6253133b5bb138fc3306cebfbda2c520f545d36b5be2c7255cc528bb45d6/typing_extensions-4.16.0.tar.gz", hash = "sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5", size = 113555, upload-time = "2026-07-02T08:40:05.92Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/49/d3/b8441a820a491ddfc024b0b0cf0393375b75ea13866d9c66727e54c2fc80/typing_extensions-4.16.0-py3-none-any.whl", hash = "sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8", size = 45571, upload-time = "2026-07-02T08:40:04.659Z" }, +] + +[[package]] +name = "tzdata" +version = "2026.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7a/29/6a1391d31cd00ff9e927215a224a880e1e84e826b64ba80a499e51c074d8/tzdata-2026.6.tar.gz", hash = "sha256:85d2cd9c09f81f0bb29db27759fc83af4f1e620efb814f8c5bf8b4cc4feece6d", size = 201017, upload-time = "2026-10-28T09:57:09.137Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0a/66/9a06c2227fedfd4919f0eaaa8b94377d9730882a7e4f95823c9a331ab5e1/tzdata-2026.6-py2.py3-none-any.whl", hash = "sha256:0a10eec6e13ef16b627fabed9a5f2ba61a62b1c67f327305f4e7e412a70b4aec", size = 348210, upload-time = "2026-10-28T09:57:07.276Z" }, +] + +[[package]] +name = "urllib3" +version = "2.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e3/05/b17359e1cefb4f909b5e40b1b90a496d987258916dbbf88e842c729f510e/urllib3-2.8.0.tar.gz", hash = "sha256:63bf2ead4c879426ebf22ef2a781eeb4aa3b4ae798a0435506f8687fd5bb9b63", size = 458972, upload-time = "2026-09-15T19:29:36.253Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/92/9d/c4e665119135114480843e7ab388fa94d8480650450e6f8e26b70d323a4c/urllib3-2.8.0-py3-none-any.whl", hash = "sha256:0cf3cae568d36aa9576b28dfb35f11328f1cb974ca7647d9475ebb86c75ac6e3", size = 135717, upload-time = "2026-09-15T19:29:34.577Z" }, +] + +[[package]] +name = "virtualenv" +version = "21.15.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "distlib" }, + { name = "filelock" }, + { name = "packaging" }, + { name = "platformdirs" }, + { name = "python-discovery" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/38/9e/40c7098240cedd3a2f32e42a74b35c4563c617fdd7e41ae6268d4fdd8915/virtualenv-21.15.2.tar.gz", hash = "sha256:dbd886e6e5689eb4dde6a9bbc478ad14b4fc8ed0852690d8273ccfb8c467f635", size = 5520597, upload-time = "2026-10-30T19:18:04.759Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f7/d8/09c9d6df253b2016312dd5ccbb962dfa86a5202f2452604b52e0a9844661/virtualenv-21.15.2-py3-none-any.whl", hash = "sha256:fb8c62c7c328e0238061c721068f06b7bc6bd10f7a0f5987195cef1008837ee6", size = 5559530, upload-time = "2026-10-30T19:18:01.134Z" }, +] + +[[package]] +name = "wheel" +version = "0.48.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d0/20/50ed6bdf27dec98b568a8ae25dc599f35baa3d9709f9e83fd1edb56b9a90/wheel-0.48.0.tar.gz", hash = "sha256:94800765601e9171bf5d58d066e640662842bcedcbab982b2c90787a2c987322", size = 66471, upload-time = "2026-08-11T22:02:27.327Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2e/29/69cfbb602cd91690c55d38ba9fe53e6a7e76a6fa647bf38f19c138d25449/wheel-0.48.0-py3-none-any.whl", hash = "sha256:3217dcc807155e45db462d7ef2431f5ddda0d7273b700d05a67b271ceb1287ab", size = 33320, upload-time = "2026-08-11T22:02:26.1Z" }, +] + +[[package]] +name = "xarray" +version = "2026.9.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "numpy", version = "2.5.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "packaging" }, + { name = "pandas" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ff/f5/781c70b234f54e0401f4f7c4427a7486da38d50dbb138c7d7c55144b3d86/xarray-2026.9.0.tar.gz", hash = "sha256:6abc69694c22fa1f0fb2f357ff4e41d88beb4477ed71091f944b7dbf67ed54fe", size = 3176922, upload-time = "2026-09-29T23:06:25.807Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3f/44/3159af4fb3c868970a412e294320ed6bb1a9fbdfa6431050eda8e0b34b6e/xarray-2026.9.0-py3-none-any.whl", hash = "sha256:fe349fa871628b1a0a5217af3fe1283a2862d5485156e6eda354fffb81c3bb7c", size = 1445445, upload-time = "2026-09-29T23:06:23.686Z" }, +] + +[[package]] +name = "zarr" +version = "2.12.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "asciitree" }, + { name = "fasteners" }, + { name = "numcodecs", version = "0.16.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "numcodecs", version = "0.17.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "numpy", version = "2.5.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/51/8b/2480d3758e5e70765c2c9f3c13ca8fb576f1ad73c31f6a696d6720b7f562/zarr-2.12.0.tar.gz", hash = "sha256:515a31ee4bad6bb48ae05178c588ae49a02a35defa01dd9a3293306903368165", size = 3571172, upload-time = "2022-06-23T08:47:33.863Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/56/a6/817d25f7b77b695b1014030bfff90288846862a50d72c1ee85a8a233c040/zarr-2.12.0-py3-none-any.whl", hash = "sha256:3713f28f3fafa838f62bb442504da8e89bb969ecd1da09c9a96927536ec9be34", size = 185751, upload-time = "2022-06-23T08:47:31.251Z" }, +] + +[[package]] +name = "zipp" +version = "4.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/18/05/ba1458dee0ac485b2a01071c1b88f5199fe878a485df482e10e1ef2205cc/zipp-4.1.2.tar.gz", hash = "sha256:cf474b90b07ea932637e17c6b9479de2b2882900b0d311bdaf4f5c000a15a97f", size = 27864, upload-time = "2026-10-21T18:57:04.743Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b8/44/7bd2007260da11cccca29ba2d4c345f1287aa663db38ee7cefeabf6386a5/zipp-4.1.2-py3-none-any.whl", hash = "sha256:b30cb6cd253122169c47de245ca24a86bac2a5d3802ba15bf7ba6414eb9e144d", size = 10581, upload-time = "2026-10-21T18:57:03.79Z" }, +]