MotorSignal-App/lib/services/routes/valhalla_polyline_decoder.dart

72 lines
2.2 KiB
Dart

import '../map/map_provider_interface.dart';
/// Decodes Valhalla's precision-6 encoded polyline into standard AppLatLng coordinates.
class ValhallaPolylineDecoder {
/// Decodes precision-6 polyline string into a list of [AppLatLng].
///
/// Throws [FormatException] if the encoded string is truncated, contains
/// invalid character codes (< 63), or resolves to coordinates outside valid
/// geographic bounds (lat: [-90, 90], lng: [-180, 180]).
static List<AppLatLng> decode(String encoded) {
if (encoded.isEmpty) return const [];
final List<AppLatLng> polyline = [];
int index = 0;
int lat = 0;
int lng = 0;
final int len = encoded.length;
while (index < len) {
// Decode latitude delta
int b;
int shift = 0;
int result = 0;
do {
if (index >= len) {
throw const FormatException('Truncated polyline data while reading latitude');
}
final code = encoded.codeUnitAt(index++);
if (code < 63) {
throw FormatException('Invalid polyline character at index ${index - 1}: code $code');
}
b = code - 63;
result |= (b & 0x1f) << shift;
shift += 5;
} while (b >= 0x20);
final int dlat = (result & 1) != 0 ? ~(result >> 1) : (result >> 1);
lat += dlat;
// Decode longitude delta
shift = 0;
result = 0;
do {
if (index >= len) {
throw const FormatException('Truncated polyline data while reading longitude');
}
final code = encoded.codeUnitAt(index++);
if (code < 63) {
throw FormatException('Invalid polyline character at index ${index - 1}: code $code');
}
b = code - 63;
result |= (b & 0x1f) << shift;
shift += 5;
} while (b >= 0x20);
final int dlng = (result & 1) != 0 ? ~(result >> 1) : (result >> 1);
lng += dlng;
final latDeg = lat / 1e6;
final lngDeg = lng / 1e6;
if (latDeg < -90.0 || latDeg > 90.0 || lngDeg < -180.0 || lngDeg > 180.0) {
throw FormatException('Decoded coordinate out of geographic bounds: ($latDeg, $lngDeg)');
}
polyline.add(AppLatLng(latDeg, lngDeg));
}
return polyline;
}
}