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:
@@ -48,7 +48,9 @@ String escapeXml(String text) {
|
||||
|
||||
/// Segment order first, then any points whose segment is missing, so nothing is dropped.
|
||||
List<int> _orderedSegmentIds(
|
||||
List<Segment> segments, Map<int, List<TrackPoint>> bySegment) {
|
||||
List<Segment> segments,
|
||||
Map<int, List<TrackPoint>> bySegment,
|
||||
) {
|
||||
final ids = <int>[
|
||||
for (final s in segments)
|
||||
if (bySegment.containsKey(s.id)) s.id,
|
||||
@@ -87,7 +89,8 @@ String gpx(
|
||||
final b = StringBuffer();
|
||||
b.write('<?xml version="1.0" encoding="UTF-8"?>\n');
|
||||
b.write(
|
||||
'<gpx version="1.1" creator="Rippr" xmlns="http://www.topografix.com/GPX/1/1">\n');
|
||||
'<gpx version="1.1" creator="Rippr" xmlns="http://www.topografix.com/GPX/1/1">\n',
|
||||
);
|
||||
b.write(' <metadata>\n');
|
||||
b.write(' <name>${escapeXml(name)}</name>\n');
|
||||
b.write(' <time>${_time(trip.startedAt)}</time>\n');
|
||||
@@ -99,7 +102,8 @@ String gpx(
|
||||
b.write(' <trkseg>\n');
|
||||
for (final p in bySegment[segmentId]!) {
|
||||
b.write(
|
||||
' <trkpt lat="${_coord(p.latitude)}" lon="${_coord(p.longitude)}">\n');
|
||||
' <trkpt lat="${_coord(p.latitude)}" lon="${_coord(p.longitude)}">\n',
|
||||
);
|
||||
b.write(' <ele>${_num(p.altitudeM)}</ele>\n');
|
||||
b.write(' <time>${_time(p.timestamp)}</time>\n');
|
||||
b.write(' <extensions>\n');
|
||||
@@ -129,17 +133,21 @@ String geoJson(
|
||||
final bySegment = _groupBySegment(points);
|
||||
final ordered = _orderedSegmentIds(segments, bySegment);
|
||||
|
||||
final features = ordered.map((segmentId) {
|
||||
final coords = bySegment[segmentId]!
|
||||
.map((p) =>
|
||||
'[${_coord(p.longitude)}, ${_coord(p.latitude)}, ${_num(p.altitudeM)}]')
|
||||
.join(', ');
|
||||
return ''' {
|
||||
final features = ordered
|
||||
.map((segmentId) {
|
||||
final coords = bySegment[segmentId]!
|
||||
.map(
|
||||
(p) =>
|
||||
'[${_coord(p.longitude)}, ${_coord(p.latitude)}, ${_num(p.altitudeM)}]',
|
||||
)
|
||||
.join(', ');
|
||||
return ''' {
|
||||
"type": "Feature",
|
||||
"properties": { "segment_id": $segmentId },
|
||||
"geometry": { "type": "LineString", "coordinates": [$coords] }
|
||||
}''';
|
||||
}).join(',\n');
|
||||
})
|
||||
.join(',\n');
|
||||
|
||||
return '''{
|
||||
"type": "FeatureCollection",
|
||||
|
||||
Reference in New Issue
Block a user