MapSurvey - refraktorálás
This commit is contained in:
parent
53888c9dcb
commit
ce8b539be3
@ -64,8 +64,8 @@ class MapSurveyController extends GetxController {
|
|||||||
RxDouble get gpsLongitudeError => _gnss.longitudeError;
|
RxDouble get gpsLongitudeError => _gnss.longitudeError;
|
||||||
RxDouble get gpsAltitudeError => _gnss.altitudeError;
|
RxDouble get gpsAltitudeError => _gnss.altitudeError;
|
||||||
Rx<DateTime> get gpsDateTime => _gnss.gpsDateTime;
|
Rx<DateTime> get gpsDateTime => _gnss.gpsDateTime;
|
||||||
RxBool get gpsIsConnected =>
|
bool get gpsIsConnected =>
|
||||||
(_gnss.connectionState.value == GnssConnectionState.connected).obs;
|
_gnss.connectionState.value == GnssConnectionState.connected;
|
||||||
|
|
||||||
// NTRIP állapot
|
// NTRIP állapot
|
||||||
RxBool get ntripIsConnected => _ntrip.isConnected;
|
RxBool get ntripIsConnected => _ntrip.isConnected;
|
||||||
@ -85,6 +85,9 @@ class MapSurveyController extends GetxController {
|
|||||||
Rx<Eov> eov = Eov(0, 0).obs;
|
Rx<Eov> eov = Eov(0, 0).obs;
|
||||||
Rx<double?> eovHeight = (0.0 as double?).obs;
|
Rx<double?> eovHeight = (0.0 as double?).obs;
|
||||||
|
|
||||||
|
final eovY = 0.0.obs;
|
||||||
|
final eovX = 0.0.obs;
|
||||||
|
|
||||||
// DMS formátum
|
// DMS formátum
|
||||||
RxInt latDegree = 0.obs;
|
RxInt latDegree = 0.obs;
|
||||||
RxInt latMin = 0.obs;
|
RxInt latMin = 0.obs;
|
||||||
@ -101,17 +104,18 @@ class MapSurveyController extends GetxController {
|
|||||||
RxBool isMapMoveToCenter = true.obs;
|
RxBool isMapMoveToCenter = true.obs;
|
||||||
RxBool mapIsInitialized = false.obs;
|
RxBool mapIsInitialized = false.obs;
|
||||||
|
|
||||||
late final MapController mapController;
|
final MapController mapController = MapController();
|
||||||
|
|
||||||
final currentLocationMarker = <Marker>[];
|
final currentLocationMarker = <Marker>[].obs;
|
||||||
final pointNotesMarker = <Marker>[];
|
final pointNotesMarker = <Marker>[].obs;
|
||||||
final pointsToMeasureMarker = <Marker>[];
|
final pointsToMeasureMarker = <Marker>[].obs;
|
||||||
final pointsToMeasureLabel = <PolyWidget>[];
|
final pointsToMeasureLabel = <PolyWidget>[].obs;
|
||||||
final pointsToMeasureDropDownMenuItem = <DropdownMenuItem<int>>[];
|
final pointsToMeasureDropDownMenuItem = <DropdownMenuItem<int>>[].obs;
|
||||||
|
|
||||||
// ── Pont adatok ───────────────────────────────────────────────────
|
// ── Pont adatok ───────────────────────────────────────────────────
|
||||||
List<PointToMeasure> pointsToMeasure = [];
|
final RxList<PointToMeasure> pointsToMeasure = <PointToMeasure>[].obs;
|
||||||
List<PointWithDescription> pointWithDescriptionList = [];
|
final RxList<PointWithDescription> pointWithDescriptionList =
|
||||||
|
<PointWithDescription>[].obs;
|
||||||
RxInt pointsToMeasureSelectedValue = (-1).obs;
|
RxInt pointsToMeasureSelectedValue = (-1).obs;
|
||||||
RxDouble distance = 0.0.obs;
|
RxDouble distance = 0.0.obs;
|
||||||
|
|
||||||
@ -149,22 +153,17 @@ class MapSurveyController extends GetxController {
|
|||||||
// ─────────────────────────────────────────────────────────────────
|
// ─────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onInit() async {
|
void onInit() {
|
||||||
super.onInit();
|
super.onInit();
|
||||||
|
_initAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _initAsync() async {
|
||||||
prefs = await SharedPreferences.getInstance();
|
prefs = await SharedPreferences.getInstance();
|
||||||
geoidGrid = await GeoidGrid.load('assets/Grids/geoid_eht2014.gtx');
|
geoidGrid = await GeoidGrid.load('assets/Grids/geoid_eht2014.gtx');
|
||||||
mapController = MapController();
|
|
||||||
|
|
||||||
// ── NTRIP RTCM adat → GNSS vevő ──────────────────────────────
|
NtripService.to.onRtcmData = (data) => GnssService.to.sendToReceiver(data);
|
||||||
NtripService.to.onRtcmData = (data) {
|
_gnssUpdateSub = _gnss.onDataUpdated.listen((_) => _onGnssUpdate());
|
||||||
GnssService.to.sendToReceiver(data);
|
|
||||||
};
|
|
||||||
|
|
||||||
// ── GnssService pozíció változás → EOV, marker, NTRIP GGA ────
|
|
||||||
_gnssUpdateSub = _gnss.onDataUpdated.listen((_) {
|
|
||||||
_onGnssUpdate();
|
|
||||||
});
|
|
||||||
|
|
||||||
// ── Supabase realtime ─────────────────────────────────────────
|
// ── Supabase realtime ─────────────────────────────────────────
|
||||||
_supaChannel = Supabase.instance.client
|
_supaChannel = Supabase.instance.client
|
||||||
@ -193,18 +192,19 @@ class MapSurveyController extends GetxController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onClose() async {
|
void onClose() {
|
||||||
super.onClose();
|
|
||||||
|
|
||||||
_phoneLocationSub?.cancel();
|
_phoneLocationSub?.cancel();
|
||||||
_gnssUpdateSub?.cancel();
|
_gnssUpdateSub?.cancel();
|
||||||
await _supaChannel?.unsubscribe();
|
final f = _supaChannel?.unsubscribe();
|
||||||
|
if (f != null) unawaited(f);
|
||||||
|
|
||||||
pointIdController.dispose();
|
pointIdController.dispose();
|
||||||
pointDescriptionController.dispose();
|
pointDescriptionController.dispose();
|
||||||
gpsHeightController.dispose();
|
gpsHeightController.dispose();
|
||||||
pointPrefixController.dispose();
|
pointPrefixController.dispose();
|
||||||
pointPostfixController.dispose();
|
pointPostfixController.dispose();
|
||||||
|
|
||||||
|
super.onClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─────────────────────────────────────────────────────────────────
|
// ─────────────────────────────────────────────────────────────────
|
||||||
@ -220,7 +220,11 @@ class MapSurveyController extends GetxController {
|
|||||||
final sep = _gnss.geoidSeparation.value;
|
final sep = _gnss.geoidSeparation.value;
|
||||||
|
|
||||||
// EOV konverzió
|
// EOV konverzió
|
||||||
eov.value = ConvertCoordinate.ConvertWgsToEov(lat, lon);
|
final converted = ConvertCoordinate.ConvertWgsToEov(lat, lon);
|
||||||
|
eov.value = converted;
|
||||||
|
eovY.value = converted.Y.toDouble();
|
||||||
|
eovX.value = converted.X.toDouble();
|
||||||
|
|
||||||
eovHeight.value = geoidGrid.toEovHeight(lat, lon, alt, sep);
|
eovHeight.value = geoidGrid.toEovHeight(lat, lon, alt, sep);
|
||||||
|
|
||||||
// DMS
|
// DMS
|
||||||
@ -254,10 +258,6 @@ class MapSurveyController extends GetxController {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─────────────────────────────────────────────────────────────────
|
|
||||||
// Telefon GPS fallback
|
|
||||||
// ─────────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
// ─────────────────────────────────────────────────────────────────
|
// ─────────────────────────────────────────────────────────────────
|
||||||
// Térkép vezérlők
|
// Térkép vezérlők
|
||||||
// ─────────────────────────────────────────────────────────────────
|
// ─────────────────────────────────────────────────────────────────
|
||||||
@ -288,21 +288,23 @@ class MapSurveyController extends GetxController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _updateCurrentLocationMarker() {
|
void _updateCurrentLocationMarker() {
|
||||||
currentLocationMarker.clear();
|
currentLocationMarker.assignAll([
|
||||||
currentLocationMarker.add(Marker(
|
Marker(
|
||||||
point: LatLng(currentLatitude.value, currentLongitude.value),
|
point: LatLng(currentLatitude.value, currentLongitude.value),
|
||||||
width: 15.0,
|
|
||||||
height: 15.0,
|
|
||||||
child: Container(
|
|
||||||
width: 15.0,
|
width: 15.0,
|
||||||
height: 15.0,
|
height: 15.0,
|
||||||
decoration: BoxDecoration(
|
child: Container(
|
||||||
color: getCurrentLocationMarkerColor(_gnss.gpsQuality.value),
|
width: 15.0,
|
||||||
shape: BoxShape.circle,
|
height: 15.0,
|
||||||
border: Border.all(width: 1.5, color: Colors.white),
|
decoration: BoxDecoration(
|
||||||
|
color: getCurrentLocationMarkerColor(_gnss.gpsQuality.value),
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
border: Border.all(width: 1.5, color: Colors.white),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
)
|
||||||
));
|
]);
|
||||||
|
|
||||||
if (isMapMoveToCenter.value) {
|
if (isMapMoveToCenter.value) {
|
||||||
mapController.move(
|
mapController.move(
|
||||||
LatLng(currentLatitude.value, currentLongitude.value),
|
LatLng(currentLatitude.value, currentLongitude.value),
|
||||||
@ -622,7 +624,7 @@ class MapSurveyController extends GetxController {
|
|||||||
// ─────────────────────────────────────────────────────────────────
|
// ─────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
double calculateDistance(LatLng start, LatLng end) {
|
double calculateDistance(LatLng start, LatLng end) {
|
||||||
const r = 6371.0;
|
const r = 6371000.0;
|
||||||
final lat1 = start.latitude * (pi / 180);
|
final lat1 = start.latitude * (pi / 180);
|
||||||
final lon1 = start.longitude * (pi / 180);
|
final lon1 = start.longitude * (pi / 180);
|
||||||
final lat2 = end.latitude * (pi / 180);
|
final lat2 = end.latitude * (pi / 180);
|
||||||
|
|||||||
@ -1,13 +1,8 @@
|
|||||||
import 'dart:io';
|
|
||||||
import 'dart:math';
|
|
||||||
|
|
||||||
import 'package:file_picker/file_picker.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_map_polywidget/flutter_map_polywidget.dart';
|
import 'package:flutter_map_polywidget/flutter_map_polywidget.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:flutter_map/flutter_map.dart';
|
import 'package:flutter_map/flutter_map.dart';
|
||||||
import 'package:latlong2/latlong.dart';
|
import 'package:latlong2/latlong.dart';
|
||||||
import 'package:rive/rive.dart';
|
|
||||||
import 'package:terepi_seged/pages/map_survey/presentations/controllers/map_survey_controller.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/map_survey/presentations/views/settings_dialog.dart';
|
||||||
import 'package:terepi_seged/utils/rive_utils.dart';
|
import 'package:terepi_seged/utils/rive_utils.dart';
|
||||||
@ -24,12 +19,29 @@ class MapSurveyView extends GetView<MapSurveyController> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Stack(children: [
|
return Stack(children: [
|
||||||
const SharedMapWidget(),
|
SharedMapWidget(
|
||||||
|
mapController: controller.mapController,
|
||||||
|
currentZoom: controller.currentZoom,
|
||||||
|
onZoomIn: controller.mapZoomIn,
|
||||||
|
onZoomOut: controller.mapZoomOut,
|
||||||
|
onCenterOnGps: controller.isMapMoveToCenter,
|
||||||
|
),
|
||||||
Positioned(
|
Positioned(
|
||||||
top: 8,
|
top: 8,
|
||||||
right: 60,
|
right: 60,
|
||||||
left: 8,
|
left: 8,
|
||||||
child: CoordinatePanel.fromController(controller),
|
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,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
// Positioned(top: 8, left: 0, right: 0, child: _ModeSelector()),
|
// Positioned(top: 8, left: 0, right: 0, child: _ModeSelector()),
|
||||||
// Positioned(
|
// Positioned(
|
||||||
|
|||||||
@ -23,6 +23,8 @@ class ShellView extends GetView<ShellController> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
|
extendBody: true,
|
||||||
|
extendBodyBehindAppBar: false,
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
// Cím reaktívan frissül tab váltáskor
|
// Cím reaktívan frissül tab váltáskor
|
||||||
title: Obx(() => Text(controller.currentTitle)),
|
title: Obx(() => Text(controller.currentTitle)),
|
||||||
|
|||||||
@ -37,8 +37,8 @@ class NtripService extends GetxService {
|
|||||||
final host = '84.206.45.44'.obs; // gnssnet.hu IP
|
final host = '84.206.45.44'.obs; // gnssnet.hu IP
|
||||||
final port = 2101.obs;
|
final port = 2101.obs;
|
||||||
final mountpoint = 'SGO_RTK3.2'.obs;
|
final mountpoint = 'SGO_RTK3.2'.obs;
|
||||||
final username = ''.obs;
|
final username = 'elgi03'.obs;
|
||||||
final password = ''.obs;
|
final password = 'StEfan14'.obs;
|
||||||
|
|
||||||
// ── UI controllerek (beállítás dialóghoz) ────────────────────────
|
// ── UI controllerek (beállítás dialóghoz) ────────────────────────
|
||||||
final hostController = TextEditingController();
|
final hostController = TextEditingController();
|
||||||
@ -228,8 +228,8 @@ class NtripService extends GetxService {
|
|||||||
host.value = prefs.getString('ntrip_host') ?? '84.206.45.44';
|
host.value = prefs.getString('ntrip_host') ?? '84.206.45.44';
|
||||||
port.value = prefs.getInt('ntrip_port') ?? 2101;
|
port.value = prefs.getInt('ntrip_port') ?? 2101;
|
||||||
mountpoint.value = prefs.getString('ntrip_mountpoint') ?? 'SGO_RTK3.2';
|
mountpoint.value = prefs.getString('ntrip_mountpoint') ?? 'SGO_RTK3.2';
|
||||||
username.value = prefs.getString('ntrip_username') ?? '';
|
username.value = prefs.getString('ntrip_username') ?? 'elgi01';
|
||||||
password.value = prefs.getString('ntrip_password') ?? '';
|
password.value = prefs.getString('ntrip_password') ?? 'StEfan14';
|
||||||
}
|
}
|
||||||
|
|
||||||
void _syncControllersFromValues() {
|
void _syncControllersFromValues() {
|
||||||
|
|||||||
@ -1,264 +1,102 @@
|
|||||||
// lib/widgets/shared_map_widget.dart
|
// lib/widgets/shared_map_widgets.dart
|
||||||
import 'dart:math' as math;
|
|
||||||
import 'dart:math';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_map/flutter_map.dart';
|
import 'package:flutter_map/flutter_map.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:latlong2/latlong.dart';
|
import 'package:latlong2/latlong.dart';
|
||||||
|
|
||||||
import '../services/gnss/gnss_service.dart';
|
class SharedMapWidget extends StatelessWidget {
|
||||||
import '../services/coord_converter_service.dart';
|
final MapController mapController;
|
||||||
import '../pages/map_survey/presentations/controllers/map_survey_controller.dart';
|
final List<Widget> layers;
|
||||||
|
|
||||||
// ─── SharedMapWidget ──────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
class SharedMapWidget extends StatefulWidget {
|
|
||||||
/// Extra flutter_map rétegek (pl. PolylineLayer, PolygonLayer).
|
|
||||||
final List<Widget> extraLayers;
|
|
||||||
|
|
||||||
/// Külső MapController — ha az oldal saját maga is mozgatja a térképet.
|
|
||||||
final MapController? mapController;
|
|
||||||
|
|
||||||
/// Hosszú nyomás callback (terepbejárás pont hozzáadáshoz).
|
|
||||||
final void Function(TapPosition, LatLng)? onLongPress;
|
final void Function(TapPosition, LatLng)? onLongPress;
|
||||||
|
final void Function(MapCamera, bool)? onPositionChanged;
|
||||||
|
|
||||||
/// Megjelenítendő vezérlők.
|
|
||||||
final MapControls controls;
|
final MapControls controls;
|
||||||
|
final LatLng initialCenter;
|
||||||
/// Kezdeti zoom szint.
|
|
||||||
final double initialZoom;
|
final double initialZoom;
|
||||||
|
final double minZoom;
|
||||||
|
final double maxZoom;
|
||||||
|
|
||||||
|
// Controller-owned state (csak megjelenítéshez)
|
||||||
|
final RxBool? isFollowing;
|
||||||
|
final RxBool? isNorthUp;
|
||||||
|
final RxDouble? currentZoom;
|
||||||
|
final RxDouble? currentRotationRad;
|
||||||
|
|
||||||
|
// Controller-owned commands
|
||||||
|
final VoidCallback? onZoomIn;
|
||||||
|
final VoidCallback? onZoomOut;
|
||||||
|
final VoidCallback? onCenterOnGps;
|
||||||
|
final VoidCallback? onResetNorth;
|
||||||
|
|
||||||
const SharedMapWidget({
|
const SharedMapWidget({
|
||||||
super.key,
|
super.key,
|
||||||
this.extraLayers = const [],
|
required this.mapController,
|
||||||
this.mapController,
|
this.layers = const [],
|
||||||
this.onLongPress,
|
this.onLongPress,
|
||||||
|
this.onPositionChanged,
|
||||||
this.controls = const MapControls(),
|
this.controls = const MapControls(),
|
||||||
|
this.initialCenter = const LatLng(47.5, 19.0),
|
||||||
this.initialZoom = 18.0,
|
this.initialZoom = 18.0,
|
||||||
|
this.minZoom = 3.0,
|
||||||
|
this.maxZoom = 25.0,
|
||||||
|
this.isFollowing,
|
||||||
|
this.isNorthUp,
|
||||||
|
this.currentZoom,
|
||||||
|
this.currentRotationRad,
|
||||||
|
this.onZoomIn,
|
||||||
|
this.onZoomOut,
|
||||||
|
this.onCenterOnGps,
|
||||||
|
this.onResetNorth,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
|
||||||
State<SharedMapWidget> createState() => _SharedMapWidgetState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _SharedMapWidgetState extends State<SharedMapWidget> {
|
|
||||||
late final MapController _mapController;
|
|
||||||
|
|
||||||
// Reaktív belső állapot
|
|
||||||
final _isFollowing = true.obs; // GPS követés be/ki
|
|
||||||
final _currentZoom = 18.0.obs;
|
|
||||||
final _isNorthUp = true.obs; // forgó térkép vs. É-up
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
_mapController = widget.mapController ?? MapController();
|
|
||||||
_currentZoom.value = widget.initialZoom;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
// Csak akkor disposoljuk, ha belső controller
|
|
||||||
if (widget.mapController == null) {
|
|
||||||
_mapController.dispose();
|
|
||||||
}
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── GPS pozíció követése ────────────────────────────────────────────
|
|
||||||
|
|
||||||
void _onPositionChanged(MapCamera camera, bool hasGesture) {
|
|
||||||
_currentZoom.value = camera.zoom;
|
|
||||||
// Ha a felhasználó manuálisan mozgatja → kikapcsol a követés
|
|
||||||
if (hasGesture && _isFollowing.value) {
|
|
||||||
_isFollowing.value = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _centerOnPosition() {
|
|
||||||
final gnss = GnssService.to;
|
|
||||||
if (gnss.latitude.value == 0) return;
|
|
||||||
_mapController.move(
|
|
||||||
LatLng(gnss.latitude.value, gnss.longitude.value),
|
|
||||||
_currentZoom.value,
|
|
||||||
);
|
|
||||||
_isFollowing.value = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void _zoomIn() =>
|
|
||||||
_mapController.move(_mapController.camera.center, _currentZoom.value + 1);
|
|
||||||
|
|
||||||
void _zoomOut() =>
|
|
||||||
_mapController.move(_mapController.camera.center, _currentZoom.value - 1);
|
|
||||||
|
|
||||||
void _resetNorth() {
|
|
||||||
_mapController.rotate(0);
|
|
||||||
_isNorthUp.value = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── GPS stream → térkép mozgatás ───────────────────────────────────
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Obx(() {
|
return Stack(
|
||||||
final gnss = GnssService.to;
|
children: [
|
||||||
final lat = gnss.latitude.value;
|
FlutterMap(
|
||||||
final lon = gnss.longitude.value;
|
mapController: mapController,
|
||||||
|
options: MapOptions(
|
||||||
if (_isFollowing.value && lat != 0 && lon != 0) {
|
initialCenter: initialCenter,
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
initialZoom: initialZoom,
|
||||||
if (!mounted) return;
|
minZoom: minZoom,
|
||||||
_mapController.move(LatLng(lat, lon), _currentZoom.value);
|
maxZoom: maxZoom,
|
||||||
});
|
onLongPress: onLongPress,
|
||||||
}
|
onPositionChanged: onPositionChanged,
|
||||||
|
interactionOptions: const InteractionOptions(
|
||||||
return Stack(
|
flags: InteractiveFlag.all,
|
||||||
children: [
|
|
||||||
// ── Térkép ────────────────────────────────────────────────
|
|
||||||
FlutterMap(
|
|
||||||
mapController: _mapController,
|
|
||||||
options: MapOptions(
|
|
||||||
initialCenter:
|
|
||||||
lat != 0 ? LatLng(lat, lon) : const LatLng(47.5, 19.0),
|
|
||||||
initialZoom: widget.initialZoom,
|
|
||||||
maxZoom: 25,
|
|
||||||
minZoom: 3,
|
|
||||||
onLongPress: widget.onLongPress,
|
|
||||||
onPositionChanged: _onPositionChanged,
|
|
||||||
interactionOptions: const InteractionOptions(
|
|
||||||
flags: InteractiveFlag.all,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
children: [
|
|
||||||
// 1. Alaptérkép
|
|
||||||
TileLayer(
|
|
||||||
urlTemplate:
|
|
||||||
'http://{s}.google.com/vt/lyrs=s,h&x={x}&y={y}&z={z}',
|
|
||||||
subdomains: const ['mt0', 'mt1', 'mt2', 'mt3'],
|
|
||||||
maxNativeZoom: 18,
|
|
||||||
),
|
|
||||||
// 2. Extra rétegek (terepbejárás elemei)
|
|
||||||
...widget.extraLayers,
|
|
||||||
// 3. Bemért pontok
|
|
||||||
// if (Get.isRegistered<MapSurveyController>())
|
|
||||||
// Obx(() => MarkerLayer(
|
|
||||||
// markers: _buildMeasuredPointMarkers(),
|
|
||||||
// )),
|
|
||||||
// 4. Kitűzési célpont + vonal
|
|
||||||
if (Get.isRegistered<MapSurveyController>())
|
|
||||||
Obx(() {
|
|
||||||
final lat = GnssService.to.latitude.value;
|
|
||||||
final lon = GnssService.to.longitude.value;
|
|
||||||
return _buildStakeoutLayer(lat, lon);
|
|
||||||
}),
|
|
||||||
// 5. GPS pozíció
|
|
||||||
Obx(() {
|
|
||||||
final lat = GnssService.to.latitude.value;
|
|
||||||
final lon = GnssService.to.longitude.value;
|
|
||||||
return MarkerLayer(
|
|
||||||
markers: lat == 0 && lon == 0
|
|
||||||
? []
|
|
||||||
: [_buildCurrentPositionMarker(lat, lon)],
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
|
children: [
|
||||||
// ── Vezérlők ──────────────────────────────────────────────
|
TileLayer(
|
||||||
_MapControlsOverlay(
|
urlTemplate:
|
||||||
controls: widget.controls,
|
'http://{s}.google.com/vt/lyrs=s,h&x={x}&y={y}&z={z}',
|
||||||
isFollowing: _isFollowing,
|
subdomains: const ['mt0', 'mt1', 'mt2', 'mt3'],
|
||||||
isNorthUp: _isNorthUp,
|
maxNativeZoom: 18,
|
||||||
currentZoom: _currentZoom,
|
|
||||||
onZoomIn: _zoomIn,
|
|
||||||
onZoomOut: _zoomOut,
|
|
||||||
onCenterOnGps: _centerOnPosition,
|
|
||||||
onResetNorth: _resetNorth,
|
|
||||||
),
|
|
||||||
|
|
||||||
// ── Zoom szint jelzés (opcionális) ────────────────────────
|
|
||||||
if (widget.controls.showZoomLevel)
|
|
||||||
Positioned(
|
|
||||||
bottom: 8,
|
|
||||||
left: 8,
|
|
||||||
child: Obx(() => _ZoomLabel(_currentZoom.value)),
|
|
||||||
),
|
),
|
||||||
],
|
...layers,
|
||||||
);
|
],
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── Marker builder metódusok ────────────────────────────────────────
|
|
||||||
|
|
||||||
List<Marker> _buildMeasuredPointMarkers() {
|
|
||||||
if (!Get.isRegistered<MapSurveyController>()) return [];
|
|
||||||
return MapSurveyController.to.pointNotesMarker;
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildStakeoutLayer(double lat, double lon) {
|
|
||||||
if (!Get.isRegistered<MapSurveyController>())
|
|
||||||
return const SizedBox.shrink();
|
|
||||||
final ctrl = MapSurveyController.to;
|
|
||||||
if (ctrl.mode.value != MapSurveyMode.stakeout ||
|
|
||||||
ctrl.targetEovY.value == 0) {
|
|
||||||
return const SizedBox.shrink();
|
|
||||||
}
|
|
||||||
|
|
||||||
final wgs = CoordConverterService.to
|
|
||||||
.eovToWgsPoint(ctrl.targetEovY.value, ctrl.targetEovX.value);
|
|
||||||
final targetLatLng = LatLng(wgs.y, wgs.x);
|
|
||||||
final dx = ctrl.eov.value.X - ctrl.targetEovX.value;
|
|
||||||
final dy = ctrl.eov.value.Y - ctrl.targetEovY.value;
|
|
||||||
final dist = sqrt(dx * dx + dy * dy);
|
|
||||||
final onTarget = dist < 0.05;
|
|
||||||
|
|
||||||
return Stack(children: [
|
|
||||||
// Szaggatott vonal
|
|
||||||
PolylineLayer(polylines: [
|
|
||||||
Polyline(
|
|
||||||
points: [LatLng(lat, lon), targetLatLng],
|
|
||||||
color: Colors.orange.withOpacity(0.85),
|
|
||||||
strokeWidth: 2.5,
|
|
||||||
),
|
),
|
||||||
]),
|
_MapControlsOverlay(
|
||||||
// Célpont marker
|
controls: controls,
|
||||||
MarkerLayer(markers: [
|
isFollowing: isFollowing,
|
||||||
Marker(
|
isNorthUp: isNorthUp,
|
||||||
point: targetLatLng,
|
currentRotationRad: currentRotationRad,
|
||||||
width: 130,
|
onZoomIn: onZoomIn,
|
||||||
height: 72,
|
onZoomOut: onZoomOut,
|
||||||
alignment: Alignment.bottomCenter,
|
onCenterOnGps: onCenterOnGps,
|
||||||
child: _LabeledMarker(
|
onResetNorth: onResetNorth,
|
||||||
label: ctrl.targetName.value,
|
),
|
||||||
icon: Icons.flag,
|
if (controls.showZoomLevel && currentZoom != null)
|
||||||
color: Colors.orange,
|
Positioned(
|
||||||
activeColor: onTarget ? Colors.green : null,
|
bottom: 8,
|
||||||
sublabel: onTarget ? '✓ Célponton' : '${dist.toStringAsFixed(3)} m',
|
left: 8,
|
||||||
|
child: Obx(() => _ZoomLabel(currentZoom!.value)),
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
]),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
Marker _buildCurrentPositionMarker(double lat, double lon) {
|
|
||||||
final color = switch (GnssService.to.gpsQuality.value) {
|
|
||||||
4 => Colors.green,
|
|
||||||
5 => Colors.lightGreen,
|
|
||||||
2 => Colors.blue,
|
|
||||||
1 => Colors.orange,
|
|
||||||
_ => Colors.grey,
|
|
||||||
};
|
|
||||||
return Marker(
|
|
||||||
point: LatLng(lat, lon),
|
|
||||||
width: 24,
|
|
||||||
height: 24,
|
|
||||||
child: _PulsingDot(color: color),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── Vezérlők konfigurációja ──────────────────────────────────────────────────
|
|
||||||
|
|
||||||
class MapControls {
|
class MapControls {
|
||||||
final bool showZoomButtons;
|
final bool showZoomButtons;
|
||||||
final bool showFollowButton;
|
final bool showFollowButton;
|
||||||
@ -274,7 +112,6 @@ class MapControls {
|
|||||||
this.showCompass = false,
|
this.showCompass = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
/// Terepbejárás módhoz — nincs follow (rajzolás közben szabad mozgás)
|
|
||||||
const MapControls.fieldTrip()
|
const MapControls.fieldTrip()
|
||||||
: showZoomButtons = true,
|
: showZoomButtons = true,
|
||||||
showFollowButton = false,
|
showFollowButton = false,
|
||||||
@ -282,7 +119,6 @@ class MapControls {
|
|||||||
showZoomLevel = true,
|
showZoomLevel = true,
|
||||||
showCompass = false;
|
showCompass = false;
|
||||||
|
|
||||||
/// Navigáció módhoz — minden vezérlő
|
|
||||||
const MapControls.navigation()
|
const MapControls.navigation()
|
||||||
: showZoomButtons = true,
|
: showZoomButtons = true,
|
||||||
showFollowButton = true,
|
showFollowButton = true,
|
||||||
@ -290,7 +126,6 @@ class MapControls {
|
|||||||
showZoomLevel = false,
|
showZoomLevel = false,
|
||||||
showCompass = true;
|
showCompass = true;
|
||||||
|
|
||||||
/// Minimális — csak zoom
|
|
||||||
const MapControls.minimal()
|
const MapControls.minimal()
|
||||||
: showZoomButtons = true,
|
: showZoomButtons = true,
|
||||||
showFollowButton = false,
|
showFollowButton = false,
|
||||||
@ -299,23 +134,22 @@ class MapControls {
|
|||||||
showCompass = false;
|
showCompass = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── Vezérlők overlay ────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
class _MapControlsOverlay extends StatelessWidget {
|
class _MapControlsOverlay extends StatelessWidget {
|
||||||
final MapControls controls;
|
final MapControls controls;
|
||||||
final RxBool isFollowing;
|
final RxBool? isFollowing;
|
||||||
final RxBool isNorthUp;
|
final RxBool? isNorthUp;
|
||||||
final RxDouble currentZoom;
|
final RxDouble? currentRotationRad;
|
||||||
final VoidCallback onZoomIn;
|
|
||||||
final VoidCallback onZoomOut;
|
final VoidCallback? onZoomIn;
|
||||||
final VoidCallback onCenterOnGps;
|
final VoidCallback? onZoomOut;
|
||||||
final VoidCallback onResetNorth;
|
final VoidCallback? onCenterOnGps;
|
||||||
|
final VoidCallback? onResetNorth;
|
||||||
|
|
||||||
const _MapControlsOverlay({
|
const _MapControlsOverlay({
|
||||||
required this.controls,
|
required this.controls,
|
||||||
required this.isFollowing,
|
required this.isFollowing,
|
||||||
required this.isNorthUp,
|
required this.isNorthUp,
|
||||||
required this.currentZoom,
|
required this.currentRotationRad,
|
||||||
required this.onZoomIn,
|
required this.onZoomIn,
|
||||||
required this.onZoomOut,
|
required this.onZoomOut,
|
||||||
required this.onCenterOnGps,
|
required this.onCenterOnGps,
|
||||||
@ -326,41 +160,37 @@ class _MapControlsOverlay extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Positioned(
|
return Positioned(
|
||||||
right: 10,
|
right: 10,
|
||||||
bottom: 80, // BottomNav felett
|
bottom: 80,
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
// ── Iránytű / É-ra forgat ─────────────────────────────
|
if (controls.showNorthButton && isNorthUp != null)
|
||||||
if (controls.showNorthButton)
|
Obx(() {
|
||||||
Obx(() => _ControlButton(
|
final active = isNorthUp!.value;
|
||||||
icon: Icons.navigation,
|
final angle = currentRotationRad?.value ?? 0.0;
|
||||||
tooltip: 'Észak felfelé',
|
return _ControlButton(
|
||||||
active: isNorthUp.value,
|
tooltip: 'Észak felfelé',
|
||||||
// A gomb elfordul ahogy a térkép forog — vizuális jelzés
|
active: active,
|
||||||
child: Transform.rotate(
|
onTap: onResetNorth,
|
||||||
angle: 0,
|
child: Transform.rotate(
|
||||||
child: const Icon(Icons.navigation, size: 20),
|
angle: -angle,
|
||||||
),
|
child: const Icon(Icons.navigation, size: 20),
|
||||||
onTap: onResetNorth,
|
),
|
||||||
)),
|
);
|
||||||
|
}),
|
||||||
if (controls.showNorthButton) const SizedBox(height: 6),
|
if (controls.showNorthButton) const SizedBox(height: 6),
|
||||||
|
if (controls.showFollowButton && isFollowing != null)
|
||||||
// ── GPS követés ───────────────────────────────────────
|
|
||||||
if (controls.showFollowButton)
|
|
||||||
Obx(() => _ControlButton(
|
Obx(() => _ControlButton(
|
||||||
tooltip: isFollowing.value
|
tooltip: isFollowing!.value
|
||||||
? 'GPS követés aktív'
|
? 'GPS követés aktív'
|
||||||
: 'GPS követés kikapcsolva',
|
: 'GPS követés kikapcsolva',
|
||||||
active: isFollowing.value,
|
active: isFollowing!.value,
|
||||||
icon:
|
icon: isFollowing!.value
|
||||||
isFollowing.value ? Icons.gps_fixed : Icons.gps_not_fixed,
|
? Icons.gps_fixed
|
||||||
|
: Icons.gps_not_fixed,
|
||||||
onTap: onCenterOnGps,
|
onTap: onCenterOnGps,
|
||||||
)),
|
)),
|
||||||
|
|
||||||
if (controls.showFollowButton) const SizedBox(height: 6),
|
if (controls.showFollowButton) const SizedBox(height: 6),
|
||||||
|
|
||||||
// ── Zoom gombok ───────────────────────────────────────
|
|
||||||
if (controls.showZoomButtons) ...[
|
if (controls.showZoomButtons) ...[
|
||||||
_ControlButton(
|
_ControlButton(
|
||||||
icon: Icons.add,
|
icon: Icons.add,
|
||||||
@ -385,7 +215,7 @@ class _ControlButton extends StatelessWidget {
|
|||||||
final Widget? child;
|
final Widget? child;
|
||||||
final String tooltip;
|
final String tooltip;
|
||||||
final bool active;
|
final bool active;
|
||||||
final VoidCallback onTap;
|
final VoidCallback? onTap;
|
||||||
|
|
||||||
const _ControlButton({
|
const _ControlButton({
|
||||||
this.icon,
|
this.icon,
|
||||||
@ -435,8 +265,6 @@ class _ControlButton extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── Zoom szint label ─────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
class _ZoomLabel extends StatelessWidget {
|
class _ZoomLabel extends StatelessWidget {
|
||||||
final double zoom;
|
final double zoom;
|
||||||
const _ZoomLabel(this.zoom);
|
const _ZoomLabel(this.zoom);
|
||||||
@ -457,16 +285,15 @@ class _ZoomLabel extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── Feliratozott marker ──────────────────────────────────────────────────────
|
class LabeledMarker extends StatelessWidget {
|
||||||
|
|
||||||
class _LabeledMarker extends StatelessWidget {
|
|
||||||
final String label;
|
final String label;
|
||||||
final IconData icon;
|
final IconData icon;
|
||||||
final Color color;
|
final Color color;
|
||||||
final Color? activeColor;
|
final Color? activeColor;
|
||||||
final String? sublabel;
|
final String? sublabel;
|
||||||
|
|
||||||
const _LabeledMarker({
|
const LabeledMarker({
|
||||||
|
super.key,
|
||||||
required this.label,
|
required this.label,
|
||||||
required this.icon,
|
required this.icon,
|
||||||
required this.color,
|
required this.color,
|
||||||
@ -490,7 +317,7 @@ class _LabeledMarker extends StatelessWidget {
|
|||||||
color: Colors.black.withOpacity(0.3),
|
color: Colors.black.withOpacity(0.3),
|
||||||
blurRadius: 4,
|
blurRadius: 4,
|
||||||
offset: const Offset(0, 2),
|
offset: const Offset(0, 2),
|
||||||
)
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
@ -499,39 +326,47 @@ class _LabeledMarker extends StatelessWidget {
|
|||||||
Text(
|
Text(
|
||||||
label,
|
label,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
fontSize: 11,
|
fontSize: 11,
|
||||||
fontWeight: FontWeight.w600),
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
),
|
),
|
||||||
if (sublabel != null)
|
if (sublabel != null)
|
||||||
Text(sublabel!,
|
Text(
|
||||||
style: TextStyle(
|
sublabel!,
|
||||||
color: Colors.white.withOpacity(0.9), fontSize: 9)),
|
style: TextStyle(
|
||||||
|
color: Colors.white.withOpacity(0.9),
|
||||||
|
fontSize: 9,
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Container(width: 2, height: 6, color: c),
|
Container(width: 2, height: 6, color: c),
|
||||||
Icon(icon, color: c, size: 22, shadows: const [
|
Icon(
|
||||||
Shadow(color: Colors.black45, blurRadius: 4, offset: Offset(0, 2))
|
icon,
|
||||||
]),
|
color: c,
|
||||||
|
size: 22,
|
||||||
|
shadows: const [
|
||||||
|
Shadow(color: Colors.black45, blurRadius: 4, offset: Offset(0, 2)),
|
||||||
|
],
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── Pulzáló GPS pont ─────────────────────────────────────────────────────────
|
class PulsingDot extends StatefulWidget {
|
||||||
|
|
||||||
class _PulsingDot extends StatefulWidget {
|
|
||||||
final Color color;
|
final Color color;
|
||||||
const _PulsingDot({required this.color});
|
const PulsingDot({super.key, required this.color});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<_PulsingDot> createState() => _PulsingDotState();
|
State<PulsingDot> createState() => _PulsingDotState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _PulsingDotState extends State<_PulsingDot>
|
class _PulsingDotState extends State<PulsingDot>
|
||||||
with SingleTickerProviderStateMixin {
|
with SingleTickerProviderStateMixin {
|
||||||
late AnimationController _ctrl;
|
late AnimationController _ctrl;
|
||||||
late Animation<double> _scale;
|
late Animation<double> _scale;
|
||||||
@ -540,10 +375,12 @@ class _PulsingDotState extends State<_PulsingDot>
|
|||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_ctrl = AnimationController(
|
_ctrl = AnimationController(
|
||||||
vsync: this, duration: const Duration(milliseconds: 1000))
|
vsync: this,
|
||||||
..repeat(reverse: true);
|
duration: const Duration(milliseconds: 1000),
|
||||||
_scale = Tween(begin: 0.8, end: 1.2)
|
)..repeat(reverse: true);
|
||||||
.animate(CurvedAnimation(parent: _ctrl, curve: Curves.easeInOut));
|
_scale = Tween(begin: 0.8, end: 1.2).animate(
|
||||||
|
CurvedAnimation(parent: _ctrl, curve: Curves.easeInOut),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -564,9 +401,10 @@ class _PulsingDotState extends State<_PulsingDot>
|
|||||||
border: Border.all(color: Colors.white, width: 2),
|
border: Border.all(color: Colors.white, width: 2),
|
||||||
boxShadow: [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: widget.color.withOpacity(0.5),
|
color: widget.color.withOpacity(0.5),
|
||||||
blurRadius: 8,
|
blurRadius: 8,
|
||||||
spreadRadius: 2)
|
spreadRadius: 2,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user