Files
maps-saas/packages/tactical_app/lib/controllers/resection_controller.dart
T

80 lines
3.6 KiB
Dart

import 'package:get/get.dart';
import '../models/landmark.dart';
import '../services/resection_calculator.dart';
/// ============================================================================
/// [ResectionController] - وحدة التحكم بالتقاطع البصري العكسي (GPS-Denied Fix)
/// ============================================================================
/// English:
/// GetX Controller managing camera-based optical resection observations,
/// magnetic azimuth readings, and inverse triangulation coordinate resolution.
///
/// العربية:
/// متحكم GetX لإدارة عملية التقاطع البصري العكسي عبر الكاميرا والبوصلة المغناطيسية،
/// واستخراج إحداثيات الموقع بدقة عند انقطاع الـ GPS أو التشويش الإلكتروني.
/// ============================================================================
class ResectionController extends GetxController {
// ── Observables / الحالات التفاعلية ──────────────────────────────────────
final RxList<ResectionObservation> observations = <ResectionObservation>[].obs;
final Rx<ResectionResult?> resectionResult = Rx<ResectionResult?>(null);
final RxDouble currentHeadingDeg = 0.0.obs;
final Rx<TacticalLandmark?> selectedMapLandmark = Rx<TacticalLandmark?>(null);
/// Set Landmark picked from interactive Map / تعيين المعلم المختار مباشرة من الخريطة
void setMapLandmark(double lat, double lng, {String? customName, int? elevationM}) {
selectedMapLandmark.value = TacticalLandmark(
id: 'picked_${DateTime.now().millisecondsSinceEpoch}',
name: customName ?? 'معلم تكتيكي مرصود (${lat.toStringAsFixed(4)}, ${lng.toStringAsFixed(4)})',
region: 'قاطع العمليات الميداني',
type: LandmarkType.mountain,
lat: lat,
lng: lng,
elevationM: elevationM ?? 850,
description: 'تم تحديده بصرياً عبر إشارة المصلب التفاعلية على الخريطة',
);
}
/// Add Observation / إضافة رصد اتجاهي لمعلم جغرافي
void addObservation(ResectionObservation obs) {
observations.removeWhere((o) => o.landmark.id == obs.landmark.id);
observations.add(obs);
}
/// Remove Observation / إزالة رصد معلم معين
void removeObservation(String landmarkId) {
observations.removeWhere((o) => o.landmark.id == landmarkId);
}
/// Compute Position from Observations / استخراج الإحداثيات بالتقاطع العكسي
ResectionResult? computePosition() {
if (observations.length < 2) return null;
final res = ResectionCalculator.calculatePosition(observations);
resectionResult.value = res;
return res;
}
/// Compute Position from Single Landmark + Baseline step-off
/// استخراج الموقع بالرصد على معلم واحد وخط أساس متحرك (10م، 11م، 20م، 50م)
ResectionResult? computeSingleLandmarkPosition({
required TacticalLandmark landmark,
required double azimuth1Deg,
required double azimuth2Deg,
required double baselineMeters,
}) {
final res = ResectionCalculator.calculateSingleLandmarkPolar(
landmark: landmark,
azimuth1Deg: azimuth1Deg,
azimuth2Deg: azimuth2Deg,
baselineMeters: baselineMeters,
);
resectionResult.value = res;
return res;
}
/// Reset all observations / تصفير وإعادة ضبط الرصد
void reset() {
observations.clear();
resectionResult.value = null;
}
}