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 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 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 } // ─── Track ─────────────────────────────────────────────────────────────────── class Track { final int? id; 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; const Track({ this.id, required this.name, required this.startTime, this.endTime, this.status = TrackStatus.recording, this.source = 'Telefon GPS', this.distanceMeters = 0, this.pointCount = 0, }); Track copyWith({ int? id, String? name, DateTime? startTime, DateTime? endTime, TrackStatus? status, String? source, double? distanceMeters, int? pointCount, }) => Track( id: id ?? this.id, 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, ); /// 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 toMap() => { if (id != null) 'id': id, 'name': name, 'start_time': startTime.toIso8601String(), 'end_time': endTime?.toIso8601String(), 'status': status.name, 'source': source, 'distance_meters': distanceMeters, 'point_count': pointCount, }; factory Track.fromMap(Map m) => Track( id: m['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.byName(m['status'] as String), source: m['source'] as String? ?? 'Telefon GPS', distanceMeters: (m['distance_meters'] as num?)?.toDouble() ?? 0, pointCount: m['point_count'] as int? ?? 0, ); } // ─── 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;