openskidata-format 9.0.0 → 11.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.
@@ -6,10 +6,12 @@ import { LineString } from 'geojson';
6
6
  * except for the final height which corresponds to the LineString endpoint and may have a different spacing.
7
7
  * Height values can be mapped to geographical coordinates using Turf.js: `turf.lineChunk(geometry, resolution, {units: 'meters'})`.
8
8
  * @property {number} resolution - The horizontal sampling distance in meters between consecutive height measurements.
9
+ * @property {number} targetResolution - The horizontal sampling distance in meters that was requested for the elevation profile. The actual resolution may be less to ensure an integer number of segments along the line. This is used for reconstructing the profile geometry from the feature geometry.
9
10
  */
10
11
  export type ElevationProfile = {
11
12
  heights: number[];
12
13
  resolution: number;
14
+ targetResolution: number;
13
15
  };
14
16
  /**
15
17
  * Elevation data that can be computed on demand fom a Run or Lift feature.
@@ -32,11 +34,33 @@ type PitchData = {
32
34
  pitchCalculationResolutionInMeters: number;
33
35
  };
34
36
  export declare function getElevationData(profileGeometry: GeoJSON.LineString): ElevationData;
35
- export declare function getPitchData(profileGeometry: GeoJSON.LineString, resolutionInMeters?: number): PitchData;
37
+ export declare function getPitchData(profileGeometry: GeoJSON.LineString, minResolutionInMeters?: number): PitchData;
36
38
  export declare function getProfileGeometry(geometry: GeoJSON.LineString, elevationProfile: ElevationProfile): GeoJSON.LineString;
37
39
  /**
38
- * Determines the points to use for an elevation profile.
40
+ * Determines the points to use for an elevation profile, given a minimum horizontal resolution. The resolution is selected to have evenly spaced points along the line.
39
41
  */
40
- export declare function extractPointsForElevationProfile(geometry: LineString, resolution: number): GeoJSON.LineString;
42
+ export declare function extractPointsForElevationProfile(geometry: LineString, minResolutionInMeters: number): {
43
+ resolutionInMeters: number;
44
+ geometry: GeoJSON.LineString;
45
+ };
41
46
  export declare function getAscentAndDescent(profileGeometry: GeoJSON.LineString): AscentDescentData;
47
+ /**
48
+ * Turf's lineChunk has several issues:
49
+ * - it doesn't allow for a custom resolution that divides the line evenly, which is important for consistent pitch calculations
50
+ * change: calculate a resolution that divides the line into an integer number of segments, with a maximum of the provided resolution
51
+ * - it includes elevations from the original geometry on newly created points which are wrong (https://github.com/Turfjs/turf/issues/3007)
52
+ * fix: strip Z coordinates from non-original points
53
+ * - it can produce a last point that is very close but not exactly the same as the original endpoint
54
+ * fix: set the last point to be exactly the original endpoint
55
+ * - it can produce two points very close to the end of line due to floating point precision issues
56
+ * fix: drop the second to last point if it's very close to the end
57
+ *
58
+ * @param geometry
59
+ * @param minResolutionInMeters
60
+ * @returns
61
+ */
62
+ export declare function lineChunkPatched(geometry: LineString, minResolutionInMeters: number): {
63
+ resolutionInMeters: number;
64
+ geometry: GeoJSON.LineString[];
65
+ };
42
66
  export {};
@@ -5,6 +5,7 @@ exports.getPitchData = getPitchData;
5
5
  exports.getProfileGeometry = getProfileGeometry;
6
6
  exports.extractPointsForElevationProfile = extractPointsForElevationProfile;
7
7
  exports.getAscentAndDescent = getAscentAndDescent;
8
+ exports.lineChunkPatched = lineChunkPatched;
8
9
  const tslib_1 = require("tslib");
9
10
  const distance_1 = tslib_1.__importDefault(require("@turf/distance"));
10
11
  const length_1 = tslib_1.__importDefault(require("@turf/length"));
@@ -16,7 +17,7 @@ function getElevationData(profileGeometry) {
16
17
  profileGeometry,
17
18
  };
18
19
  }
