76 lines
2.3 KiB
Dart
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);
|
|
}
|
|
}
|