-
Notifications
You must be signed in to change notification settings - Fork 3
/
compute-trajectories.js
105 lines (92 loc) · 3.05 KB
/
compute-trajectories.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
93
94
95
96
97
98
99
100
101
102
103
104
105
'use strict'
const debug = require('debug')('gtfs-utils:compute-trajectories')
const {unique: shorthash} = require('shorthash')
const readTrips = require('./read-trips')
const inMemoryStore = require('./lib/in-memory-store')
const {STOP, STATION} = require('./lib/location-types')
const readShapes = require('./read-shapes')
const computeSchedules = require('./compute-schedules')
const buildTrajectory = require('./lib/build-trajectory')
const computeTrajectories = async function* (readFile, filters = {}, opt = {}) {
if (typeof readFile !== 'function') {
throw new TypeError('readFile must be a function')
}
const {
stop: stopFilter,
} = {
stop: () => true,
...filters,
}
if (typeof stopFilter !== 'function') {
throw new TypeError('filters.stop must be a function')
}
const {
createStore,
} = {
createStore: inMemoryStore,
...opt,
}
debug('reading trips')
const trips = await readTrips(readFile, filters, {
...opt,
formatTrip: t => [t.shape_id, t.service_id],
})
debug('reading stop locations')
const stopLocsById = createStore() // stop ID -> stop location
for await (const s of await readFile('stops')) {
if (
s.location_type !== undefined && s.location_type !== ''
&& s.location_type !== STOP && s.location_type !== STATION
) continue
if (!stopFilter(s)) continue
const loc = [parseFloat(s.stop_lon), parseFloat(s.stop_lat)]
await stopLocsById.set(s.stop_id, loc)
}
debug('reading shapes')
const shapesById = createStore() // shape ID -> shape
// todo: only read shapes that belong to non-filtered trips
for await (const [shapeId, points] of readShapes(readFile, filters)) {
await shapesById.set(shapeId, points)
}
debug('computing schedules')
const schedules = await computeSchedules(readFile, filters, opt)
debug('computing trajectories')
let i = 0
for await (const schedule of schedules.values()) {
if (++i % 100 === 0) debug(i)
// Taking only the list of stops alon into account wouldn't suffice,
// as there might be two trips that visit the same stops with vastly
// different timing. Same with using the list of arrivals/departures only.
const temporalSig = shorthash(JSON.stringify({
stops: schedule.stops,
arrivals: schedule.arrivals,
departures: schedule.departures,
}))
for (const _ of schedule.trips) {
const {tripId, start: tripStartTime} = _
const [shapeId, serviceId] = await trips.get(tripId)
if (!shapeId) {
// todo: is this a bug?
debug('missing shape ID for trip ID', tripId)
continue
}
if (!serviceId) {
debug('missing service ID for trip ID', tripId)
continue
}
const shape = await shapesById.get(shapeId)
if (!shapeId) {
// todo: is this a bug?
debug('missing shape for shape ID', shapeId)
continue
}
// todo: support headway-based (frequencies.txt) trips
const tr = await buildTrajectory(shapeId, shape, schedule, stopLocsById, tripStartTime)
tr.properties.tripId = tripId
tr.properties.serviceId = serviceId
tr.properties.id = temporalSig + '-' + shapeId
yield tr
}
}
}
module.exports = computeTrajectories