connection-scan-algorithm
Version:
Connection Scan Algorithm
156 lines (155 loc) • 5.37 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const gtfs = require("gtfs-stream");
const ts_array_utils_1 = require("ts-array-utils");
const Service_1 = require("./Service");
/**
* Returns trips, transfers, interchange time and calendars from a GTFS zip.
*/
class GtfsLoader {
constructor(timeParser) {
this.timeParser = timeParser;
}
load(input) {
return new Promise(resolve => {
const processor = new StatefulGtfsLoader(this.timeParser);
input
.pipe(gtfs({ raw: true }))
.on("data", entity => processor[entity.type] && processor[entity.type](entity.data))
.on("end", () => resolve(processor.finalize()));
});
}
}
exports.GtfsLoader = GtfsLoader;
/**
* Encapsulation of the GTFS data while it is being loaded from the zip
*/
class StatefulGtfsLoader {
constructor(timeParser) {
this.timeParser = timeParser;
this.trips = [];
this.transfers = {};
this.interchange = {};
this.calendars = {};
this.dates = {};
this.stopTimes = {};
this.stops = {};
}
link(row) {
const t = {
origin: row.from_stop_id,
destination: row.to_stop_id,
duration: +row.duration,
startTime: this.timeParser.getTime(row.start_time),
endTime: this.timeParser.getTime(row.end_time)
};
ts_array_utils_1.pushNested(t, this.transfers, row.from_stop_id);
}
calendar(row) {
this.calendars[row.service_id] = {
serviceId: row.service_id,
startDate: +row.start_date,
endDate: +row.end_date,
days: {
0: row.sunday === "1",
1: row.monday === "1",
2: row.tuesday === "1",
3: row.wednesday === "1",
4: row.thursday === "1",
5: row.friday === "1",
6: row.saturday === "1"
},
include: {},
exclude: {}
};
}
calendar_date(row) {
ts_array_utils_1.setNested(row.exception_type === "1", this.dates, row.service_id, row.date);
}
trip(row) {
this.trips.push({ serviceId: row.service_id, tripId: row.trip_id, stopTimes: [], service: {} });
}
stop_time(row) {
const stopTime = {
stop: row.stop_id,
departureTime: this.timeParser.getTime(row.departure_time),
arrivalTime: this.timeParser.getTime(row.arrival_time),
pickUp: row.pickup_type === "0",
dropOff: row.drop_off_type === "0"
};
ts_array_utils_1.pushNested(stopTime, this.stopTimes, row.trip_id);
}
transfer(row) {
if (row.from_stop_id === row.to_stop_id) {
this.interchange[row.from_stop_id] = +row.min_transfer_time;
}
else {
const t = {
origin: row.from_stop_id,
destination: row.to_stop_id,
duration: +row.min_transfer_time,
startTime: 0,
endTime: Number.MAX_SAFE_INTEGER
};
ts_array_utils_1.pushNested(t, this.transfers, row.from_stop_id);
}
}
stop(row) {
const stop = {
id: row.stop_id,
code: row.stop_code,
name: row.stop_name,
description: row.stop_desc,
latitude: +row.stop_lat,
longitude: +row.stop_lon,
timezone: row.zone_id
};
ts_array_utils_1.setNested(stop, this.stops, row.stop_id);
}
finalize() {
const services = {};
const connections = [];
for (const c of Object.values(this.calendars)) {
services[c.serviceId] = new Service_1.Service(c.startDate, c.endDate, c.days, this.dates[c.serviceId] || {});
}
for (const t of this.trips) {
t.stopTimes = this.stopTimes[t.tripId];
t.service = services[t.serviceId];
connections.push(...this.getConnectionsFromTrip(t));
}
connections.sort((a, b) => a.arrivalTime - b.arrivalTime);
for (const stop of Object.keys(this.stops)) {
this.transfers[stop] = this.transfers[stop] || [];
}
return { connections, transfers: this.transfers, interchange: this.interchange, stops: this.stops };
}
getConnectionsFromTrip(t) {
const connections = [];
for (let i = 0; i < t.stopTimes.length - 1; i++) {
if (t.stopTimes[i].pickUp) {
const j = this.getNextDropOff(t.stopTimes, i);
if (j !== -1) {
connections.push({
origin: t.stopTimes[i].stop,
destination: t.stopTimes[j].stop,
departureTime: t.stopTimes[i].departureTime,
arrivalTime: t.stopTimes[j].arrivalTime,
trip: t
});
}
}
}
return connections;
}
/**
* Return the index of the first drop off point
*/
getNextDropOff(rows, i) {
for (let j = i + 1; j < rows.length; j++) {
if (rows[j].dropOff) {
return j;
}
}
return -1;
}
}