new change to use intaleq_map sdk 04-16-4

This commit is contained in:
Hamza-Ayed
2026-04-16 19:45:03 +03:00
parent 0aa1f15f25
commit a54a7a4189
850 changed files with 83282 additions and 3075 deletions

View File

@@ -107,4 +107,5 @@ class BoxName {
static const String recentLocations = 'recentLocations';
static const String tripData = 'tripData';
static const String parentTripSelected = 'parentTripSelected';
static const String styleVersion = 'styleVersion';
}

View File

@@ -91,17 +91,47 @@ class CountryPolygons {
];
// دالة تُرجع رابط API بناءً على اسم الدولة
static String getRoutingApiUrl(String countryName) {
switch (countryName) {
case 'Jordan':
return 'https://routec.intaleq.xyz/route-jo';
case 'Syria':
return 'https://routec.intaleq.xyz/route';
case 'Egypt':
return 'https://routec.intaleq.xyz/route-eg';
default:
// الافتراضي في حالة لم يقع الموقع ضمن أي من المضلعات
return 'https://routec.intaleq.xyz/route';
// static String getRoutingApiUrl(String countryName) {
// switch (countryName) {
// case 'Jordan':
// return 'https://routec.intaleq.xyz/route-jo';
// case 'Syria':
// return 'https://routec.intaleq.xyz/route';
// case 'Egypt':
// return 'https://routec.intaleq.xyz/route-eg';
// default:
// // الافتراضي في حالة لم يقع الموقع ضمن أي من المضلعات
// return 'https://routec.intaleq.xyz/route';
// }
// }
/// دالة تحدد اسم الدولة (باللغة الإنجليزية للـ API) بناءً على الإحداثيات
static String getCountryName(LatLng? point) {
if (point == null) return "jordan";
if (_isPointInPolygon(point, jordanBoundary)) return "jordan";
if (_isPointInPolygon(point, syriaBoundary)) return "syria";
if (_isPointInPolygon(point, egyptBoundary)) return "egypt";
return "jordan"; // الافتراضي
}
/// خوارزمية Ray Casting للتحقق من وقوع نقطة داخل مضلع
static bool _isPointInPolygon(LatLng p, List<LatLng> polygon) {
bool isInside = false;
int j = polygon.length - 1;
for (int i = 0; i < polygon.length; i++) {
if (((polygon[i].latitude > p.latitude) !=
(polygon[j].latitude > p.latitude)) &&
(p.longitude <
(polygon[j].longitude - polygon[i].longitude) *
(p.latitude - polygon[i].latitude) /
(polygon[j].latitude - polygon[i].latitude) +
polygon[i].longitude)) {
isInside = !isInside;
}
j = i;
}
return isInside;
}
}

View File

@@ -663,6 +663,8 @@ class CRUD {
'x-api-key': Env.mapSaasKey,
},
);
Log.print('link -MapSaas: $link');
Log.print('response -MapSaas: ${response.body}');
if (response.statusCode == 200) {
return jsonDecode(response.body);
}

View File

