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<Circle> circles = {};
Set<Polygon> polygons = {}; Set<Polygon> polygons = {};
Timer? _locationUpdateTimer; StreamSubscription<Position>? _locationStreamSubscription;
LatLng? _lastProcessedLocation; LatLng? _lastProcessedLocation;
List<dynamic> placesDestination = []; List<dynamic> placesDestination = [];
Timer? _debounce; Timer? _debounce;
// Alternative route handling
bool _hasAlternativeRoutes = false;
DateTime? _lastAutoRerouteTime;
LatLng? _finalDestination; LatLng? _finalDestination;
LatLng? _intermediateStop; LatLng? _intermediateStop;
List<Map<String, dynamic>> routeSteps = []; List<Map<String, dynamic>> routeSteps = [];
@@ -301,7 +305,7 @@ class NavigationController extends GetxController
void onInit() { void onInit() {
super.onInit(); super.onInit();
_animController = AnimationController( _animController = AnimationController(
vsync: this, duration: const Duration(milliseconds: 3800)); vsync: this, duration: const Duration(milliseconds: 1000));
_animController!.addListener(() { _animController!.addListener(() {
if (_oldLoc != null && _targetLoc != null && _mapReady) { if (_oldLoc != null && _targetLoc != null && _mapReady) {
final t = _animController!.value; final t = _animController!.value;
@@ -360,7 +364,7 @@ class NavigationController extends GetxController
@override @override
void onClose() { void onClose() {
_locationUpdateTimer?.cancel(); _locationStreamSubscription?.cancel();
_recordTimer?.cancel(); _recordTimer?.cancel();
_uploadBatchTimer?.cancel(); _uploadBatchTimer?.cancel();
_debounce?.cancel(); _debounce?.cancel();
@@ -468,29 +472,43 @@ class NavigationController extends GetxController
_smoothedHeading = position.heading; _smoothedHeading = position.heading;
update(); update();
if (isStyleLoaded) animateCameraToPosition(myLocation!); if (isStyleLoaded) animateCameraToPosition(myLocation!);
_startLocationTimer(); // Start the Location Stream for real-time updates
_startLocationStream();
_startBatchTimers(); _startBatchTimers();
} catch (e) { } catch (e) {
Log.print("DEBUG: Error getting initial location: $e"); Log.print("DEBUG: Error getting initial location: $e");
} }
} }
void _startLocationTimer() { void _startLocationStream() {
_locationUpdateTimer?.cancel(); _locationStreamSubscription?.cancel();
_locationUpdateTimer = // Listen to location updates with minimum distance filter of 2 meters
Timer.periodic(const Duration(seconds: 4), (_) => _tick()); // 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; bool _isProcessing = false;
Future<void> _tick() async { Future<void> _handleLocationUpdate(Position position) async {
if (_isTicking) return; if (_isProcessing) return;
_isTicking = true; _isProcessing = true;
try {
final position = await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.high);
final newLoc = LatLng(position.latitude, position.longitude);
currentSpeed = position.speed * 3.6;
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) { if (_lastProcessedLocation != null) {
final d = Geolocator.distanceBetween( final d = Geolocator.distanceBetween(
newLoc.latitude, newLoc.latitude,
@@ -498,11 +516,16 @@ class NavigationController extends GetxController
_lastProcessedLocation!.latitude, _lastProcessedLocation!.latitude,
_lastProcessedLocation!.longitude, _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) { if (_lastDistanceLocation != null) {
final d = Geolocator.distanceBetween( final d = Geolocator.distanceBetween(
_lastDistanceLocation!.latitude, _lastDistanceLocation!.latitude,
@@ -542,9 +565,9 @@ class NavigationController extends GetxController
} }
update(); update();
} catch (e) { } catch (e) {
Log.print("DEBUG: Error in _tick: $e"); Log.print("DEBUG: Error in _handleLocationUpdate: $e");
} finally { } finally {
_isTicking = false; _isProcessing = false;
} }
} }
@@ -581,7 +604,8 @@ class NavigationController extends GetxController
if (elapsed >= _offRouteTriggerSeconds) { if (elapsed >= _offRouteTriggerSeconds) {
_offRouteStartTime = null; _offRouteStartTime = null;
_autoRecalcInProgress = true; _autoRecalcInProgress = true;
recalculateRoute().then((_) => _autoRecalcInProgress = false); // Smart reroute: check if we have alternative routes available
_smartRecalculateRoute(pos);
} }
} }
} else { } 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() { void _startBatchTimers() {
_recordTimer?.cancel(); _recordTimer?.cancel();
_uploadBatchTimer?.cancel(); _uploadBatchTimer?.cancel();
@@ -851,6 +917,7 @@ class NavigationController extends GetxController
'toLat': destination.latitude.toString(), 'toLat': destination.latitude.toString(),
'toLng': destination.longitude.toString(), 'toLng': destination.longitude.toString(),
'steps': 'true', 'steps': 'true',
'alternatives': 'true',
'locale': langCode, 'locale': langCode,
}; };
@@ -890,7 +957,9 @@ class NavigationController extends GetxController
} }
// ── Parse alternative routes (in data['alternatives']) ── // ── Parse alternative routes (in data['alternatives']) ──
// إذا كان هناك routes بديلة متاحة من API
if (data['alternatives'] != null && data['alternatives'] is List) { if (data['alternatives'] != null && data['alternatives'] is List) {
_hasAlternativeRoutes = data['alternatives'].isNotEmpty;
for (var alt in data['alternatives']) { for (var alt in data['alternatives']) {
final altPts = alt['points']?.toString() ?? ""; final altPts = alt['points']?.toString() ?? "";
if (altPts.isEmpty) continue; if (altPts.isEmpty) continue;
@@ -904,6 +973,11 @@ class NavigationController extends GetxController
points: altPts, points: altPts,
)); ));
} }
if (_hasAlternativeRoutes) {
Log.print("DEBUG: ${routes.length - 1} alternative routes available");
}
} else {
_hasAlternativeRoutes = false;
} }
if (routes.isEmpty) { if (routes.isEmpty) {
@@ -1053,6 +1127,48 @@ class NavigationController extends GetxController
update(); 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 { Future<void> clearEverything() async {
placeDestinationController.clear(); placeDestinationController.clear();
placesDestination = []; placesDestination = [];
@@ -1077,7 +1193,7 @@ class NavigationController extends GetxController
_lastTraveledIndexInFullRoute = 0; _lastTraveledIndexInFullRoute = 0;
currentInstruction = ""; currentInstruction = "";
nextInstruction = ""; nextInstruction = "";
currentManeuverModifier = "straight"; currentManeuverModifier = "intaleq";
distanceToNextStep = ""; distanceToNextStep = "";
totalDistanceRemaining = ""; totalDistanceRemaining = "";
estimatedTimeRemaining = ""; estimatedTimeRemaining = "";
@@ -1169,38 +1285,23 @@ class NavigationController extends GetxController
update(); update();
return; return;
} }
if (myLocation == null) return; if (mapController == 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(),
};
try { try {
final response = // ✅ Use searchPlaces from intaleq_maps SDK
await CRUD().post(link: AppLink.getPlacesSyria, payload: payload); final results = await mapController!.searchPlaces(q);
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) { if (myLocation != null) {
for (final p in results) {
final plat = double.tryParse(p['latitude']?.toString() ?? '0') ?? 0.0; final plat = double.tryParse(p['latitude']?.toString() ?? '0') ?? 0.0;
final plng = double.tryParse(p['longitude']?.toString() ?? '0') ?? 0.0; final plng = double.tryParse(p['longitude']?.toString() ?? '0') ?? 0.0;
p['distanceKm'] = _haversineKm(lat, lng, plat, plng); p['distanceKm'] = _haversineKm(myLocation!.latitude, myLocation!.longitude, plat, plng);
} }
list.sort((a, b) => results.sort((a, b) =>
(a['distanceKm'] as double).compareTo(b['distanceKm'] as double)); (a['distanceKm'] as double).compareTo(b['distanceKm'] as double));
placesDestination = list; }
placesDestination = results;
update(); update();
} catch (e) { } catch (e) {
Log.print('getPlaces error: $e'); Log.print('getPlaces error: $e');
@@ -1218,7 +1319,7 @@ class NavigationController extends GetxController
void onSearchChanged(String query) { void onSearchChanged(String query) {
if (_debounce?.isActive ?? false) _debounce!.cancel(); 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) { 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

View File

@@ -1135,10 +1135,11 @@ packages:
intaleq_maps: intaleq_maps:
dependency: "direct main" dependency: "direct main"
description: description:
path: "/Users/hamzaaleghwairyeen/development/App/map-saas/packages/flutter-sdk/" name: intaleq_maps
relative: false sha256: b74c4e6f1d890f81bf253c4d3996db53149b64fcbf9869b279d417067f73128b
source: path url: "https://pub.dev"
version: "2.1.3" source: hosted
version: "2.2.0"
internet_connection_checker: internet_connection_checker:
dependency: "direct main" dependency: "direct main"
description: description:

View File

@@ -81,8 +81,7 @@ dependencies:
internet_connection_checker: ^3.0.1 internet_connection_checker: ^3.0.1
connectivity_plus: ^6.1.5 connectivity_plus: ^6.1.5
app_links: ^7.0.0 app_links: ^7.0.0
intaleq_maps: intaleq_maps: ^2.2.0
path: /Users/hamzaaleghwairyeen/development/App/map-saas/packages/flutter-sdk/
socket_io_client: 1.0.2 socket_io_client: 1.0.2
# home_widget: ^0.7.0+1 # home_widget: ^0.7.0+1