node-GTFS
loads transit data in GTFS format into a MongoDB database and provides some methods to query for agencies, routes, stops, times, fares, calendars and other GTFS data. It also offers spatial queries to find nearby stops, routes and agencies and can convert stops and shapes to geoJSON format.
Additionally, this librarty can export data from mongoDB back into GTFS (csv) format.
This library has three parts: the GTFS import script, the query methods and the GTFS export script
The GTFS-to-HTML app uses node-gtfs
for downloading, importing and querying GTFS data. It provides a good example of how to use this library and is used by over a dozen transit agencies to generate the timetables on their websites.
The GTFS-to-geojson app creates geoJSON files for transit routes for use in mapping. It uses node-gtfs
for downloading, importing and querying GTFS data. It provides a good example of how to use this library.
If you would like to use this library as a command-line utility, you can install it globally directly from npm:
npm install gtfs mongoose -g
If you are using this as a node module as part of an application, you can include it in your project's package.json
file.
Note: Mongoose is a peer dependency of node-gtfs
, so when installing globally be sure to install mongoose as well. If you are writing a application that uses node-gtfs
as a dependency, then you'll need to include mongoose as a dependency in your project's package.json
.
gtfs-import [--configPath /path/to/your/custom-config.json] [--skipDelete]
gtfs-export [--configPath /path/to/your/custom-config.json]
const gtfs = require('gtfs');
const mongoose = require('mongoose');
const config = require('./config.json');
mongoose.connect(config.mongoUrl, { useNewUrlParser: true, useCreateIndex: true, useUnifiedTopology: true });
gtfs.import(config)
.then(() => {
console.log('Import Successful');
return mongoose.connection.close();
})
.catch(err => {
console.error(err);
});
Copy config-sample.json
to config.json
and then add your projects configuration to config.json
.
cp config-sample.json config.json
option | type | description |
---|---|---|
agencies |
array | An array of GTFS files to be imported. |
csvOptions |
object | Options passed to csv-parse for parsing GTFS CSV files. |
dataExpireAfterSeconds |
integer | The number of seconds after which the data will be deleted from mongodb using a TTL index. Optional, if not specified then data will not be automatically deleted. |
mongoUrl |
string | The URL of the MongoDB database to import to. |
verbose |
boolean | Whether or not to print output to the console. |
skipDelete |
boolean | Whether or not to skip deleting existing data from the database. |
{Array} Specify the GTFS files to be imported in an agencies
array. GTFS files can be imported via a url
or a local path
.
Each file needs an agency_key
, a short name you create that is specific to that GTFS file. For GTFS files that contain more than one agency, you only need to list each GTFS file once in the agencies
array, not once per agency that it contains.
To find an agency's GTFS file, visit transitfeeds.com. You can use the URL from the agency's website or you can use a URL generated from the transitfeeds.com API along with your API token.
- Specify a download URL:
{
"agencies": [
{
"agency_key": "county-connection",
"url": "http://countyconnection.com/GTFS/google_transit.zip"
}
]
}
- Specify a download URL with custom headers:
{
"agencies": [
{
"agency_key": "county-connection",
"url": "http://countyconnection.com/GTFS/google_transit.zip",
"headers: {
"Content-Type": "application/json",
"Authorization": "bearer 1234567890"
},
}
]
}
- Specify a path to a zipped GTFS file:
{
"agencies": [
{
"agency_key": "myAgency",
"path": "/path/to/the/gtfs.zip"
}
]
}
- Specify a path to an unzipped GTFS file:
{
"agencies": [
{
"agency_key": "myAgency",
"path": "/path/to/the/unzipped/gtfs/"
}
]
}
- Exclude files - if you don't want all GTFS files to be imported, you can specify an array of files to exclude.
{
"agencies": [
{
"agency_key": "myAgency",
"path": "/path/to/the/unzipped/gtfs/",
"exclude": [
"shapes",
"stops"
]
}
]
}
- Optionally specify a proj4 projection string to correct poorly formed coordinates in the GTFS file
{
"agencies": [
{
"agency_key": "myAgency",
"path": "/path/to/the/unzipped/gtfs/",
"proj": "+proj=lcc +lat_1=46.8 +lat_0=46.8 +lon_0=0 +k_0=0.99987742 +x_0=600000 +y_0=2200000 +a=6378249.2 +b=6356515 +towgs84=-168,-60,320,0,0,0,0 +pm=paris +units=m +no_defs"
}
]
}
{Object} Add options to be passed to csv-parse
wiith the key csvOptions
. This is an optional paramenter.
For instance, if you wanted to skip importing invalid lines in the GTFS file:
"csvOptions": {
"skip_lines_with_error": true
}
See full list of options.
{Integer} The number of seconds after which the data will be deleted from mongodb using a TTL index. Optional, if not specified then data will not be automatically deleted.
"dataExpireAfterSeconds": 3600
{String} The MongoDB URI use. When running locally, you may want to use mongodb://localhost:27017/gtfs
.
{
"mongoUrl": "mongodb://localhost:27017/gtfs",
"agencies": [
{
"agency_key": "myAgency",
"path": "/path/to/the/unzipped/gtfs/"
}
]
}
{Boolean} If you don't want the import script to print any output to the console, you can set verbose
to false
. Defaults to true
.
{
"mongoUrl": "mongodb://localhost:27017/gtfs",
"agencies": [
{
"agency_key": "localAgency",
"path": ""/path/to/the/unzipped/gtfs/"
}
],
"verbose": false
}
If you want to route logs to a custom function, you can pass a function that takes a single text
argument as logFunction
. This can't be defined in config.json
but instead passed in a config object to gtfs.import()
. For example:
const gtfs = require('gtfs');
const mongoose = require('mongoose');
const config = {
mongoUrl: 'mongodb://localhost:27017/gtfs',
agencies: [
{
agency_key: 'county-connection',
url: 'http://countyconnection.com/GTFS/google_transit.zip',
exclude: [
'shapes'
]
}
],
logFunction: function(text) {
// Do something with the logs here, like save it or send it somewhere
console.log(text);
}
};
mongoose.connect(config.mongoUrl, { useNewUrlParser: true, useCreateIndex: true, useUnifiedTopology: true });
gtfs.import(config);
If you don't want the import script to delete all existing data from the database with the same agency_key
, you can set skipDelete
to true
. Defaults to false
.
{
"mongoUrl": "mongodb://localhost:27017/gtfs",
"agencies": [
{
"agency_key": "localAgency",
"path": ""/path/to/the/unzipped/gtfs/"
}
],
"skipDelete": true
}
The gtfs-import
script reads from a JSON configuration file and imports the GTFS files specified to a MongoDB database. Read more on setting up your configuration file.
If you want to run this locally, make sure MongoDB in installed and running.
mongod
gtfs-import
By default, it will look for a config.json
file in the project root. To specify a different path for the configuration file:
gtfs-import --configPath /path/to/your/custom-config.json
By default, the import script will delete any existing data with the same agency_key
from your database. If you don't want this to happen, pass the --skipDelete
flag
gtfs-import --skipDelete
You can specify the path to a config file to be used by the import script.
gtfs-import --configPath /path/to/your/custom-config.json
Show all command line options
gtfs-import --help
Use gtfs.import()
in your code to run an import of a GTFS file specified in a config.json file.
const gtfs = require('gtfs');
const mongoose = require('mongoose');
const config = require('config.json');
mongoose.connect(config.mongoUrl, { useNewUrlParser: true, useCreateIndex: true, useUnifiedTopology: true });
gtfs.import(config)
.then(() => {
console.log('Import Successful');
return mongoose.connection.close();
})
.catch(err => {
console.error(err);
});
Configuration can be a JSON object in your code
const gtfs = require('gtfs');
const mongoose = require('mongoose');
const config = {
mongoUrl: 'mongodb://localhost:27017/gtfs',
agencies: [
{
agency_key: 'county-connection',
url: 'http://countyconnection.com/GTFS/google_transit.zip',
exclude: [
'shapes'
]
}
]
};
mongoose.connect(config.mongoUrl, { useNewUrlParser: true, useCreateIndex: true, useUnifiedTopology: true );
gtfs.import(config)
.then(() => {
console.log('Import Successful');
return mongoose.connection.close();
})
.catch(err => {
console.error(err);
});
The gtfs-export
script reads from a JSON configuration file and exports data in GTFS format from a MongoDB database. Read more on setting up your configuration file.
This could be used to export a GTFS file from mongodb after changes have been made to the data in the database manually.
If you want to run this locally, make sure MongoDB in installed and running.
mongod
Nothing will be exported if there is no data to export. See the GTFS import script.
gtfs-export
By default, it will look for a config.json
file in the project root. To specify a different path for the configuration file:
gtfs-export --configPath /path/to/your/custom-config.json
You can specify the path to a config file to be used by the export script.
gtfs-export --configPath /path/to/your/custom-config.json
Show all command line options
gtfs-export --help
Use gtfs.export()
in your code to run an export of a GTFS file specified in a config.json file.
const gtfs = require('gtfs');
const mongoose = require('mongoose');
const config = {
mongoUrl: 'mongodb://localhost:27017/gtfs',
agencies: [
{
agency_key: 'county-connection',
url: 'http://countyconnection.com/GTFS/google_transit.zip',
exclude: [
'shapes'
]
}
]
};
mongoose.connect(config.mongoUrl, { useNewUrlParser: true, useCreateIndex: true, useUnifiedTopology: true });
gtfs.export(config)
.then(() => {
console.log('Export Successful');
return mongoose.connection.close();
})
.catch(err => {
console.error(err);
});
This library includes many methods you can use in your project to query GTFS data. These methods return promises.
Most methods accept three arguments: query
, projection
and options
. projection
and options
are optional and are passed to the mongoose find
query.
For example, to get a list of all agencies within 5 miles of a specific point:
gtfs.getAgencies({
within: {
lat: 37.7749,
lon: -122.4194,
radius: 5
}
})
.then(agencies => {
// Do something with the array of `agencies`
})
.catch(err => {
// Be sure to handle errors here
});
By default, projection
is set to exclude mongo _id
. projection
allows specifying which fields you would like returned. For instance:
// Gets all agencies but limits results to only include `agency_name` and `agency_lang`.
gtfs.getAgencies({}, {
_id: 0,
agency_name: 1,
agency_lang: 1
})
.then(agencies => {
// each Agency only has `agency_name` and `agency_lang`
});
Mongoose allows numerous options to be set on the query. You can specify things like sort
and limit
using the options
parameter. By default, options
is set to lean: true
to return a plain javascript object.
// Gets all agencies sorted by `agency_name`
gtfs.getAgencies({}, {}, {
sort: {agency_name: 1}
})
.then(agencies => {
});
Include this library.
var gtfs = require('gtfs');
Connect to mongo via mongoose.
const mongoose = require('mongoose');
mongoose.connect('YOUR-MONGODB-URI');
If you are running locally, your MongoDB uri might be something like:
mongodb://localhost:27017/gtfs
You probably want to use the same value used in your configuration JSON file for importing GTFS.
Queries agencies and returns a promise. The result of the promise is an array of agencies.
// Get all agencies
gtfs.getAgencies()
.then(agencies => {
});
// Get all agencies within a a `radius` of the `lat`, `lon` specified.
// `radius` is optional and in miles. Default: 25 miles.
gtfs.getAgencies({
within: {
lat: 37.7749,
lon: -122.4194,
radius: 5
}
})
.then(agencies => {
});
// Get a specific agency
gtfs.getAgencies({
agency_key: 'caltrain'
})
.then(agencies => {
});
// Get a specific agency by `agency_name`
gtfs.getAgencies({
agency_name: 'Caltrain'
})
.then(agencies => {
});
// Get all agencies in a specific timezone, limited to just `agency_name` sorted alphabetically
gtfs.getAgencies({
agency_timezone: 'America/Los_Angeles'
}, {
_id: 0,
agency_name: 1
}, {
sort: {agency_name: 1}
})
.then(agencies => {
});
Queries routes and returns a promise. The result of the promise is an array of routes.
// Get all routes for an agency
gtfs.getRoutes({
agency_key: 'caltrain'
})
.then(routes => {
});
// Get a specific route
gtfs.getRoutes({
agency_key: 'caltrain',
route_id: 'Lo-16APR'
})
.then(routes => {
});
// Get a few routes
const routeIds = [
'Bu-16APR',
'Lo-16APR'
]
gtfs.getRoutes({
agency_key: 'caltrain',
route_id: {
$in: routeIds
}
})
.then(routes => {
});
// Get all routes within a radius of the `lat`, `lon` specified.
// `radius` is optional and in miles. Default: 1 mile.
gtfs.getRoutes({
within: {
lat: 37.7749,
lon: -122.4194,
radius: 5
}
})
.then(routes => {
});
// Get routes that serve a specific stop, sorted by `stop_name`.
gtfs.getRoutes({
agency_key: 'caltrain',
stop_id: '70011'
}, {
_id: 0
}, {
sort: {stop_name: 1}
})
.then(routes => {
});
Queries stops and returns a promise. The result of the promise is an array of stops.
// Get all stops for an agency
gtfs.getStops({
agency_key: 'caltrain'
})
.then(stops => {
});
// Get a specific stop by stop_id
gtfs.getStops({
agency_key: 'caltrain',
stop_id: '70011'
})
.then(stops => {
});
// Get a few stops
const stopIds = [
'70011',
'70012'
];
gtfs.getStops({
agency_key: 'caltrain',
stop_id: {
$in: stopIds
}
})
.then(stops => {
});
// Get all stops for a specific route and direction
gtfs.getStops({
agency_key: 'caltrain',
route_id: 'Lo-16APR',
direction_id: 1
})
.then(stops => {
});
// Get all stops within a `radius` of the `lat`, `lon` specified.
// `radius` is optional and in miles. Default: 1 mile.
gtfs.getStops({
within: {
lat: 37.7749,
lon: -122.4194,
radius: 5
}
})
.then(stops => {
});
Queries stops and returns a promise. The result of the promise is an geoJSON object of stops. All valid queries for gtfs.getStops()
work for gtfs.getStopsAsGeoJSON()
.
// Get all stops for an agency as geoJSON
gtfs.getStopsAsGeoJSON({
agency_key: 'caltrain'
})
.then(geojson => {
});
// Get all stops for a specific route and direction as geoJSON
gtfs.getStopsAsGeoJSON({
agency_key: 'caltrain',
route_id: 'Lo-16APR',
direction_id: 1
})
.then(geojson => {
});
stop_ids
is optional and can be a single stop_id
or an array of stop_ids
.
Queries stop_times
and returns a promise. The result of the promise is an array of stop_times
. agency_key
is required. stop_times
are sorted by stop_sequence
by default, but can be overridden by passing a sort
parameter in an options object.
// Get all stoptimes for a specific stop
gtfs.getStoptimes({
agency_key: 'caltrain',
stop_id: '70011'
})
.then(stoptimes => {
});
// Get all stoptimes for a specific stop, route and direction
gtfs.getStoptimes({
agency_key: 'caltrain',
stop_id: '70011',
route_id: 'Lo-16APR',
direction_id: 0
})
.then(stoptimes => {
});
// Get all stoptimes for a specific stop and service_id
gtfs.getStoptimes({
agency_key: 'caltrain',
stop_id: '70011',
service_id: 'CT-16APR-Caltrain-Weekday-01'
})
.then(stoptimes => {
});
// Get all stoptimes for a route and sort by stop_id
gtfs.getStoptimes({
agency_key: 'caltrain',
route_id: '10'
}, {
_id: 0
}, {
sort: {stop_id: 1}
})
.then(stoptimes => {
});
Queries trips and returns a promise. The result of the promise is an array of trips.
// Get trips for a specific route and direction
gtfs.getTrips({
agency_key: 'caltrain',
route_id: 'Lo-16APR',
direction_id: 0
})
.then(trips => {
});
// Get trips for a specific route and direction limited by service_ids
const serviceIds = [
'CT-16APR-Caltrain-Saturday-02',
'CT-16APR-Caltrain-Sunday-02'
];
gtfs.getTrips({
agency_key: 'caltrain',
route_id: 'Lo-16APR',
direction_id: 0,
service_id: {
$in: serviceIds
}
})
.then(trips => {
});
// Get only trip_ids for trips on a specific route and direction, sorted by trip_id
gtfs.getTrips({
agency_key: 'caltrain',
route_id: 'Lo-16APR',
direction_id: 0
}, {
_id: 0,
trip_id: 1
}, {
sort: {trip_id: 1}
})
.then(trips => {
});
Queries trips and returns a promise. The result of the promise is an array of direction_ids. Useful to determine if a route has two directions or just one. agency_key
and route_id
are required.
// Find all directions for a specific route
gtfs.getDirectionsByRoute({
agency_key: 'caltrain',
route_id: 'Lo-16APR'
})
.then(directions => {
});
// Find all directions for a specific route and service_id
gtfs.getDirectionsByRoute({
agency_key: 'caltrain',
route_id: 'Lo-16APR',
service_id: 'CT-16APR-Caltrain-Sunday-02'
})
.then(directions => {
});
Example result:
[
{
route_id: 'Bu-16APR',
trip_headsign: 'SAN FRANCISCO STATION',
direction_id: 0
},
{
route_id: 'Bu-16APR',
trip_headsign: 'DIRIDON STATION',
direction_id: 1
},
{ route_id: 'Bu-16APR',
trip_headsign: 'TAMIEN STATION',
direction_id: 1
}
];
Queries shapes and returns a promise. The result of the promise is an array of shapes sorted by shape_pt_sequence
. Sort can be overridden using the sort
option.
// Get all shapes for an agency
gtfs.getShapes({
agency_key: 'caltrain'
})
.then(shapes => {
});
// Get all shapes for a specific route and direction
gtfs.getShapes({
agency_key: 'caltrain',
route_id: 'Lo-16APR',
direction_id: 0
})
.then(shapes => {
});
// Get all shapes for a specific trip_id
gtfs.getShapes({
agency_key: 'caltrain',
trip_id: '37a'
})
.then(shapes => {
});
// Get all shapes for a few trip_ids
const tripIds = [
'37a',
'39a'
];
gtfs.getShapes({
agency_key: 'caltrain',
trip_id: {
$in: tripIds
}
})
.then(shapes => {
});
Queries shapes and returns a promise. The result of the promise is an geoJSON object of shapes. All valid queries for gtfs.getShapes()
work for gtfs.getShapesAsGeoJSON()
.
Returns geoJSON of shapes for the agency_key
specified.
// Get geoJSON of all routes in an agency
gtfs.getShapesAsGeoJSON({
agency_key: 'caltrain'
})
.then(geojson => {
});
// Get geoJSON of a specific route in an agency
gtfs.getShapesAsGeoJSON({
agency_key: 'caltrain',
route_id: 'Lo-16APR'
})
.then(geojson => {
});
// Get geoJSON of a specific route and direction in an agency
gtfs.getShapesAsGeoJSON({
agency_key: 'caltrain',
route_id: 'Lo-16APR',
direction_id: 0
})
.then(geojson => {
});
Queries calendars and returns a promise. The result of the promise is an array of calendars.
// Get all calendars for an agency
gtfs.getCalendars({
agency_key: 'caltrain'
})
.then(calendars => {
});
// Get calendars for an agency between two dates that apply on a Tuesday
gtfs.getCalendars({
agency_key: 'caltrain',
start_date: {$lt: 20160405},
end_date: {$gte: 20160404},
tuesday: 1
})
.then(calendars => {
});
// Get calendars for an agency by service_ids
gtfs.getCalendars({
agency_key: 'caltrain',
service_id: 'CT-16APR-Caltrain-Sunday-02'
})
.then(calendars => {
});
// Get calendars for an specific route
gtfs.getCalendars({
agency_key: 'caltrain',
route_id: 'TaSj-16APR'
})
.then(calendars => {
});
// Get calendars for several routes
gtfs.getCalendars({
agency_key: 'caltrain',
route_id: {$in: ['TaSj-16APR', 'Lo-16APR']}
})
.then(calendars => {
});
Queries feed_info and returns a promise. The result of the promise is an array of feed_infos.
// Get feed_info for a specified agency
gtfs.getFeedInfo({
agency_key: 'caltrain'
})
.then(feedInfos => {
});
Queries fare_rules and returns a promise. The result of the promise is an array of fare_rules.
// Get fare_rules for a route
gtfs.getFareRules({
agency_key: 'caltrain',
route_id: 'Lo-16APR'
})
.then(fareRules => {
});
Queries frequencies and returns a promise. The result of the promise is an array of frequencies.
// Get frequencies for a specific trip
gtfs.getFrequencies({
agency_key: 'caltrain',
trip_id: '1234'
})
.then(frequencies => {
});
Queries transfers and returns a promise. The result of the promise is an array of transfers.
// Get transfers for a specific stop_id
gtfs.getTransfers({
agency_key: 'caltrain',
from_stop_id: '1234'
})
.then(transfers => {
});
Queries stop_attributes and returns a promise. The result of the promise is an array of stop_attributes. These are from the non-standard stop_attributes.txt
file. See documentation and examples of this file.
// Get stop attributes for a few stops
const stopIds = [
'70011',
'70012'
];
gtfs.getStopAttributes({
agency_key: 'caltrain',
stop_id: {
$in: stopIds
}
})
.then(stopAttributes => {
});
Queries timetables and returns a promise. The result of the promise is an array of timetables. These are from the non-standard timetables.txt
file. See documentation and examples of this file.
// Get all timetables for an agency
gtfs.getTimetables({
agency_key: 'caltrain'
})
.then(timetables => {
});
// Get a specific timetable
gtfs.getTimetables({
agency_key: 'caltrain',
timetable_id: '1'
})
.then(timetables => {
});
Queries timetable_stop_orders and returns a promise. The result of the promise is an array of timetable_stop_orders. These are from the non-standard timetable_stop_order.txt
file. See documentation and examples of this file.
// Get timetable_stop_orders for a specific timetable
gtfs.getTimetableStopOrders({
agency_key: 'caltrain',
timetable_id: '1'
})
.then(TimetableStopOrders => {
});
Queries timetable_pages and returns a promise. The result of the promise is an array of timetable_pages. These are from the non-standard timetable_pages.txt
file. See documentation and examples of this file.
// Get all timetable_pages for an agency
gtfs.getTimetablePages({
agency_key: 'caltrain'
})
.then(timetablePages => {
});
// Get a specific timetable_page
gtfs.getTimetablePages({
agency_key: 'caltrain',
timetable_page_id: '2'
})
.then(timetablePages => {
});
Pull requests are welcome, as is feedback and reporting issues.
To run tests:
npm test
To run a specific test:
NODE_ENV=test mocha ./test/mocha/gtfs.get-stoptimes.js
npm run lint