# ADR-002: Polygon JSONB Storage for Annotations ## Status Accepted ## Context Document annotations currently store axis-aligned bounding boxes (`x, y, width, height`). Kraken OCR outputs polygon boundaries for text lines — historical handwriting (Kurrent, Suetterlin) produces rotated and curved text that axis-aligned rectangles approximate poorly. We need to store an optional quadrilateral (4 corner points) per annotation to represent the precise text region. The polygon is display-only — overlap detection and all server-side geometry logic continues to use the AABB fields. ## Decision Add a `polygon JSONB` column to `document_annotations`: ```sql ALTER TABLE document_annotations ADD COLUMN polygon JSONB; ALTER TABLE document_annotations ADD CONSTRAINT chk_annotation_polygon_quad CHECK (polygon IS NULL OR jsonb_array_length(polygon) = 4); ``` - `null` means rectangle — render using existing `x, y, width, height` fields (fully backward compatible) - Non-null value is a normalized 4-point quadrilateral: `[[x1,y1],[x2,y2],[x3,y3],[x4,y4]]` with coordinates in the 0-1 range relative to page dimensions The existing AABB fields are always populated (even when a polygon is present) and remain the authoritative geometry for overlap detection. **Java entity:** `List> polygon` backed by a custom `AttributeConverter>, String>`. No new dependency (Hypersistence Utils is not in the project and won't be added for a single column). **Semantic invariant:** `polygon`, if present, is a 4-point quadrilateral with coordinates normalized to [0, 1] relative to page dimensions. It may originate from OCR engine output (Kraken) or from a future manual drawing tool. The AABB fields remain the geometry source of truth for server-side logic. ## Alternatives Considered | Alternative | Why rejected | |---|---| | 8 `NUMERIC(8,6)` columns (x1,y1,...,x4,y4) | Verbose, no structural enforcement, awkward to query or extend | | Separate `annotation_polygons` join table | Unnecessary complexity for a 1:1 optional relationship | | PostGIS geometry column | Adds a heavyweight extension for a display-only field with no spatial queries | | `String polygon` on the entity | Requires manual parsing at every callsite; error-prone | ## Consequences **Easier:** - Backward compatible — all existing annotations continue to work unchanged - Frontend renders `` or `` based on a simple null check - Schema can accommodate N-point polygons in the future (JSONB is flexible), though the CHECK constraint currently enforces exactly 4 **Harder:** - Cannot express range checks (`0 <= x <= 1`) as database constraints without a PL/pgSQL function — validated at the DTO layer instead - No server-side geometry queries on polygon coordinates (acceptable — polygon is display-only) - AttributeConverter adds a small amount of serialization code to maintain