Annotations

Overview

The nav.annotation subsystem produces the per-image summary-PNG overlay. Every NavModel exposes a to_annotations(context) method that returns a fresh Annotations collection holding the labels and graphical primitives for that one model’s contribution (body silhouettes, ring polylines, star markers, etc.). The orchestrator merges every model’s collection into annotations via add_annotations(); the top-level driver (navigate_image_files()) then calls combine() to render the final RGB overlay and composites it onto the contrast-stretched source image before writing the PNG.

Class hierarchy

The subsystem is intentionally narrow: four shipping types and no abstract base.

  • Annotation — a single typed primitive (line, polyline, marker, text). Each instance carries an RGBA overlay array, an overlay color, an optional avoid-mask used by the label placer, and a list of attached AnnotationTextInfo entries.

  • AnnotationTextInfo — text payload + placement parameters (anchor location, arrow style, font color). The placement constants (TEXTINFO_TOP, TEXTINFO_BOTTOM_LEFT, TEXTINFO_LEFT_ARROW, etc.) name the twelve supported anchor / arrow combinations.

  • TextLocInfo — the label placer’s per-text resolution result (chosen pixel position, arrow tail, fitness score).

  • Annotations — collection of Annotation instances. Subclass of NavBase so it inherits config and logger.

Pipeline

Annotation flow during a single navigation run:

  1. Per-model emission. Each NavModel builds its Annotations collection in to_annotations(context). Concrete bodies / rings / stars use the shared helpers on NavModelBodyBase and NavModelRingsBase so the silhouette / polyline rendering is consistent across models.

  2. Orchestrator merge. The orchestrator’s _collect_annotations step builds an empty Annotations and calls add_annotations() for each surviving model’s contribution. The merged collection is recorded on annotations.

  3. Final composition. navigate_image_files() calls combine() with the navigation offset. combine runs the label placer (consulting each Annotation’s avoid-mask), shifts every overlay by the offset, and produces a single RGB overlay array. The driver composites that overlay over the contrast-stretched source image and writes the PNG.

Reading the per-image PNG therefore tells the operator three things in one image: what was in the source (background), what each NavModel predicted (overlay), and where the orchestrator placed the predictions relative to the data (the offset shift applied at composition time).

Configuration

The nav.annotation subsystem itself consumes no YAML configuration. The label / color knobs live in the per-NavModel config blocks instead:

  • bodies.label_*, bodies.outline_thicken, bodies.min_text_area in src/nav/config_files/config_040_bodies.yaml (consumed via NavModelBodyBase).

  • rings.label_* in src/nav/config_files/config_050_rings.yaml (consumed via NavModelRingsBase).

  • stars.label_* and stars.label_star_color in src/nav/config_files/config_030_stars.yaml (consumed via NavModelStars).

Each per-NavModel page documents the relevant subset: Body Navigation Model, Ring Navigation Model, and Star Navigation Model.

Per-model annotation contributions

  • Bodyto_annotations() emits a body silhouette outline plus a body-name label with leader arrow. The label placer scans the limb for a clear gap and falls back to a coarse grid when no per-limb candidate fits.

  • Ringto_annotations() emits one polyline per surviving ring edge plus a per-planet label.

  • Starto_annotations() emits a per-star marker plus an optional per-star magnitude label.

  • Simulated body / rings — the simulated NavModels reuse their catalog-driven counterparts’ annotation helpers, so a simulated scene’s PNG overlay is visually indistinguishable from a real scene’s.

  • Titan — the placeholder to_annotations() returns an empty collection.

API reference

The autodocumented API surface is at nav.annotation.