mirror of
https://github.com/gpxstudio/gpx.studio.git
synced 2026-10-03 12:28:22 +00:00
fixes
This commit is contained in:
@@ -1,13 +1,15 @@
|
|||||||
use crate::gpx::{TrackSegment, TrackSegmentIndex};
|
use crate::{
|
||||||
|
gpx::{TrackSegment, TrackSegmentIndex},
|
||||||
|
utils::crossarc,
|
||||||
|
};
|
||||||
|
|
||||||
pub fn ramer_douglas_peucker<F, T>(
|
pub fn ramer_douglas_peucker<F>(
|
||||||
trkseg: &TrackSegment,
|
trkseg: &TrackSegment,
|
||||||
distance: &F,
|
mapping: &F,
|
||||||
epsilon: T,
|
epsilon: f64,
|
||||||
) -> Vec<TrackSegmentIndex>
|
) -> Vec<TrackSegmentIndex>
|
||||||
where
|
where
|
||||||
F: Fn(TrackSegmentIndex, TrackSegmentIndex, TrackSegmentIndex) -> T,
|
F: Fn(TrackSegmentIndex) -> (f64, f64),
|
||||||
T: Default + PartialOrd + Copy,
|
|
||||||
{
|
{
|
||||||
match trkseg.len() {
|
match trkseg.len() {
|
||||||
0 => vec![],
|
0 => vec![],
|
||||||
@@ -18,35 +20,37 @@ where
|
|||||||
let last = trkseg.last_index().unwrap();
|
let last = trkseg.last_index().unwrap();
|
||||||
|
|
||||||
let mut indices = vec![first];
|
let mut indices = vec![first];
|
||||||
ramer_douglas_peucker_helper(trkseg, first, last, distance, epsilon, &mut indices);
|
ramer_douglas_peucker_helper(trkseg, first, last, mapping, epsilon, &mut indices);
|
||||||
indices.push(last);
|
indices.push(last);
|
||||||
indices
|
indices
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ramer_douglas_peucker_helper<F, T>(
|
fn ramer_douglas_peucker_helper<F>(
|
||||||
trkseg: &TrackSegment,
|
trkseg: &TrackSegment,
|
||||||
start: TrackSegmentIndex,
|
start: TrackSegmentIndex,
|
||||||
end: TrackSegmentIndex,
|
end: TrackSegmentIndex,
|
||||||
distance: &F,
|
mapping: &F,
|
||||||
epsilon: T,
|
epsilon: f64,
|
||||||
indices: &mut Vec<TrackSegmentIndex>,
|
indices: &mut Vec<TrackSegmentIndex>,
|
||||||
) where
|
) where
|
||||||
F: Fn(TrackSegmentIndex, TrackSegmentIndex, TrackSegmentIndex) -> T,
|
F: Fn(TrackSegmentIndex) -> (f64, f64),
|
||||||
T: Default + PartialOrd + Copy,
|
|
||||||
{
|
{
|
||||||
let mut max_idx = None;
|
let mut max_idx = None;
|
||||||
let mut max_dist = T::default();
|
let mut max_dist = 0.0;
|
||||||
|
|
||||||
|
let start_pt = mapping(start);
|
||||||
|
let end_pt = mapping(end);
|
||||||
|
|
||||||
let mut cur = trkseg.next_index(Some(start));
|
let mut cur = trkseg.next_index(Some(start));
|
||||||
|
|
||||||
while let Some(idx) = cur {
|
while let Some(idx) = cur {
|
||||||
if idx == end {
|
if idx == end {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
let dist = distance(start, end, idx);
|
let pt = mapping(idx);
|
||||||
|
let dist = crossarc(start_pt.0, start_pt.1, end_pt.0, end_pt.1, pt.0, pt.1);
|
||||||
if dist > max_dist {
|
if dist > max_dist {
|
||||||
max_idx = Some(idx);
|
max_idx = Some(idx);
|
||||||
max_dist = dist;
|
max_dist = dist;
|
||||||
@@ -57,9 +61,9 @@ fn ramer_douglas_peucker_helper<F, T>(
|
|||||||
|
|
||||||
if let Some(idx) = max_idx {
|
if let Some(idx) = max_idx {
|
||||||
if max_dist > epsilon {
|
if max_dist > epsilon {
|
||||||
ramer_douglas_peucker_helper(trkseg, start, idx, distance, epsilon, indices);
|
ramer_douglas_peucker_helper(trkseg, start, idx, mapping, epsilon, indices);
|
||||||
indices.push(idx);
|
indices.push(idx);
|
||||||
ramer_douglas_peucker_helper(trkseg, idx, end, distance, epsilon, indices);
|
ramer_douglas_peucker_helper(trkseg, idx, end, mapping, epsilon, indices);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,6 +45,9 @@ macro_rules! for_each_window {
|
|||||||
let $r = window_end;
|
let $r = window_end;
|
||||||
$body
|
$body
|
||||||
// go next
|
// go next
|
||||||
|
if window_center == $right {
|
||||||
|
break;
|
||||||
|
}
|
||||||
window_center = $trkseg.next_index(window_center);
|
window_center = $trkseg.next_index(window_center);
|
||||||
}
|
}
|
||||||
}};
|
}};
|
||||||
|
|||||||
+12
-14
@@ -60,6 +60,7 @@ impl TrackSegment {
|
|||||||
let mut next = cur.map_or_default(|idx| TrackSegmentIndex {
|
let mut next = cur.map_or_default(|idx| TrackSegmentIndex {
|
||||||
chunk: idx.chunk,
|
chunk: idx.chunk,
|
||||||
pos: idx.pos + 1,
|
pos: idx.pos + 1,
|
||||||
|
flat: idx.flat + 1,
|
||||||
});
|
});
|
||||||
loop {
|
loop {
|
||||||
if next.chunk >= self.chunks.len() {
|
if next.chunk >= self.chunks.len() {
|
||||||
@@ -78,6 +79,7 @@ impl TrackSegment {
|
|||||||
let mut prev = cur.unwrap_or(TrackSegmentIndex {
|
let mut prev = cur.unwrap_or(TrackSegmentIndex {
|
||||||
chunk: self.chunks.len(),
|
chunk: self.chunks.len(),
|
||||||
pos: 0,
|
pos: 0,
|
||||||
|
flat: self.cumul_length.last().copied().unwrap_or_default(),
|
||||||
});
|
});
|
||||||
loop {
|
loop {
|
||||||
if prev.pos == 0 {
|
if prev.pos == 0 {
|
||||||
@@ -86,22 +88,15 @@ impl TrackSegment {
|
|||||||
}
|
}
|
||||||
prev.chunk -= 1;
|
prev.chunk -= 1;
|
||||||
prev.pos = self.chunks[prev.chunk].trkpt.len();
|
prev.pos = self.chunks[prev.chunk].trkpt.len();
|
||||||
|
prev.flat -= 1;
|
||||||
} else {
|
} else {
|
||||||
prev.pos -= 1;
|
prev.pos -= 1;
|
||||||
|
prev.flat -= 1;
|
||||||
return Some(prev);
|
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> {
|
fn locate(&self, idx: usize) -> Option<TrackSegmentIndex> {
|
||||||
let chunk = self.cumul_length.partition_point(|l| idx >= *l);
|
let chunk = self.cumul_length.partition_point(|l| idx >= *l);
|
||||||
if chunk >= self.chunks.len() {
|
if chunk >= self.chunks.len() {
|
||||||
@@ -115,7 +110,11 @@ impl TrackSegment {
|
|||||||
if pos >= self.chunks[chunk].trkpt.len() {
|
if pos >= self.chunks[chunk].trkpt.len() {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(TrackSegmentIndex { chunk, pos })
|
Some(TrackSegmentIndex {
|
||||||
|
chunk,
|
||||||
|
pos,
|
||||||
|
flat: idx,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -140,6 +139,7 @@ impl Index<usize> for TrackSegment {
|
|||||||
pub struct TrackSegmentIndex {
|
pub struct TrackSegmentIndex {
|
||||||
pub chunk: usize,
|
pub chunk: usize,
|
||||||
pub pos: usize,
|
pub pos: usize,
|
||||||
|
pub flat: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@@ -166,10 +166,7 @@ impl<'a> Iterator for TrackSegmentIterator<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn nth(&mut self, n: usize) -> Option<Self::Item> {
|
fn nth(&mut self, n: usize) -> Option<Self::Item> {
|
||||||
let idx = self
|
let idx = self.idx.map_or_default(|idx| idx.flat) + n;
|
||||||
.idx
|
|
||||||
.map_or_default(|idx| self.trkseg.to_flat_index(idx))
|
|
||||||
+ n;
|
|
||||||
self.idx = self.trkseg.locate(idx);
|
self.idx = self.trkseg.locate(idx);
|
||||||
self.idx.map(|idx| &self.trkseg[idx])
|
self.idx.map(|idx| &self.trkseg[idx])
|
||||||
}
|
}
|
||||||
@@ -242,6 +239,7 @@ mod tests {
|
|||||||
trkseg[TrackSegmentIndex {
|
trkseg[TrackSegmentIndex {
|
||||||
chunk: trkseg.chunks.len(),
|
chunk: trkseg.chunks.len(),
|
||||||
pos: 0,
|
pos: 0,
|
||||||
|
flat: 0,
|
||||||
}]
|
}]
|
||||||
.ele;
|
.ele;
|
||||||
}
|
}
|
||||||
|
|||||||
+101
-112
@@ -1,19 +1,19 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
algorithm::ramer_douglas_peucker,
|
algorithm::ramer_douglas_peucker,
|
||||||
for_each_window,
|
for_each_window,
|
||||||
gpx::{LngLat, LngLatBounds, TrackSegment, Trackpoint},
|
gpx::{LngLat, LngLatBounds, TrackSegment, TrackSegmentIndex, Trackpoint},
|
||||||
statistics::sum_options,
|
statistics::sum_options,
|
||||||
utils::{distance, slope, speed},
|
utils::{distance, slope, speed, time_diff},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
pub struct GPXStatistics {
|
pub struct Statistics {
|
||||||
pub global: GlobalStatistics,
|
pub global: GlobalStatistics,
|
||||||
pub local: Vec<TrackpointStatistics>,
|
pub local: Vec<TrackpointStatistics>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GPXStatistics {
|
impl Statistics {
|
||||||
pub fn total_time(&self) -> Option<i64> {
|
pub fn total_time(&self) -> Option<i32> {
|
||||||
self.global.total_time()
|
self.global.total_time()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,7 +38,8 @@ impl GPXStatistics {
|
|||||||
}
|
}
|
||||||
|
|
||||||
stats.compute_smoothed_speed(trkseg);
|
stats.compute_smoothed_speed(trkseg);
|
||||||
stats.compute_smoothed_elevation_gain(trkseg);
|
stats.compute_smoothed_elevation_segments(trkseg);
|
||||||
|
stats.compute_smoothed_slope(trkseg);
|
||||||
|
|
||||||
stats
|
stats
|
||||||
}
|
}
|
||||||
@@ -53,7 +54,7 @@ impl GPXStatistics {
|
|||||||
|
|
||||||
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 = cur.time_diff(prev);
|
let time = time_diff(&cur.time, &prev.time);
|
||||||
|
|
||||||
self.global.total_distance += dist;
|
self.global.total_distance += dist;
|
||||||
|
|
||||||
@@ -91,144 +92,132 @@ impl GPXStatistics {
|
|||||||
trkseg.first_index(),
|
trkseg.first_index(),
|
||||||
trkseg.last_index(),
|
trkseg.last_index(),
|
||||||
Some(10000),
|
Some(10000),
|
||||||
|i, j| trkseg[i].time_diff(&trkseg[j]),
|
|i, j| time_diff(&trkseg[i].time, &trkseg[j].time),
|
||||||
|i, left, right| {
|
|i, left, right| {
|
||||||
let i = trkseg.to_flat_index(i);
|
self.local[i.flat].speed =
|
||||||
let left = trkseg.to_flat_index(left);
|
time_diff(&trkseg[right].time, &trkseg[left].time).map(|t| {
|
||||||
let right = trkseg.to_flat_index(right);
|
speed(
|
||||||
self.local[i].speed = trkseg[right].time_diff(&trkseg[left]).map(|t| {
|
self.local[right.flat].total_distance
|
||||||
speed(
|
- self.local[left.flat].total_distance,
|
||||||
self.local[right].total_distance - self.local[left].total_distance,
|
t,
|
||||||
t,
|
)
|
||||||
)
|
});
|
||||||
});
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compute_smoothed_elevation_gain(&mut self, trkseg: &TrackSegment) {
|
fn compute_smoothed_elevation_segments(&mut self, trkseg: &TrackSegment) {
|
||||||
let simplified = ramer_douglas_peucker(
|
let simplified = self.get_elevation_extremas(trkseg);
|
||||||
trkseg,
|
|
||||||
&|i, j, k| {
|
|
||||||
let x1 = self.local[trkseg.to_flat_index(i)].total_distance * 1000.0;
|
|
||||||
let x2 = self.local[trkseg.to_flat_index(j)].total_distance * 1000.0;
|
|
||||||
let x3 = self.local[trkseg.to_flat_index(k)].total_distance * 1000.0;
|
|
||||||
let y1 = trkseg[i].ele;
|
|
||||||
let y2 = trkseg[j].ele;
|
|
||||||
let y3 = trkseg[k].ele;
|
|
||||||
|
|
||||||
let dist = ((y2 - y1).powi(2) + (x2 - x1).powi(2)).sqrt();
|
|
||||||
if dist == 0.0 {
|
|
||||||
((x3 - x1).powi(2) + (y3 - y1).powi(2)).sqrt()
|
|
||||||
} else {
|
|
||||||
((y2 - y1) * x3 - (x2 - x1) * y3 + x2 * y1 - y2 * x1).abs() / dist
|
|
||||||
}
|
|
||||||
},
|
|
||||||
20.0,
|
|
||||||
);
|
|
||||||
|
|
||||||
for i in 0..(simplified.len() - 1) {
|
for i in 0..(simplified.len() - 1) {
|
||||||
let start = simplified[i];
|
let start = simplified[i];
|
||||||
let end = simplified[i + 1];
|
let end = simplified[i + 1];
|
||||||
let last = i + 1 == simplified.len() - 1;
|
let last = i + 1 == simplified.len() - 1;
|
||||||
|
|
||||||
let mut cumul_ele = 0.0;
|
self.compute_smoothed_elevation_gain(trkseg, start, end, last);
|
||||||
let mut current_left = start;
|
|
||||||
let mut current_right = Some(start);
|
|
||||||
let mut prev_smoothed_ele = trkseg[start].ele;
|
|
||||||
|
|
||||||
for_each_window!(
|
|
||||||
trkseg,
|
|
||||||
Some(start),
|
|
||||||
Some(end),
|
|
||||||
0.1,
|
|
||||||
|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| {
|
|
||||||
while current_left != left {
|
|
||||||
cumul_ele -= trkseg[current_left].ele;
|
|
||||||
current_left = trkseg.next_index(Some(current_left)).unwrap();
|
|
||||||
}
|
|
||||||
while let Some(current) = current_right {
|
|
||||||
if current > right {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
cumul_ele += trkseg[current].ele;
|
|
||||||
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 {
|
|
||||||
trkseg[i].ele
|
|
||||||
} else {
|
|
||||||
cumul_ele / (flat_right - flat_left + 1) as f64
|
|
||||||
};
|
|
||||||
|
|
||||||
let delta = smoothed_ele - prev_smoothed_ele;
|
|
||||||
if delta > 0.0 {
|
|
||||||
self.global.elevation_gain += delta;
|
|
||||||
} else if delta < 0.0 {
|
|
||||||
self.global.elevation_loss -= delta;
|
|
||||||
}
|
|
||||||
|
|
||||||
if i < end || last {
|
|
||||||
self.local[flat_i].elevation_gain = self.global.elevation_gain;
|
|
||||||
self.local[flat_i].elevation_loss = self.global.elevation_loss;
|
|
||||||
}
|
|
||||||
|
|
||||||
prev_smoothed_ele = smoothed_ele;
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
let flat_start = trkseg.to_flat_index(start);
|
|
||||||
let flat_end = trkseg.to_flat_index(end);
|
|
||||||
|
|
||||||
let segment_dist =
|
let segment_dist =
|
||||||
self.local[flat_end].total_distance - self.local[flat_start].total_distance;
|
self.local[end.flat].total_distance - self.local[start.flat].total_distance;
|
||||||
let segment_ele = trkseg[end].ele - trkseg[start].ele;
|
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 flat_start..(flat_end + last as usize) {
|
for k in start.flat..(end.flat + 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,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compute_smoothed_elevation_gain(
|
||||||
|
&mut self,
|
||||||
|
trkseg: &TrackSegment,
|
||||||
|
start: TrackSegmentIndex,
|
||||||
|
end: TrackSegmentIndex,
|
||||||
|
last: bool,
|
||||||
|
) {
|
||||||
|
let mut cumul_ele = 0.0;
|
||||||
|
let mut current_left = start;
|
||||||
|
let mut current_right = Some(start);
|
||||||
|
let mut prev_smoothed_ele = trkseg[start].ele;
|
||||||
|
|
||||||
|
for_each_window!(
|
||||||
|
trkseg,
|
||||||
|
Some(start),
|
||||||
|
Some(end),
|
||||||
|
0.1,
|
||||||
|
|i, j| self.local[j.flat].total_distance - self.local[i.flat].total_distance,
|
||||||
|
|i, left, right| {
|
||||||
|
while current_left != left {
|
||||||
|
cumul_ele -= trkseg[current_left].ele;
|
||||||
|
current_left = trkseg.next_index(Some(current_left)).unwrap();
|
||||||
|
}
|
||||||
|
while let Some(current) = current_right {
|
||||||
|
if current > right {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
cumul_ele += trkseg[current].ele;
|
||||||
|
current_right = trkseg.next_index(current_right);
|
||||||
|
}
|
||||||
|
|
||||||
|
let smoothed_ele = if i == start || i == end {
|
||||||
|
trkseg[i].ele
|
||||||
|
} else {
|
||||||
|
cumul_ele / (right.flat - left.flat + 1) as f64
|
||||||
|
};
|
||||||
|
|
||||||
|
let delta = smoothed_ele - prev_smoothed_ele;
|
||||||
|
if delta > 0.0 {
|
||||||
|
self.global.elevation_gain += delta;
|
||||||
|
} else if delta < 0.0 {
|
||||||
|
self.global.elevation_loss -= delta;
|
||||||
|
}
|
||||||
|
|
||||||
|
if i < end || last {
|
||||||
|
self.local[i.flat].elevation_gain = self.global.elevation_gain;
|
||||||
|
self.local[i.flat].elevation_loss = self.global.elevation_loss;
|
||||||
|
}
|
||||||
|
|
||||||
|
prev_smoothed_ele = smoothed_ele;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compute_smoothed_slope(&mut self, trkseg: &TrackSegment) {
|
||||||
for_each_window!(
|
for_each_window!(
|
||||||
trkseg,
|
trkseg,
|
||||||
trkseg.first_index(),
|
trkseg.first_index(),
|
||||||
trkseg.last_index(),
|
trkseg.last_index(),
|
||||||
0.05,
|
0.05,
|
||||||
|i, j| {
|
|i, j| self.local[j.flat].total_distance - self.local[i.flat].total_distance,
|
||||||
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 flat_i = trkseg.to_flat_index(i);
|
|
||||||
let flat_left = trkseg.to_flat_index(left);
|
|
||||||
let flat_right = trkseg.to_flat_index(right);
|
|
||||||
let dist =
|
let dist =
|
||||||
self.local[flat_right].total_distance - self.local[flat_left].total_distance;
|
self.local[right.flat].total_distance - self.local[left.flat].total_distance;
|
||||||
let ele = trkseg[right].ele - trkseg[left].ele;
|
let ele = trkseg[right].ele - trkseg[left].ele;
|
||||||
self.local[flat_i].slope = slope(ele, dist);
|
self.local[i.flat].slope = slope(ele, dist);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_elevation_extremas(&self, trkseg: &TrackSegment) -> Vec<TrackSegmentIndex> {
|
||||||
|
ramer_douglas_peucker(
|
||||||
|
trkseg,
|
||||||
|
&|idx| {
|
||||||
|
(
|
||||||
|
self.local[idx.flat].total_distance * 1000.0,
|
||||||
|
trkseg[idx].ele,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
20.0,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
pub struct GlobalStatistics {
|
pub struct GlobalStatistics {
|
||||||
pub total_distance: f64,
|
pub total_distance: f64,
|
||||||
pub moving_distance: Option<f64>,
|
pub moving_distance: Option<f64>,
|
||||||
pub moving_time: Option<i64>,
|
pub moving_time: Option<i32>,
|
||||||
pub elevation_gain: f64,
|
pub elevation_gain: f64,
|
||||||
pub elevation_loss: f64,
|
pub elevation_loss: f64,
|
||||||
pub start_time: Option<i64>,
|
pub start_time: Option<i64>,
|
||||||
@@ -237,8 +226,8 @@ pub struct GlobalStatistics {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl GlobalStatistics {
|
impl GlobalStatistics {
|
||||||
pub fn total_time(&self) -> Option<i64> {
|
pub fn total_time(&self) -> Option<i32> {
|
||||||
self.start_time.zip(self.end_time).map(|(t1, t2)| t2 - t1)
|
time_diff(&self.start_time, &self.end_time)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn total_speed(&self) -> Option<f64> {
|
pub fn total_speed(&self) -> Option<f64> {
|
||||||
@@ -265,8 +254,8 @@ impl GlobalStatistics {
|
|||||||
pub struct TrackpointStatistics {
|
pub struct TrackpointStatistics {
|
||||||
pub total_distance: f64,
|
pub total_distance: f64,
|
||||||
pub moving_distance: Option<f64>,
|
pub moving_distance: Option<f64>,
|
||||||
pub total_time: Option<i64>,
|
pub total_time: Option<i32>,
|
||||||
pub moving_time: Option<i64>,
|
pub moving_time: Option<i32>,
|
||||||
pub speed: Option<f64>,
|
pub speed: Option<f64>,
|
||||||
pub elevation_gain: f64,
|
pub elevation_gain: f64,
|
||||||
pub elevation_loss: f64,
|
pub elevation_loss: f64,
|
||||||
@@ -275,7 +264,7 @@ pub struct TrackpointStatistics {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl TrackpointStatistics {
|
impl TrackpointStatistics {
|
||||||
fn from_partial_stats(stats: &GPXStatistics) -> Self {
|
fn from_partial_stats(stats: &Statistics) -> Self {
|
||||||
Self {
|
Self {
|
||||||
total_distance: stats.global.total_distance,
|
total_distance: stats.global.total_distance,
|
||||||
moving_distance: stats.global.moving_distance,
|
moving_distance: stats.global.moving_distance,
|
||||||
@@ -313,7 +302,7 @@ mod tests {
|
|||||||
let gpx = parse(data.as_bytes()).unwrap();
|
let gpx = parse(data.as_bytes()).unwrap();
|
||||||
|
|
||||||
let trkseg = &gpx.trk[0].trkseg[0];
|
let trkseg = &gpx.trk[0].trkseg[0];
|
||||||
let stats = GPXStatistics::compute(trkseg);
|
let stats = Statistics::compute(trkseg);
|
||||||
assert_eq!(stats.local.len(), trkseg.len());
|
assert_eq!(stats.local.len(), trkseg.len());
|
||||||
for trkpt_stats in stats.local.iter() {
|
for trkpt_stats in stats.local.iter() {
|
||||||
assert!(trkpt_stats.speed.is_some());
|
assert!(trkpt_stats.speed.is_some());
|
||||||
|
|||||||
Reference in New Issue
Block a user