Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 100 additions & 13 deletions src/gtfs/__tests__/transfers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ describe('GTFS transfers parser', () => {
],
]);

const result = await parseTransfers(mockedStream, stopsMap);
const result = await parseTransfers(mockedStream, stopsMap, new Set());

const expectedTransfers = new Map([
[
Expand Down Expand Up @@ -148,7 +148,7 @@ describe('GTFS transfers parser', () => {
],
]);

const result = await parseTransfers(mockedStream, stopsMap);
const result = await parseTransfers(mockedStream, stopsMap, new Set());

assert.deepEqual(result.transfers, new Map());
assert.deepEqual(result.tripContinuations, []);
Expand Down Expand Up @@ -186,7 +186,7 @@ describe('GTFS transfers parser', () => {
],
]);

const result = await parseTransfers(mockedStream, stopsMap);
const result = await parseTransfers(mockedStream, stopsMap, new Set());

assert.deepEqual(result.transfers, new Map());
assert.deepEqual(result.tripContinuations, []);
Expand Down Expand Up @@ -244,7 +244,7 @@ describe('GTFS transfers parser', () => {
],
]);

const result = await parseTransfers(mockedStream, stopsMap);
const result = await parseTransfers(mockedStream, stopsMap, new Set());

const expectedTripContinuations = [
{
Expand Down Expand Up @@ -318,7 +318,7 @@ describe('GTFS transfers parser', () => {
],
]);

const result = await parseTransfers(mockedStream, stopsMap);
const result = await parseTransfers(mockedStream, stopsMap, new Set());

const expectedGuaranteedTripTransfers = [
{
Expand Down Expand Up @@ -398,7 +398,7 @@ describe('GTFS transfers parser', () => {
],
]);

const result = await parseTransfers(mockedStream, stopsMap);
const result = await parseTransfers(mockedStream, stopsMap, new Set());

// Type 1 with trip IDs -> guaranteedTripTransfers
const expectedGuaranteedTripTransfers = [
Expand Down Expand Up @@ -486,7 +486,7 @@ describe('GTFS transfers parser', () => {
],
]);

const result = await parseTransfers(mockedStream, stopsMap);
const result = await parseTransfers(mockedStream, stopsMap, new Set());

assert.deepEqual(result.transfers, new Map());
assert.deepEqual(result.tripContinuations, []);
Expand Down Expand Up @@ -523,7 +523,7 @@ describe('GTFS transfers parser', () => {
],
]);

const result = await parseTransfers(mockedStream, stopsMap);
const result = await parseTransfers(mockedStream, stopsMap, new Set());

assert.deepEqual(result.transfers, new Map());
assert.deepEqual(result.tripContinuations, []);
Expand Down Expand Up @@ -560,7 +560,7 @@ describe('GTFS transfers parser', () => {
],
]);

const result = await parseTransfers(mockedStream, stopsMap);
const result = await parseTransfers(mockedStream, stopsMap, new Set());

assert.deepEqual(result.transfers, new Map());
assert.deepEqual(result.tripContinuations, []);
Expand Down Expand Up @@ -618,7 +618,7 @@ describe('GTFS transfers parser', () => {
],
]);

const result = await parseTransfers(mockedStream, stopsMap);
const result = await parseTransfers(mockedStream, stopsMap, new Set());

