105 lines
3.5 KiB
Dart
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,
|
|
);
|
|
}
|
|
}
|