timetable
Version:
Using GTFS and other sources to produce beautiful and usable timetables for public transport.
452 lines (388 loc) • 15.3 kB
JavaScript
// 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);