const expectedTransfers = new Map([
[
Expand Down Expand Up @@ -698,7 +698,7 @@ describe('GTFS transfers parser', () => {
],
]);

const result = await parseTransfers(mockedStream, stopsMap);
const result = await parseTransfers(mockedStream, stopsMap, new Set());

const expectedTransfers = new Map([
[
Expand Down Expand Up @@ -726,6 +726,93 @@ describe('GTFS transfers parser', () => {
assert.deepEqual(result.tripContinuations, expectedTripContinuations);
});

it('should filter service-scoped transfers by active service regardless of type', async () => {
const mockedStream = new Readable();
mockedStream.push(
'from_stop_id,to_stop_id,from_trip_id,to_trip_id,service_id,transfer_type,min_transfer_time\n',
);
mockedStream.push('"from","to","","","","0",""\n');
mockedStream.push('"from","to","","","","2","120"\n');
mockedStream.push('"from","to","","","active","1",""\n');
mockedStream.push('"from","to","","","inactive","2","180"\n');
mockedStream.push('"from","to","tripA","tripB","active","1",""\n');
mockedStream.push('"from","to","tripC","tripD","inactive","1",""\n');
mockedStream.push('"from","to","tripE","tripF","active","4",""\n');
mockedStream.push('"from","to","tripG","tripH","inactive","4",""\n');
mockedStream.push(null);

const stopsMap: GtfsStopsMap = new Map([
[
'from',
{
id: 0,
sourceStopId: 'from',
name: 'From',
children: [],
locationType: 'SIMPLE_STOP_OR_PLATFORM',
},
],
[
'to',
{
id: 1,
sourceStopId: 'to',
name: 'To',
children: [],
locationType: 'SIMPLE_STOP_OR_PLATFORM',
},
],
]);

const result = await parseTransfers(
mockedStream,
stopsMap,
new Set(['active']),
);

assert.deepEqual(
result.transfers,
new Map([
[
0,
[
{
destination: 1,
type: TransferTypes.RECOMMENDED,
minTransferTime: durationFromSeconds(0),
},
{
destination: 1,
type: TransferTypes.REQUIRES_MINIMAL_TIME,
minTransferTime: durationFromSeconds(120),
},
{
destination: 1,
type: TransferTypes.GUARANTEED,
minTransferTime: durationFromSeconds(0),
},
],
],
]),
);
assert.deepEqual(result.guaranteedTripTransfers, [
{
fromStop: 0,
fromTrip: 'tripA',
toStop: 1,
toTrip: 'tripB',
},
]);
assert.deepEqual(result.tripContinuations, [
{
fromStop: 0,
fromTrip: 'tripE',
toStop: 1,
toTrip: 'tripF',
},
]);
});

it('should handle empty transfers file', async () => {
const mockedStream = new Readable();
mockedStream.push(
Expand All @@ -735,7 +822,7 @@ describe('GTFS transfers parser', () => {

const stopsMap: GtfsStopsMap = new Map();

const result = await parseTransfers(mockedStream, stopsMap);
const result = await parseTransfers(mockedStream, stopsMap, new Set());

assert.deepEqual(result.transfers, new Map());
assert.deepEqual(result.tripContinuations, []);
Expand Down Expand Up @@ -774,7 +861,7 @@ describe('GTFS transfers parser', () => {
],
]);

const result = await parseTransfers(mockedStream, stopsMap);
const result = await parseTransfers(mockedStream, stopsMap, new Set());

const expectedTransfers = new Map([
[
Expand Down
2 changes: 1 addition & 1 deletion src/gtfs/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ export class GtfsParser {
transfers: parsedTransfers,
tripContinuations: parsedTripContinuations,
guaranteedTripTransfers: parsedGuaranteedTripTransfers,
} = await parseTransfers(transfersStream, parsedStops);
} = await parseTransfers(transfersStream, parsedStops, activeServiceIds);
transfers = parsedTransfers;
tripContinuationsList = parsedTripContinuations;
guaranteedTripTransfersList = parsedGuaranteedTripTransfers;
Expand Down
16 changes: 14 additions & 2 deletions src/gtfs/transfers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
TripTransfers as TripTransfers,
} from '../timetable/timetable.js';
import { encode } from '../timetable/tripStopId.js';
import { ServiceId, ServiceIds } from './services.js';
import { GtfsStopsMap } from './stops.js';
import { GtfsTripId, TripsMapping } from './trips.js';
import { parseCsv } from './utils.js';
Expand Down Expand Up @@ -41,6 +42,7 @@ export type TransferEntry = {
to_trip_id?: GtfsTripId;
from_route_id?: ServiceRouteId;
to_route_id?: ServiceRouteId;
service_id?: ServiceId;
transfer_type: GtfsTransferType;
min_transfer_time?: number;
};
Expand Down Expand Up @@ -180,12 +182,15 @@ const processStopToStopTransfer = (
/**
* Parses the transfers.txt file from a GTFS feed.
*
* @param stopsStream The readable stream containing the stops data.
* @return A mapping of stop IDs to corresponding stop details.
* @param transfersStream The readable stream containing the transfers data.
* @param stopsMap The parsed GTFS stops indexed by their source IDs.
* @param activeServiceIds The service IDs active for the requested date.
* @returns Parsed stop transfers, trip continuations, and guaranteed trip transfers.
*/
export const parseTransfers = async (
transfersStream: NodeJS.ReadableStream,
stopsMap: GtfsStopsMap,
activeServiceIds: ServiceIds,
): Promise<{
transfers: TransfersMap;
tripContinuations: GtfsTripTransfer[];
Expand All @@ -201,6 +206,13 @@ export const parseTransfers = async (
])) {
const transferEntry = rawLine as TransferEntry;

if (
transferEntry.service_id &&
!activeServiceIds.has(transferEntry.service_id)
) {
continue;
}

if (
transferEntry.transfer_type === 3 ||
transferEntry.transfer_type === 5
Expand Down
Loading