diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c26b3daf..589243d3 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] diff --git a/docs/concepts/datasets.md b/docs/concepts/datasets.md index e07daa9f..55e3e4e3 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 421ee613..d113d33f 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 a0fdf435..16bcba2e 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 8aa73517..a874d145 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 224e6910..56f6560e 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 cbc81f12..6c8a1990 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 68386d13..4eb9d838 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 d304692d..0552c124 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 f6f28a75..638a1941 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 253bb4eb..d5cdd0f9 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 31b64f1b..1c0499e7 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 c6e08b2d..4252a01e 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 9a97723a..a0d270b2 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 ecc60aab..336c858a 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 ef267b82..03c29cec 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 951f6e00..b2e01073 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 052d9eb0..aa958ed3 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 f6a18db4..0f59bf2a 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 588f8e53..43dfa5a3 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 294244ff..a97205a1 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 a36c666e..7f5a89f0 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 0512cacd..32954d59 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 0daeeb28..61c5d722 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 495d324c..f371a103 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 53e35d4a..c4a69338 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 f53aa7af..dd14b712 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 d0748da3..c7e86a6b 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 263398e7..a8a0d9e6 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 a5f46ade..0bc812df 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 4a48a128..6bf93eea 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 86ee7880..71ed4112 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 d1d57cb8..2b6e02c7 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 03bf028c..0b3ad3c1 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 2527bd1f..ba04a80c 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 54b476c0..f170d0ca 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 f56cf9a9..e378eb49 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 4343fde4..86e0990b 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 bc9a93b4..11210287 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 97d63572..008ff55e 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):