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 TrackStatus { recording, paused, finished }
|
||||||
|
|
||||||
|
enum TrackSyncMode { localOnly, online }
|
||||||
|
|
||||||
|
enum TrackSyncStatus { pending, synced, error }
|
||||||
// ─── Track ───────────────────────────────────────────────────────────────────
|
// ─── Track ───────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
class Track {
|
class Track {
|
||||||
@ -68,41 +71,50 @@ class Track {
|
|||||||
// Statisztikák — ezeket a DB is tárolja a gyors listázáshoz
|
// Statisztikák — ezeket a DB is tárolja a gyors listázáshoz
|
||||||
final double distanceMeters;
|
final double distanceMeters;
|
||||||
final int pointCount;
|
final int pointCount;
|
||||||
|
final TrackSyncMode syncMode;
|
||||||
|
final TrackSyncStatus syncStatus;
|
||||||
|
final String? supabaseId;
|
||||||
|
|
||||||
const Track({
|
const Track(
|
||||||
this.id,
|
{this.id,
|
||||||
this.projectId,
|
this.projectId,
|
||||||
required this.name,
|
required this.name,
|
||||||
required this.startTime,
|
required this.startTime,
|
||||||
this.endTime,
|
this.endTime,
|
||||||
this.status = TrackStatus.recording,
|
this.status = TrackStatus.recording,
|
||||||
this.source = 'Telefon GPS',
|
this.source = 'Telefon GPS',
|
||||||
this.distanceMeters = 0,
|
this.distanceMeters = 0,
|
||||||
this.pointCount = 0,
|
this.pointCount = 0,
|
||||||
});
|
this.syncMode = TrackSyncMode.online,
|
||||||
|
this.syncStatus = TrackSyncStatus.pending,
|
||||||
|
this.supabaseId});
|
||||||
|
|
||||||
Track copyWith({
|
Track copyWith(
|
||||||
int? id,
|
{int? id,
|
||||||
int? projectId,
|
int? projectId,
|
||||||
String? name,
|
String? name,
|
||||||
DateTime? startTime,
|
DateTime? startTime,
|
||||||
DateTime? endTime,
|
DateTime? endTime,
|
||||||
TrackStatus? status,
|
TrackStatus? status,
|
||||||
String? source,
|
String? source,
|
||||||
double? distanceMeters,
|
double? distanceMeters,
|
||||||
int? pointCount,
|
int? pointCount,
|
||||||
}) =>
|
TrackSyncMode? syncMode,
|
||||||
|
TrackSyncStatus? syncStatus,
|
||||||
|
String? supabaseId}) =>
|
||||||
Track(
|
Track(
|
||||||
id: id ?? this.id,
|
id: id ?? this.id,
|
||||||
projectId: projectId ?? this.projectId,
|
projectId: projectId ?? this.projectId,
|
||||||
name: name ?? this.name,
|
name: name ?? this.name,
|
||||||
startTime: startTime ?? this.startTime,
|
startTime: startTime ?? this.startTime,
|
||||||
endTime: endTime ?? this.endTime,
|
endTime: endTime ?? this.endTime,
|
||||||
status: status ?? this.status,
|
status: status ?? this.status,
|
||||||
source: source ?? this.source,
|
source: source ?? this.source,
|
||||||
distanceMeters: distanceMeters ?? this.distanceMeters,
|
distanceMeters: distanceMeters ?? this.distanceMeters,
|
||||||
pointCount: pointCount ?? this.pointCount,
|
pointCount: pointCount ?? this.pointCount,
|
||||||
);
|
syncMode: syncMode ?? this.syncMode,
|
||||||
|
syncStatus: syncStatus ?? this.syncStatus,
|
||||||
|
supabaseId: supabaseId ?? this.supabaseId);
|
||||||
|
|
||||||
/// Formázott időtartam (óó:pp:mm)
|
/// Formázott időtartam (óó:pp:mm)
|
||||||
String get durationFormatted {
|
String get durationFormatted {
|
||||||
@ -132,24 +144,34 @@ class Track {
|
|||||||
'source': source,
|
'source': source,
|
||||||
'distance_m': distanceMeters,
|
'distance_m': distanceMeters,
|
||||||
'point_count': pointCount,
|
'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(
|
factory Track.fromMap(Map<String, dynamic> m) => Track(
|
||||||
id: m['id'] as int?,
|
id: m['id'] as int?,
|
||||||
projectId: m['project_id'] as int?,
|
projectId: m['project_id'] as int?,
|
||||||
name: m['name'] as String,
|
name: m['name'] as String,
|
||||||
startTime: DateTime.parse(m['start_time'] as String),
|
startTime: DateTime.parse(m['start_time'] as String),
|
||||||
endTime: m['end_time'] != null
|
endTime: m['end_time'] != null
|
||||||
? DateTime.parse(m['end_time'] as String)
|
? DateTime.parse(m['end_time'] as String)
|
||||||
: null,
|
: null,
|
||||||
status: TrackStatus.values.firstWhere(
|
status: TrackStatus.values.firstWhere(
|
||||||
(s) => s.name == (m['status'] as String?),
|
(s) => s.name == (m['status'] as String?),
|
||||||
orElse: () => TrackStatus.finished,
|
orElse: () => TrackStatus.finished,
|
||||||
),
|
),
|
||||||
source: m['source'] as String? ?? 'Telefon GPS',
|
source: m['source'] as String? ?? 'Telefon GPS',
|
||||||
distanceMeters: (m['distance_m'] as num?)?.toDouble() ?? 0,
|
distanceMeters: (m['distance_m'] as num?)?.toDouble() ?? 0,
|
||||||
pointCount: m['point_count'] as int? ?? 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 ───────────────────────────────────────
|
// ─── 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:intl/intl.dart';
|
||||||
import 'package:latlong2/latlong.dart';
|
import 'package:latlong2/latlong.dart';
|
||||||
import 'package:share_plus/share_plus.dart';
|
import 'package:share_plus/share_plus.dart';
|
||||||
|
import 'package:terepi_seged/services/project_service.dart';
|
||||||
|
|
||||||
import '../../../../services/location_source.dart';
|
import '../../../../services/location_source.dart';
|
||||||
import '../../../../services/phone_gps_source.dart';
|
import '../../../../services/phone_gps_source.dart';
|
||||||
@ -116,19 +117,23 @@ class TrackingController extends GetxController {
|
|||||||
// ── Publikus API ───────────────────────────────────────────────────────────
|
// ── Publikus API ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/// Elindítja a rögzítést a megadott forrással (alapértelmezett: telefon GPS).
|
/// 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;
|
if (isRecording.value) return;
|
||||||
|
|
||||||
_source = source ?? PhoneGpsSource(intervalMs: 1000, distanceFilter: 2.0);
|
_source = source ?? PhoneGpsSource(intervalMs: 1000, distanceFilter: 2.0);
|
||||||
|
|
||||||
// Track létrehozása az adatbázisban
|
// Track létrehozása az adatbázisban
|
||||||
final now = DateTime.now();
|
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(
|
final trackId = await _db.insertTrack(Track(
|
||||||
name: name,
|
name: trackName,
|
||||||
startTime: now,
|
startTime: now,
|
||||||
source: _source!.displayName,
|
source: _source!.displayName,
|
||||||
));
|
projectId: ProjectService.to.activeProjectId,
|
||||||
|
syncMode: syncMode));
|
||||||
currentTrack.value = await _db.getTrack(trackId);
|
currentTrack.value = await _db.getTrack(trackId);
|
||||||
|
|
||||||
// Állapot reset
|
// Állapot reset
|
||||||
@ -141,7 +146,7 @@ class TrackingController extends GetxController {
|
|||||||
// Foreground service indítása (háttér-működéshez)
|
// Foreground service indítása (háttér-működéshez)
|
||||||
await FlutterForegroundTask.startService(
|
await FlutterForegroundTask.startService(
|
||||||
notificationTitle: 'Track rögzítése',
|
notificationTitle: 'Track rögzítése',
|
||||||
notificationText: name,
|
notificationText: trackName,
|
||||||
callback: startTrackingCallback,
|
callback: startTrackingCallback,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@ -87,7 +87,10 @@ class AppDatabase {
|
|||||||
status TEXT NOT NULL DEFAULT 'recording',
|
status TEXT NOT NULL DEFAULT 'recording',
|
||||||
source TEXT NOT NULL DEFAULT 'Telefon GPS',
|
source TEXT NOT NULL DEFAULT 'Telefon GPS',
|
||||||
distance_m REAL NOT NULL DEFAULT 0,
|
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)');
|
await db.execute('CREATE INDEX idx_tracks_project ON tracks(project_id)');
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user