// lib/model/activity/activity_track.dart import 'package:latlong2/latlong.dart'; class ActivityTrack { final String name; final String format; final Set> 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 get points sync* { for (final segment in tracks) { yield* segment; } } List 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( 0, (sum, point) => sum + point.latitude, ) / allPoints.length; final longitude = allPoints.fold( 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; } }