19
- function getPitchData(profileGeometry, resolutionInMeters = 25) {
20
+ function getPitchData(profileGeometry, minResolutionInMeters = 25) {
20
21
  const coordinates = profileGeometry.coordinates;
21
22
  if (coordinates[0].length < 3) {
22
23
  throw 'Elevation data is required for slope analysis';
@@ -47,7 +48,7 @@ function getPitchData(profileGeometry, resolutionInMeters = 25) {
47
48
  // If the line is too short relative to the resolution, pitch calculations are unreliable
48
49
  // due to elevation data resolution. A 1m elevation change over 1m distance would be a 45 degree slope,
49
50
  // which can easily happen due to elevation data precision issues.
50
- if (totalLength < resolutionInMeters / 2) {
51
+ if (totalLength < minResolutionInMeters / 2) {
51
52
  return {
52
53
  averagePitchInPercent: null,
53
54
  maxPitchInPercent: null,
@@ -56,18 +57,8 @@ function getPitchData(profileGeometry, resolutionInMeters = 25) {
56
57
  pitchCalculationResolutionInMeters: totalLength,
57
58
  };
58
59
  }
59
- // Calculate a resolution that divides the distance evenly, with the constraint that it must be <= resolutionInMeters
60
- const numSegments = Math.ceil(totalLength / resolutionInMeters);
61
- const actualResolution = totalLength / numSegments;
62
60
  // Chunk the line into segments using the calculated resolution for max pitch calculation
63
- const chunkedGeometries = (0, line_chunk_1.lineChunk)(profileGeometry, actualResolution, {
64
- units: 'meters',
65
- }).features.map((feature) => feature.geometry);
66
- // turf 7.3+ adds Z coordinates to newly created points using segment-end elevation,
67
- // but we need distance-based interpolation. Strip Z from non-original vertices so
68
- // interpolateElevation can add them correctly.
69
- // https://github.com/Turfjs/turf/issues/3007
70
- stripNonOriginalElevations(chunkedGeometries, coordinates);
61
+ const { geometry: chunkedGeometries, resolutionInMeters: actualResolutionInMeters, } = lineChunkPatched(profileGeometry, minResolutionInMeters);
71
62
  // Mutates the geometries in place to add elevation data
72
63
  interpolateElevation(chunkedGeometries);
73
64
  // Calculate max pitch over the evenly-divided chunks
@@ -90,14 +81,18 @@ function getPitchData(profileGeometry, resolutionInMeters = 25) {
90
81
  maxPitchInPercent: maxPitchValue,
91
82
  inclinedLengthInMeters: totalInclinedLength,
92
83
  overallPitchInPercent: overallElevationChange / totalLength,
93
- pitchCalculationResolutionInMeters: actualResolution,
84
+ pitchCalculationResolutionInMeters: actualResolutionInMeters,
94
85
  };
95
86
  }
96
87
  function getProfileGeometry(geometry, elevationProfile) {
97
- const profileLine = extractPointsForElevationProfile(geometry, elevationProfile.resolution);
88
+ const { geometry: profileLine, resolutionInMeters: actualResolution } = extractPointsForElevationProfile(geometry, elevationProfile.targetResolution);
98
89
  if (profileLine.coordinates.length !== elevationProfile.heights.length) {
99
90
  throw `Mismatch of points & elevation profile`;
100
91
  }
92
+ if (Math.abs(actualResolution - elevationProfile.resolution) >
93
+ elevationProfile.resolution * 0.001) {
94
+ throw `Resolution mismatch between profile geometry (${actualResolution}) and elevation profile (${elevationProfile.resolution})`;
95
+ }
101
96
  for (let i = 0; i < profileLine.coordinates.length; i++) {
102
97
  const point = profileLine.coordinates[i];
103
98
  const height = elevationProfile.heights[i];
@@ -106,13 +101,10 @@ function getProfileGeometry(geometry, elevationProfile) {
106
101
  return profileLine;
107
102
  }
108
103
  /**
109
- * Determines the points to use for an elevation profile.
104
+ * Determines the points to use for an elevation profile, given a minimum horizontal resolution. The resolution is selected to have evenly spaced points along the line.
110
105
  */
111
- function extractPointsForElevationProfile(geometry, resolution) {
112
- // Important: Z coordinates in the output of lineChunk may be incorrect, ignore them (https://github.com/Turfjs/turf/issues/3007)
113
- const lineChunks = (0, line_chunk_1.lineChunk)(geometry, resolution, {
114
- units: 'meters',
115
- }).features.map((feature) => feature.geometry);
106
+ function extractPointsForElevationProfile(geometry, minResolutionInMeters) {
107
+ const { geometry: lineChunks, resolutionInMeters } = lineChunkPatched(geometry, minResolutionInMeters);
116
108
  const points = [];
117
109
  for (let subline of lineChunks) {
118
110
  const point = subline.coordinates[0];
@@ -127,8 +119,11 @@ function extractPointsForElevationProfile(geometry, resolution) {
127
119
  }
128
120
  }
129
121
  return {
130
- type: 'LineString',
131
- coordinates: points,
122
+ resolutionInMeters,
123
+ geometry: {
124
+ type: 'LineString',
125
+ coordinates: points,
126
+ },
132
127
  };
133
128
  }
134
129
  function getAscentAndDescent(profileGeometry) {
@@ -169,6 +164,53 @@ function getAscentAndDescent(profileGeometry) {
169
164
  verticalInMeters: result.maxElevation - result.minElevation,
170
165
  };
171
166
  }
167
+ /**
168
+ * Turf's lineChunk has several issues:
169
+ * - it doesn't allow for a custom resolution that divides the line evenly, which is important for consistent pitch calculations
170
+ * change: calculate a resolution that divides the line into an integer number of segments, with a maximum of the provided resolution
171
+ * - it includes elevations from the original geometry on newly created points which are wrong (https://github.com/Turfjs/turf/issues/3007)
172
+ * fix: strip Z coordinates from non-original points
173
+ * - it can produce a last point that is very close but not exactly the same as the original endpoint
174
+ * fix: set the last point to be exactly the original endpoint
175
+ * - it can produce two points very close to the end of line due to floating point precision issues
176
+ * fix: drop the second to last point if it's very close to the end
177
+ *
178
+ * @param geometry
179
+ * @param minResolutionInMeters
180
+ * @returns
181
+ */
182
+ function lineChunkPatched(geometry, minResolutionInMeters) {
183
+ const resolutionInMeters = horizontalResolutionFitting(geometry, minResolutionInMeters);
184
+ const lineChunks = (0, line_chunk_1.lineChunk)(geometry, resolutionInMeters, {
185
+ units: 'meters',
186
+ }).features.map((feature) => feature.geometry);
187
+ // Check the last segment has the expected length
188
+ const lastSegment = lineChunks[lineChunks.length - 1];
189
+ const lastSegmentLength = (0, length_1.default)({ type: 'Feature', geometry: lastSegment, properties: {} }, { units: 'meters' });
190
+ if (lastSegmentLength < resolutionInMeters * 0.01) {
191
+ // If the last segment is very short, which can happen due to floating point precision issues, drop it to avoid issues with elevation interpolation
192
+ lineChunks.pop();
193
+ }
194
+ // Set the last point from the original coordinates, turf line chunk can sometimes produce a last point that
195
+ // is very close but not exactly the same as the original endpoint, which can cause issues with elevation interpolation
196
+ const lastOriginalPoint = geometry.coordinates[geometry.coordinates.length - 1];
197
+ const lastChunkCoords = lineChunks[lineChunks.length - 1].coordinates;
198
+ lastChunkCoords[lastChunkCoords.length - 1] = lastOriginalPoint;
199
+ // turf 7.3+ adds Z coordinates to newly created points using segment-end elevation,
200
+ // but we need distance-based interpolation. Strip Z from non-original vertices so
201
+ // interpolateElevation can add them correctly.
202
+ // https://github.com/Turfjs/turf/issues/3007
203
+ stripNonOriginalElevations(lineChunks, geometry.coordinates);
204
+ return { resolutionInMeters, geometry: lineChunks };
205
+ }
206
+ function horizontalResolutionFitting(geometry, minResolution) {
207
+ const totalLength = (0, length_1.default)({ type: 'Feature', geometry, properties: {} }, { units: 'meters' });
208
+ if (totalLength === 0) {
209
+ return 0;
210
+ }
211
+ const numSegments = Math.ceil(totalLength / minResolution);
212
+ return totalLength / numSegments;
213
+ }
172
214
  /**
173
215
  * Strip Z coordinates from points that were created by lineChunk (not original vertices).
174
216
  * Original vertices are identified by matching lon/lat coordinates.
@@ -224,7 +266,7 @@ function interpolateElevation(geometries) {
224
266
  // Find elevation reference points
225
267
  const referencePoints = allPoints.filter((p) => p.hasElevation);
226
268
  if (referencePoints.length <= 1) {
227
- throw 'At least two points with elevation data are required';
269
+ throw `At least two points with elevation data are required ${JSON.stringify(allPoints)}`;
228
270
  }
229
271
  // Process segments between each pair of reference points
230
272
  for (let i = 0; i < referencePoints.length - 1; i++) {
@@ -1 +1 @@
1
- {"version":3,"file":"ElevationProfile.js","sourceRoot":"","sources":["../src/ElevationProfile.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ElevationProfile.js","sourceRoot":"","sources":["../src/ElevationProfile.ts"],"names":[],"mappings":"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"}
package/dist/Lift.d.ts CHANGED
@@ -40,6 +40,7 @@ export type Access = 'private' | null;
40
40
  * @property {boolean | null} detachable - Whether the lift has detachable grips. Derived from the OpenStreetMap aerialway:detachable tag.
41
41
  * @property {boolean | null} bubble - Whether the lift has bubbles/covers to protect from weather. Derived from the OpenStreetMap aerialway:bubble tag.
42
42
  * @property {boolean | null} heating - Whether the lift has heated carriers/seats. Derived from the OpenStreetMap aerialway:heating tag.
43
+ * @property {boolean | null} tunnel - Whether the lift passes through a tunnel, derived from the OpenStreetMap "tunnel" tag. True for any tunnel value (e.g. "yes", "avalanche_protector", "building_passage"), null if untagged.
43
44
  * @property {LiftStationSpotFeature[]} stations - Lift station spot features associated with this lift.
44
45
  * @property {SkiAreaSummaryFeature[]} skiAreas - Ski areas this lift is a part of.
45
46
  * @property {Source[]} sources - Data sources for the feature.
@@ -64,6 +65,7 @@ export type LiftProperties = {
64
65
  detachable: boolean | null;
65
66
  bubble: boolean | null;
66
67
  heating: boolean | null;
68
+ tunnel: boolean | null;
67
69
  stations: LiftStationSpotFeature[];
68
70
  skiAreas: SkiAreaSummaryFeature[];
69
71
  sources: Source[];
package/dist/Lift.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"Lift.js","sourceRoot":"","sources":["../src/Lift.ts"],"names":[],"mappings":";;;AAmGA,oDAwBC;AAED,oDA6BC;AAED,oCAgBC;AA5KD,yDAAoE;AAMpE,qCAAiC;AACjC,4EAAwE;AAwExE,IAAY,QAaX;AAbD,WAAY,QAAQ;IAClB,kCAAsB,CAAA;IACtB,+BAAmB,CAAA;IACnB,oCAAwB,CAAA;IACxB,oCAAwB,CAAA;IACxB,kCAAsB,CAAA;IACtB,0BAAc,CAAA;IACd,0BAAc,CAAA;IACd,+BAAmB,CAAA;IACnB,gCAAoB,CAAA;IACpB,wCAA4B,CAAA;IAC5B,mCAAuB,CAAA;IACvB,+BAAmB,CAAA;AACrB,CAAC,EAbW,QAAQ,wBAAR,QAAQ,QAanB;AAOD,SAAgB,oBAAoB,CAClC,OAAoB;IAEpB,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAA;IACjC,IACE,CAAC,QAAQ;QACT,QAAQ,CAAC,IAAI,KAAK,YAAY;QAC9B,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,EAClC,CAAC;QACD,OAAO,IAAI,CAAA;IACb,CAAC;IAED,MAAM,aAAa,GAAG,IAAA,mCAAgB,EAAC,QAAQ,CAAC,CAAA;IAChD,MAAM,iBAAiB,GAAG,OAAO,CAAC,UAAU,CAAC,QAAQ,CAAA;IAErD,OAAO;QACL,GAAG,aAAa;QAChB,sBAAsB,EAAE,iBAAiB;YACvC,CAAC,CAAC,aAAa,CAAC,sBAAsB,GAAG,iBAAiB;YAC1D,CAAC,CAAC,IAAI;QACR,8BAA8B,EAAE,iBAAiB;YAC/C,CAAC,CAAC,aAAa,CAAC,gBAAgB,GAAG,iBAAiB;YACpD,CAAC,CAAC,IAAI;KACT,CAAA;AACH,CAAC;AAED,SAAgB,oBAAoB,CAAC,QAAkB;IACrD,QAAQ,QAAQ,EAAE,CAAC;QACjB,KAAK,QAAQ,CAAC,QAAQ;YACpB,OAAO,WAAW,CAAA;QACpB,KAAK,QAAQ,CAAC,OAAO;YACnB,OAAO,SAAS,CAAA;QAClB,KAAK,QAAQ,CAAC,SAAS;YACrB,OAAO,WAAW,CAAA;QACpB,KAAK,QAAQ,CAAC,SAAS;YACrB,OAAO,QAAQ,CAAA;QACjB,KAAK,QAAQ,CAAC,QAAQ;YACpB,OAAO,WAAW,CAAA;QACpB,KAAK,QAAQ,CAAC,IAAI;YAChB,OAAO,OAAO,CAAA;QAChB,KAAK,QAAQ,CAAC,IAAI;YAChB,OAAO,OAAO,CAAA;QAChB,KAAK,QAAQ,CAAC,OAAO;YACnB,OAAO,SAAS,CAAA;QAClB,KAAK,QAAQ,CAAC,OAAO;YACnB,OAAO,SAAS,CAAA;QAClB,KAAK,QAAQ,CAAC,WAAW;YACvB,OAAO,cAAc,CAAA;QACvB,KAAK,QAAQ,CAAC,SAAS;YACrB,OAAO,WAAW,CAAA;QACpB,KAAK,QAAQ,CAAC,OAAO;YACnB,OAAO,SAAS,CAAA;QAClB;YACE,OAAO,IAAA,iDAAuB,EAAC,QAAQ,CAAC,CAAA;IAC5C,CAAC;AACH,CAAC;AAED,SAAgB,YAAY,CAAC,MAAc;IACzC,MAAM,gBAAgB,GAAG,kBAAkB,CAAA;IAC3C,MAAM,aAAa,GAAG,kBAAkB,CAAA;IAExC,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,eAAM,CAAC,OAAO,CAAC;QACpB,KAAK,eAAM,CAAC,SAAS;YACnB,OAAO,aAAa,CAAA;QACtB,KAAK,eAAM,CAAC,QAAQ,CAAC;QACrB,KAAK,eAAM,CAAC,OAAO,CAAC;QACpB,KAAK,eAAM,CAAC,YAAY,CAAC;QACzB,KAAK,eAAM,CAAC,SAAS;YACnB,OAAO,gBAAgB,CAAA;QACzB;YACE,OAAO,IAAA,iDAAuB,EAAC,MAAM,CAAC,CAAA;IAC1C,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"Lift.js","sourceRoot":"","sources":["../src/Lift.ts"],"names":[],"mappings":"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"}
package/dist/Run.d.ts CHANGED
@@ -44,6 +44,7 @@ export type RunFeature = GeoJSON.Feature<RunGeometry, RunProperties>;
44
44
  * @property {boolean | null} patrolled - Whether the run is patrolled by ski patrol, derived from the OpenStreetMap "piste:patrolled" or "patrolled" tag.
45
45
  * @property {boolean | null} snowmaking - Whether the run has its operation secured through snowmaking, derived from the OpenStreetMap "piste:snowmaking" tag.
46
46
  * @property {boolean | null} snowfarming - Whether the run has its early season operation secured through snowfarming (storing snow from previous season), derived from the OpenStreetMap "piste:snowfarming" tag.
47
+ * @property {boolean | null} tunnel - Whether the run passes through a tunnel, derived from the OpenStreetMap "tunnel" tag. True for any tunnel value (e.g. "yes", "avalanche_protector", "building_passage"), null if untagged.
47
48
  * @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".
48
49
  * @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.
49
50
  * @property {ElevationProfile | null} elevationProfile - Elevation profile of the run, only available for runs with LineString geometry.
@@ -69,6 +70,7 @@ export type RunProperties = {
69
70
  patrolled: boolean | null;
70
71
  snowmaking: boolean | null;
71
72
  snowfarming: boolean | null;
73
+ tunnel: boolean | null;
72
74
  grooming: RunGrooming | null;
73
75
  skiAreas: SkiAreaSummaryFeature[];
74
76
  elevationProfile: ElevationProfile | null;
package/dist/Run.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"Run.js","sourceRoot":"","sources":["../src/Run.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Run.js","sourceRoot":"","sources":["../src/Run.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openskidata-format",
3
- "version": "9.0.0",
3
+ "version": "11.0.0",
4
4
  "description": "Data format for OpenSkiMap.org",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -10,6 +10,7 @@
10
10
  ],
11
11
  "scripts": {
12
12
  "build": "rm -rf dist/ && tsc",
13
+ "prepare": "npm run build",
13
14
  "release": "npm run-script build && npm publish",
14
15
  "test": "jest",
15
16
  "test:watch": "jest --watch",
@@ -1,16 +1,13 @@
1
+ import length from '@turf/length'
1
2
  import {
2
3
  extractPointsForElevationProfile,
3
4
  getAscentAndDescent,
4
5
  getPitchData,
6
+ lineChunkPatched,
5
7
  } from './ElevationProfile'
6
8
  import { FeatureType } from './FeatureType'
7
9
  import { getLiftElevationData, LiftFeature, LiftType } from './Lift'
8
- import {
9
- getRunElevationData,
10
- RunDifficulty,
11
- RunFeature,
12
- RunUse,
13
- } from './Run'
10
+ import { getRunElevationData, RunDifficulty, RunFeature, RunUse } from './Run'
14
11
  import { RunDifficultyConvention } from './RunDifficultyConvention'
15
12
  import { Status } from './Status'
16
13
 
@@ -75,10 +72,10 @@ describe('ElevationProfile', () => {
75
72
  const result = getPitchData(profileGeometry)
76
73
 
77
74
  // With the new resolution calculation, the actual resolution divides the distance evenly
78
- // Total length is ~60.44m, with default resolution of 25m: numSegments = ceil(60.44/25) = 3, actualResolution = 60.44/3 ≈ 20.15m
79
- expect(result.pitchCalculationResolutionInMeters).toBeCloseTo(20.15, 1)
75
+ // Total length is ~60.44m, with default resolution of 25m: numSegments = ceil(60.44/25) = 3, actualResolution = 60.44/3 ≈ 20.14m
76
+ expect(result.pitchCalculationResolutionInMeters).toBeCloseTo(20.14)
80
77
  expect(result.pitchCalculationResolutionInMeters).toBeLessThanOrEqual(25)
81
- expect(result.maxPitchInPercent).toBeCloseTo(0.489, 2)
78
+ expect(result.maxPitchInPercent).toBeCloseTo(0.489)
82
79
  expect(result.averagePitchInPercent).toBeCloseTo(0.2482)
83
80
  expect(result.inclinedLengthInMeters).toBeCloseTo(63.0597)
84
81
  })
@@ -119,8 +116,8 @@ describe('ElevationProfile', () => {
119
116
 
120
117
  // With ~222m total length and 50m input resolution:
121
118
  // numSegments = ceil(222/50) = 5
122
- // actualResolution = 222/5 = 44.4m
123
- expect(result.pitchCalculationResolutionInMeters).toBeCloseTo(44.4, 0)
119
+ // actualResolution = 222/5 44.48m
120
+ expect(result.pitchCalculationResolutionInMeters).toBeCloseTo(44.48)
124
121
  })
125
122
 
126
123
  it('uses total length as resolution when line is shorter than input resolution', () => {
@@ -135,8 +132,8 @@ describe('ElevationProfile', () => {
135
132
  const result = getPitchData(profileGeometry, 50)
136
133
 
137
134
  // Resolution should equal the total length since numSegments = ceil(11/50) = 1
138
- // actualResolution = 11/1 = 11m
139
- expect(result.pitchCalculationResolutionInMeters).toBeCloseTo(11.1, 0)
135
+ // actualResolution = 11/1 11.12m
136
+ expect(result.pitchCalculationResolutionInMeters).toBeCloseTo(11.12)
140
137
  expect(result.pitchCalculationResolutionInMeters).toBeLessThanOrEqual(50)
141
138
  })
142
139
 
@@ -156,7 +153,7 @@ describe('ElevationProfile', () => {
156
153
  // With evenly divided chunks, no chunks should be skipped
157
154
  // The max pitch should be calculated from all segments
158
155
  expect(result.maxPitchInPercent).toBeGreaterThan(0)
159
- expect(result.pitchCalculationResolutionInMeters).toBeCloseTo(20.15, 1)
156
+ expect(result.pitchCalculationResolutionInMeters).toBeCloseTo(20.14)
160
157
  })
161
158
 
162
159
  it('returns null pitch data for very short lines', () => {
@@ -172,7 +169,7 @@ describe('ElevationProfile', () => {
172
169
 
173
170
  // Lines shorter than half the resolution should return null pitch data
174
171
  // because elevation data resolution makes pitch calculations unreliable
175
- expect(result.pitchCalculationResolutionInMeters).toBeCloseTo(1.11, 1)
172
+ expect(result.pitchCalculationResolutionInMeters).toBeCloseTo(1.11)
176
173
  expect(result.maxPitchInPercent).toBeNull()
177
174
  expect(result.averagePitchInPercent).toBeNull()
178
175
  expect(result.overallPitchInPercent).toBeNull()
@@ -191,7 +188,7 @@ describe('ElevationProfile', () => {
191
188
  const result = getPitchData(profileGeometry, 25)
192
189
 
193
190
  // Lines at or above half the resolution should calculate pitch data
194
- expect(result.pitchCalculationResolutionInMeters).toBeCloseTo(16.7, 0)
191
+ expect(result.pitchCalculationResolutionInMeters).toBeCloseTo(16.68)
195
192
  expect(result.maxPitchInPercent).not.toBeNull()
196
193
  expect(result.averagePitchInPercent).not.toBeNull()
197
194
  expect(result.overallPitchInPercent).not.toBeNull()
@@ -211,13 +208,13 @@ describe('ElevationProfile', () => {
211
208
  // With 20m resolution, there should be 12 points
212
209
  const result = extractPointsForElevationProfile(geometry, 20)
213
210
 
214
- expect(result.coordinates.length).toEqual(12)
215
- expect(result.coordinates[0]).toEqual([
211
+ expect(result.geometry.coordinates.length).toEqual(12)
212
+ expect(result.geometry.coordinates[0]).toEqual([
216
213
  11.177452968770694, 47.312650638218656,
217
214
  ])
218
- expect(result.coordinates[result.coordinates.length - 1]).toEqual([
219
- 11.175409464719593, 47.31138883724759,
220
- ])
215
+ expect(
216
+ result.geometry.coordinates[result.geometry.coordinates.length - 1],
217
+ ).toEqual([11.175409464719593, 47.31138883724759])
221
218
  })
222
219
 
223
220
  it('handles points that already have elevation', () => {
@@ -232,9 +229,9 @@ describe('ElevationProfile', () => {
232
229
  const result = extractPointsForElevationProfile(geometry, 20)
233
230
 
234
231
  // Should still work properly, returning 2D points regardless of input dimension
235
- expect(result.coordinates.length).toBeGreaterThan(1)
236
- expect(result.coordinates[0].length).toBe(2) // Should always return 2D points
237
- expect(result.coordinates[0]).toEqual([
232
+ expect(result.geometry.coordinates.length).toBeGreaterThan(1)
233
+ expect(result.geometry.coordinates[0].length).toBe(2) // Should always return 2D points
234
+ expect(result.geometry.coordinates[0]).toEqual([
238
235
  11.177452968770694, 47.312650638218656,
239
236
  ])
240
237
  })
@@ -278,6 +275,7 @@ describe('ElevationProfile', () => {
278
275
  grooming: null,
279
276
  websites: [],
280
277
  wikidataID: null,
278
+ tunnel: null,
281
279
  places: [],
282
280
  },
283
281
  }
@@ -317,6 +315,7 @@ describe('ElevationProfile', () => {
317
315
  grooming: null,
318
316
  websites: [],
319
317
  wikidataID: null,
318
+ tunnel: null,
320
319
  places: [],
321
320
  },
322
321
  }
@@ -331,7 +330,12 @@ describe('ElevationProfile', () => {
331
330
  type: 'Feature',
332
331
  geometry: {
333
332
  type: 'MultiLineString',
334
- coordinates: [[[0, 0, 100], [1, 1, 200]]],
333
+ coordinates: [
334
+ [
335
+ [0, 0, 100],
336
+ [1, 1, 200],
337
+ ],
338
+ ],
335
339
  },
336
340
  properties: {
337
341
  type: FeatureType.Lift,
@@ -355,6 +359,7 @@ describe('ElevationProfile', () => {
355
359
  stations: [],
356
360
  websites: [],
357
361
  wikidataID: null,
362
+ tunnel: null,
358
363
  places: [],
359
364
  },
360
365
  }
@@ -395,11 +400,13 @@ describe('ElevationProfile', () => {
395
400
  1574, 1572, 1571, 1571, 1572, 1572, 1573, 1574, 1573, 1572, 1572,
396
401
  1573, 1575, 1576, 1577, 1580, 1583, 1584, 1586,
397
402
  ],
398
- resolution: 25,
403
+ resolution: 24.737575778032642,
404
+ targetResolution: 25,
399
405
  },
400
406
  sources: [],
401
407
  websites: [],
402
408
  wikidataID: null,
409
+ tunnel: null,
403
410
  places: [],
404
411
  },
405
412
  geometry: {
@@ -472,7 +479,255 @@ describe('ElevationProfile', () => {
472
479
  } as RunFeature
473
480
 
474
481
  const result = getRunElevationData(runFeature)
475
- expect(result?.maxPitchInPercent).toBeCloseTo(0.12, 2)
482
+ expect(result?.maxPitchInPercent).toBeCloseTo(0.12)
483
+ })
484
+ })
485
+
486
+ describe('lineChunkPatched', () => {
487
+ it('chunks a line into evenly spaced segments', () => {
488
+ const geometry: GeoJSON.LineString = {
489
+ type: 'LineString',
490
+ coordinates: [
491
+ [0, 0, 100],
492
+ [0, 0.001, 110], // ~111m distance
493
+ [0, 0.002, 105], // ~111m distance
494
+ ],
495
+ }
496
+
497
+ const result = lineChunkPatched(geometry, 50)
498
+
499
+ // Total length ~222m, with 50m input resolution:
500
+ // numSegments = ceil(222/50) = 5, actualResolution = 222/5 ≈ 44.48m
501
+ expect(result.resolutionInMeters).toBeCloseTo(44.48)
502
+ expect(result.geometry.length).toBe(5)
503
+ })
504
+
505
+ it('drops very short last segment caused by floating point precision issues', () => {
506
+ // This geometry used to produce a 0-length last segment
507
+ const geometry: GeoJSON.LineString = {
508
+ type: 'LineString',
509
+ coordinates: [
510
+ [11.0796655, 47.4527256, 1692.2],
511
+ [11.079720619903608, 47.45294351099136, 1681.8],
512
+ [11.079741829533312, 47.4531647479081, 1668.6],
513
+ [11.079727503541202, 47.453383892073894, 1656.7],
514
+ [11.07965005139028, 47.453599318647775, 1644.3],
515
+ [11.079572598604779, 47.4538147451695, 1630.9],
516
+ [11.07942834590789, 47.45401183160125, 1618.7],
517
+ [11.079261771477254, 47.45420278959679, 1607.3],
518
+ [11.079114389973567, 47.454399266146474, 1594.7],
519
+ [11.0790705, 47.45461691834462, 1583.1],
520
+ [11.079181898893397, 47.45481861566543, 1576.6],
521
+ [11.079441578857713, 47.45494597233321, 1569.5],
522
+ [11.079759061257176, 47.454999538815876, 1562],
523
+ [11.080078284221905, 47.4550501436389, 1556.1],
524
+ [11.080397507801036, 47.45510074757592, 1552.5],
525
+ [11.080713989344984, 47.45515789593482, 1548.1],
526
+ [11.081008027508169, 47.45525121278351, 1543.2],
527
+ [11.081212874246143, 47.455416412781105, 1540.9],
528
+ [11.081238270576348, 47.455636585144894, 1535.5],
529
+ [11.081208499760809, 47.45585723881769, 1526.8],
530
+ [11.081175991164459, 47.456077847750485, 1510.4],
531
+ [11.081099444790018, 47.456292146692356, 1497.9],
532
+ [11.080996819007606, 47.45650270883295, 1484.3],
533
+ [11.080897169545302, 47.45671391641284, 1472.4],
534
+ [11.080799550285963, 47.45692556428702, 1456.4],
535
+ [11.080701930240755, 47.457137212078344, 1443.9],
536
+ [11.08059047871202, 47.4573452579998, 1434.7],
537
+ [11.080452900000004, 47.45754649999998, 1427.4],
538
+ ],
539
+ }
540
+
541
+ const result = lineChunkPatched(geometry, 25)
542
+
543
+ // Verify that all segments have correct length
544
+ for (const chunk of result.geometry) {
545
+ const chunkLength = length(
546
+ { type: 'Feature', geometry: chunk, properties: {} },
547
+ { units: 'meters' },
548
+ )
549
+ expect(chunkLength).toBeCloseTo(result.resolutionInMeters)
550
+ }
551
+ })
552
+
553
+ it('preserves the exact last point from the original geometry', () => {
554
+ // This geometry had a split last point that was different from the input last point
555
+ const geometry: GeoJSON.LineString = {
556
+ type: 'LineString',
557
+ coordinates: [
558
+ [11.1215994, 47.466431, 886.6],
559
+ [11.118092699999998, 47.4702057, 768.5],
560
+ ],
561
+ }
562
+
563
+ const result = lineChunkPatched(geometry, 25)
564
+
565
+ // The last point of the last chunk should be exactly the same as the original last point
566
+ const lastChunk = result.geometry[result.geometry.length - 1]
567
+ const lastPoint = lastChunk.coordinates[lastChunk.coordinates.length - 1]
568
+ const originalLastPoint =
569
+ geometry.coordinates[geometry.coordinates.length - 1]
570
+
571
+ expect(lastPoint[0]).toBe(originalLastPoint[0])
572
+ expect(lastPoint[1]).toBe(originalLastPoint[1])
573
+ expect(lastPoint[2]).toBe(originalLastPoint[2])
574
+ })
575
+
576
+ it('strips elevation from non-original points', () => {
577
+ const geometry: GeoJSON.LineString = {
578
+ type: 'LineString',
579
+ coordinates: [
580
+ [0, 0, 100],
581
+ [0, 0.001, 110], // ~111m distance
582
+ [0, 0.002, 120], // ~111m distance
583
+ ],
584
+ }
585
+
586
+ const result = lineChunkPatched(geometry, 50)
587
+
588
+ // Check that interpolated points (not from original geometry) don't have elevation
589
+ for (const chunk of result.geometry) {
590
+ for (const point of chunk.coordinates) {
591
+ const key = `${point[0]},${point[1]}`
592
+ const originalKey = geometry.coordinates.some(
593
+ (c) => `${c[0]},${c[1]}` === key,
594
+ )
595
+
596
+ if (!originalKey) {
597
+ // Non-original points should not have elevation (should be 2D)
598
+ expect(point.length).toBe(2)
599
+ } else {
600
+ // Original points should still have elevation
601
+ expect(point.length).toBe(3)
602
+ }
603
+ }
604
+ }
605
+ })
606
+
607
+ it('handles very short lines', () => {
608
+ const geometry: GeoJSON.LineString = {
609
+ type: 'LineString',
610
+ coordinates: [
611
+ [0, 0, 100],
612
+ [0, 0.00001, 101], // ~1.11m distance
613
+ ],
614
+ }
615
+
616
+ const result = lineChunkPatched(geometry, 25)
617
+
618
+ // Should create a single chunk for very short lines
619
+ expect(result.geometry.length).toBe(1)
620
+ expect(result.resolutionInMeters).toBeCloseTo(1.11)
621
+ })
622
+
623
+ it('handles lines with exact multiples of resolution', () => {
624
+ const geometry: GeoJSON.LineString = {
625
+ type: 'LineString',
626
+ coordinates: [
627
+ [0, 0, 100],
628
+ [0, 0.0009, 110],
629
+ ],
630
+ }
631
+
632
+ const result = lineChunkPatched(geometry, 50)
633
+
634
+ // The actual distance is calculated and divided evenly
635
+ // Resolution should be <= 50m
636
+ expect(result.resolutionInMeters).toBeLessThanOrEqual(50)
637
+ expect(result.geometry.length).toBeGreaterThanOrEqual(1)
638
+ })
639
+
640
+ it('returns first and last points matching original geometry', () => {
641
+ const geometry: GeoJSON.LineString = {
642
+ type: 'LineString',
643
+ coordinates: [
644
+ [11.177425600412874, 47.31265682344346, 100],
645
+ [11.177224899122194, 47.312533118812354, 110],
646
+ [11.176823496540862, 47.31229807921545, 105],
647
+ ],
648
+ }
649
+
650
+ const result = lineChunkPatched(geometry, 25)
651
+
652
+ // First point of first chunk should match original first point
653
+ const firstPoint = result.geometry[0].coordinates[0]
654
+ expect(firstPoint[0]).toBe(geometry.coordinates[0][0])
655
+ expect(firstPoint[1]).toBe(geometry.coordinates[0][1])
656
+ expect(firstPoint[2]).toBe(geometry.coordinates[0][2])
657
+
658
+ // Last point of last chunk should match original last point
659
+ const lastChunk = result.geometry[result.geometry.length - 1]
660
+ const lastPoint = lastChunk.coordinates[lastChunk.coordinates.length - 1]
661
+ const originalLastPoint =
662
+ geometry.coordinates[geometry.coordinates.length - 1]
663
+ expect(lastPoint[0]).toBe(originalLastPoint[0])
664
+ expect(lastPoint[1]).toBe(originalLastPoint[1])
665
+ expect(lastPoint[2]).toBe(originalLastPoint[2])
666
+ })
667
+
668
+ it('calculates resolution that is less than or equal to requested resolution', () => {
669
+ const geometry: GeoJSON.LineString = {
670
+ type: 'LineString',
671
+ coordinates: [
672
+ [0, 0, 100],
673
+ [0, 0.0015, 110], // ~166.7m distance
674
+ ],
675
+ }
676
+
677
+ const result = lineChunkPatched(geometry, 50)
678
+
679
+ // Resolution should never exceed the requested minimum resolution
680
+ expect(result.resolutionInMeters).toBeLessThanOrEqual(50)
681
+ })
682
+
683
+ it('handles zero-length segments in original geometry', () => {
684
+ const geometry: GeoJSON.LineString = {
685
+ type: 'LineString',
686
+ coordinates: [
687
+ [0, 0, 100],
688
+ [0, 0, 100], // Duplicate point (0 length segment)
689
+ [0, 0.001, 110],
690
+ ],
691
+ }
692
+
693
+ const result = lineChunkPatched(geometry, 25)
694
+
695
+ // Should still work and produce valid chunks
696
+ expect(result.geometry.length).toBeGreaterThan(0)
697
+ expect(result.resolutionInMeters).toBeGreaterThan(0)
698
+ })
699
+
700
+ it('preserves all original points that fall on chunk boundaries', () => {
701
+ const geometry: GeoJSON.LineString = {
702
+ type: 'LineString',
703
+ coordinates: [
704
+ [0, 0, 100],
705
+ [0, 0.0005, 105], // ~55.6m - should be close to a chunk boundary with 50m resolution
706
+ [0, 0.001, 110],
707
+ ],
708
+ }
709
+
710
+ const result = lineChunkPatched(geometry, 50)
711
+
712
+ // Count how many original points are preserved in the chunks
713
+ let preservedOriginalPoints = 0
714
+ for (const chunk of result.geometry) {
715
+ for (const point of chunk.coordinates) {
716
+ if (
717
+ geometry.coordinates.some(
718
+ (c) => c[0] === point[0] && c[1] === point[1],
719
+ )
720
+ ) {
721
+ preservedOriginalPoints++
722
+ }
723
+ }
724
+ }
725
+
726
+ // All original points should be present somewhere in the chunks
727
+ // (Note: points may appear multiple times if they're at chunk boundaries)
728
+ expect(preservedOriginalPoints).toBeGreaterThanOrEqual(
729
+ geometry.coordinates.length,
730
+ )
476
731
  })
477
732
  })
478
733
  })
@@ -10,10 +10,12 @@ import { LineString } from 'geojson'
10
10
  * except for the final height which corresponds to the LineString endpoint and may have a different spacing.
11
11
  * Height values can be mapped to geographical coordinates using Turf.js: `turf.lineChunk(geometry, resolution, {units: 'meters'})`.
12
12
  * @property {number} resolution - The horizontal sampling distance in meters between consecutive height measurements.
13
+ * @property {number} targetResolution - The horizontal sampling distance in meters that was requested for the elevation profile. The actual resolution may be less to ensure an integer number of segments along the line. This is used for reconstructing the profile geometry from the feature geometry.
13
14
  */
14
15
  export type ElevationProfile = {
15
16
  heights: number[]
16
17
  resolution: number
18
+ targetResolution: number
17
19
  }
18
20
 
19
21
  /**
@@ -52,7 +54,7 @@ export function getElevationData(
52
54
 
53
55
  export function getPitchData(
54
56
  profileGeometry: GeoJSON.LineString,
55
- resolutionInMeters: number = 25, // Default resolution of 25 meters for pitch calculation
57
+ minResolutionInMeters: number = 25, // Default resolution of 25 meters for pitch calculation
56
58
  ): PitchData {
57
59
  const coordinates = profileGeometry.coordinates
58
60
  if (coordinates[0].length < 3) {
@@ -90,7 +92,7 @@ export function getPitchData(
90
92
  // If the line is too short relative to the resolution, pitch calculations are unreliable
91
93
  // due to elevation data resolution. A 1m elevation change over 1m distance would be a 45 degree slope,
92
94
  // which can easily happen due to elevation data precision issues.
93
- if (totalLength < resolutionInMeters / 2) {
95
+ if (totalLength < minResolutionInMeters / 2) {
94
96
  return {
95
97
  averagePitchInPercent: null,
96
98
  maxPitchInPercent: null,
@@ -100,20 +102,11 @@ export function getPitchData(
100
102
  }
101
103
  }
102
104
 
103
- // Calculate a resolution that divides the distance evenly, with the constraint that it must be <= resolutionInMeters
104
- const numSegments = Math.ceil(totalLength / resolutionInMeters)
105
- const actualResolution = totalLength / numSegments
106
-
107
105
  // Chunk the line into segments using the calculated resolution for max pitch calculation
108
- const chunkedGeometries = lineChunk(profileGeometry, actualResolution, {
109
- units: 'meters',
110
- }).features.map((feature) => feature.geometry)
111
-
112
- // turf 7.3+ adds Z coordinates to newly created points using segment-end elevation,
113
- // but we need distance-based interpolation. Strip Z from non-original vertices so
114
- // interpolateElevation can add them correctly.
115
- // https://github.com/Turfjs/turf/issues/3007
116
- stripNonOriginalElevations(chunkedGeometries, coordinates)
106
+ const {
107
+ geometry: chunkedGeometries,
108
+ resolutionInMeters: actualResolutionInMeters,
109
+ } = lineChunkPatched(profileGeometry, minResolutionInMeters)
117
110
 
118
111
  // Mutates the geometries in place to add elevation data
119
112
  interpolateElevation(chunkedGeometries)
@@ -148,7 +141,7 @@ export function getPitchData(
148
141
  maxPitchInPercent: maxPitchValue,
149
142
  inclinedLengthInMeters: totalInclinedLength,
150
143
  overallPitchInPercent: overallElevationChange / totalLength,
151
- pitchCalculationResolutionInMeters: actualResolution,
144
+ pitchCalculationResolutionInMeters: actualResolutionInMeters,
152
145
  }
153
146
  }
154
147
 
@@ -156,14 +149,22 @@ export function getProfileGeometry(
156
149
  geometry: GeoJSON.LineString,
157
150
  elevationProfile: ElevationProfile,
158
151
  ): GeoJSON.LineString {
159
- const profileLine = extractPointsForElevationProfile(
160
- geometry,
161
- elevationProfile.resolution,
162
- )
152
+ const { geometry: profileLine, resolutionInMeters: actualResolution } =
153
+ extractPointsForElevationProfile(
154
+ geometry,
155
+ elevationProfile.targetResolution,
156
+ )
163
157
  if (profileLine.coordinates.length !== elevationProfile.heights.length) {
164
158
  throw `Mismatch of points & elevation profile`
165
159
  }
166
160
 
161
+ if (
162
+ Math.abs(actualResolution - elevationProfile.resolution) >
163
+ elevationProfile.resolution * 0.001
164
+ ) {
165
+ throw `Resolution mismatch between profile geometry (${actualResolution}) and elevation profile (${elevationProfile.resolution})`
166
+ }
167
+
167
168
  for (let i = 0; i < profileLine.coordinates.length; i++) {
168
169
  const point = profileLine.coordinates[i]
169
170
  const height = elevationProfile.heights[i]
@@ -174,16 +175,16 @@ export function getProfileGeometry(
174
175
  }
175
176
 
176
177
  /**
177
- * Determines the points to use for an elevation profile.
178
+ * Determines the points to use for an elevation profile, given a minimum horizontal resolution. The resolution is selected to have evenly spaced points along the line.
178
179
  */
179
180
  export function extractPointsForElevationProfile(
180
181
  geometry: LineString,
181
- resolution: number,
182
- ): GeoJSON.LineString {
183
- // Important: Z coordinates in the output of lineChunk may be incorrect, ignore them (https://github.com/Turfjs/turf/issues/3007)
184
- const lineChunks = lineChunk(geometry, resolution, {
185
- units: 'meters',
186
- }).features.map((feature) => feature.geometry)
182
+ minResolutionInMeters: number,
183
+ ): { resolutionInMeters: number; geometry: GeoJSON.LineString } {
184
+ const { geometry: lineChunks, resolutionInMeters } = lineChunkPatched(
185
+ geometry,
186
+ minResolutionInMeters,
187
+ )
187
188
 
188
189
  const points: GeoJSON.Position[] = []
189
190
  for (let subline of lineChunks) {
@@ -200,8 +201,11 @@ export function extractPointsForElevationProfile(
200
201
  }
201
202
 
202
203
  return {
203
- type: 'LineString',
204
- coordinates: points,
204
+ resolutionInMeters,
205
+ geometry: {
206
+ type: 'LineString',
207
+ coordinates: points,
208
+ },
205
209
  }
206
210
  }
207
211
 
@@ -256,6 +260,78 @@ export function getAscentAndDescent(
256
260
  }
257
261
  }
258
262
 
263
+ /**
264
+ * Turf's lineChunk has several issues:
265
+ * - it doesn't allow for a custom resolution that divides the line evenly, which is important for consistent pitch calculations
266
+ * change: calculate a resolution that divides the line into an integer number of segments, with a maximum of the provided resolution
267
+ * - it includes elevations from the original geometry on newly created points which are wrong (https://github.com/Turfjs/turf/issues/3007)
268
+ * fix: strip Z coordinates from non-original points
269
+ * - it can produce a last point that is very close but not exactly the same as the original endpoint
270
+ * fix: set the last point to be exactly the original endpoint
271
+ * - it can produce two points very close to the end of line due to floating point precision issues
272
+ * fix: drop the second to last point if it's very close to the end
273
+ *
274
+ * @param geometry
275
+ * @param minResolutionInMeters
276
+ * @returns
277
+ */
278
+ export function lineChunkPatched(
279
+ geometry: LineString,
280
+ minResolutionInMeters: number,
281
+ ): { resolutionInMeters: number; geometry: GeoJSON.LineString[] } {
282
+ const resolutionInMeters = horizontalResolutionFitting(
283
+ geometry,
284
+ minResolutionInMeters,
285
+ )
286
+
287
+ const lineChunks = lineChunk(geometry, resolutionInMeters, {
288
+ units: 'meters',
289
+ }).features.map((feature) => feature.geometry)
290
+
291
+ // Check the last segment has the expected length
292
+ const lastSegment = lineChunks[lineChunks.length - 1]
293
+ const lastSegmentLength = length(
294
+ { type: 'Feature', geometry: lastSegment, properties: {} },
295
+ { units: 'meters' },
296
+ )
297
+ if (lastSegmentLength < resolutionInMeters * 0.01) {
298
+ // If the last segment is very short, which can happen due to floating point precision issues, drop it to avoid issues with elevation interpolation
299
+ lineChunks.pop()
300
+ }
301
+
302
+ // Set the last point from the original coordinates, turf line chunk can sometimes produce a last point that
303
+ // is very close but not exactly the same as the original endpoint, which can cause issues with elevation interpolation
304
+ const lastOriginalPoint =
305
+ geometry.coordinates[geometry.coordinates.length - 1]
306
+ const lastChunkCoords = lineChunks[lineChunks.length - 1].coordinates
307
+ lastChunkCoords[lastChunkCoords.length - 1] = lastOriginalPoint
308
+
309
+ // turf 7.3+ adds Z coordinates to newly created points using segment-end elevation,
310
+ // but we need distance-based interpolation. Strip Z from non-original vertices so
311
+ // interpolateElevation can add them correctly.
312
+ // https://github.com/Turfjs/turf/issues/3007
313
+ stripNonOriginalElevations(lineChunks, geometry.coordinates)
314
+
315
+ return { resolutionInMeters, geometry: lineChunks }
316
+ }
317
+
318
+ function horizontalResolutionFitting(
319
+ geometry: LineString,
320
+ minResolution: number,
321
+ ): number {
322
+ const totalLength = length(
323
+ { type: 'Feature', geometry, properties: {} },
324
+ { units: 'meters' },
325
+ )
326
+
327
+ if (totalLength === 0) {
328
+ return 0
329
+ }
330
+
331
+ const numSegments = Math.ceil(totalLength / minResolution)
332
+ return totalLength / numSegments
333
+ }
334
+
259
335
  /**
260
336
  * Strip Z coordinates from points that were created by lineChunk (not original vertices).
261
337
  * Original vertices are identified by matching lon/lat coordinates.
@@ -328,7 +404,7 @@ function interpolateElevation(geometries: GeoJSON.LineString[]) {
328
404
  const referencePoints = allPoints.filter((p) => p.hasElevation)
329
405
 
330
406
  if (referencePoints.length <= 1) {
331
- throw 'At least two points with elevation data are required'
407
+ throw `At least two points with elevation data are required ${JSON.stringify(allPoints)}`
332
408
  }
333
409
 
334
410
  // Process segments between each pair of reference points
package/src/Lift.ts CHANGED
@@ -45,6 +45,7 @@ export type Access = 'private' | null
45
45
  * @property {boolean | null} detachable - Whether the lift has detachable grips. Derived from the OpenStreetMap aerialway:detachable tag.
46
46
  * @property {boolean | null} bubble - Whether the lift has bubbles/covers to protect from weather. Derived from the OpenStreetMap aerialway:bubble tag.
47
47
  * @property {boolean | null} heating - Whether the lift has heated carriers/seats. Derived from the OpenStreetMap aerialway:heating tag.
48
+ * @property {boolean | null} tunnel - Whether the lift passes through a tunnel, derived from the OpenStreetMap "tunnel" tag. True for any tunnel value (e.g. "yes", "avalanche_protector", "building_passage"), null if untagged.
48
49
  * @property {LiftStationSpotFeature[]} stations - Lift station spot features associated with this lift.
49
50
  * @property {SkiAreaSummaryFeature[]} skiAreas - Ski areas this lift is a part of.
50
51
  * @property {Source[]} sources - Data sources for the feature.
@@ -69,6 +70,7 @@ export type LiftProperties = {
69
70
  detachable: boolean | null
70
71
  bubble: boolean | null
71
72
  heating: boolean | null
73
+ tunnel: boolean | null
72
74
  stations: LiftStationSpotFeature[]
73
75
  skiAreas: SkiAreaSummaryFeature[]
74
76
  sources: Source[]
package/src/Run.ts CHANGED
@@ -53,6 +53,7 @@ export type RunFeature = GeoJSON.Feature<RunGeometry, RunProperties>
53
53
  * @property {boolean | null} patrolled - Whether the run is patrolled by ski patrol, derived from the OpenStreetMap "piste:patrolled" or "patrolled" tag.
54
54
  * @property {boolean | null} snowmaking - Whether the run has its operation secured through snowmaking, derived from the OpenStreetMap "piste:snowmaking" tag.
55
55
  * @property {boolean | null} snowfarming - Whether the run has its early season operation secured through snowfarming (storing snow from previous season), derived from the OpenStreetMap "piste:snowfarming" tag.
56
+ * @property {boolean | null} tunnel - Whether the run passes through a tunnel, derived from the OpenStreetMap "tunnel" tag. True for any tunnel value (e.g. "yes", "avalanche_protector", "building_passage"), null if untagged.
56
57
  * @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".
57
58
  * @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.
58
59
  * @property {ElevationProfile | null} elevationProfile - Elevation profile of the run, only available for runs with LineString geometry.
@@ -78,6 +79,7 @@ export type RunProperties = {
78
79
  patrolled: boolean | null
79
80
  snowmaking: boolean | null
80
81
  snowfarming: boolean | null
82
+ tunnel: boolean | null
81
83
  grooming: RunGrooming | null
82
84
  skiAreas: SkiAreaSummaryFeature[]
83
85
  elevationProfile: ElevationProfile | null
@@ -28,6 +28,7 @@ describe("getEstimatedRunDifficulty", () => {
28
28
  sources: [],
29
29
  websites: [],
30
30
  wikidataID: null,
31
+ tunnel: null,
31
32
  places: [],
32
33
  },
33
34
  });
@@ -61,6 +62,7 @@ describe("getEstimatedRunDifficulty", () => {
61
62
  sources: [],
62
63
  websites: [],
63
64
  wikidataID: null,
65
+ tunnel: null,
64
66
  places: [],
65
67
  },
66
68
  });
@@ -98,6 +100,7 @@ describe("getEstimatedRunDifficulty", () => {
98
100
  sources: [],
99
101
  websites: [],
100
102
  wikidataID: null,
103
+ tunnel: null,
101
104
  places: [],
102
105
  },
103
106
  });
@@ -133,6 +136,7 @@ describe("getEstimatedRunDifficulty", () => {
133
136
  sources: [],
134
137
  websites: [],
135
138
  wikidataID: null,
139
+ tunnel: null,
136
140
  places: [],
137
141
  },
138
142
  });