Skip to content

feat: add RasterKind.SEMANTIC and make intake layout-aware - #7

Merged
leiverkus merged 1 commit into
mainfrom
feat/intake-semantic-kind
Jul 26, 2026
Merged

feat: add RasterKind.SEMANTIC and make intake layout-aware#7
leiverkus merged 1 commit into
mainfrom
feat/intake-semantic-kind

Conversation

@leiverkus

Copy link
Copy Markdown
Owner

Closes the intake prerequisite for v0.8 (ROADMAP.md) and the intake-layout open decision (docs/architecture.md).

The bug

Effigies ships a per-pixel class field at odm_semantic/orthophoto_semantic.tif. Point Structura at such a delivery tree today and that raster is tagged ORTHO — the filename contains "ortho", and discover_inputs tested that substring first. It then goes to the 2D segmenter, which reads its class codes (03) as pixel intensities and emits spurious STONE polygons.

Nothing downstream re-checks the kind, so there is no error, no warning, and the output looks plausible. The added pipeline test reproduces it against the old code as assert 4 == 2, with the extra features carrying orthophoto_semantic.tif as their source raster.

The fix

Classification prefers the delivery layout. A raster under odm_orthophoto/, odm_dem/ or odm_semantic/ takes its kind from the directory, because the directory states what the product is — independent of what the file is called. This is what architecture.md said the heuristic should eventually do.

The filename fallback survives for flat or ad-hoc input directories, but "semantic" is now tested before "ortho". That ordering is load-bearing and the comment says so, because the failure it prevents is invisible.

RasterKind.SEMANTIC is added and the field is tagged but not consumed — consuming it as a prior is the rest of v0.8. The explicit branch in pipeline.run documents that rather than letting semantic products fall through the dispatch unmentioned. Tagging it is precisely what keeps it out of the segmenter in the meantime.

classify is split out of discover_inputs, so the rule is testable without a directory walk.

One behaviour change worth naming

Rasters matching neither the layout nor the filename rules are now skipped rather than guessed at. In practice this only affects files that were previously mis-tagged — the old code had no "unknown" outcome for anything containing ortho/dem/dsm/dtm, and skipped everything else already.

Tests

16 new tests (41 → 57). tests/test_intake.py covers the regression, layout-beats-filename, the filename fallback table, skipping unrelated rasters, and deterministic ordering. tests/test_pipeline.py gets the end-to-end guard described above.

The pipeline regression test was run against the pre-fix src/ to confirm it actually fails there — it does, assert 4 == 2.

Verification

Clean venvs on both matrix versions:

3.11 3.12
ruff check . pass pass
mypy src 26 files clean 26 files clean
pytest 57 passed, 2 skipped 57 passed, 2 skipped

Plus a CLI run against a mock delivery tree — dem/dem/ortho/semantic tagged correctly, the sibling orthophoto_semantic.legend.json untouched.

🤖 Generated with Claude Code

Effigies now ships a per-pixel class field at
`odm_semantic/orthophoto_semantic.tif`. Pointed at such a delivery tree,
`discover_inputs` tagged it ORTHO — the filename contains "ortho" and
that substring was tested first — so the class raster went to the 2D
segmenter, which read its class codes as pixel intensities and emitted
spurious STONE polygons. Nothing downstream re-checks the kind, so this
failed silently; the added pipeline test reproduces it as 4 features
where 2 are correct.

Classification now prefers the delivery layout: a raster under
odm_orthophoto/, odm_dem/ or odm_semantic/ takes its kind from the
directory, because the directory states what the product is. The
filename fallback remains for flat input dirs, with "semantic" tested
before "ortho". Rasters matching neither rule are skipped rather than
guessed at, which is a behaviour change only for files that were
previously mis-tagged.

`classify` is split out of `discover_inputs` so the rule is testable
without a directory walk.

SEMANTIC products are tagged and deliberately not consumed — the field
becomes a prior for the vector tracks in v0.8. The explicit branch in
`pipeline.run` documents that, rather than letting them fall through the
dispatch unmentioned.

Closes the intake prerequisite for v0.8 and the intake-layout open
decision. Verified on 3.11 and 3.12: ruff clean, mypy clean on 26 files,
57 passed / 2 skipped.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@leiverkus
leiverkus merged commit 807c67e into main Jul 26, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant