openskidata-format 14.0.0 → 16.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.
Files changed (94) hide show
  1. package/dist/ElevationProfile.d.ts +1 -0
  2. package/dist/ElevationProfile.d.ts.map +1 -0
  3. package/dist/ElevationProfile.js +15 -24
  4. package/dist/ElevationProfile.js.map +1 -1
  5. package/dist/FeatureType.d.ts +1 -0
  6. package/dist/FeatureType.d.ts.map +1 -0
  7. package/dist/FeatureType.js +2 -5
  8. package/dist/FeatureType.js.map +1 -1
  9. package/dist/Lift.d.ts +10 -8
  10. package/dist/Lift.d.ts.map +1 -0
  11. package/dist/Lift.js +22 -29
  12. package/dist/Lift.js.map +1 -1
  13. package/dist/LiftNameFormatter.d.ts +2 -1
  14. package/dist/LiftNameFormatter.d.ts.map +1 -0
  15. package/dist/LiftNameFormatter.js +11 -14
  16. package/dist/LiftNameFormatter.js.map +1 -1
  17. package/dist/Place.d.ts +1 -0
  18. package/dist/Place.d.ts.map +1 -0
  19. package/dist/Place.js +1 -2
  20. package/dist/Run.d.ts +10 -9
  21. package/dist/Run.d.ts.map +1 -0
  22. package/dist/Run.js +23 -30
  23. package/dist/Run.js.map +1 -1
  24. package/dist/RunDifficultyConvention.d.ts +2 -1
  25. package/dist/RunDifficultyConvention.d.ts.map +1 -0
  26. package/dist/RunDifficultyConvention.js +10 -15
  27. package/dist/RunDifficultyConvention.js.map +1 -1
  28. package/dist/SkiArea.d.ts +11 -12
  29. package/dist/SkiArea.d.ts.map +1 -0
  30. package/dist/SkiArea.js +2 -5
  31. package/dist/SkiArea.js.map +1 -1
  32. package/dist/SlopeGradingScale.d.ts +3 -2
  33. package/dist/SlopeGradingScale.d.ts.map +1 -0
  34. package/dist/SlopeGradingScale.js +21 -26
  35. package/dist/SlopeGradingScale.js.map +1 -1
  36. package/dist/SnowCoverHistory.d.ts +1 -0
  37. package/dist/SnowCoverHistory.d.ts.map +1 -0
  38. package/dist/SnowCoverHistory.js +9 -8
  39. package/dist/SnowCoverHistory.js.map +1 -1
  40. package/dist/Source.d.ts +2 -14
  41. package/dist/Source.d.ts.map +1 -0
  42. package/dist/Source.js +7 -43
  43. package/dist/Source.js.map +1 -1
  44. package/dist/Spot.d.ts +6 -5
  45. package/dist/Spot.d.ts.map +1 -0
  46. package/dist/Spot.js +6 -9
  47. package/dist/Spot.js.map +1 -1
  48. package/dist/Status.d.ts +1 -0
  49. package/dist/Status.d.ts.map +1 -0
  50. package/dist/Status.js +2 -5
  51. package/dist/Status.js.map +1 -1
  52. package/dist/ViewportHint.d.ts +1 -0
  53. package/dist/ViewportHint.d.ts.map +1 -0
  54. package/dist/ViewportHint.js +4 -10
  55. package/dist/ViewportHint.js.map +1 -1
  56. package/dist/index.d.ts +15 -15
  57. package/dist/index.d.ts.map +1 -0
  58. package/dist/index.js +14 -18
  59. package/dist/index.js.map +1 -1
  60. package/dist/util/exhaustiveMatchingGuard.d.ts +1 -0
  61. package/dist/util/exhaustiveMatchingGuard.d.ts.map +1 -0
  62. package/dist/util/exhaustiveMatchingGuard.js +1 -5
  63. package/dist/util/exhaustiveMatchingGuard.js.map +1 -1
  64. package/package.json +35 -23
  65. package/dist/SkiPass.d.ts +0 -82
  66. package/dist/SkiPass.js +0 -3
  67. package/dist/SkiPass.js.map +0 -1
  68. package/dist/testUtils.d.ts +0 -2
  69. package/dist/testUtils.js +0 -7
  70. package/dist/testUtils.js.map +0 -1
  71. package/src/ElevationProfile.test.ts +0 -738
  72. package/src/ElevationProfile.ts +0 -452
  73. package/src/FeatureType.ts +0 -6
  74. package/src/Lift.test.ts +0 -195
  75. package/src/Lift.ts +0 -219
  76. package/src/LiftNameFormatter.ts +0 -116
  77. package/src/Place.ts +0 -11
  78. package/src/Run.ts +0 -242
  79. package/src/RunDifficultyConvention.test.ts +0 -79
  80. package/src/RunDifficultyConvention.ts +0 -335
  81. package/src/SkiArea.ts +0 -128
  82. package/src/SkiPass.ts +0 -89
  83. package/src/SlopeGradingScale.test.ts +0 -151
  84. package/src/SlopeGradingScale.ts +0 -96
  85. package/src/SnowCoverHistory.test.ts +0 -103
  86. package/src/SnowCoverHistory.ts +0 -107
  87. package/src/Source.test.ts +0 -50
  88. package/src/Source.ts +0 -72
  89. package/src/Spot.ts +0 -140
  90. package/src/Status.ts +0 -20
  91. package/src/ViewportHint.ts +0 -208
  92. package/src/index.ts +0 -15
  93. package/src/testUtils.ts +0 -5
  94. package/src/util/exhaustiveMatchingGuard.ts +0 -3
