final intaleq map 26-4-18-1

This commit is contained in:
Hamza-Ayed
2026-04-18 22:48:51 +03:00
parent 61343111a2
commit 13ef5f21b7
5 changed files with 2509 additions and 1248 deletions

View File

@@ -70,12 +70,16 @@ class NavigationController extends GetxController
Set<Circle> circles = {};
Set<Polygon> polygons = {};
Timer? _locationUpdateTimer;
StreamSubscription<Position>? _locationStreamSubscription;
LatLng? _lastProcessedLocation;
List<dynamic> placesDestination = [];
Timer? _debounce;
// Alternative route handling
bool _hasAlternativeRoutes = false;
DateTime? _lastAutoRerouteTime;
LatLng? _finalDestination;
LatLng? _intermediateStop;
List<Map<String, dynamic>> routeSteps = [];
@@ -301,7 +305,7 @@ class NavigationController extends GetxController
void onInit() {
super.onInit();
_animController = AnimationController(
vsync: this, duration: const Duration(milliseconds: 3800));
vsync: this, duration: const Duration(milliseconds: 1000));
_animController!.addListener(() {
if (_oldLoc != null && _targetLoc != null && _mapReady) {
final t = _animController!.value;
@@ -360,7 +364,7 @@ class NavigationController extends GetxController
@override
void onClose() {
_locationUpdateTimer?.cancel();
_locationStreamSubscription?.cancel();
_recordTimer?.cancel();
_uploadBatchTimer?.cancel();
_debounce?.cancel();
@@ -468,29 +472,43 @@ class NavigationController extends GetxController
_smoothedHeading = position.heading;
update();
if (isStyleLoaded) animateCameraToPosition(myLocation!);
_startLocationTimer();
// Start the Location Stream for real-time updates
_startLocationStream();
_startBatchTimers();
} catch (e) {
Log.print("DEBUG: Error getting initial location: $e");
}
}
void _startLocationTimer() {
_locationUpdateTimer?.cancel();
_locationUpdateTimer =
Timer.periodic(const Duration(seconds: 4), (_) => _tick());
void _startLocationStream() {
_locationStreamSubscription?.cancel();
// Listen to location updates with minimum distance filter of 2 meters
// This provides real-time updates without the 3-4 second delay
_locationStreamSubscription = Geolocator.getPositionStream(
locationSettings: const LocationSettings(
accuracy: LocationAccuracy.high,
distanceFilter: 2, // Update every 2 meters
),
).listen(
(Position position) {
_handleLocationUpdate(position);
},
onError: (error) {
Log.print("DEBUG: Location stream error: $error");
},
);
}
bool _isTicking = false;
Future<void> _tick() async {
if (_isTicking) return;
_isTicking = true;
try {
final position = await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.high);
final newLoc = LatLng(position.latitude, position.longitude);
currentSpeed = position.speed * 3.6;
bool _isProcessing = false;
Future<void> _handleLocationUpdate(Position position) async {
if (_isProcessing) return;
_isProcessing = true;
try {
final newLoc = LatLng(position.latitude, position.longitude);
currentSpeed = position.speed * 3.6; // Convert m/s to km/h
// Skip if movement is too small
if (_lastProcessedLocation != null) {
final d = Geolocator.distanceBetween(
newLoc.latitude,
@@ -498,11 +516,16 @@ class NavigationController extends GetxController
_lastProcessedLocation!.latitude,
_lastProcessedLocation!.longitude,
);
if (d < _minMoveToProcess) return;
if (d < _minMoveToProcess) {
_isProcessing = false;
return;
}
}
Log.print(
"DEBUG: Location tick - Speed: ${currentSpeed.toStringAsFixed(1)} km/h, Loc: $newLoc");
Log.print(
"DEBUG: Location update - Speed: ${currentSpeed.toStringAsFixed(1)} km/h, Loc: $newLoc");
// Update total distance
if (_lastDistanceLocation != null) {
final d = Geolocator.distanceBetween(
_lastDistanceLocation!.latitude,
@@ -542,9 +565,9 @@ class NavigationController extends GetxController
}
update();
} catch (e) {
Log.print("DEBUG: Error in _tick: $e");
Log.print("DEBUG: Error in _handleLocationUpdate: $e");
} finally {
_isTicking = false;
_isProcessing = false;
}
}
@@ -581,7 +604,8 @@ class NavigationController extends GetxController
if (elapsed >= _offRouteTriggerSeconds) {
_offRouteStartTime = null;
_autoRecalcInProgress = true;
recalculateRoute().then((_) => _autoRecalcInProgress = false);
// Smart reroute: check if we have alternative routes available
_smartRecalculateRoute(pos);
}
}
} else {
@@ -589,6 +613,48 @@ class NavigationController extends GetxController
}
}
/// الحل الذكي: إذا كان هناك مسارات بديلة متاحة، اختر الأقرب.
/// وإلا فاطلب مسار جديد من الموقع الحالي إلى الوجهة.
Future<void> _smartRecalculateRoute(LatLng currentPos) async {
try {
// Check if we have alternative routes
if (routes.isNotEmpty && selectedRouteIndex < routes.length - 1) {
// Try using the next alternative route
final nextIndex = selectedRouteIndex + 1;
final nextRoute = routes[nextIndex];
// Calculate distance from current position to this alternative route's start
double minDist = double.infinity;
for (var coord in nextRoute.coordinates) {
final d = Geolocator.distanceBetween(
currentPos.latitude,
currentPos.longitude,
coord.latitude,
coord.longitude,
);
if (d < minDist) minDist = d;
}
// If this alternative is reasonable, switch to it
if (minDist < 100) {
selectRoute(nextIndex);
Log.print("DEBUG: Switched to alternative route due to deviation");
_autoRecalcInProgress = false;
return;
}
}
// No good alternative, recalculate from current position to destination
if (_finalDestination != null) {
await recalculateRoute();
}
_autoRecalcInProgress = false;
} catch (e) {
Log.print("DEBUG: Error in smart recalculate: $e");
_autoRecalcInProgress = false;
}
}
void _startBatchTimers() {
_recordTimer?.cancel();
_uploadBatchTimer?.cancel();
@@ -851,6 +917,7 @@ class NavigationController extends GetxController
'toLat': destination.latitude.toString(),
'toLng': destination.longitude.toString(),
'steps': 'true',
'alternatives': 'true',
'locale': langCode,
};
@@ -890,7 +957,9 @@ class NavigationController extends GetxController
}
// ── Parse alternative routes (in data['alternatives']) ──
// إذا كان هناك routes بديلة متاحة من API
if (data['alternatives'] != null && data['alternatives'] is List) {
_hasAlternativeRoutes = data['alternatives'].isNotEmpty;
for (var alt in data['alternatives']) {
final altPts = alt['points']?.toString() ?? "";
if (altPts.isEmpty) continue;
@@ -904,6 +973,11 @@ class NavigationController extends GetxController
points: altPts,
));
}
if (_hasAlternativeRoutes) {
Log.print("DEBUG: ${routes.length - 1} alternative routes available");
}
} else {
_hasAlternativeRoutes = false;
}
if (routes.isEmpty) {
@@ -1053,6 +1127,48 @@ class NavigationController extends GetxController
update();
}
Future<void> startActiveNavigation() async {
if (routes.isEmpty) {
mySnackbarWarning(box.read(BoxName.lang) == 'ar'
? 'لا يوجد مسار لبدء الملاحة.'
: 'No route to start navigation.');
return;
}
if (isNavigating) return;
isNavigating = true;
_cameraLockedToUser = true;
// Ensure ETA and distances are up-to-date
_lastTraveledIndexInFullRoute = _lastTraveledIndexInFullRoute;
_recomputeETA();
// Initialize current instruction if available
if (routeSteps.isNotEmpty && currentStepIndex < routeSteps.length) {
currentInstruction = routeSteps[currentStepIndex]['text'] ?? "";
currentManeuverModifier = routeSteps[currentStepIndex]['sign'] ?? 0;
nextInstruction = (currentStepIndex + 1) < routeSteps.length
? (box.read(BoxName.lang) == 'ar'
? "ثم ${routeSteps[currentStepIndex + 1]['text']}"
: "Then ${routeSteps[currentStepIndex + 1]['text']}")
: (box.read(BoxName.lang) == 'ar' ? 'الوجهة' : 'Destination');
if (!isMuted) {
try {
Get.find<TextToSpeechController>().speakText(currentInstruction);
} catch (_) {}
}
}
// Center camera on user for navigation mode
if (myLocation != null) {
animateCameraToPosition(myLocation!,
bearing: _smoothedHeading, zoom: _targetZoom, tilt: _targetTilt);
}
update();
}
Future<void> clearEverything() async {
placeDestinationController.clear();
placesDestination = [];
@@ -1077,14 +1193,14 @@ class NavigationController extends GetxController
_lastTraveledIndexInFullRoute = 0;
currentInstruction = "";
nextInstruction = "";
currentManeuverModifier = "straight";
currentManeuverModifier = "intaleq";
distanceToNextStep = "";
totalDistanceRemaining = "";
estimatedTimeRemaining = "";
arrivalTime = "--:--";
_routeTotalDistanceM = 0;
_routeTotalDurationS = 0;
if (!isNewRoute) {
await _updateCarMarker();
}
@@ -1169,38 +1285,23 @@ class NavigationController extends GetxController
update();
return;
}
if (myLocation == null) return;
final lat = myLocation!.latitude;
final lng = myLocation!.longitude;
const radiusKm = 200.0;
final payload = {
'query': q,
'lat_min': (lat - _kmToLatDelta(radiusKm)).toString(),
'lat_max': (lat + _kmToLatDelta(radiusKm)).toString(),
'lng_min': (lng - _kmToLngDelta(radiusKm, lat)).toString(),
'lng_max': (lng + _kmToLngDelta(radiusKm, lat)).toString(),
};
if (mapController == null) return;
try {
final response =
await CRUD().post(link: AppLink.getPlacesSyria, payload: payload);
List list;
if (response is Map && response['status'] == 'success') {
list = List.from(response['message'] as List);
} else if (response is List)
list = List.from(response);
else
return;
for (final p in list) {
final plat = double.tryParse(p['latitude']?.toString() ?? '0') ?? 0.0;
final plng = double.tryParse(p['longitude']?.toString() ?? '0') ?? 0.0;
p['distanceKm'] = _haversineKm(lat, lng, plat, plng);
// ✅ Use searchPlaces from intaleq_maps SDK
final results = await mapController!.searchPlaces(q);
if (myLocation != null) {
for (final p in results) {
final plat = double.tryParse(p['latitude']?.toString() ?? '0') ?? 0.0;
final plng = double.tryParse(p['longitude']?.toString() ?? '0') ?? 0.0;
p['distanceKm'] = _haversineKm(myLocation!.latitude, myLocation!.longitude, plat, plng);
}
results.sort((a, b) =>
(a['distanceKm'] as double).compareTo(b['distanceKm'] as double));
}
list.sort((a, b) =>
(a['distanceKm'] as double).compareTo(b['distanceKm'] as double));
placesDestination = list;
placesDestination = results;
update();
} catch (e) {
Log.print('getPlaces error: $e');
@@ -1218,7 +1319,7 @@ class NavigationController extends GetxController
void onSearchChanged(String query) {
if (_debounce?.isActive ?? false) _debounce!.cancel();
_debounce = Timer(const Duration(milliseconds: 700), () => getPlaces());
_debounce = Timer(const Duration(milliseconds: 500), () => getPlaces());
}
double _haversineKm(double lat1, double lon1, double lat2, double lon2) {

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff