# Architecture and data flow ## System overview WeatherTool is a Scala backend, SolidJS frontend, and PostgreSQL database used to inspect Latvian weather observations and prepare map-based newsroom graphics. ```text External providers Local/staging workflow LVGMC station FTP/CSV ----+ Synthetic SQL seed LVGMC forecast CSV -------+ | DMI HARMONIE APIs --------+ v v PostgreSQL Scala ingestion | | | +---------> Scala/http4s API | v SolidJS web UI | v Canvas preview and PNG export ``` Staging currently exercises the PostgreSQL → API → frontend path with synthetic observations. It does not emulate LVGMC forecast files or HARMONIE GRIB files. ## Components ### Scala backend - Serves JSON API routes and the built frontend from `web/dist`. - Queries and aggregates station observations in PostgreSQL. - Contains ingestion/parsing code for LVGMC station and forecast data. - Fetches LVĢMC open hydrometeorological-warning metadata and polygon coordinates on request, with a 5-minute in-memory cache and stale-data fallback if the upstream call fails. - Contains HARMONIE discovery, download, GRIB parsing, and rendering support. - Starts scheduled ingestion/cleanup tasks unless `ENABLE_SCHEDULED_JOBS=false`. ### SolidJS frontend - Queries the backend using the same origin in production builds. - Provides Stacijas, Kartes, Faktiskā, Ūdens, Apskats, Arhīvs, Harmonie, and LVĢMC workspaces. - Uses HTML canvas for broadcast maps and PNG export. - Uses normalized transparent PNG assets for weather symbols; the internal letter codes remain stable identifiers and are not rendered as text or emoji. ### PostgreSQL - Stores normalized weather observations used by Stacijas, Kartes, Faktiskā, and Apskats. - Is accessible only inside the isolated Compose network in staging. - Uses a bind-mounted `postgres/` runtime directory in the current Compose configuration. ## Main observation-data flow 1. An ingestion job parses provider station data, or the development seed inserts synthetic rows. 2. Observations are stored in the `weather` table. 3. The frontend requests an API route containing cities, time range, granularity, field, and aggregate key. 4. The backend performs the database query and returns JSON. 5. The frontend renders cards, lists, charts, or canvas graphics. 6. Broadcast maps can be downloaded as PNG files entirely in the browser. ## Broadcast-map flow ### Kartes temperature maps Queried city values are placed at predefined coordinates on one of four map backgrounds: - `map_1920x1080` - `map_1920x1080_wind` - `map_3840x1440` - `map_3840x1440_wind` The operator can add title/source overlays and export the rendered canvas. ### Faktiskā Faktiskā has a fixed 13-position station configuration. On page entry the frontend calls `/api/query/latest-temperatures/`. The backend selects the newest row per requested city and returns `tempAvg`, the station name, and its observation timestamp. The UI highlights missing or older observations and permits an explicit manual override without changing the stored observation. Weather symbols and wind values remain manual editorial inputs. One symbol can be applied to all fixed stations and exceptions changed individually. The browser composes these values on the fixed wind-template canvas and exports exactly 3840×1440 pixels. The production master images define the required output geometry; final Monda pixel-level comparison remains pending. The renderer attaches each normalized 256×256 weather image to the measured edge of its temperature badge. All assets share one canvas, center, and scale, so placement does not depend on font bearings or baselines. The configuration permits per-station offsets when later production comparison requires them. Operator input selects the symbol but never controls its coordinates. ### Ūdens Ūdens is an independent fixed-template renderer. Its six ranges are manual editorial inputs rather than station-query results. The 1920×1080 and 3840×1440 products use separate authoritative templates and six separately measured value rectangles. Canvas `TextMetrics` visible-glyph bounds center the Monda text optically inside each rectangle. Preview scaling never changes native export geometry, and both output sizes have been visually validated against the supplied newsroom templates. Monda Regular and Bold are bundled locally and used by the interface and generated text. Weather symbols are bundled as independent transparent PNG assets. ### Brīdinājumi Brīdinājumi renders the current LVĢMC warning polygons over the same `map_1920x1080`/`map_3840x1440` backgrounds used by Kartes, plus a production border-outline overlay drawn last so it stays crisp above the softened data layer. Warning severity is blurred and filled onto an offscreen layer before compositing, giving the broadcast graphic a feathered rather than hard-edged warning area. The canvas always renders exactly one operator-selected warning, matching how these graphics are produced for air; there is no multi-warning composite and no on-canvas legend. The title is derived from the selected warning's phenomenon (`Brīdinājums: {phenomenon}`) but stays manually editable. Each warning is listed as a compact phenomenon-icon chip (icon from the already-bundled `lucide-solid` set, keyword-matched against the LVĢMC phenomenon text). Selecting a warning for the map and viewing its full text are deliberately independent actions on the same chip: a checkbox drives map selection, while clicking the chip body opens that warning's full text in a dialog rendered through a Solid `Portal` above a dimmed, blurred backdrop — so an operator can read a warning without disturbing whichever warning is currently on the map. The chip grid stays uniform regardless of how long an individual warning's description is. The backend returns warning polygons as raw latitude/longitude points; the frontend projects them to pixel space with a per-resolution affine transform (`x = a·lon + b·lat + c`, `y = d·lon + e·lat + f`). Each transform is a least-squares fit of well-spread Latvian cities' real-world coordinates against the pixel positions already validated for these same map backgrounds by Kartes/Faktiskā (`cityCoords.ts` + `mapConsts.ts`), rather than an independently eyeballed calibration. An earlier 4-corner bounding-box version of this projection could not represent the source artwork's rotation/shear and drifted up to ~200px on the 3840 canvas; the affine fit lands within ~35px. ## What the repository does not provide by itself - Authorized provider accounts and credentials. - Workplace deployment topology or authentication requirements. - Real LVGMC forecast CSV fixtures for development. - Real HARMONIE GRIB fixtures for development. - A complete automated test suite. - Production monitoring, backups, incident handling, or a reviewed security boundary. See `UPDATE_ROADMAP.md` before considering workplace deployment. ## Proposed temporary public-testing edge WeatherTool has not yet been deployed publicly. The proposed month-long user-acceptance environment adds a removable edge layer without changing the application into the authentication authority: ```text Cloudflare -> VPS Nginx -> Authelia authorization -> WeatherTool -> PostgreSQL ``` Rocky remains the trusted build and verification host. The VPS receives a commit-addressed immutable image and release metadata rather than pulling and building the development repository. WeatherTool, its database, and Authelia must use a dedicated Compose project, private network, storage paths, and loopback-only upstream port so the existing VPS services remain untouched. See `VPS_STAGING_PLAN.md` for the proposed topology and release procedure.