261 lines
7.8 KiB
Dart

import 'dart:math' as math;
import 'package:geolocator/geolocator.dart';
/// Utility Klasse für erweiterte Geo-Funktionen
class GeoUtils {
/// Private Konstruktor
GeoUtils._();
/// Erdradius in Kilometern
static const double earthRadiusKm = 6371.0;
/// Konvertiert Grad zu Radiant
static double degreesToRadians(double degrees) {
return degrees * math.pi / 180.0;
}
/// Konvertiert Radiant zu Grad
static double radiansToDegrees(double radians) {
return radians * 180.0 / math.pi;
}
/// Berechnet die Distanz zwischen zwei Punkten mit der Haversine-Formel
/// Ergebnis in Kilometern
static double calculateDistanceKm({
required double startLat,
required double startLng,
required double endLat,
required double endLng,
}) {
double dLat = degreesToRadians(endLat - startLat);
double dLng = degreesToRadians(endLng - startLng);
double a = math.sin(dLat / 2) * math.sin(dLat / 2) +
math.cos(degreesToRadians(startLat)) *
math.cos(degreesToRadians(endLat)) *
math.sin(dLng / 2) *
math.sin(dLng / 2);
double c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a));
return earthRadiusKm * c;
}
/// Berechnet einen neuen Punkt basierend auf Startpunkt, Distanz und Richtung
/// [distance] in Kilometern
/// [bearing] in Grad (0 = Norden, 90 = Osten)
static Map<String, double> calculateDestination({
required double startLat,
required double startLng,
required double distance,
required double bearing,
}) {
double lat1 = degreesToRadians(startLat);
double lng1 = degreesToRadians(startLng);
double brng = degreesToRadians(bearing);
double d = distance / earthRadiusKm;
double lat2 = math.asin(
math.sin(lat1) * math.cos(d) +
math.cos(lat1) * math.sin(d) * math.cos(brng),
);
double lng2 = lng1 +
math.atan2(
math.sin(brng) * math.sin(d) * math.cos(lat1),
math.cos(d) - math.sin(lat1) * math.sin(lat2),
);
return {
'latitude': radiansToDegrees(lat2),
'longitude': radiansToDegrees(lng2),
};
}
/// Prüft ob ein Punkt innerhalb eines Kreises liegt
/// [radius] in Kilometern
static bool isPointInCircle({
required double pointLat,
required double pointLng,
required double centerLat,
required double centerLng,
required double radius,
}) {
double distance = calculateDistanceKm(
startLat: pointLat,
startLng: pointLng,
endLat: centerLat,
endLng: centerLng,
);
return distance <= radius;
}
/// Berechnet die Richtung zwischen zwei Punkten
/// Ergebnis in Grad (0-360, 0 = Norden)
static double calculateBearing({
required double startLat,
required double startLng,
required double endLat,
required double endLng,
}) {
double lat1 = degreesToRadians(startLat);
double lat2 = degreesToRadians(endLat);
double dLng = degreesToRadians(endLng - startLng);
double y = math.sin(dLng) * math.cos(lat2);
double x = math.cos(lat1) * math.sin(lat2) -
math.sin(lat1) * math.cos(lat2) * math.cos(dLng);
double bearing = radiansToDegrees(math.atan2(y, x));
return (bearing + 360) % 360;
}
/// Konvertiert Geschwindigkeit von m/s zu km/h
static double mpsToKmh(double mps) {
return mps * 3.6;
}
/// Konvertiert Geschwindigkeit von km/h zu m/s
static double kmhToMps(double kmh) {
return kmh / 3.6;
}
/// Formatiert Koordinaten als String
static String formatCoordinates(double lat, double lng, {int precision = 6}) {
return '${lat.toStringAsFixed(precision)}, ${lng.toStringAsFixed(precision)}';
}
/// Formatiert Koordinaten als DMS (Degrees, Minutes, Seconds)
static String formatCoordinatesDMS(double lat, double lng) {
String latDMS = _decimalToDMS(lat.abs(), lat >= 0 ? 'N' : 'S');
String lngDMS = _decimalToDMS(lng.abs(), lng >= 0 ? 'E' : 'W');
return '$latDMS, $lngDMS';
}
/// Hilfsfunktion für DMS Konvertierung
static String _decimalToDMS(double decimal, String direction) {
int degrees = decimal.floor();
double minutesDecimal = (decimal - degrees) * 60;
int minutes = minutesDecimal.floor();
double seconds = (minutesDecimal - minutes) * 60;
return '$degrees°$minutes\'${seconds.toStringAsFixed(2)}"$direction';
}
/// Berechnet die Mittelpunkt zwischen mehreren Positionen
static Map<String, double>? calculateCentroid(List<Position> positions) {
if (positions.isEmpty) return null;
double x = 0, y = 0, z = 0;
for (Position position in positions) {
double lat = degreesToRadians(position.latitude);
double lng = degreesToRadians(position.longitude);
x += math.cos(lat) * math.cos(lng);
y += math.cos(lat) * math.sin(lng);
z += math.sin(lat);
}
int count = positions.length;
x /= count;
y /= count;
z /= count;
double centralLng = math.atan2(y, x);
double centralSqrt = math.sqrt(x * x + y * y);
double centralLat = math.atan2(z, centralSqrt);
return {
'latitude': radiansToDegrees(centralLat),
'longitude': radiansToDegrees(centralLng),
};
}
/// Berechnet das Bounding Box für eine Liste von Positionen
static Map<String, double>? calculateBoundingBox(List<Position> positions) {
if (positions.isEmpty) return null;
double minLat = positions.first.latitude;
double maxLat = positions.first.latitude;
double minLng = positions.first.longitude;
double maxLng = positions.first.longitude;
for (Position position in positions) {
if (position.latitude < minLat) minLat = position.latitude;
if (position.latitude > maxLat) maxLat = position.latitude;
if (position.longitude < minLng) minLng = position.longitude;
if (position.longitude > maxLng) maxLng = position.longitude;
}
return {
'minLatitude': minLat,
'maxLatitude': maxLat,
'minLongitude': minLng,
'maxLongitude': maxLng,
};
}
/// Validiert Koordinaten
static bool isValidCoordinate(double latitude, double longitude) {
return latitude >= -90 && latitude <= 90 && longitude >= -180 && longitude <= 180;
}
/// Berechnet die Geschwindigkeit zwischen zwei Positionen mit Zeitstempel
static double? calculateSpeed(Position position1, Position position2) {
double distance = Geolocator.distanceBetween(
position1.latitude,
position1.longitude,
position2.latitude,
position2.longitude,
);
int timeDifference = position2.timestamp
.difference(position1.timestamp)
.inMilliseconds;
if (timeDifference <= 0) return null;
// Geschwindigkeit in m/s
return distance / (timeDifference / 1000);
}
/// Glättet GPS-Koordinaten mit einem einfachen Moving Average Filter
static List<Position> smoothTrack(List<Position> positions, {int windowSize = 5}) {
if (positions.length <= windowSize) return positions;
List<Position> smoothed = [];
for (int i = 0; i < positions.length; i++) {
int start = math.max(0, i - windowSize ~/ 2);
int end = math.min(positions.length - 1, i + windowSize ~/ 2);
double avgLat = 0;
double avgLng = 0;
int count = 0;
for (int j = start; j <= end; j++) {
avgLat += positions[j].latitude;
avgLng += positions[j].longitude;
count++;
}
avgLat /= count;
avgLng /= count;
smoothed.add(Position(
latitude: avgLat,
longitude: avgLng,
timestamp: positions[i].timestamp,
accuracy: positions[i].accuracy,
altitude: positions[i].altitude,
altitudeAccuracy: positions[i].altitudeAccuracy,
heading: positions[i].heading,
headingAccuracy: positions[i].headingAccuracy,
speed: positions[i].speed,
speedAccuracy: positions[i].speedAccuracy,
));
}
return smoothed;
}
}