mirror of
https://github.com/gpxstudio/gpx.studio.git
synced 2025-12-02 10:02:12 +00:00
elevation gain computation hybrid between ramer-douglas-peucker and smoothing
This commit is contained in:
102
gpx/src/gpx.ts
102
gpx/src/gpx.ts
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user