diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 712d606..4427909 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"] + python-version: ["3.11", "3.12", "3.13", "3.14"] steps: - name: Checkout repository diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index d03fd84..2878982 100644 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -1,6 +1,5 @@ # This workflow will upload a Python Package using Twine when a release is created # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python#publishing-to-package-registries - # This workflow uses actions that are not certified by GitHub. # They are provided by a third-party and are governed by # separate terms of service, privacy policy, and support @@ -14,26 +13,28 @@ on: permissions: contents: read - id-token: write jobs: deploy: - runs-on: ubuntu-latest - steps: - uses: actions/checkout@v4 + - name: Set up Python uses: actions/setup-python@v4 with: python-version: '3.x' + - name: Install dependencies run: | python -m pip install --upgrade pip pip install build + - name: Build package run: python -m build + - name: Publish package uses: pypa/gh-action-pypi-publish@release/v1 with: - repository-url: https://upload.pypi.org/legacy/ + password: ${{ secrets.PYPI_API_TOKEN }} + repository-url: https://upload.pypi.org/legacy/ \ No newline at end of file diff --git a/rasters/clip.py b/rasters/clip.py index 28bd67b..fb2b1db 100644 --- a/rasters/clip.py +++ b/rasters/clip.py @@ -3,30 +3,33 @@ from typing import Union, TYPE_CHECKING import numpy as np +from geopandas.array import GeometryArray # Import GeometryArray for type checking +from shapely.geometry import box # Import for bounding box clipping if TYPE_CHECKING: from .raster import Raster def clip(a: Union[Raster, np.ndarray], a_min, a_max, out=None, **kwargs) -> Union[Raster, np.ndarray]: """ - Clips the values of an array or Raster to a specified range. + Clips the values of an array, Raster, or GeometryArray to a specified range. - This function limits the values in the input array or Raster to fall within the - range defined by `a_min` and `a_max`. Values below `a_min` are set to `a_min`, - and values above `a_max` are set to `a_max`. + This function limits the values in the input array, Raster, or GeometryArray to fall within the + range defined by `a_min` and `a_max`. For arrays, values below `a_min` are set to `a_min`, + and values above `a_max` are set to `a_max`. For GeometryArray, geometries are clipped to + the bounding box defined by `a_min` and `a_max`. Args: - a (Union[Raster, np.ndarray]): The input array or Raster to clip. - a_min (float, optional): The minimum value to clip to. If None, no minimum clipping is performed. - a_max (float, optional): The maximum value to clip to. If None, no maximum clipping is performed. + a (Union[Raster, np.ndarray, GeometryArray]): The input data to clip. + a_min (float, optional): The minimum value or bounding box limit. If None, no minimum clipping is performed. + a_max (float, optional): The maximum value or bounding box limit. If None, no maximum clipping is performed. out (np.ndarray, optional): An optional output array to store the result. Must be the same shape and dtype as `a`. **kwargs: Additional keyword arguments passed to np.clip (currently unused). Returns: - Union[Raster, np.ndarray]: The clipped array or Raster. If `a` is a Raster, a new + Union[Raster, np.ndarray, GeometryArray]: The clipped data. If `a` is a Raster, a new Raster object with the clipped data is returned. - Otherwise, a NumPy array is returned. + Otherwise, a NumPy array or GeometryArray is returned. """ from .raster import Raster # Import here to avoid circular dependency @@ -35,6 +38,17 @@ def clip(a: Union[Raster, np.ndarray], a_min, a_max, out=None, **kwargs) -> Unio result = a # Initialize result with the input data + if isinstance(result, GeometryArray): + if a_min is None or a_max is None: + raise ValueError("Both a_min and a_max must be specified for GeometryArray clipping.") + + # Create a bounding box from a_min and a_max + bbox = box(a_min[0], a_min[1], a_max[0], a_max[1]) + + # Clip each geometry to the bounding box + result = GeometryArray([geom.intersection(bbox) for geom in result]) + return result + if a_min is not None: result = np.where(result < a_min, a_min, result) # Clip values below a_min if a_max is not None: