Skip to content

Chipping Strategies

geoembed.chipping.strategies.base

Abstract base classes for chipping strategies.

ChipResult(metadata, chip_path, parent_id) dataclass

Standardised output from a local chipping operation.

Immutable data carrier linking generated metadata to its physical file.

Attributes:

Name Type Description
metadata dict[str, Any]

STAC-compliant metadata dictionary for the chip.

chip_path Path

Absolute file path to the generated GeoTIFF chip.

parent_id str

ID of the source image this chip was derived from.

ChipperStrategy

Bases: ABC

Strategy interface for local chipping — defines HOW to chip a raster.

Implementations determine the tiling algorithm (e.g., sliding window, grid). Used with a JobExecutor for parallel local execution.

process_item(item) abstractmethod

Process a single source item to generate image chips.

Parameters:

Name Type Description Default
item Any

A STAC item or similar metadata object representing the source raster.

required

Returns:

Type Description
list[ChipResult]

List of ChipResults containing metadata and paths for every generated chip.

Source code in src/geoembed/chipping/strategies/base.py
@abstractmethod
def process_item(self, item: Any) -> list[ChipResult]:
    """
    Process a single source item to generate image chips.

    Args:
        item: A STAC item or similar metadata object representing the source raster.

    Returns:
        List of ChipResults containing metadata and paths for every generated chip.
    """
    ...

SparkChipperStrategy

Bases: ABC

Strategy interface for Spark-based chipping — generates chip metadata from a parent image.

Unlike ChipperStrategy (which produces physical files), this generates only virtual chip metadata (offsets, geometries) for later materialisation or direct read.

generate_chips(parent_path) abstractmethod

Generator yielding metadata dictionaries for each chip.

Parameters:

Name Type Description Default
parent_path str

Path to the source COG (e.g., Unity Catalog Volume path).

required

Yields:

Type Description
dict

dict with keys: chip_id, parent_id, parent_path, col_off, row_off,

dict

width, height, geometry_wkb, minx, miny, maxx, maxy.

Source code in src/geoembed/chipping/strategies/base.py
@abstractmethod
def generate_chips(self, parent_path: str) -> Iterator[dict]:
    """
    Generator yielding metadata dictionaries for each chip.

    Args:
        parent_path: Path to the source COG (e.g., Unity Catalog Volume path).

    Yields:
        dict with keys: chip_id, parent_id, parent_path, col_off, row_off,
        width, height, geometry_wkb, minx, miny, maxx, maxy.
    """
    ...

geoembed.chipping.strategies.sliding_window

Sliding window chipping strategy — pixel-based tiling with optimal overlap.

SlidingWindowStrategy(chip_size=224)

Bases: SparkChipperStrategy

Implements pixel-based sliding window chipping.

Uses StrideService to calculate optimal overlap that ensures the image is fully covered without gaps, adjusting the stride dynamically so the last chip aligns perfectly with the image edge.

For 12.5cm imagery with chip_size=224: - Each chip = 28m x 28m physical footprint - Typical overlap = ~8 pixels (1m) for 8000x8000 parent images - Grid = 37x37 = 1,369 chips per parent image

Parameters:

Name Type Description Default
chip_size int

Square chip dimension in pixels.

224
Source code in src/geoembed/chipping/strategies/sliding_window.py
def __init__(self, chip_size: int = 224):
    """
    Args:
        chip_size: Square chip dimension in pixels.
    """
    self.chip_size = chip_size

generate_chips(parent_path)

Generate virtual chip metadata for a parent image.

Opens the COG to read dimensions and transform, then calculates all chip positions using optimal overlap. Only reads headers (no pixels).

Parameters:

Name Type Description Default
parent_path str

Path to the parent COG.

required

Yields:

Type Description
dict

Metadata dict per chip with geometry (WKB), offsets, and bounds.

