gtfs 4.13.4 → 4.14.1
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 +36 -20
- package/dist/bin/gtfs-export.js +4 -8
- package/dist/bin/gtfs-export.js.map +1 -1
- package/dist/bin/gtfs-import.js +93 -66
- package/dist/bin/gtfs-import.js.map +1 -1
- package/dist/bin/gtfsrealtime-update.js +89 -197
- package/dist/bin/gtfsrealtime-update.js.map +1 -1
- package/dist/index.d.ts +325 -43
- package/dist/index.js +116 -88
- package/dist/index.js.map +1 -1
- package/dist/models/models.d.ts +0 -8
- package/dist/models/models.js +0 -5
- package/dist/models/models.js.map +1 -1
- package/package.json +8 -8
package/dist/index.js
CHANGED
|
@@ -634,11 +634,6 @@ var fareTransferRules = {
|
|
|
634
634
|
min: -1,
|
|
635
635
|
primary: true
|
|
636
636
|
},
|
|
637
|
-
{
|
|
638
|
-
name: "transfer_id",
|
|
639
|
-
type: "text",
|
|
640
|
-
prefix: true
|
|
641
|
-
},
|
|
642
637
|
{
|
|
643
638
|
name: "duration_limit",
|
|
644
639
|
type: "integer",
|
|
@@ -3192,6 +3187,7 @@ function generateFolderName(folderName) {
|
|
|
3192
3187
|
// src/lib/geojson-utils.ts
|
|
3193
3188
|
import {
|
|
3194
3189
|
cloneDeep,
|
|
3190
|
+
compact,
|
|
3195
3191
|
filter,
|
|
3196
3192
|
groupBy,
|
|
3197
3193
|
last,
|
|
@@ -3220,14 +3216,14 @@ function isValidLineString(lineString) {
|
|
|
3220
3216
|
}
|
|
3221
3217
|
return true;
|
|
3222
3218
|
}
|
|
3223
|
-
function consolidateShapes(
|
|
3219
|
+
function consolidateShapes(shapeGroups) {
|
|
3224
3220
|
const keys = /* @__PURE__ */ new Set();
|
|
3225
|
-
const segmentsArray =
|
|
3226
|
-
(
|
|
3221
|
+
const segmentsArray = shapeGroups.map(
|
|
3222
|
+
(shapes2) => shapes2.reduce(
|
|
3227
3223
|
(memo, point, idx) => {
|
|
3228
3224
|
if (idx > 0) {
|
|
3229
3225
|
memo.push([
|
|
3230
|
-
[
|
|
3226
|
+
[shapes2[idx - 1].shape_pt_lon, shapes2[idx - 1].shape_pt_lat],
|
|
3231
3227
|
[point.shape_pt_lon, point.shape_pt_lat]
|
|
3232
3228
|
]);
|
|
3233
3229
|
}
|
|
@@ -3296,15 +3292,18 @@ function shapesToGeoJSONFeature(shapes2, properties = {}) {
|
|
|
3296
3292
|
);
|
|
3297
3293
|
}
|
|
3298
3294
|
function stopsToGeoJSONFeatureCollection(stops2) {
|
|
3299
|
-
const features = stops2.map(
|
|
3300
|
-
(stop)
|
|
3295
|
+
const features = compact(stops2.map((stop) => {
|
|
3296
|
+
if (!stop.stop_lon || !stop.stop_lat) {
|
|
3297
|
+
return;
|
|
3298
|
+
}
|
|
3299
|
+
return feature(
|
|
3301
3300
|
{
|
|
3302
3301
|
type: "Point",
|
|
3303
3302
|
coordinates: [stop.stop_lon, stop.stop_lat]
|
|
3304
3303
|
},
|
|
3305
3304
|
formatProperties(omit2(stop, ["stop_lat", "stop_lon"]))
|
|
3306
|
-
)
|
|
3307
|
-
);
|
|
3305
|
+
);
|
|
3306
|
+
}));
|
|
3308
3307
|
return featureCollection(features);
|
|
3309
3308
|
}
|
|
3310
3309
|
|
|
@@ -3480,31 +3479,34 @@ function formatOrderByClause(orderBy2) {
|
|
|
3480
3479
|
|
|
3481
3480
|
// src/lib/import.ts
|
|
3482
3481
|
var downloadFiles = async (task) => {
|
|
3483
|
-
task.log(`Downloading GTFS from ${task.
|
|
3482
|
+
task.log(`Downloading GTFS from ${task.url}`);
|
|
3484
3483
|
task.path = `${task.downloadDir}/gtfs.zip`;
|
|
3485
|
-
const response = await fetch(task.
|
|
3484
|
+
const response = await fetch(task.url, {
|
|
3486
3485
|
method: "GET",
|
|
3487
3486
|
headers: task.headers || {},
|
|
3488
3487
|
signal: task.downloadTimeout ? AbortSignal.timeout(task.downloadTimeout) : void 0
|
|
3489
3488
|
});
|
|
3490
3489
|
if (response.status !== 200) {
|
|
3491
|
-
throw new Error(`Unable to download GTFS from ${task.
|
|
3490
|
+
throw new Error(`Unable to download GTFS from ${task.url}`);
|
|
3492
3491
|
}
|
|
3493
3492
|
const buffer = await response.arrayBuffer();
|
|
3494
3493
|
await writeFile(task.path, Buffer.from(buffer));
|
|
3495
3494
|
task.log("Download successful");
|
|
3496
3495
|
};
|
|
3497
|
-
var downloadGtfsRealtimeData = async (
|
|
3498
|
-
|
|
3496
|
+
var downloadGtfsRealtimeData = async (urlAndHeaders, task) => {
|
|
3497
|
+
task.log(`Downloading GTFS-Realtime from ${urlAndHeaders.url}`);
|
|
3498
|
+
const response = await fetch(urlAndHeaders.url, {
|
|
3499
3499
|
method: "GET",
|
|
3500
3500
|
headers: {
|
|
3501
|
-
...
|
|
3501
|
+
...urlAndHeaders.headers ?? {},
|
|
3502
3502
|
"Accept-Encoding": "gzip"
|
|
3503
3503
|
},
|
|
3504
3504
|
signal: task.downloadTimeout ? AbortSignal.timeout(task.downloadTimeout) : void 0
|
|
3505
3505
|
});
|
|
3506
3506
|
if (response.status !== 200) {
|
|
3507
|
-
task.logWarning(
|
|
3507
|
+
task.logWarning(
|
|
3508
|
+
`Unable to download GTFS-Realtime from ${urlAndHeaders.url}`
|
|
3509
|
+
);
|
|
3508
3510
|
return null;
|
|
3509
3511
|
}
|
|
3510
3512
|
const buffer = await response.arrayBuffer();
|
|
@@ -3585,48 +3587,70 @@ var prepareRealtimeValue = (entity, column, task) => {
|
|
|
3585
3587
|
);
|
|
3586
3588
|
};
|
|
3587
3589
|
var updateRealtimeData = async (task) => {
|
|
3588
|
-
if (
|
|
3590
|
+
if (task.realtimeAlerts === void 0 && task.realtimeTripUpdates === void 0 && task.realtimeVehiclePositions === void 0) {
|
|
3589
3591
|
return;
|
|
3590
3592
|
}
|
|
3591
3593
|
const db = openDb({
|
|
3592
3594
|
sqlitePath: task.sqlitePath
|
|
3593
3595
|
});
|
|
3594
|
-
task.
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
|
|
3596
|
+
if (task.realtimeAlerts?.url) {
|
|
3597
|
+
const gtfsRealtimeData = await downloadGtfsRealtimeData(
|
|
3598
|
+
task.realtimeAlerts,
|
|
3599
|
+
task
|
|
3600
|
+
);
|
|
3601
|
+
if (gtfsRealtimeData?.entity) {
|
|
3602
|
+
task.log(`Download successful`);
|
|
3603
|
+
let totalLineCount = 0;
|
|
3604
|
+
for (const entity of gtfsRealtimeData.entity) {
|
|
3605
|
+
const fieldValues = serviceAlerts.schema.map(
|
|
3606
|
+
(column) => prepareRealtimeValue(entity, column, task)
|
|
3607
|
+
);
|
|
3608
|
+
try {
|
|
3609
|
+
db.prepare(
|
|
3610
|
+
`REPLACE INTO ${serviceAlerts.filenameBase} (${serviceAlerts.schema.map((column) => column.name).join(", ")}) VALUES (${fieldValues.join(", ")})`
|
|
3611
|
+
).run();
|
|
3612
|
+
} catch (error) {
|
|
3613
|
+
task.logWarning("Import error: " + error.message);
|
|
3614
|
+
}
|
|
3615
|
+
const alertTargetArray = [];
|
|
3616
|
+
for (const informedEntity of entity.alert.informedEntity) {
|
|
3617
|
+
informedEntity.parent = entity;
|
|
3618
|
+
const subValues = serviceAlertTargets.schema.map(
|
|
3619
|
+
(column) => prepareRealtimeValue(informedEntity, column, task)
|
|
3620
|
+
);
|
|
3621
|
+
alertTargetArray.push(`(${subValues.join(", ")})`);
|
|
3622
|
+
totalLineCount++;
|
|
3623
|
+
}
|
|
3624
|
+
try {
|
|
3625
|
+
db.prepare(
|
|
3626
|
+
`REPLACE INTO ${serviceAlertTargets.filenameBase} (${serviceAlertTargets.schema.map((column) => column.name).join(", ")}) VALUES ${alertTargetArray.join(", ")}`
|
|
3627
|
+
).run();
|
|
3628
|
+
} catch (error) {
|
|
3629
|
+
task.logWarning("Import error: " + error.message);
|
|
3630
|
+
}
|
|
3631
|
+
task.log(`Importing - ${totalLineCount++} entries imported\r`, true);
|
|
3628
3632
|
}
|
|
3629
|
-
|
|
3633
|
+
}
|
|
3634
|
+
}
|
|
3635
|
+
if (task.realtimeTripUpdates?.url) {
|
|
3636
|
+
const gtfsRealtimeData = await downloadGtfsRealtimeData(
|
|
3637
|
+
task.realtimeTripUpdates,
|
|
3638
|
+
task
|
|
3639
|
+
);
|
|
3640
|
+
if (gtfsRealtimeData?.entity) {
|
|
3641
|
+
task.log(`Download successful`);
|
|
3642
|
+
let totalLineCount = 0;
|
|
3643
|
+
for (const entity of gtfsRealtimeData.entity) {
|
|
3644
|
+
const fieldValues = tripUpdates.schema.map(
|
|
3645
|
+
(column) => prepareRealtimeValue(entity, column, task)
|
|
3646
|
+
);
|
|
3647
|
+
try {
|
|
3648
|
+
db.prepare(
|
|
3649
|
+
`REPLACE INTO ${tripUpdates.filenameBase} (${tripUpdates.schema.map((column) => column.name).join(", ")}) VALUES (${fieldValues.join(", ")})`
|
|
3650
|
+
).run();
|
|
3651
|
+
} catch (error) {
|
|
3652
|
+
task.logWarning("Import error: " + error.message);
|
|
3653
|
+
}
|
|
3630
3654
|
const stopTimeUpdateArray = [];
|
|
3631
3655
|
for (const stopTimeUpdate of entity.tripUpdate.stopTimeUpdate) {
|
|
3632
3656
|
stopTimeUpdate.parent = entity;
|
|
@@ -3643,26 +3667,31 @@ var updateRealtimeData = async (task) => {
|
|
|
3643
3667
|
} catch (error) {
|
|
3644
3668
|
task.logWarning("Import error: " + error.message);
|
|
3645
3669
|
}
|
|
3670
|
+
task.log(`Importing - ${totalLineCount++} entries imported\r`, true);
|
|
3646
3671
|
}
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
|
|
3656
|
-
|
|
3672
|
+
}
|
|
3673
|
+
}
|
|
3674
|
+
if (task.realtimeVehiclePositions?.url) {
|
|
3675
|
+
const gtfsRealtimeData = await downloadGtfsRealtimeData(
|
|
3676
|
+
task.realtimeVehiclePositions,
|
|
3677
|
+
task
|
|
3678
|
+
);
|
|
3679
|
+
if (gtfsRealtimeData?.entity) {
|
|
3680
|
+
task.log(`Download successful`);
|
|
3681
|
+
let totalLineCount = 0;
|
|
3682
|
+
for (const entity of gtfsRealtimeData.entity) {
|
|
3683
|
+
const fieldValues = tripUpdates.schema.map(
|
|
3684
|
+
(column) => prepareRealtimeValue(entity, column, task)
|
|
3685
|
+
);
|
|
3657
3686
|
try {
|
|
3658
3687
|
db.prepare(
|
|
3659
|
-
`REPLACE INTO ${
|
|
3688
|
+
`REPLACE INTO ${tripUpdates.filenameBase} (${tripUpdates.schema.map((column) => column.name).join(", ")}) VALUES (${fieldValues.join(", ")})`
|
|
3660
3689
|
).run();
|
|
3661
3690
|
} catch (error) {
|
|
3662
3691
|
task.logWarning("Import error: " + error.message);
|
|
3663
3692
|
}
|
|
3693
|
+
task.log(`Importing - ${totalLineCount++} entries imported\r`, true);
|
|
3664
3694
|
}
|
|
3665
|
-
task.log(`Importing - ${totalLineCount++} entries imported\r`, true);
|
|
3666
3695
|
}
|
|
3667
3696
|
}
|
|
3668
3697
|
task.log(`GTFS-Realtime data import complete`);
|
|
@@ -3974,10 +4003,11 @@ async function importGtfs(initialConfig) {
|
|
|
3974
4003
|
const tempPath = temporaryDirectory();
|
|
3975
4004
|
const task = {
|
|
3976
4005
|
exclude: agency2.exclude,
|
|
3977
|
-
|
|
4006
|
+
url: agency2.url,
|
|
3978
4007
|
headers: agency2.headers,
|
|
3979
|
-
|
|
3980
|
-
|
|
4008
|
+
realtimeAlerts: agency2.realtimeAlerts,
|
|
4009
|
+
realtimeTripUpdates: agency2.realtimeTripUpdates,
|
|
4010
|
+
realtimeVehiclePositions: agency2.realtimeVehiclePositions,
|
|
3981
4011
|
downloadDir: tempPath,
|
|
3982
4012
|
downloadTimeout: config.downloadTimeout,
|
|
3983
4013
|
gtfsRealtimeExpirationSeconds: config.gtfsRealtimeExpirationSeconds,
|
|
@@ -3992,7 +4022,7 @@ async function importGtfs(initialConfig) {
|
|
|
3992
4022
|
logError: logError2
|
|
3993
4023
|
};
|
|
3994
4024
|
try {
|
|
3995
|
-
if (task.
|
|
4025
|
+
if (task.url) {
|
|
3996
4026
|
await downloadFiles(task);
|
|
3997
4027
|
}
|
|
3998
4028
|
await readFiles(task);
|
|
@@ -4040,12 +4070,10 @@ async function updateGtfsRealtime(initialConfig) {
|
|
|
4040
4070
|
deleteExpiredRealtimeData(config);
|
|
4041
4071
|
await Promise.all(
|
|
4042
4072
|
config.agencies.map(async (agency2) => {
|
|
4043
|
-
if (agency2.realtimeUrls === void 0) {
|
|
4044
|
-
return;
|
|
4045
|
-
}
|
|
4046
4073
|
const task = {
|
|
4047
|
-
|
|
4048
|
-
|
|
4074
|
+
realtimeAlerts: agency2.realtimeAlerts,
|
|
4075
|
+
realtimeTripUpdates: agency2.realtimeTripUpdates,
|
|
4076
|
+
realtimeVehiclePositions: agency2.realtimeVehiclePositions,
|
|
4049
4077
|
downloadTimeout: config.downloadTimeout,
|
|
4050
4078
|
gtfsRealtimeExpirationSeconds: config.gtfsRealtimeExpirationSeconds,
|
|
4051
4079
|
sqlitePath: config.sqlitePath,
|
|
@@ -4068,7 +4096,7 @@ async function updateGtfsRealtime(initialConfig) {
|
|
|
4068
4096
|
} catch (error) {
|
|
4069
4097
|
if (error?.code === "SQLITE_CANTOPEN") {
|
|
4070
4098
|
logError2(
|
|
4071
|
-
`
|
|
4099
|
+
`Unable to open sqlite database "${config.sqlitePath}" defined as \`sqlitePath\` config.json. Ensure the parent directory exists or remove \`sqlitePath\` from config.json.`
|
|
4072
4100
|
);
|
|
4073
4101
|
}
|
|
4074
4102
|
throw error;
|
|
@@ -4078,7 +4106,7 @@ async function updateGtfsRealtime(initialConfig) {
|
|
|
4078
4106
|
// src/lib/export.ts
|
|
4079
4107
|
import path2 from "node:path";
|
|
4080
4108
|
import { writeFile as writeFile2 } from "node:fs/promises";
|
|
4081
|
-
import { without, compact } from "lodash-es";
|
|
4109
|
+
import { without, compact as compact2 } from "lodash-es";
|
|
4082
4110
|
import pluralize2 from "pluralize";
|
|
4083
4111
|
import { stringify } from "csv-stringify";
|
|
4084
4112
|
import sqlString3 from "sqlstring-sqlite";
|
|
@@ -4185,7 +4213,7 @@ var exportGtfs = async (initialConfig) => {
|
|
|
4185
4213
|
return `${model.filenameBase}.${model.filenameExtension}`;
|
|
4186
4214
|
}
|
|
4187
4215
|
);
|
|
4188
|
-
if (
|
|
4216
|
+
if (compact2(exportedFiles).length === 0) {
|
|
4189
4217
|
log2("No GTFS data exported. Be sure to first import data into SQLite.");
|
|
4190
4218
|
return;
|
|
4191
4219
|
}
|
|
@@ -4515,7 +4543,7 @@ function getRoutes(query = {}, fields = [], orderBy2 = [], options = {}) {
|
|
|
4515
4543
|
}
|
|
4516
4544
|
|
|
4517
4545
|
// src/lib/gtfs/shapes.ts
|
|
4518
|
-
import { compact as
|
|
4546
|
+
import { compact as compact3, omit as omit4, pick as pick2 } from "lodash-es";
|
|
4519
4547
|
import { featureCollection as featureCollection2 } from "@turf/helpers";
|
|
4520
4548
|
|
|
4521
4549
|
// src/lib/gtfs-plus/route-attributes.ts
|
|
@@ -4565,7 +4593,7 @@ function getShapesAsGeoJSON(query = {}, options = {}) {
|
|
|
4565
4593
|
const agencies = getAgencies2({}, [], [], options);
|
|
4566
4594
|
const routeQuery = pick2(query, ["route_id"]);
|
|
4567
4595
|
const routes2 = getRoutes(routeQuery, [], [], options);
|
|
4568
|
-
const features =
|
|
4596
|
+
const features = compact3(
|
|
4569
4597
|
routes2.map((route) => {
|
|
4570
4598
|
const shapeQuery = {
|
|
4571
4599
|
route_id: route.route_id,
|
|
@@ -4692,14 +4720,14 @@ function getStopsAsGeoJSON(query = {}, options = {}) {
|
|
|
4692
4720
|
const routeSubquery = "SELECT DISTINCT route_id FROM trips WHERE trip_id IN (SELECT DISTINCT trip_id FROM stop_times WHERE stop_id = ?)";
|
|
4693
4721
|
const routes2 = db.prepare(`SELECT * FROM routes WHERE route_id IN (${routeSubquery})`).all(stop.stop_id);
|
|
4694
4722
|
const stopAttributes2 = getStopAttributes({ stop_id: stop.stop_id });
|
|
4695
|
-
stop.routes = orderBy(
|
|
4696
|
-
routes2,
|
|
4697
|
-
(route) => route?.route_short_name ? Number.parseInt(route.route_short_name, 10) : 0
|
|
4698
|
-
);
|
|
4699
|
-
stop.agency_name = agencies[0].agency_name;
|
|
4700
4723
|
return {
|
|
4701
4724
|
...stop,
|
|
4702
|
-
...stopAttributes2?.[0] || []
|
|
4725
|
+
...stopAttributes2?.[0] || [],
|
|
4726
|
+
routes: orderBy(
|
|
4727
|
+
routes2,
|
|
4728
|
+
(route) => route?.route_short_name ? Number.parseInt(route.route_short_name, 10) : 0
|
|
4729
|
+
),
|
|
4730
|
+
agency_name: agencies[0].agency_name
|
|
4703
4731
|
};
|
|
4704
4732
|
});
|
|
4705
4733
|
const filteredStops = preparedStops.filter((stop) => stop.routes.length > 0);
|