Files
intaleq/lib/controller/home/map/location_search_controller.dart

1053 lines
34 KiB
Dart

import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:math' show cos, pi, max, min, atan2, sin, sqrt;
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:geolocator/geolocator.dart';
import 'package:get/get.dart';
import 'package:intaleq_maps/intaleq_maps.dart';
import 'package:location/location.dart';
import 'package:http/http.dart' as http;
import '../../../constant/box_name.dart';
import '../../../constant/links.dart';
import '../../../constant/colors.dart';
import '../../../constant/style.dart';
import '../../../constant/table_names.dart';
import '../../../main.dart'; // contains global 'box', 'sql'
import '../../../print.dart';
import '../../../services/offline_map_service.dart';
import '../../functions/crud.dart';
import '../points_for_rider_controller.dart';
import '../../../views/home/map_widget.dart/form_serch_multiy_point.dart';
import '../../../views/widgets/error_snakbar.dart';
import 'map_engine_controller.dart';
import '../deep_link_controller.dart';
import 'ride_lifecycle_controller.dart';
import 'ride_state.dart';
import '../../../constant/country_polygons.dart';
import '../../../constant/univeries_polygon.dart';
class LocationSearchController extends GetxController {
List<Map<String, dynamic>> waypoints = [];
String passengerLocationStringUnvirsity = 'Not in University';
bool isInUniversity = false;
List<String> coordinatesWithoutEmpty = [];
LatLng? latestPosition;
List<LatLng> polylineCoordinates0 = [];
List<LatLng> polylineCoordinates1 = [];
List<LatLng> polylineCoordinates2 = [];
List<LatLng> polylineCoordinates3 = [];
List<LatLng> polylineCoordinates4 = [];
late List<List<LatLng>> polylineCoordinatesPointsAll = [
polylineCoordinates0,
polylineCoordinates1,
polylineCoordinates2,
polylineCoordinates3,
polylineCoordinates4,
];
bool isLoading = true;
TextEditingController placeDestinationController = TextEditingController();
TextEditingController placeStartController = TextEditingController();
TextEditingController wayPoint0Controller = TextEditingController();
TextEditingController wayPoint1Controller = TextEditingController();
TextEditingController wayPoint2Controller = TextEditingController();
TextEditingController wayPoint3Controller = TextEditingController();
TextEditingController wayPoint4Controller = TextEditingController();
TextEditingController whatsAppLocationText = TextEditingController();
final sosFormKey = GlobalKey<FormState>();
List<LatLng> bounds = [];
List placesStart = [];
List<TextEditingController> allTextEditingPlaces = [];
List placesDestination = [];
List wayPoint0 = [];
List wayPoint1 = [];
List wayPoint2 = [];
List wayPoint3 = [];
List wayPoint4 = [];
List<List<dynamic>> placeListResponseAll = [];
List<Widget> placeListResponse = [];
PermissionStatus? permissionGranted;
final location = Location();
double speed = 0;
late LatLng passengerLocation = const LatLng(32, 34);
late LatLng newMyLocation = const LatLng(32.115295, 36.064773);
late LatLng newStartPointLocation = const LatLng(32.115295, 36.064773);
late LatLng newPointLocation0 = const LatLng(32.115295, 36.064773);
late LatLng newPointLocation1 = const LatLng(32.115295, 36.064773);
late LatLng newPointLocation2 = const LatLng(32.115295, 36.064773);
late LatLng newPointLocation3 = const LatLng(32.115295, 36.064773);
late LatLng newPointLocation4 = const LatLng(32.115295, 36.064773);
late LatLng myDestination = const LatLng(32, 34);
bool startLocationFromMap = false;
bool passengerStartLocationFromMap = false;
bool workLocationFromMap = false;
bool homeLocationFromMap = false;
bool isPassengerRideLocationWidget = false;
bool startLocationFromMap0 = false;
bool startLocationFromMap1 = false;
bool startLocationFromMap2 = false;
bool startLocationFromMap3 = false;
bool startLocationFromMap4 = false;
List startLocationFromMapAll = [];
bool currentLocationToFormPlaces = false;
bool currentLocationToFormPlaces0 = false;
bool currentLocationToFormPlaces1 = false;
bool currentLocationToFormPlaces2 = false;
bool currentLocationToFormPlaces3 = false;
bool currentLocationToFormPlaces4 = false;
List currentLocationToFormPlacesAll = [];
// Multi-Waypoint (max 2 stops)
List<LatLng?> menuWaypoints = [null, null];
List<String> menuWaypointNames = ['', ''];
int activeMenuWaypointCount = 0;
bool isPickingWaypoint = false;
int pickingWaypointIndex = -1;
int wayPointIndex = 0;
String hintTextStartPoint = 'Search for your Start point'.tr;
String hintTextwayPoint0 = 'Search for waypoint'.tr;
String hintTextwayPoint1 = 'Search for waypoint'.tr;
String hintTextwayPoint2 = 'Search for waypoint'.tr;
String hintTextwayPoint3 = 'Search for waypoint'.tr;
String hintTextwayPoint4 = 'Search for waypoint'.tr;
String currentLocationString = 'Current Location'.tr;
String currentLocationString0 = 'Current Location'.tr;
String currentLocationString1 = 'Add Location 1'.tr;
String currentLocationString2 = 'Add Location 2'.tr;
String currentLocationString3 = 'Add Location 3'.tr;
String currentLocationString4 = 'Add Location 4'.tr;
String placesCoordinate0 = ''.tr;
String placesCoordinate1 = ''.tr;
String placesCoordinate2 = ''.tr;
String placesCoordinate3 = ''.tr;
String placesCoordinate4 = ''.tr;
List<String> currentLocationStringAll = [];
List<String> hintTextwayPointStringAll = [];
var placesCoordinate = <String>[];
String hintTextDestinationPoint = 'Select your destination'.tr;
late String startNameAddress = '';
late String endNameAddress = '';
List<Map<String, dynamic>> stopPoints = [];
double latitudeWhatsApp = 0;
double longitudeWhatsApp = 0;
late List recentPlaces = [];
Timer? _camThrottle;
final DeepLinkController _deepLinkController =
Get.isRegistered<DeepLinkController>()
? Get.find<DeepLinkController>()
: Get.put(DeepLinkController());
@override
void onInit() {
super.onInit();
placeListResponse = [
formSearchPlaces(0),
formSearchPlaces(1),
formSearchPlaces(2),
formSearchPlaces(3),
];
readyWayPoints();
getLocation();
}
void readyWayPoints() {
hintTextwayPointStringAll = [
hintTextwayPoint0,
hintTextwayPoint1,
hintTextwayPoint2,
hintTextwayPoint3,
hintTextwayPoint4,
];
allTextEditingPlaces = [
wayPoint0Controller,
wayPoint1Controller,
wayPoint2Controller,
wayPoint3Controller,
wayPoint4Controller,
];
currentLocationToFormPlacesAll = [
currentLocationToFormPlaces0,
currentLocationToFormPlaces1,
currentLocationToFormPlaces2,
currentLocationToFormPlaces3,
currentLocationToFormPlaces4,
];
placeListResponseAll = [
wayPoint0,
wayPoint1,
wayPoint2,
wayPoint3,
wayPoint4
];
startLocationFromMapAll = [
startLocationFromMap0,
startLocationFromMap1,
startLocationFromMap2,
startLocationFromMap3,
startLocationFromMap4,
];
currentLocationStringAll = [
currentLocationString0,
currentLocationString1,
currentLocationString2,
currentLocationString3,
currentLocationString4,
];
placesCoordinate = [
placesCoordinate0,
placesCoordinate1,
placesCoordinate2,
placesCoordinate3,
placesCoordinate4,
];
update();
}
void removeStop(Map<String, dynamic> stop) {
stopPoints.remove(stop);
update();
}
Future<void> savePlaceToServer(
String latitude, String longitude, String name, String rate) async {
var data = {
'latitude': latitude,
'longitude': longitude,
'name': name,
'rate': rate,
};
try {
CRUD().post(
link: AppLink.savePlacesServer,
payload: data,
);
} catch (e) {
Log.print('Error: $e');
}
}
Future<void> getLocation() async {
Log.print('🛰️ getLocation() called');
permissionGranted = await location.hasPermission();
if (permissionGranted == PermissionStatus.denied) {
permissionGranted = await location.requestPermission();
if (permissionGranted != PermissionStatus.granted) {
return;
}
}
// Pre-populate with last known position for instant loading
try {
Position? lastPosition = await Geolocator.getLastKnownPosition();
if (lastPosition != null) {
passengerLocation =
LatLng(lastPosition.latitude, lastPosition.longitude);
newStartPointLocation = passengerLocation;
newMyLocation = passengerLocation;
Log.print(
'📍 Pre-populated location from last known: $passengerLocation');
}
} catch (e) {
Log.print('⚠️ Error getting last known position: $e');
}
LocationData? _locationData;
try {
_locationData = await location.getLocation().timeout(
const Duration(seconds: 5),
onTimeout: () {
Log.print("⚠️ Location fetch timed out after 5s.");
return LocationData.fromMap({
"latitude": passengerLocation.latitude,
"longitude": passengerLocation.longitude,
"speed": 0.0
});
},
);
} catch (e) {
Log.print("⚠️ Error fetching location: $e");
}
if (_locationData == null) {
isLoading = false;
update();
return;
}
passengerLocation =
(_locationData.latitude != null && _locationData.longitude != null
? LatLng(_locationData.latitude!, _locationData.longitude!)
: const LatLng(32, 34));
newStartPointLocation = passengerLocation;
newMyLocation = passengerLocation;
if (Get.isRegistered<RideLifecycleController>()) {
final rideLifecycle = Get.find<RideLifecycleController>();
rideLifecycle.getLocationArea(
passengerLocation.latitude, passengerLocation.longitude);
rideLifecycle.resetNoRideSearch();
}
try {
getReverseGeocoding(passengerLocation).then((address) {
currentLocationString = address;
update();
});
} catch (e) {
Log.print('Error resolving current location: $e');
}
OfflineMapService.instance
.downloadRegion(passengerLocation, radiusKm: 10.0);
try {
final mapEngine = Get.find<MapEngineController>();
if (mapEngine.mapController != null) {
mapEngine.mapController!.animateCamera(
CameraUpdate.newLatLng(passengerLocation),
);
}
} catch (e) {
Log.print('Error animating camera to passenger location: $e');
}
speed = _locationData.speed ?? 0.0;
isLoading = false;
update();
}
void getCurrentLocationFormString() async {
currentLocationToFormPlaces = true;
currentLocationString = 'Waiting for your location'.tr;
await getLocation();
currentLocationString = passengerLocation.toString();
newStartPointLocation = passengerLocation;
update();
}
Future<void> getPlaces() async {
final q = placeDestinationController.text.trim();
if (q.isEmpty || q.length < 3) {
placesDestination = [];
update();
return;
}
final lat = passengerLocation.latitude;
final lng = passengerLocation.longitude;
final country = CountryPolygons.getCountryName(passengerLocation);
try {
final url =
'${AppLink.searchGeocoding}?q=${Uri.encodeComponent(q)}&lat=$lat&lng=$lng&radius=15000&country=$country';
final response = await CRUD().getMapSaas(link: url);
if (response != null && response['results'] is List) {
List results = List.from(response['results']);
final List filteredResults = [];
final Set<String> seenPlaces = {};
for (final p in results) {
final name = p['name_ar'] ?? p['name'] ?? '';
final district = p['district'] ?? '';
final plat = p['latitude']?.toString() ?? '0';
final plng = p['longitude']?.toString() ?? '0';
final dedupeKey =
"${name.trim().toLowerCase()}_${district.trim().toLowerCase()}";
if (!seenPlaces.contains(dedupeKey)) {
seenPlaces.add(dedupeKey);
p['distanceKm'] = (p['distance'] as num).toDouble() / 1000.0;
p['latitude'] = plat;
p['longitude'] = plng;
p['name'] = name;
p['address'] = p['full_address'] ??
(district.isNotEmpty
? "$district، ${p['governorate'] ?? ''}"
: (p['governorate'] ?? ''));
filteredResults.add(p);
}
}
placesDestination = filteredResults;
update();
}
} catch (e) {
Log.print('Exception in getPlaces: $e');
}
}
Future<void> getPlacesStart() async {
final q = placeStartController.text.trim();
if (q.isEmpty || q.length < 3) {
placesStart = [];
update();
return;
}
final lat = passengerLocation.latitude;
final lng = passengerLocation.longitude;
final country = CountryPolygons.getCountryName(passengerLocation);
try {
final url =
'${AppLink.searchGeocoding}?q=${Uri.encodeComponent(q)}&lat=$lat&lng=$lng&radius=15000&country=$country';
final response = await CRUD().getMapSaas(link: url);
if (response != null && response['results'] is List) {
List list = List.from(response['results']);
for (final p in list) {
p['distanceKm'] = (p['distance'] as num).toDouble() / 1000.0;
p['latitude'] = p['latitude'].toString();
p['longitude'] = p['longitude'].toString();
p['name'] = p['name_ar'] ?? p['name'] ?? '';
p['address'] = p['full_address'] ??
(p['district'] != null
? "${p['district']}، ${p['governorate'] ?? ''}"
: (p['governorate'] ?? ''));
}
placesStart = list;
update();
}
} catch (e) {
Log.print('Exception in getPlacesStart: $e');
}
}
Future<void> getPlacesListsWayPoint(int index) async {
final q = wayPoint0Controller.text.trim();
if (q.length < 3) return;
final lat = passengerLocation.latitude;
final lng = passengerLocation.longitude;
final country = CountryPolygons.getCountryName(passengerLocation);
try {
final url =
'${AppLink.searchGeocoding}?q=${Uri.encodeComponent(q)}&lat=$lat&lng=$lng&radius=15000&country=$country';
final response = await CRUD().getMapSaas(link: url);
if (response != null && response['results'] is List) {
List list = List.from(response['results']);
for (final p in list) {
p['distanceKm'] = (p['distance'] as num).toDouble() / 1000.0;
p['latitude'] = p['latitude'].toString();
p['longitude'] = p['longitude'].toString();
p['name'] = p['name_ar'] ?? p['name'] ?? '';
p['address'] = p['full_address'] ??
(p['district'] != null
? "${p['district']}، ${p['governorate'] ?? ''}"
: (p['governorate'] ?? ''));
}
wayPoint0 = list;
placeListResponseAll[index] = list;
update();
}
} catch (e) {
Log.print('Error fetching places in WayPoint: $e');
}
}
Future<String> getReverseGeocoding(LatLng location) async {
final lat = location.latitude;
final lng = location.longitude;
final url = '${AppLink.reverseGeocoding}?lat=$lat&lng=$lng';
try {
final response = await CRUD().getMapSaas(link: url);
if (response != null && response is List && response.isNotEmpty) {
final data = response[0];
String name = data['name_ar'] ?? data['name'] ?? 'Unknown Location'.tr;
return name;
}
return 'Unknown Location'.tr;
} catch (e) {
Log.print('ReverseGeocoding Exception: $e');
return 'Unknown Location'.tr;
}
}
void updateCurrentLocationFromCamera(LatLng target) {
Log.print('📍 updateCurrentLocationFromCamera: $target');
newMyLocation = target;
if (startLocationFromMap == true) {
Log.print('📍 Updating startLocationFromMap to $target');
newStartPointLocation = target;
} else if (passengerStartLocationFromMap == true) {
Log.print('📍 Updating passengerStartLocationFromMap to $target');
newStartPointLocation = target;
}
int waypointsLength = Get.find<WayPointController>().wayPoints.length;
if (waypointsLength > 0 &&
wayPointIndex >= 0 &&
wayPointIndex < placesCoordinate.length) {
Log.print('📍 Updating wayPointIndex $wayPointIndex to $target');
placesCoordinate[wayPointIndex] =
'${target.latitude},${target.longitude}';
}
if (Get.isRegistered<RideLifecycleController>()) {
final rideLifecycle = Get.find<RideLifecycleController>();
rideLifecycle.getLocationArea(target.latitude, target.longitude);
rideLifecycle.resetNoRideSearch();
}
update();
}
void onCameraMoveThrottled(CameraPosition pos) {
_camThrottle?.cancel();
_camThrottle = Timer(const Duration(milliseconds: 160), () {
Log.print('📸 onCameraMoveThrottled: ${pos.target}');
int waypointsLength = Get.find<WayPointController>().wayPoints.length;
int index = wayPointIndex;
if (waypointsLength > 0 && index < placesCoordinate.length) {
placesCoordinate[index] =
'${pos.target.latitude},${pos.target.longitude}';
}
newMyLocation = pos.target;
});
}
void convertHintTextPlaces(int index, var res) {
if (placeListResponseAll[index].isEmpty) {
placeListResponseAll[index] = res;
hintTextwayPointStringAll[index] = 'Search for your Start point'.tr;
update();
} else {
hintTextwayPointStringAll[index] = res['name'];
currentLocationStringAll[index] = res['name'];
placesCoordinate[index] =
'${res['geometry']['location']['lat']},${res['geometry']['location']['lng']}';
placeListResponseAll[index] = [];
allTextEditingPlaces[index].clear();
update();
Get.back();
}
}
void convertHintTextDestinationNewPlaces(int index) {
if (placesDestination.isEmpty) {
hintTextDestinationPoint = 'Search for your destination'.tr;
update();
} else {
var res = placesDestination[index];
hintTextDestinationPoint = res['displayName']?['text'] ??
res['formattedAddress'] ??
res['name'] ??
'Unknown Place';
double? lat = res['location']?['latitude'] ??
double.tryParse(res['latitude']?.toString() ?? '');
double? lng = res['location']?['longitude'] ??
double.tryParse(res['longitude']?.toString() ?? '');
if (lat != null && lng != null) {
newMyLocation = LatLng(lat, lng);
final mapEngine = Get.find<MapEngineController>();
mapEngine.mapController
?.animateCamera(CameraUpdate.newLatLngZoom(newMyLocation, 16));
}
update();
}
}
void convertHintTextDestinationNewPlacesFromRecent(
List recentLocations, int index) {
hintTextDestinationPoint = recentLocations[index]['name'];
double lat = recentLocations[index]['latitude'];
double lng = recentLocations[index]['longitude'];
newMyLocation = LatLng(lat, lng);
final mapEngine = Get.find<MapEngineController>();
mapEngine.mapController
?.animateCamera(CameraUpdate.newLatLngZoom(newMyLocation, 16));
update();
}
void clearPlacesDestination() {
placesDestination = [];
hintTextDestinationPoint = 'Search for your destination'.tr;
update();
}
void clearPlacesStart() {
placesStart = [];
hintTextStartPoint = 'Search for your Start point'.tr;
update();
}
void clearPlaces(int index) {
placeListResponseAll[index] = [];
hintTextwayPointStringAll[index] = 'Search for waypoint'.tr;
update();
}
Future<Map<String, double>?> extractCoordinatesFromLinkAsync(
String link) async {
try {
if (link.startsWith('geo:') || link.startsWith('google.navigation:')) {
RegExp regex = RegExp(r'(-?\d+\.\d+)[,/~=](-?\d+\.\d+)');
var match = regex.firstMatch(link);
if (match != null) {
double lat = double.parse(match.group(1)!);
double lng = double.parse(match.group(2)!);
if (lat > 40 && lat > lng) {
double temp = lat;
lat = lng;
lng = temp;
}
return {'latitude': lat, 'longitude': lng};
}
}
int urlStartIndex = link.indexOf(RegExp(r'https?://'));
if (urlStartIndex == -1) return null;
String cleanLink = link.substring(urlStartIndex).trim();
Uri uri = Uri.parse(cleanLink);
String finalUrl = cleanLink;
if (cleanLink.contains('goo.gl') ||
cleanLink.contains('maps.google.com')) {
try {
var response =
await http.get(uri).timeout(const Duration(seconds: 5));
finalUrl = response.request?.url.toString() ?? cleanLink;
} catch (e) {
Log.print('Redirect logic failed, using original: $e');
}
}
RegExp regex = RegExp(r'(-?\d+\.\d+)[,/~](-?\d+\.\d+)');
var match = regex.firstMatch(finalUrl);
if (match != null) {
double lat = double.parse(match.group(1)!);
double lng = double.parse(match.group(2)!);
if (lat > 40 && lat > lng) {
Log.print("⚠️ Detected Swapped Coordinates in Link. Correcting...");
double temp = lat;
lat = lng;
lng = temp;
}
return {
'latitude': lat,
'longitude': lng,
};
}
} catch (e) {
Log.print('Error parsing location link: $e');
}
return null;
}
void handleWhatsAppLink(String link) async {
Map<String, double>? coordinates =
await extractCoordinatesFromLinkAsync(link);
if (coordinates != null) {
latitudeWhatsApp = coordinates['latitude']!;
longitudeWhatsApp = coordinates['longitude']!;
Log.print(
'Extracted coordinates: Lat: $latitudeWhatsApp, Long: $longitudeWhatsApp');
} else {
Log.print('Failed to extract coordinates from the link');
}
}
void goToWhatappLocation() async {
if (sosFormKey.currentState!.validate()) {
Map<String, double>? coordinates =
await extractCoordinatesFromLinkAsync(whatsAppLocationText.text);
if (coordinates != null) {
latitudeWhatsApp = coordinates['latitude']!;
longitudeWhatsApp = coordinates['longitude']!;
Log.print(
'📍 Final Coordinates for OSM: Lat: $latitudeWhatsApp, Lng: $longitudeWhatsApp');
final mapEngine = Get.find<MapEngineController>();
mapEngine.changeIsWhatsAppOrder(true);
Get.back();
myDestination = LatLng(latitudeWhatsApp, longitudeWhatsApp);
if (passengerLocation != null) {
await mapEngine.mapController?.animateCamera(CameraUpdate.newLatLng(
LatLng(passengerLocation.latitude, passengerLocation.longitude)));
}
mapEngine.changeMainBottomMenuMap();
passengerStartLocationFromMap = true;
mapEngine.isPickerShown = true;
update();
} else {
mySnackbarWarning('لم نتمكن من استخراج الموقع من الرابط');
}
}
}
void addMenuWaypoint() {
if (activeMenuWaypointCount >= 2) return;
activeMenuWaypointCount++;
final mapEngine = Get.find<MapEngineController>();
mapEngine.mainBottomMenuMapHeight =
Get.height * .6 + (activeMenuWaypointCount * 56);
update();
}
void removeMenuWaypoint(int index) {
if (index < 0 || index >= 2) return;
if (index == 0 && activeMenuWaypointCount == 2) {
menuWaypoints[0] = menuWaypoints[1];
menuWaypointNames[0] = menuWaypointNames[1];
}
menuWaypoints[activeMenuWaypointCount - 1] = null;
menuWaypointNames[activeMenuWaypointCount - 1] = '';
activeMenuWaypointCount--;
final mapEngine = Get.find<MapEngineController>();
mapEngine.mainBottomMenuMapHeight =
Get.height * .6 + (activeMenuWaypointCount * 56);
update();
}
void clearAllMenuWaypoints() {
menuWaypoints = [null, null];
menuWaypointNames = ['', ''];
activeMenuWaypointCount = 0;
isPickingWaypoint = false;
pickingWaypointIndex = -1;
update();
}
void startPickingWaypointOnMap(int index) {
pickingWaypointIndex = index;
isPickingWaypoint = true;
final mapEngine = Get.find<MapEngineController>();
mapEngine.isPickerShown = true;
mapEngine.heightPickerContainer = 150;
mapEngine.isMainBottomMenuMap = true;
mapEngine.mainBottomMenuMapHeight = Get.height * .22;
update();
}
void setMenuWaypointFromMap(int index, LatLng position) {
Log.print('📍 setMenuWaypointFromMap called: index=$index, pos=$position');
if (index < 0 || index >= 2) return;
menuWaypoints[index] = position;
menuWaypointNames[index] =
'${position.latitude.toStringAsFixed(4)}, ${position.longitude.toStringAsFixed(4)}';
isPickingWaypoint = false;
pickingWaypointIndex = -1;
final mapEngine = Get.find<MapEngineController>();
mapEngine.isPickerShown = false;
mapEngine.isMainBottomMenuMap = false;
mapEngine.mainBottomMenuMapHeight =
Get.height * .6 + (activeMenuWaypointCount * 56);
update();
}
void setMenuWaypointFromSearch(int index, LatLng pos, String name) {
if (index < 0 || index >= 2) return;
menuWaypoints[index] = pos;
menuWaypointNames[index] = name;
update();
}
String buildOsrmWaypointCoords() {
String coords = '';
for (int i = 0; i < activeMenuWaypointCount; i++) {
final wp = menuWaypoints[i];
if (wp != null) {
coords += ';${wp.longitude},${wp.latitude}';
}
}
return coords;
}
void changeHeightPointsPageForRider() {
final mapEngine = Get.find<MapEngineController>();
mapEngine.isPointsPageForRider = !mapEngine.isPointsPageForRider;
mapEngine.heightPointsPageForRider =
mapEngine.isPointsPageForRider == true ? Get.height : 0;
update();
}
getCoordinateFromMapWayPoints(int index) {
placesCoordinate[index] = newStartPointLocation.toString();
update();
}
void addWaypoint(Map<String, dynamic> placeDetails) {
waypoints.add(placeDetails);
update();
}
void removeWaypoint(int index) {
if (index >= 0 && index < waypoints.length) {
waypoints.removeAt(index);
update();
}
}
getFavioratePlaces() async {
recentPlaces = await sql.getCustomQuery(
'SELECT * FROM ${TableName.recentLocations} ORDER BY createdAt DESC');
update();
}
void _listenForDeepLink() {
ever(_deepLinkController.rawDeepLink, (String? link) async {
if (link != null && link.isNotEmpty) {
Log.print('📍 MapPassengerController processing link: $link');
Map<String, double>? coordinates =
await extractCoordinatesFromLinkAsync(link);
if (coordinates != null) {
double destLat = coordinates['latitude']!;
double destLng = coordinates['longitude']!;
myDestination = LatLng(destLat, destLng);
if (passengerLocation == null ||
(passengerLocation.latitude == 0 &&
passengerLocation.longitude == 0)) {
Log.print('⏳ Waiting for current location to calculate route...');
await getLocation();
}
if (passengerLocation != null) {
String originStr =
'${passengerLocation.latitude},${passengerLocation.longitude}';
String destStr = '$destLat,$destLng';
Log.print(
'🚀 Drawing route from Deep Link: $originStr to $destStr');
final mapEngine = Get.find<MapEngineController>();
mapEngine.clearPolyline();
waypoints.clear();
clearAllMenuWaypoints();
final rideLife = Get.find<RideLifecycleController>();
await rideLife.getDirectionMap(originStr, destStr);
mapEngine.isBottomSheetShown = true;
mapEngine.heightBottomSheetShown = 250;
update();
Get.snackbar(
'Location Received'.tr,
'Route and prices have been calculated successfully!'.tr,
backgroundColor: AppColor.greenColor,
colorText: Colors.white,
);
}
} else {
Log.print('⚠️ Could not extract valid coordinates from link: $link');
}
_deepLinkController.rawDeepLink.value = null;
}
});
if (_deepLinkController.rawDeepLink.value != null &&
_deepLinkController.rawDeepLink.value!.isNotEmpty) {
String link = _deepLinkController.rawDeepLink.value!;
_deepLinkController.rawDeepLink.value = null;
Future.delayed(const Duration(milliseconds: 500), () async {
Log.print(
'📍 MapPassengerController processing link (Cold Start): $link');
Map<String, double>? coordinates =
await extractCoordinatesFromLinkAsync(link);
if (coordinates != null) {
double destLat = coordinates['latitude']!;
double destLng = coordinates['longitude']!;
myDestination = LatLng(destLat, destLng);
if (passengerLocation == null ||
(passengerLocation.latitude == 0 &&
passengerLocation.longitude == 0)) {
await getLocation();
}
if (passengerLocation != null) {
String originStr =
'${passengerLocation.latitude},${passengerLocation.longitude}';
String destStr = '$destLat,$destLng';
final mapEngine = Get.find<MapEngineController>();
mapEngine.clearPolyline();
waypoints.clear();
clearAllMenuWaypoints();
final rideLife = Get.find<RideLifecycleController>();
await rideLife.getDirectionMap(originStr, destStr);
mapEngine.isBottomSheetShown = true;
mapEngine.heightBottomSheetShown = 250;
update();
}
}
});
}
}
// --- Polygon Math and University Check Methods ---
bool isPointInPolygon(LatLng point, List<LatLng> polygon) {
int intersections = 0;
for (int i = 0; i < polygon.length; i++) {
LatLng vertex1 = polygon[i];
LatLng vertex2 = polygon[(i + 1) % polygon.length];
if (_rayIntersectsSegment(point, vertex1, vertex2)) {
intersections++;
}
}
return intersections % 2 != 0;
}
bool _rayIntersectsSegment(LatLng point, LatLng vertex1, LatLng vertex2) {
double px = point.longitude;
double py = point.latitude;
double v1x = vertex1.longitude;
double v1y = vertex1.latitude;
double v2x = vertex2.longitude;
double v2y = vertex2.latitude;
if ((py < v1y && py < v2y) || (py > v1y && py > v2y)) {
return false;
}
double intersectX = v1x + (py - v1y) * (v2x - v1x) / (v2y - v1y);
return intersectX > px;
}
String checkPassengerLocation(LatLng passengerLocation,
List<List<LatLng>> universityPolygons, List<String> universityNames) {
for (int i = 0; i < universityPolygons.length; i++) {
if (isPointInPolygon(passengerLocation, universityPolygons[i])) {
isInUniversity = true;
return "Passenger is in ${universityNames[i]}";
}
}
return "Passenger is not in any university";
}
void getPassengerLocationUniversity() {
passengerLocationStringUnvirsity = checkPassengerLocation(
passengerLocation,
UniversitiesPolygons.universityPolygons,
UniversitiesPolygons.universityNames,
);
Log.print(passengerLocationStringUnvirsity);
}
void getMapPointsForAllMethods() async {
final mapEngine = Get.find<MapEngineController>();
final rideLife = Get.find<RideLifecycleController>();
mapEngine.clearPolyline();
mapEngine.isMarkersShown = false;
mapEngine.isWayPointStopsSheetUtilGetMap = false;
mapEngine.isWayPointSheet = false;
rideLife.durationToRide = 0;
rideLife.distanceOfDestination = 0;
mapEngine.wayPointSheetHeight = 0;
rideLife.remainingTime = 25;
rideLife.haveSteps = true;
// Filter out empty value
coordinatesWithoutEmpty =
placesCoordinate.where((coord) => coord.isNotEmpty).toList();
latestPosition = LatLng(
double.parse(coordinatesWithoutEmpty.last.split(',')[0]),
double.parse(coordinatesWithoutEmpty.last.split(',')[1]));
for (var i = 0; i < coordinatesWithoutEmpty.length; i++) {
if ((i + 1) < coordinatesWithoutEmpty.length) {
await rideLife.getMapPoints(
coordinatesWithoutEmpty[i].toString(),
coordinatesWithoutEmpty[i + 1].toString(),
i,
);
if (i == 0) {
startNameAddress = rideLife.data[0]['start_address'];
}
if (i == coordinatesWithoutEmpty.length - 1) {
endNameAddress = rideLife.data[0]['end_address'];
}
}
}
if (rideLife.haveSteps) {
String latestWaypoint =
placesCoordinate.lastWhere((coord) => coord.isNotEmpty);
update();
}
}
double _haversineKm(double lat1, double lon1, double lat2, double lon2) {
const double r = 6371.0;
double dLat = (lat2 - lat1) * pi / 180.0;
double dLon = (lon2 - lon1) * pi / 180.0;
double a = sin(dLat / 2) * sin(dLat / 2) +
cos(lat1 * pi / 180.0) *
cos(lat2 * pi / 180.0) *
sin(dLon / 2) *
sin(dLon / 2);
double c = 2 * atan2(sqrt(a), sqrt(1 - a));
return r * c;
}
double _kmToLatDelta(double km) {
return km / 111.0;
}
double _kmToLngDelta(double km, double lat) {
return km / (111.0 * cos(lat * pi / 180.0));
}
String _buildOsrmWaypointCoords() {
String coords = '';
for (int i = 0; i < activeMenuWaypointCount; i++) {
final wp = menuWaypoints[i];
if (wp != null) {
coords += ';${wp.longitude},${wp.latitude}';
}
}
return coords;
}
@override
void onClose() {
_camThrottle?.cancel();
super.onClose();
}
}