@opentripplanner/core-utils 9.0.0-alpha.2 → 9.0.0-alpha.4
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/esm/graphql.d.js +2 -0
- package/esm/graphql.d.js.map +1 -0
- package/esm/index.js +2 -0
- package/esm/index.js.map +1 -1
- package/esm/itinerary.js +4 -3
- package/esm/itinerary.js.map +1 -1
- package/esm/query-gen.js +182 -0
- package/esm/query-gen.js.map +1 -0
- package/esm/state.js +2 -0
- package/esm/state.js.map +1 -0
- package/lib/graphql.d.js +2 -0
- package/lib/graphql.d.js.map +1 -0
- package/lib/index.d.ts +2 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +3 -0
- package/lib/index.js.map +1 -1
- package/lib/itinerary.d.ts +7 -7
- package/lib/itinerary.d.ts.map +1 -1
- package/lib/itinerary.js +4 -3
- package/lib/itinerary.js.map +1 -1
- package/lib/query-gen.d.ts +53 -0
- package/lib/query-gen.d.ts.map +1 -0
- package/lib/query-gen.js +162 -0
- package/lib/query-gen.js.map +1 -0
- package/lib/state.d.ts +1 -0
- package/lib/state.d.ts.map +1 -0
- package/lib/state.js +2 -0
- package/lib/state.js.map +1 -0
- package/package.json +5 -3
- package/src/__tests__/query-params.js +184 -0
- package/src/graphql.d.ts +7 -0
- package/src/index.ts +2 -0
- package/src/itinerary.ts +14 -8
- package/src/planQuery.graphql +141 -0
- package/src/query-gen.ts +206 -0
- package/src/state.ts +0 -0
- package/tsconfig.json +1 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { reduceOtpFlexModes } from "../query";
|
|
2
2
|
import queryParams, { getCustomQueryParams } from "../query-params";
|
|
3
|
+
import { extractAdditionalModes, generateCombinations } from "../query-gen";
|
|
3
4
|
|
|
4
5
|
const customWalkDistanceOptions = [
|
|
5
6
|
{
|
|
@@ -12,6 +13,189 @@ const customWalkDistanceOptions = [
|
|
|
12
13
|
}
|
|
13
14
|
];
|
|
14
15
|
|
|
16
|
+
function modeStrToTransportMode(m) {
|
|
17
|
+
const splitVals = m.split("_");
|
|
18
|
+
return {
|
|
19
|
+
mode: splitVals[0],
|
|
20
|
+
qualifier: splitVals?.[1] || null
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const mockLatLon = {
|
|
25
|
+
lat: 1,
|
|
26
|
+
lon: 2
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
function expectModes(modes, expectedModes) {
|
|
30
|
+
const generatedModesList = generateCombinations({
|
|
31
|
+
modes: modes.map(modeStrToTransportMode),
|
|
32
|
+
to: mockLatLon,
|
|
33
|
+
from: mockLatLon,
|
|
34
|
+
modeSettings: []
|
|
35
|
+
});
|
|
36
|
+
const expandedExpectedModesList = expectedModes.map(em => ({
|
|
37
|
+
modes: em.map(modeStrToTransportMode),
|
|
38
|
+
to: mockLatLon,
|
|
39
|
+
from: mockLatLon,
|
|
40
|
+
modeSettings: []
|
|
41
|
+
}));
|
|
42
|
+
return it(
|
|
43
|
+
modes.join(" "),
|
|
44
|
+
() =>
|
|
45
|
+
expect(generatedModesList.length === expandedExpectedModesList.length) &&
|
|
46
|
+
expect(new Set(generatedModesList)).toEqual(
|
|
47
|
+
new Set(expandedExpectedModesList)
|
|
48
|
+
)
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
describe("extract-modes", () => {
|
|
53
|
+
const mode = {
|
|
54
|
+
mode: "UNICYCLE"
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const testTransportMode = {
|
|
58
|
+
mode: "testMode"
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const checkboxModeSetting = {
|
|
62
|
+
type: "CHECKBOX",
|
|
63
|
+
icon: null,
|
|
64
|
+
label: "test",
|
|
65
|
+
applicableMode: testTransportMode.mode,
|
|
66
|
+
key: "test",
|
|
67
|
+
value: true,
|
|
68
|
+
addTransportMode: mode
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const dropdownModeSetting = {
|
|
72
|
+
type: "DROPDOWN",
|
|
73
|
+
label: "test",
|
|
74
|
+
applicableMode: testTransportMode.mode,
|
|
75
|
+
key: "test",
|
|
76
|
+
options: [{ text: "testop", value: "1", addTransportMode: mode }],
|
|
77
|
+
value: "1"
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
it("a checkbox setting", () => {
|
|
81
|
+
expect(
|
|
82
|
+
extractAdditionalModes([checkboxModeSetting], [testTransportMode])
|
|
83
|
+
).toEqual([mode]);
|
|
84
|
+
});
|
|
85
|
+
it("a dropdown setting", () => {
|
|
86
|
+
expect(
|
|
87
|
+
extractAdditionalModes([dropdownModeSetting], [testTransportMode])
|
|
88
|
+
).toEqual([mode]);
|
|
89
|
+
});
|
|
90
|
+
it("a disabled mode setting", () => {
|
|
91
|
+
expect(
|
|
92
|
+
extractAdditionalModes(
|
|
93
|
+
[{ ...checkboxModeSetting, value: false }],
|
|
94
|
+
[testTransportMode]
|
|
95
|
+
)
|
|
96
|
+
).toEqual([]);
|
|
97
|
+
});
|
|
98
|
+
it("a setting from a disabled mode", () => {
|
|
99
|
+
expect(extractAdditionalModes([{ ...checkboxModeSetting }], [])).toEqual(
|
|
100
|
+
[]
|
|
101
|
+
);
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
describe("query-gen", () => {
|
|
106
|
+
describe("generateCombinations", () => {
|
|
107
|
+
expectModes(["WALK"], [["WALK"]]);
|
|
108
|
+
expectModes(["WALK", "TRANSIT"], [["WALK"], ["TRANSIT"]]);
|
|
109
|
+
expectModes(
|
|
110
|
+
["WALK", "TRANSIT", "BICYCLE"],
|
|
111
|
+
[["WALK"], ["TRANSIT"], ["BICYCLE"], ["TRANSIT", "BICYCLE"]]
|
|
112
|
+
);
|
|
113
|
+
expectModes(
|
|
114
|
+
["WALK", "TRANSIT", "CAR"],
|
|
115
|
+
[["WALK"], ["TRANSIT"], ["TRANSIT", "CAR"]]
|
|
116
|
+
);
|
|
117
|
+
expectModes(["TRANSIT", "CAR"], [["TRANSIT"], ["TRANSIT", "CAR"]]);
|
|
118
|
+
expectModes(["CAR"], []);
|
|
119
|
+
expectModes(
|
|
120
|
+
["WALK", "TRANSIT", "BICYCLE", "CAR"],
|
|
121
|
+
[
|
|
122
|
+
["WALK"],
|
|
123
|
+
["TRANSIT"],
|
|
124
|
+
["TRANSIT", "BICYCLE"],
|
|
125
|
+
["BICYCLE"],
|
|
126
|
+
["TRANSIT", "CAR"]
|
|
127
|
+
]
|
|
128
|
+
);
|
|
129
|
+
expectModes(
|
|
130
|
+
["BICYCLE_RENT", "TRANSIT", "WALK"],
|
|
131
|
+
[
|
|
132
|
+
["TRANSIT"],
|
|
133
|
+
["BICYCLE_RENT", "TRANSIT"],
|
|
134
|
+
["BICYCLE_RENT", "WALK"],
|
|
135
|
+
["WALK"]
|
|
136
|
+
]
|
|
137
|
+
);
|
|
138
|
+
expectModes(
|
|
139
|
+
["BICYCLE_RENT", "BICYCLE", "TRANSIT", "WALK"],
|
|
140
|
+
[
|
|
141
|
+
["TRANSIT"],
|
|
142
|
+
["BICYCLE_RENT", "TRANSIT"],
|
|
143
|
+
["BICYCLE", "TRANSIT"],
|
|
144
|
+
["BICYCLE_RENT", "WALK"],
|
|
145
|
+
["BICYCLE"],
|
|
146
|
+
["WALK"]
|
|
147
|
+
]
|
|
148
|
+
);
|
|
149
|
+
expectModes(
|
|
150
|
+
["SCOOTER_RENT", "BICYCLE_RENT", "TRANSIT", "WALK"],
|
|
151
|
+
[
|
|
152
|
+
["TRANSIT"],
|
|
153
|
+
["BICYCLE_RENT", "TRANSIT"],
|
|
154
|
+
["BICYCLE_RENT", "WALK"],
|
|
155
|
+
["SCOOTER_RENT", "TRANSIT"],
|
|
156
|
+
["SCOOTER_RENT", "WALK"],
|
|
157
|
+
["WALK"]
|
|
158
|
+
]
|
|
159
|
+
);
|
|
160
|
+
expectModes(
|
|
161
|
+
["FLEX", "TRANSIT", "WALK"],
|
|
162
|
+
[["TRANSIT"], ["FLEX", "TRANSIT"], ["FLEX", "WALK"], ["WALK"]]
|
|
163
|
+
);
|
|
164
|
+
expectModes(
|
|
165
|
+
["FLEX", "SCOOTER_RENT", "TRANSIT", "WALK"],
|
|
166
|
+
[
|
|
167
|
+
["TRANSIT"],
|
|
168
|
+
["FLEX", "TRANSIT"],
|
|
169
|
+
["WALK"],
|
|
170
|
+
["FLEX", "WALK"],
|
|
171
|
+
["FLEX", "SCOOTER_RENT", "WALK"], // Is this sensible?
|
|
172
|
+
["FLEX", "SCOOTER_RENT", "TRANSIT"],
|
|
173
|
+
["SCOOTER_RENT", "WALK"],
|
|
174
|
+
["SCOOTER_RENT", "TRANSIT"]
|
|
175
|
+
]
|
|
176
|
+
);
|
|
177
|
+
expectModes(
|
|
178
|
+
["FLEX", "SCOOTER_RENT", "TRANSIT"],
|
|
179
|
+
[
|
|
180
|
+
["TRANSIT"],
|
|
181
|
+
["FLEX", "TRANSIT"],
|
|
182
|
+
["FLEX", "SCOOTER_RENT", "TRANSIT"],
|
|
183
|
+
["SCOOTER_RENT", "TRANSIT"]
|
|
184
|
+
]
|
|
185
|
+
);
|
|
186
|
+
expectModes(
|
|
187
|
+
// Transit is required to enable other transit submodes
|
|
188
|
+
["BUS", "RAIL", "GONDOLA", "TRAM", "TRANSIT"],
|
|
189
|
+
[["BUS", "RAIL", "GONDOLA", "TRAM"]]
|
|
190
|
+
);
|
|
191
|
+
expectModes(
|
|
192
|
+
// Transit is required to enable other transit submodes
|
|
193
|
+
["TRANSIT"],
|
|
194
|
+
[["TRANSIT"]]
|
|
195
|
+
);
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
|
|
15
199
|
describe("query-params", () => {
|
|
16
200
|
describe("getCustomQueryParams", () => {
|
|
17
201
|
it("should return the original unmodified queryParams if no customizations", () => {
|
package/src/graphql.d.ts
ADDED
package/src/index.ts
CHANGED
|
@@ -7,12 +7,14 @@ import * as route from "./route";
|
|
|
7
7
|
import * as storage from "./storage";
|
|
8
8
|
import * as time from "./time";
|
|
9
9
|
import * as ui from "./ui";
|
|
10
|
+
import * as queryGen from "./query-gen";
|
|
10
11
|
|
|
11
12
|
const core = {
|
|
12
13
|
itinerary,
|
|
13
14
|
map,
|
|
14
15
|
profile,
|
|
15
16
|
query,
|
|
17
|
+
queryGen,
|
|
16
18
|
queryParams,
|
|
17
19
|
route,
|
|
18
20
|
storage,
|
package/src/itinerary.ts
CHANGED
|
@@ -4,9 +4,10 @@ import {
|
|
|
4
4
|
Config,
|
|
5
5
|
ElevationProfile,
|
|
6
6
|
FlexBookingInfo,
|
|
7
|
-
|
|
7
|
+
ItineraryOnlyLegsRequired,
|
|
8
8
|
LatLngArray,
|
|
9
9
|
Leg,
|
|
10
|
+
MassUnitOption,
|
|
10
11
|
Money,
|
|
11
12
|
Place,
|
|
12
13
|
Step,
|
|
@@ -212,7 +213,9 @@ export function getCompanyFromLeg(leg: Leg): string {
|
|
|
212
213
|
return null;
|
|
213
214
|
}
|
|
214
215
|
|
|
215
|
-
export function getItineraryBounds(
|
|
216
|
+
export function getItineraryBounds(
|
|
217
|
+
itinerary: ItineraryOnlyLegsRequired
|
|
218
|
+
): LatLngArray[] {
|
|
216
219
|
let coords = [];
|
|
217
220
|
itinerary.legs.forEach(leg => {
|
|
218
221
|
const legCoords = polyline
|
|
@@ -407,7 +410,7 @@ export function getTNCLocation(leg: Leg, type: string): string {
|
|
|
407
410
|
}
|
|
408
411
|
|
|
409
412
|
export function calculatePhysicalActivity(
|
|
410
|
-
itinerary:
|
|
413
|
+
itinerary: ItineraryOnlyLegsRequired
|
|
411
414
|
): {
|
|
412
415
|
bikeDuration: number;
|
|
413
416
|
caloriesBurned: number;
|
|
@@ -433,7 +436,9 @@ export function calculatePhysicalActivity(
|
|
|
433
436
|
* these values and currency info.
|
|
434
437
|
* It is assumed that the same currency is used for all TNC legs.
|
|
435
438
|
*/
|
|
436
|
-
export function calculateTncFares(
|
|
439
|
+
export function calculateTncFares(
|
|
440
|
+
itinerary: ItineraryOnlyLegsRequired
|
|
441
|
+
): TncFare {
|
|
437
442
|
return itinerary.legs
|
|
438
443
|
.filter(leg => leg.mode === "CAR" && leg.hailedCar && leg.tncData)
|
|
439
444
|
.reduce(
|
|
@@ -501,15 +506,16 @@ const CARBON_INTENSITY_DEFAULTS = {
|
|
|
501
506
|
};
|
|
502
507
|
|
|
503
508
|
/**
|
|
504
|
-
* @param
|
|
505
|
-
* @param
|
|
509
|
+
* @param {itinerary} itinerary OTP trip itinierary, only legs is required.
|
|
510
|
+
* @param {carbonIntensity} carbonIntensity carbon intensity by mode in grams/meter
|
|
506
511
|
* @param {units} units units to be used in return value
|
|
507
512
|
* @return Amount of carbon in chosen unit
|
|
508
513
|
*/
|
|
509
514
|
export function calculateEmissions(
|
|
510
|
-
|
|
515
|
+
// This type makes all the properties from Itinerary optional except legs.
|
|
516
|
+
itinerary: ItineraryOnlyLegsRequired,
|
|
511
517
|
carbonIntensity: Record<string, number> = {},
|
|
512
|
-
units?:
|
|
518
|
+
units?: MassUnitOption
|
|
513
519
|
): number {
|
|
514
520
|
// Apply defaults for any values that we don't have.
|
|
515
521
|
const carbonIntensityWithDefaults = {
|
|
@@ -0,0 +1,141 @@
|
|
|
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,
|
|
16
|
+
) {
|
|
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
|
+
alerts {
|
|
55
|
+
alertHeaderText
|
|
56
|
+
alertDescriptionText
|
|
57
|
+
alertUrl
|
|
58
|
+
effectiveStartDate
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
legGeometry {
|
|
62
|
+
length
|
|
63
|
+
points
|
|
64
|
+
}
|
|
65
|
+
intermediateStops {
|
|
66
|
+
lat
|
|
67
|
+
lon
|
|
68
|
+
name
|
|
69
|
+
stopCode: code
|
|
70
|
+
stopId: id
|
|
71
|
+
locationType
|
|
72
|
+
}
|
|
73
|
+
route {
|
|
74
|
+
shortName
|
|
75
|
+
color
|
|
76
|
+
textColor
|
|
77
|
+
id
|
|
78
|
+
type
|
|
79
|
+
alerts {
|
|
80
|
+
alertHeaderText
|
|
81
|
+
alertDescriptionText
|
|
82
|
+
alertUrl
|
|
83
|
+
effectiveStartDate
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
from {
|
|
87
|
+
lat
|
|
88
|
+
lon
|
|
89
|
+
name
|
|
90
|
+
vertexType
|
|
91
|
+
stop {
|
|
92
|
+
id
|
|
93
|
+
code
|
|
94
|
+
alerts {
|
|
95
|
+
alertHeaderText
|
|
96
|
+
alertDescriptionText
|
|
97
|
+
alertUrl
|
|
98
|
+
effectiveStartDate
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
to {
|
|
103
|
+
lat
|
|
104
|
+
lon
|
|
105
|
+
name
|
|
106
|
+
vertexType
|
|
107
|
+
stop {
|
|
108
|
+
id
|
|
109
|
+
code
|
|
110
|
+
alerts {
|
|
111
|
+
alertHeaderText
|
|
112
|
+
alertDescriptionText
|
|
113
|
+
alertUrl
|
|
114
|
+
effectiveStartDate
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
steps {
|
|
119
|
+
distance
|
|
120
|
+
lat
|
|
121
|
+
lon
|
|
122
|
+
relativeDirection
|
|
123
|
+
absoluteDirection
|
|
124
|
+
stayOn
|
|
125
|
+
streetName
|
|
126
|
+
area
|
|
127
|
+
alerts{
|
|
128
|
+
alertHeaderText
|
|
129
|
+
alertDescriptionText
|
|
130
|
+
alertUrl
|
|
131
|
+
effectiveStartDate
|
|
132
|
+
}
|
|
133
|
+
elevationProfile {
|
|
134
|
+
distance
|
|
135
|
+
elevation
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
package/src/query-gen.ts
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
2
|
+
import { print } from "graphql";
|
|
3
|
+
import {
|
|
4
|
+
ModeSetting,
|
|
5
|
+
ModeSettingValues,
|
|
6
|
+
TransportMode
|
|
7
|
+
} from "@opentripplanner/types";
|
|
8
|
+
import { LonLatOutput } from "@conveyal/lonlat";
|
|
9
|
+
import PlanQuery from "./planQuery.graphql";
|
|
10
|
+
|
|
11
|
+
type OTPQueryParams = {
|
|
12
|
+
to: LonLatOutput & { name?: string };
|
|
13
|
+
from: LonLatOutput & { name?: string };
|
|
14
|
+
modes: Array<TransportMode>;
|
|
15
|
+
modeSettings: ModeSetting[];
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
type GraphQLQuery = {
|
|
19
|
+
query: string;
|
|
20
|
+
variables: Record<string, unknown>;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Mode Settings can contain additional modes to add to the query,
|
|
25
|
+
* this function extracts those additional modes from the settings
|
|
26
|
+
* and returns them in an array.
|
|
27
|
+
* @param modeSettings List of mode settings with values populated
|
|
28
|
+
* @returns Additional transport modes to add to query
|
|
29
|
+
*/
|
|
30
|
+
export function extractAdditionalModes(
|
|
31
|
+
modeSettings: ModeSetting[],
|
|
32
|
+
enabledModes: TransportMode[]
|
|
33
|
+
): Array<TransportMode> {
|
|
34
|
+
return modeSettings.reduce<TransportMode[]>((prev, cur) => {
|
|
35
|
+
// First, ensure that the mode associated with this setting is even enabled
|
|
36
|
+
if (!enabledModes.map(m => m.mode).includes(cur.applicableMode)) {
|
|
37
|
+
return prev;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// In checkboxes, mode must be enabled and have a transport mode in it
|
|
41
|
+
if (cur.type === "CHECKBOX" && cur.addTransportMode && cur.value) {
|
|
42
|
+
return [...prev, cur.addTransportMode];
|
|
43
|
+
}
|
|
44
|
+
if (cur.type === "DROPDOWN") {
|
|
45
|
+
const transportMode = cur.options.find(o => o.value === cur.value)
|
|
46
|
+
.addTransportMode;
|
|
47
|
+
if (transportMode) {
|
|
48
|
+
return [
|
|
49
|
+
...prev,
|
|
50
|
+
cur.options.find(o => o.value === cur.value).addTransportMode
|
|
51
|
+
];
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return prev;
|
|
55
|
+
}, []);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Generates every possible mathematical subset of the input TransportModes.
|
|
60
|
+
* Uses code from:
|
|
61
|
+
* https://stackoverflow.com/questions/5752002/find-all-possible-subset-combos-in-an-array
|
|
62
|
+
* @param array Array of input transport modes
|
|
63
|
+
* @returns 2D array representing every possible subset of transport modes from input
|
|
64
|
+
*/
|
|
65
|
+
function combinations(array: TransportMode[]): TransportMode[][] {
|
|
66
|
+
if (!array) return [];
|
|
67
|
+
return (
|
|
68
|
+
// eslint-disable-next-line no-bitwise
|
|
69
|
+
new Array(1 << array.length)
|
|
70
|
+
.fill(null)
|
|
71
|
+
// eslint-disable-next-line no-bitwise
|
|
72
|
+
.map((e1, i) => array.filter((e2, j) => i & (1 << j)))
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export const SIMPLIFICATIONS = {
|
|
77
|
+
AIRPLANE: "TRANSIT",
|
|
78
|
+
BICYCLE: "PERSONAL",
|
|
79
|
+
BUS: "TRANSIT",
|
|
80
|
+
CABLE_CAR: "TRANSIT",
|
|
81
|
+
CAR: "CAR",
|
|
82
|
+
FERRY: "TRANSIT",
|
|
83
|
+
FLEX: "SHARED", // TODO: this allows FLEX+WALK. Is this reasonable?
|
|
84
|
+
FUNICULAR: "TRANSIT",
|
|
85
|
+
GONDOLA: "TRANSIT",
|
|
86
|
+
RAIL: "TRANSIT",
|
|
87
|
+
SCOOTER: "PERSONAL",
|
|
88
|
+
SUBWAY: "TRANSIT",
|
|
89
|
+
TRAM: "TRANSIT",
|
|
90
|
+
TRANSIT: "TRANSIT",
|
|
91
|
+
WALK: "WALK"
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
export const TRANSIT_SUBMODES = Object.keys(SIMPLIFICATIONS).filter(
|
|
95
|
+
mode => SIMPLIFICATIONS[mode] === "TRANSIT" && mode !== "TRANSIT"
|
|
96
|
+
);
|
|
97
|
+
export const TRANSIT_SUBMODES_AND_TRANSIT = Object.keys(SIMPLIFICATIONS).filter(
|
|
98
|
+
mode => SIMPLIFICATIONS[mode] === "TRANSIT"
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Generates list of queries for OTP to get a comprehensive
|
|
103
|
+
* set of results based on the modes input.
|
|
104
|
+
* @param params OTP Query Params
|
|
105
|
+
* @returns Set of parameters to generate queries
|
|
106
|
+
*/
|
|
107
|
+
export function generateCombinations(params: OTPQueryParams): OTPQueryParams[] {
|
|
108
|
+
const VALID_COMBOS = [
|
|
109
|
+
["WALK"],
|
|
110
|
+
["PERSONAL"],
|
|
111
|
+
["TRANSIT", "SHARED"],
|
|
112
|
+
["WALK", "SHARED"],
|
|
113
|
+
["TRANSIT"],
|
|
114
|
+
["TRANSIT", "PERSONAL"],
|
|
115
|
+
["TRANSIT", "CAR"]
|
|
116
|
+
];
|
|
117
|
+
|
|
118
|
+
const BANNED_TOGETHER = ["SCOOTER", "BICYCLE"];
|
|
119
|
+
|
|
120
|
+
const completeModeList = [
|
|
121
|
+
...extractAdditionalModes(params.modeSettings, params.modes),
|
|
122
|
+
...params.modes
|
|
123
|
+
];
|
|
124
|
+
|
|
125
|
+
// List of the transit *submodes* that are included in the input params
|
|
126
|
+
const queryTransitSubmodes = completeModeList
|
|
127
|
+
.filter(mode => TRANSIT_SUBMODES.includes(mode.mode))
|
|
128
|
+
.map(mode => mode.mode);
|
|
129
|
+
|
|
130
|
+
return combinations(completeModeList)
|
|
131
|
+
.filter(combo => {
|
|
132
|
+
if (combo.length === 0) return false;
|
|
133
|
+
|
|
134
|
+
// All current qualifiers currently simplify to "SHARED"
|
|
135
|
+
const simplifiedModes = Array.from(
|
|
136
|
+
new Set(
|
|
137
|
+
combo.map(c => (c.qualifier ? "SHARED" : SIMPLIFICATIONS[c.mode]))
|
|
138
|
+
)
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
// Ensure that if we have one transit mode, then we include ALL transit modes
|
|
142
|
+
if (simplifiedModes.includes("TRANSIT")) {
|
|
143
|
+
// Don't allow TRANSIT along with any other submodes
|
|
144
|
+
if (
|
|
145
|
+
queryTransitSubmodes.length &&
|
|
146
|
+
combo.find(c => c.mode === "TRANSIT")
|
|
147
|
+
) {
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (
|
|
152
|
+
combo.reduce((prev, cur) => {
|
|
153
|
+
if (queryTransitSubmodes.includes(cur.mode)) {
|
|
154
|
+
return prev - 1;
|
|
155
|
+
}
|
|
156
|
+
return prev;
|
|
157
|
+
}, queryTransitSubmodes.length) !== 0
|
|
158
|
+
) {
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
// Continue to the other checks
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// OTP doesn't support multiple non-walk modes
|
|
165
|
+
if (BANNED_TOGETHER.every(m => combo.find(c => c.mode === m)))
|
|
166
|
+
return false;
|
|
167
|
+
|
|
168
|
+
return !!VALID_COMBOS.find(
|
|
169
|
+
vc =>
|
|
170
|
+
simplifiedModes.every(m => vc.includes(m)) &&
|
|
171
|
+
vc.every(m => simplifiedModes.includes(m))
|
|
172
|
+
);
|
|
173
|
+
})
|
|
174
|
+
.map(combo => ({ ...params, modes: combo }));
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// eslint-disable-next-line import/prefer-default-export
|
|
178
|
+
export function generateOtp2Query(params: OTPQueryParams): GraphQLQuery {
|
|
179
|
+
const { to, from, modeSettings } = params;
|
|
180
|
+
|
|
181
|
+
// This extracts the values from the mode settings to key value pairs
|
|
182
|
+
const modeSettingValues = modeSettings.reduce((prev, cur) => {
|
|
183
|
+
prev[cur.key] = cur.value;
|
|
184
|
+
return prev;
|
|
185
|
+
}, {}) as ModeSettingValues;
|
|
186
|
+
|
|
187
|
+
const {
|
|
188
|
+
walkReluctance,
|
|
189
|
+
wheelchair,
|
|
190
|
+
bikeReluctance,
|
|
191
|
+
carReluctance
|
|
192
|
+
} = modeSettingValues;
|
|
193
|
+
|
|
194
|
+
return {
|
|
195
|
+
query: print(PlanQuery),
|
|
196
|
+
variables: {
|
|
197
|
+
fromPlace: `${from.name}::${from.lat},${from.lon}}`,
|
|
198
|
+
toPlace: `${to.name}::${to.lat},${to.lon}}`,
|
|
199
|
+
modes: params.modes,
|
|
200
|
+
walkReluctance,
|
|
201
|
+
wheelchair,
|
|
202
|
+
bikeReluctance,
|
|
203
|
+
carReluctance
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
}
|
package/src/state.ts
ADDED
|
File without changes
|