gtfs 4.15.4 → 4.15.6
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/dist/bin/gtfs-export.js +32 -54
- package/dist/bin/gtfs-export.js.map +1 -1
- package/dist/bin/gtfs-import.js +50 -63
- package/dist/bin/gtfs-import.js.map +1 -1
- package/dist/bin/gtfsrealtime-update.js +32 -55
- package/dist/bin/gtfsrealtime-update.js.map +1 -1
- package/dist/index.d.ts +332 -58
- package/dist/index.js +84 -69
- package/dist/index.js.map +1 -1
- package/package.json +6 -5
package/dist/index.js
CHANGED
|
@@ -3175,11 +3175,20 @@ async function prepDirectory(exportPath) {
|
|
|
3175
3175
|
await mkdir(exportPath, { recursive: true });
|
|
3176
3176
|
}
|
|
3177
3177
|
async function unzip(zipfilePath, exportPath) {
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
|
|
3178
|
+
try {
|
|
3179
|
+
const zip = new StreamZip.async({ file: zipfilePath });
|
|
3180
|
+
await zip.extract(null, exportPath);
|
|
3181
|
+
await zip.close();
|
|
3182
|
+
} catch (error) {
|
|
3183
|
+
throw new Error(
|
|
3184
|
+
`Failed to extract zip file: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
3185
|
+
);
|
|
3186
|
+
}
|
|
3181
3187
|
}
|
|
3182
3188
|
function generateFolderName(folderName) {
|
|
3189
|
+
if (!folderName || typeof folderName !== "string") {
|
|
3190
|
+
throw new Error("Folder name must be a non-empty string");
|
|
3191
|
+
}
|
|
3183
3192
|
return snakeCase(sanitize(folderName));
|
|
3184
3193
|
}
|
|
3185
3194
|
|
|
@@ -3198,69 +3207,63 @@ import { feature, featureCollection } from "@turf/helpers";
|
|
|
3198
3207
|
function isValidJSON(string) {
|
|
3199
3208
|
try {
|
|
3200
3209
|
JSON.parse(string);
|
|
3210
|
+
return true;
|
|
3201
3211
|
} catch (error) {
|
|
3202
3212
|
return false;
|
|
3203
3213
|
}
|
|
3204
|
-
return true;
|
|
3205
3214
|
}
|
|
3206
3215
|
function isValidLineString(lineString) {
|
|
3207
|
-
if (!lineString) {
|
|
3216
|
+
if (!lineString || lineString.length <= 1) {
|
|
3208
3217
|
return false;
|
|
3209
3218
|
}
|
|
3210
|
-
if (lineString.length
|
|
3211
|
-
|
|
3212
|
-
|
|
3213
|
-
if (lineString.length === 2 && lineString[0][0] === lineString[1][0] && lineString[0][1] === lineString[1][1]) {
|
|
3214
|
-
return false;
|
|
3219
|
+
if (lineString.length === 2) {
|
|
3220
|
+
const [[x1, y1], [x2, y2]] = lineString;
|
|
3221
|
+
return !(x1 === x2 && y1 === y2);
|
|
3215
3222
|
}
|
|
3216
3223
|
return true;
|
|
3217
3224
|
}
|
|
3218
3225
|
function consolidateShapes(shapeGroups) {
|
|
3219
3226
|
const keys = /* @__PURE__ */ new Set();
|
|
3220
3227
|
const segmentsArray = shapeGroups.map(
|
|
3221
|
-
(shapes2) => shapes2.reduce(
|
|
3222
|
-
(
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
[]
|
|
3232
|
-
)
|
|
3228
|
+
(shapes2) => shapes2.reduce((memo, point, idx) => {
|
|
3229
|
+
if (idx > 0) {
|
|
3230
|
+
const prevPoint = shapes2[idx - 1];
|
|
3231
|
+
memo.push([
|
|
3232
|
+
[prevPoint.shape_pt_lon, prevPoint.shape_pt_lat],
|
|
3233
|
+
[point.shape_pt_lon, point.shape_pt_lat]
|
|
3234
|
+
]);
|
|
3235
|
+
}
|
|
3236
|
+
return memo;
|
|
3237
|
+
}, [])
|
|
3233
3238
|
);
|
|
3234
3239
|
const consolidatedLineStrings = [];
|
|
3235
3240
|
for (const segments of segmentsArray) {
|
|
3236
3241
|
consolidatedLineStrings.push([]);
|
|
3237
3242
|
for (const segment of segments) {
|
|
3238
|
-
const key1 =
|
|
3239
|
-
const key2 =
|
|
3243
|
+
const key1 = segment.flat().join(",");
|
|
3244
|
+
const key2 = segment.reverse().flat().join(",");
|
|
3240
3245
|
const currentLine = last(consolidatedLineStrings);
|
|
3241
|
-
if (keys.has(key1) || keys.has(key2)) {
|
|
3246
|
+
if (!currentLine || keys.has(key1) || keys.has(key2)) {
|
|
3242
3247
|
consolidatedLineStrings.push([]);
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
currentLine?.push(segment[1]);
|
|
3248
|
-
keys.add(key1);
|
|
3249
|
-
keys.add(key2);
|
|
3248
|
+
continue;
|
|
3249
|
+
}
|
|
3250
|
+
if (currentLine.length === 0) {
|
|
3251
|
+
currentLine.push(segment[0]);
|
|
3250
3252
|
}
|
|
3253
|
+
currentLine.push(segment[1]);
|
|
3254
|
+
keys.add(key1);
|
|
3255
|
+
keys.add(key2);
|
|
3251
3256
|
}
|
|
3252
3257
|
}
|
|
3253
3258
|
return filter(consolidatedLineStrings, isValidLineString);
|
|
3254
3259
|
}
|
|
3255
3260
|
function formatHexColor(color) {
|
|
3256
|
-
if (color
|
|
3257
|
-
return;
|
|
3258
|
-
}
|
|
3261
|
+
if (!color) return void 0;
|
|
3259
3262
|
return `#${color}`;
|
|
3260
3263
|
}
|
|
3261
3264
|
function formatProperties(properties) {
|
|
3262
3265
|
const formattedProperties = cloneDeep(
|
|
3263
|
-
omitBy(properties, (value) => value
|
|
3266
|
+
omitBy(properties, (value) => value == null)
|
|
3264
3267
|
);
|
|
3265
3268
|
const formattedRouteColor = formatHexColor(properties.route_color);
|
|
3266
3269
|
const formattedRouteTextColor = formatHexColor(properties.route_text_color);
|
|
@@ -3294,7 +3297,7 @@ function stopsToGeoJSONFeatureCollection(stops2) {
|
|
|
3294
3297
|
const features = compact(
|
|
3295
3298
|
stops2.map((stop) => {
|
|
3296
3299
|
if (!stop.stop_lon || !stop.stop_lat) {
|
|
3297
|
-
return;
|
|
3300
|
+
return void 0;
|
|
3298
3301
|
}
|
|
3299
3302
|
return feature(
|
|
3300
3303
|
{
|
|
@@ -3319,14 +3322,14 @@ import { clearLine, cursorTo } from "node:readline";
|
|
|
3319
3322
|
import { noop } from "lodash-es";
|
|
3320
3323
|
import * as colors from "yoctocolors";
|
|
3321
3324
|
function log(config) {
|
|
3322
|
-
if (config.verbose
|
|
3325
|
+
if (!config.verbose) {
|
|
3323
3326
|
return noop;
|
|
3324
3327
|
}
|
|
3325
3328
|
if (config.logFunction) {
|
|
3326
3329
|
return config.logFunction;
|
|
3327
3330
|
}
|
|
3328
|
-
return (text, overwrite) => {
|
|
3329
|
-
if (overwrite
|
|
3331
|
+
return (text, overwrite = false) => {
|
|
3332
|
+
if (overwrite && process.stdout.isTTY) {
|
|
3330
3333
|
clearLine(process.stdout, 0);
|
|
3331
3334
|
cursorTo(process.stdout, 0);
|
|
3332
3335
|
} else {
|
|
@@ -3356,16 +3359,12 @@ ${formatError(text)}
|
|
|
3356
3359
|
};
|
|
3357
3360
|
}
|
|
3358
3361
|
function formatWarning(text) {
|
|
3359
|
-
|
|
3360
|
-
return colors.yellow(warningMessage);
|
|
3362
|
+
return colors.yellow(`${colors.underline("Warning")}: ${text}`);
|
|
3361
3363
|
}
|
|
3362
3364
|
function formatError(error) {
|
|
3363
3365
|
const messageText = error instanceof Error ? error.message : error;
|
|
3364
|
-
const
|
|
3365
|
-
|
|
3366
|
-
""
|
|
3367
|
-
)}`;
|
|
3368
|
-
return colors.red(errorMessage);
|
|
3366
|
+
const cleanMessage = messageText.replace(/^Error:\s*/i, "");
|
|
3367
|
+
return colors.red(`${colors.underline("Error")}: ${cleanMessage}`);
|
|
3369
3368
|
}
|
|
3370
3369
|
|
|
3371
3370
|
// src/lib/utils.ts
|
|
@@ -3398,14 +3397,17 @@ function setDefaultConfig(initialConfig) {
|
|
|
3398
3397
|
}
|
|
3399
3398
|
function convertLongTimeToDate(longDate) {
|
|
3400
3399
|
const { high, low, unsigned } = longDate;
|
|
3401
|
-
return new Date(
|
|
3400
|
+
return new Date(
|
|
3401
|
+
Long.fromBits(low, high, unsigned).toNumber() * 1e3
|
|
3402
|
+
).toISOString();
|
|
3402
3403
|
}
|
|
3403
3404
|
function calculateSecondsFromMidnight(time) {
|
|
3404
|
-
|
|
3405
|
-
|
|
3405
|
+
if (!time || typeof time !== "string") return null;
|
|
3406
|
+
const [hours, minutes, seconds] = time.split(":").map(Number);
|
|
3407
|
+
if ([hours, minutes, seconds].some(isNaN) || hours >= 24 || minutes >= 60 || seconds >= 60) {
|
|
3406
3408
|
return null;
|
|
3407
3409
|
}
|
|
3408
|
-
return
|
|
3410
|
+
return hours * 3600 + minutes * 60 + seconds;
|
|
3409
3411
|
}
|
|
3410
3412
|
function padLeadingZeros(time) {
|
|
3411
3413
|
const split = time.split(":").map((d) => String(Number(d)).padStart(2, "0"));
|
|
@@ -3437,19 +3439,22 @@ function degree2radian(angle) {
|
|
|
3437
3439
|
function radian2degree(angle) {
|
|
3438
3440
|
return angle / Math.PI * 180;
|
|
3439
3441
|
}
|
|
3442
|
+
var EARTH_RADIUS_METERS = 6371e3;
|
|
3440
3443
|
function formatWhereClauseBoundingBox(latitudeDegree, longitudeDegree, boundingBoxSideMeters) {
|
|
3441
|
-
const
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
const
|
|
3447
|
-
const
|
|
3448
|
-
|
|
3449
|
-
);
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3444
|
+
const lat = Number(latitudeDegree);
|
|
3445
|
+
const lon = Number(longitudeDegree);
|
|
3446
|
+
if (isNaN(lat) || isNaN(lon) || lat < -90 || lat > 90 || lon < -180 || lon > 180) {
|
|
3447
|
+
throw new Error("Invalid latitude or longitude values");
|
|
3448
|
+
}
|
|
3449
|
+
const latitudeRadian = degree2radian(lat);
|
|
3450
|
+
const radiusFromLatitude = Math.cos(latitudeRadian) * EARTH_RADIUS_METERS;
|
|
3451
|
+
const halfSide = boundingBoxSideMeters / 2;
|
|
3452
|
+
const deltaLatitude = radian2degree(halfSide / EARTH_RADIUS_METERS);
|
|
3453
|
+
const deltaLongitude = radian2degree(halfSide / radiusFromLatitude);
|
|
3454
|
+
return [
|
|
3455
|
+
`stop_lat BETWEEN ${lat - deltaLatitude} AND ${lat + deltaLatitude}`,
|
|
3456
|
+
`stop_lon BETWEEN ${lon - deltaLongitude} AND ${lon + deltaLongitude}`
|
|
3457
|
+
].join(" AND ");
|
|
3453
3458
|
}
|
|
3454
3459
|
function formatWhereClause(key, value) {
|
|
3455
3460
|
if (Array.isArray(value)) {
|
|
@@ -3485,7 +3490,7 @@ function formatOrderByClause(orderBy2) {
|
|
|
3485
3490
|
return orderByClause;
|
|
3486
3491
|
}
|
|
3487
3492
|
function getDayOfWeekFromDate(date) {
|
|
3488
|
-
const
|
|
3493
|
+
const DAYS_OF_WEEK = [
|
|
3489
3494
|
"sunday",
|
|
3490
3495
|
"monday",
|
|
3491
3496
|
"tuesday",
|
|
@@ -3494,10 +3499,17 @@ function getDayOfWeekFromDate(date) {
|
|
|
3494
3499
|
"friday",
|
|
3495
3500
|
"saturday"
|
|
3496
3501
|
];
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3502
|
+
if (!Number.isInteger(date) || date.toString().length !== 8) {
|
|
3503
|
+
throw new Error("Date must be in YYYYMMDD format");
|
|
3504
|
+
}
|
|
3505
|
+
const year = Math.floor(date / 1e4);
|
|
3506
|
+
const month = Math.floor(date % 1e4 / 100);
|
|
3507
|
+
const day = date % 100;
|
|
3508
|
+
const dateObj = new Date(year, month - 1, day);
|
|
3509
|
+
if (dateObj.toString() === "Invalid Date") {
|
|
3510
|
+
throw new Error("Invalid date");
|
|
3511
|
+
}
|
|
3512
|
+
return DAYS_OF_WEEK[dateObj.getDay()];
|
|
3501
3513
|
}
|
|
3502
3514
|
|
|
3503
3515
|
// src/lib/import-gtfs-realtime.ts
|
|
@@ -4018,6 +4030,9 @@ var importGtfsFiles = (db, task) => mapSeries2(
|
|
|
4018
4030
|
task.log(`Importing - ${filename}\r`);
|
|
4019
4031
|
const columns = model.schema.filter((column) => column.name !== "id");
|
|
4020
4032
|
const placeholder = columns.map(({ name }) => `@${name}`).join(", ");
|
|
4033
|
+
const prefixedColumns = new Set(
|
|
4034
|
+
columns.filter((col) => col.prefix).map((col) => col.name)
|
|
4035
|
+
);
|
|
4021
4036
|
const prepareStatement = `INSERT ${task.ignoreDuplicates ? "OR IGNORE" : ""} INTO ${model.filenameBase} (${columns.map((column) => column.name).join(", ")}) VALUES (${placeholder})`;
|
|
4022
4037
|
const insert = db.prepare(prepareStatement);
|
|
4023
4038
|
const insertLines = db.transaction((lines) => {
|
|
@@ -4031,7 +4046,7 @@ var importGtfsFiles = (db, task) => mapSeries2(
|
|
|
4031
4046
|
line
|
|
4032
4047
|
).map(([columnName, value]) => [
|
|
4033
4048
|
columnName,
|
|
4034
|
-
|
|
4049
|
+
prefixedColumns.has(columnName) && value !== null ? `${task.prefix}${value}` : value
|
|
4035
4050
|
])
|
|
4036
4051
|
);
|
|
4037
4052
|
insert.run(prefixedLine);
|