80 lines
3.6 KiB
Dart
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;
|
|
}
|
|
}
|