T19/T21: map and export, completing Phase 4

flutter_map with the same OSM tiles and no-API-key reasoning that chose osmdroid.
All four load-bearing behaviours ported and, unlike the native app's map, tested:
one polyline per segment so a pause is a real gap, render-only decimation, the
zoom clamp at OSM's max tile zoom with a short-ride fallback, and a real user
agent. Speed colouring is bucketed per run rather than per-vertex, since neither
osmdroid nor flutter_map makes per-vertex paint reasonable.

Export via share_plus, which also handles the iPad popover anchor a naive port
forgets. It passes the raw stored points, never the map's decimated path.

164 tests passing, analyze clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-15 16:27:42 -05:00
parent bcc12b314f
commit d501528e69
20 changed files with 1085 additions and 478 deletions

View File

@@ -74,19 +74,18 @@ class Trip {
double? maxSpeedKmh,
double? elevationGainM,
int? pointCount,
}) =>
Trip(
id: id ?? this.id,
startedAt: startedAt ?? this.startedAt,
endedAt: endedAt ?? this.endedAt,
name: name ?? this.name,
state: state ?? this.state,
distanceM: distanceM ?? this.distanceM,
movingMillis: movingMillis ?? this.movingMillis,
maxSpeedKmh: maxSpeedKmh ?? this.maxSpeedKmh,
elevationGainM: elevationGainM ?? this.elevationGainM,
pointCount: pointCount ?? this.pointCount,
);
}) => Trip(
id: id ?? this.id,
startedAt: startedAt ?? this.startedAt,
endedAt: endedAt ?? this.endedAt,
name: name ?? this.name,
state: state ?? this.state,
distanceM: distanceM ?? this.distanceM,
movingMillis: movingMillis ?? this.movingMillis,
maxSpeedKmh: maxSpeedKmh ?? this.maxSpeedKmh,
elevationGainM: elevationGainM ?? this.elevationGainM,
pointCount: pointCount ?? this.pointCount,
);
}
/// One pause-free stretch of recording within a [Trip].