mirror of
https://github.com/gpxstudio/gpx.studio.git
synced 2026-10-09 23:26:31 +00:00
progress
This commit is contained in:
1 parent
606a0e974a
commit
67a4c090bd
107 files changed
+3474
-3948
No files matched your search
@@ -21,6 +21,7 @@ enum GPXElement {
|
||||
Track(Track),
|
||||
Segment(TrackSegment),
|
||||
Trackpoint(Trackpoint),
|
||||
RoutePoint(LngLat),
|
||||
Waypoint(Waypoint),
|
||||
Elevation,
|
||||
Time,
|
||||
@@ -51,6 +52,11 @@ fn parse_coordinates(attributes: Attributes<'_>) -> LngLat {
|
||||
coordinates
|
||||
}
|
||||
|
||||
/// Whether the element is a point of the detailed path of a route point (`gpxx:rpt`).
|
||||
fn is_route_point_extension(name: &str) -> bool {
|
||||
name == "rpt" || name.ends_with(":rpt")
|
||||
}
|
||||
|
||||
/// Whether the element holds a value, which is its text.
|
||||
fn holds_text(element: Option<&GPXElement>) -> bool {
|
||||
matches!(
|
||||
@@ -262,6 +268,10 @@ pub fn parse(data: &[u8], categories: &mut TrackpointCategories) -> Result<File,
|
||||
let mut stack: Vec<GPXElement> = vec![];
|
||||
let mut trkpt_chunk = TrackpointChunk::default();
|
||||
let mut wpt_chunk = WaypointChunk::default();
|
||||
// Routes are read as tracks of a single segment, which is what `rte_segment` collects the points of. A route
|
||||
// point that comes with the detailed path of the route to the next one (`gpxx:rpt`) is replaced by it.
|
||||
let mut rte_segment = TrackSegment::default();
|
||||
let mut rpt_points: Vec<Trackpoint> = vec![];
|
||||
// the text of the element at the top of the stack, up to now
|
||||
let mut text = String::new();
|
||||
loop {
|
||||
@@ -283,11 +293,11 @@ pub fn parse(data: &[u8], categories: &mut TrackpointCategories) -> Result<File,
|
||||
stack.push(GPXElement::Link(link));
|
||||
}
|
||||
"text" => stack.push(GPXElement::Text),
|
||||
"trk" => stack.push(GPXElement::Track(Track::default())),
|
||||
"trk" | "rte" => stack.push(GPXElement::Track(Track::default())),
|
||||
"trkseg" => {
|
||||
stack.push(GPXElement::Segment(TrackSegment::default()));
|
||||
}
|
||||
"trkpt" => {
|
||||
"trkpt" | "rtept" => {
|
||||
stack.push(GPXElement::Trackpoint(Trackpoint {
|
||||
coordinates: parse_coordinates(e.attributes()),
|
||||
..Default::default()
|
||||
@@ -299,6 +309,9 @@ pub fn parse(data: &[u8], categories: &mut TrackpointCategories) -> Result<File,
|
||||
..Default::default()
|
||||
}));
|
||||
}
|
||||
name if is_route_point_extension(name) => {
|
||||
stack.push(GPXElement::RoutePoint(parse_coordinates(e.attributes())));
|
||||
}
|
||||
"ele" => stack.push(GPXElement::Elevation),
|
||||
"time" => stack.push(GPXElement::Time),
|
||||
e if e.ends_with("atemp") => stack.push(GPXElement::Temperature),
|
||||
@@ -330,6 +343,25 @@ pub fn parse(data: &[u8], categories: &mut TrackpointCategories) -> Result<File,
|
||||
}
|
||||
}
|
||||
}
|
||||
"rtept" => {
|
||||
if let Some(GPXElement::Track(_)) = stack.last() {
|
||||
trkpt_chunk.trkpt.push(Trackpoint {
|
||||
coordinates: parse_coordinates(e.attributes()),
|
||||
..Default::default()
|
||||
});
|
||||
if trkpt_chunk.is_full() {
|
||||
rte_segment.push(std::mem::take(&mut trkpt_chunk));
|
||||
}
|
||||
}
|
||||
}
|
||||
name if is_route_point_extension(name) => {
|
||||
if let Some(GPXElement::Trackpoint(_)) = stack.last() {
|
||||
rpt_points.push(Trackpoint {
|
||||
coordinates: parse_coordinates(e.attributes()),
|
||||
..Default::default()
|
||||
});
|
||||
}
|
||||
}
|
||||
"wpt" => {
|
||||
wpt_chunk.wpt.push(Waypoint {
|
||||
coordinates: parse_coordinates(e.attributes()),
|
||||
@@ -385,6 +417,42 @@ pub fn parse(data: &[u8], categories: &mut TrackpointCategories) -> Result<File,
|
||||
gpx.trk.push(trk);
|
||||
}
|
||||
}
|
||||
"rte" => {
|
||||
if let Some(GPXElement::Track(mut trk)) = stack.pop() {
|
||||
rte_segment.push(std::mem::take(&mut trkpt_chunk));
|
||||
if !rte_segment.is_empty() {
|
||||
let mut trkseg = std::mem::take(&mut rte_segment);
|
||||
trkseg.compute_anchors();
|
||||
trk.trkseg.push(trkseg);
|
||||
}
|
||||
gpx.trk.push(trk);
|
||||
}
|
||||
}
|
||||
"rtept" => {
|
||||
if let Some(GPXElement::Trackpoint(rtept)) = stack.pop()
|
||||
&& let Some(GPXElement::Track(_)) = stack.last()
|
||||
{
|
||||
let points = if rpt_points.is_empty() {
|
||||
vec![rtept]
|
||||
} else {
|
||||
std::mem::take(&mut rpt_points)
|
||||
};
|
||||
for point in points {
|
||||
trkpt_chunk.trkpt.push(point);
|
||||
if trkpt_chunk.is_full() {
|
||||
rte_segment.push(std::mem::take(&mut trkpt_chunk));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
name if is_route_point_extension(name) => {
|
||||
if let Some(GPXElement::RoutePoint(coordinates)) = stack.pop() {
|
||||
rpt_points.push(Trackpoint {
|
||||
coordinates,
|
||||
..Default::default()
|
||||
});
|
||||
}
|
||||
}
|
||||
"trkseg" => {
|
||||
if let Some(GPXElement::Segment(mut trkseg)) = stack.pop()
|
||||
&& let Some(GPXElement::Track(trk)) = stack.last_mut()
|
||||
@@ -571,6 +639,52 @@ mod tests {
|
||||
assert_eq!(trkpt.ele, 115.5);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_routes_as_tracks() {
|
||||
let gpx = parse_data("with_routes");
|
||||
|
||||
assert_eq!(gpx.trk.len(), 2);
|
||||
let trk = &gpx.trk[0];
|
||||
assert_eq!(trk.info.name.as_deref(), Some("route 1"));
|
||||
assert_eq!(trk.info.type_.as_deref(), Some("Cycling"));
|
||||
assert_eq!(trk.trkseg.len(), 1);
|
||||
let trkseg = &trk.trkseg[0];
|
||||
assert_eq!(trkseg.len(), 49);
|
||||
let trkpt = &trkseg[0];
|
||||
assert_eq!(trkpt.coordinates.lat, 50.790867);
|
||||
assert_eq!(trkpt.coordinates.lng, 4.404968);
|
||||
assert_eq!(trkpt.ele, 109.0);
|
||||
|
||||
let trk = &gpx.trk[1];
|
||||
assert_eq!(trk.info.name.as_deref(), Some("route 2"));
|
||||
assert_eq!(trk.trkseg[0].len(), 28);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_route_point_extensions() {
|
||||
let gpx = parse_data("with_route_extensions");
|
||||
|
||||
// a route without points has no segment
|
||||
assert_eq!(gpx.trk.len(), 2);
|
||||
assert!(gpx.trk[1].trkseg.is_empty());
|
||||
|
||||
let trk = &gpx.trk[0];
|
||||
assert_eq!(trk.info.name.as_deref(), Some("with extensions"));
|
||||
assert_eq!(trk.trkseg.len(), 1);
|
||||
let points: Vec<_> = trk.trkseg[0].iter().collect();
|
||||
// the detailed path replaces the route point it comes with
|
||||
let coordinates: Vec<_> = points
|
||||
.iter()
|
||||
.map(|p| (p.coordinates.lat, p.coordinates.lng))
|
||||
.collect();
|
||||
assert_eq!(
|
||||
coordinates,
|
||||
[(1.1, 1.1), (1.2, 1.2), (2.0, 2.0), (3.0, 3.0)]
|
||||
);
|
||||
assert_eq!(points[2].ele, 20.0);
|
||||
assert!(points[2].time.is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_segments() {
|
||||
let gpx = parse_data("with_segments");
|
||||
|
||||
Reference in new issue
Block a user