openskidata-format 5.2.0 → 5.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Run.d.ts +1 -31
- package/dist/Run.js +5 -36
- package/dist/Run.js.map +1 -1
- package/dist/RunDifficultyConvention.d.ts +42 -0
- package/dist/RunDifficultyConvention.js +333 -0
- package/dist/RunDifficultyConvention.js.map +1 -0
- package/dist/SkiArea.d.ts +2 -1
- package/dist/SkiArea.js.map +1 -1
- package/dist/SlopeGradingScale.d.ts +44 -0
- package/dist/SlopeGradingScale.js +78 -0
- package/dist/SlopeGradingScale.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/package.json +4 -2
- package/src/ElevationProfile.test.ts +1 -1
- package/src/Run.ts +1 -33
- package/src/RunDifficultyConvention.test.ts +79 -0
- package/src/RunDifficultyConvention.ts +335 -0
- package/src/SkiArea.ts +2 -1
- package/src/SlopeGradingScale.test.ts +129 -0
- package/src/SlopeGradingScale.ts +94 -0
- package/src/index.ts +2 -0
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { RunDifficulty, RunUse } from "./Run";
|
|
2
|
+
import { getEstimatedRunDifficulty, getSlopeGradingScale } from "./SlopeGradingScale";
|
|
3
|
+
|
|
4
|
+
describe("getEstimatedRunDifficulty", () => {
|
|
5
|
+
it("should return null for steepness below minimum threshold", () => {
|
|
6
|
+
const downhillScale = getSlopeGradingScale({
|
|
7
|
+
type: "Feature",
|
|
8
|
+
geometry: { type: "LineString", coordinates: [] },
|
|
9
|
+
properties: {
|
|
10
|
+
type: "Run" as any,
|
|
11
|
+
uses: [RunUse.Downhill],
|
|
12
|
+
id: "test",
|
|
13
|
+
name: null,
|
|
14
|
+
ref: null,
|
|
15
|
+
status: "operating" as any,
|
|
16
|
+
description: null,
|
|
17
|
+
difficulty: null,
|
|
18
|
+
difficultyConvention: "europe" as any,
|
|
19
|
+
oneway: null,
|
|
20
|
+
lit: null,
|
|
21
|
+
gladed: null,
|
|
22
|
+
patrolled: null,
|
|
23
|
+
grooming: null,
|
|
24
|
+
skiAreas: [],
|
|
25
|
+
elevationProfile: null,
|
|
26
|
+
sources: [],
|
|
27
|
+
websites: [],
|
|
28
|
+
wikidata_id: null,
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
expect(getEstimatedRunDifficulty(0.03, downhillScale)).toBeNull(); // 3% steepness
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it("should return correct difficulty for downhill slopes", () => {
|
|
36
|
+
const downhillScale = getSlopeGradingScale({
|
|
37
|
+
type: "Feature",
|
|
38
|
+
geometry: { type: "LineString", coordinates: [] },
|
|
39
|
+
properties: {
|
|
40
|
+
type: "Run" as any,
|
|
41
|
+
uses: [RunUse.Downhill],
|
|
42
|
+
id: "test",
|
|
43
|
+
name: null,
|
|
44
|
+
ref: null,
|
|
45
|
+
status: "operating" as any,
|
|
46
|
+
description: null,
|
|
47
|
+
difficulty: null,
|
|
48
|
+
difficultyConvention: "europe" as any,
|
|
49
|
+
oneway: null,
|
|
50
|
+
lit: null,
|
|
51
|
+
gladed: null,
|
|
52
|
+
patrolled: null,
|
|
53
|
+
grooming: null,
|
|
54
|
+
skiAreas: [],
|
|
55
|
+
elevationProfile: null,
|
|
56
|
+
sources: [],
|
|
57
|
+
websites: [],
|
|
58
|
+
wikidata_id: null,
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
expect(getEstimatedRunDifficulty(0.1, downhillScale)).toBe(RunDifficulty.EASY); // 10%
|
|
63
|
+
expect(getEstimatedRunDifficulty(0.3, downhillScale)).toBe(RunDifficulty.INTERMEDIATE); // 30%
|
|
64
|
+
expect(getEstimatedRunDifficulty(0.6, downhillScale)).toBe(RunDifficulty.ADVANCED); // 60%
|
|
65
|
+
expect(getEstimatedRunDifficulty(1.0, downhillScale)).toBe(RunDifficulty.EXPERT); // 100%
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it("should return correct difficulty for nordic slopes", () => {
|
|
69
|
+
const nordicScale = getSlopeGradingScale({
|
|
70
|
+
type: "Feature",
|
|
71
|
+
geometry: { type: "LineString", coordinates: [] },
|
|
72
|
+
properties: {
|
|
73
|
+
type: "Run" as any,
|
|
74
|
+
uses: [RunUse.Nordic],
|
|
75
|
+
id: "test",
|
|
76
|
+
name: null,
|
|
77
|
+
ref: null,
|
|
78
|
+
status: "operating" as any,
|
|
79
|
+
description: null,
|
|
80
|
+
difficulty: null,
|
|
81
|
+
difficultyConvention: "europe" as any,
|
|
82
|
+
oneway: null,
|
|
83
|
+
lit: null,
|
|
84
|
+
gladed: null,
|
|
85
|
+
patrolled: null,
|
|
86
|
+
grooming: null,
|
|
87
|
+
skiAreas: [],
|
|
88
|
+
elevationProfile: null,
|
|
89
|
+
sources: [],
|
|
90
|
+
websites: [],
|
|
91
|
+
wikidata_id: null,
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
expect(getEstimatedRunDifficulty(0.05, nordicScale)).toBe(RunDifficulty.EASY); // 5%
|
|
96
|
+
expect(getEstimatedRunDifficulty(0.12, nordicScale)).toBe(RunDifficulty.INTERMEDIATE); // 12%
|
|
97
|
+
expect(getEstimatedRunDifficulty(0.25, nordicScale)).toBe(RunDifficulty.ADVANCED); // 25%
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it("should handle negative steepness (uphill)", () => {
|
|
101
|
+
const downhillScale = getSlopeGradingScale({
|
|
102
|
+
type: "Feature",
|
|
103
|
+
geometry: { type: "LineString", coordinates: [] },
|
|
104
|
+
properties: {
|
|
105
|
+
type: "Run" as any,
|
|
106
|
+
uses: [RunUse.Downhill],
|
|
107
|
+
id: "test",
|
|
108
|
+
name: null,
|
|
109
|
+
ref: null,
|
|
110
|
+
status: "operating" as any,
|
|
111
|
+
description: null,
|
|
112
|
+
difficulty: null,
|
|
113
|
+
difficultyConvention: "europe" as any,
|
|
114
|
+
oneway: null,
|
|
115
|
+
lit: null,
|
|
116
|
+
gladed: null,
|
|
117
|
+
patrolled: null,
|
|
118
|
+
grooming: null,
|
|
119
|
+
skiAreas: [],
|
|
120
|
+
elevationProfile: null,
|
|
121
|
+
sources: [],
|
|
122
|
+
websites: [],
|
|
123
|
+
wikidata_id: null,
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
expect(getEstimatedRunDifficulty(-0.1, downhillScale)).toBe(RunDifficulty.EASY); // -10%
|
|
128
|
+
});
|
|
129
|
+
});
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { RunDifficulty, RunFeature, RunUse } from "./Run";
|
|
2
|
+
import { RunDifficultyConvention } from "./RunDifficultyConvention";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Represents a slope grading scale with steepness thresholds for different run types.
|
|
6
|
+
* Used to estimate run difficulty based on slope steepness. Each scale defines
|
|
7
|
+
* threshold points where the difficulty classification changes based on gradient.
|
|
8
|
+
*/
|
|
9
|
+
export type SlopeGradingScale = {
|
|
10
|
+
/** Stops must be ordered ascending by steepness */
|
|
11
|
+
stops: { maxSteepness: number; difficulty: RunDifficulty | null }[];
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Gets the appropriate slope grading scale for a run based on its use types.
|
|
16
|
+
* Different run uses (downhill, nordic, etc.) have different steepness thresholds
|
|
17
|
+
* for difficulty classification due to the nature of the activity and equipment used.
|
|
18
|
+
*
|
|
19
|
+
* @param feature - The run feature to get the grading scale for
|
|
20
|
+
* @returns The appropriate slope grading scale with steepness thresholds
|
|
21
|
+
*/
|
|
22
|
+
export function getSlopeGradingScale(feature: RunFeature): SlopeGradingScale {
|
|
23
|
+
// Check for downhill or skitour uses first (they have the same scale)
|
|
24
|
+
if (
|
|
25
|
+
feature.properties.uses.includes(RunUse.Downhill) ||
|
|
26
|
+
feature.properties.uses.includes(RunUse.Skitour)
|
|
27
|
+
) {
|
|
28
|
+
return getSlopeGradingScaleForUse(RunUse.Downhill, feature.properties.difficultyConvention);
|
|
29
|
+
} else if (feature.properties.uses.includes(RunUse.Nordic)) {
|
|
30
|
+
return getSlopeGradingScaleForUse(RunUse.Nordic, feature.properties.difficultyConvention);
|
|
31
|
+
} else {
|
|
32
|
+
return { stops: [] };
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Estimates the run difficulty based on steepness and the provided slope grading scale.
|
|
38
|
+
* Uses the absolute value of steepness to handle both uphill and downhill slopes.
|
|
39
|
+
* Finds the first threshold that the steepness falls under.
|
|
40
|
+
*
|
|
41
|
+
* @param steepness - The steepness value (slope gradient as a decimal, e.g., 0.1 = 10%)
|
|
42
|
+
* @param scale - The slope grading scale with steepness thresholds
|
|
43
|
+
* @returns The estimated difficulty based on steepness, or null if no match or below minimum threshold
|
|
44
|
+
*/
|
|
45
|
+
export function getEstimatedRunDifficulty(
|
|
46
|
+
steepness: number,
|
|
47
|
+
scale: SlopeGradingScale
|
|
48
|
+
): RunDifficulty | null {
|
|
49
|
+
const absoluteSteepness = Math.abs(steepness);
|
|
50
|
+
return (
|
|
51
|
+
scale.stops.find((stop) => stop.maxSteepness > absoluteSteepness)
|
|
52
|
+
?.difficulty || null
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Gets the appropriate slope grading scale for a specific run use and region.
|
|
58
|
+
* Different run uses (downhill, nordic, etc.) have different steepness thresholds
|
|
59
|
+
* for difficulty classification. This function allows for regional variations
|
|
60
|
+
* in the future if different conventions use different slope scales.
|
|
61
|
+
*
|
|
62
|
+
* @param runUse - The type of run use to get the grading scale for
|
|
63
|
+
* @param convention - The regional difficulty convention (currently not used but available for future regional variations)
|
|
64
|
+
* @returns The appropriate slope grading scale with steepness thresholds
|
|
65
|
+
*/
|
|
66
|
+
export function getSlopeGradingScaleForUse(
|
|
67
|
+
runUse: RunUse,
|
|
68
|
+
convention: RunDifficultyConvention
|
|
69
|
+
): SlopeGradingScale {
|
|
70
|
+
// Currently convention is not used, but is available for future regional variations
|
|
71
|
+
switch (runUse) {
|
|
72
|
+
case RunUse.Downhill:
|
|
73
|
+
case RunUse.Skitour:
|
|
74
|
+
return {
|
|
75
|
+
stops: [
|
|
76
|
+
{ maxSteepness: 0.05, difficulty: null }, // No difficulty below 5%
|
|
77
|
+
{ maxSteepness: 0.25, difficulty: RunDifficulty.EASY },
|
|
78
|
+
{ maxSteepness: 0.4, difficulty: RunDifficulty.INTERMEDIATE },
|
|
79
|
+
{ maxSteepness: 0.8, difficulty: RunDifficulty.ADVANCED }, // Advanced up to 80%
|
|
80
|
+
{ maxSteepness: 1.2, difficulty: RunDifficulty.EXPERT }, // Expert up to 120%
|
|
81
|
+
],
|
|
82
|
+
};
|
|
83
|
+
case RunUse.Nordic:
|
|
84
|
+
return {
|
|
85
|
+
stops: [
|
|
86
|
+
{ maxSteepness: 0.1, difficulty: RunDifficulty.EASY },
|
|
87
|
+
{ maxSteepness: 0.15, difficulty: RunDifficulty.INTERMEDIATE },
|
|
88
|
+
{ maxSteepness: 0.3, difficulty: RunDifficulty.ADVANCED },
|
|
89
|
+
],
|
|
90
|
+
};
|
|
91
|
+
default:
|
|
92
|
+
return { stops: [] };
|
|
93
|
+
}
|
|
94
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -4,6 +4,8 @@ export * from './Lift'
|
|
|
4
4
|
export * from './LiftNameFormatter'
|
|
5
5
|
export * from './Location'
|
|
6
6
|
export * from './Run'
|
|
7
|
+
export * from './RunDifficultyConvention'
|
|
8
|
+
export * from './SlopeGradingScale'
|
|
7
9
|
export * from './SkiArea'
|
|
8
10
|
export * from './SnowCoverHistory'
|
|
9
11
|
export * from './Source'
|