Files
maps-saas/packages/tactical_app/lib/services/minefield_engine.dart
T

105 lines
3.5 KiB
Dart

import 'dart:math' as math;
import 'package:intaleq_maps/intaleq_maps.dart';
import '../models/military_operations_models.dart';
import 'military_grid_utils.dart';
/// Sovereign On-Device Minefield Threat & Breaching Corridor Engine
class MinefieldEngine {
MinefieldEngine._();
static const double earthRadiusM = 6371000.0;
/// Build Minefield Boundary Box, Density Calculation, and Safe Breaching Corridor
static MinefieldZoneResult calculateMinefieldZone({
required LatLng startPoint,
required LatLng endPoint,
required MinefieldType type,
double widthMeters = 200.0,
}) {
// 1. Calculate Length & Azimuth
final lengthMeters = MilitaryGridUtils.haversineDistance(
startPoint.latitude,
startPoint.longitude,
endPoint.latitude,
endPoint.longitude,
);
final azimuthDeg = MilitaryGridUtils.calculateBearing(
startPoint.latitude,
startPoint.longitude,
endPoint.latitude,
endPoint.longitude,
);
final azRad = (azimuthDeg * math.pi) / 180.0;
final perpRad = azRad + (math.pi / 2.0);
final halfWidth = widthMeters / 2.0;
// Helper: offset lat/lng by distance and bearing
LatLng offsetCoord(LatLng origin, double distM, double bearingRad) {
final dLat = (distM / earthRadiusM) * (180.0 / math.pi) * math.cos(bearingRad);
final dLng = (distM / (earthRadiusM * math.cos(origin.latitude * math.pi / 180.0))) * (180.0 / math.pi) * math.sin(bearingRad);
return LatLng(origin.latitude + dLat, origin.longitude + dLng);
}
// 2. Build 4 Corners of Minefield Polygon
final p1 = offsetCoord(startPoint, halfWidth, perpRad);
final p2 = offsetCoord(endPoint, halfWidth, perpRad);
final p3 = offsetCoord(endPoint, halfWidth, perpRad + math.pi);
final p4 = offsetCoord(startPoint, halfWidth, perpRad + math.pi);
final boundary = [p1, p2, p3, p4, p1];
// 3. Generate Safe Breaching Lane (الممر الآمن عبر الثغرة)
// 16-meter wide swept corridor right through the middle
const double breachWidthM = 16.0;
final halfBreach = breachWidthM / 2.0;
final midStart = LatLng(
(p1.latitude + p4.latitude) / 2.0,
(p1.longitude + p4.longitude) / 2.0,
);
final midEnd = LatLng(
(p2.latitude + p3.latitude) / 2.0,
(p2.longitude + p3.longitude) / 2.0,
);
final b1 = offsetCoord(midStart, halfBreach, perpRad);
final b2 = offsetCoord(midEnd, halfBreach, perpRad);
final b3 = offsetCoord(midEnd, halfBreach, perpRad + math.pi);
final b4 = offsetCoord(midStart, halfBreach, perpRad + math.pi);
final breachPolygon = [b1, b2, b3, b4, b1];
final breachCenterline = [midStart, midEnd];
// 4. Estimate Mines Count based on standard doctrine density
double densityPerM2;
switch (type) {
case MinefieldType.antiTank:
densityPerM2 = 0.005; // ~1 per 200 m2
break;
case MinefieldType.antiPersonnel:
densityPerM2 = 0.025; // ~1 per 40 m2
break;
case MinefieldType.mixedBarrier:
densityPerM2 = 0.035;
break;
}
final areaM2 = lengthMeters * widthMeters;
final estimatedMines = (areaM2 * densityPerM2).roundToDouble();
return MinefieldZoneResult(
startPoint: startPoint,
endPoint: endPoint,
type: type,
widthMeters: widthMeters,
lengthMeters: (lengthMeters * 10).round() / 10.0,
estimatedMinesCount: estimatedMines,
boundaryPolygon: boundary,
breachLaneCenterline: breachCenterline,
breachLanePolygon: breachPolygon,
);
}
}