UNPKG

timetable

Version:

Using GTFS and other sources to produce beautiful and usable timetables for public transport.

190 lines (158 loc) 6.84 kB
// Generated by CoffeeScript 1.7.1 /* Given a `trip`, return a `reltrip-info` object', which is a `reltrip` object with a `course` object. Example for a reltrip info object: { '~isa': 'reltrip', 'course-id': 'course/537f81c06', 'offset.hhmmss': '14:04:00', 'offset.s': 50640, 'course': [ { stop_id: '9130002', arrdep_rel: [ 0, 0 ] }, { stop_id: '9130011', arrdep_rel: [ 60, 60 ] }, { stop_id: '9110001', arrdep_rel: [ 180, 180 ] }, { stop_id: '9110006', arrdep_rel: [ 300, 300 ] } ] } A `reltrip` object is obtained by taking the course out, leaving just the course ID, and adding any missing attributes from the trip object. A course details both the stop identities and arrival and departure times in seconds relative to that trip's first departure; the course ID is obtained by calculating a cryptographic hash digest from the stop IDs and the relative times. This procedure means that all trips that visit the same stops in the same order and the same relative travelling times have identical course IDs, which helps both in identifying regularities in the timetable and to reduce the amount of data needed. Actual times may be reconstructed by adding the offset given to each arrival and departure time for a given trip. The term 'course' is inspired by the German word *Kurswagen* meaning *through coach*, presumably so named because a through coach follows its own course (sequence of stops). */ (function() { var REGISTRY, TEXT, TRM, alert, badge, debug, echo, help, info, log, njs_crypto, rainbow, rpr, urge, warn, whisper, __modulo = function(a, b) { return (a % b + +b) % b; }; njs_crypto = require('crypto'); TEXT = require('coffeenode-text'); TRM = require('coffeenode-trm'); rpr = TRM.rpr.bind(TRM); badge = 'TIMETABLE/course-from-trip'; log = TRM.get_logger('plain', badge); info = TRM.get_logger('info', badge); whisper = TRM.get_logger('whisper', badge); alert = TRM.get_logger('alert', badge); debug = TRM.get_logger('debug', badge); warn = TRM.get_logger('warn', badge); help = TRM.get_logger('help', badge); urge = TRM.get_logger('urge', badge); echo = TRM.echo.bind(TRM); rainbow = TRM.rainbow.bind(TRM); REGISTRY = require('./REGISTRY'); this.reltrip_info_from_abstrip = function(trip) { var R, arr_hhmmss, arr_rel, course_id, dep_hhmmss, dep_rel, digest, gtfs_stop_id, halt, halt_id, halt_idx, halts, offset_hhmmss, offset_s, stoptime, stoptimes, _i, _j, _len, _len1, _ref; stoptimes = trip['%gtfs-stoptimes']; if (stoptimes.length === 0) { return null; } course_id = []; halts = []; offset_hhmmss = (_ref = stoptimes[0]['dep']) != null ? _ref : stoptimes[0]['arr']; offset_s = this._seconds_from_time_text(offset_hhmmss); R = { '~isa': 'node', '~label': 'reltrip-info', 'id': null, 'course-id': null, 'headsign': trip['headsign'], 'offset.hhmmss': offset_hhmmss, 'offset.s': offset_s, 'halts': halts }; for (halt_idx = _i = 0, _len = stoptimes.length; _i < _len; halt_idx = ++_i) { stoptime = stoptimes[halt_idx]; gtfs_stop_id = stoptime['%gtfs-stop-id']; arr_hhmmss = stoptime['arr']; dep_hhmmss = stoptime['dep']; arr_rel = arr_hhmmss != null ? (this._seconds_from_time_text(arr_hhmmss)) - offset_s : null; dep_rel = dep_hhmmss != null ? (this._seconds_from_time_text(dep_hhmmss)) - offset_s : null; /* TAINT contains duplications */ halt = { '~isa': 'node', '~label': 'halt', 'idx': halt_idx, 'id': null, '%gtfs-stop-id': gtfs_stop_id, 'course-id': null, 'arr-rel.s': arr_rel, 'dep-rel.s': dep_rel, 'display': this._format_relative_seconds(dep_rel != null ? dep_rel : arr_rel) }; halts.push(halt); course_id.push("" + gtfs_stop_id + "," + arr_rel + "," + dep_rel); } course_id = course_id.join(';'); digest = this._digest_from_text(course_id); R['course-id'] = course_id = 'course/' + digest; for (halt_idx = _j = 0, _len1 = halts.length; _j < _len1; halt_idx = ++_j) { halt = halts[halt_idx]; halt_id = "halt/" + (course_id.replace('/', ':')) + "/" + halt_idx; halt['id'] = halt_id; halt['course-id'] = course_id; } return R; }; this.registered_course_from_reltrip_info = function(registry, reltrip_info) { var R, course_id, gtfs_stop_id, halt, station, station_id, _i, _len, _ref; course_id = reltrip_info['course-id']; R = registry['course'][course_id]; if (R != null) { return R; } R = { '~isa': 'node', '~label': 'course', 'id': course_id, 'headsign': reltrip_info['headsign'] }; REGISTRY.register(registry, R); _ref = reltrip_info['halts']; for (_i = 0, _len = _ref.length; _i < _len; _i++) { halt = _ref[_i]; gtfs_stop_id = halt['%gtfs-stop-id']; station = registry['%gtfs']['stops'][gtfs_stop_id]; station_id = station['id']; halt['station-id'] = station_id; delete halt['%gtfs-stop-id']; REGISTRY.register(registry, halt); } return R; }; this._digest_from_text = function(text) { /* TAINT arbitrarily shortened ID */ var hash; hash = (njs_crypto.createHash('sha1')).update(text, 'utf-8'); return (hash.digest('hex')).slice(0, 9); }; this._seconds_from_time_text = function(time_txt) { /* OBS We do not use an established module for converting a time of the day to seconds since the GTFS data use the (very sensible) convention to represent times of a trip that continues after midnight by hours 24 and greater (so when you leave a place at 23:10 one day and arrive at 01:44 the following day, the GTFS data will represent 01:44 as 25:44). */ var hours, hours_txt, ignore, match, minutes, minutes_txt, seconds, seconds_txt; match = time_txt.match(/([0-9]{2}):([0-9]{2}):([0-9]{2})/); if (match == null) { throw new Error("invalid time text " + (rpr(time_txt))); } ignore = match[0], hours_txt = match[1], minutes_txt = match[2], seconds_txt = match[3]; hours = parseInt(hours_txt, 10); minutes = parseInt(minutes_txt, 10); seconds = parseInt(seconds_txt, 10); minutes = minutes + hours * 60; seconds = seconds + minutes * 60; return seconds; }; this._format_relative_seconds = function(seconds) { var hours, minutes; minutes = Math.floor(seconds / 60); hours = Math.floor(minutes / 60); minutes = __modulo(minutes, 60); seconds = __modulo(seconds, 60); minutes = TEXT.flush_right(minutes, 2, '0'); seconds = TEXT.flush_right(seconds, 2, '0'); return "+" + hours + ":" + minutes + ":" + seconds; }; }).call(this);