gtfs 4.17.6 → 4.17.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -2
- package/dist/bin/gtfs-export.js +14 -14
- package/dist/bin/gtfs-export.js.map +1 -1
- package/dist/bin/gtfs-import.js +86 -29
- package/dist/bin/gtfs-import.js.map +1 -1
- package/dist/bin/gtfsrealtime-update.js +13 -13
- package/dist/bin/gtfsrealtime-update.js.map +1 -1
- package/dist/index.js +165 -106
- package/dist/index.js.map +1 -1
- package/package.json +1 -6
package/dist/index.js
CHANGED
|
@@ -9,11 +9,8 @@ import path2 from "path";
|
|
|
9
9
|
import { createReadStream, existsSync as existsSync2, lstatSync } from "fs";
|
|
10
10
|
import { cp, readdir, rename, readFile as readFile2, rm as rm2, writeFile } from "fs/promises";
|
|
11
11
|
import { parse } from "csv-parse";
|
|
12
|
-
import pluralize2 from "pluralize";
|
|
13
12
|
import stripBomStream from "strip-bom-stream";
|
|
14
13
|
import { temporaryDirectory } from "tempy";
|
|
15
|
-
import Timer from "timer-machine";
|
|
16
|
-
import untildify3 from "untildify";
|
|
17
14
|
import mapSeries2 from "promise-map-series";
|
|
18
15
|
|
|
19
16
|
// src/models/models.ts
|
|
@@ -3976,84 +3973,14 @@ var vehicles = {
|
|
|
3976
3973
|
// src/lib/db.ts
|
|
3977
3974
|
import fs from "fs";
|
|
3978
3975
|
import Database from "better-sqlite3";
|
|
3979
|
-
import untildify from "untildify";
|
|
3980
|
-
var dbs = {};
|
|
3981
|
-
function setupDb(sqlitePath) {
|
|
3982
|
-
const db = new Database(untildify(sqlitePath));
|
|
3983
|
-
db.pragma("journal_mode = OFF");
|
|
3984
|
-
db.pragma("synchronous = OFF");
|
|
3985
|
-
db.pragma("temp_store = MEMORY");
|
|
3986
|
-
dbs[sqlitePath] = db;
|
|
3987
|
-
return db;
|
|
3988
|
-
}
|
|
3989
|
-
function openDb(config = null) {
|
|
3990
|
-
if (config) {
|
|
3991
|
-
const { sqlitePath = ":memory:", db } = config;
|
|
3992
|
-
if (db) {
|
|
3993
|
-
return db;
|
|
3994
|
-
}
|
|
3995
|
-
if (dbs[sqlitePath]) {
|
|
3996
|
-
return dbs[sqlitePath];
|
|
3997
|
-
}
|
|
3998
|
-
return setupDb(sqlitePath);
|
|
3999
|
-
}
|
|
4000
|
-
if (Object.keys(dbs).length === 0) {
|
|
4001
|
-
return setupDb(":memory:");
|
|
4002
|
-
}
|
|
4003
|
-
if (Object.keys(dbs).length === 1) {
|
|
4004
|
-
const filename = Object.keys(dbs)[0];
|
|
4005
|
-
return dbs[filename];
|
|
4006
|
-
}
|
|
4007
|
-
if (Object.keys(dbs).length > 1) {
|
|
4008
|
-
throw new Error(
|
|
4009
|
-
"Multiple databases open, please specify which one to use."
|
|
4010
|
-
);
|
|
4011
|
-
}
|
|
4012
|
-
throw new Error("Unable to find database connection.");
|
|
4013
|
-
}
|
|
4014
|
-
function closeDb(db = null) {
|
|
4015
|
-
if (Object.keys(dbs).length === 0) {
|
|
4016
|
-
throw new Error(
|
|
4017
|
-
"No database connection. Call `openDb(config)` before using any methods."
|
|
4018
|
-
);
|
|
4019
|
-
}
|
|
4020
|
-
if (!db) {
|
|
4021
|
-
if (Object.keys(dbs).length > 1) {
|
|
4022
|
-
throw new Error(
|
|
4023
|
-
"Multiple database connections. Pass the db you want to close as a parameter to `closeDb`."
|
|
4024
|
-
);
|
|
4025
|
-
}
|
|
4026
|
-
db = dbs[Object.keys(dbs)[0]];
|
|
4027
|
-
}
|
|
4028
|
-
db.close();
|
|
4029
|
-
delete dbs[db.name];
|
|
4030
|
-
}
|
|
4031
|
-
function deleteDb(db = null) {
|
|
4032
|
-
if (Object.keys(dbs).length === 0) {
|
|
4033
|
-
throw new Error(
|
|
4034
|
-
"No database connection. Call `openDb(config)` before using any methods."
|
|
4035
|
-
);
|
|
4036
|
-
}
|
|
4037
|
-
if (!db) {
|
|
4038
|
-
if (Object.keys(dbs).length > 1) {
|
|
4039
|
-
throw new Error(
|
|
4040
|
-
"Multiple database connections. Pass the db you want to delete as a parameter to `deleteDb`."
|
|
4041
|
-
);
|
|
4042
|
-
}
|
|
4043
|
-
db = dbs[Object.keys(dbs)[0]];
|
|
4044
|
-
}
|
|
4045
|
-
db.close();
|
|
4046
|
-
fs.unlinkSync(db.name);
|
|
4047
|
-
delete dbs[db.name];
|
|
4048
|
-
}
|
|
4049
3976
|
|
|
4050
3977
|
// src/lib/file-utils.ts
|
|
4051
3978
|
import path from "path";
|
|
4052
3979
|
import { existsSync } from "fs";
|
|
3980
|
+
import { homedir } from "os";
|
|
4053
3981
|
import { mkdir, readFile, rm } from "fs/promises";
|
|
4054
3982
|
import { omit, snakeCase } from "lodash-es";
|
|
4055
3983
|
import sanitize from "sanitize-filename";
|
|
4056
|
-
import untildify2 from "untildify";
|
|
4057
3984
|
import StreamZip from "node-stream-zip";
|
|
4058
3985
|
|
|
4059
3986
|
// src/lib/log-utils.ts
|
|
@@ -4107,6 +4034,7 @@ function formatError(error) {
|
|
|
4107
4034
|
}
|
|
4108
4035
|
|
|
4109
4036
|
// src/lib/file-utils.ts
|
|
4037
|
+
var homeDirectory = homedir();
|
|
4110
4038
|
async function prepDirectory(exportPath) {
|
|
4111
4039
|
await rm(exportPath, { recursive: true, force: true });
|
|
4112
4040
|
await mkdir(exportPath, { recursive: true });
|
|
@@ -4128,6 +4056,80 @@ function generateFolderName(folderName) {
|
|
|
4128
4056
|
}
|
|
4129
4057
|
return snakeCase(sanitize(folderName));
|
|
4130
4058
|
}
|
|
4059
|
+
function untildify(pathWithTilde) {
|
|
4060
|
+
return homeDirectory ? pathWithTilde.replace(/^~(?=$|\/|\\)/, homeDirectory) : pathWithTilde;
|
|
4061
|
+
}
|
|
4062
|
+
|
|
4063
|
+
// src/lib/db.ts
|
|
4064
|
+
var dbs = {};
|
|
4065
|
+
function setupDb(sqlitePath) {
|
|
4066
|
+
const db = new Database(untildify(sqlitePath));
|
|
4067
|
+
db.pragma("journal_mode = OFF");
|
|
4068
|
+
db.pragma("synchronous = OFF");
|
|
4069
|
+
db.pragma("temp_store = MEMORY");
|
|
4070
|
+
dbs[sqlitePath] = db;
|
|
4071
|
+
return db;
|
|
4072
|
+
}
|
|
4073
|
+
function openDb(config = null) {
|
|
4074
|
+
if (config) {
|
|
4075
|
+
const { sqlitePath = ":memory:", db } = config;
|
|
4076
|
+
if (db) {
|
|
4077
|
+
return db;
|
|
4078
|
+
}
|
|
4079
|
+
if (dbs[sqlitePath]) {
|
|
4080
|
+
return dbs[sqlitePath];
|
|
4081
|
+
}
|
|
4082
|
+
return setupDb(sqlitePath);
|
|
4083
|
+
}
|
|
4084
|
+
if (Object.keys(dbs).length === 0) {
|
|
4085
|
+
return setupDb(":memory:");
|
|
4086
|
+
}
|
|
4087
|
+
if (Object.keys(dbs).length === 1) {
|
|
4088
|
+
const filename = Object.keys(dbs)[0];
|
|
4089
|
+
return dbs[filename];
|
|
4090
|
+
}
|
|
4091
|
+
if (Object.keys(dbs).length > 1) {
|
|
4092
|
+
throw new Error(
|
|
4093
|
+
"Multiple databases open, please specify which one to use."
|
|
4094
|
+
);
|
|
4095
|
+
}
|
|
4096
|
+
throw new Error("Unable to find database connection.");
|
|
4097
|
+
}
|
|
4098
|
+
function closeDb(db = null) {
|
|
4099
|
+
if (Object.keys(dbs).length === 0) {
|
|
4100
|
+
throw new Error(
|
|
4101
|
+
"No database connection. Call `openDb(config)` before using any methods."
|
|
4102
|
+
);
|
|
4103
|
+
}
|
|
4104
|
+
if (!db) {
|
|
4105
|
+
if (Object.keys(dbs).length > 1) {
|
|
4106
|
+
throw new Error(
|
|
4107
|
+
"Multiple database connections. Pass the db you want to close as a parameter to `closeDb`."
|
|
4108
|
+
);
|
|
4109
|
+
}
|
|
4110
|
+
db = dbs[Object.keys(dbs)[0]];
|
|
4111
|
+
}
|
|
4112
|
+
db.close();
|
|
4113
|
+
delete dbs[db.name];
|
|
4114
|
+
}
|
|
4115
|
+
function deleteDb(db = null) {
|
|
4116
|
+
if (Object.keys(dbs).length === 0) {
|
|
4117
|
+
throw new Error(
|
|
4118
|
+
"No database connection. Call `openDb(config)` before using any methods."
|
|
4119
|
+
);
|
|
4120
|
+
}
|
|
4121
|
+
if (!db) {
|
|
4122
|
+
if (Object.keys(dbs).length > 1) {
|
|
4123
|
+
throw new Error(
|
|
4124
|
+
"Multiple database connections. Pass the db you want to delete as a parameter to `deleteDb`."
|
|
4125
|
+
);
|
|
4126
|
+
}
|
|
4127
|
+
db = dbs[Object.keys(dbs)[0]];
|
|
4128
|
+
}
|
|
4129
|
+
db.close();
|
|
4130
|
+
fs.unlinkSync(db.name);
|
|
4131
|
+
delete dbs[db.name];
|
|
4132
|
+
}
|
|
4131
4133
|
|
|
4132
4134
|
// src/lib/geojson-utils.ts
|
|
4133
4135
|
import {
|
|
@@ -4249,7 +4251,6 @@ function stopsToGeoJSONFeatureCollection(stops2) {
|
|
|
4249
4251
|
}
|
|
4250
4252
|
|
|
4251
4253
|
// src/lib/import-gtfs-realtime.ts
|
|
4252
|
-
import pluralize from "pluralize";
|
|
4253
4254
|
import GtfsRealtimeBindings from "gtfs-realtime-bindings";
|
|
4254
4255
|
import mapSeries from "promise-map-series";
|
|
4255
4256
|
import { get } from "lodash-es";
|
|
@@ -4419,6 +4420,9 @@ function applyPrefixToValue(value, columnShouldBePrefixed, prefix) {
|
|
|
4419
4420
|
}
|
|
4420
4421
|
return `${prefix}${value}`;
|
|
4421
4422
|
}
|
|
4423
|
+
function pluralize(singularWord, pluralWord, count) {
|
|
4424
|
+
return count === 1 ? singularWord : pluralWord;
|
|
4425
|
+
}
|
|
4422
4426
|
|
|
4423
4427
|
// src/lib/import-gtfs-realtime.ts
|
|
4424
4428
|
async function fetchGtfsRealtimeData(urlConfig, task) {
|
|
@@ -4653,9 +4657,9 @@ async function updateGtfsRealtime(initialConfig) {
|
|
|
4653
4657
|
const agencyCount = config.agencies.length;
|
|
4654
4658
|
log(config)(
|
|
4655
4659
|
`Starting GTFS-Realtime refresh for ${pluralize(
|
|
4660
|
+
"agency",
|
|
4656
4661
|
"agencies",
|
|
4657
|
-
agencyCount
|
|
4658
|
-
true
|
|
4662
|
+
agencyCount
|
|
4659
4663
|
)} using SQLite database at ${config.sqlitePath}`
|
|
4660
4664
|
);
|
|
4661
4665
|
removeExpiredRealtimeData(config);
|
|
@@ -4686,9 +4690,9 @@ async function updateGtfsRealtime(initialConfig) {
|
|
|
4686
4690
|
});
|
|
4687
4691
|
log(config)(
|
|
4688
4692
|
`Completed GTFS-Realtime refresh for ${pluralize(
|
|
4693
|
+
"agency",
|
|
4689
4694
|
"agencies",
|
|
4690
|
-
agencyCount
|
|
4691
|
-
true
|
|
4695
|
+
agencyCount
|
|
4692
4696
|
)}
|
|
4693
4697
|
`
|
|
4694
4698
|
);
|
|
@@ -4731,7 +4735,7 @@ var extractGtfsFiles = async (task) => {
|
|
|
4731
4735
|
if (!task.path) {
|
|
4732
4736
|
throw new Error("No `path` specified in config");
|
|
4733
4737
|
}
|
|
4734
|
-
const gtfsPath =
|
|
4738
|
+
const gtfsPath = untildify(task.path);
|
|
4735
4739
|
task.log(`Importing GTFS from ${task.path}\r`);
|
|
4736
4740
|
if (path2.extname(gtfsPath) === ".zip") {
|
|
4737
4741
|
try {
|
|
@@ -4983,7 +4987,12 @@ var importGtfsFiles = (db, task) => mapSeries2(
|
|
|
4983
4987
|
}
|
|
4984
4988
|
}
|
|
4985
4989
|
} catch (error) {
|
|
4986
|
-
|
|
4990
|
+
if (task.ignoreErrors) {
|
|
4991
|
+
task.logError(`Error processing ${filename}: ${error.message}`);
|
|
4992
|
+
resolve();
|
|
4993
|
+
} else {
|
|
4994
|
+
reject(error);
|
|
4995
|
+
}
|
|
4987
4996
|
}
|
|
4988
4997
|
});
|
|
4989
4998
|
parser.on("end", () => {
|
|
@@ -4992,7 +5001,16 @@ var importGtfsFiles = (db, task) => mapSeries2(
|
|
|
4992
5001
|
try {
|
|
4993
5002
|
insertLines(lines);
|
|
4994
5003
|
} catch (error) {
|
|
4995
|
-
|
|
5004
|
+
if (task.ignoreErrors) {
|
|
5005
|
+
task.logError(
|
|
5006
|
+
`Error inserting data for ${filename}: ${error.message}`
|
|
5007
|
+
);
|
|
5008
|
+
resolve();
|
|
5009
|
+
return;
|
|
5010
|
+
} else {
|
|
5011
|
+
reject(error);
|
|
5012
|
+
return;
|
|
5013
|
+
}
|
|
4996
5014
|
}
|
|
4997
5015
|
}
|
|
4998
5016
|
task.log(
|
|
@@ -5001,15 +5019,34 @@ var importGtfsFiles = (db, task) => mapSeries2(
|
|
|
5001
5019
|
);
|
|
5002
5020
|
resolve();
|
|
5003
5021
|
} catch (error) {
|
|
5022
|
+
if (task.ignoreErrors) {
|
|
5023
|
+
task.logError(`Error finalizing ${filename}: ${error.message}`);
|
|
5024
|
+
resolve();
|
|
5025
|
+
} else {
|
|
5026
|
+
reject(error);
|
|
5027
|
+
}
|
|
5028
|
+
}
|
|
5029
|
+
});
|
|
5030
|
+
parser.on("error", (error) => {
|
|
5031
|
+
if (task.ignoreErrors) {
|
|
5032
|
+
task.logError(`Parser error for ${filename}: ${error.message}`);
|
|
5033
|
+
resolve();
|
|
5034
|
+
} else {
|
|
5004
5035
|
reject(error);
|
|
5005
5036
|
}
|
|
5006
5037
|
});
|
|
5007
|
-
parser.on("error", reject);
|
|
5008
5038
|
createReadStream(filepath).pipe(stripBomStream()).pipe(parser);
|
|
5009
5039
|
} else if (model.filenameExtension === "geojson") {
|
|
5010
5040
|
readFile2(filepath, "utf8").then((data) => {
|
|
5011
5041
|
if (isValidJSON(data) === false) {
|
|
5012
|
-
|
|
5042
|
+
if (task.ignoreErrors) {
|
|
5043
|
+
task.logError(`Invalid JSON in ${filename}`);
|
|
5044
|
+
resolve();
|
|
5045
|
+
return;
|
|
5046
|
+
} else {
|
|
5047
|
+
reject(new Error(`Invalid JSON in ${filename}`));
|
|
5048
|
+
return;
|
|
5049
|
+
}
|
|
5013
5050
|
}
|
|
5014
5051
|
totalLineCount += 1;
|
|
5015
5052
|
const line = formatGtfsLine(
|
|
@@ -5017,30 +5054,54 @@ var importGtfsFiles = (db, task) => mapSeries2(
|
|
|
5017
5054
|
model,
|
|
5018
5055
|
totalLineCount
|
|
5019
5056
|
);
|
|
5020
|
-
|
|
5021
|
-
|
|
5022
|
-
|
|
5023
|
-
|
|
5057
|
+
try {
|
|
5058
|
+
insertLines([line]);
|
|
5059
|
+
task.log(
|
|
5060
|
+
`Importing - ${filename} - ${totalLineCount} lines imported\r`,
|
|
5061
|
+
true
|
|
5062
|
+
);
|
|
5063
|
+
resolve();
|
|
5064
|
+
} catch (error) {
|
|
5065
|
+
if (task.ignoreErrors) {
|
|
5066
|
+
task.logError(
|
|
5067
|
+
`Error inserting data for ${filename}: ${error.message}`
|
|
5068
|
+
);
|
|
5069
|
+
resolve();
|
|
5070
|
+
} else {
|
|
5071
|
+
reject(error);
|
|
5072
|
+
}
|
|
5073
|
+
}
|
|
5074
|
+
}).catch((error) => {
|
|
5075
|
+
if (task.ignoreErrors) {
|
|
5076
|
+
task.logError(`Error reading ${filename}: ${error.message}`);
|
|
5077
|
+
resolve();
|
|
5078
|
+
} else {
|
|
5079
|
+
reject(error);
|
|
5080
|
+
}
|
|
5081
|
+
});
|
|
5082
|
+
} else {
|
|
5083
|
+
if (task.ignoreErrors) {
|
|
5084
|
+
task.logError(
|
|
5085
|
+
`Unsupported file type: ${model.filenameExtension} for ${filename}`
|
|
5024
5086
|
);
|
|
5025
5087
|
resolve();
|
|
5026
|
-
}
|
|
5027
|
-
|
|
5028
|
-
|
|
5029
|
-
|
|
5030
|
-
|
|
5088
|
+
} else {
|
|
5089
|
+
reject(
|
|
5090
|
+
new Error(`Unsupported file type: ${model.filenameExtension}`)
|
|
5091
|
+
);
|
|
5092
|
+
}
|
|
5031
5093
|
}
|
|
5032
5094
|
})
|
|
5033
5095
|
);
|
|
5034
5096
|
async function importGtfs(initialConfig) {
|
|
5035
|
-
const
|
|
5036
|
-
timer.start();
|
|
5097
|
+
const startTime = process.hrtime.bigint();
|
|
5037
5098
|
const config = setDefaultConfig(initialConfig);
|
|
5038
5099
|
validateConfigForImport(config);
|
|
5039
5100
|
try {
|
|
5040
5101
|
const db = openDb(config);
|
|
5041
5102
|
const agencyCount = config.agencies.length;
|
|
5042
5103
|
log(config)(
|
|
5043
|
-
`Starting GTFS import for ${
|
|
5104
|
+
`Starting GTFS import for ${pluralize("file", "files", agencyCount)} using SQLite database at ${config.sqlitePath}`
|
|
5044
5105
|
);
|
|
5045
5106
|
createGtfsTables(db);
|
|
5046
5107
|
await mapSeries2(config.agencies, async (agency2) => {
|
|
@@ -5084,10 +5145,10 @@ async function importGtfs(initialConfig) {
|
|
|
5084
5145
|
});
|
|
5085
5146
|
log(config)(`Creating DB indexes`);
|
|
5086
5147
|
createGtfsIndexes(db);
|
|
5087
|
-
const
|
|
5088
|
-
|
|
5148
|
+
const endTime = process.hrtime.bigint();
|
|
5149
|
+
const elapsedSeconds = Number(endTime - startTime) / 1e9;
|
|
5089
5150
|
log(config)(
|
|
5090
|
-
`Completed GTFS import
|
|
5151
|
+
`Completed GTFS import in ${elapsedSeconds.toFixed(1)} seconds
|
|
5091
5152
|
`
|
|
5092
5153
|
);
|
|
5093
5154
|
} catch (error) {
|
|
@@ -5104,11 +5165,9 @@ async function importGtfs(initialConfig) {
|
|
|
5104
5165
|
import path3 from "path";
|
|
5105
5166
|
import { writeFile as writeFile2 } from "fs/promises";
|
|
5106
5167
|
import { without, compact as compact2 } from "lodash-es";
|
|
5107
|
-
import pluralize3 from "pluralize";
|
|
5108
5168
|
import { stringify } from "csv-stringify";
|
|
5109
5169
|
import sqlString2 from "sqlstring-sqlite";
|
|
5110
5170
|
import mapSeries3 from "promise-map-series";
|
|
5111
|
-
import untildify4 from "untildify";
|
|
5112
5171
|
var getAgencies = (db, config) => {
|
|
5113
5172
|
try {
|
|
5114
5173
|
return db.prepare("SELECT agency_name FROM agency;").all();
|
|
@@ -5138,15 +5197,15 @@ var exportGtfs = async (initialConfig) => {
|
|
|
5138
5197
|
);
|
|
5139
5198
|
}
|
|
5140
5199
|
log(config)(
|
|
5141
|
-
`Starting GTFS export for ${
|
|
5200
|
+
`Starting GTFS export for ${pluralize(
|
|
5142
5201
|
"agency",
|
|
5143
|
-
|
|
5144
|
-
|
|
5202
|
+
"agencies",
|
|
5203
|
+
agencyCount
|
|
5145
5204
|
)} using SQLite database at ${config.sqlitePath}`
|
|
5146
5205
|
);
|
|
5147
5206
|
const folderName = generateFolderName(agencies[0].agency_name);
|
|
5148
5207
|
const defaultExportPath = path3.join(process.cwd(), "gtfs-export", folderName);
|
|
5149
|
-
const exportPath =
|
|
5208
|
+
const exportPath = untildify(config.exportPath || defaultExportPath);
|
|
5150
5209
|
await prepDirectory(exportPath);
|
|
5151
5210
|
const modelsToExport = Object.values(models_exports).filter(
|
|
5152
5211
|
(model) => model.extension !== "gtfs-realtime"
|
|
@@ -5214,7 +5273,7 @@ var exportGtfs = async (initialConfig) => {
|
|
|
5214
5273
|
}
|
|
5215
5274
|
log(config)(`Completed GTFS export to ${exportPath}`);
|
|
5216
5275
|
log(config)(
|
|
5217
|
-
`Completed GTFS export for ${
|
|
5276
|
+
`Completed GTFS export for ${pluralize("agency", "agencies", agencyCount)}
|
|
5218
5277
|
`
|
|
5219
5278
|
);
|
|
5220
5279
|
};
|