Files
intaleq_driver/lib/controller/home/navigation/route_matcher_worker.dart
Hamza-Ayed a69e4c6912 25-10-11/1
2025-11-06 12:29:17 +03:00

146 lines
5.0 KiB
Dart

// lib/controllers/navigation/route_matcher_worker.dart
import 'dart:async';
import 'dart:isolate';
import 'dart:typed_data';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'dart:math';
/// Worker entrypoint (spawnUri/spawn).
/// Messages:
/// - init: {'type':'init','coords': Float64List}
/// - match: {'type':'match','id': int, 'lat': double, 'lng': double, 'lastIndex': int, 'window': int}
/// - dispose: {'type':'dispose'}
///
/// Responses are sent back as Map via SendPort:
/// - {'type':'ready'}
/// - {'type':'matchResult','id': id, 'index': overallIndex, 'lat': lat, 'lng': lng, 'dist': meters}
void routeMatcherIsolateEntry(SendPort sendPort) {
final ReceivePort port = ReceivePort();
sendPort.send({'type': 'ready', 'port': port.sendPort});
Float64List? flat; // [lat,lng,lat,lng,...]
int nPoints = 0;
port.listen((dynamic message) {
try {
if (message is Map<String, dynamic>) {
final type = message['type'] as String? ?? '';
if (type == 'init') {
final data = message['coords'] as Float64List?;
if (data != null) {
flat = data;
nPoints = flat!.length ~/ 2;
sendPort.send({'type': 'inited', 'points': nPoints});
} else {
sendPort.send({'type': 'error', 'message': 'init missing coords'});
}
} else if (type == 'match') {
if (flat == null) {
sendPort.send({'type': 'error', 'message': 'not inited'});
return;
}
final int id = message['id'] as int;
final double lat = (message['lat'] as num).toDouble();
final double lng = (message['lng'] as num).toDouble();
final int lastIndex = (message['lastIndex'] as int?) ?? 0;
final int window = (message['window'] as int?) ?? 120;
final result =
_findClosestWindowInternal(flat!, lat, lng, lastIndex, window);
sendPort.send({
'type': 'matchResult',
'id': id,
'index': result['index'],
'lat': result['lat'],
'lng': result['lng'],
'dist': result['dist']
});
} else if (type == 'dispose') {
port.close();
sendPort.send({'type': 'disposed'});
} else {
sendPort.send({'type': 'error', 'message': 'unknown message type'});
}
}
} catch (e, st) {
sendPort.send(
{'type': 'error', 'message': e.toString(), 'stack': st.toString()});
}
});
}
/// Internal helper: projection on segments, windowed search.
/// Returns Map {index, lat, lng, dist}
Map<String, dynamic> _findClosestWindowInternal(
Float64List flat, double lat, double lng, int lastIndex, int window) {
final int n = flat.length ~/ 2;
final int start = max(0, lastIndex - window);
final int end = min(n - 1, lastIndex + window);
double minDist = double.infinity;
int bestIdx = lastIndex;
double bestLat = flat[lastIndex * 2];
double bestLng = flat[lastIndex * 2 + 1];
for (int i = start; i < end; i++) {
final double aLat = flat[i * 2];
final double aLng = flat[i * 2 + 1];
final double bLat = flat[(i + 1) * 2];
final double bLng = flat[(i + 1) * 2 + 1];
final proj = _closestPointOnSegmentLatLng(lat, lng, aLat, aLng, bLat, bLng);
final double d = proj['dist'] as double;
if (d < minDist) {
minDist = d;
bestLat = proj['lat'] as double;
bestLng = proj['lng'] as double;
// choose overall index: i or i+1 depending on t
final double t = proj['t'] as double;
bestIdx = i + (t > 0.5 ? 1 : 0);
}
}
return {'index': bestIdx, 'lat': bestLat, 'lng': bestLng, 'dist': minDist};
}
/// Projection math on geodetic points approximated in degrees (good for short distances).
Map<String, dynamic> _closestPointOnSegmentLatLng(
double px, double py, double ax, double ay, double bx, double by) {
// Here px=lat, py=lng; ax=lat, ay=lng, etc.
final double x0 = px;
final double y0 = py;
final double x1 = ax;
final double y1 = ay;
final double x2 = bx;
final double y2 = by;
final double dx = x2 - x1;
final double dy = y2 - y1;
double t = 0.0;
final double len2 = dx * dx + dy * dy;
if (len2 > 0) {
t = ((x0 - x1) * dx + (y0 - y1) * dy) / len2;
if (t < 0) t = 0;
if (t > 1) t = 1;
}
final double projX = x1 + t * dx;
final double projY = y1 + t * dy;
final double distMeters = _haversineDistanceMeters(x0, y0, projX, projY);
return {'lat': projX, 'lng': projY, 't': t, 'dist': distMeters};
}
/// Haversine distance (meters)
double _haversineDistanceMeters(
double lat1, double lng1, double lat2, double lng2) {
final double R = 6371000.0;
final double dLat = _deg2rad(lat2 - lat1);
final double dLon = _deg2rad(lng2 - lng1);
final double a = sin(dLat / 2) * sin(dLat / 2) +
cos(_deg2rad(lat1)) * cos(_deg2rad(lat2)) * sin(dLon / 2) * sin(dLon / 2);
final double c = 2 * atan2(sqrt(a), sqrt(1 - a));
return R * c;
}
double _deg2rad(double deg) => deg * pi / 180.0;