mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-24 16:03:02 +00:00
feat(tours): show the known SAC grades along a hike (MAP-03)
A hike in the planner, being edited or viewed as an import, now has an "SAC grades" card. Switched on, it asks the router which OpenStreetMap ways lie under the line (matched on foot, every grade of path open) and the server reads their sac_scale through the Overpass mirrors, the same trail-grades read the mountain bike grades use. The grades are drawn along the route on the planner map in one ramp of a single hue, T1 to T6, and listed by share and length; a row draws its class alone. Each row names its source in a tooltip: the SAC hiking scale and the sac_scale value OpenStreetMap wrote. What is not known stays unknown and says why, never T1: a way without a grade, a grade that names no single step (handed on as written), a stretch with no way clearly under the tour's line, or a way that could not be asked about. For imported tracks and stretches drawn by hand a matched way only counts where it keeps within 25 m of the tour's own line, so the original line is kept and nothing is guessed. The router's own reading of the scale is not asked for, since it may be normalised. The routing limit of a planned hike sits apart as a setting with its own badge, and a known step above it is marked. Nothing about the tour, its places or its days changes; switched off, the map is as it was. No regional colour categories and no grade derived from slope, height or trail markings.
This commit is contained in:
@@ -4,7 +4,7 @@ import { Test } from '@nestjs/testing';
|
||||
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
// TOUR-GRADE-001 to TOUR-GRADE-006
|
||||
// TOUR-GRADE-001 to TOUR-GRADE-008
|
||||
|
||||
const query = vi.fn<(q: string, caller: string) => Promise<unknown[]>>();
|
||||
let service: TourTrailGradeService;
|
||||
@@ -18,6 +18,8 @@ beforeEach(async () => {
|
||||
});
|
||||
|
||||
const way = (id: number, tags: Record<string, string>) => ({ type: 'way', id, tags });
|
||||
const NO_SAC = { sac_scale: null, sac_scale_unclear: null };
|
||||
const NO_MTB = { mtb_scale: null, mtb_scale_uphill: null, mtb_scale_imba: null };
|
||||
|
||||
describe('TourTrailGradeService (BIK-05)', () => {
|
||||
it('TOUR-GRADE-001: reads each scale to its steps and leaves a way without one out', async () => {
|
||||
@@ -30,8 +32,8 @@ describe('TourTrailGradeService (BIK-05)', () => {
|
||||
const answer = await service.grades({ way_ids: [1, 2, 3, 4, 5] });
|
||||
expect(answer).toEqual({
|
||||
ways: [
|
||||
{ id: 1, mtb_scale: '2+', mtb_scale_uphill: '3', mtb_scale_imba: null },
|
||||
{ id: 2, mtb_scale: '1', mtb_scale_uphill: null, mtb_scale_imba: '2' },
|
||||
{ ...NO_SAC, id: 1, mtb_scale: '2+', mtb_scale_uphill: '3', mtb_scale_imba: null },
|
||||
{ ...NO_SAC, id: 2, mtb_scale: '1', mtb_scale_uphill: null, mtb_scale_imba: '2' },
|
||||
],
|
||||
unanswered: [],
|
||||
});
|
||||
@@ -91,4 +93,30 @@ describe('TourTrailGradeService (BIK-05)', () => {
|
||||
await service.grades({ way_ids: [52_000] });
|
||||
expect(query).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('TOUR-GRADE-007: reads sac_scale as OpenStreetMap wrote it and hands an unclear one on as written (MAP-03)', async () => {
|
||||
query.mockResolvedValue([
|
||||
way(1, { sac_scale: 'demanding_mountain_hiking' }),
|
||||
way(2, { sac_scale: ' Alpine_Hiking ', 'mtb:scale': '3' }),
|
||||
way(3, { sac_scale: 'T3' }),
|
||||
way(4, { sac_scale: 'hiking;mountain_hiking' }),
|
||||
way(5, { sac_scale: ' ' }),
|
||||
way(6, { sac_scale: `x${'y'.repeat(80)}` }),
|
||||
]);
|
||||
const answer = await service.grades({ way_ids: [1, 2, 3, 4, 5, 6] });
|
||||
expect(answer.ways).toEqual([
|
||||
{ ...NO_MTB, id: 1, sac_scale: 'demanding_mountain_hiking', sac_scale_unclear: null },
|
||||
{ ...NO_MTB, id: 2, mtb_scale: '3', sac_scale: 'alpine_hiking', sac_scale_unclear: null },
|
||||
{ ...NO_MTB, id: 3, sac_scale: null, sac_scale_unclear: 'T3' },
|
||||
{ ...NO_MTB, id: 4, sac_scale: null, sac_scale_unclear: 'hiking;mountain_hiking' },
|
||||
{ ...NO_MTB, id: 6, sac_scale: null, sac_scale_unclear: `x${'y'.repeat(63)}` },
|
||||
]);
|
||||
});
|
||||
|
||||
it('TOUR-GRADE-008: never reads a hiking grade from anything but sac_scale (MAP-03)', async () => {
|
||||
query.mockResolvedValue([
|
||||
way(1, { trail_visibility: 'bad', 'osmc:symbol': 'blue:white:blue_bar', highway: 'path', incline: '30%' }),
|
||||
]);
|
||||
expect(await service.grades({ way_ids: [1] })).toEqual({ ways: [], unanswered: [] });
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user