MobilApp/lib/models/track.dart

192 lines
6.4 KiB
Dart
Raw Normal View History

2026-05-10 02:31:27 +02:00
import 'dart:math' as math;
// ─── TrackPoint ───────────────────────────────────────────────────────────────
class TrackPoint {
final int? id;
final int trackId;
final double latitude;
final double longitude;
final double? altitude;
final double? accuracy;
final double? speed; // m/s
final double? heading;
final DateTime timestamp;
const TrackPoint({
this.id,
required this.trackId,
required this.latitude,
required this.longitude,
this.altitude,
this.accuracy,
this.speed,
this.heading,
required this.timestamp,
});
Map<String, dynamic> toMap() => {
if (id != null) 'id': id,
'track_id': trackId,
'latitude': latitude,
'longitude': longitude,
'altitude': altitude,
'accuracy': accuracy,
'speed': speed,
'heading': heading,
'timestamp': timestamp.toIso8601String(),
};
factory TrackPoint.fromMap(Map<String, dynamic> m) => TrackPoint(
id: m['id'] as int?,
trackId: m['track_id'] as int,
latitude: m['latitude'] as double,
longitude: m['longitude'] as double,
altitude: m['altitude'] as double?,
accuracy: m['accuracy'] as double?,
speed: m['speed'] as double?,
heading: m['heading'] as double?,
timestamp: DateTime.parse(m['timestamp'] as String),
);
}
// ─── Track státusz ────────────────────────────────────────────────────────────
enum TrackStatus { recording, paused, finished }
enum TrackSyncMode { localOnly, online }
enum TrackSyncStatus { pending, synced, error }
2026-05-10 02:31:27 +02:00
// ─── Track ───────────────────────────────────────────────────────────────────
class Track {
final int? id;
2026-06-11 01:20:55 +02:00
final int? projectId;
2026-05-10 02:31:27 +02:00
final String name;
final DateTime startTime;
final DateTime? endTime;
final TrackStatus status;
final String source; // pl. "Telefon GPS", "BLE GNSS"
// Statisztikák — ezeket a DB is tárolja a gyors listázáshoz
final double distanceMeters;
final int pointCount;
final TrackSyncMode syncMode;
final TrackSyncStatus syncStatus;
final String? supabaseId;
const Track(
{this.id,
this.projectId,
required this.name,
required this.startTime,
this.endTime,
this.status = TrackStatus.recording,
this.source = 'Telefon GPS',
this.distanceMeters = 0,
this.pointCount = 0,
this.syncMode = TrackSyncMode.online,
this.syncStatus = TrackSyncStatus.pending,
this.supabaseId});
Track copyWith(
{int? id,
int? projectId,
String? name,
DateTime? startTime,
DateTime? endTime,
TrackStatus? status,
String? source,
double? distanceMeters,
int? pointCount,
TrackSyncMode? syncMode,
TrackSyncStatus? syncStatus,
String? supabaseId}) =>
2026-05-10 02:31:27 +02:00
Track(
id: id ?? this.id,
projectId: projectId ?? this.projectId,
name: name ?? this.name,
startTime: startTime ?? this.startTime,
endTime: endTime ?? this.endTime,
status: status ?? this.status,
source: source ?? this.source,
distanceMeters: distanceMeters ?? this.distanceMeters,
pointCount: pointCount ?? this.pointCount,
syncMode: syncMode ?? this.syncMode,
syncStatus: syncStatus ?? this.syncStatus,
supabaseId: supabaseId ?? this.supabaseId);
2026-05-10 02:31:27 +02:00
/// Formázott időtartam (óó:pp:mm)
String get durationFormatted {
final end = endTime ?? DateTime.now();
final d = end.difference(startTime);
final h = d.inHours.toString().padLeft(2, '0');
final m = (d.inMinutes % 60).toString().padLeft(2, '0');
final s = (d.inSeconds % 60).toString().padLeft(2, '0');
return '$h:$m:$s';
}
/// Formázott távolság
String get distanceFormatted {
if (distanceMeters < 1000) {
return '${distanceMeters.toStringAsFixed(0)} m';
}
return '${(distanceMeters / 1000).toStringAsFixed(2)} km';
}
Map<String, dynamic> toMap() => {
if (id != null) 'id': id,
2026-06-11 01:20:55 +02:00
if (projectId != null) 'project_id': projectId,
2026-05-10 02:31:27 +02:00
'name': name,
'start_time': startTime.toIso8601String(),
'end_time': endTime?.toIso8601String(),
'status': status.name,
'source': source,
2026-06-11 01:20:55 +02:00
'distance_m': distanceMeters,
2026-05-10 02:31:27 +02:00
'point_count': pointCount,
'is_local_only': syncMode == TrackSyncMode.localOnly ? 1 : 0,
'sync_status': syncStatus.name,
'supabase_id': supabaseId,
2026-05-10 02:31:27 +02:00
};
factory Track.fromMap(Map<String, dynamic> m) => Track(
id: m['id'] as int?,
projectId: m['project_id'] as int?,
name: m['name'] as String,
startTime: DateTime.parse(m['start_time'] as String),
endTime: m['end_time'] != null
? DateTime.parse(m['end_time'] as String)
: null,
status: TrackStatus.values.firstWhere(
(s) => s.name == (m['status'] as String?),
orElse: () => TrackStatus.finished,
),
source: m['source'] as String? ?? 'Telefon GPS',
distanceMeters: (m['distance_m'] as num?)?.toDouble() ?? 0,
pointCount: m['point_count'] as int? ?? 0,
syncMode: (m['is_local_only'] as int?) == 1
? TrackSyncMode.localOnly
: TrackSyncMode.online,
syncStatus: TrackSyncStatus.values
.byName(m['sync_status'] as String? ?? 'pending'),
supabaseId: m['supabase_id'] as String?);
bool get isLocalOnly => syncMode == TrackSyncMode.localOnly;
2026-05-10 02:31:27 +02:00
}
// ─── Segédszámítás: Haversine távolság ───────────────────────────────────────
double haversineMeters(double lat1, double lon1, double lat2, double lon2) {
const r = 6371000.0;
final dLat = _toRad(lat2 - lat1);
final dLon = _toRad(lon2 - lon1);
final a = math.sin(dLat / 2) * math.sin(dLat / 2) +
math.cos(_toRad(lat1)) *
math.cos(_toRad(lat2)) *
math.sin(dLon / 2) *
math.sin(dLon / 2);
return r * 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a));
}
double _toRad(double deg) => deg * math.pi / 180;