core_lens.base.entity#

Base entity contract for core_lens.

All entities — built-in or plugin — must subclass BaseEntity and implement its abstract interface. Concrete implementations live in core_lens.entities.* (built-ins) or in third-party packages (plugins).

Plugin authors import from the public surface:

from core_lens.base import BaseEntity

Exceptions#

EntityValidationError

Raised when an entity fails validation at AoI.register() time.

Classes#

BaseEntity

Abstract base class for every entity in the core_lens plugin system.

Module Contents#

class core_lens.base.entity.BaseEntity(data_root: str | pathlib.Path | None = None, storage_options: dict[str, Any] | None = None)#

Bases: abc.ABC

Abstract base class for every entity in the core_lens plugin system.

An entity represents a geospatial primitive (e.g. microwatershed, village, district) backed by one or more Parquet/GeoParquet files. Entities are descriptors — they carry path and schema metadata but hold no per-row state themselves. Row-level data lives in View and Result.

Subclasses must implement:

  • key_cols — column(s) that uniquely identify one entity instance

  • geometry_col — geometry column name in the static GeoParquet file

  • static_path — path to the static GeoParquet file (mandatory).

    May be relative; resolved against data_root when the entity is instantiated by AoI.

Subclasses may override:

where, spatial_filter, spatial_join, and schema_profile are all implemented on this base class. Subclasses only need to declare paths and keys.

Plugin example:

from core_lens.base import BaseEntity

class ForestEntity(BaseEntity):
    key_cols     = [\"forest_patch_id\"]
    geometry_col = \"geometry\"
    static_path  = \"forest/static.geoparquet\"  # relative to AoI data_root
    annual_path  = \"forest/annual.parquet\"

AoI.register(ForestEntity)

Validation rules enforced at AoI() instantiation time (relative paths) or at AoI.register() time (absolute paths):

  1. static_path exists and is readable.

  2. key_cols are present and unique in the static file.

  3. geometry_col is present and contains valid geometries.

  4. annual_path and sub_annual_path exist if declared.

Any failure raises EntityValidationError.

property key_cols: list[str]#
Abstractmethod:

Columns that uniquely identify one instance of this entity.

For built-in entities this is always a single-element list (e.g. [\"mws_id\"]), but the contract allows composite keys for plugins.

Returns:

A list of column name strings present in the static file.

Return type:

list[str]

property geometry_col: str#
Abstractmethod:

Name of the geometry column in the static GeoParquet file.

The column must contain a geometry type understood by GeoPandas (WKB bytes, WKT string, or a native geometry column).

Returns:

The column name as a string.

Return type:

str

property static_path: str#
Abstractmethod:

Absolute filesystem path to the static GeoParquet file.

The path must be absolute. If a relative path is provided it is resolved against the current working directory at first use. A FileNotFoundError is raised if the file does not exist.

Returns:

A path string.

Return type:

str

property annual_path: str | None#

Path to the annual time-series Parquet file, or None.

Override in subclasses that carry annual temporal data. If declared, the file must exist at AoI.register() time or EntityValidationError is raised.

Returns:

A path string, or None if the entity has no annual data.

Return type:

str | None

property sub_annual_path: str | None#

Path to the sub_annual time-series Parquet file, or None.

Override in subclasses that carry sub_annual temporal data. If declared, the file must exist at AoI.register() time or EntityValidationError is raised.

Returns:

A path string, or None if the entity has no sub_annual data.

Return type:

str | None

property schema_profile: core_lens.schema.profile.SchemaProfile#

Validated schema descriptor for this entity’s data files.

Auto-detected from Parquet file metadata on first access. The result is cached at two levels:

  1. Instance level_schema_profile attribute set on self so that repeated property access within the same instance is a bare attribute lookup with no dict overhead.

  2. Process level_cached_detect() (backed by functools.cache) keyed on the resolved paths and entity configuration. Different AoI() calls that point at the same data directory share one SchemaProfile object, avoiding redundant collect_schema() I/O.

Override in subclasses to provide an explicit profile instead of relying on detection.

Returns:

A fully-validated SchemaProfile.

Return type:

SchemaProfile

property geometry_lazy: polars.LazyFrame#

A cached LazyFrame representing the geometry of this entity.

Contains only the key columns and the geometry column. Used to avoid repeatedly scanning and selecting geometries in downstream operations.

where(**kwargs: Any) core_lens.base.view.View#

Return a lazy View filtered by attributes.

Each keyword argument is interpreted attribute-first: if the kwarg key exists as a column in the static file the filter is applied directly. If a kwarg key does not exist as a column it is resolved as a registered entity name and the matching entity’s geometry is used for a spatial filter (e.g. district="Shimla" finds all MWS whose centroid falls within Shimla district).

Multiple attribute kwargs are AND-ed. Multiple spatial-entity kwargs are AND-ed via sequential spatial filters.

Parameters:

**kwargs – Column–value pairs to filter on. Unknown column names are resolved as entity-name lookups.

Returns:

A lazy View with resolved key pairs.

Return type:

View

Raises:

ValueError – If a kwarg cannot be resolved as either an attribute column or a registered entity name.

spatial_filter(geometry: shapely.Geometry | None = None, bbox: tuple[float, float, float, float] | None = None, relationship: str = 'centroid', threshold: float = 0.5) core_lens.base.view.View#

Return a lazy View filtered by geometry.

Uses the in-memory bbox index for a fast rectangular pre-filter, then refines with a Shapely STRtree exact-relationship check.

Parameters:
  • geometry (shapely.Geometry | None, optional) – A Shapely geometry representing the spatial extent.

  • bbox (tuple[float, float, float, float] | None, optional) – Bounding box as (minx, miny, maxx, maxy) in WGS-84. Converted to a shapely.geometry.box internally.

  • relationship (str, optional) –

    Spatial relationship mode.

    • "centroid" (default) — entity centroid must lie within the geometry.

    • "area" — intersection area / entity area must exceed threshold.

  • threshold (float, optional) – Area coverage threshold for "area" mode (0–1). Default 0.5.

Returns:

A lazy View scoped to the given spatial extent.

Return type:

View

Raises:

ValueError – If neither geometry nor bbox is provided.

spatial_join(other: BaseEntity, agg: dict[str, str]) core_lens.base.view.View#

Return a lazy View with a cross-entity join pending.

The join is recorded in the View’s join_spec and computed only at materialisation time (.static, .annual, or .sub_annual). Joined columns are namespaced as {entity_name}_{column_name}.

Parameters:
  • other (BaseEntity) – The secondary BaseEntity whose columns will be joined and aggregated onto self.

  • agg (dict[str, str]) – Mapping of {column: aggregation} specifying which columns from other to bring in and how to aggregate them. Valid aggregation strings are \"area\", \"count\", \"mean\", \"sum\", \"min\", and \"max\".

Returns:

A lazy View with the join spec recorded for deferred execution.

Return type:

View

exception core_lens.base.entity.EntityValidationError#

Bases: Exception

Raised when an entity fails validation at AoI.register() time.

The message will describe exactly which check failed (missing file, absent key column, invalid geometry column, etc.) to give plugin authors actionable feedback.