import 'dart:math' as math; import 'package:intaleq_maps/intaleq_maps.dart'; /// Military Coordinates Utility (WGS84 <-> UTM Zone 36N & MGRS Grid) /// Specifically optimized for Jordan & Levantine military operations (Zone 36N / 37N) class MilitaryGridUtils { MilitaryGridUtils._(); static const double _a = 6378137.0; // WGS84 semi-major axis static const double _f = 1 / 298.257223563; // WGS84 flattening static const double _b = _a * (1.0 - _f); static const double _eSq = (_a * _a - _b * _b) / (_a * _a); static const double _ePrimeSq = (_a * _a - _b * _b) / (_b * _b); static const double _k0 = 0.9996; // UTM scale factor static const double earthRadiusM = 6371000.0; /// Haversine Great Circle Distance in meters static double haversineDistance(double lat1, double lng1, double lat2, double lng2) { final dLat = (lat2 - lat1) * (math.pi / 180.0); final dLng = (lng2 - lng1) * (math.pi / 180.0); final a = math.sin(dLat / 2.0) * math.sin(dLat / 2.0) + math.cos(lat1 * math.pi / 180.0) * math.cos(lat2 * math.pi / 180.0) * math.sin(dLng / 2.0) * math.sin(dLng / 2.0); final c = 2.0 * math.atan2(math.sqrt(a), math.sqrt(1.0 - a)); return earthRadiusM * c; } /// Initial Great Circle Bearing in degrees (0..360) static double calculateBearing(double lat1, double lng1, double lat2, double lng2) { final phi1 = lat1 * (math.pi / 180.0); final phi2 = lat2 * (math.pi / 180.0); final deltaLambda = (lng2 - lng1) * (math.pi / 180.0); final y = math.sin(deltaLambda) * math.cos(phi2); final x = math.cos(phi1) * math.sin(phi2) - math.sin(phi1) * math.cos(phi2) * math.cos(deltaLambda); final theta = math.atan2(y, x); return (theta * (180.0 / math.pi) + 360.0) % 360.0; } /// Convert Azimuth Degrees to Arabic Cardinal Name static String azimuthToCardinalArabic(double azimuthDeg) { final deg = (azimuthDeg % 360.0 + 360.0) % 360.0; if (deg >= 337.5 || deg < 22.5) return 'شمال (N)'; if (deg >= 22.5 && deg < 67.5) return 'شمال شرق (NE)'; if (deg >= 67.5 && deg < 112.5) return 'شرق (E)'; if (deg >= 112.5 && deg < 157.5) return 'جنوب شرق (SE)'; if (deg >= 157.5 && deg < 202.5) return 'جنوب (S)'; if (deg >= 202.5 && deg < 247.5) return 'جنوب غرب (SW)'; if (deg >= 247.5 && deg < 292.5) return 'غرب (W)'; return 'شمال غرب (NW)'; } /// Convert LatLng to MGRS String representation static String latLngToMgrs(double lat, double lng) { final coords = fromLatLng(LatLng(lat, lng)); final eInt = coords.easting.round() % 100000; final nInt = coords.northing.round() % 100000; final eStr = (eInt ~/ 10).toString().padLeft(4, '0'); final nStr = (nInt ~/ 10).toString().padLeft(4, '0'); return '${coords.zone}R YU $eStr $nStr'; } /// Convert WGS84 Lat/Lng to UTM Zone 36N Easting (شرقيات) and Northing (شماليات) static MilitaryCoordinates fromLatLng(LatLng latLng, {int zone = 36}) { final lat = latLng.latitude; final lng = latLng.longitude; final latRad = lat * (math.pi / 180.0); final lngRad = lng * (math.pi / 180.0); final centralMeridianDeg = (zone - 1) * 6 - 180 + 3; final centralMeridianRad = centralMeridianDeg * (math.pi / 180.0); final n = _a / math.sqrt(1.0 - _eSq * math.sin(latRad) * math.sin(latRad)); final t = math.tan(latRad) * math.tan(latRad); final c = _ePrimeSq * math.cos(latRad) * math.cos(latRad); final aCoeff = math.cos(latRad) * (lngRad - centralMeridianRad); final m = _a * ((1.0 - _eSq / 4.0 - 3.0 * _eSq * _eSq / 64.0 - 5.0 * _eSq * _eSq * _eSq / 256.0) * latRad - (3.0 * _eSq / 8.0 + 3.0 * _eSq * _eSq / 32.0 + 45.0 * _eSq * _eSq * _eSq / 1024.0) * math.sin(2.0 * latRad) + (15.0 * _eSq * _eSq / 256.0 + 45.0 * _eSq * _eSq * _eSq / 1024.0) * math.sin(4.0 * latRad) - (35.0 * _eSq * _eSq * _eSq / 3072.0) * math.sin(6.0 * latRad)); final easting = 500000.0 + _k0 * n * (aCoeff + (1.0 - t + c) * math.pow(aCoeff, 3) / 6.0 + (5.0 - 18.0 * t + t * t + 72.0 * c - 58.0 * _ePrimeSq) * math.pow(aCoeff, 5) / 120.0); final northing = _k0 * (m + n * math.tan(latRad) * (aCoeff * aCoeff / 2.0 + (5.0 - t + 9.0 * c + 4.0 * c * c) * math.pow(aCoeff, 4) / 24.0 + (61.0 - 58.0 * t + t * t + 600.0 * c - 330.0 * _ePrimeSq) * math.pow(aCoeff, 6) / 720.0)); return MilitaryCoordinates( lat: lat, lng: lng, easting: easting, northing: northing, zone: zone, ); } /// Convert UTM Zone 36N Easting (شرقيات) and Northing (شماليات) to WGS84 Lat/Lng static LatLng toLatLng({ required double easting, required double northing, int zone = 36, }) { final e1 = (1.0 - math.sqrt(1.0 - _eSq)) / (1.0 + math.sqrt(1.0 - _eSq)); final x = easting - 500000.0; final y = northing; final m = y / _k0; final mu = m / (_a * (1.0 - _eSq / 4.0 - 3.0 * _eSq * _eSq / 64.0 - 5.0 * _eSq * _eSq * _eSq / 256.0)); final phi1Rad = mu + (3.0 * e1 / 2.0 - 27.0 * math.pow(e1, 3) / 32.0) * math.sin(2.0 * mu) + (21.0 * e1 * e1 / 16.0 - 55.0 * math.pow(e1, 4) / 32.0) * math.sin(4.0 * mu) + (151.0 * math.pow(e1, 3) / 96.0) * math.sin(6.0 * mu) + (1097.0 * math.pow(e1, 4) / 512.0) * math.sin(8.0 * mu); final n1 = _a / math.sqrt(1.0 - _eSq * math.sin(phi1Rad) * math.sin(phi1Rad)); final t1 = math.tan(phi1Rad) * math.tan(phi1Rad); final c1 = _ePrimeSq * math.cos(phi1Rad) * math.cos(phi1Rad); final r1 = _a * (1.0 - _eSq) / math.pow(1.0 - _eSq * math.sin(phi1Rad) * math.sin(phi1Rad), 1.5); final d = x / (n1 * _k0); final latRad = phi1Rad - (n1 * math.tan(phi1Rad) / r1) * (d * d / 2.0 - (5.0 + 3.0 * t1 + 10.0 * c1 - 4.0 * c1 * c1 - 9.0 * _ePrimeSq) * math.pow(d, 4) / 24.0 + (61.0 + 90.0 * t1 + 298.0 * c1 + 45.0 * t1 * t1 - 252.0 * _ePrimeSq - 3.0 * c1 * c1) * math.pow(d, 6) / 720.0); final centralMeridianDeg = (zone - 1) * 6 - 180 + 3; final centralMeridianRad = centralMeridianDeg * (math.pi / 180.0); final lngRad = centralMeridianRad + (d - (1.0 + 2.0 * t1 + c1) * math.pow(d, 3) / 6.0 + (5.0 - 2.0 * c1 + 28.0 * t1 - 3.0 * c1 * c1 + 8.0 * _ePrimeSq + 24.0 * t1 * t1) * math.pow(d, 5) / 120.0) / math.cos(phi1Rad); return LatLng( latRad * (180.0 / math.pi), lngRad * (180.0 / math.pi), ); } } class MilitaryCoordinates { final double lat; final double lng; final double easting; final double northing; final int zone; MilitaryCoordinates({ required this.lat, required this.lng, required this.easting, required this.northing, this.zone = 36, }); /// Short 6-digit Easting / Northing string for tactical voice transmission (e.g. 845 412) String get shortGrid => '${(easting.round() % 100000 ~/ 100).toString().padLeft(3, '0')} ${(northing.round() % 100000 ~/ 100).toString().padLeft(3, '0')}'; /// Standard Arabic military format (شرقيات: 784520 م • شماليات: 3541280 م) String get arabicFullFormat => 'شرقيات: ${easting.round()} م • شماليات: ${northing.round()} م (${zone}N)'; String get eastingStr => '${easting.round()}'; String get northingStr => '${northing.round()}'; }