summaryrefslogtreecommitdiff
path: root/web/frontend
diff options
context:
space:
mode:
Diffstat (limited to 'web/frontend')
-rw-r--r--web/frontend/official-climb-profile-logic.test.ts6
-rw-r--r--web/frontend/official-climb-profile-logic.ts5
-rw-r--r--web/frontend/official-climb-profile.ts27
-rw-r--r--web/frontend/types.d.ts5
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;