import 'package:flutter/material.dart'; import 'package:flutter_map_polygon_editor/polygon_editor/polygon_editor.dart'; import 'package:flutter_map_polywidget/flutter_map_polywidget.dart'; import 'package:get/get.dart'; import 'package:flutter_map/flutter_map.dart'; import 'package:latlong2/latlong.dart'; import 'package:terepi_seged/enums/map_edit_tool.dart'; import 'package:terepi_seged/enums/map_survey_mode.dart'; import 'package:terepi_seged/pages/map_survey/presentations/controllers/map_survey_controller.dart'; import 'package:terepi_seged/pages/map_survey/presentations/views/settings_dialog.dart'; import 'package:terepi_seged/pages/tracking/presentation/controllers/tracking_controller.dart'; import 'package:terepi_seged/utils/rive_utils.dart'; import 'package:terepi_seged/widgets/coordinate_panel.dart'; import 'package:terepi_seged/widgets/map_bottom_panel.dart'; import 'package:terepi_seged/widgets/map_edit_tools/map_edit_drawing_toolbar.dart'; import 'package:terepi_seged/widgets/map_edit_tools/map_edit_toolbar.dart'; import 'package:terepi_seged/widgets/map_info_card_column.dart'; import 'package:terepi_seged/widgets/save_point_fab.dart'; import 'package:terepi_seged/widgets/shared_map_widgets.dart'; import 'map_add_point_dialog.dart'; class MapSurveyView extends GetView { const MapSurveyView({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return Stack(children: [ SharedMapWidget( mapController: controller.mapController, currentZoom: controller.currentZoom, onZoomIn: controller.mapZoomIn, onZoomOut: controller.mapZoomOut, onCenterOnGps: controller.isMapMoveToCenter, onLongPress: (tapPosition, point) { if (controller.activeEditTool.value == MapEditTool.point) { controller.saveEditedPoint(point: point); } if (controller.activeEditTool.value == MapEditTool.line || controller.activeEditTool.value == MapEditTool.polygon) { controller.polygonEditorController.addPoint(point); } }, onTap: (tapPosition, point) { if (controller.mode.value != MapSurveyMode.fieldWalk) return; if (controller.isMapEditing) return; final polygonHit = controller.polygonHitNotifier.value; if (polygonHit != null && polygonHit.hitValues.isNotEmpty) { final id = polygonHit.hitValues.first; controller.selectedNoteItem(id); return; } final polylineHit = controller.polylineHitNotifier.value; if (polylineHit != null && polylineHit.hitValues.isNotEmpty) { final id = polylineHit.hitValues.first; controller.selectedNoteItem(id); return; } controller.clearNoteItemSelection(); }, layers: [ Obx(() => MarkerLayer(markers: controller.currentLocationMarker.toList())), // Track polyline Obx(() { final isTracking = TrackingController.to.isRecording.value; final inTrackMode = controller.mode.value == MapSurveyMode.track; if (!isTracking && !inTrackMode) { return const SizedBox.shrink(); } else { return _buildTrackLayer(); } }), Obx(() { if (controller.mode.value != MapSurveyMode.fieldWalk) { return const SizedBox.shrink(); } return MarkerLayer(markers: [...controller.pointNotes]); }), Obx(() { if (controller.mode.value != MapSurveyMode.fieldWalk) { return const SizedBox.shrink(); } return PolylineLayer( hitNotifier: controller.polylineHitNotifier, polylines: [...controller.polylineNotes]); }), Obx(() { if (controller.mode.value != MapSurveyMode.fieldWalk) { return const SizedBox.shrink(); } return PolygonLayer( hitNotifier: controller.polygonHitNotifier, polygons: [...controller.polygonNotes], useAltRendering: true); }), Obx(() { if (controller.mode.value != MapSurveyMode.fieldWalk) { return const SizedBox.shrink(); } final selectedId = controller.selectedNoteItemId.value; if (selectedId == null) return const SizedBox.shrink(); // Polygon kiemelés final selectedPolygon = controller.polygonNotes .where((p) => p.hitValue == selectedId) .firstOrNull; if (selectedPolygon != null) { return PolygonLayer(polygons: [ Polygon( points: selectedPolygon.points, color: Colors.transparent, borderColor: Colors.white, borderStrokeWidth: selectedPolygon.borderStrokeWidth + 3, ), ]); } // Polyline kiemelés final selectedPolyline = controller.polylineNotes .where((p) => p.hitValue == selectedId) .firstOrNull; if (selectedPolyline != null) { return PolylineLayer(polylines: [ Polyline( points: selectedPolyline.points, color: Colors.white.withOpacity(0.6), strokeWidth: selectedPolyline.strokeWidth + 4, ), ]); } return const SizedBox.shrink(); }), Obx(() { if (controller.mode.value != MapSurveyMode.fieldWalk) { return const SizedBox.shrink(); } return PolygonEditor( controller: controller.polygonEditorController, throttleDuration: Duration.zero); }), ], ), Positioned( top: 12, left: 12, child: MapInfoCardColumn(controller: controller), ), // Positioned( // top: 390, // right: 60, // left: 8, // child: CoordinatePanel( // eovY: controller.eovY, // eovX: controller.eovX, // horError: controller.gpsLatitudeError, // vertError: controller.gpsAltitudeError, // altitudeMsl: controller.gpsAltitude, // geoidSeparation: controller.gpsGeoidSeparation, // ntripConnected: controller.ntripIsConnected, // ntripBytes: controller.ntripReceivedData, // ntripPackets: controller.ntripDataPacketNumbers, // ggaPackets: controller.ggaSenDataPacketNumber, // ), // ), Obx(() { if (controller.mode.value != MapSurveyMode.fieldWalk) { return const SizedBox.shrink(); } if (controller.activeEditTool.value == MapEditTool.none) { return Positioned( left: 0, right: 0, bottom: 0, child: MapEditCompactToolbar(controller: controller)); } return Positioned( left: 0, right: 0, bottom: 0, child: MapEditDrawingToolbar(controller: controller)); }) // Positioned(top: 8, left: 0, right: 0, child: _ModeSelector()), // Positioned( // bottom: 80, // left: 8, // right: 8, // child: Obx( // () => controller.mode.value == MapSurveyMode.stakeout // ? _StakeoutPanel() // ΔY, ΔX, távolság, irányszög // : const SizedBox.shrink(), // ), // ), // Positioned( // bottom: 16, // right: 16, // child: SavePointFab(controller: controller), // ), // Positioned( // bottom: 0, // left: 0, // right: 0, // child: MapBottomPanel(controller: controller)) ]); } Widget _buildTrackLayer() { // FutureBuilder helyett a controller livePoints-ból return Obx(() { final ctrl = TrackingController.to; if (ctrl.livePoints.isEmpty) return const SizedBox.shrink(); return PolylineLayer(polylines: [ Polyline( points: ctrl.livePoints .map((p) => LatLng(p.latitude, p.longitude)) .toList(), color: Colors.red.withOpacity(0.85), strokeWidth: 3.0, ), ]); }); } } class _ModeSelector extends GetView { const _ModeSelector(); @override Widget build(BuildContext context) { return Obx(() => SegmentedButton( segments: const [ ButtonSegment( value: MapSurveyMode.measure, icon: Icon(Icons.gps_fixed, size: 16), label: Text('Bemérés'), ), ButtonSegment( value: MapSurveyMode.stakeout, icon: Icon(Icons.my_location, size: 16), label: Text('Kitűzés'), ), ], selected: {controller.mode.value}, onSelectionChanged: (s) => controller.switchMode(s.first), )); } } class _StakeoutPanel extends GetView { const _StakeoutPanel(); @override Widget build(BuildContext context) { return Card( child: Padding( padding: const EdgeInsets.all(12), child: Obx(() { final onTarget = controller.isOnTarget; final dy = controller.deltaY; final dx = controller.deltaX; final dist = controller.distanceToTarget; return Column( mainAxisSize: MainAxisSize.min, children: [ // Célpont neve Row(children: [ const Icon(Icons.flag, size: 16, color: Colors.orange), const SizedBox(width: 6), Text(controller.targetName.value, style: const TextStyle(fontWeight: FontWeight.w600)), const Spacer(), TextButton( onPressed: _showTargetPicker, child: const Text('Változtat'), ), ]), const Divider(height: 16), // Eltérések Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ _DeltaCell( label: 'ΔY', value: dy, unit: 'm', ), _DeltaCell( label: 'ΔX', value: dx, unit: 'm', ), _DeltaCell( label: 'Táv', value: dist, unit: 'm', alwaysPositive: true, ), ], ), const SizedBox(height: 8), // Státusz Container( width: double.infinity, padding: const EdgeInsets.symmetric(vertical: 6), decoration: BoxDecoration( color: onTarget ? Colors.green.withOpacity(0.12) : Colors.orange.withOpacity(0.12), borderRadius: BorderRadius.circular(8), ), child: Text( onTarget ? '✓ Célponton — pont rögzíthető' : '${controller.distanceToTarget.toStringAsFixed(3)} m a céltól', textAlign: TextAlign.center, style: TextStyle( fontWeight: FontWeight.w500, color: onTarget ? Colors.green : Colors.orange, fontSize: 13, ), ), ), ], ); }), ), ); } void _showTargetPicker() { // Lista a korábban bemért vagy tervezett pontokból //Get.bottomSheet(const _TargetPickerSheet()); } } class _DeltaCell extends StatelessWidget { final String label; final double value; final String unit; final bool alwaysPositive; const _DeltaCell({ required this.label, required this.value, required this.unit, this.alwaysPositive = false, }); @override Widget build(BuildContext context) { final display = alwaysPositive ? value.abs() : value; final prefix = (!alwaysPositive && value > 0) ? '+' : ''; final color = value.abs() < 0.05 ? Colors.green : value.abs() < 0.5 ? Colors.orange : Colors.red; return Column( children: [ Text(label, style: const TextStyle(fontSize: 11, color: Colors.grey)), const SizedBox(height: 2), Text( '$prefix${display.toStringAsFixed(3)} $unit', style: TextStyle( fontSize: 15, fontWeight: FontWeight.w600, color: color, ), ), ], ); } }