aves_mio0.31/lib/utils/gps_smoothing.dart
2026-07-18 13:39:22 +02:00

76 lines
2.3 KiB
Dart

// 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<LatLng> douglasPeucker(List<LatLng> points, double epsilonMeters) {
if (points.length < 3 || epsilonMeters <= 0) return List<LatLng>.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 <LatLng>[...left, ...right.skip(1)];
}
return <LatLng>[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);
}
}