final intaleq map 26-4-18-1
This commit is contained in:
@@ -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
1613
lib/views/home/navigation/navigation_view_old.dart
Normal file
1613
lib/views/home/navigation/navigation_view_old.dart
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1135,10 +1135,11 @@ packages:
|
||||
intaleq_maps:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "/Users/hamzaaleghwairyeen/development/App/map-saas/packages/flutter-sdk/"
|
||||
relative: false
|
||||
source: path
|
||||
version: "2.1.3"
|
||||
name: intaleq_maps
|
||||
sha256: b74c4e6f1d890f81bf253c4d3996db53149b64fcbf9869b279d417067f73128b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.0"
|
||||
internet_connection_checker:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
||||
@@ -81,8 +81,7 @@ dependencies:
|
||||
internet_connection_checker: ^3.0.1
|
||||
connectivity_plus: ^6.1.5
|
||||
app_links: ^7.0.0
|
||||
intaleq_maps:
|
||||
path: /Users/hamzaaleghwairyeen/development/App/map-saas/packages/flutter-sdk/
|
||||
intaleq_maps: ^2.2.0
|
||||
socket_io_client: 1.0.2
|
||||
# home_widget: ^0.7.0+1
|
||||
|
||||
|
||||
Reference in New Issue
Block a user