openskidata-format 0.14.0 → 1.0.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/ElevationProfile.d.ts +10 -2
- package/dist/Lift.d.ts +36 -5
- package/dist/Lift.js +21 -0
- package/dist/Lift.js.map +1 -1
- package/dist/LiftNameFormatter.d.ts +1 -1
- package/dist/LiftNameFormatter.js +16 -21
- package/dist/LiftNameFormatter.js.map +1 -1
- package/dist/Run.d.ts +83 -12
- package/dist/Run.js +88 -57
- package/dist/Run.js.map +1 -1
- package/dist/SkiArea.d.ts +55 -24
- package/dist/SkiArea.js +6 -0
- package/dist/SkiArea.js.map +1 -1
- package/dist/Source.d.ts +2 -2
- package/dist/Status.d.ts +12 -0
- package/dist/Status.js +12 -0
- package/dist/Status.js.map +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/index.js.map +1 -1
- package/dist/util/exhaustiveMatchingGuard.d.ts +1 -0
- package/dist/util/exhaustiveMatchingGuard.js +8 -0
- package/dist/util/exhaustiveMatchingGuard.js.map +1 -0
- package/package.json +1 -1
- package/src/ElevationProfile.ts +9 -5
- package/src/Lift.ts +56 -5
- package/src/LiftNameFormatter.ts +10 -17
- package/src/Run.ts +122 -53
- package/src/SkiArea.ts +60 -22
- package/src/Source.ts +1 -1
- package/src/Status.ts +12 -0
- package/src/index.ts +0 -1
- package/src/util/exhaustiveMatchingGuard.ts +3 -0
- package/dist/Activity.d.ts +0 -5
- package/dist/Activity.js +0 -10
- package/dist/Activity.js.map +0 -1
- package/src/Activity.ts +0 -5
package/dist/SkiArea.d.ts
CHANGED
|
@@ -1,38 +1,76 @@
|
|
|
1
|
-
import { Activity } from './Activity';
|
|
2
1
|
import { FeatureType } from './FeatureType';
|
|
3
2
|
import { LiftType } from './Lift';
|
|
4
3
|
import { Location } from './Location';
|
|
5
|
-
import {
|
|
6
|
-
import Source from './Source';
|
|
4
|
+
import { RunDifficulty, RunDifficultyConvention } from './Run';
|
|
5
|
+
import { Source } from './Source';
|
|
7
6
|
import { Status } from './Status';
|
|
7
|
+
/**
|
|
8
|
+
* A ski area feature is derived from several sources:
|
|
9
|
+
* - OpenStreetMap site=piste relations
|
|
10
|
+
* - OpenStreetMap landuse=winter_sports areas that contains ski runs
|
|
11
|
+
* - Skimap.org ski areas
|
|
12
|
+
*
|
|
13
|
+
* The processor attempts to merge the data of all sources to create a single ski area feature.
|
|
14
|
+
*
|
|
15
|
+
* In some cases merging may fail and duplicate ski areas will be produced. This is likely due to ambiguous data.
|
|
16
|
+
* Check:
|
|
17
|
+
* - that a site=piste relation contains all the lifts/runs. If many lifts/runs are missing, merging will fail.
|
|
18
|
+
* - that if there are multiple landuse=winter_sports areas representing a single ski area. In this case a multipolygon relation should be used.
|
|
19
|
+
* - that if there are multiple Skimap.org ski areas representing a single ski area.
|
|
20
|
+
*
|
|
21
|
+
* General recommendation for ski area tagging in OpenStreetMap:
|
|
22
|
+
* - start with a landuse=winter_sports area as it's easy to set up and maintain.
|
|
23
|
+
* - if that's not explicit enough, add a site=piste relation as well.
|
|
24
|
+
*
|
|
25
|
+
* Note: if runs are present but there is no associated ski area source, a ski area feature is generated with no name and no source.
|
|
26
|
+
*/
|
|
8
27
|
export type SkiAreaFeature = GeoJSON.Feature<SkiAreaGeometry, SkiAreaProperties>;
|
|
9
28
|
export type SkiAreaGeometry = GeoJSON.Point | GeoJSON.Polygon | GeoJSON.MultiPolygon;
|
|
10
|
-
export
|
|
29
|
+
export type SkiAreaSummaryFeature = GeoJSON.Feature<SkiAreaGeometry, SkiAreaSummaryProperties>;
|
|
30
|
+
/**
|
|
31
|
+
* @property {string} id - Unique identifier for the ski area. The ID is just a hash of the feature, so will change if the feature changes in any way. If a stable identifier is needed, use the wikidata_id property, or a source id.
|
|
32
|
+
* @property {string | null} name - Name of the ski area.
|
|
33
|
+
* @property {Source[]} sources - Data sources.
|
|
34
|
+
* @property {SkiAreaActivity[]} activities - Activities available at this ski area, derived from presence of ski runs and Skimap.org data.
|
|
35
|
+
* @property {SkiAreaStatistics} statistics - Statistics generated from associated runs / lifts.
|
|
36
|
+
* @property {Status | null} status - Operational status. Derived from OpenStreetMap lifecycle tags and Skimap.org data.
|
|
37
|
+
* @property {RunColorConvention} runConvention - Color convention used for runs at this ski area.
|
|
38
|
+
* @property {string[]} websites - Official website(s) of the ski area. Derived from the OpenStreetMap website tag and Skimap.org data.
|
|
39
|
+
* @property {string | null} wikidata_id - Wikidata identifier. Derived from the OpenStreetMap wikidata tag.
|
|
40
|
+
* @property {Location | null} location - Reverse geocoded country / region information.
|
|
41
|
+
*/
|
|
42
|
+
export type SkiAreaProperties = SkiAreaSummaryProperties & {
|
|
43
|
+
sources: Source[];
|
|
44
|
+
statistics?: SkiAreaStatistics;
|
|
45
|
+
runConvention: RunDifficultyConvention;
|
|
46
|
+
websites: string[];
|
|
47
|
+
wikidata_id: string | null;
|
|
48
|
+
};
|
|
49
|
+
export type SkiAreaSummaryProperties = {
|
|
11
50
|
type: FeatureType.SkiArea;
|
|
12
51
|
id: string;
|
|
13
52
|
name: string | null;
|
|
14
|
-
|
|
15
|
-
activities: Activity[];
|
|
16
|
-
generated: boolean;
|
|
17
|
-
statistics?: SkiAreaStatistics;
|
|
53
|
+
activities: SkiAreaActivity[];
|
|
18
54
|
status: Status | null;
|
|
19
|
-
runConvention: RunConvention;
|
|
20
|
-
websites: string[];
|
|
21
55
|
location: Location | null;
|
|
56
|
+
};
|
|
57
|
+
export declare enum SkiAreaActivity {
|
|
58
|
+
Downhill = "downhill",
|
|
59
|
+
Nordic = "nordic"
|
|
22
60
|
}
|
|
23
|
-
export
|
|
61
|
+
export type SkiAreaStatistics = {
|
|
24
62
|
runs: RunStatistics;
|
|
25
63
|
lifts: LiftStatistics;
|
|
26
64
|
minElevation?: number;
|
|
27
65
|
maxElevation?: number;
|
|
28
|
-
}
|
|
29
|
-
export
|
|
66
|
+
};
|
|
67
|
+
export type RunStatistics = {
|
|
30
68
|
minElevation?: number;
|
|
31
69
|
maxElevation?: number;
|
|
32
70
|
byActivity: RunStatisticsByActivityAndDifficulty;
|
|
33
|
-
}
|
|
71
|
+
};
|
|
34
72
|
export type RunStatisticsByActivityAndDifficulty = {
|
|
35
|
-
[key in
|
|
73
|
+
[key in SkiAreaActivity | 'other']?: {
|
|
36
74
|
byDifficulty: RunStatisticsByDifficulty;
|
|
37
75
|
};
|
|
38
76
|
};
|
|
@@ -42,11 +80,11 @@ export type RunStatisticsByDifficulty = {
|
|
|
42
80
|
lengthInKm: number;
|
|
43
81
|
};
|
|
44
82
|
};
|
|
45
|
-
export
|
|
83
|
+
export type LiftStatistics = {
|
|
46
84
|
minElevation?: number;
|
|
47
85
|
maxElevation?: number;
|
|
48
86
|
byType: LiftStatisticsByType;
|
|
49
|
-
}
|
|
87
|
+
};
|
|
50
88
|
export type LiftStatisticsByTypeKey = LiftType | 'other';
|
|
51
89
|
export type LiftStatisticsByType = {
|
|
52
90
|
[key in LiftStatisticsByTypeKey]?: {
|
|
@@ -54,10 +92,3 @@ export type LiftStatisticsByType = {
|
|
|
54
92
|
lengthInKm: number;
|
|
55
93
|
};
|
|
56
94
|
};
|
|
57
|
-
export interface MapObjectStatistics {
|
|
58
|
-
count: number;
|
|
59
|
-
lengthInKm: number;
|
|
60
|
-
minElevation?: number;
|
|
61
|
-
maxElevation?: number;
|
|
62
|
-
combinedElevationChange?: number;
|
|
63
|
-
}
|
package/dist/SkiArea.js
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SkiAreaActivity = void 0;
|
|
4
|
+
var SkiAreaActivity;
|
|
5
|
+
(function (SkiAreaActivity) {
|
|
6
|
+
SkiAreaActivity["Downhill"] = "downhill";
|
|
7
|
+
SkiAreaActivity["Nordic"] = "nordic";
|
|
8
|
+
})(SkiAreaActivity || (exports.SkiAreaActivity = SkiAreaActivity = {}));
|
|
3
9
|
//# sourceMappingURL=SkiArea.js.map
|
package/dist/SkiArea.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SkiArea.js","sourceRoot":"","sources":["../src/SkiArea.ts"],"names":[],"mappings":""}
|
|
1
|
+
{"version":3,"file":"SkiArea.js","sourceRoot":"","sources":["../src/SkiArea.ts"],"names":[],"mappings":";;;AAoEA,IAAY,eAGX;AAHD,WAAY,eAAe;IACzB,wCAAqB,CAAA;IACrB,oCAAiB,CAAA;AACnB,CAAC,EAHW,eAAe,+BAAf,eAAe,QAG1B"}
|
package/dist/Source.d.ts
CHANGED
package/dist/Status.d.ts
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Status of a feature.
|
|
3
|
+
*
|
|
4
|
+
* Note that this is not real-time status but indicates whether a feature is in operation when conditions permit or is abandoned, etc.
|
|
5
|
+
*
|
|
6
|
+
* Primarily derived from OpenStreetMap tags.
|
|
7
|
+
* Additionally, Skimap.org data provides status information for ski area features only.
|
|
8
|
+
*
|
|
9
|
+
* Tagging schemes supported are:
|
|
10
|
+
* - lifecycle prefixes (preferred) https://wiki.openstreetmap.org/wiki/Lifecycle_prefix
|
|
11
|
+
* - separate lifecycle tags ("proposed=yes", etc)
|
|
12
|
+
*/
|
|
1
13
|
export declare enum Status {
|
|
2
14
|
Operating = "operating",
|
|
3
15
|
Disused = "disused",
|
package/dist/Status.js
CHANGED
|
@@ -1,6 +1,18 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Status = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Status of a feature.
|
|
6
|
+
*
|
|
7
|
+
* Note that this is not real-time status but indicates whether a feature is in operation when conditions permit or is abandoned, etc.
|
|
8
|
+
*
|
|
9
|
+
* Primarily derived from OpenStreetMap tags.
|
|
10
|
+
* Additionally, Skimap.org data provides status information for ski area features only.
|
|
11
|
+
*
|
|
12
|
+
* Tagging schemes supported are:
|
|
13
|
+
* - lifecycle prefixes (preferred) https://wiki.openstreetmap.org/wiki/Lifecycle_prefix
|
|
14
|
+
* - separate lifecycle tags ("proposed=yes", etc)
|
|
15
|
+
*/
|
|
4
16
|
var Status;
|
|
5
17
|
(function (Status) {
|
|
6
18
|
Status["Operating"] = "operating";
|
package/dist/Status.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Status.js","sourceRoot":"","sources":["../src/Status.ts"],"names":[],"mappings":";;;AAAA,IAAY,MAOX;AAPD,WAAY,MAAM;IAChB,iCAAuB,CAAA;IACvB,6BAAmB,CAAA;IACnB,iCAAuB,CAAA;IACvB,+BAAqB,CAAA;IACrB,6BAAmB,CAAA;IACnB,uCAA6B,CAAA;AAC/B,CAAC,EAPW,MAAM,sBAAN,MAAM,QAOjB"}
|
|
1
|
+
{"version":3,"file":"Status.js","sourceRoot":"","sources":["../src/Status.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;;;;GAWG;AACH,IAAY,MAOX;AAPD,WAAY,MAAM;IAChB,iCAAuB,CAAA;IACvB,6BAAmB,CAAA;IACnB,iCAAuB,CAAA;IACvB,+BAAqB,CAAA;IACrB,6BAAmB,CAAA;IACnB,uCAA6B,CAAA;AAC/B,CAAC,EAPW,MAAM,sBAAN,MAAM,QAOjB"}
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const tslib_1 = require("tslib");
|
|
4
|
-
tslib_1.__exportStar(require("./Activity"), exports);
|
|
5
4
|
tslib_1.__exportStar(require("./ElevationProfile"), exports);
|
|
6
5
|
tslib_1.__exportStar(require("./FeatureType"), exports);
|
|
7
6
|
tslib_1.__exportStar(require("./Lift"), exports);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,6DAAkC;AAClC,wDAA6B;AAC7B,iDAAsB;AACtB,8DAAmC;AACnC,qDAA0B;AAC1B,gDAAqB;AACrB,oDAAyB;AACzB,mDAAwB;AACxB,mDAAwB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const exhaustiveMatchingGuard: (value: never) => never;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.exhaustiveMatchingGuard = void 0;
|
|
4
|
+
const exhaustiveMatchingGuard = (value) => {
|
|
5
|
+
throw new Error(`Unhandled case: ${value}`);
|
|
6
|
+
};
|
|
7
|
+
exports.exhaustiveMatchingGuard = exhaustiveMatchingGuard;
|
|
8
|
+
//# sourceMappingURL=exhaustiveMatchingGuard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exhaustiveMatchingGuard.js","sourceRoot":"","sources":["../../src/util/exhaustiveMatchingGuard.ts"],"names":[],"mappings":";;;AAAO,MAAM,uBAAuB,GAAG,CAAC,KAAY,EAAS,EAAE;IAC7D,MAAM,IAAI,KAAK,CAAC,mBAAmB,KAAK,EAAE,CAAC,CAAA;AAC7C,CAAC,CAAA;AAFY,QAAA,uBAAuB,2BAEnC"}
|
package/package.json
CHANGED
package/src/ElevationProfile.ts
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Represents an elevation profile with evenly distributed height measurements along a LineString.
|
|
3
|
+
*
|
|
4
|
+
* @property {number[]} heights - Array of height measurements in meters. These heights are sampled at regular intervals along the LineString,
|
|
5
|
+
* except for the final height which corresponds to the LineString endpoint and may have a different spacing.
|
|
6
|
+
* Height values can be mapped to geographical coordinates using Turf.js: `turf.lineChunk(geometry, resolution, {units: 'meters'})`.
|
|
7
|
+
* @property {number} resolution - The horizontal sampling distance in meters between consecutive height measurements.
|
|
8
|
+
*/
|
|
9
|
+
export type ElevationProfile = {
|
|
5
10
|
heights: number[]
|
|
6
|
-
// Horizontal resolution in meters of the profile
|
|
7
11
|
resolution: number
|
|
8
12
|
}
|
package/src/Lift.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { Location, SkiAreaFeature } from '.'
|
|
2
1
|
import { FeatureType } from './FeatureType'
|
|
3
|
-
import
|
|
2
|
+
import { SkiAreaSummaryFeature } from './SkiArea'
|
|
3
|
+
import { Source } from './Source'
|
|
4
4
|
import { Status } from './Status'
|
|
5
|
+
import { exhaustiveMatchingGuard } from './util/exhaustiveMatchingGuard'
|
|
5
6
|
|
|
6
7
|
export type LiftFeature = GeoJSON.Feature<LiftGeometry, LiftProperties>
|
|
7
8
|
|
|
@@ -13,12 +14,41 @@ export type LiftGeometry =
|
|
|
13
14
|
| GeoJSON.Polygon
|
|
14
15
|
| GeoJSON.MultiPolygon
|
|
15
16
|
|
|
17
|
+
/**
|
|
18
|
+
* A feature representing a ski lift.
|
|
19
|
+
*
|
|
20
|
+
* Lifts are derived from OpenStreetMap aerialway/railway features that are commonly used for winter sports.
|
|
21
|
+
*
|
|
22
|
+
* Note:
|
|
23
|
+
* - Private lifts are not included in this dataset.
|
|
24
|
+
* - Rack railways are included only if they are part of a site=piste relation.
|
|
25
|
+
* - Some lifts included in the dataset may be for other purposes (amusement parks, etc).
|
|
26
|
+
*
|
|
27
|
+
* Properties:
|
|
28
|
+
* @property {FeatureType.Lift} type - The feature type, which is always 'Lift'.
|
|
29
|
+
* @property {string} id - Unique identifier for the lift. The ID is just a hash of the feature, so will change if the feature changes in any way.
|
|
30
|
+
* @property {LiftType} liftType - Type of lift (e.g. chair_lift, gondola). Derived from OpenStreetMap aerialway/railway tags.
|
|
31
|
+
* @property {Status} status - Operational status of the lift. Derived from OpenStreetMap lifecycle tags.
|
|
32
|
+
* @property {string | null} name - Name of the lift. Derived from the OpenStreetMap name tag.
|
|
33
|
+
* @property {string | null} ref - Reference code/number for the lift. Derived from the OpenStreetMap ref tag.
|
|
34
|
+
* @property {string | null} description - Description of the lift. Derived from the OpenStreetMap description tag.
|
|
35
|
+
* @property {boolean | null} oneway - Whether the lift allows riding only in one direction. Derived from the OpenStreetMap oneway tag.
|
|
36
|
+
* @property {number | null} occupancy - Number of people each carrier can transport. Derived from the OpenStreetMap aerialway:occupancy tag.
|
|
37
|
+
* @property {number | null} capacity - Transport capacity in persons/hour. Derived from the OpenStreetMap aerialway:capacity tag.
|
|
38
|
+
* @property {number | null} duration - Duration of lift ride in seconds. Derived from the OpenStreetMap aerialway:duration tag.
|
|
39
|
+
* @property {boolean | null} detachable - Whether the lift has detachable grips. Derived from the OpenStreetMap aerialway:detachable tag.
|
|
40
|
+
* @property {boolean | null} bubble - Whether the lift has bubbles/covers to protect from weather. Derived from the OpenStreetMap aerialway:bubble tag.
|
|
41
|
+
* @property {boolean | null} heating - Whether the lift has heated carriers/seats. Derived from the OpenStreetMap aerialway:heating tag.
|
|
42
|
+
* @property {SkiAreaSummaryFeature[]} skiAreas - Ski areas this lift is a part of.
|
|
43
|
+
* @property {Source[]} sources - Data sources for the feature.
|
|
44
|
+
* @property {string[]} websites - Websites associated with this lift. Derived from the OpenStreetMap website tag.
|
|
45
|
+
* @property {string | null} wikidata_id - Wikidata identifier. Derived from the OpenStreetMap wikidata tag.
|
|
46
|
+
*/
|
|
16
47
|
export type LiftProperties = {
|
|
17
48
|
type: FeatureType.Lift
|
|
18
49
|
id: string
|
|
19
50
|
liftType: LiftType
|
|
20
51
|
status: Status
|
|
21
|
-
color: string
|
|
22
52
|
name: string | null
|
|
23
53
|
ref: string | null
|
|
24
54
|
description: string | null
|
|
@@ -26,12 +56,13 @@ export type LiftProperties = {
|
|
|
26
56
|
occupancy: number | null
|
|
27
57
|
capacity: number | null
|
|
28
58
|
duration: number | null
|
|
59
|
+
detachable: boolean | null
|
|
29
60
|
bubble: boolean | null
|
|
30
61
|
heating: boolean | null
|
|
31
|
-
skiAreas:
|
|
62
|
+
skiAreas: SkiAreaSummaryFeature[]
|
|
32
63
|
sources: Source[]
|
|
33
|
-
location: Location | null
|
|
34
64
|
websites: string[]
|
|
65
|
+
wikidata_id: string | null
|
|
35
66
|
}
|
|
36
67
|
|
|
37
68
|
export enum LiftType {
|
|
@@ -75,5 +106,25 @@ export function getFormattedLiftType(liftType: LiftType): string {
|
|
|
75
106
|
return 'Funicular'
|
|
76
107
|
case LiftType.RackRailway:
|
|
77
108
|
return 'Rack Railway'
|
|
109
|
+
default:
|
|
110
|
+
return exhaustiveMatchingGuard(liftType)
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function getLiftColor(status: Status): string {
|
|
115
|
+
const BRIGHT_RED_COLOR = 'hsl(0, 82%, 42%)'
|
|
116
|
+
const DIM_RED_COLOR = 'hsl(0, 53%, 42%)'
|
|
117
|
+
|
|
118
|
+
switch (status) {
|
|
119
|
+
case Status.Disused:
|
|
120
|
+
case Status.Abandoned:
|
|
121
|
+
return DIM_RED_COLOR
|
|
122
|
+
case Status.Proposed:
|
|
123
|
+
case Status.Planned:
|
|
124
|
+
case Status.Construction:
|
|
125
|
+
case Status.Operating:
|
|
126
|
+
return BRIGHT_RED_COLOR
|
|
127
|
+
default:
|
|
128
|
+
return exhaustiveMatchingGuard(status)
|
|
78
129
|
}
|
|
79
130
|
}
|
package/src/LiftNameFormatter.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { LiftProperties, LiftType } from '
|
|
2
|
-
import { getFormattedLiftType } from './Lift'
|
|
1
|
+
import { LiftProperties, LiftType, getFormattedLiftType } from './Lift'
|
|
3
2
|
|
|
4
3
|
export function getLiftNameAndType(properties: LiftProperties) {
|
|
5
4
|
const name = properties.name
|
|
@@ -7,7 +6,7 @@ export function getLiftNameAndType(properties: LiftProperties) {
|
|
|
7
6
|
const liftType = getAugmentedLiftType(properties)
|
|
8
7
|
|
|
9
8
|
if (name && liftType) {
|
|
10
|
-
return name
|
|
9
|
+
return `${name} (${liftType})`
|
|
11
10
|
} else if (name) {
|
|
12
11
|
return name
|
|
13
12
|
} else {
|
|
@@ -54,15 +53,11 @@ function getAugmentedLiftType(properties: LiftProperties): string | null {
|
|
|
54
53
|
}
|
|
55
54
|
|
|
56
55
|
function formattedDuration(duration: number): string {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
}
|
|
62
|
-
if (seconds < 10) {
|
|
63
|
-
seconds = '0' + seconds
|
|
64
|
-
}
|
|
65
|
-
return minutes + ':' + seconds
|
|
56
|
+
const minutes = Math.floor(duration / 60)
|
|
57
|
+
.toString()
|
|
58
|
+
.padStart(2, '0')
|
|
59
|
+
const seconds = (duration % 60).toString().padStart(2, '0')
|
|
60
|
+
return `${minutes}:${seconds}`
|
|
66
61
|
}
|
|
67
62
|
|
|
68
63
|
function implicitOccupancyLiftType(properties: LiftProperties): string | null {
|
|
@@ -130,9 +125,7 @@ function containsAny(input: string | null, lowerCaseSearchList: string[]) {
|
|
|
130
125
|
}
|
|
131
126
|
|
|
132
127
|
const lowerCaseInput = input.toLowerCase()
|
|
133
|
-
return (
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
}) !== -1
|
|
137
|
-
)
|
|
128
|
+
return lowerCaseSearchList.some((searchItem) => {
|
|
129
|
+
return lowerCaseInput.includes(searchItem)
|
|
130
|
+
})
|
|
138
131
|
}
|
package/src/Run.ts
CHANGED
|
@@ -1,15 +1,55 @@
|
|
|
1
1
|
import * as GeoJSON from 'geojson'
|
|
2
2
|
import { ElevationProfile } from './ElevationProfile'
|
|
3
3
|
import { FeatureType } from './FeatureType'
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import Source from './Source'
|
|
4
|
+
import { SkiAreaSummaryFeature } from './SkiArea'
|
|
5
|
+
import { Source } from './Source'
|
|
7
6
|
import { Status } from './Status'
|
|
7
|
+
import { exhaustiveMatchingGuard } from './util/exhaustiveMatchingGuard'
|
|
8
8
|
|
|
9
9
|
export type RunGeometry = GeoJSON.LineString | GeoJSON.Polygon
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Represents a segment of a ski run/trail.
|
|
13
|
+
*
|
|
14
|
+
* Runs are derived from OpenStreetMap pistes, which are defined as all ways/areas/relations containing a "piste:type" tag.
|
|
15
|
+
* Piste relations support is limited, it is only used to enhance the data of its member ways. There is no way to access the relation itself in the output data.
|
|
16
|
+
*
|
|
17
|
+
* A single run as marked on the mountain may be broken up into several run features, especially if the run has branches, or changes in tags (e.g. difficulty, use).
|
|
18
|
+
*
|
|
19
|
+
* In the future, there might be a "Route" entity introduced to represent a whole run, which would be composed of multiple run features.
|
|
20
|
+
*
|
|
21
|
+
* Note: winter hiking trails and trails of other winter sports disciplines are also included in this dataset.
|
|
22
|
+
*
|
|
23
|
+
* In postprocessing:
|
|
24
|
+
* - overlapping ways are merged into a single run feature with multiple uses.
|
|
25
|
+
* - connected run segments with the same properties are merged into a single run feature.
|
|
26
|
+
*/
|
|
11
27
|
export type RunFeature = GeoJSON.Feature<RunGeometry, RunProperties>
|
|
12
28
|
|
|
29
|
+
/**
|
|
30
|
+
* Represents the properties of a ski run.
|
|
31
|
+
*
|
|
32
|
+
* Properties:
|
|
33
|
+
* @property {FeatureType.Run} type - The feature type, which is always 'Run'.
|
|
34
|
+
* @property {RunUse[]} uses - Use types for the run (e.g. downhill, nordic), derived from OpenStreetMap "piste:type" tags.
|
|
35
|
+
* @property {string} id - Unique identifier for the run. The ID is just a hash of the feature, so will change if the feature changes in any way.
|
|
36
|
+
* @property {string | null} name - Name of the run, if available
|
|
37
|
+
* @property {string | null} ref - Reference code/number for the run. Derived from the OpenStreetMap piste:ref and ref tags.
|
|
38
|
+
* @property {Status} status - Operational status. Note: as only operational runs are included in this dataset, this will always be 'operating'.
|
|
39
|
+
* @property {string | null} description - Description of the run, derived from the OpenStreetMap "piste:description" or "description" tag.
|
|
40
|
+
* @property {RunDifficulty | null} difficulty - Difficulty rating of the run, derived from the OpenStreetMap "piste:difficulty" tag
|
|
41
|
+
* @property {RunDifficultyConvention} difficultyConvention - Regional convention used for representing run difficulty. Derived from location.
|
|
42
|
+
* @property {boolean | null} oneway - Whether the run is one-way only, derived from the OpenStreetMap "piste:oneway" or "oneway" tag, in addition to defaults based on run use.
|
|
43
|
+
* @property {boolean | null} lit - Whether the run has lighting for night skiing, derived from the OpenStreetMap "piste:lit" or "lit" tag.
|
|
44
|
+
* @property {boolean | null} gladed - Whether the run is through gladed/tree terrain, derived from the OpenStreetMap "piste:gladed" or "gladed" tag.
|
|
45
|
+
* @property {boolean | null} patrolled - Whether the run is patrolled by ski patrol, derived from the OpenStreetMap "piste:patrolled" or "patrolled" tag.
|
|
46
|
+
* @property {RunGrooming | null} grooming - Grooming status/type of the run, derived from the OpenStreetMap "piste:grooming" tag. If not specified explicitly, for difficulties "expert", "freeride", and "extreme", grooming is assumed to be "backcountry".
|
|
47
|
+
* @property {SkiAreaSummaryFeature[]} skiAreas - Ski areas this run belongs to. Derived from the OpenStreetMap site=piste relation or landuse=winter_sports area and proximity to ski area features. Runs with "backcountry" grooming are not associated with ski areas unless they have a "piste:patrolled=yes" or "patrolled=yes" OpenStreetMap tag, or are part of a "site=piste" ski area relation.
|
|
48
|
+
* @property {ElevationProfile | null} elevationProfile - Elevation profile of the run, only available for runs with LineString geometry.
|
|
49
|
+
* @property {Source[]} sources - Data sources for this run's information
|
|
50
|
+
* @property {string[]} websites - Websites associated with this run, derived from the OpenStreetMap website tag
|
|
51
|
+
* @property {string | null} wikidata_id - Wikidata identifier, if available, derived from the OpenStreetMap wikidata tag
|
|
52
|
+
*/
|
|
13
53
|
export type RunProperties = {
|
|
14
54
|
type: FeatureType.Run
|
|
15
55
|
uses: RunUse[]
|
|
@@ -19,19 +59,17 @@ export type RunProperties = {
|
|
|
19
59
|
status: Status
|
|
20
60
|
description: string | null
|
|
21
61
|
difficulty: RunDifficulty | null
|
|
22
|
-
|
|
62
|
+
difficultyConvention: RunDifficultyConvention
|
|
23
63
|
oneway: boolean | null
|
|
24
64
|
lit: boolean | null
|
|
25
65
|
gladed: boolean | null
|
|
26
66
|
patrolled: boolean | null
|
|
27
|
-
color: string
|
|
28
|
-
colorName: ColorName
|
|
29
67
|
grooming: RunGrooming | null
|
|
30
|
-
skiAreas:
|
|
68
|
+
skiAreas: SkiAreaSummaryFeature[]
|
|
31
69
|
elevationProfile: ElevationProfile | null
|
|
32
70
|
sources: Source[]
|
|
33
|
-
location: Location | null
|
|
34
71
|
websites: string[]
|
|
72
|
+
wikidata_id: string | null
|
|
35
73
|
}
|
|
36
74
|
|
|
37
75
|
export enum RunUse {
|
|
@@ -67,7 +105,7 @@ export enum RunDifficulty {
|
|
|
67
105
|
EXTREME = 'extreme',
|
|
68
106
|
}
|
|
69
107
|
|
|
70
|
-
export enum
|
|
108
|
+
export enum RunColor {
|
|
71
109
|
GREEN = 'green',
|
|
72
110
|
BLUE = 'blue',
|
|
73
111
|
RED = 'red',
|
|
@@ -76,92 +114,123 @@ export enum ColorName {
|
|
|
76
114
|
GREY = 'grey',
|
|
77
115
|
}
|
|
78
116
|
|
|
79
|
-
export enum
|
|
117
|
+
export enum RunColorValue {
|
|
118
|
+
GREEN = 'hsl(125, 100%, 33%)',
|
|
119
|
+
BLUE = 'hsl(208, 100%, 33%)',
|
|
120
|
+
RED = 'hsl(359, 94%, 53%)',
|
|
121
|
+
BLACK = 'hsl(0, 0%, 0%)',
|
|
122
|
+
ORANGE = 'hsl(34, 100%, 50%)',
|
|
123
|
+
GREY = 'hsl(0, 0%, 35%)',
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Run difficulty colors vary by region. This enum defines the color convention used for runs at a ski area.
|
|
128
|
+
* @enum {string}
|
|
129
|
+
*/
|
|
130
|
+
export enum RunDifficultyConvention {
|
|
131
|
+
/**
|
|
132
|
+
* European color convention:
|
|
133
|
+
* - Green: Novice
|
|
134
|
+
* - Blue: Easy
|
|
135
|
+
* - Red: Intermediate
|
|
136
|
+
* - Black: Advanced/Expert
|
|
137
|
+
* - Orange: Freeride/Extreme
|
|
138
|
+
*/
|
|
80
139
|
EUROPE = 'europe',
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Japanese color convention:
|
|
143
|
+
* - Green: Novice/Easy
|
|
144
|
+
* - Red: Intermediate
|
|
145
|
+
* - Black: Advanced/Expert
|
|
146
|
+
* - Orange: Freeride/Extreme
|
|
147
|
+
*/
|
|
81
148
|
JAPAN = 'japan',
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* North American color convention:
|
|
152
|
+
* - Green: Novice/Easy
|
|
153
|
+
* - Blue: Intermediate
|
|
154
|
+
* - Black: Advanced/Expert
|
|
155
|
+
* - Orange: Freeride/Extreme
|
|
156
|
+
*/
|
|
82
157
|
NORTH_AMERICA = 'north_america',
|
|
83
158
|
}
|
|
84
159
|
|
|
85
|
-
|
|
86
|
-
const GREEN_COLOR = 'hsl(125, 100%, 33%)'
|
|
87
|
-
const BLUE_COLOR = 'hsl(208, 100%, 33%)'
|
|
88
|
-
const RED_COLOR = 'hsl(359, 94%, 53%)'
|
|
89
|
-
const BLACK_COLOR = 'hsl(0, 0%, 0%)'
|
|
90
|
-
const ORANGE_COLOR = 'hsl(34, 100%, 50%)'
|
|
91
|
-
const GREY_COLOR = 'hsl(0, 0%, 35%)'
|
|
92
|
-
|
|
93
|
-
export function getColorName(color: string): ColorName {
|
|
160
|
+
export function getColorValue(color: RunColor): RunColorValue {
|
|
94
161
|
switch (color) {
|
|
95
|
-
case
|
|
96
|
-
return
|
|
97
|
-
case
|
|
98
|
-
return
|
|
99
|
-
case
|
|
100
|
-
return
|
|
101
|
-
case
|
|
102
|
-
return
|
|
103
|
-
case
|
|
104
|
-
return
|
|
105
|
-
case
|
|
106
|
-
return
|
|
162
|
+
case RunColor.GREEN:
|
|
163
|
+
return RunColorValue.GREEN
|
|
164
|
+
case RunColor.BLUE:
|
|
165
|
+
return RunColorValue.BLUE
|
|
166
|
+
case RunColor.RED:
|
|
167
|
+
return RunColorValue.RED
|
|
168
|
+
case RunColor.BLACK:
|
|
169
|
+
return RunColorValue.BLACK
|
|
170
|
+
case RunColor.ORANGE:
|
|
171
|
+
return RunColorValue.ORANGE
|
|
172
|
+
case RunColor.GREY:
|
|
173
|
+
return RunColorValue.GREY
|
|
107
174
|
default:
|
|
108
|
-
throw '
|
|
175
|
+
throw 'invalid color'
|
|
109
176
|
}
|
|
110
177
|
}
|
|
111
178
|
|
|
112
179
|
export function getRunColor(
|
|
113
|
-
convention:
|
|
180
|
+
convention: RunDifficultyConvention,
|
|
114
181
|
difficulty: RunDifficulty | null,
|
|
115
|
-
):
|
|
182
|
+
): RunColor {
|
|
116
183
|
switch (convention) {
|
|
117
|
-
case
|
|
184
|
+
case RunDifficultyConvention.EUROPE:
|
|
118
185
|
switch (difficulty) {
|
|
119
186
|
case RunDifficulty.NOVICE:
|
|
120
|
-
return
|
|
187
|
+
return RunColor.GREEN
|
|
121
188
|
case RunDifficulty.EASY:
|
|
122
|
-
return
|
|
189
|
+
return RunColor.BLUE
|
|
123
190
|
case RunDifficulty.INTERMEDIATE:
|
|
124
|
-
return
|
|
191
|
+
return RunColor.RED
|
|
125
192
|
case RunDifficulty.ADVANCED:
|
|
126
193
|
case RunDifficulty.EXPERT:
|
|
127
|
-
return
|
|
194
|
+
return RunColor.BLACK
|
|
128
195
|
case RunDifficulty.FREERIDE:
|
|
129
196
|
case RunDifficulty.EXTREME:
|
|
130
|
-
return
|
|
197
|
+
return RunColor.ORANGE
|
|
131
198
|
default:
|
|
132
|
-
return
|
|
199
|
+
return RunColor.GREY
|
|
133
200
|
}
|
|
134
|
-
case
|
|
201
|
+
case RunDifficultyConvention.JAPAN:
|
|
135
202
|
switch (difficulty) {
|
|
136
203
|
case RunDifficulty.NOVICE:
|
|
137
204
|
case RunDifficulty.EASY:
|
|
138
|
-
return
|
|
205
|
+
return RunColor.GREEN
|
|
139
206
|
case RunDifficulty.INTERMEDIATE:
|
|
140
|
-
return
|
|
207
|
+
return RunColor.RED
|
|
141
208
|
case RunDifficulty.ADVANCED:
|
|
142
209
|
case RunDifficulty.EXPERT:
|
|
143
|
-
return
|
|
210
|
+
return RunColor.BLACK
|
|
144
211
|
case RunDifficulty.FREERIDE:
|
|
145
212
|
case RunDifficulty.EXTREME:
|
|
146
|
-
return
|
|
213
|
+
return RunColor.ORANGE
|
|
147
214
|
default:
|
|
148
|
-
return
|
|
215
|
+
return RunColor.GREY
|
|
149
216
|
}
|
|
150
|
-
case
|
|
217
|
+
case RunDifficultyConvention.NORTH_AMERICA:
|
|
151
218
|
switch (difficulty) {
|
|
152
219
|
case RunDifficulty.NOVICE:
|
|
153
220
|
case RunDifficulty.EASY:
|
|
154
|
-
return
|
|
221
|
+
return RunColor.GREEN
|
|
155
222
|
case RunDifficulty.INTERMEDIATE:
|
|
156
|
-
return
|
|
223
|
+
return RunColor.BLUE
|
|
157
224
|
case RunDifficulty.ADVANCED:
|
|
158
225
|
case RunDifficulty.EXPERT:
|
|
159
|
-
return
|
|
226
|
+
return RunColor.BLACK
|
|
160
227
|
case RunDifficulty.FREERIDE:
|
|
161
228
|
case RunDifficulty.EXTREME:
|
|
162
|
-
return
|
|
229
|
+
return RunColor.ORANGE
|
|
163
230
|
default:
|
|
164
|
-
return
|
|
231
|
+
return RunColor.GREY
|
|
165
232
|
}
|
|
233
|
+
default:
|
|
234
|
+
return exhaustiveMatchingGuard(convention)
|
|
166
235
|
}
|
|
167
236
|
}
|