feat(train): routes kept as sealed rows, planned on the map (ADR 0030, D44)

Routes are synced `route` rows (the model `SavedRoute`, as Flutter has a
`Route`), sealed as plans are; the migration `routes` only creates the
table, its space index and its cascade to the sync engine's spaces. A
route's payload holds its name, activity, profile, waypoints and the way
planned, points and elevations delta-coded and gzipped.

Home gets a fourth tab, Routes, and the planner: waypoints tapped on the
map, held and dragged, the last undone, a new route's first where the
phone is (GPS through the recorder's channel), the way planned by the
instance's BRouter after each change, an earlier answer dropped; its
distance, climb, descent, elevation and surfaces under the map.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
Tizian 「ティツィアーン」 2026-10-29 00:37:44 +01:00
commit aea99c68ca
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64

View file

@ -867,6 +867,23 @@ Strava. What was found, 27 Oct:
descent, an elevation profile under the map and the surfaces it runs on as BRouter
tells them (over distance and climb alone, and over loops of a given length, which
BRouter can't make and Train would only guess at).
Built: the `routing` endpoint and the deploy (9a, `ab128550`, `7fcb91c6`); routes
as synced, sealed `route` rows and the planner (9b). A route's payload holds its
name, activity, profile, waypoints and the way planned (points to a micro-degree
and elevations to a decimetre, each delta-coded, its turns, its stretches),
gzipped as a workout's series are, since a planned route has thousands of points.
The server's model is `SavedRoute` on the table `route`, since Flutter's widgets
have a `Route` of their own. Home's fourth tab lists the routes; the planner is the
map above and the route's figures under it: a tap adds a waypoint, one held and
dragged moves it (held first, so that dragging the map never moves one), the
button above the map takes the last off, and the instance plans the way after each
change, an answer to an earlier change dropped once a later one is asked. A new
route starts where the phone is: the recorder's channel asks GPS for its last fix
of the past two minutes or a new one (15 s at most), the location permission asked
first (no package, as recording takes no other); on a device that can't tell it
starts empty. Surfaces are grouped from OpenStreetMap's tags as paved, compacted,
gravel, natural ground and not known. An instance without BRouter says so on the
planner.
- **D45 Following a route** (recommended, the developer's; 28 Oct): the recording
screen keeps its figures first and swipes to a map with the route, the track so far
and the position, north or heading up as tapped, with a route or without (over the