Source code for shapelib.core.handlers.geopackage_handler
from pathlib import Path
from typing import Any
import fiona
import geopandas as gpd
from geopandas import GeoDataFrame
from shapelib.core.geometries.base import BaseShapes
from shapelib.core.handlers.geo_file_handler import GeoFileHandler
from shapelib.factories.registry import register_handler
[docs]
@register_handler(file_extension=".gpkg")
class GeopackageHandler(GeoFileHandler):
"""
A handler for GeoPackage (.gpkg) format.
This class provides functionality for reading and validating GeoPackage files
using the GeoPandas library and GDAL for additional checks.
Attributes
----------
driver : str
The GDAL driver used for handling GeoPackage files ("GPKG").
extension : str
The file extension associated with GeoPackage files (".gpkg").
Methods
-------
__init__(path)
Initializes the handler with a given GeoPackage path.
validate_file()
Validates the GeoPackage to ensure it exists, is non-empty, and can be
opened by GDAL.
load_data(**kwargs) -> GeoDataFrame
Loads the GeoPackage as a GeoDataFrame.
create_obj(**kwargs) -> BaseShapes
Creates and returns an appropriate geometry object based on the contents
of the GeoPackage.
"""
driver: str = "GPKG"
extension: str = ".gpkg"
def __init__(self, path: Path) -> None:
"""
Initialize the GeopackageHandler with a given .gpkg file path.
Parameters
----------
path : Path
The path to the GeoPackage (.gpkg) file to be processed.
Raises
------
FileNotFoundError
If the .gpkg file does not exist or is not a valid file.
ValueError
If the file is empty or GDAL cannot recognize it as a GeoPackage.
"""
super().__init__(path)
self.validate_file()
[docs]
def validate_file(self) -> None:
"""
Validate the GeoPackage file to ensure it exists, is non-empty, and
can be opened by GDAL.
This method checks whether the file is empty based on file size and
then tries to open the file with GDAL to confirm it is a valid
GeoPackage.
Raises
------
ValueError
If the file is empty or cannot be recognized as a GeoPackage by GDAL.
GDALException
If GDAL encounters an error opening the file.
"""
try:
layers = fiona.listlayers(self.path)
if not layers:
print(f"GeoPackage '{self.path.name}' has no layers.")
except Exception as e:
raise ValueError(
f"Could not open the GeoPackage: {self.path.as_posix()}\n"
f"Ensure the file is a valid .gpkg and not corrupted. ({e})"
) from e
[docs]
def load_data(self, **kwargs: Any) -> GeoDataFrame:
"""
Load the GeoPackage using GeoPandas.
Parameters
----------
**kwargs : dict of str, Any
Additional keyword arguments to pass to :func:`geopandas.read_file`.
For example, `layer="layer_name"` if the GeoPackage has multiple layers.
Returns
-------
GeoDataFrame
The loaded GeoDataFrame from the GeoPackage.
"""
return gpd.read_file(self.path, **kwargs)
[docs]
def create_obj(self, **kwargs: Any) -> BaseShapes:
"""
Create an object representing the GeoPackage's geometries based on the
most common geometry type.
This method uses ``create_appropriate_obj`` from the base class to
generate Polygons, Lines, Points, or an Empty geometry object.
Parameters
----------
**kwargs : dict of str, Any
Additional keyword arguments passed to :meth:`load_data`.
Returns
-------
BaseShapes
An instance of one of the geometry classes (Polygons, Lines,
Points, or Empty), depending on the most common geometry type
in the GeoPackage's data.
"""
data = self.load_data(**kwargs)
return self.create_geometry_instance(data=data, driver=self.driver)