-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathfind-alternative-trips.js
92 lines (77 loc) · 2.95 KB
/
find-alternative-trips.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
'use strict'
const debug = require('debug')('gtfs-utils:find-alternative-trips')
const readStopTimezones = require('./lib/read-stop-timezones')
const inMemoryStore = require('./lib/in-memory-store')
const readTrips = require('./read-trips')
const resolveTime = require('./lib/resolve-time')
const isObj = o => 'object' === typeof o && o !== null && !Array.isArray(o)
const createFindAlternativeTrips = async (readFile, timezone, services, schedules) => {
debug('reading stops.stop_timezone')
// stop.stop_id -> stop.stop_timezone || parent.stop_timezone
const stopTimezones = await readStopTimezones(readFile, {
stop: () => true,
}, inMemoryStore)
debug('reading trips')
const svcIdsRouteIdsByTrip = await readTrips(readFile, {}, {
formatTrip: t => [t.service_id, t.route_id],
})
const findAlternativeTrips = async function* (fromId, tDep, toId, tArr) {
if ('string' !== typeof fromId || !fromId) {
throw new Error('fromId must be a non-empty string.')
}
if ('number' !== typeof tDep) {
throw new Error('tDep must be a number.')
}
if ('string' !== typeof toId || !toId) {
throw new Error('toId must be a non-empty string.')
}
if ('number' !== typeof tArr) {
throw new Error('tArr must be a number.')
}
if (fromId === toId) {
throw new Error('fromId and toId must be different.')
}
if (tDep >= tArr) throw new Error('tDep must be < tArr.')
const fromTz = await stopTimezones.get(fromId) || timezone
const toTz = await stopTimezones.get(toId) || timezone
for await (const sched of schedules.values()) {
// Does it run from `fromId` to `toId`?
const fromI = sched.stops.indexOf(fromId)
if (fromI === -1) continue
const toI = sched.stops.indexOf(toId)
if (toI === -1) continue
if (toI < fromI) continue
// Does it take too long?
const dTDep = sched.departures[fromI]
const dTArr = sched.arrivals[toI]
if (dTArr - dTDep > tArr - tDep) continue // todo: add threshold?
// Does it run at the right point in time?
// For each trip that follows this schedule, find its service and
// sort out by absolute departure/arrival times.
for (const {tripId, start} of sched.trips) {
const _ = await svcIdsRouteIdsByTrip.get(tripId)
if (!_) continue // invalid `tripId` or unknwon trip
const [svcId, routeId] = _
const dates = await services.get(svcId)
if (!dates) continue // invalid service ID
// todo: replace by `for ... of` loop once they're fast enough
for (let svcI = 0; svcI < dates.length; svcI++) {
const date = dates[svcI]
const tAltDep = resolveTime(fromTz, date, start + dTDep)
if (tAltDep < tDep) continue // departs too early
const tAltArr = resolveTime(toTz, date, start + dTArr)
if (tAltArr > tArr) continue // arrives too late
yield {
tripId,
routeId: routeId,
serviceId: svcId,
arrival: tAltArr,
departure: tAltDep
}
}
}
}
}
return findAlternativeTrips
}
module.exports = createFindAlternativeTrips