From ce449f2a040a143c01602e1d757efe266a00f3c4 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 31 Aug 2026 18:32:25 +0000 Subject: [PATCH 1/2] chore: [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/pre-commit-hooks: v5.0.0 → v6.0.0](https://github.com/pre-commit/pre-commit-hooks/compare/v5.0.0...v6.0.0) - [github.com/astral-sh/ruff-pre-commit: v0.11.13 → v0.16.5](https://github.com/astral-sh/ruff-pre-commit/compare/v0.11.13...v0.16.5) - [github.com/gitleaks/gitleaks: v8.27.2 → v8.30.0](https://github.com/gitleaks/gitleaks/compare/v8.27.2...v8.30.0) - [github.com/codespell-project/codespell: v2.4.1 → v2.4.3](https://github.com/codespell-project/codespell/compare/v2.4.1...v2.4.3) - [github.com/compilerla/conventional-pre-commit: v4.2.0 → v4.4.0](https://github.com/compilerla/conventional-pre-commit/compare/v4.2.0...v4.4.0) --- .pre-commit-config.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c26b3da..589243d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -4,7 +4,7 @@ default_language_version: python: python3.10 repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v5.0.0 + rev: v6.0.0 hooks: - id: check-added-large-files - id: check-toml @@ -14,7 +14,7 @@ repos: - id: end-of-file-fixer - id: trailing-whitespace - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.11.13 + rev: v0.16.5 hooks: - id: ruff args: @@ -23,17 +23,17 @@ repos: files: ^(siapy|tests)/ - id: ruff-format - repo: https://github.com/gitleaks/gitleaks - rev: v8.27.2 + rev: v8.30.0 hooks: - id: gitleaks - repo: https://github.com/codespell-project/codespell - rev: v2.4.1 + rev: v2.4.3 hooks: - id: codespell additional_dependencies: - tomli - repo: https://github.com/compilerla/conventional-pre-commit - rev: v4.2.0 + rev: v4.4.0 hooks: - id: conventional-pre-commit stages: [commit-msg] From a9a6f92de54f376c068b5beea78cc24c857c3036 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 31 Aug 2026 18:33:41 +0000 Subject: [PATCH 2/2] chore: [pre-commit.ci] Auto format from pre-commit.com hooks --- docs/concepts/datasets.md | 2 +- docs/concepts/entities.md | 28 +++++++++++------------ docs/concepts/features.md | 12 +++++----- docs/concepts/optimizers.md | 10 ++++---- docs/concepts/transformations.md | 10 ++++---- docs/concepts/utils_image.md | 8 +++---- docs/concepts/utils_plotting.md | 10 ++++---- docs/examples/case_study.md | 18 +++++++-------- siapy/core/exceptions.py | 8 +++---- siapy/core/types.py | 15 ++++++------ siapy/datasets/__init__.py | 1 - siapy/datasets/helpers.py | 1 - siapy/datasets/schemas.py | 5 ++-- siapy/datasets/tabular.py | 2 +- siapy/entities/__init__.py | 6 ++--- siapy/entities/images/__init__.py | 2 +- siapy/entities/images/interfaces.py | 11 --------- siapy/entities/images/spimage.py | 5 ++-- siapy/entities/imagesets.py | 3 ++- siapy/entities/pixels.py | 23 ++++++++++--------- siapy/entities/shapes/geometric_shapes.py | 5 ++-- siapy/entities/shapes/shape.py | 9 ++++---- siapy/entities/signatures.py | 9 ++++---- siapy/features/features.py | 11 +++++---- siapy/features/helpers.py | 4 ++-- siapy/features/spectral_indices.py | 5 ++-- siapy/optimizers/configs.py | 3 ++- siapy/optimizers/evaluators.py | 3 ++- siapy/optimizers/parameters.py | 5 ++-- siapy/optimizers/scorers.py | 3 ++- siapy/transformations/corregistrator.py | 5 ++-- siapy/transformations/image.py | 5 ++-- siapy/utils/general.py | 19 +++++++-------- siapy/utils/image_validators.py | 4 ++-- siapy/utils/images.py | 14 ++++++------ siapy/utils/plots.py | 6 ++--- tests/data_manager.py | 3 +-- tests/utils/test_utils_images.py | 2 +- tests/utils/test_utils_signatures.py | 2 +- 39 files changed, 150 insertions(+), 147 deletions(-) diff --git a/docs/concepts/datasets.md b/docs/concepts/datasets.md index e07daa9..55e3e4e 100644 --- a/docs/concepts/datasets.md +++ b/docs/concepts/datasets.md @@ -6,5 +6,5 @@ The datasets module provides structured containers and utilities for transforming spectral image data into formats optimized for analysis and machine learning. It bridges the gap between raw spectral data and analytical workflows. ```python ---8<-- "docs/concepts/src/datasets_01.py" +--8 < --"docs/concepts/src/datasets_01.py" ``` diff --git a/docs/concepts/entities.md b/docs/concepts/entities.md index 421ee61..d113d33 100644 --- a/docs/concepts/entities.md +++ b/docs/concepts/entities.md @@ -55,7 +55,7 @@ Since spectral images often contain distinct objects with different spectral pro The `Pixels` class represents spatial coordinates within spectral image, providing a container for *(x, y)* coordinate pairs. It uses pandas DataFrame internally for storage, enabling high-performance operations. The class provides multiple initialization methods and conversion functions to work with different data representations (i.e. DataFrames, list, arrays) ```python ---8<-- "docs/concepts/src/pixels_01.py" +--8 < --"docs/concepts/src/pixels_01.py" ``` ## Signals @@ -66,7 +66,7 @@ The `Pixels` class represents spatial coordinates within spectral image, providi The `Signals` class stores spectral data for each pixel in a pandas DataFrame, allowing you to use any column names you choose (e.g. "band_1", "nir", "red_edge"). You can initialize it from a DataFrame, lists, dicts or NumPy arrays. ```python ---8<-- "docs/concepts/src/signals_01.py" +--8 < --"docs/concepts/src/signals_01.py" ``` However, direct initialization of `Signals` is typically not necessary in practice. When you create a `Signatures` instance, the underlying `Signals` object is automatically generated and managed for you. This section demonstrates the `Signals` class primarily to illustrate how the `Signatures` class (discussed next) is composed internally and to provide insight into the data structure that powers spectral analysis. @@ -81,19 +81,19 @@ The `Signatures` class represents spectral data collections by combining spatial `Signatures` can be initialized in multiple ways. The explicit approach creates each component separately before combining them, providing clarity about the composition: ```python ---8<-- "docs/concepts/src/signatures_01.py:long" +--8 < --"docs/concepts/src/signatures_01.py:long" ``` For more concise code, you can initialize a `Signatures` object directly from coordinate and signal values: ```python ---8<-- "docs/concepts/src/signatures_01.py:short" +--8 < --"docs/concepts/src/signatures_01.py:short" ``` Both approaches yield equivalent results when initialized with the same data. You can access and work with the data using various DataFrame operations and conversion methods: ```python ---8<-- "docs/concepts/src/signatures_01.py:assert" +--8 < --"docs/concepts/src/signatures_01.py:assert" ``` ## Shape @@ -104,7 +104,7 @@ Both approaches yield equivalent results when initialized with the same data. Yo The `Shape` class represents geometric shapes that can be associated with images, such as points, lines, and polygons. ```python ---8<-- "docs/concepts/src/shapes_01.py" +--8 < --"docs/concepts/src/shapes_01.py" ``` ## Spectral Image @@ -121,7 +121,7 @@ A `SpectralImage` is the primary container for spectral image data. It's a gener This is commonly used for hyperspectral imagery from airborne or satellite sensors. ```python ---8<-- "docs/concepts/src/spectral_image_01.py" +--8 < --"docs/concepts/src/spectral_image_01.py" ``` #### 2. Load from GeoTIFF or other geospatial formats (using rasterio) @@ -129,7 +129,7 @@ This is commonly used for hyperspectral imagery from airborne or satellite senso Perfect for georeferenced data with spatial information. ```python ---8<-- "docs/concepts/src/spectral_image_02.py" +--8 < --"docs/concepts/src/spectral_image_02.py" ``` #### 3. Create from numpy array @@ -137,7 +137,7 @@ Perfect for georeferenced data with spatial information. Useful for testing or when you already have image data in memory. ```python ---8<-- "docs/concepts/src/spectral_image_03.py" +--8 < --"docs/concepts/src/spectral_image_03.py" ``` #### 4. Create your own custom image class @@ -145,7 +145,7 @@ Useful for testing or when you already have image data in memory. For specialized file formats or custom processing needs, you can extend the ImageBase class. ```python ---8<-- "docs/concepts/src/spectral_image_04.py" +--8 < --"docs/concepts/src/spectral_image_04.py" ``` ### Data conversion methods @@ -156,7 +156,7 @@ The example below demonstrates two key conversion methods of `SpectralImage` ins 2. `to_subarray()`: Converts selected pixel data to a NumPy array for numerical processing or integration with other scientific libraries ```python ---8<-- "docs/concepts/src/spectral_image_05.py" +--8 < --"docs/concepts/src/spectral_image_05.py" ``` ### Manipulation of Shapes @@ -166,13 +166,13 @@ Each `SpectralImage` instance automatically initializes a `GeometricShapes` obje The `GeometricShapes` class provides a list-like interface that wraps a standard Python list, enhancing it with specialized functionality for manipulating geometric shapes while preserving standard list behavior. The list of shapes can be accessed via the `image.geometric_shapes.shapes` property. ```python ---8<-- "docs/concepts/src/spectral_image_shapes_01.py:init" +--8 < --"docs/concepts/src/spectral_image_shapes_01.py:init" ``` As a result, shapes can be added to the spectral image using standard list operations. The example below demonstrates how this can be done: ```python ---8<-- "docs/concepts/src/spectral_image_shapes_01.py:operations" +--8 < --"docs/concepts/src/spectral_image_shapes_01.py:operations" ``` ## Spectral Image Set @@ -183,5 +183,5 @@ As a result, shapes can be added to the spectral image using standard list opera The `SpectralImageSet` class manages a collection of spectral images. ```python ---8<-- "docs/concepts/src/spectral_image_set_01.py" +--8 < --"docs/concepts/src/spectral_image_set_01.py" ``` diff --git a/docs/concepts/features.md b/docs/concepts/features.md index a0fdf43..16bcba2 100644 --- a/docs/concepts/features.md +++ b/docs/concepts/features.md @@ -17,7 +17,7 @@ Spectral indices are mathematical combinations of spectral bands that highlight The `get_spectral_indices()` function returns all spectral indices that can be computed from the available bands: ```python ---8<-- "docs/concepts/src/features_01.py" +--8 < --"docs/concepts/src/features_01.py" ``` ### Computing spectral indices @@ -25,7 +25,7 @@ The `get_spectral_indices()` function returns all spectral indices that can be c The `compute_spectral_indices()` function calculates spectral indices from DataFrame data: ```python ---8<-- "docs/concepts/src/features_02.py" +--8 < --"docs/concepts/src/features_02.py" ``` ### Band mapping @@ -33,7 +33,7 @@ The `compute_spectral_indices()` function calculates spectral indices from DataF When your data uses non-standard column names, use the `bands_map` parameter: ```python ---8<-- "docs/concepts/src/features_03.py:map" +--8 < --"docs/concepts/src/features_03.py:map" ``` ## Automatic features generation @@ -49,7 +49,7 @@ When your data uses non-standard column names, use the `bands_map` parameter: The AutoFeat classes provide deterministic wrappers around the AutoFeat library, which automatically generates and selects engineered features through symbolic regression. ```python ---8<-- "docs/concepts/src/features_04.py" +--8 < --"docs/concepts/src/features_04.py" ``` ### Features extracted using spectral indices @@ -57,7 +57,7 @@ The AutoFeat classes provide deterministic wrappers around the AutoFeat library, These classes integrate spectral index computation with automated feature selection, offering end-to-end pipelines for identifying the most relevant spectral indices. ```python ---8<-- "docs/concepts/src/features_05.py" +--8 < --"docs/concepts/src/features_05.py" ``` ## Integration with siapy enitites @@ -65,5 +65,5 @@ These classes integrate spectral index computation with automated feature select The features module integrates seamlessly with siapy entity system. ```python ---8<-- "docs/concepts/src/features_06.py" +--8 < --"docs/concepts/src/features_06.py" ``` diff --git a/docs/concepts/optimizers.md b/docs/concepts/optimizers.md index 8aa7351..a874d14 100644 --- a/docs/concepts/optimizers.md +++ b/docs/concepts/optimizers.md @@ -14,7 +14,7 @@ The optimizers module provides hyperparameter optimization capabilities for mach The `TabularOptimizer` class provides automated hyperparameter optimization for sklearn-compatible models using tabular spectral data. ```python ---8<-- "docs/concepts/src/optimizers_01.py" +--8 < --"docs/concepts/src/optimizers_01.py" ``` ## Trial Parameters @@ -28,7 +28,7 @@ The `TabularOptimizer` class provides automated hyperparameter optimization for Trial parameters define the hyperparameter search space for optimization. You can specify integer, float, and categorical parameters: ```python ---8<-- "docs/concepts/src/optimizers_02.py" +--8 < --"docs/concepts/src/optimizers_02.py" ``` ## Scorers @@ -43,7 +43,7 @@ Scorers define how model performance is evaluated during optimization. Use cross-validation for robust model evaluation: ```python ---8<-- "docs/concepts/src/optimizers_03.py" +--8 < --"docs/concepts/src/optimizers_03.py" ``` ### Hold-out scorer @@ -51,7 +51,7 @@ Use cross-validation for robust model evaluation: Use hold-out validation for faster evaluation: ```python ---8<-- "docs/concepts/src/optimizers_04.py" +--8 < --"docs/concepts/src/optimizers_04.py" ``` ## Integration with siapy entities @@ -59,5 +59,5 @@ Use hold-out validation for faster evaluation: The optimizers module integrates seamlessly with the siapy entity system. ```python ---8<-- "docs/concepts/src/optimizers_05.py" +--8 < --"docs/concepts/src/optimizers_05.py" ``` diff --git a/docs/concepts/transformations.md b/docs/concepts/transformations.md index 224e691..56f6560 100644 --- a/docs/concepts/transformations.md +++ b/docs/concepts/transformations.md @@ -13,7 +13,7 @@ The transformations module provides essential image processing and co-registrati ### Basic transformations ```python ---8<-- "docs/concepts/src/transformations_01.py" +--8 < --"docs/concepts/src/transformations_01.py" ``` ### Data augmentation @@ -21,7 +21,7 @@ The transformations module provides essential image processing and co-registrati Data augmentation transformations are useful for expanding training datasets and testing algorithm robustness. ```python ---8<-- "docs/concepts/src/transformations_02.py" +--8 < --"docs/concepts/src/transformations_02.py" ``` ### Normalization @@ -29,7 +29,7 @@ Data augmentation transformations are useful for expanding training datasets and The `area_normalization` function normalizes spectral signals by their area under the curve, which is particularly useful for comparing spectral shapes regardless of overall intensity. ```python ---8<-- "docs/concepts/src/transformations_03.py" +--8 < --"docs/concepts/src/transformations_03.py" ``` ## Co-registration @@ -44,7 +44,7 @@ Co-registration enables alignment and coordinate transformation between differen The typical co-registration workflow involves selecting corresponding points in both images and computing a transformation matrix: ```python ---8<-- "docs/concepts/src/transformations_04.py" +--8 < --"docs/concepts/src/transformations_04.py" ``` ### Applying transformations @@ -52,5 +52,5 @@ The typical co-registration workflow involves selecting corresponding points in Once you have a transformation matrix, you can transform pixel coordinates between image spaces: ```python ---8<-- "docs/concepts/src/transformations_05.py" +--8 < --"docs/concepts/src/transformations_05.py" ``` diff --git a/docs/concepts/utils_image.md b/docs/concepts/utils_image.md index cbc81f1..6c8a199 100644 --- a/docs/concepts/utils_image.md +++ b/docs/concepts/utils_image.md @@ -12,7 +12,7 @@ The image utilities module provides functions for saving, loading, and processin The SPy backend saves images in ENVI format. ```python ---8<-- "docs/concepts/src/utils_image_01.py" +--8 < --"docs/concepts/src/utils_image_01.py" ``` ### Rasterio backend @@ -20,7 +20,7 @@ The SPy backend saves images in ENVI format. The Rasterio backend provides geospatial capabilities and supports various formats. ```python ---8<-- "docs/concepts/src/utils_image_02.py" +--8 < --"docs/concepts/src/utils_image_02.py" ``` ## Radiance to Reflectance Conversion @@ -28,11 +28,11 @@ The Rasterio backend provides geospatial capabilities and supports various forma Converting radiance measurements to reflectance using reference panels is essential for quantitative spectral analysis. ```python ---8<-- "docs/concepts/src/utils_image_03.py" +--8 < --"docs/concepts/src/utils_image_03.py" ``` ## Additional Utility Functions ```python ---8<-- "docs/concepts/src/utils_image_04.py" +--8 < --"docs/concepts/src/utils_image_04.py" ``` diff --git a/docs/concepts/utils_plotting.md b/docs/concepts/utils_plotting.md index 68386d1..4eb9d83 100644 --- a/docs/concepts/utils_plotting.md +++ b/docs/concepts/utils_plotting.md @@ -12,7 +12,7 @@ The plotting utilities module provides interactive tools for pixel and area sele Select individual pixels from an image by clicking on them. ```python ---8<-- "docs/concepts/src/utils_plotting_01.py" +--8 < --"docs/concepts/src/utils_plotting_01.py" ``` ### Area-based Selection @@ -20,7 +20,7 @@ Select individual pixels from an image by clicking on them. Select irregular areas from an image using lasso selection tool. ```python ---8<-- "docs/concepts/src/utils_plotting_02.py" +--8 < --"docs/concepts/src/utils_plotting_02.py" ``` ## Image Visualization @@ -30,7 +30,7 @@ Select irregular areas from an image using lasso selection tool. Visualize spectral images with overlaid selected pixels or areas. ```python ---8<-- "docs/concepts/src/utils_plotting_03.py" +--8 < --"docs/concepts/src/utils_plotting_03.py" ``` ### Multiple Image Comparison @@ -38,7 +38,7 @@ Visualize spectral images with overlaid selected pixels or areas. Display multiple images side by side with their corresponding selected areas. ```python ---8<-- "docs/concepts/src/utils_plotting_04.py" +--8 < --"docs/concepts/src/utils_plotting_04.py" ``` ## Signal Visualization @@ -46,5 +46,5 @@ Display multiple images side by side with their corresponding selected areas. Plot mean spectral signatures with standard deviation bands for different classes. ```python ---8<-- "docs/concepts/src/utils_plotting_05.py" +--8 < --"docs/concepts/src/utils_plotting_05.py" ``` diff --git a/docs/examples/case_study.md b/docs/examples/case_study.md index d304692..0552c12 100644 --- a/docs/examples/case_study.md +++ b/docs/examples/case_study.md @@ -47,7 +47,7 @@ The file names encode important metadata: Before diving into the examples, verify that your SiaPy installation and data are correctly configured: ```python ---8<-- "docs/examples/src/spectral_imageset_load_01.py" +--8 < --"docs/examples/src/spectral_imageset_load_01.py" ``` /// Warning @@ -66,7 +66,7 @@ If you encounter issues: **Example:** ```python ---8<-- "docs/examples/src/spectral_image_01.py" +--8 < --"docs/examples/src/spectral_image_01.py" ``` **Source: `spectral_image_01.py`** @@ -88,7 +88,7 @@ If you encounter issues: **Example:** ```python ---8<-- "docs/examples/src/spectral_image_02.py" +--8 < --"docs/examples/src/spectral_image_02.py" ``` **Source: `spectral_image_02.py`** @@ -109,7 +109,7 @@ If you encounter issues: **Example:** ```python ---8<-- "docs/examples/src/spectral_imageset_01.py" +--8 < --"docs/examples/src/spectral_imageset_01.py" ``` **Source: `spectral_imageset_01.py`** @@ -130,7 +130,7 @@ If you encounter issues: **Example:** ```python ---8<-- "docs/examples/src/visualization_01.py" +--8 < --"docs/examples/src/visualization_01.py" ``` **Source: `visualization_01.py`** @@ -152,7 +152,7 @@ The selected pixels are highlighted in the image below. **Example:** ```python ---8<-- "docs/examples/src/visualization_02.py" +--8 < --"docs/examples/src/visualization_02.py" ``` **Source: `visualization_02.py`** @@ -176,7 +176,7 @@ The selected areas are highlighted in the image below. **Example:** ```python ---8<-- "docs/examples/src/transformations_01.py" +--8 < --"docs/examples/src/transformations_01.py" ``` **Source: `transformations_01.py`** @@ -194,7 +194,7 @@ The selected areas are highlighted in the image below. **Example:** ```python ---8<-- "docs/examples/src/transformations_02.py" +--8 < --"docs/examples/src/transformations_02.py" ``` **Source: `transformations_02.py`** @@ -212,7 +212,7 @@ The selected areas are highlighted in the image below. **Example:** ```python ---8<-- "docs/examples/src/transformations_03.py" +--8 < --"docs/examples/src/transformations_03.py" ``` **Source: `transformations_03.py`** diff --git a/siapy/core/exceptions.py b/siapy/core/exceptions.py index f6f28a7..638a194 100644 --- a/siapy/core/exceptions.py +++ b/siapy/core/exceptions.py @@ -9,14 +9,14 @@ from typing import Any __all__ = [ - "SiapyError", + "ConfigurationError", + "DirectInitializationError", "InvalidFilepathError", "InvalidInputError", "InvalidTypeError", - "ProcessingError", - "ConfigurationError", "MethodNotImplementedError", - "DirectInitializationError", + "ProcessingError", + "SiapyError", ] diff --git a/siapy/core/types.py b/siapy/core/types.py index 253bb4e..d5cdd0f 100644 --- a/siapy/core/types.py +++ b/siapy/core/types.py @@ -5,7 +5,8 @@ and consistency across the codebase. """ -from typing import Any, Sequence +from collections.abc import Sequence +from typing import Any import numpy as np import pandas as pd @@ -17,14 +18,14 @@ from siapy.entities import SpectralImage, SpectralImageSet __all__ = [ - "SpectralLibType", - "XarrayType", - "ImageType", - "ImageSizeType", - "ImageDataType", - "ImageContainerType", "ArrayLike1dType", "ArrayLike2dType", + "ImageContainerType", + "ImageDataType", + "ImageSizeType", + "ImageType", + "SpectralLibType", + "XarrayType", ] SpectralLibType = sp.io.envi.BilFile | sp.io.envi.BipFile | sp.io.envi.BsqFile diff --git a/siapy/datasets/__init__.py b/siapy/datasets/__init__.py index 31b64f1..1c0499e 100644 --- a/siapy/datasets/__init__.py +++ b/siapy/datasets/__init__.py @@ -1,6 +1,5 @@ from .tabular import TabularDataset - __all__ = [ "TabularDataset", ] diff --git a/siapy/datasets/helpers.py b/siapy/datasets/helpers.py index c6e08b2..4252a01 100644 --- a/siapy/datasets/helpers.py +++ b/siapy/datasets/helpers.py @@ -126,4 +126,3 @@ def merge_signals_from_multiple_cameras(data: "TabularDatasetData") -> None: Implement the actual merging logic based on camera specifications and data alignment requirements. """ - pass diff --git a/siapy/datasets/schemas.py b/siapy/datasets/schemas.py index 9a97723..a0d270b 100644 --- a/siapy/datasets/schemas.py +++ b/siapy/datasets/schemas.py @@ -1,6 +1,7 @@ from abc import ABC, abstractmethod +from collections.abc import Iterable from dataclasses import dataclass -from typing import Any, Iterable, Optional +from typing import Any import pandas as pd from pydantic import BaseModel, ConfigDict @@ -368,7 +369,7 @@ def from_dict(cls, data: dict[str, Any]) -> "TabularDatasetData": return cls(signatures=signatures, metadata=metadata, target=target) @staticmethod - def target_from_dict(data: dict[str, Any] | None = None) -> Optional[Target]: + def target_from_dict(data: dict[str, Any] | None = None) -> Target | None: """Create an appropriate Target instance from a dictionary. Automatically determines whether to create a ClassificationTarget or diff --git a/siapy/datasets/tabular.py b/siapy/datasets/tabular.py index ecc60aa..336c858 100644 --- a/siapy/datasets/tabular.py +++ b/siapy/datasets/tabular.py @@ -1,6 +1,6 @@ +from collections.abc import Iterator from dataclasses import dataclass from pathlib import Path -from typing import Iterator import pandas as pd from pydantic import BaseModel, ConfigDict diff --git a/siapy/entities/__init__.py b/siapy/entities/__init__.py index ef267b8..03c29ce 100644 --- a/siapy/entities/__init__.py +++ b/siapy/entities/__init__.py @@ -5,9 +5,9 @@ from .signatures import Signatures __all__ = [ - "SpectralImage", - "SpectralImageSet", "Pixels", - "Signatures", "Shape", + "Signatures", + "SpectralImage", + "SpectralImageSet", ] diff --git a/siapy/entities/images/__init__.py b/siapy/entities/images/__init__.py index 951f6e0..b2e0107 100644 --- a/siapy/entities/images/__init__.py +++ b/siapy/entities/images/__init__.py @@ -5,7 +5,7 @@ __all__ = [ "ImageBase", - "SpectralLibImage", "RasterioLibImage", "SpectralImage", + "SpectralLibImage", ] diff --git a/siapy/entities/images/interfaces.py b/siapy/entities/images/interfaces.py index 052d9eb..aa958ed 100644 --- a/siapy/entities/images/interfaces.py +++ b/siapy/entities/images/interfaces.py @@ -41,7 +41,6 @@ def open(cls: type["ImageBase"], *args: Any, **kwargs: Any) -> "ImageBase": Each implementation defines its own signature for this method based on the specific requirements of the underlying library. """ - pass @property @abstractmethod @@ -51,7 +50,6 @@ def filepath(self) -> Path: Returns: A Path object representing the location of the image file. For in-memory images, this may return an empty Path. """ - pass @property @abstractmethod @@ -61,7 +59,6 @@ def metadata(self) -> dict[str, Any]: Returns: A dictionary containing image metadata such as coordinate reference system, geotransform information, wavelength data, and other image properties. The specific contents depend on the underlying format and library. """ - pass @property @abstractmethod @@ -71,7 +68,6 @@ def shape(self) -> tuple[int, int, int]: Returns: A tuple (height, width, bands) representing the image dimensions. """ - pass @property @abstractmethod @@ -81,7 +77,6 @@ def bands(self) -> int: Returns: The number of spectral bands (channels) in the image. """ - pass @property @abstractmethod @@ -91,7 +86,6 @@ def default_bands(self) -> list[int]: Returns: A list of band indices typically used for red, green, and blue channels when displaying the image as an RGB composite. """ - pass @property @abstractmethod @@ -101,7 +95,6 @@ def wavelengths(self) -> list[float]: Returns: A list of wavelength values (typically in nanometers) for each band. For non-spectral data, this may return band numbers or other identifiers. """ - pass @property @abstractmethod @@ -111,7 +104,6 @@ def camera_id(self) -> str: Returns: A string identifying the camera or sensor used to capture the image. May return an empty string if no camera information is available. """ - pass @abstractmethod def to_display(self, equalize: bool = True) -> Image.Image: @@ -123,7 +115,6 @@ def to_display(self, equalize: bool = True) -> Image.Image: Returns: A PIL Image object suitable for display, typically as an RGB composite created from the default bands with appropriate scaling and normalization. """ - pass @abstractmethod def to_numpy(self, nan_value: float | None = None) -> NDArray[np.floating[Any]]: @@ -135,7 +126,6 @@ def to_numpy(self, nan_value: float | None = None) -> NDArray[np.floating[Any]]: Returns: A 3D numpy array with shape (height, width, bands) containing the image data. The array dtype should be a floating-point type. """ - pass @abstractmethod def to_xarray(self) -> "XarrayType": @@ -144,4 +134,3 @@ def to_xarray(self) -> "XarrayType": Returns: An xarray DataArray with labeled dimensions and coordinates, suitable for advanced analysis and visualization. The array should include appropriate coordinate information and metadata attributes. """ - pass diff --git a/siapy/entities/images/spimage.py b/siapy/entities/images/spimage.py index f6a18db..0f59bf2 100644 --- a/siapy/entities/images/spimage.py +++ b/siapy/entities/images/spimage.py @@ -1,6 +1,7 @@ +from collections.abc import Iterable, Sequence from dataclasses import dataclass from pathlib import Path -from typing import TYPE_CHECKING, Any, Generic, Iterable, Sequence, TypeVar +from typing import TYPE_CHECKING, Any, Generic, TypeVar import numpy as np import pandas as pd @@ -61,7 +62,7 @@ def __lt__(self, other: "SpectralImage[Any]") -> bool: """ return self.filepath.name < other.filepath.name - def __eq__(self, other: Any) -> bool: + def __eq__(self, other: object) -> bool: """Check equality between two SpectralImage instances. Args: diff --git a/siapy/entities/imagesets.py b/siapy/entities/imagesets.py index 588f8e5..43dfa5a 100644 --- a/siapy/entities/imagesets.py +++ b/siapy/entities/imagesets.py @@ -1,6 +1,7 @@ +from collections.abc import Iterator, Sequence from dataclasses import dataclass from pathlib import Path -from typing import Any, Iterator, Sequence +from typing import Any import numpy as np from rich.progress import track diff --git a/siapy/entities/pixels.py b/siapy/entities/pixels.py index 294244f..a97205a 100644 --- a/siapy/entities/pixels.py +++ b/siapy/entities/pixels.py @@ -1,8 +1,9 @@ from __future__ import annotations +from collections.abc import Iterable, Sequence from dataclasses import dataclass from pathlib import Path -from typing import Any, ClassVar, Iterable, NamedTuple, Sequence, TypeAlias +from typing import Any, ClassVar, NamedTuple, TypeAlias import numpy as np import pandas as pd @@ -11,10 +12,10 @@ from siapy.core.exceptions import InvalidInputError, InvalidTypeError __all__ = [ - "Pixels", - "PixelCoordinate", "CoordinateInput", "HomogeneousCoordinate", + "PixelCoordinate", + "Pixels", "validate_pixel_input", ] @@ -45,13 +46,13 @@ def __len__(self) -> int: def __repr__(self) -> str: return f"Pixels(\n{self.df}\n)" - def __getitem__(self, indices: Any) -> "Pixels": + def __getitem__(self, indices: Any) -> Pixels: df_slice = self.df.iloc[indices] if isinstance(df_slice, pd.Series): df_slice = df_slice.to_frame().T return Pixels(df_slice) - def __eq__(self, other: Any) -> bool: + def __eq__(self, other: object) -> bool: if not isinstance(other, Pixels): return False return self.df.equals(other.df) @@ -67,13 +68,13 @@ def __post_init__(self) -> None: validate_pixel_input_dimensions(self._data) @classmethod - def from_iterable(cls, iterable: Iterable[CoordinateInput]) -> "Pixels": + def from_iterable(cls, iterable: Iterable[CoordinateInput]) -> Pixels: df = pd.DataFrame(iterable, columns=[cls.coords.X, cls.coords.Y]) validate_pixel_input_dimensions(df) return cls(df) @classmethod - def load_from_parquet(cls, filepath: str | Path) -> "Pixels": + def load_from_parquet(cls, filepath: str | Path) -> Pixels: df = pd.read_parquet(filepath) validate_pixel_input_dimensions(df) return cls(df) @@ -87,10 +88,10 @@ def df_homogenious(self) -> pd.DataFrame: df_homo[self.coords.H] = 1 return df_homo - def x(self) -> "pd.Series[float]": + def x(self) -> pd.Series[float]: return self.df[self.coords.X] - def y(self) -> "pd.Series[float]": + def y(self) -> pd.Series[float]: return self.df[self.coords.Y] def to_numpy(self) -> NDArray[np.floating[Any]]: @@ -102,7 +103,7 @@ def to_list(self) -> list[PixelCoordinate]: def save_to_parquet(self, filepath: str | Path) -> None: self.df.to_parquet(filepath, index=True) - def as_type(self, dtype: type) -> "Pixels": + def as_type(self, dtype: type) -> Pixels: converted_df = self.df.copy() converted_df[self.coords.X] = converted_df[self.coords.X].astype(dtype) converted_df[self.coords.Y] = converted_df[self.coords.Y].astype(dtype) @@ -173,7 +174,7 @@ def validate_pixel_input(input_data: Pixels | pd.DataFrame | Iterable[Coordinate raise InvalidInputError( input_value=input_data, - message=f"Failed to convert input to Pixels: {str(e)}" + message=f"Failed to convert input to Pixels: {e!s}" f"\nExpected a Pixels instance or an iterable (e.g. list, np.array, tuple, pd.DataFrame)." f"\nThe input must contain 2D coordinates with x and y values.", ) diff --git a/siapy/entities/shapes/geometric_shapes.py b/siapy/entities/shapes/geometric_shapes.py index a36c666..7f5a89f 100644 --- a/siapy/entities/shapes/geometric_shapes.py +++ b/siapy/entities/shapes/geometric_shapes.py @@ -1,6 +1,7 @@ import sys +from collections.abc import Iterable, Iterator from dataclasses import dataclass -from typing import TYPE_CHECKING, Any, Iterable, Iterator, Optional +from typing import TYPE_CHECKING, Any, Optional from siapy.core.exceptions import InvalidInputError @@ -42,7 +43,7 @@ def __setitem__(self, index: int, shape: "Shape") -> None: def __len__(self) -> int: return len(self._geometric_shapes) - def __eq__(self, other: Any) -> bool: + def __eq__(self, other: object) -> bool: if not isinstance(other, GeometricShapes): raise InvalidInputError( { diff --git a/siapy/entities/shapes/shape.py b/siapy/entities/shapes/shape.py index 0512cac..32954d5 100644 --- a/siapy/entities/shapes/shape.py +++ b/siapy/entities/shapes/shape.py @@ -1,7 +1,8 @@ +from collections.abc import Iterable from dataclasses import dataclass from enum import Enum from pathlib import Path -from typing import Any, Iterable, Optional +from typing import Any import geopandas as gpd import numpy as np @@ -50,8 +51,8 @@ class Shape: def __init__( self, label: str = "", - geometry: Optional[BaseGeometry] = None, - geo_dataframe: Optional[gpd.GeoDataFrame] = None, + geometry: BaseGeometry | None = None, + geo_dataframe: gpd.GeoDataFrame | None = None, ): """Initialize Shape with either a geometry or geodataframe""" self._label = label @@ -149,7 +150,7 @@ def from_multiline( def from_polygon( cls, exterior: Pixels | pd.DataFrame | Iterable[CoordinateInput], - holes: Optional[list[Pixels | pd.DataFrame | Iterable[CoordinateInput]]] = None, + holes: list[Pixels | pd.DataFrame | Iterable[CoordinateInput]] | None = None, label: str = "", ) -> "Shape": exterior = validate_pixel_input(exterior) diff --git a/siapy/entities/signatures.py b/siapy/entities/signatures.py index 0daeeb2..61c5d72 100644 --- a/siapy/entities/signatures.py +++ b/siapy/entities/signatures.py @@ -1,6 +1,7 @@ +from collections.abc import Iterable, Sequence from dataclasses import dataclass from pathlib import Path -from typing import Any, Iterable, Sequence +from typing import Any import numpy as np import pandas as pd @@ -12,8 +13,8 @@ from .pixels import CoordinateInput, Pixels, validate_pixel_input __all__ = [ - "Signatures", "Signals", + "Signatures", ] @@ -98,7 +99,7 @@ def validate_signal_input(input_data: Signals | pd.DataFrame | Iterable[Sequence raise InvalidInputError( input_value=input_data, - message=f"Failed to convert input to Signals: {str(e)}" + message=f"Failed to convert input to Signals: {e!s}" f"\nExpected a Signals instance or an iterable (e.g. list, np.array, pd.DataFrame)." f"\nThe input must contain spectral signal values.", ) @@ -120,7 +121,7 @@ def __getitem__(self, indices: Any) -> "Signatures": signals = self.signals[indices] return Signatures(pixels, signals) - def __eq__(self, other: Any) -> bool: + def __eq__(self, other: object) -> bool: if not isinstance(other, Signatures): return False return self.pixels.df.equals(other.pixels.df) and self.signals.df.equals(other.signals.df) diff --git a/siapy/features/features.py b/siapy/features/features.py index 495d324..f371a10 100644 --- a/siapy/features/features.py +++ b/siapy/features/features.py @@ -1,6 +1,7 @@ from __future__ import annotations -from typing import Any, Iterable, Literal +from collections.abc import Iterable +from typing import Any, Literal import numpy as np import pandas as pd @@ -66,7 +67,7 @@ def __init__( def fit( self, data: np.ndarray[Any, Any] | pd.DataFrame, target: np.ndarray[Any, Any] | pd.DataFrame - ) -> "AutoFeatClassification": + ) -> AutoFeatClassification: set_random_seed(self.random_seed) super().fit(data, target) return self @@ -127,7 +128,7 @@ def __init__( def fit( self, data: np.ndarray[Any, Any] | pd.DataFrame, target: np.ndarray[Any, Any] | pd.DataFrame - ) -> "AutoFeatRegression": + ) -> AutoFeatRegression: set_random_seed(self.random_seed) super().fit(data, target) return self @@ -160,7 +161,7 @@ def __init__( self.bands_map = bands_map self.merge_with_original = merge_with_original - def fit(self, data: pd.DataFrame, target: "pd.Series[Any]") -> BaseEstimator: + def fit(self, data: pd.DataFrame, target: pd.Series[Any]) -> BaseEstimator: df_indices = compute_spectral_indices( data=data, spectral_indices=self.spectral_indices, @@ -184,7 +185,7 @@ def transform(self, data: pd.DataFrame) -> pd.DataFrame: return pd.concat([data, df_indices], axis=1) return df_indices - def fit_transform(self, data: pd.DataFrame, target: "pd.Series[Any]") -> pd.DataFrame: + def fit_transform(self, data: pd.DataFrame, target: pd.Series[Any]) -> pd.DataFrame: self.fit(data, target) return self.transform(data) diff --git a/siapy/features/helpers.py b/siapy/features/helpers.py index 53e35d4..c4a6933 100644 --- a/siapy/features/helpers.py +++ b/siapy/features/helpers.py @@ -72,14 +72,14 @@ def feature_selector_factory( ) sfs = SequentialFeatureSelector( estimator=algo, - k_features=k_features, # type: ignore # noqa + k_features=k_features, # type: ignore forward=forward, floating=floating, verbose=verbose, scoring=scoring, cv=cv, n_jobs=n_jobs, - pre_dispatch=pre_dispatch, # type: ignore # noqa + pre_dispatch=pre_dispatch, # type: ignore ) return make_pipeline(RobustScaler(), sfs, memory=None) diff --git a/siapy/features/spectral_indices.py b/siapy/features/spectral_indices.py index f53aa7a..dd14b71 100644 --- a/siapy/features/spectral_indices.py +++ b/siapy/features/spectral_indices.py @@ -1,5 +1,6 @@ import warnings -from typing import Any, Iterable +from collections.abc import Iterable +from typing import Any import numpy as np import pandas as pd @@ -15,8 +16,8 @@ import spyndex # type: ignore __all__ = [ - "get_spectral_indices", "compute_spectral_indices", + "get_spectral_indices", ] diff --git a/siapy/optimizers/configs.py b/siapy/optimizers/configs.py index d0748da..c7e86a6 100644 --- a/siapy/optimizers/configs.py +++ b/siapy/optimizers/configs.py @@ -1,4 +1,5 @@ -from typing import Callable, Iterable, Literal +from collections.abc import Callable, Iterable +from typing import Literal import optuna from pydantic import BaseModel, ConfigDict diff --git a/siapy/optimizers/evaluators.py b/siapy/optimizers/evaluators.py index 263398e..a8a0d9e 100644 --- a/siapy/optimizers/evaluators.py +++ b/siapy/optimizers/evaluators.py @@ -1,4 +1,5 @@ -from typing import Annotated, Any, Callable, Iterable, Literal +from collections.abc import Callable, Iterable +from typing import Annotated, Any, Literal import numpy as np from numpy.typing import NDArray diff --git a/siapy/optimizers/parameters.py b/siapy/optimizers/parameters.py index a5f46ad..0bc812d 100644 --- a/siapy/optimizers/parameters.py +++ b/siapy/optimizers/parameters.py @@ -1,12 +1,13 @@ +from collections.abc import Sequence from dataclasses import dataclass -from typing import Annotated, Any, Sequence +from typing import Annotated, Any from pydantic import BaseModel __all__ = [ + "CategoricalParameter", "FloatParameter", "IntParameter", - "CategoricalParameter", "TrialParameters", ] diff --git a/siapy/optimizers/scorers.py b/siapy/optimizers/scorers.py index 4a48a12..6bf93ee 100644 --- a/siapy/optimizers/scorers.py +++ b/siapy/optimizers/scorers.py @@ -1,5 +1,6 @@ +from collections.abc import Callable, Iterable from functools import partial -from typing import Annotated, Callable, Iterable, Literal, Any +from typing import Annotated, Any, Literal import numpy as np from numpy.typing import NDArray diff --git a/siapy/transformations/corregistrator.py b/siapy/transformations/corregistrator.py index 86ee788..71ed411 100644 --- a/siapy/transformations/corregistrator.py +++ b/siapy/transformations/corregistrator.py @@ -1,4 +1,5 @@ -from typing import Any, Iterable, Sequence +from collections.abc import Iterable, Sequence +from typing import Any import matplotlib.pyplot as plt import numpy as np @@ -8,9 +9,9 @@ from siapy.entities.pixels import CoordinateInput, Pixels, validate_pixel_input __all__ = [ - "map_affine_approx_2d", "affine_matx_2d", "align", + "map_affine_approx_2d", "transform", ] diff --git a/siapy/transformations/image.py b/siapy/transformations/image.py index d1d57cb..2b6e02c 100644 --- a/siapy/transformations/image.py +++ b/siapy/transformations/image.py @@ -1,5 +1,6 @@ import random -from typing import Any, Callable +from collections.abc import Callable +from typing import Any import numpy as np from numpy.typing import NDArray @@ -10,11 +11,11 @@ __all__ = [ "add_gaussian_noise", + "area_normalization", "random_crop", "random_mirror", "random_rotation", "rescale", - "area_normalization", ] diff --git a/siapy/utils/general.py b/siapy/utils/general.py index 03bf028..0b3ad3c 100644 --- a/siapy/utils/general.py +++ b/siapy/utils/general.py @@ -3,9 +3,10 @@ import random import re import types +from collections.abc import Callable, Generator, Iterable from functools import partial from pathlib import Path -from typing import Any, Callable, Generator, Iterable, Optional +from typing import Any import numpy as np @@ -13,22 +14,22 @@ from siapy.core.exceptions import InvalidInputError __all__ = [ - "initialize_object", - "initialize_function", - "ensure_dir", - "get_number_cpus", "dict_zip", - "get_increasing_seq_indices", - "set_random_seed", + "ensure_dir", "get_classmethods", + "get_increasing_seq_indices", + "get_number_cpus", + "initialize_function", + "initialize_object", "match_iterable_items_by_regex", + "set_random_seed", ] def initialize_object( module: types.ModuleType | Any, module_name: str, - module_args: Optional[dict[str, Any]] = None, + module_args: dict[str, Any] | None = None, *args: Any, **kwargs: Any, ) -> Any: @@ -41,7 +42,7 @@ def initialize_object( def initialize_function( module: types.ModuleType | Any, module_name: str, - module_args: Optional[dict[str, Any]] = None, + module_args: dict[str, Any] | None = None, *args: Any, **kwargs: Any, ) -> Callable[..., Any]: diff --git a/siapy/utils/image_validators.py b/siapy/utils/image_validators.py index 2527bd1..ba04a80 100644 --- a/siapy/utils/image_validators.py +++ b/siapy/utils/image_validators.py @@ -9,9 +9,9 @@ from siapy.entities import SpectralImage __all__ = [ - "validate_image_to_numpy_3channels", - "validate_image_to_numpy", "validate_image_size", + "validate_image_to_numpy", + "validate_image_to_numpy_3channels", ] diff --git a/siapy/utils/images.py b/siapy/utils/images.py index 54b476c..f170d0c 100644 --- a/siapy/utils/images.py +++ b/siapy/utils/images.py @@ -18,16 +18,16 @@ from siapy.utils.signatures import get_signatures_within_convex_hull __all__ = [ - "spy_save_image", - "spy_create_image", - "spy_merge_images_by_specter", - "rasterio_save_image", - "rasterio_create_image", - "convert_radiance_image_to_reflectance", + "blockfy_image", "calculate_correction_factor", "calculate_correction_factor_from_panel", - "blockfy_image", "calculate_image_background_percentage", + "convert_radiance_image_to_reflectance", + "rasterio_create_image", + "rasterio_save_image", + "spy_create_image", + "spy_merge_images_by_specter", + "spy_save_image", ] diff --git a/siapy/utils/plots.py b/siapy/utils/plots.py index f56cf9a..e378eb4 100644 --- a/siapy/utils/plots.py +++ b/siapy/utils/plots.py @@ -18,12 +18,12 @@ from siapy.utils.image_validators import validate_image_to_numpy_3channels __all__ = [ - "pixels_select_click", - "pixels_select_lasso", + "InteractiveButtonsEnum", "display_image_with_areas", "display_multiple_images_with_areas", "display_signals", - "InteractiveButtonsEnum", + "pixels_select_click", + "pixels_select_lasso", ] diff --git a/tests/data_manager.py b/tests/data_manager.py index 4343fde..86e0990 100644 --- a/tests/data_manager.py +++ b/tests/data_manager.py @@ -42,8 +42,7 @@ def download_file(url: str, save_path: Path) -> None: response.raise_for_status() with open(save_path, "wb") as f: - for chunk in response.iter_content(chunk_size=8192): - f.write(chunk) + f.writelines(response.iter_content(chunk_size=8192)) def extract_archive(archive_path: Path, extract_dir: Path) -> None: diff --git a/tests/utils/test_utils_images.py b/tests/utils/test_utils_images.py index bc9a93b..1121028 100644 --- a/tests/utils/test_utils_images.py +++ b/tests/utils/test_utils_images.py @@ -5,7 +5,7 @@ import numpy as np import pytest -import rioxarray # noqa +import rioxarray import spectral as sp from siapy.core.exceptions import InvalidInputError diff --git a/tests/utils/test_utils_signatures.py b/tests/utils/test_utils_signatures.py index 97d6357..008ff55 100644 --- a/tests/utils/test_utils_signatures.py +++ b/tests/utils/test_utils_signatures.py @@ -2,8 +2,8 @@ import pytest from siapy.entities import Pixels, Shape, SpectralImage -from siapy.utils.signatures import get_signatures_within_convex_hull from siapy.utils.plots import display_image_with_areas +from siapy.utils.signatures import get_signatures_within_convex_hull def test_get_signatures_within_convex_hull(configs):