import 'dart:math' as math; 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/services/coord_converter_service.dart'; import 'package:terepi_seged/services/gnss/gnss_service.dart'; import 'package:terepi_seged/services/stakeout_service.dart'; import '../../../../models/stakeout_point.dart'; /// Kitűzési térképréteg — a SharedMapWidget children listájába illesztendő. /// /// Csak akkor rajzol, ha a StakeoutService aktív (mode == stakeout), így /// feltétel nélkül bent maradhat a rétegek között. Tartalma: vonalanként /// polyline (station-sorrendben), státusz-színezett markerek (koppintás = /// célváltás), vezetővonal a pozíciótól a célig. class StakeoutMapLayers extends StatelessWidget { const StakeoutMapLayers({super.key}); @override Widget build(BuildContext context) { if (!Get.isRegistered()) return const SizedBox.shrink(); return Obx(() { final svc = StakeoutService.to; if (!svc.active.value || svc.points.isEmpty) { return const SizedBox.shrink(); } final points = svc.points; final target = svc.target.value; final g = GnssService.to; final hasPos = svc.hasPosition.value && g.latitude.value != 0; // Vonalanként polyline (station-sorrendben, eltolt pontok nélkül). final linePolys = []; for (final line in svc.lines) { final lp = points.where((p) => p.lineId == line && !p.isOffset).toList() ..sort((a, b) => a.station.compareTo(b.station)); if (lp.length < 2) continue; linePolys.add(Polyline( points: [for (final p in lp) LatLng(p.planLat, p.planLon)], color: Colors.blueGrey.withOpacity(0.5), strokeWidth: 2, )); } // Transzverzális az aktuális célnál: a vonalra merőleges segédvonal // ±30 m hosszan, 10 m-enkénti méter-címkékkel — terepi akadálynál // e mentén látszik, hova tolható el a pont. final transversal = []; final transversalTicks = []; if (target != null && Get.isRegistered()) { final lb = svc.lineBearingAt(target); if (lb != null) { final perpRad = (lb + 90) * math.pi / 180; LatLng at(double d) { final y = target.planEovY + d * math.sin(perpRad); final x = target.planEovX + d * math.cos(perpRad); final w = CoordConverterService.to.eovToWgsPoint(y, x); return LatLng(w.y, w.x); } transversal ..add(at(-30)) ..add(at(30)); for (final d in const [-30.0, -20.0, -10.0, 10.0, 20.0, 30.0]) { transversalTicks.add(Marker( point: at(d), width: 24, height: 16, child: Container( alignment: Alignment.center, decoration: BoxDecoration( color: Colors.orange.withOpacity(0.85), borderRadius: BorderRadius.circular(4), ), child: Text( d.abs().toStringAsFixed(0), style: const TextStyle( fontSize: 9, color: Colors.white, fontWeight: FontWeight.w700), ), ), )); } } } // 300 pont fölött a címkék csak a célon (teljesítmény). final showLabels = points.length <= 300; return Stack(children: [ PolylineLayer(polylines: [ if (transversal.isNotEmpty) Polyline( points: transversal, color: Colors.orange.withValues(alpha: 0.8), strokeWidth: 2), ...linePolys, if (hasPos && target != null) Polyline( points: [ LatLng(g.latitude.value, g.longitude.value), LatLng(target.planLat, target.planLon), ], color: Colors.blue, strokeWidth: 3, ), ]), MarkerLayer(markers: [ ...transversalTicks, for (final p in points) Marker( point: LatLng(p.planLat, p.planLon), width: 56, height: 40, child: GestureDetector( onTap: () => svc.setTarget(p), child: _StakeoutMarker( point: p, isTarget: p.uuid == target?.uuid, showLabel: showLabels || p.uuid == target?.uuid, ), ), ), ]), ]); }); } } class _StakeoutMarker extends StatelessWidget { final StakeoutPoint point; final bool isTarget; final bool showLabel; const _StakeoutMarker( {required this.point, required this.isTarget, required this.showLabel}); @override Widget build(BuildContext context) { final (color, icon) = switch (point.status) { StakeoutStatus.staked => (Colors.green, Icons.check_circle), StakeoutStatus.skipped => (Colors.grey, Icons.block), StakeoutStatus.pending => point.isOffset ? (Colors.purple, Icons.change_history) : (Colors.deepOrange, Icons.change_history), }; return Column( mainAxisSize: MainAxisSize.min, children: [ Icon(icon, size: isTarget ? 26 : 16, color: isTarget ? Colors.red : color), if (showLabel) Container( padding: const EdgeInsets.symmetric(horizontal: 3), decoration: BoxDecoration( color: Colors.white.withOpacity(0.85), borderRadius: BorderRadius.circular(4), ), child: Text( point.name, style: TextStyle( fontSize: 9, fontWeight: isTarget ? FontWeight.w700 : FontWeight.w400, color: isTarget ? Colors.red : Colors.black87, ), ), ), ], ); } }