@wemap/routers 12.7.3 → 12.7.5

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/index.ts CHANGED
@@ -6,7 +6,7 @@ export { default as Leg, type LegJson, type DestinationJson, type TransportInfo
6
6
  export type { Step, MinStepInfo, StepJson } from './src/model/Step.js';
7
7
  export type { LevelChange } from './src/model/LevelChange.js';
8
8
  export type { StepExtra } from './src/model/StepExtra.js';
9
- export { type TransitMode } from './src/model/TransitMode.js';
9
+ export { type TransitMode, areTransitAndTravelModeConsistent } from './src/model/TransitMode.js';
10
10
  export { type TravelMode, type TravelModePreference } from './src/model/TravelMode.js';
11
11
 
12
12
  /** Graph */
package/package.json CHANGED
@@ -12,7 +12,7 @@
12
12
  "directory": "packages/routers"
13
13
  },
14
14
  "name": "@wemap/routers",
15
- "version": "12.7.3",
15
+ "version": "12.7.5",
16
16
  "bugs": {
17
17
  "url": "https://github.com/wemap/wemap-modules-js/issues"
18
18
  },
@@ -52,5 +52,5 @@
52
52
  },
53
53
  "./helpers/*": "./helpers/*"
54
54
  },
55
- "gitHead": "67ad72998468c84862793eb326649b6923b0f6ec"
55
+ "gitHead": "b35505c8f8fbcd73aa7203c315fdc2fbaca129e1"
56
56
  }
