import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:flutter_map/flutter_map.dart'; import 'package:latlong2/latlong.dart'; import '../enums/note_type.dart'; class NoteItem { final int? id; final int? projectId; final NoteType type; final List points; // ← szerkesztéshez mindig elérhető final Color color; final double opacity; final double strokeWidth; final Color strokeColor; final String label; final DateTime createdAt; const NoteItem( {this.id, this.projectId, required this.type, required this.points, required this.color, this.opacity = 0.5, this.strokeWidth = 3.0, required this.strokeColor, this.label = '', required this.createdAt}); NoteItem copyWith({ List? points, Color? color, double? opacity, double? strokeWidth, Color? strokeColor, String? label, }) => NoteItem( id: id, projectId: projectId, type: type, points: points ?? this.points, color: color ?? this.color, opacity: opacity ?? this.opacity, strokeWidth: strokeWidth ?? this.strokeWidth, strokeColor: strokeColor ?? this.strokeColor, label: label ?? this.label, createdAt: createdAt, ); // ── Koordináta ↔ GeoJSON ──────────────────────────────────────── static List _parsePoints(String json) { final geom = jsonDecode(json) as Map; final gType = geom['type'] as String; final coords = geom['coordinates']; switch (gType) { case 'Point': final c = coords as List; return [ LatLng( (c[1] as num).toDouble(), (c[0] as num).toDouble(), ) ]; case 'LineString': return (coords as List) .map((c) => LatLng( (c[1] as num).toDouble(), (c[0] as num).toDouble(), )) .toList(); case 'Polygon': // Külső gyűrű (index 0) return ((coords as List)[0] as List) .map((c) => LatLng( (c[1] as num).toDouble(), (c[0] as num).toDouble(), )) .toList(); default: return []; } } String _toPointsJson() { List coords; String gType; switch (type) { case NoteType.point: // GeoJSON: [lon, lat] gType = 'Point'; coords = [points.first.longitude, points.first.latitude]; case NoteType.line: gType = 'LineString'; coords = points.map((p) => [p.longitude, p.latitude]).toList(); case NoteType.polygon: gType = 'Polygon'; final ring = points.map((p) => [p.longitude, p.latitude]).toList(); // Polygon zárt: első = utolsó pont if (ring.isNotEmpty) { final first = ring.first; final last = ring.last; final isClosed = first[0] == last[0] && first[1] == last[1]; if (!isClosed) ring.add(List.from(first)); } coords = [ring]; } return jsonEncode({'type': gType, 'coordinates': coords}); } // ── SQLite ↔ Map ──────────────────────────────────────────────── static Color _parseColor(String hex) { final h = hex.replaceFirst('#', ''); return Color(int.parse(h.length == 6 ? 'FF$h' : h, radix: 16)); } static String _colorHex(Color c) => '#${c.value.toRadixString(16).padLeft(8, '0').substring(2).toUpperCase()}'; Map toMap() => { if (id != null) 'id': id, if (projectId != null) 'project_id': projectId, 'type': type.name, 'points_json': _toPointsJson(), 'color': _colorHex(color), 'opacity': opacity, 'stroke_width': strokeWidth, 'stroke_color': _colorHex(strokeColor), 'label': label, 'created_at': createdAt.toIso8601String(), }; factory NoteItem.fromMap(Map m) => NoteItem( id: m['id'] as int?, projectId: m['project_id'] as int?, type: NoteType.values.firstWhere((t) => t.name == (m['type'] as String), orElse: () => NoteType.point), points: _parsePoints(m['points_json'] as String), color: _parseColor(m['color'] as String? ?? '#185FA5'), opacity: (m['opacity'] as num?)?.toDouble() ?? 0.5, strokeWidth: (m['stroke_width'] as num?)?.toDouble() ?? 3.0, strokeColor: _parseColor(m['stroke_color'] as String? ?? '#FFD700'), label: m['label'] as String? ?? '', createdAt: DateTime.parse(m['created_at'] as String), ); /// Kitöltési szín az opacity-val alkalmazva. Color get fillColor => Color.fromARGB( (opacity * 255).round(), color.red, color.green, color.blue, ); // Rendereléshez — a hitValue hordozza az id-t a tap detektáláshoz Polyline toPolyline() => Polyline( points: points, color: color, strokeWidth: strokeWidth, hitValue: id!, ); Polygon toPolygon() => Polygon( points: points, color: color.withOpacity(opacity), borderColor: strokeColor, borderStrokeWidth: strokeWidth, label: label.isEmpty ? null : label, hitValue: id!, ); }