'use strict';
/**
* GTFS Trip Class
* @see {@link Trip}
* @module gtfs/Trip
*/
const provided = require('../utils/provided.js');
const StopTime = require('./StopTime.js');
/**
* GTFS Trip
* ---------
* Representation of the GTFS Trip definition
*
* GTFS Required Fields:
* - Trip ID
* - Route
* - Service
* - List of StopTimes
*
* GTFS Optional Fields:
* - Trip Headsign
* - Trip Short Name
* - Trip Direction ID
* - Trip Block ID
* - Trip Shape ID
* - Trip Wheelchair Accessibility
* - Trip Bikes Allowed
*
* Right Track Fields:
* - Trip Direction Description
* - Trip Peak Indicator
*
* **Module:** {@link module:gtfs/Trip|gtfs/Trip}
*
* @see {@link https://developers.google.com/transit/gtfs/reference/trips-file|GTFS Spec}
* @class
* @alias Trip
*/
class Trip {
/**
* Trip Constructor
* @constructor
* @param {string} id Trip ID
* @param {Route} route Trip Route
* @param {Service} service Trip Service
* @param {StopTime[]} stopTimes Trip StopTimes list
* @param {Object} [optional] Optional Arguments
* @param {string} [optional.headsign] Trip Headsign
* @param {string} [optional.shortName] Trip Short Name
* @param {int} [optional.directionId=-1] Trip Direction ID
* @param {string} [optional.directionDescription] Trip Direction Description
* @param {int} [optional.blockId] Trip Block ID
* @param {int} [optional.shapeId] Trip Shape ID
* @param {int} [optional.wheelchairAccessible=0] Trip Wheelchair Accessibility code
* @param {int} [optional.bikesAllowed=0] Trip Bikes Allowed code
* @param {boolean} [optional.peak] Trip Peak Status
*/
constructor(id, route, service, stopTimes, optional={}) {
/**
* A unique ID representing the Trip
* @type {string}
*/
this.id = id;
/**
* The Trip's parent Route
* @type {Route}
*/
this.route = route;
/**
* The Service the Trip operates on
* @type {Service}
*/
this.service = service;
/**
* List of scheduled StopTimes for the Trip
* @type {StopTime[]}
*/
this.stopTimes = stopTimes;
/**
* Trip Headsign - text displayed to passengers indicating the Trip's destination
* @type {string}
*/
this.headsign = provided(optional.headsign);
/**
* Publicly-viewable Trip ID or name
* @type {string}
*/
this.shortName = provided(optional.shortName, this.id);
/**
* Direction of travel for the Trip
* @type {int}
* @default -1
*/
this.directionId = provided(optional.directionId, -1);
/**
* A description of the Trip's direction
* @type {string}
*/
this.directionDescription = provided(optional.directionDescription, "Unspecified Direction");
/**
* The ID of the block the Trip belongs to
* @type {string}
*/
this.blockId = provided(optional.blockId);
/**
* The ID of the shape the Trip belongs to
* @type {string}
*/
this.shapeId = provided(optional.shapeId);
/**
* Value indicating the wheelchair accessibility of the vehicle
* making the Trip
* @type {int}
* @default 0
*/
this.wheelchairAccessible = provided(optional.wheelchairAccessible, Trip.WHEELCHAIR_ACCESSIBLE_UNKNOWN);
/**
* Value indicating whether bikes are allowed on the vehicle making the Trip
* @type {int}
* @default 0
*/
this.bikesAllowed = provided(optional.bikesAllowed, Trip.BIKES_ALLOWED_UNKNOWN);
/**
* Value indicating the peak status of the Trip.
* @type {boolean}
* @default false
*/
this.peak = provided(optional.peak, false);
// Sort stoptimes by stop sequence
if ( this.stopTimes ) {
this.stopTimes.sort(StopTime.sortByStopSequence);
}
}
/**
* Get the StopTime associated with the specified Stop
* @param {Stop|String} stop Stop (or Stop ID) to find in Trip's list of StopTimes
* @returns {StopTime} matching StopTime
*/
getStopTime(stop) {
if ( typeof stop === 'object' ) {
stop = stop.id;
}
for ( let i = 0; i < this.stopTimes.length; i++ ) {
let stopTime = this.stopTimes[i];
if ( this.stopTimes[i].stop.id === stop ) {
return this.stopTimes[i];
}
}
}
/**
* Check if the Trip's list of StopTimes includes the specified Stop
* @param {Stop|String} stop Stop (or Stop ID) to check for
* @returns {boolean} true if the Stop is found in list of StopTimes
*/
hasStopTime(stop) {
if ( typeof stop === 'object' ) {
stop = stop.id;
}
for ( let i = 0; i < this.stopTimes.length; i++ ) {
if ( this.stopTimes[i].stop.id === stop ) {
return true;
}
}
return false;
}
}
// ==== WHEELCHAIR ACCESSIBILITY CODES ==== //
/**
* Trip Wheelchair Accessibility: Unknown
* @const {number}
* @default 0
*/
Trip.WHEELCHAIR_ACCESSIBLE_UNKNOWN = 0;
/**
* Trip Wheelchair Accessibility: Accessible
* @const {number}
* @default
*/
Trip.WHEELCHAIR_ACCESSIBLE_YES = 1;
/**
* Trip Wheelchair Accessibility: Inaccessible
* @const {number}
* @default
*/
Trip.WHEELCHAIR_ACCESSIBLE_N0 = 2;
// ==== BIKES ALLOWED CODES ==== //
/**
* Trip Bikes Allowed: Unknown
* @const {number}
* @default 0
*/
Trip.BIKES_ALLOWED_UNKNOWN = 0;
/**
* Trip Bikes Allowed: At least one allowed
* @const {number}
* @default
*/
Trip.BIKES_ALLOWED_YES = 1;
/**
* Trip Bikes Allowed: No bikes allowed
* @const {number}
* @default
*/
Trip.BIKES_ALLOWED_NO = 2;
// ==== SORT METHODS ==== //
/**
* Sort Trips by departure time of the first Stop (or reference Stop, if defined)
* @param {Trip} a first Trip
* @param {Trip} b second Trip
* @returns {number} compare integer
*/
Trip.sortByDepartureTime = function(a, b) {
if ( a._referenceStopId && b._referenceStopId && a._referenceStopId === b._referenceStopId ) {
let stopId = a._referenceStopId;
if ( a.getStopTime(stopId).departure.toTimestamp() < b.getStopTime(stopId).departure.toTimestamp() ) {
return -1;
}
else if ( a.getStopTime(stopId).departure.toTimestamp() > b.getStopTime(stopId).departure.toTimestamp() ) {
return 1;
}
else {
return 0;
}
}
else {
if ( a.stopTimes[0].departure.toTimestamp() < b.stopTimes[0].departure.toTimestamp() ) {
return -1;
}
else if ( a.stopTimes[0].departure.toTimestamp() > b.stopTimes[0].departure.toTimestamp() ) {
return 1;
}
else {
return 0;
}
}
};
module.exports = Trip;