@@ -72,7 +72,20 @@ class Graph {
72
72
 
73
73
  const useMultiLevelSegments = !('useMultiLevelSegments' in options) || options.useMultiLevelSegments;
74
74
 
75
- let bestProjection: GraphProjection | null = null;
75
+ let bestProjection: GraphProjection<U> | null = null;
76
+
77
+ // (for as any) See. https://www.totaltypescript.com/any-considered-harmful#returning-conditional-types-from-generic-functions
78
+ const adaptProjectionCoords = (projCoords: Coordinates): U => {
79
+ if (!(origin instanceof UserPosition)) return projCoords as any;
80
+ const p = origin.clone();
81
+ p.lat = projCoords.lat;
82
+ p.lng = projCoords.lng;
83
+ p.level = projCoords.level;
84
+ p.alt = projCoords.alt;
85
+ p.heightFromFloor = projCoords.heightFromFloor;
86
+ p.heightFromGround = projCoords.heightFromGround;
87
+ return p as any;
88
+ }
76
89
 
77
90
  this.edges.forEach(edge => {
78
91
 
@@ -92,7 +105,7 @@ class Graph {
92
105
  if (useMaxDistance && distanceToSegment > maxDistance) return;
93
106
 
94
107
  if (distanceToSegment < (bestProjection?.distanceFromNearestElement ?? Number.MAX_VALUE)) {
95
- bestProjection = new GraphProjection(origin, distanceToSegment, segmentProjection, edge);
108
+ bestProjection = new GraphProjection(origin, distanceToSegment, adaptProjectionCoords(segmentProjection), edge);
96
109
  }
97
110
  });
98
111
 
@@ -117,14 +130,14 @@ class Graph {
117
130
 
118
131
  // If distance below epsilon consider it as best projection
119
132
  if (distanceToVertex < GeoConstants.EPS_MM) {
120
- bestProjection = new GraphProjection(origin, 0, vertexCoords, vertex);
133
+ bestProjection = new GraphProjection(origin, 0, adaptProjectionCoords(vertexCoords), vertex);
121
134
  return;
122
135
  }
123
136
 
124
137
  if (useMaxDistance && distanceToVertex > maxDistance) return;
125
138
 
126
139
  if (distanceToVertex < (bestProjection?.distanceFromNearestElement ?? Number.MAX_VALUE)) {
127
- bestProjection = new GraphProjection(origin, distanceToVertex, vertexCoords, vertex);
140
+ bestProjection = new GraphProjection(origin, distanceToVertex, adaptProjectionCoords(vertexCoords), vertex);
128
141
  }
129
142
  });
130
143
 
@@ -93,30 +93,32 @@ export default class Itinerary {
93
93
  }
94
94
 
95
95
  set transitMode(_) {
96
- throw new Error('Itinerary.mode cannot be set. They are calculated from Itinerary.legs.');
96
+ throw new Error('Itinerary.transitMode cannot be set. They are calculated from Itinerary.legs.');
97
97
  }
98
98
 
99
+ // Transit mode will return MULTI if there are several transit modes except WALK
100
+ // Else it will return the only transit mode
101
+ // fallback to WALK if no transit mode
99
102
  get transitMode() {
100
103
  if (!this._transitMode) {
101
- let isPublicTransport = false;
102
- let isBicycle = false;
103
- let isDriving = false;
104
+ const legTransitModes = new Set(this.legs.map(leg => leg.transitMode));
104
105
 
105
- this.legs.forEach((leg) => {
106
- isPublicTransport = isPublicTransport || isTransitModePublicTransport(leg.transitMode);
107
- isBicycle = isBicycle || leg.transitMode === 'BIKE';
108
- isDriving = isDriving || leg.transitMode === 'CAR';
109
- });
106
+ // Remove WALK from transit modes
107
+ legTransitModes.delete('WALK');
110
108
 
111
- if (isPublicTransport) {
109
+ if (legTransitModes.size > 1) {
112
110
  this._transitMode = 'MULTI';
113
- } else if (isDriving) {
114
- this._transitMode = 'CAR';
115
- } else if (isBicycle) {
116
- this._transitMode = 'BIKE';
117
- } else {
118
- this._transitMode = 'WALK';
111
+
112
+ return this._transitMode;
119
113
  }
114
+
115
+ if (legTransitModes.size === 1) {
116
+ this._transitMode = legTransitModes.values().next().value as TransitMode;
117
+
118
+ return this._transitMode;
119
+ }
120
+
121
+ this._transitMode = 'WALK';
120
122
  }
121
123
 
122
124
  return this._transitMode;
@@ -0,0 +1,31 @@
1
+ import chai from 'chai';
2
+
3
+ import { isTransitModePublicTransport, areTransitAndTravelModeConsistent } from './TransitMode.js';
4
+
5
+ const { expect } = chai;
6
+
7
+ describe('TransitMode', () => {
8
+
9
+ it('transit and travel mode consistent', () => {
10
+ expect(areTransitAndTravelModeConsistent('AIRPLANE', 'TRANSIT')).is.true;
11
+ expect(areTransitAndTravelModeConsistent('BOAT', 'TRANSIT' )).is.true;
12
+ expect(areTransitAndTravelModeConsistent('BUS', 'TRANSIT' )).is.true;
13
+ expect(areTransitAndTravelModeConsistent('FERRY', 'TRANSIT' )).is.true;
14
+ expect(areTransitAndTravelModeConsistent('FUNICULAR', 'TRANSIT' )).is.true;
15
+ expect(areTransitAndTravelModeConsistent('METRO', 'TRANSIT' )).is.true;
16
+ expect(areTransitAndTravelModeConsistent('TRAIN', 'TRANSIT' )).is.true;
17
+ expect(areTransitAndTravelModeConsistent('TRAM', 'TRANSIT' )).is.true;
18
+ expect(areTransitAndTravelModeConsistent('MULTI', 'TRANSIT' )).is.true;
19
+
20
+ expect(areTransitAndTravelModeConsistent('BIKE', 'TRANSIT')).is.false;
21
+ expect(areTransitAndTravelModeConsistent('CAR', 'TRANSIT')).is.false;
22
+ expect(areTransitAndTravelModeConsistent('MOTO', 'TRANSIT')).is.false;
23
+ expect(areTransitAndTravelModeConsistent('TAXI', 'TRANSIT')).is.false;
24
+ expect(areTransitAndTravelModeConsistent('WALK', 'TRANSIT')).is.false;
25
+
26
+ expect(areTransitAndTravelModeConsistent('WALK', 'WALK')).is.true;
27
+ expect(areTransitAndTravelModeConsistent('BIKE', 'BIKE')).is.true;
28
+ expect(areTransitAndTravelModeConsistent('CAR', 'CAR')).is.true;
29
+ });
30
+
31
+ });
@@ -1,13 +1,19 @@
1
+ import { TravelMode } from "./TravelMode";
2
+
1
3
  export type TransitMode = 'AIRPLANE' | 'BOAT' | 'BIKE' | 'BUS' | 'CAR' |
2
4
  'FERRY' | 'FUNICULAR' | 'METRO' | 'MOTO' | 'TRAIN' | 'TAXI' |
3
5
  'TRAM' | 'WALK' | 'MULTI' | 'UNKNOWN';
4
6
 
5
7
  export type PublicTransport = 'AIRPLANE' | 'BOAT' | 'BUS' |
6
- 'FERRY' | 'FUNICULAR' | 'METRO' | 'TRAIN' | 'TRAM';
8
+ 'FERRY' | 'FUNICULAR' | 'METRO' | 'MULTI' | 'TRAIN' | 'TRAM';
7
9
 
8
- export function isTransitModePublicTransport(routingMode: TransitMode): routingMode is PublicTransport {
10
+ export function isTransitModePublicTransport(transitMode: TransitMode): transitMode is PublicTransport {
9
11
  return [
10
12
  'AIRPLANE', 'BOAT', 'BUS', 'FERRY',
11
- 'FUNICULAR', 'METRO', 'TRAIN', 'TRAM'
12
- ].includes(routingMode);
13
+ 'FUNICULAR', 'METRO', 'MULTI', 'TRAIN', 'TRAM'
14
+ ].includes(transitMode);
15
+ }
16
+
17
+ export function areTransitAndTravelModeConsistent(transitMode: TransitMode, travelMode: TravelMode): boolean {
18
+ return transitMode === travelMode || (isTransitModePublicTransport(transitMode) && travelMode === 'TRANSIT');
13
19
  }
@@ -8,7 +8,7 @@ import { Coordinates } from '@wemap/geo';
8
8
 
9
9
  import CitywayRemoteRouter from './CitywayRemoteRouter.js';
10
10
  import { verifyStepsCoherence } from '../../model/Itinerary.spec.js';
11
-
11
+ import { areTransitAndTravelModeConsistent } from '../../model/TransitMode.js';
12
12
 
13
13
  const { expect } = chai;
14
14
 
@@ -33,12 +33,14 @@ describe('CitywayRemoteRouter - parseResponse', () => {
33
33
  expect(itinerary1.destination.equals(new Coordinates(49.5067090188444, 0.16948421154178309))).true;
34
34
  expect(itinerary1.distance).to.be.closeTo(6887, 1);
35
35
  expect(itinerary1.duration).equal(2379);
36
- expect(itinerary1.transitMode).equal('MULTI');
36
+ expect(itinerary1.transitMode).equal('BUS');
37
37
  // Do not work because of the input time format
38
38
  expect(itinerary1.startTime).equal(1620659156000);
39
39
  expect(itinerary1.endTime).equal(1620661535000);
40
40
  expect(itinerary1.legs.length).equal(5);
41
41
 
42
+ expect(areTransitAndTravelModeConsistent(itinerary1.transitMode, 'TRANSIT')).is.true;
43
+
42
44
  const itinerary1leg1 = itinerary1.legs[0];
43
45
  // Do not work because of the input time format
44
46
  expect(itinerary1leg1.startTime).equal(1620659156000);
@@ -70,6 +72,8 @@ describe('CitywayRemoteRouter - parseResponse', () => {
70
72
 
71
73
  expect(itineraries.length).equal(1);
72
74
  expect(itineraries[0].transitMode).equal('WALK');
75
+ expect(areTransitAndTravelModeConsistent(itineraries[0].transitMode, 'WALK')).is.true;
76
+
73
77
  });
74
78
 
75
79
  it('Itineraries - 3', () => {
@@ -80,8 +84,10 @@ describe('CitywayRemoteRouter - parseResponse', () => {
80
84
  const itineraries = CitywayRemoteRouter.parseResponse(json);
81
85
  itineraries.forEach(verifyStepsCoherence);
82
86
 
83
- expect(itineraries[0].transitMode).equal('MULTI');
87
+ expect(itineraries[0].transitMode).equal('FUNICULAR');
84
88
  expect(itineraries[0].legs[1].transitMode).equal('FUNICULAR');
89
+ expect(areTransitAndTravelModeConsistent(itineraries[0].transitMode, 'TRANSIT')).is.true;
90
+
85
91
  });
86
92
 
87
93
  it('Itineraries - 4', () => {
@@ -94,6 +100,7 @@ describe('CitywayRemoteRouter - parseResponse', () => {
94
100
 
95
101
  expect(itineraries[0].transitMode).equal('BIKE');
96
102
  expect(itineraries[0].legs[0].transitMode).equal('BIKE');
103
+ expect(areTransitAndTravelModeConsistent(itineraries[0].transitMode, 'BIKE')).is.true;
97
104
  });
98
105
 
99
106
  it('Itineraries - 5', () => {
@@ -104,7 +111,9 @@ describe('CitywayRemoteRouter - parseResponse', () => {
104
111
  const itineraries = CitywayRemoteRouter.parseResponse(json);
105
112
  itineraries.forEach(verifyStepsCoherence);
106
113
 
107
- expect(itineraries[1].transitMode).equal('MULTI');
114
+ expect(itineraries[1].transitMode).equal('TRAM');
115
+ expect(areTransitAndTravelModeConsistent(itineraries[1].transitMode, 'TRANSIT')).is.true;
116
+
108
117
  expect(itineraries[1].legs[1].transitMode).equal('TRAM');
109
118
  expect(itineraries[1].legs[1].transportInfo?.name).equal('B');
110
119
  });
@@ -8,6 +8,7 @@ import { Coordinates } from '@wemap/geo';
8
8
 
9
9
  import IdfmRemoteRouter from './IdfmRemoteRouter.js';
10
10
  import { verifyStepsCoherence } from '../../model/Itinerary.spec.js';
11
+ import { areTransitAndTravelModeConsistent } from '../../model/TransitMode.js';
11
12
 
12
13
  const { expect } = chai;
13
14
 
@@ -32,12 +33,14 @@ describe('IdfmRouter - parseResponse', () => {
32
33
  expect(itinerary1.destination.equals(new Coordinates(48.877877, 2.351929))).true;
33
34
  expect(itinerary1.distance).to.be.closeTo(5264, 1);
34
35
  expect(itinerary1.duration).equal(1842);
35
- expect(itinerary1.transitMode).equal('MULTI');
36
+ expect(itinerary1.transitMode).equal('METRO');
36
37
  // Do not work because of the input time format
37
38
  expect(itinerary1.startTime).equal(1637596640000);
38
39
  expect(itinerary1.endTime).equal(1637598482000);
39
40
  expect(itinerary1.legs.length).equal(3);
40
41
 
42
+ expect(areTransitAndTravelModeConsistent(itinerary1.transitMode, 'TRANSIT')).is.true;
43
+
41
44
  const itinerary1leg1 = itinerary1.legs[0];
42
45
  // Do not work because of the input time format
43
46
  expect(itinerary1leg1.startTime).equal(1637596640000);
@@ -59,4 +62,29 @@ describe('IdfmRouter - parseResponse', () => {
59
62
  expect(itinerary1leg2.transportInfo?.directionName).equal('Nation (Paris)');
60
63
 
61
64
  });
65
+
66
+ it('Itineraries - 2', () => {
67
+
68
+ const filePath = path.resolve(assetsPath, 'itinerary-paris-idfm-2.json');
69
+ const fileString = fs.readFileSync(filePath, 'utf8');
70
+ const json = JSON.parse(fileString);
71
+
72
+ const itineraries = IdfmRemoteRouter.parseResponse(json);
73
+ itineraries.forEach(verifyStepsCoherence);
74
+
75
+ expect(itineraries.length).equal(9);
76
+
77
+ const itinerary1 = itineraries[0];
78
+ expect(itinerary1.origin.equals(new Coordinates(48.836123, 2.341896))).true;
79
+ expect(itinerary1.destination.equals(new Coordinates(48.867999, 2.32563))).true;
80
+ expect(itinerary1.distance).to.be.closeTo(5765, 1);
81
+ expect(itinerary1.duration).equal(1977);
82
+ expect(itinerary1.transitMode).equal('MULTI');
83
+ // Do not work because of the input time format
84
+ expect(itinerary1.startTime).equal(1712073372000);
85
+ expect(itinerary1.endTime).equal(1712075349000);
86
+ expect(itinerary1.legs.length).equal(4);
87
+
88
+ expect(areTransitAndTravelModeConsistent(itinerary1.transitMode, 'TRANSIT')).is.true;
89
+ });
62
90
  });
@@ -7,7 +7,7 @@ import Leg, { TransportInfo } from '../../model/Leg.js';
7
7
  import { dateWithTimeZone } from '../RemoteRouterUtils.js';
8
8
  import StepsBuilder from '../../model/StepsBuilder.js';
9
9
  import RemoteRouter from '../RemoteRouter.js';
10
- import { type TransitMode } from '../../model/TransitMode.js';
10
+ import { areTransitAndTravelModeConsistent, type TransitMode } from '../../model/TransitMode.js';
11
11
  import { type MinStepInfo } from '../../model/Step.js';
12
12
  import { RouterRequest } from '../../model/RouterRequest.js';
13
13
  import { RemoteRoutingError } from '../../RoutingError.js';
@@ -185,7 +185,7 @@ class IdfmRemoteRouter extends RemoteRouter {
185
185
 
186
186
  const itineraries = this.parseResponse(jsonResponse);
187
187
 
188
- const sameModeFound = itineraries.some((itinerary) => itinerary.transitMode === routerRequest.travelMode);
188
+ const sameModeFound = itineraries.some((itinerary) => areTransitAndTravelModeConsistent(itinerary.transitMode, routerRequest.travelMode));
189
189
 
190
190
  if (!sameModeFound) {
191
191
  throw RemoteRoutingError.notFound(
@@ -9,6 +9,7 @@ import { Coordinates } from '@wemap/geo';
9
9
 
10
10
  import OsrmRemoteRouter from './OsrmRemoteRouter.js';
11
11
  import { verifyStepsCoherence } from '../../model/Itinerary.spec.js';
12
+ import { areTransitAndTravelModeConsistent } from '../../model/TransitMode.js';
12
13
 
13
14
 
14
15
  const { expect } = chai;
@@ -43,6 +44,7 @@ describe('OsrmRemoteRouter - parseResponse', () => {
43
44
  expect(itinerary1.legs.length).equal(1);
44
45
  expect(itinerary1.coords.length).equal(19);
45
46
  expect(itinerary1.transitMode).equal('WALK');
47
+ expect(areTransitAndTravelModeConsistent(itinerary1.transitMode, 'WALK')).is.true;
46
48
 
47
49
  const itinerary1leg1 = itinerary1.legs[0];
48
50
  expect(itinerary1leg1.startTime).is.null;
@@ -76,6 +78,7 @@ describe('OsrmRemoteRouter - parseResponse', () => {
76
78
  expect(itineraries[0].transitMode).equal('WALK');
77
79
  expect(itineraries[0].origin.equals(origin)).true;
78
80
  expect(itineraries[0].destination.equals(destination)).true;
81
+ expect(areTransitAndTravelModeConsistent(itineraries[0].transitMode, 'WALK')).is.true;
79
82
 
80
83
  const steps = itineraries[0].steps;
81
84
  expect(steps.length).equal(2);
@@ -99,6 +102,7 @@ describe('OsrmRemoteRouter - parseResponse', () => {
99
102
  expect(itineraries[0].transitMode).equal('CAR');
100
103
  expect(itineraries[0].origin.equals(origin)).true;
101
104
  expect(itineraries[0].destination.equals(destination)).true;
105
+ expect(areTransitAndTravelModeConsistent(itineraries[0].transitMode, 'CAR')).is.true;
102
106
 
103
107
  const steps = itineraries[0].steps;
104
108
  expect(steps.length).equal(2);
@@ -9,6 +9,7 @@ import { Coordinates } from '@wemap/geo';
9
9
 
10
10
  import OtpRemoteRouter from './OtpRemoteRouter.js';
11
11
  import { verifyStepsCoherence } from '../../model/Itinerary.spec.js';
12
+ import { areTransitAndTravelModeConsistent } from '../../model/TransitMode.js';
12
13
 
13
14
 
14
15
  const { expect } = chai;
@@ -38,7 +39,8 @@ describe('OtpRouter - createRouterResponseFromJson', () => {
38
39
  expect(itinerary1.startTime).equal(1614607210000);
39
40
  expect(itinerary1.endTime).equal(1614608416000);
40
41
  expect(itinerary1.legs.length).equal(3);
41
- expect(itinerary1.transitMode).equal('MULTI');
42
+ expect(itinerary1.transitMode).equal('BUS');
43
+ expect(areTransitAndTravelModeConsistent(itinerary1.transitMode, 'TRANSIT')).is.true;
42
44
 
43
45
  const itinerary1leg1 = itinerary1.legs[0];
44
46
  expect(itinerary1leg1.startTime).equal(1614607210000);
@@ -78,7 +80,8 @@ describe('OtpRouter - createRouterResponseFromJson', () => {
78
80
  expect(itinerary1.destination.equals(new Coordinates(45.18544, 5.73123))).true;
79
81
  expect(itinerary1.legs.length).equal(5);
80
82
 
81
- expect(itinerary1.transitMode).equal('MULTI');
83
+ expect(itinerary1.transitMode).equal('TRAM');
84
+ expect(areTransitAndTravelModeConsistent(itinerary1.transitMode, 'TRANSIT')).is.true;
82
85
 
83
86
  expect(itinerary1.legs[0].transitMode).equal('WALK');
84
87