final intaleq map 26-4-18-1
This commit is contained in:
@@ -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,14 +1193,14 @@ class NavigationController extends GetxController
|
|||||||
_lastTraveledIndexInFullRoute = 0;
|
_lastTraveledIndexInFullRoute = 0;
|
||||||
currentInstruction = "";
|
currentInstruction = "";
|
||||||
nextInstruction = "";
|
nextInstruction = "";
|
||||||
currentManeuverModifier = "straight";
|
currentManeuverModifier = "intaleq";
|
||||||
distanceToNextStep = "";
|
distanceToNextStep = "";
|
||||||
totalDistanceRemaining = "";
|
totalDistanceRemaining = "";
|
||||||
estimatedTimeRemaining = "";
|
estimatedTimeRemaining = "";
|
||||||
arrivalTime = "--:--";
|
arrivalTime = "--:--";
|
||||||
_routeTotalDistanceM = 0;
|
_routeTotalDistanceM = 0;
|
||||||
_routeTotalDurationS = 0;
|
_routeTotalDurationS = 0;
|
||||||
|
|
||||||
if (!isNewRoute) {
|
if (!isNewRoute) {
|
||||||
await _updateCarMarker();
|
await _updateCarMarker();
|
||||||
}
|
}
|
||||||
@@ -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') {
|
if (myLocation != null) {
|
||||||
list = List.from(response['message'] as List);
|
for (final p in results) {
|
||||||
} else if (response is List)
|
final plat = double.tryParse(p['latitude']?.toString() ?? '0') ?? 0.0;
|
||||||
list = List.from(response);
|
final plng = double.tryParse(p['longitude']?.toString() ?? '0') ?? 0.0;
|
||||||
else
|
p['distanceKm'] = _haversineKm(myLocation!.latitude, myLocation!.longitude, plat, plng);
|
||||||
return;
|
}
|
||||||
|
results.sort((a, b) =>
|
||||||
for (final p in list) {
|
(a['distanceKm'] as double).compareTo(b['distanceKm'] as double));
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
list.sort((a, b) =>
|
|
||||||
(a['distanceKm'] as double).compareTo(b['distanceKm'] as double));
|
placesDestination = results;
|
||||||
placesDestination = list;
|
|
||||||
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
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:
|
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:
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user