189 lines
4.5 KiB
Text
189 lines
4.5 KiB
Text
// lib/model/activity/activity_track.dart
|
|
import 'package:latlong2/latlong.dart';
|
|
|
|
class ActivityTrack {
|
|
final String name;
|
|
final String format;
|
|
final Set<List<LatLng>> tracks;
|
|
|
|
/// Numero punti validi letti dal file prima di eventuale semplificazione grafica.
|
|
final int pointCount;
|
|
|
|
final double? distanceMeters;
|
|
final double? elevationGainMeters;
|
|
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.distanceMeters,
|
|
this.elevationGainMeters,
|
|
this.duration,
|
|
this.startTime,
|
|
this.sport,
|
|
this.diagnostics,
|
|
});
|
|
|
|
bool get isEmpty => tracks.isEmpty || tracks.every((segment) => segment.isEmpty);
|
|
|
|
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;
|
|
|
|
/// Angolo sud-ovest del rettangolo che contiene tutta la traccia.
|
|
LatLng? get southWest {
|
|
final south = minLatitude;
|
|
final west = minLongitude;
|
|
|
|
if (south == null || west == null) return null;
|
|
return LatLng(south, west);
|
|
}
|
|
|
|
/// Angolo nord-est del rettangolo che contiene tutta la traccia.
|
|
LatLng? get northEast {
|
|
final north = maxLatitude;
|
|
final east = maxLongitude;
|
|
|
|
if (north == null || east == null) return null;
|
|
return LatLng(north, east);
|
|
}
|
|
|
|
/// Centro geometrico del bounding box.
|
|
///
|
|
/// A differenza di [center], non dipende dalla densita dei punti. Per la mappa
|
|
/// spesso e' piu stabile per tracce con pause o punti molto concentrati.
|
|
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 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;
|
|
}
|
|
}
|