Troi & Config¶
The Troi (Time and Region Of Interest) is the immutable, content-addressed object that flows
through every stage of the pipeline. A Troi describes a region
(bounding box, EPSG:4326) and a time window; every stage derives its
input and output paths from it.
The Config controls global behavior — where outputs go, where caches
live, and credentials for the SILO and SLGA stages. A Config is
attached to every Troi (defaulting to the one loaded from
~/.config/Troi.json, or built-in defaults if that file is
absent).
Key ideas¶
- Content addressing. If you don't pass a
stub, it's computed assha256(bbox + start + end)so two queries with identical inputs share outputs on disk. - Region identity. Bounding boxes are snapped to ~100 m precision
(3 decimal places) before hashing into
bbox_hash, so near-identical bboxes share one region identity.bbox_hashkeys the registry and the PaddockTS region × time cache; the data stores dedup at their own finer granularity (grid chunks / points) regardless. - Registry. Every constructed
Troiis recorded in{config.out_dir}/queries.jsonunder itsbbox_hash. Reusing astubfor a different(bbox, start, end)raisesValueError. - Derived paths live elsewhere.
Troicarries no storage layout: input data (Sentinel-2, climate, terrain, soils) is cached by the machine-wide stores, and PaddockTS's own artifacts (fractional cover, SAM paddocks, …) live onPaddockTS.paths.Paths, keyed by the Troi's identity hashes.
The diagram below shows how the four inputs fan out. Input data never
lives per-troi: the machine-wide stores (pysentinel2, pysilo,
pyozwald, pycopdem, pyslga) cache it once per machine at chunk/point
granularity. bbox_hash × time_hash keys only PaddockTS's own derived
artifacts, and the human-readable stub names the per-troi scratch
and final-output directories.
flowchart TD
Q["<b>Troi</b><br/>bbox · start · end · stub"]
Q -->|"snap to ~100 m,<br/>then sha256"| BH["<b>bbox_hash</b>"]
Q -->|"sha256(start+end)"| TH["<b>time_hash</b>"]
Q -->|"verbatim"| ST["<b>stub</b>"]
Q -.->|"windows served on demand,<br/>cached machine-wide"| STORES["pysentinel2 · pysilo · pyozwald ·<br/>pycopdem · pyslga<br/><i>shared data stores</i>"]
BH --> QD["{tmp_dir}/paddockts/{bbox_hash}/{time_hash}/<br/><i>PaddockTS region × time cache</i>"]
TH --> QD
QD --> ART["fractional_cover.zarr · preseg.tif<br/>sam_mask.tif · sam_raw.gpkg · sam_paddocks.gpkg"]
ST --> TMP["{tmp_dir}/{stub}/<br/><i>per-troi intermediates (time series)</i>"]
ST --> OUT["{out_dir}/{stub}/<br/><i>final outputs + queries.json</i>"]
classDef default color:#000;
Construct a Troi¶
From a bounding box¶
from datetime import date
from troi.troi import Troi
q = Troi(
bbox=[148.36265, -33.52606, 148.38265, -33.50606], # [W, S, E, N]
start=date(2020, 1, 1),
end=date(2021, 12, 31),
stub="my_first_run",
)
print(q.out_dir)
# ~/Documents/Troi-Outputs/my_first_run
From a centre point + buffer in km¶
q = Troi.from_lat_lon(
lat=-35.098087,
lon=148.929983,
buffer_km=2.0, # ~ 4 km × 4 km AOI
start=date(2025, 1, 1),
end=date(2025, 6, 30),
stub="point_buffered",
)
From an existing paddocks file¶
q = Troi.build_from_paddocks(
paddocks_filepath="/path/to/paddocks.gpkg",
start=date(2024, 1, 1),
end=date(2024, 12, 31),
stub="my_farm",
label_col="paddock_name",
)
Reads the file (.gpkg, .shp, .geojson, or .json), reprojects to
EPSG:4326 if needed, takes the envelope of all features as the bbox,
and optionally renames label_col → "paddock" for downstream
compatibility.
Custom config¶
The default Config reads from ~/.config/Troi.json if present,
otherwise uses ~/Documents/Troi-Outputs and
~/Downloads/Troi-Tmp. Override per-Troi by passing a Config
explicitly:
from troi.config import Config
from troi.troi import Troi
cfg = Config(
out_dir="/data/paddockts/outputs",
tmp_dir="/data/paddockts/tmp",
email="you@example.org", # required for SILO
tern_api_key="<your-tern-key>", # required for SLGA
)
q = Troi(
bbox=[148.36265, -33.52606, 148.38265, -33.50606],
start=date(2020, 1, 1),
end=date(2021, 12, 31),
stub="my_run",
config=cfg,
)
Troi reference¶
The generic core (bbox, dates, stub, cache directories, registry,
alternate constructors) lives in the shared
troi
package; troi.troi.Troi subclasses it to add the
Sentinel-2 / SAM output paths.
troi.troi ¶
Troi ¶
A request to run a pipeline over a region and time range.
The identity layer of the Borevitz Lab ecosystem: this region, these
dates, this name. Immutable and hashable — two queries with the same
inputs are the same troi and share every cached artefact downstream.
Storage layout is deliberately NOT this class's concern: packages
compose with a Troi (no inheritance) and derive their own cache
locations in a per-package Paths class, typically from
bbox_hash / time_hash.
Attributes:
| Name | Type | Description |
|---|---|---|
bbox |
list[float]
|
Bounding box |
start |
date
|
Inclusive start date. |
end |
date
|
Inclusive end date. |
stub |
str
|
Short identifier used in every output filename. Defaults to
a SHA-256 hash of |
tmp_dir |
str
|
Per-troi intermediates directory
( |
out_dir |
str
|
Per-troi final-outputs directory
( |
bbox_hash |
str
|
Region identity — bbox snapped to ~100 m, then SHA-256. Keys the persistent registry. |
time_hash |
str
|
Date-range identity (SHA-256 of |
centre_lon |
float
|
Centre longitude of |
centre_lat |
float
|
Centre latitude of |
Example
register ¶
Insert this troi into the persistent registry indexed by bbox_hash.
Idempotent on exact match — re-registering an identical troi updates
its last_run_at instead of appending a duplicate.
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
from_lat_lon
classmethod
¶
from_lat_lon(lat: float, lon: float, buffer_km: float, start: date, end: date, stub: str = None, config: Config = default_config)
Build a Troi from a centre point and a square buffer in kilometres.
Convenience constructor for users who think in "X km around a point" rather than bounding-box corners. The km-to-degrees conversion is approximate (treats the Earth locally as a sphere) which is fine for the buffer sizes typical of paddock-scale work (≲50 km).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
lat
|
float
|
Centre latitude in decimal degrees (EPSG:4326). |
required |
lon
|
float
|
Centre longitude in decimal degrees (EPSG:4326). |
required |
buffer_km
|
float
|
Half-side of the square buffer, in kilometres. The
resulting bbox spans |
required |
start
|
date
|
Inclusive start date. |
required |
end
|
date
|
Inclusive end date. |
required |
stub
|
str
|
Optional human-readable stub identifier. If omitted, a SHA-256 hash of the inputs is used. |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
Troi |
Instance with |
build_from_paddocks
classmethod
¶
build_from_paddocks(paddocks_filepath: str, start: date, end: date, stub: str = None, label_col: str = None, geometry_col: str = None, crs: str = 'EPSG:4326', config=default_config)
Build a Troi from a paddocks file, enveloping all geometries into a bbox.
Reads paddock geometries from a GeoPackage, Shapefile, or GeoJSON and computes a bounding box that contains all features.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
paddocks_filepath
|
str
|
Path to the paddocks file. Supported formats:
- GeoPackage ( |
required |
start
|
date
|
Inclusive start date. |
required |
end
|
date
|
Inclusive end date. |
required |
stub
|
str
|
Optional human-readable stub identifier. If omitted, a SHA-256 hash of the inputs is used. |
None
|
label_col
|
str
|
Column name containing paddock labels/names (e.g.
|
None
|
geometry_col
|
str
|
Column name containing geometry data. For standard geo formats this is auto-detected. |
None
|
crs
|
str
|
Coordinate reference system to assume if the file has none.
Default |
'EPSG:4326'
|
Returns:
| Name | Type | Description |
|---|---|---|
Troi |
Instance with |
|
|
encompassing all paddock geometries. |