128 lines
4.2 KiB
Dart
128 lines
4.2 KiB
Dart
import 'dart:convert';
|
|
import 'package:flutter/foundation.dart';
|
|
import 'package:http/http.dart' as http;
|
|
import 'package:intaleq_maps/intaleq_maps.dart';
|
|
import '../config/app_config.dart';
|
|
|
|
class TacticalRouteResult {
|
|
final List<LatLng> points;
|
|
final double distanceKm;
|
|
final double durationMinutes;
|
|
final String profile;
|
|
|
|
TacticalRouteResult({
|
|
required this.points,
|
|
required this.distanceKm,
|
|
required this.durationMinutes,
|
|
required this.profile,
|
|
});
|
|
}
|
|
|
|
class TacticalApiService {
|
|
static String get defaultServerUrl => AppConfig.serverUrl;
|
|
static String get defaultApiKey => AppConfig.apiKey;
|
|
|
|
/// Calculate tactical route between waypoints using server routing engine
|
|
static Future<TacticalRouteResult?> calculateTacticalRoute({
|
|
required LatLng origin,
|
|
required LatLng destination,
|
|
String profile = 'car', // 'car', 'offroad', 'convoy', 'emergency'
|
|
String? serverUrl,
|
|
String? apiKey,
|
|
}) async {
|
|
final url = serverUrl ?? defaultServerUrl;
|
|
final key = apiKey ?? defaultApiKey;
|
|
try {
|
|
final uri = Uri.parse(
|
|
'$url/api/maps/route?fromLat=${origin.latitude}&fromLng=${origin.longitude}'
|
|
'&toLat=${destination.latitude}&toLng=${destination.longitude}'
|
|
'&profile=$profile&steps=true&locale=ar',
|
|
);
|
|
|
|
final resp = await http.get(uri, headers: {'x-api-key': key}).timeout(const Duration(seconds: 10));
|
|
|
|
if (resp.statusCode == 200) {
|
|
final data = jsonDecode(resp.body);
|
|
if (data['paths'] != null && (data['paths'] as List).isNotEmpty) {
|
|
final path = data['paths'][0];
|
|
final distanceMeters = (path['distance'] as num?)?.toDouble() ?? 0.0;
|
|
final timeMs = (path['time'] as num?)?.toDouble() ?? 0.0;
|
|
|
|
final coords = <LatLng>[];
|
|
if (path['points'] != null && path['points']['coordinates'] != null) {
|
|
final List<dynamic> coordList = path['points']['coordinates'];
|
|
for (final c in coordList) {
|
|
coords.add(LatLng((c[1] as num).toDouble(), (c[0] as num).toDouble()));
|
|
}
|
|
}
|
|
|
|
return TacticalRouteResult(
|
|
points: coords,
|
|
distanceKm: distanceMeters / 1000.0,
|
|
durationMinutes: timeMs / (1000.0 * 60.0),
|
|
profile: profile,
|
|
);
|
|
}
|
|
}
|
|
} catch (e) {
|
|
debugPrint('Tactical route API error: $e');
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/// Calculate Tactical Line of Sight (LOS) between Observer and Target
|
|
static Future<Map<String, dynamic>?> calculateLineOfSight({
|
|
required LatLng observer,
|
|
required LatLng target,
|
|
double observerHeight = 2.0,
|
|
double targetHeight = 2.0,
|
|
String? serverUrl,
|
|
String? apiKey,
|
|
}) async {
|
|
final url = serverUrl ?? defaultServerUrl;
|
|
final key = apiKey ?? defaultApiKey;
|
|
try {
|
|
final uri = Uri.parse(
|
|
'$url/api/tactical/line-of-sight?observerLat=${observer.latitude}&observerLng=${observer.longitude}'
|
|
'&targetLat=${target.latitude}&targetLng=${target.longitude}'
|
|
'&observerHeight=$observerHeight&targetHeight=$targetHeight',
|
|
);
|
|
|
|
final resp = await http.get(uri, headers: {'x-api-key': key}).timeout(const Duration(seconds: 10));
|
|
|
|
if (resp.statusCode == 200) {
|
|
return jsonDecode(resp.body);
|
|
}
|
|
} catch (e) {
|
|
debugPrint('Tactical LOS API error: $e');
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/// Calculate Tactical Isochrone Reachability Polygons
|
|
static Future<Map<String, dynamic>?> calculateIsochrone({
|
|
required LatLng center,
|
|
List<int> timeBuckets = const [300, 600, 900], // 5, 10, 15 minutes
|
|
String? serverUrl,
|
|
String? apiKey,
|
|
}) async {
|
|
final url = serverUrl ?? defaultServerUrl;
|
|
final key = apiKey ?? defaultApiKey;
|
|
try {
|
|
final timesStr = timeBuckets.join(',');
|
|
final uri = Uri.parse(
|
|
'$url/api/tactical/isochrone?lat=${center.latitude}&lng=${center.longitude}×=$timesStr&profile=emergency',
|
|
);
|
|
|
|
final resp = await http.get(uri, headers: {'x-api-key': key}).timeout(const Duration(seconds: 10));
|
|
|
|
if (resp.statusCode == 200) {
|
|
return jsonDecode(resp.body);
|
|
}
|
|
} catch (e) {
|
|
debugPrint('Tactical Isochrone API error: $e');
|
|
}
|
|
return null;
|
|
}
|
|
}
|