Térképi rétegkezelő lérehozása, track online mentésének előkészítése
This commit is contained in:
parent
644201dc8a
commit
beb07fe007
1
lib/enums/map_layer_type.dart
Normal file
1
lib/enums/map_layer_type.dart
Normal file
@ -0,0 +1 @@
|
||||
enum LayerType { geojson, shapefile, dxf, csv, postgis, wms, track, gnss }
|
||||
1
lib/enums/map_layer_visibility.dart
Normal file
1
lib/enums/map_layer_visibility.dart
Normal file
@ -0,0 +1 @@
|
||||
enum LayerVisibility { visible, hidden }
|
||||
11
lib/map_layers/map_dxf_layer.dart
Normal file
11
lib/map_layers/map_dxf_layer.dart
Normal file
@ -0,0 +1,11 @@
|
||||
// import 'package:flutter/material.dart';
|
||||
// import 'package:terepi_seged/map_layers/map_layer.dart';
|
||||
|
||||
// import 'map_geojson_layer.dart';
|
||||
|
||||
// class DxfLayer extends MapLayer {
|
||||
// // DXF parsing — nincs Flutter csomag, szerver oldali konverzió kell
|
||||
// // Supabase Edge Function: DXF → GeoJSON (ogr2ogr alapú)
|
||||
// @override
|
||||
// Widget buildFlutterMapLayer() => GeoJsonLayer.fromFeatures(_features);
|
||||
// }
|
||||
17
lib/map_layers/map_geojson_layer.dart
Normal file
17
lib/map_layers/map_geojson_layer.dart
Normal file
@ -0,0 +1,17 @@
|
||||
// import 'package:flutter/material.dart';
|
||||
// import 'package:flutter_map/flutter_map.dart';
|
||||
|
||||
// import 'map_layer.dart';
|
||||
|
||||
// class GeoJsonLayer extends MapLayer {
|
||||
// final String sourceUrl; // Supabase Storage URL
|
||||
// final String? localPath; // offline cache
|
||||
// FeatureCollection? _data;
|
||||
|
||||
// @override
|
||||
// Widget buildFlutterMapLayer() {
|
||||
// if (_data == null) return const SizedBox.shrink();
|
||||
// return PolygonLayer(polygons: _buildPolygons());
|
||||
// // vagy PolylineLayer / MarkerLayer a geometry type-tól függően
|
||||
// }
|
||||
// }
|
||||
20
lib/map_layers/map_layer.dart
Normal file
20
lib/map_layers/map_layer.dart
Normal file
@ -0,0 +1,20 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:terepi_seged/enums/map_layer_type.dart';
|
||||
import 'package:terepi_seged/enums/map_layer_visibility.dart';
|
||||
|
||||
abstract class MapLayer {
|
||||
late final String id;
|
||||
late final String name;
|
||||
late final LayerType type;
|
||||
late LayerVisibility visibility;
|
||||
late double opacity;
|
||||
late int zIndex;
|
||||
//LayerStyle style;
|
||||
|
||||
// Minden réteg tudja magát flutter_map widget-té alakítani
|
||||
Widget buildFlutterMapLayer();
|
||||
|
||||
// Offline cache-elhetőség
|
||||
Future<void> prefetch();
|
||||
bool get isCached;
|
||||
}
|
||||
23
lib/map_layers/map_postgis_layer.dart
Normal file
23
lib/map_layers/map_postgis_layer.dart
Normal file
@ -0,0 +1,23 @@
|
||||
// import 'package:flutter/material.dart';
|
||||
// import 'package:flutter_map/flutter_map.dart';
|
||||
// import 'package:supabase_flutter/supabase_flutter.dart';
|
||||
|
||||
// import 'map_layer.dart';
|
||||
|
||||
// class PostGisLayer extends MapLayer {
|
||||
// final String table; // Supabase tábla neve
|
||||
// final String geomColumn; // geometry oszlop
|
||||
// final String? filter; // pl. "project_id=eq.123"
|
||||
// final BoundingBox? bbox; // csak látható terület lekérése
|
||||
|
||||
// @override
|
||||
// Widget buildFlutterMapLayer() {
|
||||
// // flutter_map_tile_caching + PostGIS tile endpoint
|
||||
// return TileLayer(urlTemplate: _buildMvtUrl());
|
||||
// }
|
||||
|
||||
// // MapBox Vector Tiles (MVT) Supabase Edge Function-ből
|
||||
// String _buildMvtUrl() =>
|
||||
// '${Supabase.instance.client.supabaseUrl}/functions/v1/tiles'
|
||||
// '/$table/{z}/{x}/{y}?geom=$geomColumn${filter != null ? "&filter=$filter" : ""}';
|
||||
// }
|
||||
11
lib/map_layers/map_shapefile_layer.dart
Normal file
11
lib/map_layers/map_shapefile_layer.dart
Normal file
@ -0,0 +1,11 @@
|
||||
// import 'package:flutter/material.dart';
|
||||
|
||||
// import 'map_geojson_layer.dart';
|
||||
// import 'map_layer.dart';
|
||||
|
||||
// class ShapefileLayer extends MapLayer {
|
||||
// // SHP → GeoJSON konverzió device-on
|
||||
// // shapefile_dart csomag
|
||||
// @override
|
||||
// Widget buildFlutterMapLayer() => GeoJsonLayer.fromFeatures(_features);
|
||||
// }
|
||||
@ -54,6 +54,9 @@ class TrackPoint {
|
||||
|
||||
enum TrackStatus { recording, paused, finished }
|
||||
|
||||
enum TrackSyncMode { localOnly, online }
|
||||
|
||||
enum TrackSyncStatus { pending, synced, error }
|
||||
// ─── Track ───────────────────────────────────────────────────────────────────
|
||||
|
||||
class Track {
|
||||
@ -68,9 +71,12 @@ class Track {
|
||||
// Statisztikák — ezeket a DB is tárolja a gyors listázáshoz
|
||||
final double distanceMeters;
|
||||
final int pointCount;
|
||||
final TrackSyncMode syncMode;
|
||||
final TrackSyncStatus syncStatus;
|
||||
final String? supabaseId;
|
||||
|
||||
const Track({
|
||||
this.id,
|
||||
const Track(
|
||||
{this.id,
|
||||
this.projectId,
|
||||
required this.name,
|
||||
required this.startTime,
|
||||
@ -79,10 +85,12 @@ class Track {
|
||||
this.source = 'Telefon GPS',
|
||||
this.distanceMeters = 0,
|
||||
this.pointCount = 0,
|
||||
});
|
||||
this.syncMode = TrackSyncMode.online,
|
||||
this.syncStatus = TrackSyncStatus.pending,
|
||||
this.supabaseId});
|
||||
|
||||
Track copyWith({
|
||||
int? id,
|
||||
Track copyWith(
|
||||
{int? id,
|
||||
int? projectId,
|
||||
String? name,
|
||||
DateTime? startTime,
|
||||
@ -91,7 +99,9 @@ class Track {
|
||||
String? source,
|
||||
double? distanceMeters,
|
||||
int? pointCount,
|
||||
}) =>
|
||||
TrackSyncMode? syncMode,
|
||||
TrackSyncStatus? syncStatus,
|
||||
String? supabaseId}) =>
|
||||
Track(
|
||||
id: id ?? this.id,
|
||||
projectId: projectId ?? this.projectId,
|
||||
@ -102,7 +112,9 @@ class Track {
|
||||
source: source ?? this.source,
|
||||
distanceMeters: distanceMeters ?? this.distanceMeters,
|
||||
pointCount: pointCount ?? this.pointCount,
|
||||
);
|
||||
syncMode: syncMode ?? this.syncMode,
|
||||
syncStatus: syncStatus ?? this.syncStatus,
|
||||
supabaseId: supabaseId ?? this.supabaseId);
|
||||
|
||||
/// Formázott időtartam (óó:pp:mm)
|
||||
String get durationFormatted {
|
||||
@ -132,6 +144,9 @@ class Track {
|
||||
'source': source,
|
||||
'distance_m': distanceMeters,
|
||||
'point_count': pointCount,
|
||||
'is_local_only': syncMode == TrackSyncMode.localOnly ? 1 : 0,
|
||||
'sync_status': syncStatus.name,
|
||||
'supabase_id': supabaseId,
|
||||
};
|
||||
|
||||
factory Track.fromMap(Map<String, dynamic> m) => Track(
|
||||
@ -149,7 +164,14 @@ class Track {
|
||||
source: m['source'] as String? ?? 'Telefon GPS',
|
||||
distanceMeters: (m['distance_m'] as num?)?.toDouble() ?? 0,
|
||||
pointCount: m['point_count'] as int? ?? 0,
|
||||
);
|
||||
syncMode: (m['is_local_only'] as int?) == 1
|
||||
? TrackSyncMode.localOnly
|
||||
: TrackSyncMode.online,
|
||||
syncStatus: TrackSyncStatus.values
|
||||
.byName(m['sync_status'] as String? ?? 'pending'),
|
||||
supabaseId: m['supabase_id'] as String?);
|
||||
|
||||
bool get isLocalOnly => syncMode == TrackSyncMode.localOnly;
|
||||
}
|
||||
|
||||
// ─── Segédszámítás: Haversine távolság ───────────────────────────────────────
|
||||
|
||||
@ -5,6 +5,7 @@ import 'package:get/get.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:latlong2/latlong.dart';
|
||||
import 'package:share_plus/share_plus.dart';
|
||||
import 'package:terepi_seged/services/project_service.dart';
|
||||
|
||||
import '../../../../services/location_source.dart';
|
||||
import '../../../../services/phone_gps_source.dart';
|
||||
@ -116,19 +117,23 @@ class TrackingController extends GetxController {
|
||||
// ── Publikus API ───────────────────────────────────────────────────────────
|
||||
|
||||
/// Elindítja a rögzítést a megadott forrással (alapértelmezett: telefon GPS).
|
||||
Future<void> startRecording({LocationSource? source}) async {
|
||||
Future<void> startRecording(
|
||||
{LocationSource? source,
|
||||
String? name,
|
||||
TrackSyncMode syncMode = TrackSyncMode.online}) async {
|
||||
if (isRecording.value) return;
|
||||
|
||||
_source = source ?? PhoneGpsSource(intervalMs: 1000, distanceFilter: 2.0);
|
||||
|
||||
// Track létrehozása az adatbázisban
|
||||
final now = DateTime.now();
|
||||
final name = DateFormat('yyyy-MM-dd HH:mm').format(now);
|
||||
final trackName = name ?? DateFormat('yyyy-MM-dd HH:mm').format(now);
|
||||
final trackId = await _db.insertTrack(Track(
|
||||
name: name,
|
||||
name: trackName,
|
||||
startTime: now,
|
||||
source: _source!.displayName,
|
||||
));
|
||||
projectId: ProjectService.to.activeProjectId,
|
||||
syncMode: syncMode));
|
||||
currentTrack.value = await _db.getTrack(trackId);
|
||||
|
||||
// Állapot reset
|
||||
@ -141,7 +146,7 @@ class TrackingController extends GetxController {
|
||||
// Foreground service indítása (háttér-működéshez)
|
||||
await FlutterForegroundTask.startService(
|
||||
notificationTitle: 'Track rögzítése',
|
||||
notificationText: name,
|
||||
notificationText: trackName,
|
||||
callback: startTrackingCallback,
|
||||
);
|
||||
|
||||
|
||||
@ -87,7 +87,10 @@ class AppDatabase {
|
||||
status TEXT NOT NULL DEFAULT 'recording',
|
||||
source TEXT NOT NULL DEFAULT 'Telefon GPS',
|
||||
distance_m REAL NOT NULL DEFAULT 0,
|
||||
point_count INTEGER NOT NULL DEFAULT 0
|
||||
point_count INTEGER NOT NULL DEFAULT 0,
|
||||
is_local_only INTEGER NOT NULL DEFAULT 0,
|
||||
sync_status TEXT NOT NULL DEFAULT 'pending',
|
||||
supabase_id TEXT
|
||||
)
|
||||
''');
|
||||
await db.execute('CREATE INDEX idx_tracks_project ON tracks(project_id)');
|
||||
|
||||
Loading…
Reference in New Issue
Block a user