@opentripplanner/core-utils 9.0.0-alpha.3 → 9.0.0-alpha.30

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.
@@ -1,107 +1,223 @@
1
- query PlanQuery(
2
- $fromPlace: String!,
3
- $toPlace: String!,
4
- $modes: [TransportMode],
5
- $time: String,
6
- $date: String,
7
- $wheelchair: Boolean,
8
- $bikeReluctance: Float,
9
- $carReluctance: Float,
10
- $walkReluctance: Float,
11
- $arriveBy: Boolean,
12
- $intermediatePlaces: [InputCoordinates],
13
- $preferred: InputPreferred,
14
- $unpreferred: InputUnpreferred,
15
- $banned: InputBanned,
1
+ query Plan(
2
+ $fromPlace: String!
3
+ $toPlace: String!
4
+ $modes: [TransportMode]
5
+ $time: String
6
+ $date: String
7
+ $wheelchair: Boolean
8
+ $bikeReluctance: Float
9
+ $carReluctance: Float
10
+ $walkReluctance: Float
11
+ $arriveBy: Boolean
12
+ $intermediatePlaces: [InputCoordinates]
13
+ $preferred: InputPreferred
14
+ $unpreferred: InputUnpreferred
15
+ $banned: InputBanned
16
+ $numItineraries: Int
16
17
  ) {
17
- plan(
18
- fromPlace: $fromPlace
19
- toPlace: $toPlace
20
- transportModes: $modes
21
- locale: "en",
22
- time: $time,
23
- date: $date,
24
- wheelchair: $wheelchair,
25
- bikeReluctance: $bikeReluctance,
26
- carReluctance: $carReluctance,
27
- walkReluctance: $walkReluctance,
28
- arriveBy: $arriveBy,
29
- intermediatePlaces: $intermediatePlaces,
30
- preferred: $preferred,
31
- unpreferred: $unpreferred,
32
- banned: $banned,
33
- ) {
34
- itineraries {
35
- duration
36
- endTime
37
- startTime
38
- waitingTime
39
- walkTime
40
- legs {
41
- distance
42
- duration
43
- endTime
44
- mode
45
- realTime
46
- realtimeState
47
- startTime
48
- transitLeg
49
- agency {
50
- name
51
- id
52
- timezone
53
- url
54
- }
55
- legGeometry {
56
- length
57
- points
58
- }
59
- intermediateStops {
60
- lat
61
- lon
62
- name
63
- stopCode: code
64
- stopId: id
65
- locationType
66
- }
67
- route {
68
- shortName
69
- color
70
- textColor
71
- id
72
- type
73
- }
74
- from {
75
- lat
76
- lon
77
- name
78
- vertexType
79
- stop {
80
- id
81
- code
82
-
83
- }
84
- }
85
- to {
86
- lat
87
- lon
88
- name
89
- vertexType
90
- stop {
91
- id
92
- code
93
- }
94
- }
95
- steps {
96
- distance
97
- lat
98
- lon
99
- elevationProfile {
100
- distance
101
- elevation
102
- }
103
- }
104
- }
105
- }
106
- }
107
- }
18
+ plan(
19
+ arriveBy: $arriveBy
20
+ banned: $banned
21
+ bikeReluctance: $bikeReluctance
22
+ carReluctance: $carReluctance
23
+ date: $date
24
+ fromPlace: $fromPlace
25
+ intermediatePlaces: $intermediatePlaces
26
+ # Currently only supporting EN locale, used for times and text
27
+ locale: "en"
28
+ numItineraries: $numItineraries
29
+ preferred: $preferred
30
+ time: $time
31
+ toPlace: $toPlace
32
+ transportModes: $modes
33
+ unpreferred: $unpreferred
34
+ walkReluctance: $walkReluctance
35
+ wheelchair: $wheelchair
36
+ ) {
37
+ itineraries {
38
+ accessibilityScore
39
+ duration
40
+ endTime
41
+ legs {
42
+ accessibilityScore
43
+ agency {
44
+ alerts {
45
+ alertDescriptionText
46
+ alertHeaderText
47
+ alertUrl
48
+ effectiveStartDate
49
+ id
50
+ }
51
+ id
52
+ name
53
+ timezone
54
+ url
55
+ }
56
+ arrivalDelay
57
+ departureDelay
58
+ distance
59
+ dropoffType
60
+ duration
61
+ endTime
62
+ fareProducts {
63
+ id
64
+ product {
65
+ id
66
+ medium {
67
+ id
68
+ name
69
+ }
70
+ name
71
+ price {
72
+ amount
73
+ currency {
74
+ code
75
+ digits
76
+ }
77
+ }
78
+ riderCategory {
79
+ id
80
+ name
81
+ }
82
+ }
83
+ }
84
+ from {
85
+ lat
86
+ lon
87
+ name
88
+ rentalVehicle {
89
+ id
90
+ network
91
+ }
92
+ stop {
93
+ alerts {
94
+ alertDescriptionText
95
+ alertHeaderText
96
+ alertUrl
97
+ effectiveStartDate
98
+ id
99
+ }
100
+ code
101
+ gtfsId
102
+ id
103
+ }
104
+ vertexType
105
+ }
106
+ interlineWithPreviousLeg
107
+ intermediateStops {
108
+ lat
109
+ locationType
110
+ lon
111
+ name
112
+ stopCode: code
113
+ stopId: id
114
+ }
115
+ legGeometry {
116
+ length
117
+ points
118
+ }
119
+ mode
120
+ pickupBookingInfo {
121
+ earliestBookingTime {
122
+ daysPrior
123
+ }
124
+ }
125
+ pickupType
126
+ realTime
127
+ realtimeState
128
+ rentedBike
129
+ rideHailingEstimate {
130
+ arrival
131
+ maxPrice {
132
+ amount
133
+ currency {
134
+ code
135
+ }
136
+ }
137
+ minPrice {
138
+ amount
139
+ currency {
140
+ code
141
+ }
142
+ }
143
+ provider {
144
+ id
145
+ }
146
+ }
147
+ route {
148
+ alerts {
149
+ alertDescriptionText
150
+ alertHeaderText
151
+ alertUrl
152
+ effectiveStartDate
153
+ id
154
+ }
155
+ color
156
+ id
157
+ longName
158
+ shortName
159
+ textColor
160
+ type
161
+ }
162
+ startTime
163
+ steps {
164
+ absoluteDirection
165
+ alerts {
166
+ alertDescriptionText
167
+ alertHeaderText
168
+ alertUrl
169
+ effectiveStartDate
170
+ id
171
+ }
172
+ area
173
+ distance
174
+ elevationProfile {
175
+ distance
176
+ elevation
177
+ }
178
+ lat
179
+ lon
180
+ relativeDirection
181
+ stayOn
182
+ streetName
183
+ }
184
+ to {
185
+ lat
186
+ lon
187
+ name
188
+ rentalVehicle {
189
+ id
190
+ network
191
+ }
192
+ stop {
193
+ alerts {
194
+ alertDescriptionText
195
+ alertHeaderText
196
+ alertUrl
197
+ effectiveStartDate
198
+ id
199
+ }
200
+ code
201
+ gtfsId
202
+ id
203
+ }
204
+ vertexType
205
+ }
206
+ transitLeg
207
+ trip {
208
+ gtfsId
209
+ id
210
+ tripHeadsign
211
+ }
212
+ }
213
+ startTime
214
+ waitingTime
215
+ walkTime
216
+ }
217
+ routingErrors {
218
+ code
219
+ description
220
+ inputField
221
+ }
222
+ }
223
+ }
package/src/query-gen.ts CHANGED
@@ -1,20 +1,81 @@
1
- /* eslint-disable @typescript-eslint/no-unused-vars */
1
+ import { LonLatOutput } from "@conveyal/lonlat";
2
2
  import { print } from "graphql";
