diff options
Diffstat (limited to 'web/frontend')
| -rw-r--r-- | web/frontend/official-climb-profile-logic.test.ts | 6 | ||||
| -rw-r--r-- | web/frontend/official-climb-profile-logic.ts | 5 | ||||
| -rw-r--r-- | web/frontend/official-climb-profile.ts | 27 | ||||
| -rw-r--r-- | web/frontend/types.d.ts | 5 |
4 files changed, 25 insertions, 18 deletions
diff --git a/web/frontend/official-climb-profile-logic.test.ts b/web/frontend/official-climb-profile-logic.test.ts index 31391b2..32521da 100644 --- a/web/frontend/official-climb-profile-logic.test.ts +++ b/web/frontend/official-climb-profile-logic.test.ts @@ -11,11 +11,13 @@ test("selects a display step that keeps long profiles readable", () => { test("assigns each slope to its color band", () => { assert.equal(profileBandForSlope(-0.1), "downhill"); + assert.equal(profileBandForSlope(0), "downhill"); + assert.equal(profileBandForSlope(0.1), "0-3"); assert.equal(profileBandForSlope(2.9), "0-3"); assert.equal(profileBandForSlope(3), "3-6"); assert.equal(profileBandForSlope(6), "6-9"); - assert.equal(profileBandForSlope(9), "9-12"); - assert.equal(profileBandForSlope(12), "12-plus"); + assert.equal(profileBandForSlope(9), "9-plus"); + assert.equal(profileBandForSlope(12), "9-plus"); }); test("interpolates profile sections at the selected step", () => { diff --git a/web/frontend/official-climb-profile-logic.ts b/web/frontend/official-climb-profile-logic.ts index 1b2bc83..893588d 100644 --- a/web/frontend/official-climb-profile-logic.ts +++ b/web/frontend/official-climb-profile-logic.ts @@ -14,12 +14,11 @@ const profileStepSizesM: number[] = [100, 200, 500, 1000]; const displayStepForLength = (lengthM: number): number => profileStepSizesM.find((stepM) => Math.ceil(lengthM / stepM) <= 30) || profileStepSizesM[profileStepSizesM.length - 1]; const profileBandForSlope = (slopePercent: number): ProfileBand => { - if (slopePercent < 0) return "downhill"; + if (slopePercent <= 0) return "downhill"; if (slopePercent < 3) return "0-3"; if (slopePercent < 6) return "3-6"; if (slopePercent < 9) return "6-9"; - if (slopePercent < 12) return "9-12"; - return "12-plus"; + return "9-plus"; }; const officialProfileSections = (points: ProfilePoint[], startIndex: number, endIndex: number, stepM: number): OfficialProfileSection[] => { const startDistanceM = points[startIndex].distanceKm * 1000; diff --git a/web/frontend/official-climb-profile.ts b/web/frontend/official-climb-profile.ts index 82ecc01..b60a1fa 100644 --- a/web/frontend/official-climb-profile.ts +++ b/web/frontend/official-climb-profile.ts @@ -35,8 +35,7 @@ export class OfficialClimbProfileController { "0-3": resolveColor("--color-profile-0-3"), "3-6": resolveColor("--color-profile-3-6"), "6-9": resolveColor("--color-profile-6-9"), - "9-12": resolveColor("--color-profile-9-12"), - "12-plus": resolveColor("--color-profile-12-plus"), + "9-plus": resolveColor("--color-profile-9-plus"), plotSurface: resolveColor("--color-plot-surface"), grid: resolveColor("--color-plot-grid"), subtle: resolveColor("--color-subtle"), @@ -66,7 +65,7 @@ export class OfficialClimbProfileController { profileCanvas.width = Math.floor(rect.width * ratio); profileCanvas.height = Math.floor(rect.height * ratio); context.setTransform(ratio, 0, 0, ratio, 0, 0); - const plot = { left: 10, right: rect.width - 10, top: 16, bottom: rect.height - 24 }; + const plot = { left: 10, right: rect.width - 10, top: 16, bottom: rect.height - 34 }; const climbLengthM = (points[endIndex].distanceKm - points[startIndex].distanceKm) * 1000; const sections = officialProfileSections(points, startIndex, endIndex, displayStepForLength(climbLengthM)); if (sections.length === 0) return; @@ -126,8 +125,7 @@ export class OfficialClimbProfileController { const startX = xForDistance(section.startDistanceKm); const endX = xForDistance(section.endDistanceKm); const label = `${section.slopePercent.toFixed(1)}%`; - const labelY = Math.max(plot.top + 10, Math.min(yForElevation(section.startElevation), yForElevation(section.endElevation)) - 4); - context.fillText(label, (startX + endX) / 2, labelY); + context.fillText(label, (startX + endX) / 2, plot.bottom - 4); } context.restore(); let topElevation = sections[0].startElevation; @@ -146,13 +144,22 @@ export class OfficialClimbProfileController { context.beginPath(); context.arc(xForDistance(topDistance), yForElevation(topElevation), 3.5, 0, 2 * Math.PI); context.fill(); - context.font = "11px system-ui, sans-serif"; + context.font = "9px system-ui, sans-serif"; context.fillStyle = colors.subtle; context.textBaseline = "top"; - context.textAlign = "left"; - context.fillText(this.formatDistance(0), plot.left, rect.height - 16); - context.textAlign = "right"; - context.fillText(this.formatDistance(maxDistance - minDistance), plot.right, rect.height - 16); + context.strokeStyle = colors.grid; + context.lineWidth = 1; + for (let sectionIndex = 0; sectionIndex <= sections.length; sectionIndex++) { + const distanceKm = + sectionIndex === sections.length ? maxDistance - minDistance : sections[sectionIndex].startDistanceKm - minDistance; + const x = xForDistance(minDistance + distanceKm); + context.beginPath(); + context.moveTo(x, plot.bottom); + context.lineTo(x, plot.bottom + 3); + context.stroke(); + context.textAlign = sectionIndex === 0 ? "left" : sectionIndex === sections.length ? "right" : "center"; + context.fillText(this.formatDistance(distanceKm), x, plot.bottom + 5); + } } formatDistance(distance: number): string { diff --git a/web/frontend/types.d.ts b/web/frontend/types.d.ts index 3642971..6fa9976 100644 --- a/web/frontend/types.d.ts +++ b/web/frontend/types.d.ts @@ -1,4 +1,4 @@ -export type ProfileBand = "downhill" | "0-3" | "3-6" | "6-9" | "9-12" | "12-plus"; +export type ProfileBand = "downhill" | "0-3" | "3-6" | "6-9" | "9-plus"; export type BoundaryTarget = "start" | "end"; export type BoundarySource = "profile" | "map"; @@ -77,8 +77,7 @@ export interface OfficialProfileColors { "0-3": string; "3-6": string; "6-9": string; - "9-12": string; - "12-plus": string; + "9-plus": string; plotSurface: string; grid: string; subtle: string; |