package/src/Spot.ts DELETED
@@ -1,140 +0,0 @@
1
- import * as GeoJSON from 'geojson'
2
- import { FeatureType } from './FeatureType'
3
- import { Place } from './Place'
4
- import { SkiAreaSummaryFeature } from './SkiArea'
5
- import { Source } from './Source'
6
- import { ViewportHint } from './ViewportHint'
7
-
8
- /**
9
- * A GeoJSON feature representing a spot (point of interest) in or around a ski area.
10
- *
11
- * Spots are used to represent lift stations, road crossings, or certain terrain features.
12
- */
13
- export type SpotFeature = GeoJSON.Feature<SpotGeometry, SpotProperties>
14
-
15
- /**
16
- * A GeoJSON feature representing a lift station spot.
17
- */
18
- export type LiftStationSpotFeature = GeoJSON.Feature<
19
- SpotGeometry,
20
- LiftStationSpotProperties
21
- >
22
-
23
- /**
24
- * Geometry type for spots - always a point.
25
- */
26
- export type SpotGeometry = GeoJSON.Point
27
-
28
- /**
29
- * Union of all spot property types.
30
- */
31
- export type SpotProperties =
32
- | CrossingSpotProperties
33
- | LiftStationSpotProperties
34
- | AvalancheTransceiverTrainingSpotProperties
35
- | AvalancheTransceiverCheckpointSpotProperties
36
- | HalfpipeSpotProperties
37
-
38
- /**
39
- * Base properties shared by all spot types.
40
- */
41
- export type SpotBaseProperties = {
42
- type: FeatureType.Spot
43
- id: string
44
- skiAreas: SkiAreaSummaryFeature[]
45
- sources: Source[]
46
- places: Place[]
47
- viewportHint: ViewportHint
48
- }
49
-
50
- /**
51
- * Types of spots that can be found in or around ski areas.
52
- */
53
- export enum SpotType {
54
- Crossing = 'crossing',
55
- LiftStation = 'lift_station',
56
- AvalancheTransceiverTraining = 'avalanche_transceiver_training',
57
- AvalancheTransceiverCheckpoint = 'avalanche_transceiver_checkpoint',
58
- Halfpipe = 'halfpipe',
59
- }
60
-
61
- /**
62
- * A crossing is a point where a ski run intersects with a road or path,
63
- * potentially requiring skiers to remove their skis.
64
- *
65
- * From OpenStreetMap node with tag `piste:dismount=yes|no|sometimes`.
66
- */
67
- export type CrossingSpotProperties = SpotBaseProperties & {
68
- spotType: SpotType.Crossing
69
- dismount: DismountRequirement
70
- }
71
-
72
- /**
73
- * Whether dismounting from skis is required at a crossing.
74
- */
75
- export enum DismountRequirement {
76
- Yes = 'yes',
77
- No = 'no',
78
- Sometimes = 'sometimes',
79
- }
80
-
81
- /**
82
- * A lift station is a boarding or alighting point for a ski lift.
83
- *
84
- * From OpenStreetMap node or area with tag `aerialway=station`.
85
- * Areas are represented as a point (centroid).
86
- *
87
- * @property {string | null} name - Name of the station, from OpenStreetMap tag `name`.
88
- * @property {LiftStationPosition | null} position - From OpenStreetMap tag `aerialway:station=bottom|mid|top`.
89
- * @property {boolean | null} entry - Whether passengers can board here. From OpenStreetMap tag `aerialway:access=entry|both|no`.
90
- * @property {boolean | null} exit - Whether passengers can alight here. From OpenStreetMap tag `aerialway:access=exit|both|no`.
91
- * @property {string} liftId - ID of the lift feature associated with this station.
92
- */
93
- export type LiftStationSpotProperties = SpotBaseProperties & {
94
- spotType: SpotType.LiftStation
95
- name: string | null
96
- position: LiftStationPosition | null
97
- entry: boolean | null
98
- exit: boolean | null
99
- liftId: string
100
- }
101
-
102
- /**
103
- * Position of a lift station along the lift line.
104
- */
105
- export enum LiftStationPosition {
106
- Top = 'top',
107
- Mid = 'mid',
108
- Bottom = 'bottom',
109
- }
110
-
111
- /**
112
- * A designated area where skiers can practice using avalanche transceivers.
113
- *
114
- * From OpenStreetMap node or area with tags `amenity=avalanche_transceiver` and `avalanche_transceiver=training`.
115
- * Areas are represented as a point (centroid).
116
- */
117
- export type AvalancheTransceiverTrainingSpotProperties = SpotBaseProperties & {
118
- spotType: SpotType.AvalancheTransceiverTraining
119
- }
120
-
121
- /**
122
- * A checkpoint where skiers can verify their avalanche transceiver is functioning.
123
- *
124
- * From OpenStreetMap node or area with tags `amenity=avalanche_transceiver` and `avalanche_transceiver=checkpoint`.
125
- * Areas are represented as a point (centroid).
126
- */
127
- export type AvalancheTransceiverCheckpointSpotProperties =
128
- SpotBaseProperties & {
129
- spotType: SpotType.AvalancheTransceiverCheckpoint
130
- }
131
-
132
- /**
133
- * A halfpipe structure for freestyle skiing or snowboarding.
134
- *
135
- * From OpenStreetMap node/way/area with tag `man_made=piste:halfpipe`.
136
- * Areas are represented as a point (centroid).
137
- */
138
- export type HalfpipeSpotProperties = SpotBaseProperties & {
139
- spotType: SpotType.Halfpipe
140
- }
package/src/Status.ts DELETED
@@ -1,20 +0,0 @@
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
- */
13
- export enum Status {
14
- Operating = 'operating',
15
- Disused = 'disused',
16
- Abandoned = 'abandoned',
17
- Proposed = 'proposed',
18
- Planned = 'planned',
19
- Construction = 'construction',
20
- }
@@ -1,208 +0,0 @@
1
- import * as GeoJSON from 'geojson'
2
- import distance from '@turf/distance'
3
-
4
- /**
5
- * Pre-computed viewport positioning data stored on every feature's properties.
6
- * Camera space: Y axis points uphill (along bearing), X axis points screen-right.
7
- * Metric values are in that rotated frame with elevation-adjusted Y positions
8
- * (at 45° pitch, Δe metres above mean elevation shifts Y by Δe metres).
9
- */
10
- export type ViewportHint = {
11
- /** Geographic centre of the rotated bounding box as [lng, lat]. */
12
- center: [number, number]
13
-
14
- /**
15
- * Uphill bearing in degrees clockwise from north. null when elevation variation is
16
- * insufficient to determine orientation — client should use top-down view (pitch 0, bearing 0).
17
- */
18
- bearing: number | null
19
-
20
- /** Bounding box width along the camera X axis, in metres. */
21
- rotatedWidthMeters: number
22
-
23
- /** Bounding box height along the camera Y axis, in metres (elevation-adjusted). */
24
- rotatedHeightMeters: number
25
-
26
- /**
27
- * Most-downhill edge of the elevation-adjusted bounding box in camera-Y metres, relative
28
- * to mean feature elevation. Used to compute the perspective centre shift at 45° pitch:
29
- * look-at point = minCameraY + nearFraction * rotatedHeightMeters, where
30
- * nearFraction = nearGround / (nearGround + farGround) from camera pitch and vFOV.
31
- */
32
- minCameraY: number
33
- }
34
-
35
- /**
36
- * Computes a ViewportHint from one or more GeoJSON geometries.
37
- * All geometry types are accepted; elevation is read from the Z coordinate (defaults to 0).
38
- * Throws if no coordinates are found.
39
- */
40
- export function computeViewportHint(
41
- geometries: GeoJSON.Geometry[],
42
- ): ViewportHint {
43
- // Collect all coordinates (for bbox + elevation stats) and consecutive segment pairs
44
- // (for the downhill bearing calculation).
45
- const allCoords: [number, number, number][] = []
46
- const bearingSegments: [[number, number, number], [number, number, number]][] =
47
- []
48
-
49
- for (const geom of geometries) {
50
- collectGeometry(geom, allCoords, bearingSegments)
51
- }
52
-
53
- if (allCoords.length === 0) throw new Error('computeViewportHint: no coordinates found in provided geometries')
54
-
55
- let minLng = Infinity,
56
- maxLng = -Infinity
57
- let minLat = Infinity,
58
- maxLat = -Infinity
59
- for (const [lng, lat] of allCoords) {
60
- if (lng < minLng) minLng = lng
61
- if (lng > maxLng) maxLng = lng
62
- if (lat < minLat) minLat = lat
63
- if (lat > maxLat) maxLat = lat
64
- }
65
-
66
- const centerLng = (minLng + maxLng) / 2
67
- const centerLat = (minLat + maxLat) / 2
68
-
69
- // Weight each segment's downhill direction by length to find dominant bearing.
70
- let sumSin = 0,
71
- sumCos = 0
72
- for (const [[lng0, lat0, elev0], [lng1, lat1, elev1]] of bearingSegments) {
73
- if (elev0 === elev1) continue
74
- const [fromLng, fromLat, toLng, toLat] =
75
- elev0 > elev1
76
- ? [lng0, lat0, lng1, lat1]
77
- : [lng1, lat1, lng0, lat0]
78
- const bear = greatCircleBearing(fromLat, fromLng, toLat, toLng)
79
- const len = distance([lng0, lat0], [lng1, lat1], { units: 'meters' })
80
- const bearingRad = (bear * Math.PI) / 180
81
- sumSin += Math.sin(bearingRad) * len
82
- sumCos += Math.cos(bearingRad) * len
83
- }
84
-
85
- // Uphill bearing = downhill + 180°; null if no elevation variation found.
86
- // A small offset is added so the camera is angled slightly to the side of the fall line,
87
- // giving a better 3-D profile of the terrain rather than pointing straight uphill.
88
- const bearingSideOffsetDeg = 30
89
- let mapBearing: number | null = null
90
- if (sumSin !== 0 || sumCos !== 0) {
91
- const dominantDownhill = (Math.atan2(sumSin, sumCos) * 180) / Math.PI
92
- const normalizedDownhill = ((dominantDownhill % 360) + 360) % 360
93
- mapBearing = (normalizedDownhill + 180 + bearingSideOffsetDeg) % 360
94
- }
95
-
96
- const metersPerDeg = 111320
97
- const cosLat = Math.cos(centerLat * (Math.PI / 180))
98
- const bearingRad = ((mapBearing ?? 0) * Math.PI) / 180
99
-
100
- let elevationSum = 0
101
- for (const [, , elev] of allCoords) elevationSum += elev
102
- const meanElevation = elevationSum / allCoords.length
103
-
104
- let minCameraX = Infinity,
105
- maxCameraX = -Infinity
106
- let minCameraY = Infinity,
107
- maxCameraY = -Infinity
108
-
109
- for (const [lng, lat, elev] of allCoords) {
110
- const dx = (lng - centerLng) * cosLat * metersPerDeg
111
- const dy = (lat - centerLat) * metersPerDeg
112
- const cx = dx * Math.cos(bearingRad) - dy * Math.sin(bearingRad)
113
- const cy = dx * Math.sin(bearingRad) + dy * Math.cos(bearingRad)
114
- // At 45° pitch, elevation above mean shifts apparent Y by the same amount (tan 45° = 1).
115
- const cyEffective = cy + (elev - meanElevation)
116
- if (cx < minCameraX) minCameraX = cx
117
- if (cx > maxCameraX) maxCameraX = cx
118
- if (cyEffective < minCameraY) minCameraY = cyEffective
119
- if (cyEffective > maxCameraY) maxCameraY = cyEffective
120
- }
121
-
122
- return {
123
- center: [centerLng, centerLat],
124
- bearing: mapBearing,
125
- rotatedWidthMeters: maxCameraX - minCameraX,
126
- rotatedHeightMeters: maxCameraY - minCameraY,
127
- minCameraY,
128
- }
129
- }
130
-
131
- /** Recursively extracts coordinates and segment pairs from a geometry. */
132
- function collectGeometry(
133
- geom: GeoJSON.Geometry,
134
- allCoords: [number, number, number][],
135
- bearingSegments: [[number, number, number], [number, number, number]][],
136
- ): void {
137
- switch (geom.type) {
138
- case 'Point': {
139
- const [lng, lat, elev = 0] = geom.coordinates
140
- allCoords.push([lng, lat, elev])
141
- break
142
- }
143
- case 'MultiPoint': {
144
- for (const pos of geom.coordinates) {
145
- const [lng, lat, elev = 0] = pos
146
- allCoords.push([lng, lat, elev])
147
- }
148
- break
149
- }
150
- case 'LineString':
151
- collectPositions(geom.coordinates, allCoords, bearingSegments)
152
- break
153
- case 'MultiLineString':
154
- for (const line of geom.coordinates) {
155
- collectPositions(line, allCoords, bearingSegments)
156
- }
157
- break
158
- case 'Polygon':
159
- for (const ring of geom.coordinates) {
160
- collectPositions(ring, allCoords, bearingSegments)
161
- }
162
- break
163
- case 'MultiPolygon':
164
- for (const polygon of geom.coordinates) {
165
- for (const ring of polygon) {
166
- collectPositions(ring, allCoords, bearingSegments)
167
- }
168
- }
169
- break
170
- case 'GeometryCollection':
171
- for (const g of geom.geometries) {
172
- collectGeometry(g, allCoords, bearingSegments)
173
- }
174
- break
175
- }
176
- }
177
-
178
- /** Adds positions to allCoords and consecutive pairs to bearingSegments. Z defaults to 0. */
179
- function collectPositions(
180
- positions: GeoJSON.Position[],
181
- allCoords: [number, number, number][],
182
- bearingSegments: [[number, number, number], [number, number, number]][],
183
- ): void {
184
- const elevated: [number, number, number][] = positions.map(
185
- ([lng, lat, elev = 0]) => [lng, lat, elev],
186
- )
187
- for (const coord of elevated) allCoords.push(coord)
188
- for (let i = 0; i < elevated.length - 1; i++) {
189
- bearingSegments.push([elevated[i], elevated[i + 1]])
190
- }
191
- }
192
-
193
- /** Great-circle bearing from (lat1, lng1) to (lat2, lng2), in degrees 0–360. */
194
- function greatCircleBearing(
195
- lat1: number,
196
- lng1: number,
197
- lat2: number,
198
- lng2: number,
199
- ): number {
200
- const toRad = (d: number) => (d * Math.PI) / 180
201
- const φ1 = toRad(lat1)
202
- const φ2 = toRad(lat2)
203
- const Δλ = toRad(lng2 - lng1)
204
- const y = Math.sin(Δλ) * Math.cos(φ2)
205
- const x =
206
- Math.cos(φ1) * Math.sin(φ2) - Math.sin(φ1) * Math.cos(φ2) * Math.cos(Δλ)
207
- return ((Math.atan2(y, x) * 180) / Math.PI + 360) % 360
208
- }
package/src/index.ts DELETED
@@ -1,15 +0,0 @@
1
- export * from './ElevationProfile'
2
- export * from './FeatureType'
3
- export * from './Lift'
4
- export * from './LiftNameFormatter'
5
- export * from './Place'
6
- export * from './Run'
7
- export * from './RunDifficultyConvention'
8
- export * from './SlopeGradingScale'
9
- export * from './SkiArea'
10
- export * from './SkiPass'
11
- export * from './SnowCoverHistory'
12
- export * from './Source'
13
- export * from './Spot'
14
- export * from './Status'
15
- export * from './ViewportHint'
package/src/testUtils.ts DELETED
@@ -1,5 +0,0 @@
1
- import { ViewportHint } from './ViewportHint'
2
-
3
- export function mockViewportHint(): ViewportHint {
4
- return { center: [0, 0], bearing: null, rotatedWidthMeters: 0, rotatedHeightMeters: 0, minCameraY: 0 }
5
- }
@@ -1,3 +0,0 @@
1
- export const exhaustiveMatchingGuard = (value: never): never => {
2
- throw new Error(`Unhandled case: ${value}`)
3
- }