201 lines
7.3 KiB
Dart
201 lines
7.3 KiB
Dart
|
|
// Térkép overlay és panel widget az importált rétegekhez.
|
||
|
|
// Az ImportedLayer már kész flutter_map objektumokat tartalmaz —
|
||
|
|
// itt csak megjelenítjük őket.
|
||
|
|
|
||
|
|
import 'package:flutter/material.dart';
|
||
|
|
import 'package:flutter_map/flutter_map.dart';
|
||
|
|
import 'package:get/get.dart';
|
||
|
|
import 'package:latlong2/latlong.dart';
|
||
|
|
import 'package:terepi_seged/enums/layer_import_source_type.dart';
|
||
|
|
|
||
|
|
import '../../models/imported_layer.dart';
|
||
|
|
import '../../services/layer_import_service.dart';
|
||
|
|
|
||
|
|
// ════════════════════════════════════════════════════════════════════
|
||
|
|
// Térkép réteg — a flutter_map layers listájába kerül
|
||
|
|
// ════════════════════════════════════════════════════════════════════
|
||
|
|
|
||
|
|
class ImportedLayerOverlay extends StatelessWidget {
|
||
|
|
const ImportedLayerOverlay({super.key});
|
||
|
|
|
||
|
|
@override
|
||
|
|
Widget build(BuildContext context) {
|
||
|
|
if (!Get.isRegistered<LayerImportService>()) {
|
||
|
|
return const SizedBox.shrink();
|
||
|
|
}
|
||
|
|
|
||
|
|
return Obx(() {
|
||
|
|
final visible = LayerImportService.to.visibleLayers;
|
||
|
|
if (visible.isEmpty) return const SizedBox.shrink();
|
||
|
|
|
||
|
|
// Az összes látható réteg objektumait összegyűjtjük
|
||
|
|
final polylines = visible.expand((l) => l.polylines).toList();
|
||
|
|
final polygons = visible.expand((l) => l.polygons).toList();
|
||
|
|
final markers = visible.expand((l) => l.markers).toList();
|
||
|
|
|
||
|
|
return Stack(children: [
|
||
|
|
if (polygons.isNotEmpty) PolygonLayer(polygons: polygons),
|
||
|
|
if (polylines.isNotEmpty) PolylineLayer(polylines: polylines),
|
||
|
|
if (markers.isNotEmpty) MarkerLayer(markers: markers),
|
||
|
|
]);
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// ════════════════════════════════════════════════════════════════════
|
||
|
|
// Réteg panel — import gomb + betöltött rétegek listája
|
||
|
|
// ════════════════════════════════════════════════════════════════════
|
||
|
|
|
||
|
|
class ImportLayerPanel extends StatelessWidget {
|
||
|
|
/// Ha meg van adva, a rétegre zoom gomb ezt hívja
|
||
|
|
final void Function(LatLngBounds bounds)? onFitBounds;
|
||
|
|
|
||
|
|
const ImportLayerPanel({super.key, this.onFitBounds});
|
||
|
|
|
||
|
|
@override
|
||
|
|
Widget build(BuildContext context) {
|
||
|
|
final svc = LayerImportService.to;
|
||
|
|
|
||
|
|
return Obx(() => Column(
|
||
|
|
mainAxisSize: MainAxisSize.min,
|
||
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||
|
|
children: [
|
||
|
|
// Import gomb
|
||
|
|
_ImportButton(svc: svc),
|
||
|
|
|
||
|
|
// Réteg lista
|
||
|
|
if (svc.layers.isNotEmpty) ...[
|
||
|
|
const SizedBox(height: 12),
|
||
|
|
const Divider(height: 1),
|
||
|
|
const SizedBox(height: 8),
|
||
|
|
...svc.layers.map((layer) => _LayerTile(
|
||
|
|
layer: layer,
|
||
|
|
onToggle: () => svc.toggleLayer(layer.id),
|
||
|
|
onRemove: () => svc.removeLayer(layer.id),
|
||
|
|
onZoomTo: onFitBounds != null
|
||
|
|
? () {
|
||
|
|
final b = layer.bounds;
|
||
|
|
if (b != null) onFitBounds!(b);
|
||
|
|
}
|
||
|
|
: null,
|
||
|
|
)),
|
||
|
|
],
|
||
|
|
],
|
||
|
|
));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// ─── Import gomb ─────────────────────────────────────────────────────────────
|
||
|
|
|
||
|
|
class _ImportButton extends StatelessWidget {
|
||
|
|
final LayerImportService svc;
|
||
|
|
const _ImportButton({required this.svc});
|
||
|
|
|
||
|
|
@override
|
||
|
|
Widget build(BuildContext context) {
|
||
|
|
return Obx(() => svc.isLoading.value
|
||
|
|
? const Padding(
|
||
|
|
padding: EdgeInsets.symmetric(vertical: 8),
|
||
|
|
child: Row(mainAxisSize: MainAxisSize.min, children: [
|
||
|
|
SizedBox(
|
||
|
|
width: 16,
|
||
|
|
height: 16,
|
||
|
|
child: CircularProgressIndicator(strokeWidth: 2)),
|
||
|
|
SizedBox(width: 8),
|
||
|
|
Text('Betöltés...', style: TextStyle(fontSize: 13)),
|
||
|
|
]),
|
||
|
|
)
|
||
|
|
: OutlinedButton.icon(
|
||
|
|
onPressed: () => svc.importFile(),
|
||
|
|
icon: const Icon(Icons.file_open_outlined, size: 18),
|
||
|
|
label: const Text('GeoJSON / KML / KMZ'),
|
||
|
|
));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// ─── Egy réteg sor ───────────────────────────────────────────────────────────
|
||
|
|
|
||
|
|
class _LayerTile extends StatelessWidget {
|
||
|
|
final ImportedLayer layer;
|
||
|
|
final VoidCallback onToggle;
|
||
|
|
final VoidCallback onRemove;
|
||
|
|
final VoidCallback? onZoomTo;
|
||
|
|
|
||
|
|
const _LayerTile({
|
||
|
|
required this.layer,
|
||
|
|
required this.onToggle,
|
||
|
|
required this.onRemove,
|
||
|
|
this.onZoomTo,
|
||
|
|
});
|
||
|
|
|
||
|
|
@override
|
||
|
|
Widget build(BuildContext context) {
|
||
|
|
final icon = switch (layer.sourceType) {
|
||
|
|
LayerImportSourceType.geoJson => Icons.data_object,
|
||
|
|
LayerImportSourceType.kml => Icons.map_outlined,
|
||
|
|
LayerImportSourceType.kmz => Icons.folder_zip_outlined,
|
||
|
|
};
|
||
|
|
|
||
|
|
return Padding(
|
||
|
|
padding: const EdgeInsets.only(bottom: 6),
|
||
|
|
child: Row(children: [
|
||
|
|
// Láthatóság
|
||
|
|
Transform.scale(
|
||
|
|
scale: 0.85,
|
||
|
|
child: Switch.adaptive(
|
||
|
|
value: layer.isVisible,
|
||
|
|
onChanged: (_) => onToggle(),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
// Ikon
|
||
|
|
Icon(icon, size: 15, color: Colors.grey.shade500),
|
||
|
|
const SizedBox(width: 6),
|
||
|
|
// Név + statisztika
|
||
|
|
Expanded(
|
||
|
|
child: Column(
|
||
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||
|
|
mainAxisSize: MainAxisSize.min,
|
||
|
|
children: [
|
||
|
|
Text(layer.name,
|
||
|
|
style: const TextStyle(
|
||
|
|
fontSize: 13, fontWeight: FontWeight.w500),
|
||
|
|
overflow: TextOverflow.ellipsis),
|
||
|
|
Text(_stats(),
|
||
|
|
style: TextStyle(fontSize: 10, color: Colors.grey.shade500)),
|
||
|
|
],
|
||
|
|
),
|
||
|
|
),
|
||
|
|
// Zoom gomb
|
||
|
|
if (onZoomTo != null)
|
||
|
|
IconButton(
|
||
|
|
icon: const Icon(Icons.fit_screen, size: 16),
|
||
|
|
onPressed: onZoomTo,
|
||
|
|
tooltip: 'Ráközelítés',
|
||
|
|
padding: EdgeInsets.zero,
|
||
|
|
constraints: const BoxConstraints(minWidth: 28, minHeight: 28),
|
||
|
|
color: Colors.grey.shade500,
|
||
|
|
),
|
||
|
|
// Törlés
|
||
|
|
IconButton(
|
||
|
|
icon: const Icon(Icons.close, size: 16),
|
||
|
|
onPressed: onRemove,
|
||
|
|
tooltip: 'Eltávolítás',
|
||
|
|
padding: EdgeInsets.zero,
|
||
|
|
constraints: const BoxConstraints(minWidth: 28, minHeight: 28),
|
||
|
|
color: Colors.grey.shade400,
|
||
|
|
),
|
||
|
|
]),
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
String _stats() {
|
||
|
|
final parts = <String>[];
|
||
|
|
if (layer.markers.isNotEmpty) parts.add('${layer.markers.length} pont');
|
||
|
|
if (layer.polylines.isNotEmpty)
|
||
|
|
parts.add('${layer.polylines.length} vonal');
|
||
|
|
if (layer.polygons.isNotEmpty)
|
||
|
|
parts.add('${layer.polygons.length} terület');
|
||
|
|
return parts.isEmpty ? 'Üres réteg' : parts.join(' · ');
|
||
|
|
}
|
||
|
|
}
|