Source code in src/geoembed/chipping/strategies/sliding_window.py
def generate_chips(self, parent_path: str) -> Iterator[dict]:
    """
    Generate virtual chip metadata for a parent image.

    Opens the COG to read dimensions and transform, then calculates all
    chip positions using optimal overlap. Only reads headers (no pixels).

    Args:
        parent_path: Path to the parent COG.

    Yields:
        Metadata dict per chip with geometry (WKB), offsets, and bounds.
    """
    parent_path = self._clean_path(parent_path)

    with rasterio.open(parent_path) as src:
        w, h = src.width, src.height
        transform = src.transform
        parent_name = os.path.splitext(os.path.basename(parent_path))[0]

        overlap_x, overlap_y = StrideService.calculate_optimal_overlap(
            (w, h), (self.chip_size, self.chip_size)
        )

        sx = max(1, self.chip_size - overlap_x)
        sy = max(1, self.chip_size - overlap_y)

        row_starts = range(0, h - self.chip_size + 1, sy)
        col_starts = range(0, w - self.chip_size + 1, sx)

        idx = 0
        for r in row_starts:
            for c in col_starts:
                window = Window(c, r, self.chip_size, self.chip_size)  # pyrefly: ignore
                minx, miny, maxx, maxy = rasterio.windows.bounds(window, transform)

                yield {
                    "chip_id": f"{parent_name}_{idx:06d}",
                    "parent_id": parent_name,
                    "parent_path": parent_path,
                    "col_off": int(c),
                    "row_off": int(r),
                    "width": self.chip_size,
                    "height": self.chip_size,
                    "geometry_wkb": dumps(box(minx, miny, maxx, maxy)),
                    "minx": float(minx),
                    "miny": float(miny),
                    "maxx": float(maxx),
                    "maxy": float(maxy),
                }
                idx += 1

geoembed.chipping.strategies.bng_grid

British National Grid (BNG) chipping strategy — aligns chips to the OSGB grid.

BNGGridStrategy(resolution)

Bases: SparkChipperStrategy

Implements British National Grid (BNG) based chipping.

Instead of arbitrary pixel windows, this strategy aligns chips to the OSGB grid system (e.g., 50m grid squares). This ensures chips nest cleanly within the 1km BNG grid used by the parent imagery.

Useful when: - Working with UK Ordnance Survey / BNG-aligned imagery - Downstream analysis requires alignment to standard grid references - Comparing with other BNG-referenced datasets (e.g., AEF embeddings)

Parameters:

Name Type Description Default
resolution str | int

BNG resolution string (e.g., "50m", "10m") or integer metres.

required
Source code in src/geoembed/chipping/strategies/bng_grid.py
def __init__(self, resolution: str | int):
    """
    Args:
        resolution: BNG resolution string (e.g., "50m", "10m") or integer metres.
    """
    self.resolution = resolution

generate_chips(parent_path)

Generate chips aligned to the BNG grid.

Opens the COG, determines its BNG reference from the filename, enumerates child grid squares at the target resolution, and yields metadata for each.

Parameters:

Name Type Description Default
parent_path str

Path to the parent COG (filename must be a valid BNG reference).

required

Yields:

Type Description
dict

Metadata dict per chip with BNG-aligned geometry.

Source code in src/geoembed/chipping/strategies/bng_grid.py
def generate_chips(self, parent_path: str) -> Iterator[dict]:
    """
    Generate chips aligned to the BNG grid.

    Opens the COG, determines its BNG reference from the filename,
    enumerates child grid squares at the target resolution, and yields
    metadata for each.

    Args:
        parent_path: Path to the parent COG (filename must be a valid BNG reference).

    Yields:
        Metadata dict per chip with BNG-aligned geometry.
    """
    parent_name = os.path.splitext(os.path.basename(parent_path))[0]

    with rasterio.open(parent_path) as src:
        parent_ref = BNGReference(bng_ref_string=parent_name)
        children = parent_ref.bng_to_children(resolution=self.resolution)
        parent_bounds_geom = box(*src.bounds)

        for child in children:
            tile_geom = shape(child.__geo_interface__["geometry"])
            if not tile_geom.intersects(parent_bounds_geom):
                continue

            geom_to_read = tile_geom.intersection(parent_bounds_geom)
            window = rasterio.windows.from_bounds(*geom_to_read.bounds, transform=src.transform)

            yield {
                "chip_id": child.bng_ref_compact,
                "parent_id": parent_name,
                "parent_path": parent_path,
                "col_off": int(window.col_off),
                "row_off": int(window.row_off),
                "width": int(window.width),
                "height": int(window.height),
                "geometry_wkb": dumps(tile_geom),
                "minx": float(tile_geom.bounds[0]),
                "miny": float(tile_geom.bounds[1]),
                "maxx": float(tile_geom.bounds[2]),
                "maxy": float(tile_geom.bounds[3]),
            }