aves_mio0.31/lib/model/activity/activity_track.dart
2026-07-18 13:39:22 +02:00

224 lines
5.5 KiB
Dart

// lib/model/activity/activity_track.dart
import 'package:latlong2/latlong.dart';
class ActivityTrack {
final String name;
final String format;
final Set<List<LatLng>> tracks;
/// Nome percorso/attività letto dal file, se disponibile.
/// Se non disponibile, la UI può usare [name].
final String? routeName;
/// Numero punti validi letti dal file prima di eventuale semplificazione grafica.
final int pointCount;
/// Valori usati normalmente dalla UI.
final double? distanceMeters;
final double? elevationGainMeters;
/// Valori aggregati scritti nel file, ad esempio FIT totalDistance/totalAscent.
final double? fileDistanceMeters;
final double? fileElevationGainMeters;
/// Valori ricalcolati dai punti del file, quando disponibili.
/// Per l'elevazione serve che il parser mantenga/calcoli le quote: con soli
/// LatLng non è possibile ricalcolare il D+ in questa pagina.
final double? computedDistanceMeters;
final double? computedElevationGainMeters;
final Duration? duration;
final DateTime? startTime;
final String? sport;
final String? diagnostics;
const ActivityTrack({
required this.name,
required this.format,
required this.tracks,
required this.pointCount,
this.routeName,
this.distanceMeters,
this.elevationGainMeters,
this.fileDistanceMeters,
this.fileElevationGainMeters,
this.computedDistanceMeters,
this.computedElevationGainMeters,
this.duration,
this.startTime,
this.sport,
this.diagnostics,
});
bool get isEmpty => tracks.isEmpty || tracks.every((segment) => segment.isEmpty);
String get displayName {
final value = routeName?.trim();
if (value != null && value.isNotEmpty) return value;
return name;
}
Iterable<LatLng> get points sync* {
for (final segment in tracks) {
yield* segment;
}
}
List<LatLng> get validPoints {
return points
.where(
(point) =>
point.latitude.isFinite &&
point.longitude.isFinite &&
point.latitude.abs() <= 90 &&
point.longitude.abs() <= 180,
)
.toList(growable: false);
}
int get displayPointCount => validPoints.length;
LatLng? get center {
final allPoints = validPoints;
if (allPoints.isEmpty) return null;
final latitude = allPoints.fold<double>(
0,
(sum, point) => sum + point.latitude,
) /
allPoints.length;
final longitude = allPoints.fold<double>(
0,
(sum, point) => sum + point.longitude,
) /
allPoints.length;
return LatLng(latitude, longitude);
}
double? get minLatitude {
final allPoints = validPoints;
if (allPoints.isEmpty) return null;
return allPoints
.map((point) => point.latitude)
.reduce((a, b) => a < b ? a : b);
}
double? get maxLatitude {
final allPoints = validPoints;
if (allPoints.isEmpty) return null;
return allPoints
.map((point) => point.latitude)
.reduce((a, b) => a > b ? a : b);
}
double? get minLongitude {
final allPoints = validPoints;
if (allPoints.isEmpty) return null;
return allPoints
.map((point) => point.longitude)
.reduce((a, b) => a < b ? a : b);
}
double? get maxLongitude {
final allPoints = validPoints;
if (allPoints.isEmpty) return null;
return allPoints
.map((point) => point.longitude)
.reduce((a, b) => a > b ? a : b);
}
bool get hasBounds =>
minLatitude != null &&
maxLatitude != null &&
minLongitude != null &&
maxLongitude != null;
LatLng? get southWest {
final south = minLatitude;
final west = minLongitude;
if (south == null || west == null) return null;
return LatLng(south, west);
}
LatLng? get northEast {
final north = maxLatitude;
final east = maxLongitude;
if (north == null || east == null) return null;
return LatLng(north, east);
}
LatLng? get boundsCenter {
final south = minLatitude;
final north = maxLatitude;
final west = minLongitude;
final east = maxLongitude;
if (south == null || north == null || west == null || east == null) {
return null;
}
return LatLng(
(south + north) / 2,
(west + east) / 2,
);
}
String get distanceLabel {
final value = distanceMeters;
if (value == null) return '- km';
return '${(value / 1000).toStringAsFixed(2)} km';
}
String get elevationGainLabel {
final value = elevationGainMeters;
if (value == null) return 'D+ -';
return 'D+ ${value.round()} m';
}
String get dateLabel {
final value = startTime;
if (value == null) return 'Data -';
final local = value.toLocal();
final day = local.day.toString().padLeft(2, '0');
final month = local.month.toString().padLeft(2, '0');
final year = local.year.toString().padLeft(4, '0');
final hour = local.hour.toString().padLeft(2, '0');
final minute = local.minute.toString().padLeft(2, '0');
return '$day/$month/$year $hour:$minute';
}
String get durationLabel {
final value = duration;
if (value == null) return '-';
final hours = value.inHours;
final minutes = value.inMinutes.remainder(60);
final seconds = value.inSeconds.remainder(60);
if (hours > 0) {
return '${hours}h ${minutes}m';
}
if (minutes > 0) {
return '${minutes}m ${seconds}s';
}
return '${seconds}s';
}
String get sportLabel {
final value = sport;
if (value == null || value.trim().isEmpty) return 'Attività';
return value;
}
}