This commit is contained in:
vcoppe
2026-09-23 19:53:39 +02:00
parent a486016d96
commit 497ac120ec
16 changed files with 604 additions and 284 deletions
+29
View File
@@ -88,12 +88,24 @@ dependencies = [
"slab", "slab",
] ]
[[package]]
name = "getrandom"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099"
dependencies = [
"cfg-if",
"libc",
"r-efi",
]
[[package]] [[package]]
name = "gpx-rs" name = "gpx-rs"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"chrono", "chrono",
"quick-xml", "quick-xml",
"uuid",
"wasm-bindgen", "wasm-bindgen",
] ]
@@ -198,6 +210,12 @@ dependencies = [
"proc-macro2", "proc-macro2",
] ]
[[package]]
name = "r-efi"
version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
[[package]] [[package]]
name = "rustversion" name = "rustversion"
version = "1.0.23" version = "1.0.23"
@@ -233,6 +251,17 @@ version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
[[package]]
name = "uuid"
version = "1.26.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ef6dac1e96601b4fb3acccccff2139741fcb757cb9a36089bf5be91cfb285ce"
dependencies = [
"getrandom",
"js-sys",
"wasm-bindgen",
]
[[package]] [[package]]
name = "wasm-bindgen" name = "wasm-bindgen"
version = "0.2.126" version = "0.2.126"
+1
View File
@@ -9,4 +9,5 @@ crate-type = ["cdylib"]
[dependencies] [dependencies]
chrono = "0.4.45" chrono = "0.4.45"
quick-xml = "0.42.0" quick-xml = "0.42.0"
uuid = {version = "1.26.1", features = ["v4", "js"]}
wasm-bindgen = "0.2" wasm-bindgen = "0.2"
+39 -69
View File
@@ -1,7 +1,7 @@
use std::rc::Rc; use std::rc::Rc;
use crate::gpx::{ use crate::gpx::{
Author, GPXFile, Link, LngLat, Track, TrackPoint, TrackPointChunk, TrackSegment, Waypoint, Author, GPXFile, Link, LngLat, Track, TrackSegment, Trackpoint, TrackpointChunk, Waypoint,
WaypointChunk, WaypointChunk,
}; };
use chrono::DateTime; use chrono::DateTime;
@@ -21,7 +21,7 @@ enum GPXElement {
TEXT, TEXT,
TRACK(Track), TRACK(Track),
SEGMENT(TrackSegment), SEGMENT(TrackSegment),
TRACKPOINT(TrackPoint), TRACKPOINT(Trackpoint),
WAYPOINT(Waypoint), WAYPOINT(Waypoint),
ELEVATION, ELEVATION,
TIME, TIME,
@@ -55,7 +55,7 @@ pub fn parse(data: &[u8]) -> Result<GPXFile, Error> {
let mut buf = vec![]; let mut buf = vec![];
let mut gpx = GPXFile::default(); let mut gpx = GPXFile::default();
let mut stack: Vec<GPXElement> = vec![]; let mut stack: Vec<GPXElement> = vec![];
let mut trkpt_chunk = TrackPointChunk::default(); let mut trkpt_chunk = TrackpointChunk::default();
let mut wpt_chunk = WaypointChunk::default(); let mut wpt_chunk = WaypointChunk::default();
loop { loop {
match reader.read_event_into(&mut buf) { match reader.read_event_into(&mut buf) {
@@ -83,7 +83,7 @@ pub fn parse(data: &[u8]) -> Result<GPXFile, Error> {
stack.push(GPXElement::SEGMENT(TrackSegment::default())); stack.push(GPXElement::SEGMENT(TrackSegment::default()));
} }
"trkpt" => { "trkpt" => {
let mut trkpt = TrackPoint::default(); let mut trkpt = Trackpoint::default();
trkpt.coordinates = parse_coordinates(e.attributes()); trkpt.coordinates = parse_coordinates(e.attributes());
stack.push(GPXElement::TRACKPOINT(trkpt)); stack.push(GPXElement::TRACKPOINT(trkpt));
} }
@@ -104,7 +104,7 @@ pub fn parse(data: &[u8]) -> Result<GPXFile, Error> {
e if e.ends_with("color") => stack.push(GPXElement::COLOR), e if e.ends_with("color") => stack.push(GPXElement::COLOR),
e if e.ends_with("opacity") => stack.push(GPXElement::OPACITY), e if e.ends_with("opacity") => stack.push(GPXElement::OPACITY),
e if e.ends_with("width") => stack.push(GPXElement::WIDTH), e if e.ends_with("width") => stack.push(GPXElement::WIDTH),
_ => println!("{:?}", e), _ => (),
}, },
Ok(Event::End(e)) => match e.name().as_ref() { Ok(Event::End(e)) => match e.name().as_ref() {
"gpx" => { "gpx" => {
@@ -146,8 +146,8 @@ pub fn parse(data: &[u8]) -> Result<GPXFile, Error> {
if let Some(GPXElement::SEGMENT(mut trkseg)) = stack.pop() { if let Some(GPXElement::SEGMENT(mut trkseg)) = stack.pop() {
if let Some(GPXElement::TRACK(trk)) = stack.last_mut() { if let Some(GPXElement::TRACK(trk)) = stack.last_mut() {
if !trkpt_chunk.trkpt.is_empty() { if !trkpt_chunk.trkpt.is_empty() {
trkseg.chunks.push(Rc::new(trkpt_chunk)); trkseg.push(trkpt_chunk);
trkpt_chunk = TrackPointChunk::default(); trkpt_chunk = TrackpointChunk::default();
} }
trk.trkseg.push(trkseg); trk.trkseg.push(trkseg);
} }
@@ -158,8 +158,8 @@ pub fn parse(data: &[u8]) -> Result<GPXFile, Error> {
if let Some(GPXElement::SEGMENT(trkseg)) = stack.last_mut() { if let Some(GPXElement::SEGMENT(trkseg)) = stack.last_mut() {
trkpt_chunk.trkpt.push(trkpt); trkpt_chunk.trkpt.push(trkpt);
if trkpt_chunk.is_full() { if trkpt_chunk.is_full() {
trkseg.chunks.push(Rc::new(trkpt_chunk)); trkseg.push(trkpt_chunk);
trkpt_chunk = TrackPointChunk::default(); trkpt_chunk = TrackpointChunk::default();
} }
} }
} }
@@ -383,10 +383,8 @@ mod tests {
assert_eq!(trk.trkseg.len(), 1); assert_eq!(trk.trkseg.len(), 1);
let trkseg = &trk.trkseg[0]; let trkseg = &trk.trkseg[0];
assert_eq!(trkseg.chunks.len(), 1); assert_eq!(trkseg.len(), 80);
let chunk = &trkseg.chunks[0]; let trkpt = &trkseg[0];
assert_eq!(chunk.trkpt.len(), 80);
let trkpt = &chunk.trkpt[0];
assert_eq!(trkpt.coordinates.lat, 50.790867); assert_eq!(trkpt.coordinates.lat, 50.790867);
assert_eq!(trkpt.coordinates.lng, 4.404968); assert_eq!(trkpt.coordinates.lng, 4.404968);
assert_eq!(trkpt.ele, 109.0); assert_eq!(trkpt.ele, 109.0);
@@ -406,10 +404,8 @@ mod tests {
assert_eq!(trk.trkseg.len(), 1); assert_eq!(trk.trkseg.len(), 1);
let trkseg = &trk.trkseg[0]; let trkseg = &trk.trkseg[0];
assert_eq!(trkseg.chunks.len(), 1); assert_eq!(trkseg.len(), 49);
let chunk = &trkseg.chunks[0]; let trkpt = &trkseg[0];
assert_eq!(chunk.trkpt.len(), 49);
let trkpt = &chunk.trkpt[0];
assert_eq!(trkpt.coordinates.lat, 50.790867); assert_eq!(trkpt.coordinates.lat, 50.790867);
assert_eq!(trkpt.coordinates.lng, 4.404968); assert_eq!(trkpt.coordinates.lng, 4.404968);
assert_eq!(trkpt.ele, 109.0); assert_eq!(trkpt.ele, 109.0);
@@ -420,10 +416,8 @@ mod tests {
assert_eq!(trk.trkseg.len(), 1); assert_eq!(trk.trkseg.len(), 1);
let trkseg = &trk.trkseg[0]; let trkseg = &trk.trkseg[0];
assert_eq!(trkseg.chunks.len(), 1); assert_eq!(trkseg.len(), 28);
let chunk = &trkseg.chunks[0]; let trkpt = &trkseg[0];
assert_eq!(chunk.trkpt.len(), 28);
let trkpt = &chunk.trkpt[0];
assert_eq!(trkpt.coordinates.lat, 50.782212); assert_eq!(trkpt.coordinates.lat, 50.782212);
assert_eq!(trkpt.coordinates.lng, 4.406377); assert_eq!(trkpt.coordinates.lng, 4.406377);
assert_eq!(trkpt.ele, 115.5); assert_eq!(trkpt.ele, 115.5);
@@ -441,19 +435,15 @@ mod tests {
assert_eq!(trk.trkseg.len(), 2); assert_eq!(trk.trkseg.len(), 2);
let trkseg = &trk.trkseg[0]; let trkseg = &trk.trkseg[0];
assert_eq!(trkseg.chunks.len(), 1); assert_eq!(trkseg.len(), 49);
let chunk = &trkseg.chunks[0]; let trkpt = &trkseg[0];
assert_eq!(chunk.trkpt.len(), 49);
let trkpt = &chunk.trkpt[0];
assert_eq!(trkpt.coordinates.lat, 50.790867); assert_eq!(trkpt.coordinates.lat, 50.790867);
assert_eq!(trkpt.coordinates.lng, 4.404968); assert_eq!(trkpt.coordinates.lng, 4.404968);
assert_eq!(trkpt.ele, 109.0); assert_eq!(trkpt.ele, 109.0);
let trkseg = &trk.trkseg[1]; let trkseg = &trk.trkseg[1];
assert_eq!(trkseg.chunks.len(), 1); assert_eq!(trkseg.len(), 28);
let chunk = &trkseg.chunks[0]; let trkpt = &trkseg[0];
assert_eq!(chunk.trkpt.len(), 28);
let trkpt = &chunk.trkpt[0];
assert_eq!(trkpt.coordinates.lat, 50.782212); assert_eq!(trkpt.coordinates.lat, 50.782212);
assert_eq!(trkpt.coordinates.lng, 4.406377); assert_eq!(trkpt.coordinates.lng, 4.406377);
assert_eq!(trkpt.ele, 115.5); assert_eq!(trkpt.ele, 115.5);
@@ -471,19 +461,15 @@ mod tests {
assert_eq!(trk.trkseg.len(), 2); assert_eq!(trk.trkseg.len(), 2);
let trkseg = &trk.trkseg[0]; let trkseg = &trk.trkseg[0];
assert_eq!(trkseg.chunks.len(), 1); assert_eq!(trkseg.len(), 16);
let chunk = &trkseg.chunks[0]; let trkpt = &trkseg[0];
assert_eq!(chunk.trkpt.len(), 16);
let trkpt = &chunk.trkpt[0];
assert_eq!(trkpt.coordinates.lat, 50.790867); assert_eq!(trkpt.coordinates.lat, 50.790867);
assert_eq!(trkpt.coordinates.lng, 4.404968); assert_eq!(trkpt.coordinates.lng, 4.404968);
assert_eq!(trkpt.ele, 109.0); assert_eq!(trkpt.ele, 109.0);
let trkseg = &trk.trkseg[1]; let trkseg = &trk.trkseg[1];
assert_eq!(trkseg.chunks.len(), 1); assert_eq!(trkseg.len(), 34);
let chunk = &trkseg.chunks[0]; let trkpt = &trkseg[0];
assert_eq!(chunk.trkpt.len(), 34);
let trkpt = &chunk.trkpt[0];
assert_eq!(trkpt.coordinates.lat, 50.78727108169855); assert_eq!(trkpt.coordinates.lat, 50.78727108169855);
assert_eq!(trkpt.coordinates.lng, 4.406133681127736); assert_eq!(trkpt.coordinates.lng, 4.406133681127736);
assert_eq!(trkpt.ele, 115.0); assert_eq!(trkpt.ele, 115.0);
@@ -492,19 +478,15 @@ mod tests {
assert_eq!(trk.trkseg.len(), 2); assert_eq!(trk.trkseg.len(), 2);
let trkseg = &trk.trkseg[0]; let trkseg = &trk.trkseg[0];
assert_eq!(trkseg.chunks.len(), 1); assert_eq!(trkseg.len(), 19);
let chunk = &trkseg.chunks[0]; let trkpt = &trkseg[0];
assert_eq!(chunk.trkpt.len(), 19);
let trkpt = &chunk.trkpt[0];
assert_eq!(trkpt.coordinates.lat, 50.782212); assert_eq!(trkpt.coordinates.lat, 50.782212);
assert_eq!(trkpt.coordinates.lng, 4.406377); assert_eq!(trkpt.coordinates.lng, 4.406377);
assert_eq!(trkpt.ele, 115.5); assert_eq!(trkpt.ele, 115.5);
let trkseg = &trk.trkseg[1]; let trkseg = &trk.trkseg[1];
assert_eq!(trkseg.chunks.len(), 1); assert_eq!(trkseg.len(), 10);
let chunk = &trkseg.chunks[0]; let trkpt = &trkseg[0];
assert_eq!(chunk.trkpt.len(), 10);
let trkpt = &chunk.trkpt[0];
assert_eq!(trkpt.coordinates.lat, 50.77906316558724); assert_eq!(trkpt.coordinates.lat, 50.77906316558724);
assert_eq!(trkpt.coordinates.lng, 4.412547477922485); assert_eq!(trkpt.coordinates.lng, 4.412547477922485);
assert_eq!(trkpt.ele, 133.3); assert_eq!(trkpt.ele, 133.3);
@@ -553,10 +535,8 @@ mod tests {
let trk = &gpx.trk[0]; let trk = &gpx.trk[0];
assert_eq!(trk.trkseg.len(), 1); assert_eq!(trk.trkseg.len(), 1);
let trkseg = &trk.trkseg[0]; let trkseg = &trk.trkseg[0];
assert_eq!(trkseg.chunks.len(), 1); assert!(trkseg.len() > 0);
let chunk = &trkseg.chunks[0]; let trkpt = &trkseg[0];
assert!(!chunk.trkpt.is_empty());
let trkpt = &chunk.trkpt[0];
assert!(trkpt.time.is_some_and(|t| t == 1704063600000)); assert!(trkpt.time.is_some_and(|t| t == 1704063600000));
} }
@@ -571,10 +551,8 @@ mod tests {
let trk = &gpx.trk[0]; let trk = &gpx.trk[0];
assert_eq!(trk.trkseg.len(), 1); assert_eq!(trk.trkseg.len(), 1);
let trkseg = &trk.trkseg[0]; let trkseg = &trk.trkseg[0];
assert_eq!(trkseg.chunks.len(), 1); assert!(trkseg.len() > 0);
let chunk = &trkseg.chunks[0]; let trkpt = &trkseg[0];
assert!(!chunk.trkpt.is_empty());
let trkpt = &chunk.trkpt[0];
assert!(trkpt.hr.is_some_and(|h| h == 150)); assert!(trkpt.hr.is_some_and(|h| h == 150));
} }
@@ -589,10 +567,8 @@ mod tests {
let trk = &gpx.trk[0]; let trk = &gpx.trk[0];
assert_eq!(trk.trkseg.len(), 1); assert_eq!(trk.trkseg.len(), 1);
let trkseg = &trk.trkseg[0]; let trkseg = &trk.trkseg[0];
assert_eq!(trkseg.chunks.len(), 1); assert!(trkseg.len() > 0);
let chunk = &trkseg.chunks[0]; let trkpt = &trkseg[0];
assert!(!chunk.trkpt.is_empty());
let trkpt = &chunk.trkpt[0];
assert!(trkpt.cad.is_some_and(|c| c == 80)); assert!(trkpt.cad.is_some_and(|c| c == 80));
} }
@@ -607,10 +583,8 @@ mod tests {
let trk = &gpx.trk[0]; let trk = &gpx.trk[0];
assert_eq!(trk.trkseg.len(), 1); assert_eq!(trk.trkseg.len(), 1);
let trkseg = &trk.trkseg[0]; let trkseg = &trk.trkseg[0];
assert_eq!(trkseg.chunks.len(), 1); assert!(trkseg.len() > 0);
let chunk = &trkseg.chunks[0]; let trkpt = &trkseg[0];
assert!(!chunk.trkpt.is_empty());
let trkpt = &chunk.trkpt[0];
assert!(trkpt.power.is_some_and(|p| p == 200)); assert!(trkpt.power.is_some_and(|p| p == 200));
} }
@@ -625,10 +599,8 @@ mod tests {
let trk = &gpx.trk[0]; let trk = &gpx.trk[0];
assert_eq!(trk.trkseg.len(), 1); assert_eq!(trk.trkseg.len(), 1);
let trkseg = &trk.trkseg[0]; let trkseg = &trk.trkseg[0];
assert_eq!(trkseg.chunks.len(), 1); assert!(trkseg.len() > 0);
let chunk = &trkseg.chunks[0]; let trkpt = &trkseg[0];
assert!(!chunk.trkpt.is_empty());
let trkpt = &chunk.trkpt[0];
assert!(trkpt.power.is_some_and(|p| p == 200)); assert!(trkpt.power.is_some_and(|p| p == 200));
} }
@@ -643,10 +615,8 @@ mod tests {
let trk = &gpx.trk[0]; let trk = &gpx.trk[0];
assert_eq!(trk.trkseg.len(), 1); assert_eq!(trk.trkseg.len(), 1);
let trkseg = &trk.trkseg[0]; let trkseg = &trk.trkseg[0];
assert_eq!(trkseg.chunks.len(), 1); assert!(trkseg.len() > 0);
let chunk = &trkseg.chunks[0]; let trkpt = &trkseg[0];
assert!(!chunk.trkpt.is_empty());
let trkpt = &chunk.trkpt[0];
assert!(trkpt.atemp.is_some_and(|t| t == 21)); assert!(trkpt.atemp.is_some_and(|t| t == 21));
} }
+46 -23
View File
@@ -1,42 +1,65 @@
use crate::gpx::TrackPoint; use crate::gpx::{TrackSegment, TrackSegmentIndex};
pub fn ramer_douglas_peucker<F>(n: usize, distance: &F, epsilon: f64) -> Vec<usize> pub fn ramer_douglas_peucker<F, T>(
trkseg: &TrackSegment,
distance: &F,
epsilon: T,
) -> Vec<TrackSegmentIndex>
where where
F: Fn(usize, usize, usize) -> f64, F: Fn(TrackSegmentIndex, TrackSegmentIndex, TrackSegmentIndex) -> T,
T: Default + PartialOrd + Copy,
{ {
if n <= 2 { match trkseg.len() {
(0..n).collect() 0 => vec![],
} else { 1 => vec![trkseg.first_index().unwrap()],
let mut indices = vec![0]; 2 => vec![trkseg.last_index().unwrap()],
ramer_douglas_peucker_helper(0, n - 1, distance, epsilon, &mut indices); _ => {
indices.push(n - 1); let first = trkseg.first_index().unwrap();
let last = trkseg.last_index().unwrap();
let mut indices = vec![first];
ramer_douglas_peucker_helper(trkseg, first, last, distance, epsilon, &mut indices);
indices.push(last);
indices indices
} }
}
} }
fn ramer_douglas_peucker_helper<F>( fn ramer_douglas_peucker_helper<F, T>(
start: usize, trkseg: &TrackSegment,
end: usize, start: TrackSegmentIndex,
end: TrackSegmentIndex,
distance: &F, distance: &F,
epsilon: f64, epsilon: T,
indices: &mut Vec<usize>, indices: &mut Vec<TrackSegmentIndex>,
) where ) where
F: Fn(usize, usize, usize) -> f64, F: Fn(TrackSegmentIndex, TrackSegmentIndex, TrackSegmentIndex) -> T,
T: Default + PartialOrd + Copy,
{ {
let mut idx = 0; let mut max_idx = None;
let mut max_dist = 0.0; let mut max_dist = T::default();
for i in (start + 1)..end { let mut cur = trkseg.next_index(Some(start));
let dist = distance(start, end, i);
while let Some(idx) = cur {
if idx == end {
break;
}
let dist = distance(start, end, idx);
if dist > max_dist { if dist > max_dist {
idx = i; max_idx = Some(idx);
max_dist = dist; max_dist = dist;
} }
cur = trkseg.next_index(cur);
} }
if max_dist > epsilon && idx != 0 { if let Some(idx) = max_idx {
ramer_douglas_peucker_helper(start, idx, distance, epsilon, indices); if max_dist > epsilon {
ramer_douglas_peucker_helper(trkseg, start, idx, distance, epsilon, indices);
indices.push(idx); indices.push(idx);
ramer_douglas_peucker_helper(idx, end, distance, epsilon, indices); ramer_douglas_peucker_helper(trkseg, idx, end, distance, epsilon, indices);
}
} }
} }
+30 -19
View File
@@ -1,39 +1,50 @@
#[macro_export] #[macro_export]
macro_rules! for_each_window { macro_rules! for_each_window {
( (
$trkseg:expr,
$left:expr, $left:expr,
$right:expr, $right:expr,
$window:expr, $window:expr,
|$a:ident, $b:ident| $distance:expr, |$a:ident, $b:ident| $distance:expr,
|$i:ident, $l:ident, $r:ident| $body:block, |$i:ident, $l:ident, $r:ident| $body:block,
) => {{ ) => {{
let mut start = $left; let mut start = $left.unwrap_or_default();
let mut cur = $left;
for $i in $left..$right { while let Some(idx) = cur {
while start + 1 < $i && { // advance left if needed
while start != idx && {
let $a = start; let $a = start;
let $b = $i; let $b = idx;
$distance $distance
} > $window } > $window {
{ if let Some(next) = $trkseg.next_index(Some(start)) {
start += 1; if next == idx {
break;
} }
start = next;
let mut end = $right.min($i + 2); } else {
break;
while end < $right && { }
let $a = $i; }
// advance right if needed
let mut end = idx;
while let Some(next) = $trkseg.next_index(Some(end)) {
if {
let $a = idx;
let $b = end; let $b = end;
$distance $distance
} <= $window } > $window {
{ break;
end += 1;
} }
end = next;
}
// apply body
let $i = idx;
let $l = start; let $l = start;
let $r = end - 1; let $r = end;
$body $body
// go next
cur = $trkseg.next_index(cur);
} }
}}; }};
} }
+41 -7
View File
@@ -1,8 +1,21 @@
use std::rc::Rc; use std::rc::Rc;
use uuid::Uuid;
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
use crate::stack::Stack; use crate::{
actions::parse,
gpx::GPXFile,
stack::{Stack, StackEntry},
};
// Control flow
// - get lock to cancel any other updates
// - update files
// - update separate statistics
// - update selection
// - update selection statistics
// - notify UI (changed files, updated selection, new stats)
#[wasm_bindgen] #[wasm_bindgen]
pub struct Controller { pub struct Controller {
@@ -18,10 +31,31 @@ impl Controller {
} }
} }
// #[wasm_bindgen] #[wasm_bindgen]
// pub fn create_file(&mut self, name: &str) { pub fn create_file(&mut self, name: &str) {
// let id = self.stack.get_new_file_id(); let mut file = GPXFile::default();
// let file = Rc::new(GPXFile::new(id, name)); file.info.name = name.to_string();
// self.stack.update(&[file]); self.stack.create_and_push_next(|entry: &mut StackEntry| {
// } entry.insert(file.id, Rc::new(file));
true
});
}
#[wasm_bindgen]
pub fn load_file(&mut self, data: &[u8]) {
if let Ok(file) = parse(data) {
self.stack.create_and_push_next(|entry: &mut StackEntry| {
entry.insert(file.id, Rc::new(file));
true
});
}
}
#[wasm_bindgen]
pub fn delete_file(&mut self, id: &[u8]) {
if let Ok(id) = Uuid::from_slice(id) {
self.stack
.create_and_push_next(|entry: &mut StackEntry| entry.remove(&id).is_some());
}
}
} }
+27
View File
@@ -0,0 +1,27 @@
use crate::gpx::{Trackpoint, Waypoint};
static MAX_TRKPT_CHUNK_SIZE: usize = 4096;
#[derive(Debug, Default)]
pub struct TrackpointChunk {
pub trkpt: Vec<Trackpoint>,
}
impl TrackpointChunk {
pub fn is_full(&self) -> bool {
self.trkpt.len() == MAX_TRKPT_CHUNK_SIZE
}
}
static MAX_WPT_CHUNK_SIZE: usize = 128;
#[derive(Debug, Default)]
pub struct WaypointChunk {
pub wpt: Vec<Waypoint>,
}
impl WaypointChunk {
pub fn is_full(&self) -> bool {
self.wpt.len() == MAX_WPT_CHUNK_SIZE
}
}
+15 -1
View File
@@ -1,15 +1,29 @@
use std::rc::Rc; use std::rc::Rc;
use uuid::Uuid;
use crate::gpx::{Link, Track, WaypointChunk}; use crate::gpx::{Link, Track, WaypointChunk};
#[derive(Debug, Default)] #[derive(Debug)]
pub struct GPXFile { pub struct GPXFile {
pub id: Uuid,
pub info: GPXFileInfo, pub info: GPXFileInfo,
pub trk: Vec<Track>, pub trk: Vec<Track>,
pub wpt: Vec<Rc<WaypointChunk>>, pub wpt: Vec<Rc<WaypointChunk>>,
// TODO routes // TODO routes
} }
impl Default for GPXFile {
fn default() -> Self {
Self {
id: Uuid::new_v4(),
info: Default::default(),
trk: Default::default(),
wpt: Default::default(),
}
}
}
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct GPXFileInfo { pub struct GPXFileInfo {
pub name: String, pub name: String,
+2
View File
@@ -1,3 +1,4 @@
mod chunk;
mod common; mod common;
mod file; mod file;
mod segment; mod segment;
@@ -5,6 +6,7 @@ mod track;
mod trackpoint; mod trackpoint;
mod waypoint; mod waypoint;
pub use chunk::*;
pub use common::*; pub use common::*;
pub use file::*; pub use file::*;
pub use segment::*; pub use segment::*;
+219 -26
View File
@@ -1,60 +1,253 @@
use std::rc::Rc; use std::{ops::Index, rc::Rc};
use crate::gpx::TrackPoint; use crate::gpx::{Trackpoint, TrackpointChunk};
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct TrackSegment { pub struct TrackSegment {
pub chunks: Vec<Rc<TrackPointChunk>>, chunks: Vec<Rc<TrackpointChunk>>,
cumul_length: Vec<usize>,
} }
impl TrackSegment { impl TrackSegment {
pub fn iter(&self) -> TrackSegmentIterator { pub fn push(&mut self, chunk: TrackpointChunk) {
if chunk.trkpt.is_empty() {
return;
}
self.cumul_length
.push(self.cumul_length.last().copied().unwrap_or_default() + chunk.trkpt.len());
self.chunks.push(Rc::new(chunk));
}
pub fn len(&self) -> usize {
self.cumul_length.last().copied().unwrap_or_default()
}
pub fn iter(&self) -> TrackSegmentIterator<'_> {
TrackSegmentIterator::new(self) TrackSegmentIterator::new(self)
} }
pub fn first_index(&self) -> Option<TrackSegmentIndex> {
self.next_index(None)
}
pub fn last_index(&self) -> Option<TrackSegmentIndex> {
self.prev_index(None)
}
pub fn next_index(&self, cur: Option<TrackSegmentIndex>) -> Option<TrackSegmentIndex> {
let mut next = cur.map_or_default(|idx| TrackSegmentIndex {
chunk: idx.chunk,
pos: idx.pos + 1,
});
loop {
if next.chunk >= self.chunks.len() {
return None;
}
if next.pos == self.chunks[next.chunk].trkpt.len() {
next.chunk += 1;
next.pos = 0;
} else {
return Some(next);
}
}
}
pub fn prev_index(&self, cur: Option<TrackSegmentIndex>) -> Option<TrackSegmentIndex> {
let mut prev = cur.unwrap_or(TrackSegmentIndex {
chunk: self.chunks.len(),
pos: 0,
});
loop {
if prev.pos == 0 {
if prev.chunk == 0 {
return None;
}
prev.chunk -= 1;
prev.pos = self.chunks[prev.chunk].trkpt.len();
} else {
prev.pos -= 1;
return Some(prev);
}
}
}
pub fn to_flat_index(&self, idx: TrackSegmentIndex) -> usize {
idx.pos
+ if idx.chunk > 0 {
self.cumul_length[idx.chunk - 1]
} else {
0
}
}
fn locate(&self, idx: usize) -> Option<TrackSegmentIndex> {
let chunk = self.cumul_length.partition_point(|l| idx >= *l);
if chunk >= self.chunks.len() {
return None;
}
let pos = if chunk > 0 {
idx - self.cumul_length[chunk - 1]
} else {
idx
};
if pos >= self.chunks[chunk].trkpt.len() {
None
} else {
Some(TrackSegmentIndex { chunk, pos })
}
}
} }
impl Index<TrackSegmentIndex> for TrackSegment {
type Output = Trackpoint;
fn index(&self, idx: TrackSegmentIndex) -> &Self::Output {
&self.chunks[idx.chunk].trkpt[idx.pos]
}
}
impl Index<usize> for TrackSegment {
type Output = Trackpoint;
fn index(&self, idx: usize) -> &Self::Output {
&self[self.locate(idx).unwrap()]
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, PartialOrd, Ord)]
pub struct TrackSegmentIndex {
pub chunk: usize,
pub pos: usize,
}
#[derive(Debug, Clone)]
pub struct TrackSegmentIterator<'a> { pub struct TrackSegmentIterator<'a> {
trkseg: &'a TrackSegment, trkseg: &'a TrackSegment,
chunk_idx: usize, idx: Option<TrackSegmentIndex>,
trkpt_idx: usize,
} }
impl<'a> TrackSegmentIterator<'a> { impl<'a> TrackSegmentIterator<'a> {
pub fn new(trkseg: &'a TrackSegment) -> Self { pub fn new(trkseg: &'a TrackSegment) -> Self {
Self { Self {
trkseg, trkseg,
chunk_idx: 0, idx: Default::default(),
trkpt_idx: 0,
} }
} }
} }
impl<'a> Iterator for TrackSegmentIterator<'a> { impl<'a> Iterator for TrackSegmentIterator<'a> {
type Item = &'a TrackPoint; type Item = &'a Trackpoint;
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
if self.chunk_idx >= self.trkseg.chunks.len() { self.idx = self.trkseg.next_index(self.idx);
None self.idx.map(|idx| &self.trkseg[idx])
} else if self.trkpt_idx >= self.trkseg.chunks[self.chunk_idx].trkpt.len() {
self.chunk_idx += 1;
self.trkpt_idx = 0;
self.next()
} else {
self.trkpt_idx += 1;
Some(&self.trkseg.chunks[self.chunk_idx].trkpt[self.trkpt_idx - 1])
} }
fn nth(&mut self, n: usize) -> Option<Self::Item> {
let idx = self
.idx
.map_or_default(|idx| self.trkseg.to_flat_index(idx))
+ n;
self.idx = self.trkseg.locate(idx);
self.idx.map(|idx| &self.trkseg[idx])
} }
} }
static MAX_CHUNK_SIZE: usize = 4096; #[cfg(test)]
mod tests {
use crate::gpx::TrackpointChunk;
#[derive(Debug, Default)] use super::*;
pub struct TrackPointChunk {
pub trkpt: Vec<TrackPoint>,
}
impl TrackPointChunk { fn create_track_segment(nb_chunks: usize) -> TrackSegment {
pub fn is_full(&self) -> bool { let mut trkseg = TrackSegment::default();
self.trkpt.len() == MAX_CHUNK_SIZE let mut count = 0;
for n in 1..=nb_chunks {
let mut chunk = TrackpointChunk::default();
for _ in 0..n {
let mut trkpt = Trackpoint::default();
trkpt.ele = count as f64;
chunk.trkpt.push(trkpt);
count += 1;
}
trkseg.push(chunk);
}
trkseg
}
#[test]
fn test_len() {
let nb_chunks = 10;
let trkseg = create_track_segment(nb_chunks);
assert_eq!(trkseg.len(), nb_chunks * (nb_chunks + 1) / 2);
}
#[test]
fn test_locate() {
let nb_chunks = 10;
let trkseg = create_track_segment(nb_chunks);
for i in 0..trkseg.len() {
let idx = trkseg.locate(i);
assert!(idx.is_some());
assert_eq!(trkseg[idx.unwrap()].ele, i as f64);
}
assert!(trkseg.locate(trkseg.len()).is_none());
}
#[test]
fn test_index() {
let nb_chunks = 10;
let trkseg = create_track_segment(nb_chunks);
for i in 0..trkseg.len() {
assert_eq!(trkseg[i].ele, i as f64);
}
}
#[test]
#[should_panic]
fn test_index_out_of_bounds_1() {
let nb_chunks = 10;
let trkseg = create_track_segment(nb_chunks);
trkseg[trkseg.len()].ele;
}
#[test]
#[should_panic]
fn test_index_out_of_bounds_2() {
let nb_chunks = 10;
let trkseg = create_track_segment(nb_chunks);
trkseg[TrackSegmentIndex {
chunk: trkseg.chunks.len(),
pos: 0,
}]
.ele;
}
#[test]
fn test_iter() {
let nb_chunks = 10;
let trkseg = create_track_segment(nb_chunks);
for (i, trkpt) in trkseg.iter().enumerate() {
assert!(std::ptr::eq(&trkseg[i], trkpt));
}
}
#[test]
fn test_iter_nth() {
let nb_chunks = 10;
let trkseg = create_track_segment(nb_chunks);
let tenth = trkseg.iter().nth(10);
assert!(tenth.is_some());
assert!(std::ptr::eq(&trkseg[10], tenth.unwrap()));
}
#[test]
fn test_iter_skip() {
let nb_chunks = 10;
let trkseg = create_track_segment(nb_chunks);
for (i, trkpt) in trkseg.iter().enumerate().skip(5) {
assert!(std::ptr::eq(&trkseg[i], trkpt));
}
} }
} }
+7 -1
View File
@@ -1,7 +1,7 @@
use crate::gpx::LngLat; use crate::gpx::LngLat;
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct TrackPoint { pub struct Trackpoint {
pub coordinates: LngLat, pub coordinates: LngLat,
pub ele: f64, pub ele: f64,
pub time: Option<i64>, pub time: Option<i64>,
@@ -11,3 +11,9 @@ pub struct TrackPoint {
pub power: Option<u16>, pub power: Option<u16>,
// TODO OSM data? or store intervals at a higher level? // TODO OSM data? or store intervals at a higher level?
} }
impl Trackpoint {
pub fn time_diff(&self, other: &Trackpoint) -> Option<i64> {
self.time.zip(other.time).map(|(t1, t2)| t1 - t2)
}
}
-13
View File
@@ -12,16 +12,3 @@ pub struct Waypoint {
pub sym: Option<String>, pub sym: Option<String>,
pub type_: Option<String>, pub type_: Option<String>,
} }
static MAX_CHUNK_SIZE: usize = 128;
#[derive(Debug, Default)]
pub struct WaypointChunk {
pub wpt: Vec<Waypoint>,
}
impl WaypointChunk {
pub fn is_full(&self) -> bool {
self.wpt.len() == MAX_CHUNK_SIZE
}
}
+30 -33
View File
@@ -1,8 +1,8 @@
use std::{collections::HashMap, rc::Rc}; use std::{collections::HashMap, rc::Rc};
use crate::gpx::GPXFile; use uuid::Uuid;
pub type GPXFileId = usize; use crate::gpx::GPXFile;
#[derive(Default)] #[derive(Default)]
pub struct Stack { pub struct Stack {
@@ -18,40 +18,37 @@ impl Stack {
} }
} }
pub fn get_new_file_id(&self) -> GPXFileId { pub fn create_and_push_next<F>(&mut self, f: F)
if let Some(current) = self.current() { where
for id in 0..current.len() { F: FnOnce(&mut StackEntry) -> bool,
if !current.contains_key(&id) { {
return id; let mut next = self.current().map_or_default(|c| c.clone());
} if f(&mut next) {
}
current.len() as GPXFileId
} else {
0
}
}
pub fn update(&mut self, files: &[Rc<GPXFile>], ids: &[GPXFileId]) {
let mut next = match self.current() {
Some(current) => current.clone(),
None => StackEntry::default(),
};
for (file, id) in files.iter().zip(ids) {
next.insert(*id, file.clone());
}
self.push(next);
}
pub fn delete(&mut self, files: &[GPXFileId]) {
if let Some(current) = self.current() {
let mut next = current.clone();
for file in files {
next.remove(file);
}
self.push(next); self.push(next);
} }
} }
// pub fn update(&mut self, files: &[Rc<GPXFile>], ids: &[GPXFileId]) {
// let mut next = match self.current() {
// Some(current) => current.clone(),
// None => StackEntry::default(),
// };
// for (file, id) in files.iter().zip(ids) {
// next.insert(*id, file.clone());
// }
// self.push(next);
// }
// pub fn delete(&mut self, files: &[GPXFileId]) {
// if let Some(current) = self.current() {
// let mut next = current.clone();
// for file in files {
// next.remove(file);
// }
// self.push(next);
// }
// }
pub fn can_undo(&self) -> bool { pub fn can_undo(&self) -> bool {
self.index.is_some() self.index.is_some()
} }
@@ -100,4 +97,4 @@ impl Stack {
} }
} }
pub type StackEntry = HashMap<GPXFileId, Rc<GPXFile>>; pub type StackEntry = HashMap<Uuid, Rc<GPXFile>>;
+87 -63
View File
@@ -1,7 +1,7 @@
use crate::{ use crate::{
algorithms::ramer_douglas_peucker, algorithms::ramer_douglas_peucker,
for_each_window, for_each_window,
gpx::{LngLat, LngLatBounds, TrackPoint, TrackPointChunk}, gpx::{LngLat, LngLatBounds, TrackSegment, Trackpoint},
utils::{distance, slope, speed}, utils::{distance, slope, speed},
}; };
@@ -33,26 +33,25 @@ impl GPXStatistics {
.map(|(d, t)| speed(d, t)) .map(|(d, t)| speed(d, t))
} }
pub fn compute(chunk: &TrackPointChunk) -> Self { pub fn compute(trkseg: &TrackSegment) -> Self {
let mut stats = Self::default(); let mut stats = Self::default();
if chunk.trkpt.is_empty() { if trkseg.len() == 0 {
return stats; return stats;
} }
let mut prev = &chunk.trkpt[0]; let mut prev = &trkseg[0];
for i in 0..chunk.trkpt.len() { for cur in trkseg.iter() {
let cur = &chunk.trkpt[i];
stats.accumulate(prev, cur); stats.accumulate(prev, cur);
prev = cur; prev = cur;
} }
stats.compute_smoothed_speed(chunk); stats.compute_smoothed_speed(trkseg);
stats.compute_smoothed_elevation_gain(chunk); stats.compute_smoothed_elevation_gain(trkseg);
stats stats
} }
fn accumulate(&mut self, prev: &TrackPoint, cur: &TrackPoint) { fn accumulate(&mut self, prev: &Trackpoint, cur: &Trackpoint) {
self.accumulate_distance_and_time(prev, cur); self.accumulate_distance_and_time(prev, cur);
self.update_time_bounds(cur.time); self.update_time_bounds(cur.time);
self.update_bounds(&cur.coordinates); self.update_bounds(&cur.coordinates);
@@ -60,9 +59,9 @@ impl GPXStatistics {
.push(TrackpointStatistics::from_partial_stats(&self)); .push(TrackpointStatistics::from_partial_stats(&self));
} }
fn accumulate_distance_and_time(&mut self, prev: &TrackPoint, cur: &TrackPoint) { fn accumulate_distance_and_time(&mut self, prev: &Trackpoint, cur: &Trackpoint) {
let dist = distance(prev.coordinates, cur.coordinates); let dist = distance(prev.coordinates, cur.coordinates);
let time = prev.time.zip(cur.time).map(|(t1, t2)| t2 - t1); let time = cur.time_diff(prev);
self.total_distance += dist; self.total_distance += dist;
@@ -91,42 +90,37 @@ impl GPXStatistics {
self.bounds.ne.lat = self.bounds.ne.lat.min(coordinates.lat); self.bounds.ne.lat = self.bounds.ne.lat.min(coordinates.lat);
} }
fn compute_smoothed_speed(&mut self, chunk: &TrackPointChunk) { fn compute_smoothed_speed(&mut self, trkseg: &TrackSegment) {
for_each_window!( for_each_window!(
0, trkseg,
chunk.trkpt.len(), trkseg.first_index(),
trkseg.last_index(),
Some(10000), Some(10000),
|i, j| { |i, j| trkseg[i].time_diff(&trkseg[j]),
chunk.trkpt[i]
.time
.zip(chunk.trkpt[j].time)
.map(|(t1, t2)| t2 - t1)
},
|i, left, right| { |i, left, right| {
self.local[i].speed = let i = trkseg.to_flat_index(i);
chunk.trkpt[left] let left = trkseg.to_flat_index(left);
.time let right = trkseg.to_flat_index(right);
.zip(chunk.trkpt[right].time) self.local[i].speed = trkseg[right].time_diff(&trkseg[left]).map(|t| {
.map(|(t1, t2)| {
speed( speed(
self.local[right].total_distance - self.local[left].total_distance, self.local[right].total_distance - self.local[left].total_distance,
t2 - t1, t,
) )
}); });
}, },
); );
} }
fn compute_smoothed_elevation_gain(&mut self, chunk: &TrackPointChunk) { fn compute_smoothed_elevation_gain(&mut self, trkseg: &TrackSegment) {
let simplified = ramer_douglas_peucker( let simplified = ramer_douglas_peucker(
chunk.trkpt.len(), trkseg,
&|i, j, k| { &|i, j, k| {
let x1 = self.local[i].total_distance * 1000.0; let x1 = self.local[trkseg.to_flat_index(i)].total_distance * 1000.0;
let x2 = self.local[j].total_distance * 1000.0; let x2 = self.local[trkseg.to_flat_index(j)].total_distance * 1000.0;
let x3 = self.local[k].total_distance * 1000.0; let x3 = self.local[trkseg.to_flat_index(k)].total_distance * 1000.0;
let y1 = chunk.trkpt[i].ele; let y1 = trkseg[i].ele;
let y2 = chunk.trkpt[j].ele; let y2 = trkseg[j].ele;
let y3 = chunk.trkpt[k].ele; let y3 = trkseg[k].ele;
let dist = ((y2 - y1).powi(2) + (x2 - x1).powi(2)).sqrt(); let dist = ((y2 - y1).powi(2) + (x2 - x1).powi(2)).sqrt();
if dist == 0.0 { if dist == 0.0 {
@@ -145,28 +139,40 @@ impl GPXStatistics {
let mut cumul_ele = 0.0; let mut cumul_ele = 0.0;
let mut current_left = start; let mut current_left = start;
let mut current_right = start; let mut current_right = Some(start);
let mut prev_smoothed_ele = chunk.trkpt[start].ele; let mut prev_smoothed_ele = trkseg[start].ele;
for_each_window!( for_each_window!(
start, trkseg,
end, Some(start),
Some(end),
0.1, 0.1,
|i, j| self.local[j].total_distance - self.local[i].total_distance, |i, j| {
let i = trkseg.to_flat_index(i);
let j = trkseg.to_flat_index(j);
self.local[j].total_distance - self.local[i].total_distance
},
|i, left, right| { |i, left, right| {
for i in current_left..left { while current_left != left {
cumul_ele -= chunk.trkpt[i].ele; cumul_ele -= trkseg[current_left].ele;
current_left = trkseg.next_index(Some(current_left)).unwrap();
} }
for i in current_right..=right { while let Some(current) = current_right {
cumul_ele += chunk.trkpt[i].ele; if current > right {
break;
} }
current_left = left; cumul_ele += trkseg[current].ele;
current_right = right + 1; current_right = trkseg.next_index(current_right);
}
let flat_i = trkseg.to_flat_index(i);
let flat_left = trkseg.to_flat_index(left);
let flat_right = trkseg.to_flat_index(right);
let smoothed_ele: f64 = if i == start || i == end { let smoothed_ele: f64 = if i == start || i == end {
chunk.trkpt[i].ele trkseg[i].ele
} else { } else {
cumul_ele / (right - left + 1) as f64 cumul_ele / (flat_right - flat_left + 1) as f64
}; };
let delta = smoothed_ele - prev_smoothed_ele; let delta = smoothed_ele - prev_smoothed_ele;
@@ -177,18 +183,22 @@ impl GPXStatistics {
} }
if i < end || last { if i < end || last {
self.local[i].elevation_gain = self.elevation_gain; self.local[flat_i].elevation_gain = self.elevation_gain;
self.local[i].elevation_loss = self.elevation_loss; self.local[flat_i].elevation_loss = self.elevation_loss;
} }
prev_smoothed_ele = smoothed_ele; prev_smoothed_ele = smoothed_ele;
}, },
); );
let segment_dist = self.local[end].total_distance - self.local[start].total_distance; let flat_start = trkseg.to_flat_index(start);
let segment_ele = chunk.trkpt[end].ele - chunk.trkpt[start].ele; let flat_end = trkseg.to_flat_index(end);
let segment_dist =
self.local[flat_end].total_distance - self.local[flat_start].total_distance;
let segment_ele = trkseg[end].ele - trkseg[start].ele;
let segment_slope = slope(segment_ele, segment_dist); let segment_slope = slope(segment_ele, segment_dist);
for k in start..(end + last as usize) { for k in flat_start..(flat_end + last as usize) {
self.local[k].slope_segment = SlopeSegment { self.local[k].slope_segment = SlopeSegment {
slope: segment_slope, slope: segment_slope,
distance: segment_dist, distance: segment_dist,
@@ -197,14 +207,23 @@ impl GPXStatistics {
} }
for_each_window!( for_each_window!(
0, trkseg,
chunk.trkpt.len(), trkseg.first_index(),
trkseg.last_index(),
0.05, 0.05,
|i, j| self.local[j].total_distance - self.local[i].total_distance, |i, j| {
let i = trkseg.to_flat_index(i);
let j = trkseg.to_flat_index(j);
self.local[j].total_distance - self.local[i].total_distance
},
|i, left, right| { |i, left, right| {
let dist = self.local[right].total_distance - self.local[left].total_distance; let flat_i = trkseg.to_flat_index(i);
let ele = chunk.trkpt[right].ele - chunk.trkpt[left].ele; let flat_left = trkseg.to_flat_index(left);
self.local[i].slope = slope(ele, dist); let flat_right = trkseg.to_flat_index(right);
let dist =
self.local[flat_right].total_distance - self.local[flat_left].total_distance;
let ele = trkseg[right].ele - trkseg[left].ele;
self.local[flat_i].slope = slope(ele, dist);
}, },
); );
} }
@@ -254,15 +273,20 @@ mod tests {
use super::*; use super::*;
#[test] #[test]
fn test_parse_simple() { fn test_compute_smoothed_speed() {
let mut f = File::open("data/with_time.gpx").unwrap(); let mut f = File::open("data/with_time.gpx").unwrap();
let mut data = String::new(); let mut data = String::new();
let _ = f.read_to_string(&mut data); let _ = f.read_to_string(&mut data);
let gpx = parse(data.as_bytes()).unwrap(); let gpx = parse(data.as_bytes()).unwrap();
println!( let trkseg = &gpx.trk[0].trkseg[0];
"{:?}", let stats = GPXStatistics::compute(trkseg);
GPXStatistics::compute(&gpx.trk[0].trkseg[0].chunks[0]) assert_eq!(stats.local.len(), trkseg.len());
); for trkpt_stats in stats.local.iter() {
assert!(trkpt_stats.speed.is_some());
let speed = trkpt_stats.speed.unwrap();
assert_ne!(speed, f64::NAN);
assert!((speed - 20.0).abs() < 0.1);
}
} }
} }
+10
View File
@@ -0,0 +1,10 @@
class Engine {
_engine: Writable<any>;
constructor() {
this._engine = null;
import('gpx-rs').then((wasm) => {
this._engine = new wasm.Controller();
});
}
}
@@ -37,14 +37,6 @@
); );
onMount(async () => { onMount(async () => {
if (browser) {
const wasm = await import('gpx-rs');
const controller = new wasm.Controller();
console.log(controller);
controller.create_file();
}
settings.connectToDatabase(db); settings.connectToDatabase(db);
fileStateCollection.connectToDatabase(db).then(() => { fileStateCollection.connectToDatabase(db).then(() => {
let files: string[] = JSON.parse(page.url.searchParams.get('files') || '[]'); let files: string[] = JSON.parse(page.url.searchParams.get('files') || '[]');