Geopackage export: geometriák exportálása dátum szerinti szűréssel
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 7s

This commit is contained in:
2026-07-30 18:37:53 +02:00
parent b7da3699e8
commit 7f514da38f
2 changed files with 98 additions and 16 deletions
+42 -16
View File
@@ -29,7 +29,7 @@ import '../enums/note_type.dart';
class GeoPackageExporter {
// ── Publikus belépési pont ────────────────────────────────────────
Future<void> exportProject() async {
Future<void> exportProject({DateTime? since}) async {
final project = ProjectService.to.activeProject.value;
final projectId = ProjectService.to.activeProjectId;
final name = project?.name ?? 'projekt';
@@ -42,11 +42,11 @@ class GeoPackageExporter {
try {
// 1. GeoPackage létrehozása
final gpkgPath = p.join(workDir.path, '$name.gpkg');
await _buildGpkg(gpkgPath, projectId);
await _buildGpkg(gpkgPath, projectId, since);
// 2. Médiafájlok összegyűjtése
final mediaDir = Directory(p.join(workDir.path, 'media'));
await _collectMedia(projectId, mediaDir);
await _collectMedia(projectId, mediaDir, since);
// 3. ZIP csomagolás
final zipPath = p.join(tmpDir.path, '${name}_$ts.zip');
@@ -65,13 +65,13 @@ class GeoPackageExporter {
// ── GeoPackage (.gpkg) létrehozása ───────────────────────────────
Future<void> _buildGpkg(String path, int? projectId) async {
Future<void> _buildGpkg(String path, int? projectId, DateTime? since) async {
final db = await openDatabase(path);
try {
await _initGpkg(db);
await _exportNoteItems(db, projectId);
await _exportMeasuredPoints(db, projectId);
await _exportTracks(db, projectId);
await _exportNoteItems(db, projectId, since);
await _exportMeasuredPoints(db, projectId, since);
await _exportTracks(db, projectId, since);
} finally {
await db.close();
}
@@ -187,11 +187,17 @@ class GeoPackageExporter {
// ── NoteItem exportok ─────────────────────────────────────────────
Future<void> _exportNoteItems(Database db, int? projectId) async {
Future<void> _exportNoteItems(
Database db, int? projectId, DateTime? since) async {
final items = await AppDatabase.instance.listNoteItems(projectId);
final points = items.where((i) => i.type == NoteType.point).toList();
final lines = items.where((i) => i.type == NoteType.line).toList();
final polygons = items.where((i) => i.type == NoteType.polygon).toList();
final filtered = since != null
? items.where((i) => i.createdAt.isAfter(since)).toList()
: items;
final points = filtered.where((i) => i.type == NoteType.point).toList();
final lines = filtered.where((i) => i.type == NoteType.line).toList();
final polygons = filtered.where((i) => i.type == NoteType.polygon).toList();
if (points.isNotEmpty) await _exportPoints(db, points);
if (lines.isNotEmpty) await _exportLines(db, lines);
@@ -310,12 +316,19 @@ class GeoPackageExporter {
// ── Bemért pontok exportja ────────────────────────────────────────
Future<void> _exportMeasuredPoints(Database db, int? projectId) async {
Future<void> _exportMeasuredPoints(
Database db, int? projectId, DateTime? since) async {
final points = projectId != null
? await AppDatabase.instance.listMeasuredPoints(projectId)
: <MeasuredPoint>[];
if (points.isEmpty) return;
final filtered = since != null
? points.where((p) => p.timestamp.isAfter(since)).toList()
: points;
if (filtered.isEmpty) return;
await db.execute('''
CREATE TABLE measured_points (
id INTEGER PRIMARY KEY,
@@ -353,13 +366,20 @@ class GeoPackageExporter {
// ── Track exportja ────────────────────────────────────────────────
Future<void> _exportTracks(Database db, int? projectId) async {
Future<void> _exportTracks(
Database db, int? projectId, DateTime? since) async {
final tracks = await AppDatabase.instance.listTracks();
final filtered = projectId != null
var filtered = projectId != null
? tracks.where((t) => t.projectId == projectId).toList()
: tracks;
if (filtered.isEmpty) return;
if (since != null) {
filtered = filtered.where((t) => t.startTime.isAfter(since)).toList();
}
if (filtered.isEmpty) return;
await db.execute('''
CREATE TABLE tracks (
id INTEGER PRIMARY KEY,
@@ -395,7 +415,8 @@ class GeoPackageExporter {
// ── Médiafájlok másolása ──────────────────────────────────────────
Future<void> _collectMedia(int? projectId, Directory mediaDir) async {
Future<void> _collectMedia(
int? projectId, Directory mediaDir, DateTime? since) async {
if (projectId == null) return;
final photos = Directory(p.join(mediaDir.path, 'photos'));
@@ -404,7 +425,12 @@ class GeoPackageExporter {
await audios.create(recursive: true);
final items = await AppDatabase.instance.listNoteItems(projectId);
for (final item in items) {
final filtered = since != null
? items.where((i) => i.createdAt.isAfter(since)).toList()
: items;
for (final item in filtered) {
// Fotók
final photoList = await AppDatabase.instance.listNotePhotos(item.id!);
for (final photo in photoList) {
@@ -1895,6 +1895,9 @@ class MapSurveyController extends GetxController implements StyleEditable {
showGeometryLabels.value = !showGeometryLabels.value;
Future<void> exportProject() async {
final since = await _showExportDateDialog();
if (since == false) return; // felhasználó megszakította
Get.dialog(
Center(
child: Material(
@@ -1932,6 +1935,59 @@ class MapSurveyController extends GetxController implements StyleEditable {
}
}
/// Visszaad: DateTime (szűrt export), null (teljes export), false (mégse)
Future<dynamic> _showExportDateDialog() async {
return Get.dialog<dynamic>(AlertDialog(
title: const Text('Export tartalma'),
content: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ListTile(
leading: const Icon(Icons.all_inclusive),
title: const Text('Teljes projekt'),
onTap: () => Get.back(result: null),
),
ListTile(
leading: const Icon(Icons.today),
title: const Text('Mai nap'),
onTap: () => Get.back(
result: DateTime(DateTime.now().year, DateTime.now().month,
DateTime.now().day)),
),
ListTile(
leading: const Icon(Icons.today),
title: const Text('Tegnapi nap'),
onTap: () => Get.back(
result: DateTime(DateTime.now().year, DateTime.now().month,
DateTime.now().day)
.subtract(const Duration(days: 1))),
),
ListTile(
leading: const Icon(Icons.date_range),
title: const Text('Dátum kiválasztása...'),
onTap: () async {
Get.back();
final picked = await showDatePicker(
context: Get.context!,
initialDate: DateTime.now().subtract(const Duration(days: 7)),
firstDate: DateTime(2024),
lastDate: DateTime.now(),
);
if (picked != null) Get.back(result: picked);
},
),
],
),
actions: [
TextButton(
onPressed: () => Get.back(result: false),
child: const Text('Mégse'),
),
],
));
}
void _subscribeToTeamPosition() {
_teamChannel = Supabase.instance.client
.channel('public:terepi_seged_device_positions')