Kml, kmz és geojson import és térképi megjelnítésük.
This commit is contained in:
@@ -0,0 +1,200 @@
|
||||
// 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(' · ');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user