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 = >[]; // 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 _readLatLngElements( Iterable elements, ) { final points = []; 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 _elementsByLocalName( XmlNode node, String localName, ) { return node.descendants.whereType().where( (element) => element.name.local == localName, ); } }