@@ -32,12 +32,14 @@ import 'package:maplibre_gl/maplibre_gl.dart';
// import 'package:google_polyline_algorithm/google_polyline_algorithm.dart';
import 'package:intl/intl.dart';
import 'package:location/location.dart';
import 'package:Intaleq/constant/colors.dart';
import 'package:Intaleq/constant/country_polygons.dart';
import 'package:Intaleq/constant/links.dart';
import 'package:Intaleq/constant/style.dart';
import 'package:Intaleq/controller/home/points_for_rider_controller.dart';
import 'package:Intaleq/views/home/map_widget.dart/form_serch_multiy_point.dart';
import '../../constant/api_key.dart';
import '../../constant/box_name.dart';
import '../../constant/colors.dart';
import '../../constant/country_polygons.dart';
import '../../constant/info.dart';
import '../../constant/links.dart';
@@ -3276,7 +3278,9 @@ class MapPassengerController extends GetxController {
"step3": placesCoordinate.length > 3 ? placesCoordinate[3] : "",
"step4": placesCoordinate.length > 4 ? placesCoordinate[4] : "",
};
Log.print('payload add_ride: $payload');
Log.print(
'🏁 Ride Registration Detail: $startNameAddress -> $endNameAddress');
Log.print(' 📦 Payload: $payload');
try {
// الاتصال بـ add_ride.php
@@ -4859,7 +4863,7 @@ Intaleq Team''';
}
// 4. التخلص من متحكم الخريطة (ممارسة جيدة)
mapController?.dispose();
mapController = null;
Log.print("--- Cleanup complete. ---");
super.onClose();
@@ -5307,27 +5311,44 @@ Intaleq Team''';
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=45000&country=syria';
'${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']);
List results = List.from(response['results']);
final List filteredResults = [];
final Set<String> seenPlaces = {};
for (final p in list) {
// Normalize fields to match expected format in components
p['distanceKm'] = (p['distance'] as num).toDouble() / 1000.0;
// Ensure latitude/longitude are strings if UI expects them that way,
// though modern code usually prefers doubles.
// The old code used double.tryParse(p['latitude']?.toString() ?? '0.0')
p['latitude'] = p['latitude'].toString();
p['longitude'] = p['longitude'].toString();
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 = list;
Log.print('Updated places: $placesDestination');
placesDestination = filteredResults;
update();
}
} catch (e) {
@@ -5533,23 +5554,26 @@ Intaleq Team''';
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=200000&country=syria';
'${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;
Log.print('Updated places start: $placesStart');
update();
}
} catch (e) {
@@ -5557,34 +5581,37 @@ Intaleq Team''';
}
}
Future getPlacesListsWayPoint(int index) async {
var languageCode = wayPoint0Controller.text;
Future<void> getPlacesListsWayPoint(int index) async {
final q = wayPoint0Controller.text.trim();
if (q.length < 3) return;
// Regular expression to check for English alphabet characters
final englishRegex = RegExp(r'[a-zA-Z]');
// Check if text contains English characters
if (englishRegex.hasMatch(languageCode)) {
languageCode = 'en';
} else {
languageCode = 'ar';
}
var url =
'${AppLink.googleMapsLink}place/nearbysearch/json?keyword=${wayPoint0Controller.text}&location=${passengerLocation.latitude},${passengerLocation.longitude}&radius=250000&language=$languageCode&key=${AK.mapAPIKEY.toString()}';
final lat = passengerLocation.latitude;
final lng = passengerLocation.longitude;
final country = CountryPolygons.getCountryName(passengerLocation);
try {
var response = await CRUD().getGoogleApi(link: url, payload: {});
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'] != null) {
wayPoint0 = response['results'];
placeListResponseAll[index] = response['results'];
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();
} else {
Log.print('Error: Invalid response from Google Places API');
}
} catch (e) {
Log.print('Error fetching places: $e');
Log.print('Error fetching places in WayPoint: $e');
}
}
@@ -6292,7 +6319,7 @@ Intaleq Team''';
}
bool isDrawingRoute = false;
showDrawingBottomSheet() {
void showDrawingBottomSheet() {
final bool isDark = Get.isDarkMode;
final Color bgColor = isDark
? Colors.black.withOpacity(0.65)
@@ -6300,6 +6327,9 @@ Intaleq Team''';
final Color textColor = isDark ? Colors.white : Colors.grey.shade800;
final Color subtitleColor = isDark ? Colors.white70 : Colors.grey.shade600;
// Prevent showing multiple bottom sheets if one is already active
if (Get.isBottomSheetOpen == true) return;
Get.bottomSheet(
ClipRRect(
borderRadius: const BorderRadius.only(
@@ -6324,16 +6354,34 @@ Intaleq Team''';
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// Minimal handle
Container(
width: 40,
height: 4,
margin: const EdgeInsets.only(bottom: 24),
decoration: BoxDecoration(
color: isDark ? Colors.white24 : Colors.grey.shade300,
borderRadius: BorderRadius.circular(2),
),
// Minimal handle and Close button Row
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const SizedBox(width: 48), // Balance for the close button
Container(
width: 40,
height: 4,
decoration: BoxDecoration(
color: isDark ? Colors.white24 : Colors.grey.shade300,
borderRadius: BorderRadius.circular(2),
),
),
IconButton(
visualDensity: VisualDensity.compact,
icon: Icon(Icons.close,
color: textColor.withOpacity(0.5), size: 22),
onPressed: () {
// Force back to close the overlay regardless of GetX state check
Get.back();
isDrawingRoute = false;
isLoading = false;
update();
},
),
],
),
const SizedBox(height: 16),
Stack(
alignment: Alignment.center,
children: [
@@ -6538,26 +6586,10 @@ Intaleq Team''';
String pointsString;
dynamic routeData;
if (isSaaSRequest) {
// SaaS parsing
apiDistanceMeters = (responseData['distance'] as num).toDouble();
pointsString = responseData['points'] ?? "";
routeData = responseData; // For box storage
} else {
// OSRM parsing
if (responseData['routes'] == null || responseData['routes'].isEmpty) {
if (attemptCount < 2) {
await _retryProcess(origin, destination, waypoints, attemptCount);
return;
}
_handleFatalError("Route Not Found".tr,
"No routes available for this destination.".tr);
return;
}
routeData = responseData['routes'][0];
apiDistanceMeters = (routeData['distance'] as num).toDouble();
pointsString = routeData['geometry'] ?? "";
}
// SaaS parsing
apiDistanceMeters = (responseData['distance'] as num).toDouble();
pointsString = responseData['points'] ?? "";
routeData = responseData; // For box storage
var originCoords = origin.split(',');
double startLat = double.parse(originCoords[0]);
@@ -6613,21 +6645,14 @@ Intaleq Team''';
polylineCoordinates.clear();
polylineCoordinates.addAll(decodedPoints);
// ── 4. العناوين والتحديثات ──────────────────────────────────
final LatLng startLoc = polylineCoordinates.first;
final LatLng endLoc = polylineCoordinates.last;
try {
final results = await Future.wait([
getReverseGeocoding(startLoc),
getReverseGeocoding(endLoc),
]);
startNameAddress = results[0];
endNameAddress = results[1];
} catch (e) {
startNameAddress = 'Start Point'.tr;
endNameAddress = 'Destination'.tr;
}
// ── 4. العناوين والتحديثات ──────────────────────────────────
startNameAddress = responseData['startName'] ?? 'Start Point'.tr;
endNameAddress = responseData['endName'] ?? 'Destination'.tr;
Log.print('📍 ROUTE START: $startNameAddress');
Log.print('📍 ROUTE END: $endNameAddress');
// ── 5. Bounds Calculation (SaaS bbox vs OSRM manual) ──────────
if (isSaaSRequest && responseData['bbox'] != null) {
@@ -6863,7 +6888,7 @@ Intaleq Team''';
// -----------------------------------------------------------------------------------------
void _handleFatalError(String title, String message) {
// 1. إغلاق شاشة التحميل (Drawing route...)
if (isDrawingRoute || (Get.isBottomSheetOpen ?? false)) {
if (Get.isBottomSheetOpen == true || Get.isDialogOpen == true) {
Get.back();
}
@@ -8041,6 +8066,7 @@ Intaleq Team''';
@override
void onInit() async {
super.onInit();
_checkAndRefreshMapStyle(); // Verify style version and clear cache if needed
// // --- إضافة جديدة: تهيئة وحدة التحكم في الروابط العميقة ---
Get.put(DeepLinkController(), permanent: true);
// // ----------------------------------------------------
@@ -8189,6 +8215,34 @@ Intaleq Team''';
'The price may increase if the route changes.'.tr, 'ding');
}
}
/// Checks the current version of assets/style.json and purges the map cache if it has changed.
Future<void> _checkAndRefreshMapStyle() async {
try {
final String styleJson = await rootBundle.loadString('assets/style.json');
final Map<String, dynamic> decoded = json.decode(styleJson);
final String? currentVersion =
decoded['metadata'] != null ? decoded['metadata']['version'] : null;
if (currentVersion == null) return;
final String lastVersion = box.read(BoxName.styleVersion) ?? "0.0.0";
if (currentVersion != lastVersion) {
Log.print(
"♻️ Map Style Version mismatch ($lastVersion -> $currentVersion). Purging offline cache...");
await OfflineMapService.instance.clearCache();
// Final verification check: give native engine time to flush
await Future.delayed(const Duration(milliseconds: 500));
box.write(BoxName.styleVersion, currentVersion);
Log.print("✅ Style Version updated to $currentVersion");
}
} catch (e) {
Log.print("⚠️ Style version check failed: $e");
}
}
}
class CarLocation {

View File

@@ -119,7 +119,7 @@ class TripMonitorController extends GetxController {
@override
void onClose() {
timer.cancel();
mapController?.dispose();
mapController = null;
super.onClose();
}

View File

@@ -4,7 +4,7 @@ class Log {
Log._();
static void print(String value, {StackTrace? stackTrace}) {
// developer.log(value, name: 'LOG', stackTrace: stackTrace);
developer.log(value, name: 'LOG', stackTrace: stackTrace);
}
static Object? inspect(Object? object) {

View File

@@ -103,4 +103,19 @@ class OfflineMapService {
2;
return 12742 * math.asin(math.sqrt(a));
}
/// Clears all offline map regions and tiles from local storage
Future<void> clearCache() async {
try {
Log.print("♻️ Purging MapLibre Offline Cache...");
// In maplibre_gl 0.25.0, we use top-level functions instead of an OfflineManager class
final List<OfflineRegion> regions = await getListOfRegions();
for (var region in regions) {
await deleteOfflineRegion(region.id);
}
Log.print("✅ Map cache cleared successfully. ${regions.length} regions removed.");
} catch (e) {
Log.print("⚠️ Failed to clear map cache: $e");
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff