simplify computations

This commit is contained in:
vcoppe
2025-11-16 13:04:47 +01:00
parent 36b66100f9
commit b6f96d9f4d

View File

@@ -818,9 +818,6 @@ export class TrackSegment extends GPXTreeLeaf {
statistics.local.points = this.trkpt.map((point) => point); statistics.local.points = this.trkpt.map((point) => point);
statistics.local.elevation.smoothed = this._computeSmoothedElevation();
statistics.local.slope.at = this._computeSlope();
const points = this.trkpt; const points = this.trkpt;
for (let i = 0; i < points.length; i++) { for (let i = 0; i < points.length; i++) {
points[i]._data['index'] = i; points[i]._data['index'] = i;
@@ -961,55 +958,18 @@ export class TrackSegment extends GPXTreeLeaf {
? statistics.global.distance.moving / (statistics.global.time.moving / 3600) ? statistics.global.distance.moving / (statistics.global.time.moving / 3600)
: 0; : 0;
statistics.local.speed = timeWindowSmoothing( statistics.local.speed = timeWindowSmoothing(points, 10000, (start, end) =>
points, points[start].time && points[end].time
10000, ? (3600 *
(index) => (statistics.local.distance.total[end] -
index > 0 statistics.local.distance.total[start])) /
? distance(points[index - 1].getCoordinates(), points[index].getCoordinates()) Math.max((points[end].time.getTime() - points[start].time.getTime()) / 1000, 1)
: 0, : undefined
(accumulated, start, end) =>
points[start].time &&
points[end].time &&
points[end].time.getTime() - points[start].time.getTime() > 0
? (3600 * accumulated) /
(points[end].time.getTime() - points[start].time.getTime())
: undefined
); );
return statistics; return statistics;
} }
_computeSmoothedElevation(): number[] {
const points = this.trkpt;
let smoothed = distanceWindowSmoothing(
points,
100,
(index) => points[index].ele ?? 0,
(accumulated, start, end) => accumulated / (end - start + 1)
);
if (points.length > 0) {
smoothed[0] = points[0].ele ?? 0;
smoothed[points.length - 1] = points[points.length - 1].ele ?? 0;
}
return smoothed;
}
_computeSlope(): number[] {
const points = this.trkpt;
return distanceWindowSmoothingWithDistanceAccumulator(
points,
50,
(accumulated, start, end) =>
(100 * ((points[end].ele ?? 0) - (points[start].ele ?? 0))) /
(accumulated > 0 ? accumulated : 1)
);
}
_elevationComputation(statistics: GPXStatistics) { _elevationComputation(statistics: GPXStatistics) {
let simplified = ramerDouglasPeucker( let simplified = ramerDouglasPeucker(
this.trkpt, this.trkpt,
@@ -1068,6 +1028,12 @@ export class TrackSegment extends GPXTreeLeaf {
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) => {
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 {
@@ -1314,8 +1280,14 @@ export class TrackSegment extends GPXTreeLeaf {
lastPoint: TrackPoint | undefined lastPoint: TrackPoint | undefined
) { ) {
let og = getOriginal(this); // Read as much as possible from the original object because it is faster let og = getOriginal(this); // Read as much as possible from the original object because it is faster
let slope = og._computeSlope(); let statistics = og._computeStatistics();
let trkpt = withArtificialTimestamps(og.trkpt, totalTime, lastPoint, startTime, slope); let trkpt = withArtificialTimestamps(
og.trkpt,
totalTime,
lastPoint,
startTime,
statistics.local.slope.at
);
this.trkpt = freeze(trkpt); // Pre-freeze the array, faster as well this.trkpt = freeze(trkpt); // Pre-freeze the array, faster as well
} }
@@ -1671,7 +1643,6 @@ export class GPXStatistics {
}; };
speed: number[]; speed: number[];
elevation: { elevation: {
smoothed: number[];
gain: number[]; gain: number[];
loss: number[]; loss: number[];
}; };
@@ -1742,7 +1713,6 @@ export class GPXStatistics {
}, },
speed: [], speed: [],
elevation: { elevation: {
smoothed: [],
gain: [], gain: [],
loss: [], loss: [],
}, },
@@ -1777,9 +1747,6 @@ export class GPXStatistics {
); );
this.local.speed = this.local.speed.concat(other.local.speed); this.local.speed = this.local.speed.concat(other.local.speed);
this.local.elevation.smoothed = this.local.elevation.smoothed.concat(
other.local.elevation.smoothed
);
this.local.slope.at = this.local.slope.at.concat(other.local.slope.at); this.local.slope.at = this.local.slope.at.concat(other.local.slope.at);
this.local.slope.segment = this.local.slope.segment.concat(other.local.slope.segment); this.local.slope.segment = this.local.slope.segment.concat(other.local.slope.segment);
this.local.slope.length = this.local.slope.length.concat(other.local.slope.length); this.local.slope.length = this.local.slope.length.concat(other.local.slope.length);
@@ -1971,83 +1938,52 @@ export function getElevationDistanceFunction(statistics: GPXStatistics) {
} }
function windowSmoothing( function windowSmoothing(
points: TrackPoint[], length: number,
distance: (index1: number, index2: number) => number, distance: (index1: number, index2: number) => number,
window: number, window: number,
accumulate: (index: number) => number, compute: (start: number, end: number) => number
compute: (accumulated: number, start: number, end: number) => number,
remove?: (index: number) => number
): number[] { ): number[] {
let result = []; let result = [];
let start = 0, let start = 0,
end = 0, end = 0;
accumulated = 0; for (var i = 0; i < length; i++) {
for (var i = 0; i < points.length; i++) {
while (start + 1 < i && distance(start, i) > window) { while (start + 1 < i && distance(start, i) > window) {
if (remove) {
accumulated -= remove(start);
} else {
accumulated -= accumulate(start);
}
start++; start++;
} }
while (end < points.length && distance(i, end) <= window) { end = Math.min(i + 2, length);
accumulated += accumulate(end); while (end < length && distance(i, end) <= window) {
end++; end++;
} }
result[i] = compute(accumulated, start, end - 1); result[i] = compute(start, end - 1);
} }
return result; return result;
} }
function distanceWindowSmoothing( function distanceWindowSmoothing(
points: TrackPoint[], statistics: GPXStatistics,
window: number, window: number,
accumulate: (index: number) => number, compute: (start: number, end: number) => number
compute: (accumulated: number, start: number, end: number) => number
): number[] { ): number[] {
return windowSmoothing( return windowSmoothing(
points, statistics.local.points.length,
(index1, index2) => (index1, index2) =>
distance(points[index1].getCoordinates(), points[index2].getCoordinates()), statistics.local.distance.total[index2] - statistics.local.distance.total[index1],
window, window,
accumulate,
compute compute
); );
} }
function distanceWindowSmoothingWithDistanceAccumulator(
points: TrackPoint[],
distanceWindow: number,
compute: (accumulated: number, start: number, end: number) => number
): number[] {
return windowSmoothing(
points,
(index1, index2) =>
distance(points[index1].getCoordinates(), points[index2].getCoordinates()),
distanceWindow,
(index) =>
index > 0
? distance(points[index - 1].getCoordinates(), points[index].getCoordinates())
: 0,
compute,
(index) => distance(points[index].getCoordinates(), points[index + 1].getCoordinates())
);
}
function timeWindowSmoothing( function timeWindowSmoothing(
points: TrackPoint[], points: TrackPoint[],
window: number, window: number,
accumulate: (index: number) => number, compute: (start: number, end: number) => number
compute: (accumulated: number, start: number, end: number) => number
): number[] { ): number[] {
return windowSmoothing( return windowSmoothing(
points, points.length,
(index1, index2) => points[index2].time?.getTime() - points[index1].time?.getTime() || 0, (index1, index2) => points[index2].time?.getTime() - points[index1].time?.getTime() || 0,
window, window,
accumulate,
compute compute
); );
} }