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

108 lines
2.7 KiB
Dart

import 'package:aves/model/gpx/gpx_track.dart';
import 'package:latlong2/latlong.dart';
import 'package:xml/xml.dart';
class GpxParser {
static GpxTrack parse(
String xmlContent, {
String fallbackName = 'GPX track',
}) {
final document = XmlDocument.parse(xmlContent);
final name = _readTrackName(document) ?? fallbackName;
final segments = <List<LatLng>>[];
// Standard GPX tracks: trk/trkseg/trkpt
for (final segment in _elementsByLocalName(document, 'trkseg')) {
final points = _readLatLngElements(
segment.findElements('trkpt'),
);
if (points.length >= 2) {
segments.add(points);
}
}
// GPX routes: rte/rtept
for (final route in _elementsByLocalName(document, 'rte')) {
final points = _readLatLngElements(
route.findElements('rtept'),
);
if (points.length >= 2) {
segments.add(points);
}
}
// Fallback: waypoints as a single line only if there are enough.
if (segments.isEmpty) {
final waypoints = _readLatLngElements(
_elementsByLocalName(document, 'wpt'),
);
if (waypoints.length >= 2) {
segments.add(waypoints);
}
}
return GpxTrack(
name: name,
tracks: segments.toSet(),
);
}
static List<LatLng> _readLatLngElements(
Iterable<XmlElement> elements,
) {
final points = <LatLng>[];
for (final point in elements) {
final latString = point.getAttribute('lat');
final lonString = point.getAttribute('lon');
if (latString == null || lonString == null) continue;
final lat = double.tryParse(latString);
final lon = double.tryParse(lonString);
if (lat == null || lon == null) continue;
if (!lat.isFinite || !lon.isFinite) continue;
points.add(LatLng(lat, lon));
}
return points;
}
static String? _readTrackName(XmlDocument document) {
for (final track in _elementsByLocalName(document, 'trk')) {
for (final name in track.findElements('name')) {
final text = name.innerText.trim();
if (text.isNotEmpty) return text;
}
}
for (final route in _elementsByLocalName(document, 'rte')) {
for (final name in route.findElements('name')) {
final text = name.innerText.trim();
if (text.isNotEmpty) return text;
}
}
for (final name in _elementsByLocalName(document, 'name')) {
final text = name.innerText.trim();
if (text.isNotEmpty) return text;
}
return null;
}
static Iterable<XmlElement> _elementsByLocalName(
XmlNode node,
String localName,
) {
return node.descendants.whereType<XmlElement>().where(
(element) => element.name.local == localName,
);
}
}