mirror of
https://github.com/gpxstudio/gpx.studio.git
synced 2026-10-27 02:27:18 +00:00
route export, filter out anchors at high zoom level
This commit is contained in:
1 parent
0f957fb1f2
commit
45f877ec78
1 file changed
+40
-10
+40
-10
@@ -70,6 +70,10 @@ impl File {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The highest zoom level from which an anchor is shown that makes it a route point, see [`write`]:
|
||||||
|
/// the anchors that only appear when zooming in further are details of the path.
|
||||||
|
pub const ROUTE_POINT_MAX_ZOOM: u8 = 12;
|
||||||
|
|
||||||
const SCHEMA_LOCATION: &str = "http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd http://www.garmin.com/xmlschemas/GpxExtensions/v3 http://www.garmin.com/xmlschemas/GpxExtensionsv3.xsd http://www.garmin.com/xmlschemas/TrackPointExtension/v1 http://www.garmin.com/xmlschemas/TrackPointExtensionv1.xsd http://www.garmin.com/xmlschemas/PowerExtension/v1 http://www.garmin.com/xmlschemas/PowerExtensionv1.xsd http://www.topografix.com/GPX/gpx_style/0/2 http://www.topografix.com/GPX/gpx_style/0/2/gpx_style.xsd";
|
const SCHEMA_LOCATION: &str = "http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd http://www.garmin.com/xmlschemas/GpxExtensions/v3 http://www.garmin.com/xmlschemas/GpxExtensionsv3.xsd http://www.garmin.com/xmlschemas/TrackPointExtension/v1 http://www.garmin.com/xmlschemas/TrackPointExtensionv1.xsd http://www.garmin.com/xmlschemas/PowerExtension/v1 http://www.garmin.com/xmlschemas/PowerExtensionv1.xsd http://www.topografix.com/GPX/gpx_style/0/2 http://www.topografix.com/GPX/gpx_style/0/2/gpx_style.xsd";
|
||||||
|
|
||||||
type XmlWriter = Writer<Vec<u8>>;
|
type XmlWriter = Writer<Vec<u8>>;
|
||||||
@@ -77,9 +81,10 @@ type XmlWriter = Writer<Vec<u8>>;
|
|||||||
/// Writes a file as GPX 1.1, in UTF-8. The surface, highway, SAC scale and MTB scale of the trackpoints are
|
/// Writes a file as GPX 1.1, in UTF-8. The surface, highway, SAC scale and MTB scale of the trackpoints are
|
||||||
/// the names of their codes in `categories`.
|
/// the names of their codes in `categories`.
|
||||||
///
|
///
|
||||||
/// The routes are written as routes (`rte`): the anchors of their segment are the `rtept` and the
|
/// The routes are written as routes (`rte`): the anchors of their segment that are shown from the
|
||||||
/// trackpoints between two anchors are the `gpxx:rpt` of the `rtept` before them (which only have
|
/// zoom level [`ROUTE_POINT_MAX_ZOOM`] or a lower one are the `rtept`, and the trackpoints between
|
||||||
/// a position, unlike the `rtept`). The first and last trackpoints always are `rtept`.
|
/// two of them are the `gpxx:rpt` of the `rtept` before them (which only have a position, unlike
|
||||||
|
/// the `rtept`). The first and last trackpoints always are `rtept`.
|
||||||
pub fn write(file: &File, categories: &TrackpointCategories, options: ExportOptions) -> Vec<u8> {
|
pub fn write(file: &File, categories: &TrackpointCategories, options: ExportOptions) -> Vec<u8> {
|
||||||
let mut writer = Writer::new_with_indent(Vec::new(), b' ', 4);
|
let mut writer = Writer::new_with_indent(Vec::new(), b' ', 4);
|
||||||
// writing to memory does not fail
|
// writing to memory does not fail
|
||||||
@@ -306,12 +311,18 @@ fn write_route(
|
|||||||
write_track_info(w, name, info)?;
|
write_track_info(w, name, info)?;
|
||||||
let points: Vec<&Trackpoint> = segment.iter().collect();
|
let points: Vec<&Trackpoint> = segment.iter().collect();
|
||||||
let last = points.len().saturating_sub(1);
|
let last = points.len().saturating_sub(1);
|
||||||
let is_anchor = |i: usize| i == 0 || i == last || points[i].anchor.is_some();
|
let is_route_point = |i: usize| {
|
||||||
|
i == 0
|
||||||
|
|| i == last
|
||||||
|
|| points[i]
|
||||||
|
.anchor
|
||||||
|
.is_some_and(|zoom| zoom <= ROUTE_POINT_MAX_ZOOM)
|
||||||
|
};
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
while i < points.len() {
|
while i < points.len() {
|
||||||
// the trackpoints up to the next anchor
|
// the trackpoints up to the next route point
|
||||||
let mut next = i + 1;
|
let mut next = i + 1;
|
||||||
while next < points.len() && !is_anchor(next) {
|
while next < points.len() && !is_route_point(next) {
|
||||||
next += 1;
|
next += 1;
|
||||||
}
|
}
|
||||||
write_point(
|
write_point(
|
||||||
@@ -649,27 +660,46 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_the_anchors_of_a_route_are_its_route_points() {
|
fn test_the_anchors_shown_up_to_zoom_12_are_the_route_points() {
|
||||||
let (file, mut categories) = read("with_tracks_and_segments");
|
let (file, mut categories) = read("with_tracks_and_segments");
|
||||||
let file = as_routes(file);
|
let file = as_routes(file);
|
||||||
assert!(file.rte.len() > 2);
|
assert!(file.rte.len() > 2);
|
||||||
let written = write(&file, &categories, ExportOptions::ALL);
|
let written = write(&file, &categories, ExportOptions::ALL);
|
||||||
let again = parse(&written, &mut categories).unwrap();
|
let again = parse(&written, &mut categories).unwrap();
|
||||||
assert_eq!(again.rte.len(), file.rte.len());
|
assert_eq!(again.rte.len(), file.rte.len());
|
||||||
let anchors = |s: &crate::TrackSegment| {
|
let anchors = |s: &crate::TrackSegment, max_zoom: u8| {
|
||||||
s.iter()
|
s.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.filter(|(_, p)| p.anchor.is_some())
|
.filter(|(_, p)| p.anchor.is_some_and(|zoom| zoom <= max_zoom))
|
||||||
.map(|(i, _)| i)
|
.map(|(i, _)| i)
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
};
|
};
|
||||||
for (a, b) in file.rte.iter().zip(&again.rte) {
|
for (a, b) in file.rte.iter().zip(&again.rte) {
|
||||||
assert_eq!(a.info.name, b.info.name);
|
assert_eq!(a.info.name, b.info.name);
|
||||||
assert_eq!(a.seg.len(), b.seg.len());
|
assert_eq!(a.seg.len(), b.seg.len());
|
||||||
assert_eq!(anchors(&a.seg), anchors(&b.seg));
|
// the route points come back as anchors, the other anchors as points of the path
|
||||||
|
assert_eq!(
|
||||||
|
anchors(&a.seg, ROUTE_POINT_MAX_ZOOM),
|
||||||
|
anchors(&b.seg, u8::MAX)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_anchors_shown_from_a_higher_zoom_are_points_of_the_path() {
|
||||||
|
let (file, categories) = read("simple");
|
||||||
|
let mut file = as_routes(file);
|
||||||
|
let segment = &mut file.rte[0].seg;
|
||||||
|
segment.update_all(|_, point| point.anchor = None);
|
||||||
|
let len = segment.len();
|
||||||
|
segment.set_anchor(5, ROUTE_POINT_MAX_ZOOM);
|
||||||
|
segment.set_anchor(10, ROUTE_POINT_MAX_ZOOM + 1);
|
||||||
|
let text = written_text(&file, &categories, ExportOptions::ALL);
|
||||||
|
// the ends, and the anchor shown from zoom 12
|
||||||
|
assert_eq!(count(&text, "<rtept"), 3);
|
||||||
|
assert_eq!(count(&text, "<gpxx:rpt"), len - 3);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_the_ends_of_a_route_are_route_points_even_without_anchors() {
|
fn test_the_ends_of_a_route_are_route_points_even_without_anchors() {
|
||||||
let (file, categories) = read("simple");
|
let (file, categories) = read("simple");
|
||||||
|
|||||||
Reference in new issue
Block a user