Source code for shapelib.core.geometries.operations_mixin

from collections.abc import Callable
from typing import Any, TypeVar

import geopandas as gpd

F = TypeVar("F", bound=Callable[..., Any])


[docs] def propagate_class(method: F) -> F: """ Decorator to ensure that operations on a GeoDataFrame return an instance of the correct class and propagate metadata. This decorator is used to wrap methods that return a `GeoDataFrame`, ensuring that the resulting object is an instance of the calling class and carries over any metadata, such as the geometry column and CRS. Parameters ---------- method : Callable The method that operates on the GeoDataFrame. Returns ------- Callable A decorated method that ensures class and metadata propagation after GeoDataFrame operations. Examples -------- Applying the `propagate_class` decorator to a method that modifies a GeoDataFrame: >>> class MyGeoDataFrame(gpd.GeoDataFrame): ... @propagate_class ... def custom_method(self): ... # Some operation that returns a new GeoDataFrame ... return gpd.GeoDataFrame() Notes ----- This decorator is useful when extending or subclassing `geopandas.GeoDataFrame`, as it ensures that the result of operations like `explode` or `dissolve` remains an instance of the derived class, rather than reverting to a base `GeoDataFrame`. """ def wrapper(self: Any, *args: Any, **kwargs: Any) -> Any: result = method(self, *args, **kwargs) if isinstance(result, gpd.GeoDataFrame): # The constructor flattens Z unless told otherwise, and __finalize__ # copies keep_z only after it has run, so the flag goes in up front. extra = {"keep_z": self.keep_z} if hasattr(self, "keep_z") else {} return self._constructor(result, **extra).__finalize__(self) return result # Copiar metadados do método original do GeoDataFrame method_name = method.__name__ if hasattr(gpd.GeoDataFrame, method_name): original = getattr(gpd.GeoDataFrame, method_name) wrapper.__doc__ = original.__doc__ wrapper.__name__ = original.__name__ wrapper.__qualname__ = method.__qualname__ wrapper.__annotations__ = getattr(original, "__annotations__", {}) return wrapper # type: ignore[return-value]
[docs] class OperationsMixin: """ A mixin class that extends common GeoDataFrame operations while ensuring class and metadata propagation. This mixin applies the `propagate_class` decorator to methods such as `explode` and `dissolve` to maintain the correct class type and carry over any relevant metadata, such as CRS and the geometry column, after performing GeoDataFrame operations. """ @propagate_class def explode(self, *args: Any, **kwargs: Any) -> gpd.GeoDataFrame: """ Explode multi-part geometries into single geometries. This method wraps `geopandas.GeoDataFrame.explode`, ensuring that the resulting GeoDataFrame retains the class type and metadata of the original object. Parameters ---------- *args : tuple Positional arguments to pass to `geopandas.GeoDataFrame.explode`. **kwargs : dict Keyword arguments to pass to `geopandas.GeoDataFrame.explode`. Returns ------- gpd.GeoDataFrame A GeoDataFrame with each multi-part geometry split into single geometries, maintaining the class and metadata. Examples -------- >>> gdf = gpd.GeoDataFrame({'geometry': [MultiPolygon(...)]}) >>> exploded_gdf = gdf.explode() Notes ----- Use this method to break up geometries like MultiPolygons or MultiLinestrings into their individual components. """ return super().explode(*args, **kwargs) # type: ignore[misc] @propagate_class def overlay(self, *args: Any, **kwargs: Any) -> gpd.GeoDataFrame: """ Perform a spatial overlay between two GeoDataFrames. This method wraps `geopandas.GeoDataFrame.overlay`, ensuring that the resulting GeoDataFrame retains the class type and metadata of the original object. Parameters ---------- *args : tuple Positional arguments to pass to `geopandas.GeoDataFrame.overlay`. **kwargs : dict Keyword arguments to pass to `geopandas.GeoDataFrame.overlay`. Returns ------- gpd.GeoDataFrame A GeoDataFrame resulting from the spatial overlay operation, maintaining the class and metadata. Examples -------- Perform a spatial intersection between two GeoDataFrames: >>> gdf1 = gpd.GeoDataFrame({'geometry': [Polygon(...), Polygon(...)]}) >>> gdf2 = gpd.GeoDataFrame({'geometry': [Polygon(...)]}) >>> overlay_gdf = gdf1.overlay(gdf2, how='intersection') Notes ----- The `overlay` function allows you to perform spatial operations between two GeoDataFrames, such as intersection, union, difference, or symmetric difference. """ return super().overlay(*args, **kwargs) # type: ignore[misc]