// lib/utils/gps_smoothing.dart import 'dart:math' as math; import 'package:latlong2/latlong.dart'; class GpsSmoothing { /// Semplifica una traccia GPS con Douglas-Peucker. /// /// [epsilonMeters] è espresso in metri. /// Esempio: 5.0 mantiene una buona forma della traccia eliminando jitter minimo. static List douglasPeucker(List points, double epsilonMeters) { if (points.length < 3 || epsilonMeters <= 0) return List.from(points); final first = points.first; final last = points.last; double maxDistance = 0; int index = 0; for (int i = 1; i < points.length - 1; i++) { final d = _perpendicularDistanceMeters(points[i], first, last); if (d > maxDistance) { maxDistance = d; index = i; } } if (maxDistance > epsilonMeters) { final left = douglasPeucker(points.sublist(0, index + 1), epsilonMeters); final right = douglasPeucker(points.sublist(index), epsilonMeters); return [...left, ...right.skip(1)]; } return [first, last]; } /// Distanza perpendicolare punto-segmento in metri. /// /// Usa una proiezione locale equirettangolare, sufficiente per segmenti GPS /// consecutivi/attività outdoor. Il parametro di proiezione viene limitato /// al segmento [start, end], non alla retta infinita. static double _perpendicularDistanceMeters(LatLng p, LatLng start, LatLng end) { const metersPerDegreeLat = 111320.0; final refLatRad = start.latitude * math.pi / 180.0; final metersPerDegreeLon = metersPerDegreeLat * math.cos(refLatRad).abs(); final x = p.longitude * metersPerDegreeLon; final y = p.latitude * metersPerDegreeLat; final x1 = start.longitude * metersPerDegreeLon; final y1 = start.latitude * metersPerDegreeLat; final x2 = end.longitude * metersPerDegreeLon; final y2 = end.latitude * metersPerDegreeLat; final dx = x2 - x1; final dy = y2 - y1; if (dx == 0 && dy == 0) { final px = x - x1; final py = y - y1; return math.sqrt(px * px + py * py); } final rawT = ((x - x1) * dx + (y - y1) * dy) / (dx * dx + dy * dy); final t = rawT.clamp(0.0, 1.0).toDouble(); final projX = x1 + t * dx; final projY = y1 + t * dy; final px = x - projX; final py = y - projY; return math.sqrt(px * px + py * py); } }