3
3
  import {
4
4
  ModeSetting,
5
5
  ModeSettingValues,
6
6
  TransportMode
7
7
  } from "@opentripplanner/types";
8
- import { LonLatOutput } from "@conveyal/lonlat";
8
+
9
9
  import PlanQuery from "./planQuery.graphql";
10
10
 
11
+ type InputBanned = {
12
+ routes?: string;
13
+ agencies?: string;
14
+ trips?: string;
15
+ stops?: string;
16
+ stopsHard?: string;
17
+ };
18
+
19
+ type InputPreferred = {
20
+ routes?: string;
21
+ agencies?: string;
22
+ unpreferredCost?: string;
23
+ };
24
+
11
25
  type OTPQueryParams = {
12
- to: LonLatOutput;
13
- from: LonLatOutput;
14
- modes: Array<TransportMode>;
26
+ arriveBy: boolean;
27
+ date?: string;
28
+ from: LonLatOutput & { name?: string };
29
+ modes: TransportMode[];
15
30
  modeSettings: ModeSetting[];
31
+ time?: string;
32
+ numItineraries?: number;
33
+ to: LonLatOutput & { name?: string };
34
+ banned?: InputBanned;
35
+ preferred?: InputPreferred;
36
+ };
37
+
38
+ type GraphQLQuery = {
39
+ query: string;
40
+ variables: Record<string, unknown>;
16
41
  };
