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

175 lines
6.0 KiB
Dart

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
/// 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()}';
}