Source code for blocksnet.models.geodataframe

from abc import ABC
from typing import Generic, TypeVar

import geopandas as gpd
from pydantic import BaseModel, ConfigDict
from shapely import Geometry


T = TypeVar("T")


[docs]class BaseRow(BaseModel, ABC): """Provides an abstract for data validation in GeoDataFrame. Generics must be inherited from this base class. The inherited class also can be configured to provide default column values to avoid None and NaN""" model_config = ConfigDict(arbitrary_types_allowed=True) geometry: Geometry index: int """Index can be override but should not be set by default"""
[docs]class GeoDataFrame(gpd.GeoDataFrame, BaseModel, Generic[T]): """Basically a geopandas GeoDataFrame, but with Generic[T]. Provide a BaseRow inherited class to automatically validate data on init.""" @property def generic(self): # pydantic is only needed to access generic class return self.__pydantic_generic_metadata__["args"][0] def __init__(self, data, *args, **kwargs): """_summary_ Parameters ---------- data : _type_ _description_ """ generic_class = self.generic assert issubclass(generic_class, BaseRow), "Generic should be inherited from BaseRow" # if data is not a GeoDataFrame, we create it ourselves if not isinstance(data, gpd.GeoDataFrame): data = gpd.GeoDataFrame(data, *args, **kwargs) # next we create list of dicts from BaseRow inherited class rows: list[dict] = [] for i in data.index: dict = data.loc[i].to_dict() rows.append(generic_class(index=i, **dict).__dict__) super().__init__(rows) # and finally return index to where it belongs if "index" in self.columns: self.index = self["index"] self.drop(columns=["index"], inplace=True) index_name = data.index.name self.index.name = index_name self.set_geometry("geometry", inplace=True) # and also set crs self.crs = kwargs["crs"] if "crs" in kwargs else data.crs