Source code for pysewer.export

# SPDX-FileCopyrightText: 2023 Helmholtz Centre for Environmental Research (UFZ)
# SPDX-License-Identifier: GPL-3.0-only

"""
Since the profile and trench_depth_profile are list of tuples, they cannot be exported 
to a shapefile or GeoPackage directly using the native GeoPandas function (to_file). 
This module contains functions to convert the list of tuples to JSON strings, 
which can then be exported to a shapefile or GeoPackage. 
The option saving the data as a parquet file is added.

"""

import json

import fiona
import geopandas as gpd
from fiona.collection import CRS

from .config.manager import get_config


[docs] def map_dtype_to_fiona(dtype): """Map pandas data type to Fiona/OGR data type.""" if "int" in dtype: return "int" elif "float" in dtype: return "float" elif "bool" in dtype: return "bool" else: return "str"
[docs] def is_list_of_tuples(column): """Check if a pandas Series contains lists of tuples.""" return all( isinstance(item, (list, tuple)) and all(isinstance(sub_item, tuple) for sub_item in item) for item in column )
[docs] def is_list(column): """Check if a pandas Series contains lists.""" return all(isinstance(item, list) for item in column)
[docs] def tuple_list_to_json(tuple_list): """Serialize a list of tuples to a JSON string.""" return json.dumps(tuple_list)
[docs] def generate_schema(gdf: gpd.GeoDataFrame): """ Generate a schema based on the GeoDataFrame. Parameters ---------- gdf : gpd.GeoDataFrame The GeoDataFrame to generate the schema from. Returns ------- dict The schema dictionary. """ schema = { "geometry": gdf.geometry.type.iloc[0], "properties": { col: map_dtype_to_fiona(gdf[col].dtype.name) for col in gdf.columns if col != "geometry" }, } return schema
[docs] def write_gdf_to_gpkg(gdf: gpd.GeoDataFrame, filepath: str, layer: str | None=None, crs: CRS | None=None): """ Write a GeoDataFrame to a GeoPackage (GPKG) file using Fiona, converting lists of tuples to JSON strings. Parameters ---------- gdf : gpd.GeoDataFrame The GeoDataFrame to be written to the GPKG file. filepath : str The file path to the GPKG file. layer: str (optional) The layer name inside the GPKG file. If no layer name is specified the file name is used. crs: CRS (optional) The layer CRS Returns ------- None Notes ----- This function converts any columns with list of tuples to JSON strings before writing to the GPKG file. """ # Convert only columns with list of tuples to JSON strings for col in gdf.columns: if gdf[col].dtype == "object": if is_list_of_tuples(gdf[col]) or is_list(gdf[col]): gdf[col] = gdf[col].apply(tuple_list_to_json) # Define the schema based on the GeoDataFrame schema = generate_schema(gdf) # Open a new GPKG file in write mode with fiona.open(filepath, mode="w", driver="GPKG", schema=schema, layer=layer, crs=crs) as dst: for _, row in gdf.iterrows(): feature = { "geometry": row["geometry"].__geo_interface__, "properties": { col: row[col] for col in gdf.columns if col != "geometry" }, } dst.write(feature)
[docs] def write_gdf_to_shp(gdf: gpd.GeoDataFrame, filepath: str): """ Write a GeoDataFrame to an ESRI Shapefile (SHP) file using Fiona, converting lists of tuples to JSON strings. Parameters ---------- gdf : gpd.GeoDataFrame The GeoDataFrame to be written to the SHP file. filepath : str The file path to save the SHP file. Returns ------- None Notes ----- This function converts any columns in the GeoDataFrame that contain lists of tuples to JSON strings before writing to the SHP file. """ # Convert only columns with list of tuples to JSON strings for col in gdf.columns: if gdf[col].dtype == "object" and is_list_of_tuples(gdf[col]): gdf[col] = gdf[col].apply(tuple_list_to_json) # Define the schema based on the GeoDataFrame schema = generate_schema(gdf) # Open a new SHP file in write mode with fiona.open(filepath, mode="w", driver="ESRI Shapefile", schema=schema) as dst: for _, row in gdf.iterrows(): feature = { "geometry": row["geometry"].__geo_interface__, "properties": { col: row[col] for col in gdf.columns if col != "geometry" }, } try: dst.write(feature) except Exception as e: print(f"Error writing feature to SHP file: {e}")
[docs] def export_sewer_network( gdf: gpd.GeoDataFrame, filepath: str, file_format: str | None = None, ): """ Export a sewer network GeoDataFrame to a file. Parameters ---------- gdf : gpd.GeoDataFrame A GeoDataFrame containing the sewer network. filepath : str The path to the file to which the sewer network should be exported. file_format : str The file format to which the sewer network should be exported. Default is 'gpkg' (GeoPackage). Currently supported formats are 'gpkg' (GeoPackage), 'shp' (ESRI Shapefile) and Geoparquet 'parquet'. Raises ------ ValueError If the file format is not supported. Returns ------- None """ config = get_config() if file_format is None: file_format = config.export.file_format supported_formats = ["gpkg", "shp", "parquet"] if file_format not in supported_formats: raise ValueError(f"File format {file_format} is not supported.") if file_format == "gpkg": write_gdf_to_gpkg(gdf, filepath) elif file_format == "shp": write_gdf_to_shp(gdf, filepath) elif file_format == "parquet": gdf.to_parquet( filepath, index=False ) # index set false to avoid AttributeError: module 'pandas' has no attribute 'Int64Index' print(f"Successfully exported sewer network to {filepath}.")