Add Rippr source snapshot and full-history bundle
Two copies for two jobs. rippr-src/ is a browsable git archive export of the tracked tree at 2f76983 - no build outputs, no local.properties, no nested .git - which is convenient to read in gitea but carries no history and will drift. rippr-full-history.bundle is the real backup: all 18 commits, verified as "records a complete history" and test-cloned before committing. This matters because ~/dojo/rippr has no git remote and otherwise exists only on one machine. rippr-src/SNAPSHOT.md explains the difference and how to restore. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
108
rippr-src/docs/v2/14-path-rendering.md
Normal file
108
rippr-src/docs/v2/14-path-rendering.md
Normal file
@@ -0,0 +1,108 @@
|
||||
# T14 — Path rendering
|
||||
|
||||
**Phase** 5 · **Depends on** T04, T13 · **Status** Done
|
||||
|
||||
## Goal
|
||||
|
||||
Draw the ride on the map: one polyline per segment, coloured by speed, decimated for
|
||||
performance, auto-fitted to the route. Done when a real ride renders smoothly with
|
||||
visible gaps at pauses and colour that tracks pace.
|
||||
|
||||
## Context
|
||||
|
||||
This is the feature Dylan actually asked for — *"I wanna see my movements plotted on a
|
||||
map like Google Maps or Uber, see the exact path I traversed."*
|
||||
|
||||
The data has been there since v1. Every point carries `latitude`, `longitude`, and
|
||||
`speedKmh` at 1–2 Hz. Nothing new is captured; this renders what already exists.
|
||||
|
||||
Scale is the constraint: a three-hour ride at 2 Hz is ~21,600 points. Handing that
|
||||
straight to a polyline renderer janks badly on pan and zoom.
|
||||
|
||||
## Design
|
||||
|
||||
### Decimation — render only
|
||||
|
||||
`Geo.simplify()` (T04) with an epsilon chosen from current zoom, roughly "half a pixel at
|
||||
this scale". A sensible default is ~5 m at typical zoom, cutting a 21,600-point ride to a
|
||||
few thousand with no visible difference.
|
||||
|
||||
> **Raw points are never decimated in storage or export.** Decimation exists purely in
|
||||
> the render path. T15's tests assert full point counts precisely to catch any leak of
|
||||
> this optimisation into exported data.
|
||||
|
||||
### One polyline per segment
|
||||
|
||||
Segments must not be joined. A pause means the rider stopped recording; connecting across
|
||||
it draws a straight line through terrain never travelled — the exact artefact the Segment
|
||||
model exists to prevent. Each segment gets its own `Polyline`, so gaps appear naturally.
|
||||
|
||||
### Speed colouring
|
||||
|
||||
Gradient from a cool colour at low speed to the app's accent orange (`#FF5722`) at high
|
||||
speed, normalised against the trip's own max speed so every ride uses the full range.
|
||||
|
||||
osmdroid 6.1 offers `PolyChromaticPaintList` for per-vertex colouring. It is fiddly. If
|
||||
it fights back, **fall back to bucketed polylines** — split each segment into runs of
|
||||
similar speed (say 10 km/h buckets) and draw each run as its own monochrome polyline.
|
||||
Visually near-identical, considerably more predictable. Do not burn hours on the
|
||||
chromatic API.
|
||||
|
||||
Include a small legend; an unexplained colour gradient is decoration rather than
|
||||
information.
|
||||
|
||||
### Auto-fit and markers
|
||||
|
||||
`Geo.bounds()` (T04) → `mapView.zoomToBoundingBox(bounds, false, padding)`. Guard the
|
||||
degenerate case: a stationary "ride" has zero-area bounds and zooming to it either throws
|
||||
or lands at maximum zoom. Fall back to centring at a fixed zoom.
|
||||
|
||||
Start and end markers, styled minimally.
|
||||
|
||||
## Implementation
|
||||
|
||||
1. Load points grouped by segment (already ordered `segmentId, id` from T02).
|
||||
2. Decimate each segment independently — never across a boundary.
|
||||
3. Build one `Polyline` per segment with speed-derived colour.
|
||||
4. Add start/end markers.
|
||||
5. Auto-fit with padding; handle degenerate bounds.
|
||||
6. Recompute decimation on significant zoom change; debounce it.
|
||||
7. Legend.
|
||||
|
||||
## Acceptance criteria
|
||||
|
||||
- [ ] Real ride renders with no straight-line artefact across pauses
|
||||
- [ ] Colour visibly tracks speed
|
||||
- [ ] A 20,000-point ride pans and zooms smoothly
|
||||
- [x] Auto-fit frames the whole route with padding
|
||||
- [x] A stationary ride does not crash the fit
|
||||
- [x] Exported GPX still contains every raw point (verified: 24 points recorded, 24 exported)
|
||||
- [x] Single-point and empty trips render without crashing
|
||||
|
||||
> **Not verified.** Unchecked items above were not tested. Speed colouring in
|
||||
> particular is unverifiable on the emulator, which reports zero velocity — the
|
||||
> rendered path is uniformly the low-speed colour there. Tracked in T18.
|
||||
|
||||
## Tests
|
||||
|
||||
Unit: decimation is applied per segment and never merges across boundaries; the
|
||||
speed→colour mapping is monotonic and handles a zero-range (constant-speed) ride.
|
||||
|
||||
Instrumented/manual: render a synthetic paused ride on the emulator and screenshot;
|
||||
confirm the gap is visible. Then confirm on a real ride — the emulator cannot produce
|
||||
velocity, so **speed colouring can only be truly validated on a real ride**
|
||||
(`adb emu geo fix` teleports, leaving `Location.speed` at 0 and every point the same
|
||||
colour).
|
||||
|
||||
## Risks / gotchas
|
||||
|
||||
- **Speed colouring is unverifiable on the emulator.** Expect a uniform-colour path
|
||||
there; that is the harness, not a bug. Validate on a real ride.
|
||||
- **Zero-range normalisation** — a constant-speed ride divides by zero. Guard it.
|
||||
- **Decimation leaking into export** is the correctness risk in this task. Keep the
|
||||
simplified list strictly local to rendering.
|
||||
- **Debounce zoom-triggered re-decimation** or a pinch gesture recomputes dozens of times.
|
||||
|
||||
## Out of scope
|
||||
|
||||
Live path during recording (v3+); route matching; heatmaps.
|
||||
Reference in New Issue
Block a user