Skip to content

Engines

The engines module provides delineation engines for agricultural field boundary detection. Each engine wraps a different model or approach for extracting field boundary polygons.

engines

Delineation engines for agricultural field boundary detection.

Each engine wraps a different model or approach for extracting field boundary polygons from satellite imagery.

DelineationEngine

Bases: ABC

Abstract base class for delineation engines.

Subclasses must implement :meth:delineate to perform field boundary extraction from a raster file.

Source code in agribound/engines/base.py
class DelineationEngine(ABC):
    """Abstract base class for delineation engines.

    Subclasses must implement :meth:`delineate` to perform field boundary
    extraction from a raster file.
    """

    name: str = "base"
    supported_sources: list[str] = []
    requires_bands: list[str] = []

    @abstractmethod
    def delineate(self, raster_path: str, config: AgriboundConfig) -> gpd.GeoDataFrame:
        """Run field boundary delineation on a raster file.

        Parameters
        ----------
        raster_path : str
            Path to the input GeoTIFF (composite or local file).
        config : AgriboundConfig
            Pipeline configuration.

        Returns
        -------
        geopandas.GeoDataFrame
            Field boundary polygons with at minimum a ``geometry`` column.
        """

    def validate_input(self, raster_path: str, config: AgriboundConfig) -> None:
        """Validate that the input raster is compatible with this engine.

        Parameters
        ----------
        raster_path : str
            Path to the input raster.
        config : AgriboundConfig
            Pipeline configuration.

        Raises
        ------
        ValueError
            If the input is incompatible.
        """
        from agribound.io.raster import get_raster_info

        info = get_raster_info(raster_path)
        required_bands = len(self.requires_bands)
        if required_bands > 0 and info.count < required_bands:
            raise ValueError(
                f"Engine {self.name!r} requires at least {required_bands} bands "
                f"({self.requires_bands}), but the raster has {info.count} bands."
            )

delineate abstractmethod

delineate(raster_path: str, config: AgriboundConfig) -> gpd.GeoDataFrame

Run field boundary delineation on a raster file.

Parameters:

Name Type Description Default
raster_path str

Path to the input GeoTIFF (composite or local file).

required
config AgriboundConfig

Pipeline configuration.

required

Returns:

Type Description
GeoDataFrame

Field boundary polygons with at minimum a geometry column.

Source code in agribound/engines/base.py
@abstractmethod
def delineate(self, raster_path: str, config: AgriboundConfig) -> gpd.GeoDataFrame:
    """Run field boundary delineation on a raster file.

    Parameters
    ----------
    raster_path : str
        Path to the input GeoTIFF (composite or local file).
    config : AgriboundConfig
        Pipeline configuration.

    Returns
    -------
    geopandas.GeoDataFrame
        Field boundary polygons with at minimum a ``geometry`` column.
    """

validate_input

validate_input(raster_path: str, config: AgriboundConfig) -> None

Validate that the input raster is compatible with this engine.

Parameters:

Name Type Description Default
raster_path str

Path to the input raster.

required
config AgriboundConfig

Pipeline configuration.

required

Raises:

Type Description
ValueError

If the input is incompatible.

Source code in agribound/engines/base.py
def validate_input(self, raster_path: str, config: AgriboundConfig) -> None:
    """Validate that the input raster is compatible with this engine.

    Parameters
    ----------
    raster_path : str
        Path to the input raster.
    config : AgriboundConfig
        Pipeline configuration.

    Raises
    ------
    ValueError
        If the input is incompatible.
    """
    from agribound.io.raster import get_raster_info

    info = get_raster_info(raster_path)
    required_bands = len(self.requires_bands)
    if required_bands > 0 and info.count < required_bands:
        raise ValueError(
            f"Engine {self.name!r} requires at least {required_bands} bands "
            f"({self.requires_bands}), but the raster has {info.count} bands."
        )

get_engine

get_engine(engine_name: str) -> DelineationEngine

Factory function to get a delineation engine by name.

Parameters:

Name Type Description Default
engine_name str

Engine name (e.g. "delineate-anything", "ftw").

required

Returns:

Type Description
DelineationEngine

Engine instance.

Raises:

Type Description
ValueError

If the engine name is not recognized.

Source code in agribound/engines/base.py
def get_engine(engine_name: str) -> DelineationEngine:
    """Factory function to get a delineation engine by name.

    Parameters
    ----------
    engine_name : str
        Engine name (e.g. ``"delineate-anything"``, ``"ftw"``).

    Returns
    -------
    DelineationEngine
        Engine instance.

    Raises
    ------
    ValueError
        If the engine name is not recognized.
    """
    engine_name = engine_name.lower().strip()

    if engine_name not in ENGINE_REGISTRY:
        raise ValueError(
            f"Unknown engine {engine_name!r}. Available: {list(ENGINE_REGISTRY.keys())}"
        )

    if engine_name == "delineate-anything":
        from agribound.engines.delineate_anything import DelineateAnythingEngine

        return DelineateAnythingEngine()
    elif engine_name == "ftw":
        from agribound.engines.ftw import FTWEngine

        return FTWEngine()
    elif engine_name == "geoai":
        from agribound.engines.geoai_field import GeoAIEngine

        return GeoAIEngine()
    elif engine_name == "prithvi":
        from agribound.engines.prithvi import PrithviEngine

        return PrithviEngine()
    elif engine_name == "dinov3":
        from agribound.engines.dinov3 import DINOv3Engine

        return DINOv3Engine()
    elif engine_name == "embedding":
        from agribound.engines.embedding import EmbeddingEngine

        return EmbeddingEngine()
    elif engine_name == "ensemble":
        from agribound.engines.ensemble import EnsembleEngine

        return EnsembleEngine()
    else:
        raise ValueError(f"Engine {engine_name!r} is not implemented.")

list_engines

list_engines() -> dict[str, dict[str, Any]]

List all available delineation engines and their metadata.

Returns:

Type Description
dict[str, dict]

Dictionary mapping engine names to their metadata.

Examples:

>>> from agribound import list_engines
>>> engines = list_engines()
>>> for name, info in engines.items():
...     print(f"{name}: {info['approach']}")
Source code in agribound/engines/base.py
def list_engines() -> dict[str, dict[str, Any]]:
    """List all available delineation engines and their metadata.

    Returns
    -------
    dict[str, dict]
        Dictionary mapping engine names to their metadata.

    Examples
    --------
    >>> from agribound import list_engines
    >>> engines = list_engines()
    >>> for name, info in engines.items():
    ...     print(f"{name}: {info['approach']}")
    """
    return dict(ENGINE_REGISTRY)