Source code for shapelib.core.handlers.geo_file_handler

from abc import ABC, abstractmethod
from pathlib import Path
from typing import Any

import geopandas as gpd

from shapelib.core.geometries.base import BaseShapes
from shapelib.core.geometries.empty import Empty
from shapelib.core.geometries.lines import Lines
from shapelib.core.geometries.points import Points
from shapelib.core.geometries.polygons import Polygons
from shapelib.utils.utils import assume_wgs84_if_lonlat, get_most_common_geometry_type


[docs] class GeoFileHandler(ABC): """ Abstract base class for handling geospatial files. This class provides a common interface for geospatial file handlers, including methods for loading data, validating files, and creating objects. Specific file handlers (e.g., for Shapefiles, GeoJSON, etc.) should inherit from this class and implement the required abstract methods. Attributes ---------- driver : str The driver used for handling the file, which must be defined in subclasses. extension : str The file extension associated with the handler, which must be defined in subclasses. keep_z : bool Whether the object created from the file keeps the Z coordinate of 3-D geometries. False by default; set by the factory from ``read_file``. Methods ------- check_path() Checks if the file path exists and is a file. load_data() Abstract method to load data from the geospatial file. validate_file() Abstract method to validate the file according to specific handler requirements. create_obj() Abstract method to create and return an object representing the file's data. """ driver: str = "" # To be implemented by each subclass extension: str = "" # To be implemented by each subclass keep_z: bool = False # Set by GeoFileFactory before create_obj def __init__(self, path: Path) -> None: """ Initialize the GeoFileHandler with a file path. Parameters ---------- path : Path The path to the geospatial file. Raises ------ FileNotFoundError If the file does not exist or is not a file. """ self.path = path self.check_path()
[docs] def check_path(self) -> None: """ Check if the file path exists and is a file. This method verifies whether the provided path exists and points to a valid file. If the file does not exist, it raises a `FileNotFoundError`. Raises ------ FileNotFoundError If the file does not exist or is not a file. Examples -------- >>> handler = GeoFileHandler(path=Path("/path/to/file")) >>> handler.check_path() # Raises an error if the file is invalid """ if not self.path.exists() or not self.path.is_file(): raise FileNotFoundError(f"The file does not exist: {self.path.as_posix()}")
[docs] def create_geometry_instance(self, data: gpd.GeoDataFrame, driver: str) -> Polygons | Lines | Points | Empty: """ Create a geometry instance (Polygons, Lines, Points, or Empty) based on the most common geometry type in the GeoDataFrame. Data read without a CRS is assumed to be WGS84 when its coordinates are lon/lat, and rejected otherwise (see ``assume_wgs84_if_lonlat``). Parameters ---------- data : GeoDataFrame The GeoDataFrame containing geometries. driver : str The driver type (e.g., 'GPKG', 'ESRI Shapefile'). Returns ------- BaseShapes An instance of one of the geometry classes (Polygons, Lines, Points, or Empty). Raises ------ ValueError If the data has no CRS and its coordinates are not lon/lat. """ data = assume_wgs84_if_lonlat(data, self.path.as_posix()) common_type = get_most_common_geometry_type(data) match common_type: case "Polygon" | "MultiPolygon": return Polygons(data, path=self.path, crs=data.crs, driver=driver, keep_z=self.keep_z) case "LineString" | "MultiLineString": return Lines(data, path=self.path, crs=data.crs, driver=driver, keep_z=self.keep_z) case "Point" | "MultiPoint": return Points(data, path=self.path, crs=data.crs, driver=driver, keep_z=self.keep_z) case _: return Empty(data, path=self.path, crs=data.crs, driver=driver, keep_z=self.keep_z)
[docs] @abstractmethod def load_data(self) -> None: """ Abstract method to load data from the geospatial file. This method must be implemented by subclasses to load the contents of the file into memory or a data structure suitable for further operations. Raises ------ NotImplementedError If the method is not implemented by a subclass. """ pass
[docs] @abstractmethod def validate_file(self) -> None: """ Abstract method to validate the file according to specific handler requirements. This method must be implemented by subclasses to perform custom validation (e.g., checking the file's format or structure). Raises ------ NotImplementedError If the method is not implemented by a subclass. """ pass
[docs] @abstractmethod def create_obj(self, **kwargs: Any) -> BaseShapes: """ Abstract method to create and return an object representing the file's data. This method must be implemented by subclasses to create and return an appropriate object that represents the file's data, such as a GeoDataFrame or another custom object. Returns ------- object An object representing the data in the geospatial file. Raises ------ NotImplementedError If the method is not implemented by a subclass. """ pass