785 lines
28 KiB
Dart
785 lines
28 KiB
Dart
import 'dart:async';
|
|
import 'dart:convert';
|
|
import 'dart:io';
|
|
import 'dart:math';
|
|
|
|
import 'package:file_picker/file_picker.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/services.dart';
|
|
import 'package:flutter_map/flutter_map.dart';
|
|
// import 'package:flutter_map_geojson/flutter_map_geojson.dart';
|
|
import 'package:flutter_map_polywidget/flutter_map_polywidget.dart';
|
|
import 'package:geolocator/geolocator.dart';
|
|
import 'package:get/get.dart';
|
|
import 'package:intl/intl.dart';
|
|
// import 'package:location/location.dart';
|
|
import 'package:latlong2/latlong.dart';
|
|
import 'package:path_provider/path_provider.dart';
|
|
import 'package:share_plus/share_plus.dart';
|
|
import 'package:supabase_flutter/supabase_flutter.dart';
|
|
import 'package:terepi_seged/controls/geoid_grid.dart';
|
|
import 'package:terepi_seged/enums/map_survey_mode.dart';
|
|
import 'package:terepi_seged/eov/convert_coordinate.dart';
|
|
import 'package:terepi_seged/eov/eov.dart';
|
|
import 'package:terepi_seged/models/point_to_measure.dart';
|
|
import 'package:terepi_seged/models/point_with_description_model.dart';
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
import 'package:terepi_seged/pages/map_survey/presentations/views/measured_points_table_dialog.dart';
|
|
import 'package:terepi_seged/pages/ntrip_settings/presentation/controllers/ntrip_settings_controller.dart';
|
|
import 'package:terepi_seged/pages/ntrip_settings/presentation/views/ntrip_settings_sheet.dart';
|
|
import 'package:terepi_seged/services/coord_converter_service.dart';
|
|
import 'package:terepi_seged/services/gnss/gnss_connection.dart';
|
|
import 'package:terepi_seged/services/gnss/gnss_device_service.dart';
|
|
import 'package:terepi_seged/services/gnss/gnss_service.dart';
|
|
import 'package:terepi_seged/services/ntrip_service.dart';
|
|
|
|
class MapSurveyController extends GetxController {
|
|
static MapSurveyController get to => Get.find();
|
|
|
|
// ── Függőségek (service-ek) ───────────────────────────────────────
|
|
GnssService get _gnss => GnssService.to;
|
|
NtripService get _ntrip => NtripService.to;
|
|
|
|
final mode = MapSurveyMode.measure.obs;
|
|
final targetName = ''.obs;
|
|
final targetEovX = 0.0.obs;
|
|
final targetEovY = 0.0.obs;
|
|
|
|
double get deltaY => eov.value.Y - targetEovY.value;
|
|
double get deltaX => eov.value.X - targetEovX.value;
|
|
double get distanceToTarget => sqrt(deltaX * deltaX + deltaY * deltaY);
|
|
bool get isOnTarget => distanceToTarget < 0.05;
|
|
|
|
// ── GPS állapot — rövidítések a GnssService-hez ──────────────────
|
|
// Ezeket a view-ban közvetlenül a service-ből is lehetne olvasni,
|
|
// de a controller-en keresztül is hozzáférhetők a meglévő view kódhoz.
|
|
RxDouble get gpsLatitude => _gnss.latitude;
|
|
RxDouble get gpsLongitude => _gnss.longitude;
|
|
RxDouble get gpsAltitude => _gnss.altitude;
|
|
RxDouble get gpsGeoidSeparation => _gnss.geoidSeparation;
|
|
RxInt get gpsQuality => _gnss.gpsQuality;
|
|
RxDouble get gpsLatitudeError => _gnss.latitudeError;
|
|
RxDouble get gpsLongitudeError => _gnss.longitudeError;
|
|
RxDouble get gpsAltitudeError => _gnss.altitudeError;
|
|
Rx<DateTime> get gpsDateTime => _gnss.gpsDateTime;
|
|
bool get gpsIsConnected =>
|
|
_gnss.connectionState.value == GnssConnectionState.connected;
|
|
|
|
// NTRIP állapot
|
|
RxBool get ntripIsConnected => _ntrip.isConnected;
|
|
RxInt get ntripDataPacketNumbers => _ntrip.packetCount;
|
|
RxInt get ggaSenDataPacketNumber => _ntrip.ggaSentCount;
|
|
RxString get ggaSendLastTimeStr => _ntrip.ggaLastSentTime;
|
|
RxInt get ntripReceivedData => _ntrip.receivedBytes;
|
|
|
|
// ── Számformátumok ────────────────────────────────────────────────
|
|
final formatEov = NumberFormat('##0,000.0', 'hu-HU');
|
|
final formatEovZ = NumberFormat('###0.0', 'hu-HU');
|
|
final formatAltitudeError = NumberFormat('####0.000', 'hu-HU');
|
|
final formatEovForFile = NumberFormat('#####0.0', 'hu-HU');
|
|
final formatWgs84Sec = NumberFormat('00.000', 'hu-HU');
|
|
|
|
// ── EOV koordináták (számítottak) ─────────────────────────────────
|
|
Rx<Eov> eov = Eov(0, 0).obs;
|
|
Rx<double?> eovHeight = (0.0 as double?).obs;
|
|
|
|
final eovY = 0.0.obs;
|
|
final eovX = 0.0.obs;
|
|
|
|
// DMS formátum
|
|
RxInt latDegree = 0.obs;
|
|
RxInt latMin = 0.obs;
|
|
RxDouble latSec = 0.0.obs;
|
|
RxInt longDegree = 0.obs;
|
|
RxInt longMin = 0.obs;
|
|
RxDouble longSec = 0.0.obs;
|
|
|
|
// ── Térkép állapot ────────────────────────────────────────────────
|
|
static const double maxZoomValue = 25.0;
|
|
RxDouble currentLongitude = 0.0.obs;
|
|
RxDouble currentLatitude = 0.0.obs;
|
|
RxDouble currentZoom = 12.0.obs;
|
|
RxBool isMapMoveToCenter = true.obs;
|
|
RxBool mapIsInitialized = false.obs;
|
|
|
|
final MapController mapController = MapController();
|
|
|
|
final currentLocationMarker = <Marker>[].obs;
|
|
final pointNotesMarker = <Marker>[].obs;
|
|
final pointsToMeasureMarker = <Marker>[].obs;
|
|
final pointsToMeasureLabel = <PolyWidget>[].obs;
|
|
final pointsToMeasureDropDownMenuItem = <DropdownMenuItem<int>>[].obs;
|
|
|
|
// ── Pont adatok ───────────────────────────────────────────────────
|
|
final RxList<PointToMeasure> pointsToMeasure = <PointToMeasure>[].obs;
|
|
final RxList<PointWithDescription> pointWithDescriptionList =
|
|
<PointWithDescription>[].obs;
|
|
RxInt pointsToMeasureSelectedValue = (-1).obs;
|
|
RxDouble distance = 0.0.obs;
|
|
|
|
int pointId = 1;
|
|
String pointIdPrefix = '';
|
|
String pointIdPostfix = '';
|
|
Rx<bool> pointMeasuringDirectionForward = true.obs;
|
|
|
|
// ── Geoid grid ────────────────────────────────────────────────────
|
|
late GeoidGrid geoidGrid;
|
|
|
|
// ── Fájlok ────────────────────────────────────────────────────────
|
|
late Directory? directory;
|
|
late File dataFile;
|
|
|
|
// ── Telefon GPS fallback ──────────────────────────────────────────
|
|
StreamSubscription<Position>? _phoneLocationSub;
|
|
StreamSubscription<void>? _gnssUpdateSub;
|
|
|
|
// ── UI controllerek ───────────────────────────────────────────────
|
|
final pointIdController = TextEditingController();
|
|
final pointDescriptionController = TextEditingController();
|
|
final gpsHeightController = TextEditingController();
|
|
final pointPrefixController = TextEditingController();
|
|
final pointPostfixController = TextEditingController();
|
|
|
|
late SharedPreferences prefs;
|
|
Rx<bool> isShowPassword = false.obs;
|
|
|
|
// ── Supabase ──────────────────────────────────────────────────────
|
|
RealtimeChannel? _supaChannel;
|
|
|
|
// ─────────────────────────────────────────────────────────────────
|
|
// Lifecycle
|
|
// ─────────────────────────────────────────────────────────────────
|
|
|
|
@override
|
|
void onInit() {
|
|
super.onInit();
|
|
_initAsync();
|
|
}
|
|
|
|
Future<void> _initAsync() async {
|
|
prefs = await SharedPreferences.getInstance();
|
|
geoidGrid = await GeoidGrid.load('assets/Grids/geoid_eht2014.gtx');
|
|
|
|
NtripService.to.onRtcmData = (data) => GnssService.to.sendToReceiver(data);
|
|
_gnssUpdateSub = _gnss.onDataUpdated.listen((_) => _onGnssUpdate());
|
|
|
|
// ── Supabase realtime ─────────────────────────────────────────
|
|
_supaChannel = Supabase.instance.client
|
|
.channel('public:TerepiSeged_Receiver')
|
|
.onPostgresChanges(
|
|
event: PostgresChangeEvent.update,
|
|
schema: 'public',
|
|
table: 'TerepiSeged_Receiver',
|
|
callback: (payload) {
|
|
final id = payload.newRecord['pointNumber'] as int?;
|
|
if (id != null) updatePointStatus(id);
|
|
},
|
|
)
|
|
.subscribe();
|
|
|
|
mapIsInitialized.value = true;
|
|
}
|
|
|
|
@override
|
|
void onReady() async {
|
|
super.onReady();
|
|
|
|
await _initStorage();
|
|
|
|
gpsHeightController.text = '1.8';
|
|
}
|
|
|
|
@override
|
|
void onClose() {
|
|
_phoneLocationSub?.cancel();
|
|
_gnssUpdateSub?.cancel();
|
|
final f = _supaChannel?.unsubscribe();
|
|
if (f != null) unawaited(f);
|
|
|
|
pointIdController.dispose();
|
|
pointDescriptionController.dispose();
|
|
gpsHeightController.dispose();
|
|
pointPrefixController.dispose();
|
|
pointPostfixController.dispose();
|
|
|
|
super.onClose();
|
|
}
|
|
|
|
// ─────────────────────────────────────────────────────────────────
|
|
// GnssService frissítés kezelése
|
|
// ─────────────────────────────────────────────────────────────────
|
|
|
|
void _onGnssUpdate() {
|
|
if (!_gnss.hasValidData) return;
|
|
|
|
final lat = _gnss.latitude.value;
|
|
final lon = _gnss.longitude.value;
|
|
final alt = _gnss.altitude.value;
|
|
final sep = _gnss.geoidSeparation.value;
|
|
|
|
// EOV konverzió
|
|
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);
|
|
|
|
// DMS
|
|
latDegree.value = ConvertCoordinate.toDegree(lat);
|
|
latMin.value = ConvertCoordinate.toMinute(lat);
|
|
latSec.value = ConvertCoordinate.toSecond(lat);
|
|
longDegree.value = ConvertCoordinate.toDegree(lon);
|
|
longMin.value = ConvertCoordinate.toMinute(lon);
|
|
longSec.value = ConvertCoordinate.toSecond(lon);
|
|
|
|
// Távolság a kiválasztott ponthoz
|
|
if (pointsToMeasureSelectedValue.value >= 0 &&
|
|
pointsToMeasureSelectedValue.value < pointsToMeasure.length) {
|
|
final pt = pointsToMeasure[pointsToMeasureSelectedValue.value];
|
|
final wgs = CoordConverterService.to.eovToWgsPoint(pt.coordX, pt.coordY);
|
|
distance.value = calculateDistance(
|
|
LatLng(lat, lon),
|
|
LatLng(wgs.y, wgs.x),
|
|
);
|
|
}
|
|
|
|
// Térkép pozíció
|
|
currentLatitude.value = lat;
|
|
currentLongitude.value = lon;
|
|
_updateCurrentLocationMarker();
|
|
|
|
// NTRIP GGA küldés
|
|
NtripService.to.onGgaReceived(
|
|
_gnss.lastGgaLine.value,
|
|
_gnss.utcFix.value,
|
|
);
|
|
}
|
|
|
|
String _formatMeter(double? value, [int decimalSpace = 3]) {
|
|
if (value == null || value.isNaN || value.isInfinite) {
|
|
return '-';
|
|
}
|
|
|
|
return '${value.toStringAsFixed(decimalSpace)} m';
|
|
}
|
|
|
|
String get verticalAccuracyText {
|
|
return _formatMeter(gpsAltitudeError.value);
|
|
}
|
|
|
|
String get horizontalAccuracyText {
|
|
return _formatMeter(max(gpsLatitudeError.value, gpsLongitudeError.value));
|
|
}
|
|
|
|
void setMode(MapSurveyMode newMode) {
|
|
mode.value = newMode;
|
|
|
|
// Itt lehet módhoz kötött állapotokat állítani:
|
|
// - alsó panel tartalma
|
|
// - térképi tap viselkedés
|
|
// - aktív kártyák
|
|
// - track indítás/leállítás figyelmeztetés stb.
|
|
}
|
|
|
|
String get currentModeLabel {
|
|
switch (mode.value) {
|
|
case MapSurveyMode.browse:
|
|
return 'Térkép';
|
|
case MapSurveyMode.measure:
|
|
return 'Bemérés';
|
|
case MapSurveyMode.stakeout:
|
|
return 'Kitűzés';
|
|
case MapSurveyMode.fieldWalk:
|
|
return 'Bejárás';
|
|
}
|
|
}
|
|
|
|
IconData get currentModeIcon {
|
|
switch (mode.value) {
|
|
case MapSurveyMode.browse:
|
|
return Icons.map;
|
|
case MapSurveyMode.measure:
|
|
return Icons.add_location_alt;
|
|
case MapSurveyMode.stakeout:
|
|
return Icons.gps_fixed;
|
|
case MapSurveyMode.fieldWalk:
|
|
return Icons.hiking;
|
|
}
|
|
}
|
|
|
|
void openNtripsettings() {
|
|
if (!Get.isRegistered<NtripSettingsController>()) {
|
|
Get.put(NtripSettingsController());
|
|
}
|
|
|
|
Get.bottomSheet(const NtripSettingsSheet(),
|
|
isScrollControlled: true,
|
|
backgroundColor: Colors.transparent,
|
|
ignoreSafeArea: false);
|
|
}
|
|
|
|
void showNtripStatus() {
|
|
// Get.bottomSheet(
|
|
// const NtripStatusSheet(),
|
|
// isScrollControlled: true,
|
|
// backgroundColor: Colors.transparent,
|
|
// );
|
|
}
|
|
|
|
// ─────────────────────────────────────────────────────────────────
|
|
// Térkép vezérlők
|
|
// ─────────────────────────────────────────────────────────────────
|
|
|
|
void mapZoomIn() {
|
|
if (currentZoom.value >= maxZoomValue) return;
|
|
currentZoom.value++;
|
|
_moveMap();
|
|
}
|
|
|
|
void mapZoomOut() {
|
|
if (currentZoom.value <= 0) return;
|
|
currentZoom.value--;
|
|
_moveMap();
|
|
}
|
|
|
|
void setIsMapMoveToCenter() =>
|
|
isMapMoveToCenter.value = !isMapMoveToCenter.value;
|
|
|
|
void _moveMap() {
|
|
final lat = isMapMoveToCenter.value
|
|
? currentLatitude.value
|
|
: mapController.camera.center.latitude;
|
|
final lon = isMapMoveToCenter.value
|
|
? currentLongitude.value
|
|
: mapController.camera.center.longitude;
|
|
mapController.move(LatLng(lat, lon), currentZoom.value);
|
|
}
|
|
|
|
void _updateCurrentLocationMarker() {
|
|
currentLocationMarker.assignAll([
|
|
Marker(
|
|
point: LatLng(currentLatitude.value, currentLongitude.value),
|
|
width: 15.0,
|
|
height: 15.0,
|
|
child: Container(
|
|
width: 15.0,
|
|
height: 15.0,
|
|
decoration: BoxDecoration(
|
|
color: getCurrentLocationMarkerColor(_gnss.gpsQuality.value),
|
|
shape: BoxShape.circle,
|
|
border: Border.all(width: 1.5, color: Colors.white),
|
|
),
|
|
),
|
|
)
|
|
]);
|
|
|
|
if (isMapMoveToCenter.value) {
|
|
mapController.move(
|
|
LatLng(currentLatitude.value, currentLongitude.value),
|
|
currentZoom.value,
|
|
);
|
|
}
|
|
}
|
|
|
|
Color getCurrentLocationMarkerColor(int quality) => switch (quality) {
|
|
0 => Colors.black,
|
|
1 => Colors.red,
|
|
2 => Colors.blue,
|
|
4 => Colors.green,
|
|
5 => Colors.orange,
|
|
6 => Colors.yellow,
|
|
_ => Colors.white,
|
|
};
|
|
|
|
String getGpsQualityIndicator({required int quality}) => switch (quality) {
|
|
0 => 'Invalid',
|
|
1 => 'Standard GPS',
|
|
2 => 'Differential GPS',
|
|
4 => 'RTK Fix',
|
|
5 => 'RTK Float',
|
|
6 => 'Estimated (DR)',
|
|
_ => 'Invalid',
|
|
};
|
|
|
|
// ─────────────────────────────────────────────────────────────────
|
|
// Pont mentés dialóg
|
|
// ─────────────────────────────────────────────────────────────────
|
|
|
|
void showAddPointDialog() => onBottomNavigationBarTap(0);
|
|
|
|
void onBottomNavigationBarTap(int index) async {
|
|
if (index != 0) return;
|
|
|
|
if (pointsToMeasureSelectedValue.value >= 0 &&
|
|
pointsToMeasureSelectedValue.value < pointsToMeasure.length) {
|
|
pointIdController.text =
|
|
'${pointsToMeasure[pointsToMeasureSelectedValue.value].id}';
|
|
} else {
|
|
pointIdController.text = pointId.toString();
|
|
}
|
|
pointDescriptionController.text = '';
|
|
|
|
Get.dialog(
|
|
AlertDialog(
|
|
title: const Text('Pont rögzítése'),
|
|
content: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
TextField(
|
|
controller: pointIdController,
|
|
autofocus: true,
|
|
keyboardType: TextInputType.number,
|
|
decoration: const InputDecoration(
|
|
border: OutlineInputBorder(), labelText: 'Azonosító'),
|
|
),
|
|
const SizedBox(height: 20),
|
|
TextField(
|
|
controller: pointDescriptionController,
|
|
decoration: const InputDecoration(
|
|
border: OutlineInputBorder(), labelText: 'Leírás'),
|
|
),
|
|
const SizedBox(height: 20),
|
|
TextField(
|
|
controller: gpsHeightController,
|
|
keyboardType: TextInputType.number,
|
|
decoration: const InputDecoration(
|
|
border: OutlineInputBorder(), labelText: 'GPS magasság'),
|
|
),
|
|
],
|
|
),
|
|
actions: [
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
|
children: [
|
|
OutlinedButton(
|
|
style:
|
|
OutlinedButton.styleFrom(minimumSize: const Size(120, 40)),
|
|
onPressed: () => Get.back(),
|
|
child:
|
|
const Text('Mégsem', style: TextStyle(color: Colors.red)),
|
|
),
|
|
OutlinedButton(
|
|
style:
|
|
OutlinedButton.styleFrom(minimumSize: const Size(120, 40)),
|
|
onPressed: _saveCurrentPoint,
|
|
child: const Text('Ment',
|
|
style: TextStyle(
|
|
color: Colors.green, fontWeight: FontWeight.bold)),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
barrierDismissible: false,
|
|
);
|
|
}
|
|
|
|
Future<void> _saveCurrentPoint() async {
|
|
pointId = int.tryParse(pointIdController.text) ?? pointId;
|
|
|
|
// Helyi lista
|
|
pointWithDescriptionList.add(PointWithDescription(
|
|
pointId,
|
|
_gnss.gpsDateTime.value,
|
|
pointDescriptionController.text,
|
|
eov.value.Y,
|
|
eov.value.X,
|
|
_gnss.latitude.value,
|
|
_gnss.longitude.value,
|
|
max(_gnss.latitudeError.value, _gnss.longitudeError.value),
|
|
_gnss.altitudeError.value,
|
|
));
|
|
|
|
// Marker hozzáadása
|
|
pointNotesMarker.add(Marker(
|
|
point: LatLng(_gnss.latitude.value, _gnss.longitude.value),
|
|
width: 15.0,
|
|
height: 15.0,
|
|
child: Container(
|
|
width: 15.0,
|
|
height: 15.0,
|
|
decoration: BoxDecoration(
|
|
color: Colors.amber[700],
|
|
shape: BoxShape.circle,
|
|
border: Border.all(width: 1.0, color: Colors.black),
|
|
),
|
|
),
|
|
));
|
|
|
|
// Fájl mentés
|
|
await dataFile.writeAsString(
|
|
'$pointId;${_gnss.gpsDateTime.value};'
|
|
'${pointDescriptionController.text};'
|
|
'${formatEovForFile.format(eov.value.Y)};'
|
|
'${formatEovForFile.format(eov.value.X)};'
|
|
'${_gnss.latitude.value};${_gnss.longitude.value};'
|
|
'${_gnss.altitude.value};'
|
|
'${max(_gnss.latitudeError.value, _gnss.longitudeError.value)};'
|
|
'${_gnss.altitudeError.value};'
|
|
'${gpsHeightController.text}\r\n',
|
|
mode: FileMode.append,
|
|
);
|
|
|
|
// Supabase mentés
|
|
await Supabase.instance.client.from('TerepiSeged_MeasuredPoints').insert({
|
|
'pointNumber': pointId,
|
|
'gnssNumber': GnssDeviceService.to.selectedDevice.value?.name ?? '',
|
|
'latitude': _gnss.latitude.value,
|
|
'longitude': _gnss.longitude.value,
|
|
'altitude': _gnss.altitude.value,
|
|
'heightOfGeoid': _gnss.geoidSeparation.value,
|
|
'eovX': eov.value.X,
|
|
'eovY': eov.value.Y,
|
|
'poleHeight': double.tryParse(gpsHeightController.text),
|
|
'horizontalError':
|
|
max(_gnss.latitudeError.value, _gnss.longitudeError.value),
|
|
'verticalError': _gnss.altitudeError.value,
|
|
'description': pointDescriptionController.text,
|
|
'isDeleted': false,
|
|
'projectId': 2,
|
|
});
|
|
|
|
await Supabase.instance.client
|
|
.from('TerepiSeged_Receiver')
|
|
.update({'isMeasured': true}).eq('pointNumber', pointId);
|
|
|
|
// Következő pont léptetése
|
|
_advancePointSelection();
|
|
|
|
Get.back();
|
|
}
|
|
|
|
void _advancePointSelection() {
|
|
final len = pointsToMeasure.length;
|
|
if (len == 0) return;
|
|
|
|
if (pointMeasuringDirectionForward.isTrue) {
|
|
if (pointsToMeasureSelectedValue.value < len - 1) {
|
|
pointId++;
|
|
pointsToMeasureSelectedValue.value++;
|
|
} else {
|
|
_showNoMorePoints();
|
|
}
|
|
} else {
|
|
if (pointsToMeasureSelectedValue.value > 0) {
|
|
pointId--;
|
|
pointsToMeasureSelectedValue.value--;
|
|
} else {
|
|
_showNoMorePoints();
|
|
}
|
|
}
|
|
}
|
|
|
|
void _showNoMorePoints() {
|
|
ScaffoldMessenger.of(Get.context!).showSnackBar(
|
|
const SnackBar(content: Text('Nincs több bemérendő pont.')),
|
|
);
|
|
}
|
|
|
|
// ─────────────────────────────────────────────────────────────────
|
|
// Pont betöltés
|
|
// ─────────────────────────────────────────────────────────────────
|
|
|
|
void ReadPointsFromFile() async {
|
|
final result = await FilePicker.platform.pickFiles();
|
|
if (result == null) return;
|
|
|
|
final file = File(result.files.single.path!);
|
|
if (!await file.exists()) return;
|
|
|
|
_clearPoints();
|
|
final content = await file.readAsLines();
|
|
|
|
for (final (index, item) in content.indexed) {
|
|
if (index == 0) continue;
|
|
_addPointFromCsv(item, index - 1);
|
|
}
|
|
if (pointsToMeasure.isNotEmpty) {
|
|
pointsToMeasureSelectedValue.value = 0;
|
|
}
|
|
}
|
|
|
|
void readPointsFromSupa() async {
|
|
final data =
|
|
await Supabase.instance.client.from('TerepiSeged_Receiver').select();
|
|
|
|
_clearPoints();
|
|
for (int i = 0; i < data.length; i++) {
|
|
final item = data[i];
|
|
final id = item['pointNumber'];
|
|
final coordX = (item['eovX'] as num?)?.toDouble();
|
|
final coordY = (item['eovY'] as num?)?.toDouble();
|
|
if (id == null || coordX == null || coordY == null) continue;
|
|
_addPoint(id: id, coordX: coordX, coordY: coordY, listIndex: i);
|
|
}
|
|
if (pointsToMeasure.isNotEmpty) {
|
|
pointsToMeasureSelectedValue.value = 0;
|
|
}
|
|
}
|
|
|
|
void _clearPoints() {
|
|
pointsToMeasure.clear();
|
|
pointsToMeasureMarker.clear();
|
|
pointsToMeasureLabel.clear();
|
|
pointsToMeasureDropDownMenuItem.clear();
|
|
}
|
|
|
|
void _addPointFromCsv(String line, int listIndex) {
|
|
final parts = line.split(';');
|
|
if (parts.length < 3) return;
|
|
final id = int.tryParse(parts[0]);
|
|
final coordX = double.tryParse(parts[1].replaceAll(',', '.'));
|
|
final coordY = double.tryParse(parts[2].replaceAll(',', '.'));
|
|
if (id == null || coordX == null || coordY == null) return;
|
|
_addPoint(id: id, coordX: coordX, coordY: coordY, listIndex: listIndex);
|
|
}
|
|
|
|
void _addPoint({
|
|
required int id,
|
|
required double coordX,
|
|
required double coordY,
|
|
required int listIndex,
|
|
}) {
|
|
pointsToMeasure.add(PointToMeasure(id: id, coordX: coordX, coordY: coordY));
|
|
|
|
final wgs = CoordConverterService.to.eovToWgsPoint(coordX, coordY);
|
|
|
|
pointsToMeasureMarker.add(Marker(
|
|
point: LatLng(wgs.y, wgs.x),
|
|
width: 15.0,
|
|
height: 15.0,
|
|
child: Container(
|
|
width: 15.0,
|
|
height: 15.0,
|
|
decoration: BoxDecoration(
|
|
color: Colors.purple,
|
|
shape: BoxShape.circle,
|
|
border: Border.all(width: 1.0, color: Colors.black),
|
|
),
|
|
),
|
|
));
|
|
|
|
pointsToMeasureLabel.add(PolyWidget(
|
|
center: LatLng(wgs.y + 0.0000075, wgs.x + 0.0000075),
|
|
widthInMeters: 3,
|
|
heightInMeters: 3,
|
|
child: FittedBox(
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(4),
|
|
child: Text(
|
|
' $id ',
|
|
style: const TextStyle(
|
|
fontWeight: FontWeight.bold,
|
|
color: Colors.yellow,
|
|
fontSize: 12,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
));
|
|
|
|
pointsToMeasureDropDownMenuItem.add(DropdownMenuItem<int>(
|
|
value: listIndex,
|
|
child: Text('$id'),
|
|
));
|
|
}
|
|
|
|
void pointsToMeasureSelectedValueChanged(int value) =>
|
|
pointsToMeasureSelectedValue.value = value;
|
|
|
|
// ─────────────────────────────────────────────────────────────────
|
|
// Segédmetódusok
|
|
// ─────────────────────────────────────────────────────────────────
|
|
|
|
double calculateDistance(LatLng start, LatLng end) {
|
|
const r = 6371000.0;
|
|
final lat1 = start.latitude * (pi / 180);
|
|
final lon1 = start.longitude * (pi / 180);
|
|
final lat2 = end.latitude * (pi / 180);
|
|
final lon2 = end.longitude * (pi / 180);
|
|
final dLat = lat2 - lat1;
|
|
final dLon = lon2 - lon1;
|
|
final a = sin(dLat / 2) * sin(dLat / 2) +
|
|
cos(lat1) * cos(lat2) * sin(dLon / 2) * sin(dLon / 2);
|
|
return r * 2 * atan2(sqrt(a), sqrt(1 - a));
|
|
}
|
|
|
|
void updatePointStatus(int pointId) {}
|
|
|
|
void showMeasuredPointsTableDialog() =>
|
|
Get.to(() => MeasuredPointsTableDialog(), transition: Transition.fadeIn);
|
|
|
|
// ─────────────────────────────────────────────────────────────────
|
|
// Tárhely inicializálás
|
|
// ─────────────────────────────────────────────────────────────────
|
|
|
|
Future<void> _initStorage() async {
|
|
directory = await getExternalStorageDirectory();
|
|
if (directory != null && !await directory!.exists()) {
|
|
await directory!.create(recursive: true);
|
|
}
|
|
dataFile = File('${directory!.path}/data.txt');
|
|
if (!await dataFile.exists()) {
|
|
await dataFile.writeAsString(
|
|
'Id;DateTime;Description;EovX;EovY;Latitude;Longitude;Altitude;Hor.Err;Vert.Err\r\n',
|
|
);
|
|
}
|
|
}
|
|
|
|
// ─────────────────────────────────────────────────────────────────
|
|
// Export
|
|
// ─────────────────────────────────────────────────────────────────
|
|
|
|
Future<List> readMeasuredPoints() async => Supabase.instance.client
|
|
.from('TerepiSeged_MeasuredPoints')
|
|
.select()
|
|
.eq('projectId', 2)
|
|
.order('created_at');
|
|
|
|
void SaveMeasuredPointsToFile() async {
|
|
final dir = await getApplicationDocumentsDirectory();
|
|
final file = File('${dir.path}/measuredsPoints.csv');
|
|
|
|
if (await file.exists()) await file.delete();
|
|
await file.create();
|
|
await file.writeAsString(
|
|
'Id;DateTime;Description;EovX;EovY;Altitude;Hor.Err;Vert.Err\r\n');
|
|
|
|
final data = await readMeasuredPoints();
|
|
for (final d in data) {
|
|
file.writeAsStringSync(
|
|
'${d['id']};${d['created_at']};${d['description']};'
|
|
'${formatEov.format(d['eovY'])};${formatEov.format(d['eovX'])};'
|
|
'${formatEovZ.format((d['altitude'] as num) - (d['poleHeight'] as num))};'
|
|
'${formatAltitudeError.format(d['horizontalError'])};'
|
|
'${formatAltitudeError.format(d['verticalError'])}\r\n',
|
|
mode: FileMode.append,
|
|
encoding: utf8,
|
|
);
|
|
}
|
|
|
|
await SharePlus.instance.share(ShareParams(
|
|
text: 'Mérési eredmények',
|
|
files: [XFile('${dir.path}/measuredsPoints.csv')],
|
|
subject: 'Mérési eredmények',
|
|
));
|
|
}
|
|
|
|
void switchMode(MapSurveyMode newMode) {
|
|
mode.value = newMode;
|
|
if (newMode == MapSurveyMode.measure) {
|
|
targetName.value = '';
|
|
targetEovY.value = 0;
|
|
targetEovX.value = 0;
|
|
}
|
|
}
|
|
|
|
void setTarget(String name, double y, double x) {
|
|
targetName.value = name;
|
|
targetEovY.value = y;
|
|
targetEovX.value = x;
|
|
mode.value = MapSurveyMode.stakeout;
|
|
}
|
|
}
|