elevation gain computation hybrid between ramer-douglas-peucker and smoothing

This commit is contained in:
vcoppe
2025-11-21 20:20:42 +01:00
parent 429212ef23
commit 46fcdb4bb2

View File

@@ -973,7 +973,7 @@ export class TrackSegment extends GPXTreeLeaf {
_elevationComputation(statistics: GPXStatistics) { _elevationComputation(statistics: GPXStatistics) {
let simplified = ramerDouglasPeucker( let simplified = ramerDouglasPeucker(
this.trkpt, this.trkpt,
5, 20,
getElevationDistanceFunction(statistics) getElevationDistanceFunction(statistics)
); );
@@ -981,59 +981,67 @@ export class TrackSegment extends GPXTreeLeaf {
let start = simplified[i].point._data.index; let start = simplified[i].point._data.index;
let end = simplified[i + 1].point._data.index; let end = simplified[i + 1].point._data.index;
const ele = this.trkpt[end].ele - this.trkpt[start].ele || 0; let cumulEle = 0;
const dist = let currentStart = start;
statistics.local.distance.total[end] - statistics.local.distance.total[start]; let currentEnd = start;
let smoothedEle = distanceWindowSmoothing(start, end + 1, statistics, 0.1, (s, e) => {
for (let i = currentStart; i < s; i++) {
cumulEle -= this.trkpt[i].ele ?? 0;
}
for (let i = currentEnd; i <= e; i++) {
cumulEle += this.trkpt[i].ele ?? 0;
}
currentStart = s;
currentEnd = e + 1;
return cumulEle / (e - s + 1);
});
smoothedEle[0] = this.trkpt[start].ele ?? 0;
smoothedEle[smoothedEle.length - 1] = this.trkpt[end].ele ?? 0;
for (let j = start; j < end + (i + 1 == simplified.length - 1 ? 1 : 0); j++) { for (let j = start; j < end; j++) {
const localDist = statistics.local.elevation.gain.push(statistics.global.elevation.gain);
statistics.local.distance.total[j] - statistics.local.distance.total[start]; statistics.local.elevation.loss.push(statistics.global.elevation.loss);
const localEle = dist > 0 ? (localDist / dist) * ele : 0;
statistics.local.elevation.gain.push(
statistics.global.elevation.gain + (localEle > 0 ? localEle : 0)
);
statistics.local.elevation.loss.push(
statistics.global.elevation.loss + (localEle < 0 ? -localEle : 0)
);
}
if (ele > 0) { const ele = smoothedEle[j - start + 1] - smoothedEle[j - start];
statistics.global.elevation.gain += ele; if (ele > 0) {
} else if (ele < 0) { statistics.global.elevation.gain += ele;
statistics.global.elevation.loss -= ele; } else if (ele < 0) {
statistics.global.elevation.loss -= ele;
}
} }
} }
statistics.local.elevation.gain.push(statistics.global.elevation.gain);
statistics.local.elevation.loss.push(statistics.global.elevation.loss);
let slope = []; let slope = [];
let length = []; let length = [];
for (let a = 0; a < simplified.length - 1; ) { for (let i = 0; i < simplified.length - 1; i++) {
let b = a + 1; let start = simplified[i].point._data.index;
while (b < simplified.length - 1 && simplified[b].distance < 20) { let end = simplified[i + 1].point._data.index;
b++;
}
let start = simplified[a].point._data.index;
let end = simplified[b].point._data.index;
let dist = let dist =
statistics.local.distance.total[end] - statistics.local.distance.total[start]; statistics.local.distance.total[end] - statistics.local.distance.total[start];
let ele = (simplified[b].point.ele ?? 0) - (simplified[a].point.ele ?? 0); let ele = (simplified[i + 1].point.ele ?? 0) - (simplified[i].point.ele ?? 0);
for (let j = start; j < end + (b === simplified.length - 1 ? 1 : 0); j++) { for (let j = start; j < end + (i + 1 === simplified.length - 1 ? 1 : 0); j++) {
slope.push((0.1 * ele) / dist); slope.push((0.1 * ele) / dist);
length.push(dist); length.push(dist);
} }
a = b;
} }
statistics.local.slope.segment = slope; statistics.local.slope.segment = slope;
statistics.local.slope.length = length; statistics.local.slope.length = length;
statistics.local.slope.at = distanceWindowSmoothing(statistics, 0.05, (start, end) => { statistics.local.slope.at = distanceWindowSmoothing(
const ele = this.trkpt[end].ele - this.trkpt[start].ele || 0; 0,
const dist = this.trkpt.length,
statistics.local.distance.total[end] - statistics.local.distance.total[start]; statistics,
return dist > 0 ? (0.1 * ele) / dist : 0; 0.05,
}); (start, end) => {
const ele = this.trkpt[end].ele - this.trkpt[start].ele || 0;
const dist =
statistics.local.distance.total[end] - statistics.local.distance.total[start];
return dist > 0 ? (0.1 * ele) / dist : 0;
}
);
} }
getNumberOfTrackPoints(): number { getNumberOfTrackPoints(): number {
@@ -1938,36 +1946,39 @@ export function getElevationDistanceFunction(statistics: GPXStatistics) {
} }
function windowSmoothing( function windowSmoothing(
length: number, left: number,
right: number,
distance: (index1: number, index2: number) => number, distance: (index1: number, index2: number) => number,
window: number, window: number,
compute: (start: number, end: number) => number compute: (start: number, end: number) => number
): number[] { ): number[] {
let result = []; let result = [];
let start = 0, let start = left;
end = 0; for (var i = left; i < right; i++) {
for (var i = 0; i < length; i++) {
while (start + 1 < i && distance(start, i) > window) { while (start + 1 < i && distance(start, i) > window) {
start++; start++;
} }
end = Math.min(i + 2, length); let end = Math.min(i + 2, right);
while (end < length && distance(i, end) <= window) { while (end < right && distance(i, end) <= window) {
end++; end++;
} }
result[i] = compute(start, end - 1); result.push(compute(start, end - 1));
} }
return result; return result;
} }
function distanceWindowSmoothing( function distanceWindowSmoothing(
left: number,
right: number,
statistics: GPXStatistics, statistics: GPXStatistics,
window: number, window: number,
compute: (start: number, end: number) => number compute: (start: number, end: number) => number
): number[] { ): number[] {
return windowSmoothing( return windowSmoothing(
statistics.local.points.length, left,
right,
(index1, index2) => (index1, index2) =>
statistics.local.distance.total[index2] - statistics.local.distance.total[index1], statistics.local.distance.total[index2] - statistics.local.distance.total[index1],
window, window,
@@ -1981,6 +1992,7 @@ function timeWindowSmoothing(
compute: (start: number, end: number) => number compute: (start: number, end: number) => number
): number[] { ): number[] {
return windowSmoothing( return windowSmoothing(
0,
points.length, points.length,
(index1, index2) => (index1, index2) =>
points[index2].time?.getTime() - points[index1].time?.getTime() || 2 * window, points[index2].time?.getTime() - points[index1].time?.getTime() || 2 * window,