Files
samplez/rippr-src/docs/v2/14-path-rendering.md
uhryniuk 280fd7f988 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>
2026-08-11 08:30:25 -05:00

4.5 KiB
Raw Permalink Blame History

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
  • Auto-fit frames the whole route with padding
  • A stationary ride does not crash the fit
  • Exported GPX still contains every raw point (verified: 24 points recorded, 24 exported)
  • 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.