17
42
 
43
+ /**
44
+ * Mode Settings can contain additional modes to add to the query,
45
+ * this function extracts those additional modes from the settings
46
+ * and returns them in an array.
47
+ * @param modeSettings List of mode settings with values populated
48
+ * @returns Additional transport modes to add to query
49
+ */
50
+ export function extractAdditionalModes(
51
+ modeSettings: ModeSetting[],
52
+ enabledModes: TransportMode[]
53
+ ): TransportMode[] {
54
+ return modeSettings.reduce<TransportMode[]>((prev, cur) => {
55
+ // First, ensure that the mode associated with this setting is even enabled
56
+ if (!enabledModes.map(m => m.mode).includes(cur.applicableMode)) {
57
+ return prev;
58
+ }
59
+
60
+ // In checkboxes (or submode checkboxes), mode must be enabled and have a transport mode in it
61
+ if (
62
+ (cur.type === "CHECKBOX" || cur.type === "SUBMODE") &&
63
+ cur.addTransportMode &&
64
+ cur.value
65
+ ) {
66
+ return [...prev, cur.addTransportMode];
67
+ }
68
+ if (cur.type === "DROPDOWN") {
69
+ const transportMode = cur.options.find(o => o.value === cur.value)
70
+ .addTransportMode;
71
+ if (transportMode) {
72
+ return [...prev, transportMode];
73
+ }
74
+ }
75
+ return prev;
76
+ }, []);
77
+ }
78
+
18
79
  /**
19
80
  * Generates every possible mathematical subset of the input TransportModes.
20
81
  * Uses code from:
@@ -32,6 +93,11 @@ function combinations(array: TransportMode[]): TransportMode[][] {
32
93
  .map((e1, i) => array.filter((e2, j) => i & (1 << j)))
33
94
  );
34
95
  }
96
+
97
+ /**
98
+ * This constant maps all the transport mode to a broader mode type,
99
+ * which is used to determine the valid combinations of modes used in query generation.
100
+ */
35
101
  export const SIMPLIFICATIONS = {
36
102
  AIRPLANE: "TRANSIT",
37
103
  BICYCLE: "PERSONAL",
@@ -50,6 +116,19 @@ export const SIMPLIFICATIONS = {
50
116
  WALK: "WALK"
51
117
  };
52
118
 
119
+ // Inclusion of "TRANSIT" alone automatically implies "WALK" in OTP
120
+ const VALID_COMBOS = [
121
+ ["WALK"],
122
+ ["PERSONAL"],
123
+ ["TRANSIT", "SHARED"],
124
+ ["WALK", "SHARED"],
125
+ ["TRANSIT"],
126
+ ["TRANSIT", "PERSONAL"],
127
+ ["TRANSIT", "CAR"]
128
+ ];
129
+
130
+ const BANNED_TOGETHER = ["SCOOTER", "BICYCLE", "CAR"];
131
+
53
132
  export const TRANSIT_SUBMODES = Object.keys(SIMPLIFICATIONS).filter(
54
133
  mode => SIMPLIFICATIONS[mode] === "TRANSIT" && mode !== "TRANSIT"
55
134
  );
@@ -57,96 +136,115 @@ export const TRANSIT_SUBMODES_AND_TRANSIT = Object.keys(SIMPLIFICATIONS).filter(
57
136
  mode => SIMPLIFICATIONS[mode] === "TRANSIT"
58
137
  );
59
138
 
60
- export function generateCombinations(params: OTPQueryParams): OTPQueryParams[] {
61
- const VALID_COMBOS = [
62
- ["WALK"],
63
- ["PERSONAL"],
64
- ["TRANSIT", "SHARED"],
65
- ["WALK", "SHARED"],
66
- ["TRANSIT"],
67
- ["TRANSIT", "PERSONAL"],
68
- ["TRANSIT", "CAR"]
69
- ];
139
+ function isCombinationValid(
140
+ combo: TransportMode[],
141
+ queryTransitSubmodes: string[]
142
+ ): boolean {
143
+ if (combo.length === 0) return false;
70
144
 
71
- const BANNED_TOGETHER = ["SCOOTER", "BICYCLE"];
145
+ // All current qualifiers currently simplify to "SHARED"
146
+ const simplifiedModes = Array.from(
147
+ new Set(combo.map(c => (c.qualifier ? "SHARED" : SIMPLIFICATIONS[c.mode])))
148
+ );
72
149
 
73
- // List of the transit submodes that are included in the input params
74
- const queryTransitSubmodes = params.modes
75
- .filter(mode => TRANSIT_SUBMODES.includes(mode.mode))
76
- .map(mode => mode.mode);
150
+ // Ensure that if we have one transit mode, then we include ALL transit modes
151
+ if (simplifiedModes.includes("TRANSIT")) {
152
+ // Don't allow TRANSIT along with any other submodes
153
+ if (queryTransitSubmodes.length && combo.find(c => c.mode === "TRANSIT")) {
154
+ return false;
155
+ }
77
156
 
78
- return (
79
- combinations(params.modes)
80
- .filter(combo => {
81
- if (combo.length === 0) return false;
82
-
83
- // All current qualifiers currently simplify to "SHARED"
84
- const simplifiedModes = Array.from(
85
- new Set(
86
- combo.map(c => (c.qualifier ? "SHARED" : SIMPLIFICATIONS[c.mode]))
87
- )
88
- );
89
-
90
- // Ensure that if we have one transit mode, then we include ALL transit modes
91
- if (simplifiedModes.includes("TRANSIT")) {
92
- const flatModes = combo.map(m => m.mode);
93
- if (
94
- combo.reduce((prev, cur) => {
95
- if (queryTransitSubmodes.includes(cur.mode)) {
96
- return prev - 1;
97
- }
98
- return prev;
99
- }, queryTransitSubmodes.length) !== 0
100
- ) {
101
- return false;
102
- }
157
+ if (
158
+ combo.reduce((prev, cur) => {
159
+ if (queryTransitSubmodes.includes(cur.mode)) {
160
+ return prev - 1;
103
161
  }
162
+ return prev;
163
+ }, queryTransitSubmodes.length) !== 0
164
+ ) {
165
+ return false;
166
+ }
167
+ // Continue to the other checks
168
+ }
169
+
170
+ // OTP doesn't support multiple non-walk modes
171
+ if (BANNED_TOGETHER.filter(m => combo.find(c => c.mode === m)).length > 1) {
172
+ return false;
173
+ }
104
174
 
105
- // OTP doesn't support multiple non-walk modes
106
- if (BANNED_TOGETHER.every(m => combo.find(c => c.mode === m)))
107
- return false;
108
-
109
- return !!VALID_COMBOS.find(
110
- vc =>
111
- simplifiedModes.every(m => vc.includes(m)) &&
112
- vc.every(m => simplifiedModes.includes(m))
113
- );
114
- })
115
- // create new filter that removes subTransit modes from appearing on their own
116
- // ONLY IF there's multiple of them!
117
- .map(combo => ({ ...params, modes: combo }))
175
+ return !!VALID_COMBOS.find(
176
+ vc =>
177
+ simplifiedModes.every(m => vc.includes(m)) &&
178
+ vc.every(m => simplifiedModes.includes(m))
118
179
  );
119
180
  }
120
181
 
121
- // eslint-disable-next-line import/prefer-default-export
122
- export function generateOtp2Query(params: OTPQueryParams): any {
123
- const { to, from, modeSettings } = params;
182
+ /**
183
+ * Generates a list of queries for OTP to get a comprehensive
184
+ * set of results based on the modes input.
185
+ * @param params OTP Query Params
186
+ * @returns Set of parameters to generate queries
187
+ */
188
+ export function generateCombinations(params: OTPQueryParams): OTPQueryParams[] {
189
+ const completeModeList = [
190
+ ...extractAdditionalModes(params.modeSettings, params.modes),
191
+ ...params.modes
192
+ ];
193
+
194
+ // List of the transit *submodes* that are included in the input params
195
+ const queryTransitSubmodes = completeModeList
196
+ .filter(mode => TRANSIT_SUBMODES.includes(mode.mode))
197
+ .map(mode => mode.mode);
198
+
199
+ return combinations(completeModeList)
200
+ .filter(combo => isCombinationValid(combo, queryTransitSubmodes))
201
+ .map(combo => ({ ...params, modes: combo }));
202
+ }
124
203
 
204
+ export function generateOtp2Query({
205
+ arriveBy,
206
+ banned,
207
+ date,
208
+ from,
209
+ modes,
210
+ modeSettings,
211
+ numItineraries,
212
+ preferred,
213
+ time,
214
+ to
215
+ }: OTPQueryParams): GraphQLQuery {
125
216
  // This extracts the values from the mode settings to key value pairs
126
217
  const modeSettingValues = modeSettings.reduce((prev, cur) => {
218
+ if (cur.type === "SLIDER" && cur.inverseKey) {
219
+ prev[cur.inverseKey] = cur.high - cur.value + cur.low;
220
+ }
127
221
  prev[cur.key] = cur.value;
128
222
  return prev;
129
223
  }, {}) as ModeSettingValues;
130
224
 
131
225
  const {
132
- walkReluctance,
133
- wheelchair,
134
226
  bikeReluctance,
135
227
  carReluctance,
136
- allowBikeRental
228
+ walkReluctance,
229
+ wheelchair
137
230
  } = modeSettingValues;
138
231
 
139
232
  return {
140
233
  query: print(PlanQuery),
141
234
  variables: {
142
- fromPlace: [from.lat, from.lon].join(","),
143
- toPlace: [to.lat, to.lon].join(","),
144
- modes: params.modes,
145
- allowBikeRental,
146
- walkReluctance,
147
- wheelchair,
235
+ arriveBy,
236
+ banned,
148
237
  bikeReluctance,
149
- carReluctance
238
+ carReluctance,
239
+ date,
240
+ fromPlace: `${from.name}::${from.lat},${from.lon}}`,
241
+ modes,
242
+ numItineraries,
243
+ preferred,
244
+ time,
245
+ toPlace: `${to.name}::${to.lat},${to.lon}}`,
246
+ walkReluctance,
247
+ wheelchair
150
248
  }
151
249
  };
152
250
  }
package/tsconfig.json CHANGED
@@ -5,7 +5,9 @@
5
5
  "target": "es2019",
6
6
  "outDir": "./lib",
7
7
  "rootDir": "./src",
8
+ "lib": ["es2019", "dom"],
8
9
  "skipLibCheck": true
9
10
  },
10
- "include": ["src/**/*"]
11
+ "include": ["src/**/*"],
12
+ "exclude": ["src/__tests__/**/*"]
11
13
  }