diff options
| author | Martin Kagamino Lehoux <martin@lehoux.net> | 2026-10-18 21:19:36 +0200 |
|---|---|---|
| committer | Martin Kagamino Lehoux <martin@lehoux.net> | 2026-10-18 21:19:36 +0200 |
| commit | 8a0032d08862dcf28c653bee3fd21052e967e35d (patch) | |
| tree | 6cb0d8efe6f5c2bf9ce9eb3ef8306faecbe7cfeb /web/frontend/climb-profile.ts | |
| parent | d575274d00c8da251df29d5d6a636044673944a3 (diff) | |
Cut climb profile sections in the backend
The chart steps are computed in Go and shipped as finished sections, next to
the profile statistics the climb package already owns: climb.ProfileSections
cuts a Route at its widest bends under a ProfilePolicy, and
rideanalysis.MergeClimbProfile takes over the merge that lived in
web/climbs.go, so web only loads the attempt rides and maps the DTOs.
The payload carries sections instead of raw elevations, and the browser keeps
what needs the canvas: slope bands, labels, distance ticks and colours.
official-climb-profile-logic.ts and its tests are gone, replaced by
climb/profile_test.go and a page test reading the embedded profile JSON.
Diffstat (limited to 'web/frontend/climb-profile.ts')
| -rw-r--r-- | web/frontend/climb-profile.ts | 49 |
1 files changed, 17 insertions, 32 deletions
diff --git a/web/frontend/climb-profile.ts b/web/frontend/climb-profile.ts index 6051ec1..d06d291 100644 --- a/web/frontend/climb-profile.ts +++ b/web/frontend/climb-profile.ts @@ -1,32 +1,21 @@ -import { drawOfficialSections, officialProfilePlot, resolveOfficialProfileColors } from "./official-climb-profile.js"; -import { officialProfileSections } from "./official-climb-profile-logic.js"; -import type { OfficialProfileSection } from "./official-climb-profile-logic.js"; -import type { ProfilePoint } from "./types.js"; +import { + drawOfficialSections, + officialProfilePlot, + officialProfileSections, + resolveOfficialProfileColors, +} from "./official-climb-profile.js"; +import type { OfficialProfileSection, ProfileSectionPayload } from "./types.js"; interface ClimbProfilePayload { - distanceKm: number; - elevations: number[]; + sections: ProfileSectionPayload[]; } interface ClimbProfileState { canvas: HTMLCanvasElement; - payload: ClimbProfilePayload; sections: OfficialProfileSection[]; hoverOutput: HTMLOutputElement; } -const climbProfilePoints = (payload: ClimbProfilePayload): ProfilePoint[] => - payload.elevations.map((elevationM, index) => ({ - distanceKm: (payload.distanceKm * index) / (payload.elevations.length - 1), - elevationM, - })); - -const climbProfileSections = (payload: ClimbProfilePayload): OfficialProfileSection[] => { - const points = climbProfilePoints(payload); - if (points.length < 2) return []; - return officialProfileSections(points, 0, points.length - 1); -}; - const elevationAtDistance = (sections: OfficialProfileSection[], distanceKm: number): number | null => { for (const section of sections) { if (distanceKm >= section.startDistanceKm && distanceKm <= section.endDistanceKm) { @@ -64,15 +53,13 @@ const drawHoverMarker = (state: ClimbProfileState, distanceKm: number): void => state.hoverOutput.textContent = `${distanceKm.toFixed(2)} km ยท ${Math.round(elevation)} m`; }; -const clearHover = (state: ClimbProfileState): void => { - drawClimbProfile(state.canvas, state.payload, state.sections); - state.hoverOutput.textContent = ""; +const drawClimbProfile = (state: ClimbProfileState): void => { + drawOfficialSections(state.canvas, state.sections, resolveOfficialProfileColors()); }; -const drawClimbProfile = (canvas: HTMLCanvasElement, payload: ClimbProfilePayload, sections?: OfficialProfileSection[]): void => { - const profileSections = sections ?? climbProfileSections(payload); - if (profileSections.length === 0) return; - drawOfficialSections(canvas, profileSections, resolveOfficialProfileColors()); +const clearHover = (state: ClimbProfileState): void => { + drawClimbProfile(state); + state.hoverOutput.textContent = ""; }; const bindHover = (state: ClimbProfileState): void => { @@ -85,7 +72,7 @@ const bindHover = (state: ClimbProfileState): void => { return; } const distanceKm = plot.minDistanceKm + ((x - plot.left) / (plot.right - plot.left)) * plot.distanceSpan; - drawClimbProfile(state.canvas, state.payload, state.sections); + drawClimbProfile(state); drawHoverMarker(state, distanceKm); }); state.canvas.addEventListener("pointerleave", () => clearHover(state)); @@ -97,13 +84,11 @@ const init = (): void => { const canvas = document.querySelector<HTMLCanvasElement>("[data-climb-profile]"); if (!payloadElement || !canvas) return; const payload = JSON.parse(payloadElement.textContent ?? "null") as ClimbProfilePayload | null; - if (!payload || payload.elevations.length < 2) return; - const sections = climbProfileSections(payload); - if (sections.length === 0) return; + if (!payload || payload.sections.length === 0) return; const hoverOutput = document.querySelector<HTMLOutputElement>("[data-climb-profile-hover]"); if (!hoverOutput) return; - const state: ClimbProfileState = { canvas, payload, sections, hoverOutput }; - drawClimbProfile(canvas, payload, sections); + const state: ClimbProfileState = { canvas, sections: officialProfileSections(payload.sections), hoverOutput }; + drawClimbProfile(state); bindHover(state); }; |
