UNPKG

timetable

Version:

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

452 lines (388 loc) 15.3 kB
// Generated by CoffeeScript 1.7.1 (function() { var ASYNC, REGISTRY, T, TEXT, TRM, TYPES, alert, as_transformer, badge, create_readstream, datasource_infos, debug, echo, global_data_limit, help, info, log, new_csv_parser, njs_fs, options, rainbow, rpr, urge, warn, whisper, _new_csv_parser, _ref, _ref1; njs_fs = require('fs'); TYPES = require('coffeenode-types'); TEXT = require('coffeenode-text'); TRM = require('coffeenode-trm'); rpr = TRM.rpr.bind(TRM); badge = 'TIMETABLE/read-gtfs-data'; 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); T = require('./TRANSFORMERS'); as_transformer = T.as_transformer.bind(T); options = require('../options'); global_data_limit = (_ref = (_ref1 = options['data']) != null ? _ref1['limit'] : void 0) != null ? _ref : Infinity; datasource_infos = (require('./get-datasource-infos'))(); create_readstream = require('./create-readstream'); REGISTRY = require('./REGISTRY'); ASYNC = require('async'); /* https://github.com/wdavidw/node-csv-parse */ _new_csv_parser = require('csv-parse'); new_csv_parser = function() { return _new_csv_parser(options['parser']); }; /* TAINT very Berlin-specific method, shouldnt appear here */ this._normalize_name = function(name) { name = name.replace(/\s+\(Berlin\)(\s+Bus)?$/, ''); name = name.replace(/^(U|S\+U|S)\s+/, ''); name = name.replace(/^(Alexanderplatz) Bhf\/(.+)$/, '$1 ($2)'); name = name.replace(/^(Lichtenberg) Bhf\/(.+)$/, '$1 ($2)'); name = name.replace(/^(Alexanderplatz) Bhf/, '$1'); name = name.replace(/^(Zoologischer Garten) Bhf/, '$1'); name = name.replace(/^(Gesundbrunnen) Bhf/, '$1'); name = name.replace(/^(Potsdamer Platz) Bhf/, '$1'); name = name.replace(/^(Lichtenberg) Bhf/, '$1'); name = name.replace(/^(Friedrichstr\.) Bhf/, '$1'); name = name.replace(/^(Jungfernheide) Bhf/, '$1'); name = name.replace(/^(Stadtmitte) U[26]/, '$1'); name = name.replace(/^(.+)str\./, '$1straße'); name = name.replace(/^(.+)\s+Str\./, '$1 Straße'); return name; }; this._get_system_name = function(name) { name = name.toLowerCase(); name = name.replace(/,/g, ''); name = name.replace(/\./g, ''); name = name.replace(/\s+/g, '-'); return name; }; /* TAINT unify with following */ this.$normalize_station_name = function() { return as_transformer((function(_this) { return function(record, handler) { record['name'] = _this._normalize_name(record['name']); return handler(null, record); }; })(this)); }; this.$normalize_headsign = function() { return as_transformer((function(_this) { return function(record, handler) { record['headsign'] = _this._normalize_name(record['headsign']); return handler(null, record); }; })(this)); }; this.$convert_latlon = function() { return as_transformer((function(_this) { return function(record, handler) { record['lat'] = parseFloat(record['lat']); record['lon'] = parseFloat(record['lon']); return handler(null, record); }; })(this)); }; this.$register = function(registry) { return as_transformer((function(_this) { return function(record, handler) { REGISTRY.register_gtfs(registry, record); return record; }; })(this)); }; this.$clean_agency_record = function() { return as_transformer((function(_this) { return function(record, handler) { delete record['agency_phone']; delete record['agency_lang']; return handler(null, record); }; })(this)); }; this.$clean_stoptime_record = function() { return as_transformer((function(_this) { return function(record, handler) { delete record['stop_headsign']; delete record['pickup_type']; delete record['drop_off_type']; delete record['shape_dist_traveled']; return handler(null, record); }; })(this)); }; this.$add_stoptime_idx = function() { return as_transformer((function(_this) { return function(record, handler) { record['idx'] = (parseInt(record['stop-sequence'], 10)) - 1; delete record['stop-sequence']; return handler(null, record); }; })(this)); }; this.$clean_route_record = function() { return as_transformer((function(_this) { return function(record, handler) { delete record['route_long_name']; delete record['route_desc']; delete record['route_url']; delete record['route_color']; delete record['route_text_color']; return handler(null, record); }; })(this)); }; this.$clean_station_record = function() { return as_transformer((function(_this) { return function(record, handler) { delete record['stop_desc']; delete record['zone_id']; delete record['stop_url']; delete record['location_type']; delete record['parent_station']; return handler(null, record); }; })(this)); }; this.$add_station_system_name = function() { return as_transformer((function(_this) { return function(record, handler) { record['~name'] = _this._get_system_name(record['name']); return handler(null, record); }; })(this)); }; this.$clean_trip_record = function() { return as_transformer((function(_this) { return function(record, handler) { delete record['trip_short_name']; delete record['direction_id']; delete record['block_id']; delete record['shape_id']; return handler(null, record); }; })(this)); }; this.$add_headsign_system_name = function() { return as_transformer((function(_this) { return function(record, handler) { record['~headsign'] = _this._get_system_name(record['headsign']); return handler(null, record); }; })(this)); }; this.$add_agency_ids = function(registry) { return function(record) { record['id'] = record['%gtfs-id'].replace(/[-_]+$/, ''); return record; }; }; this.$add_stoptime_ids = function(registry) { return function(record) { var gtfs_stop_id, gtfs_trip_id, idx; gtfs_stop_id = record['%gtfs-stop-id']; gtfs_trip_id = record['%gtfs-trip-id']; idx = record['idx']; record['id'] = "gtfs-stop:" + gtfs_stop_id + "/gtfs-trip:" + gtfs_trip_id + "/idx:" + idx; return record; }; }; this.$add_route_ids = function(registry) { var route_idx; route_idx = -1; return function(record) { var agency, agency_id, gtfs_agency_id, gtfs_id, name; route_idx += 1; gtfs_agency_id = record['%gtfs-agency-id']; gtfs_id = record['%gtfs-id']; name = record['name']; agency = registry['old'][gtfs_agency_id]; if (agency == null) { return handler(new Error("unable to find agency with GTFS ID " + (rpr(gtfs_agency_id)))); } agency_id = agency['id']; record['id'] = "route:" + route_idx + "/" + agency_id + "/name:" + name; return record; }; }; this.$add_station_ids = function(registry) { return function(record) { var station_idx, stations, stations_by_names, sys_name; stations_by_names = registry['%stations-by-names'] != null ? registry['%stations-by-names'] : registry['%stations-by-names'] = {}; sys_name = record['~name']; stations = stations_by_names[sys_name] != null ? stations_by_names[sys_name] : stations_by_names[sys_name] = []; station_idx = stations.length; stations.push(record); record['id'] = "station/name:" + sys_name + "/idx:" + station_idx; return record; }; }; this.$add_trip_ids = function(registry) { return function(record) { var gtfs_route_id, gtfs_trip_id; gtfs_trip_id = record['%gtfs-id']; gtfs_route_id = record['%gtfs-route-id']; /* does this make sense?? */ record['id'] = "gtfs-route-id:" + gtfs_route_id + "/gtfs-trip-id:" + gtfs_trip_id; return record; }; }; this.finalize = function(registry, handler) { var duplicate, id, label, method, method_by_types, record, _, _ref2; method_by_types = { 'agency': (this.$add_agency_ids(registry)).bind(this), 'stoptime': (this.$add_stoptime_ids(registry)).bind(this), 'route': (this.$add_route_ids(registry)).bind(this), 'station': (this.$add_station_ids(registry)).bind(this), 'trip': (this.$add_trip_ids(registry)).bind(this) }; _ref2 = registry['old']; for (_ in _ref2) { record = _ref2[_]; method = method_by_types[label = record['~label']]; if (method == null) { warn("unable to locate method `add_" + label + "_ids; skipping"); continue; } method(record); id = record['id']; if (id == null) { warn("unable to find ID in " + record + "; skipping"); continue; } if ((duplicate = registry['new'][id]) != null) { return handler(new Error("duplicate IDs:\n" + (rpr(duplicate)) + "\n" + (rpr(record)))); } registry['new'][id] = record; } return handler(null); }; this.read_agencies = function(route, registry, handler) { var input, parser; parser = new_csv_parser(); input = create_readstream(route, 'agencies'); input.on('end', function() { info('ok: agencies'); return handler(null); }); input.pipe(parser).pipe(T.$as_pods()).pipe(this.$clean_agency_record()).pipe(T.$delete_prefix('agency_')).pipe(T.$set('%gtfs-type', 'agency')).pipe(T.$rename('id', '%gtfs-id')).pipe(T.$dasherize_field_names()).pipe(this.$register(registry)).pipe(T.$show_sample(input)); whisper('reading GTFS agencies...'); return null; }; this.read_stop_times = function(route, registry, handler) { var input, parser; parser = new_csv_parser(); input = create_readstream(route, 'stop_times'); input.on('end', function() { info('ok: stoptimes'); return handler(null); }); input.pipe(parser).pipe(T.$skip(global_data_limit)).pipe(T.$as_pods()).pipe(this.$clean_stoptime_record()).pipe(T.$delete_prefix('trip_')).pipe(T.$add_n4j_system_properties('node', 'stoptime')).pipe(T.$dasherize_field_names()).pipe(T.$rename('id', '%gtfs-trip-id')).pipe(T.$rename('stop-id', '%gtfs-stop-id')).pipe(this.$add_stoptime_idx()).pipe(T.$show_sample(input)); whisper('reading GTFS stoptimes...'); return null; }; /* TAINT name clash (filesystem route vs. GTFS route) */ this.read_routes = function(route, registry, handler) { var input, parser; parser = new_csv_parser(); input = create_readstream(route, 'routes'); input.on('end', function() { info('ok: routes'); return handler(null); }); input.pipe(parser).pipe(T.$skip(global_data_limit)).pipe(T.$as_pods()).pipe(this.$clean_route_record()).pipe(T.$dasherize_field_names()).pipe(T.$rename('route-id', '%gtfs-id')).pipe(T.$rename('agency-id', '%gtfs-agency-id')).pipe(T.$rename('route-short-name', 'name')).pipe(T.$add_n4j_system_properties('node', 'route')).pipe(T.$show_sample(input)); whisper('reading GTFS routes...'); return null; }; this.read_stops = function(route, registry, handler) { var input, parser; parser = new_csv_parser(); input = create_readstream(route, 'stops'); input.on('end', function() { info('ok: stops'); return handler(null); }); input.pipe(parser).pipe(T.$as_pods()).pipe(this.$clean_station_record()).pipe(T.$delete_prefix('stop_')).pipe(T.$set('%gtfs-type', 'stops')).pipe(T.$rename('id', '%gtfs-id')).pipe(this.$convert_latlon()).pipe(this.$register(registry)).pipe(T.$show_sample(input)); whisper('reading GTFS stops...'); return null; }; this.read_trips = function(route, registry, handler) { var input, parser; parser = new_csv_parser(); input = create_readstream(route, 'trips'); input.on('end', function() { info('ok: trips'); return handler(null); }); input.pipe(parser).pipe(T.$skip(global_data_limit)).pipe(T.$as_pods()).pipe(this.$clean_trip_record()).pipe(T.$delete_prefix('trip_')).pipe(T.$dasherize_field_names()).pipe(T.$rename('id', '%gtfs-id')).pipe(T.$rename('route-id', '%gtfs-route-id')).pipe(T.$rename('service-id', '%gtfs-service-id')).pipe(T.$copy('headsign', '%gtfs-headsign')).pipe(this.$normalize_headsign()).pipe(this.$add_headsign_system_name()).pipe(T.$add_n4j_system_properties('node', 'trip')).pipe(T.$show_sample(input)); whisper('reading GTFS trips...'); return null; }; this.main = function(registry, handler) { var gtfs_type, message, messages, method, no_method, no_source, ok_types, route, route_by_types, source_name, tasks, _fn, _i, _j, _k, _len, _len1, _len2, _ref2, _ref3; for (source_name in datasource_infos) { route_by_types = datasource_infos[source_name]; tasks = []; no_source = []; no_method = []; ok_types = []; _ref2 = options['data']['gtfs-types']; _fn = (function(_this) { return function(method, route) { return tasks.push(function(async_handler) { return method(route, registry, async_handler); }); }; })(this); for (_i = 0, _len = _ref2.length; _i < _len; _i++) { gtfs_type = _ref2[_i]; route = route_by_types[gtfs_type]; if (route == null) { no_source.push("skipping " + source_name + "/" + gtfs_type + " (no source file)"); continue; } help("found data source for " + source_name + "/" + gtfs_type); method = null; switch (gtfs_type) { case 'agency': method = this.read_agencies; break; case 'stops': method = this.read_stops; } if (method == null) { no_method.push("no method to read GTFS data of type " + (rpr(gtfs_type)) + "; skipping"); continue; } method = method.bind(this); ok_types.push(gtfs_type); _fn(method, route); } _ref3 = [no_source, no_method]; for (_j = 0, _len1 = _ref3.length; _j < _len1; _j++) { messages = _ref3[_j]; for (_k = 0, _len2 = messages.length; _k < _len2; _k++) { message = messages[_k]; warn(message); } } info("reading data for " + ok_types.length + " type(s)"); info(" (" + (ok_types.join(', ')) + ")"); } ASYNC.series(tasks, (function(_this) { return function(error) { if (error != null) { throw error; } return handler(null, registry); }; })(this)); return null; }; if (module.parent == null) { this.main(function(error, registry) { if (error != null) { throw error; } return info(registry); }); } }).call(this);