Route grade: compute over a ~30m distance window, not adjacent points

DEM elevations quantise to 30m cells; dense points inside a cell share one value and
the whole rise lands on the single boundary segment -> a false steep spike in slope
colouring. Compute each segment's grade as a central difference over a >=30m window
(config gradeWindowM) so a cell step spreads across the cell and the colour reflects
the real grade. maxGrade uses the windowed value too.
This commit is contained in:
4aiman 2026-10-26 20:55:11 +03:00
commit 80ddff9b72

View file

@ -2453,14 +2453,30 @@ GPS.routeElevStats = function(points) {
for (let i = 1; i < n; i++) {
cumKm[i] = cumKm[i-1] + GPS.distance(points[i-1].lat, points[i-1].lon, points[i].lat, points[i].lon) / 1000;
}
// Grade over a DISTANCE window (~one DEM cell), not between adjacent points. DEM
// elevations are quantised to 30m cells: dense points inside a cell share one value
// and the whole rise lands on the single boundary segment → a false steep spike.
// A windowed central difference spreads that step across the cell so the colour
// reflects the real grade. Window W metres (config gradeWindowM, default 30).
const W = GPS.config.gradeWindowM ?? 30;
const cumM = cumKm.map(k => k * 1000);
const grades = new Array(n).fill(0);
let maxGrade = 0;
for (let i = 0; i < n - 1; i++) {
const dm = GPS.distance(points[i].lat, points[i].lon, points[i+1].lat, points[i+1].lon);
const de = (smoothed[i] != null && smoothed[i+1] != null) ? (smoothed[i+1] - smoothed[i]) : 0;
const g = dm > 1 ? (de / dm) * 100 : 0;
const center = (cumM[i] + cumM[i+1]) / 2;
let a = i, b = i + 1;
while (a > 0 && center - cumM[a] < W / 2) a--;
while (b < n - 1 && cumM[b] - center < W / 2) b++;
// pull window ends in off any null-elevation points
while (a < i && smoothed[a] == null) a++;
while (b > i + 1 && smoothed[b] == null) b--;
const span = cumM[b] - cumM[a];
let g = 0;
if (span > 1 && smoothed[a] != null && smoothed[b] != null) {
g = ((smoothed[b] - smoothed[a]) / span) * 100;
}
grades[i] = g;
if (dm > 10 && Math.abs(g) > Math.abs(maxGrade)) maxGrade = g;
if (span > 10 && Math.abs(g) > Math.abs(maxGrade)) maxGrade = g;
}
let ascent = 0, descent = 0, ref = null;
for (let i = 0; i < n; i++) {