gtfs 4.11.0 → 4.11.2
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/CHANGELOG.md +22 -0
- package/README.md +12 -19
- package/lib/export.js +42 -32
- package/lib/geojson-utils.js +9 -0
- package/lib/gtfs/locations.js +32 -0
- package/lib/gtfs.js +4 -0
- package/lib/import.js +55 -27
- package/models/gtfs/agency.js +9 -8
- package/models/gtfs/areas.js +3 -2
- package/models/gtfs/attributions.js +9 -8
- package/models/gtfs/booking-rules.js +11 -10
- package/models/gtfs/calendar-dates.js +3 -2
- package/models/gtfs/calendar.js +2 -1
- package/models/gtfs/fare-attributes.js +4 -3
- package/models/gtfs/fare-leg-rules.js +8 -7
- package/models/gtfs/fare-media.js +3 -2
- package/models/gtfs/fare-products.js +5 -4
- package/models/gtfs/fare-rules.js +6 -5
- package/models/gtfs/fare-transfer-rules.js +5 -4
- package/models/gtfs/feed-info.js +8 -7
- package/models/gtfs/frequencies.js +4 -3
- package/models/gtfs/levels.js +3 -2
- package/models/gtfs/location-group-stops.js +3 -2
- package/models/gtfs/location-groups.js +3 -2
- package/models/gtfs/locations.js +12 -0
- package/models/gtfs/networks.js +3 -2
- package/models/gtfs/pathways.js +6 -5
- package/models/gtfs/route-networks.js +3 -2
- package/models/gtfs/routes.js +10 -9
- package/models/gtfs/shapes.js +2 -1
- package/models/gtfs/stop-areas.js +3 -2
- package/models/gtfs/stop-times.js +11 -10
- package/models/gtfs/stops.js +12 -11
- package/models/gtfs/timeframes.js +5 -4
- package/models/gtfs/transfers.js +7 -6
- package/models/gtfs/translations.js +8 -7
- package/models/gtfs/trips.js +8 -7
- package/models/gtfs-plus/calendar-attributes.js +3 -2
- package/models/gtfs-plus/directions.js +3 -2
- package/models/gtfs-plus/route-attributes.js +2 -1
- package/models/gtfs-plus/stop-attributes.js +5 -4
- package/models/gtfs-realtime/service-alert-targets.js +3 -3
- package/models/gtfs-realtime/service-alerts.js +5 -5
- package/models/gtfs-realtime/stop-time-updates.js +7 -7
- package/models/gtfs-realtime/trip-updates.js +8 -8
- package/models/gtfs-realtime/vehicle-positions.js +12 -12
- package/models/gtfs-ride/board-alight.js +5 -4
- package/models/gtfs-ride/ride-feed-info.js +3 -2
- package/models/gtfs-ride/rider-trip.js +9 -8
- package/models/gtfs-ride/ridership.js +8 -7
- package/models/gtfs-ride/trip-capacity.js +4 -3
- package/models/models.js +2 -0
- package/models/non-standard/timetable-notes-references.js +6 -5
- package/models/non-standard/timetable-notes.js +4 -3
- package/models/non-standard/timetable-pages.js +4 -3
- package/models/non-standard/timetable-stop-order.js +3 -2
- package/models/non-standard/timetables.js +10 -9
- package/models/non-standard/trips-dated-vehicle-journey.js +4 -3
- package/models/ods/deadhead-times.js +6 -5
- package/models/ods/deadheads.js +9 -8
- package/models/ods/ops-locations.js +5 -4
- package/models/ods/run-events.js +7 -6
- package/models/ods/runs-pieces.js +5 -4
- package/package.json +4 -5
- package/test/mocha/export-gtfs.js +5 -2
- package/test/mocha/get-locations.js +71 -0
- package/test/mocha/import-gtfs.js +4 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,28 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [4.11.2] - 2024-06-13
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Support for [locations.geojson](https://gtfs.org/schedule/reference/#locationsgeojson)
|
|
13
|
+
|
|
14
|
+
### Updated
|
|
15
|
+
|
|
16
|
+
- Update sqlite type for all models
|
|
17
|
+
- Dependency updates
|
|
18
|
+
|
|
19
|
+
## [4.11.1] - 2024-06-05
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
|
|
23
|
+
- Don't throw error on GTFS-Realtime import HTTP error
|
|
24
|
+
|
|
25
|
+
### Updated
|
|
26
|
+
|
|
27
|
+
- Remove recursive-copy module
|
|
28
|
+
- Dependency updates
|
|
29
|
+
|
|
8
30
|
## [4.11.0] - 2024-06-01
|
|
9
31
|
|
|
10
32
|
### Added
|
package/README.md
CHANGED
|
@@ -29,27 +29,9 @@ You can use it as a [command-line tool](#command-line-examples) or as a [node.js
|
|
|
29
29
|
|
|
30
30
|
This library has four parts: the [GTFS import script](#gtfs-import-script), [GTFS export script](#gtfs-export-script) and [GTFS-Realtime update script](#gtfsrealtime-update-script) and the [query methods](#query-methods)
|
|
31
31
|
|
|
32
|
-
## Breaking changes in Version 4
|
|
33
|
-
|
|
34
|
-
Version 4 of node-gtfs switched to using the better-sqlite3 library. This allowed all query methods to become synchronous and speeds up import and export.
|
|
35
|
-
|
|
36
|
-
- All query methods are now synchronous.
|
|
37
|
-
|
|
38
|
-
```js
|
|
39
|
-
// Version 3
|
|
40
|
-
const routes = await getRoutes();
|
|
41
|
-
|
|
42
|
-
// Version 4
|
|
43
|
-
const routes = getRoutes();
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
- `runRawQuery` has been removed. Use [Raw SQLite Query](#raw-sqlite-query) instead.
|
|
47
|
-
- `execRawQuery` has been removed. Use [Raw SQLite Query](#raw-sqlite-query) instead.
|
|
48
|
-
- `getDb` has been removed. Use `openDb` instead.
|
|
49
|
-
|
|
50
32
|
## Installation
|
|
51
33
|
|
|
52
|
-
To use this library as a command-line utility, install it globally [npm](https://npmjs.org):
|
|
34
|
+
To use this library as a command-line utility, install it globally with [npm](https://npmjs.org):
|
|
53
35
|
|
|
54
36
|
npm install gtfs -g
|
|
55
37
|
|
|
@@ -1224,6 +1206,17 @@ const locationGroups = getLocationGroupStops({
|
|
|
1224
1206
|
});
|
|
1225
1207
|
```
|
|
1226
1208
|
|
|
1209
|
+
#### getLocations(query, fields, sortBy, options)
|
|
1210
|
+
|
|
1211
|
+
Returns an array of locations that match query parameters. Each location is text that can be parsed into a geojson object. [Details on locations.geojson](https://gtfs.org/schedule/reference/#locationsgeojson)
|
|
1212
|
+
|
|
1213
|
+
```js
|
|
1214
|
+
import { getLocations } from 'gtfs';
|
|
1215
|
+
|
|
1216
|
+
// Get all locations
|
|
1217
|
+
const locations = getLocations();
|
|
1218
|
+
```
|
|
1219
|
+
|
|
1227
1220
|
#### getPathways(query, fields, sortBy, options)
|
|
1228
1221
|
|
|
1229
1222
|
Returns an array of pathways that match query parameters. [Details on pathways.txt](https://gtfs.org/schedule/reference/#pathwaystxt)
|
package/lib/export.js
CHANGED
|
@@ -66,52 +66,62 @@ const exportGtfs = async (initialConfig) => {
|
|
|
66
66
|
|
|
67
67
|
// Loop through each GTFS file
|
|
68
68
|
const exportedFiles = await mapSeries(models, async (model) => {
|
|
69
|
-
const
|
|
69
|
+
const filePath = path.join(
|
|
70
|
+
exportPath,
|
|
71
|
+
`${model.filenameBase}.${model.filenameExtension}`,
|
|
72
|
+
);
|
|
70
73
|
const tableName = sqlString.escapeId(model.filenameBase);
|
|
71
74
|
const lines = db.prepare(`SELECT * FROM ${tableName};`).all();
|
|
72
75
|
|
|
73
76
|
if (!lines || lines.length === 0) {
|
|
74
77
|
if (!model.nonstandard) {
|
|
75
|
-
log(
|
|
78
|
+
log(
|
|
79
|
+
`Skipping (no data) - ${model.filenameBase}.${model.filenameExtension}\r`,
|
|
80
|
+
);
|
|
76
81
|
}
|
|
77
82
|
|
|
78
83
|
return;
|
|
79
84
|
}
|
|
80
85
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
86
|
+
if (model.filenameExtension === 'txt') {
|
|
87
|
+
const excludeColumns = [
|
|
88
|
+
'id',
|
|
89
|
+
'arrival_timestamp',
|
|
90
|
+
'departure_timestamp',
|
|
91
|
+
'start_timestamp',
|
|
92
|
+
'end_timestamp',
|
|
93
|
+
'service_arrival_timestamp',
|
|
94
|
+
'service_departure_timestamp',
|
|
95
|
+
'boarding_timestamp',
|
|
96
|
+
'alighting_timestamp',
|
|
97
|
+
'ridership_start_timestamp',
|
|
98
|
+
'ridership_end_timestamp',
|
|
99
|
+
];
|
|
100
|
+
|
|
101
|
+
// If no routes have values for agency_id, add it to the excludeColumns list
|
|
102
|
+
if (model.filenameBase === 'routes') {
|
|
103
|
+
const routesWithAgencyId = db
|
|
104
|
+
.prepare('SELECT agency_id FROM routes WHERE agency_id IS NOT NULL;')
|
|
105
|
+
.all();
|
|
106
|
+
if (!routesWithAgencyId || routesWithAgencyId.length === 0) {
|
|
107
|
+
excludeColumns.push('agency_id');
|
|
108
|
+
}
|
|
102
109
|
}
|
|
103
|
-
}
|
|
104
110
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
+
const columns = without(
|
|
112
|
+
model.schema.map((column) => column.name),
|
|
113
|
+
...excludeColumns,
|
|
114
|
+
);
|
|
115
|
+
const fileText = await stringify(lines, { columns, header: true });
|
|
116
|
+
await writeFile(filePath, fileText);
|
|
117
|
+
} else if (model.filenameExtension === 'geojson') {
|
|
118
|
+
const fileText = lines?.[0].geojson ?? '';
|
|
119
|
+
await writeFile(filePath, fileText);
|
|
120
|
+
}
|
|
111
121
|
|
|
112
|
-
log(`Exporting - ${model.filenameBase}.
|
|
122
|
+
log(`Exporting - ${model.filenameBase}.${model.filenameExtension}\r`);
|
|
113
123
|
|
|
114
|
-
return `${model.filenameBase}.
|
|
124
|
+
return `${model.filenameBase}.${model.filenameExtension}`;
|
|
115
125
|
});
|
|
116
126
|
|
|
117
127
|
if (compact(exportedFiles).length === 0) {
|
package/lib/geojson-utils.js
CHANGED
|
@@ -9,6 +9,15 @@ import {
|
|
|
9
9
|
} from 'lodash-es';
|
|
10
10
|
import { feature, featureCollection } from '@turf/helpers';
|
|
11
11
|
|
|
12
|
+
export function isValidJSON(string) {
|
|
13
|
+
try {
|
|
14
|
+
JSON.parse(string);
|
|
15
|
+
} catch (error) {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
|
|
12
21
|
function isValidLineString(lineString) {
|
|
13
22
|
if (!lineString) {
|
|
14
23
|
return false;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import sqlString from 'sqlstring-sqlite';
|
|
2
|
+
|
|
3
|
+
import { openDb } from '../db.js';
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
formatOrderByClause,
|
|
7
|
+
formatSelectClause,
|
|
8
|
+
formatWhereClauses,
|
|
9
|
+
} from '../utils.js';
|
|
10
|
+
import locations from '../../models/gtfs/locations.js';
|
|
11
|
+
|
|
12
|
+
/*
|
|
13
|
+
* Returns an array of all locations that match the query parameters.
|
|
14
|
+
*/
|
|
15
|
+
export function getLocations(
|
|
16
|
+
query = {},
|
|
17
|
+
fields = [],
|
|
18
|
+
orderBy = [],
|
|
19
|
+
options = {},
|
|
20
|
+
) {
|
|
21
|
+
const db = options.db ?? openDb();
|
|
22
|
+
const tableName = sqlString.escapeId(locations.filenameBase);
|
|
23
|
+
const selectClause = formatSelectClause(fields);
|
|
24
|
+
const whereClause = formatWhereClauses(query);
|
|
25
|
+
const orderByClause = formatOrderByClause(orderBy);
|
|
26
|
+
|
|
27
|
+
return db
|
|
28
|
+
.prepare(
|
|
29
|
+
`${selectClause} FROM ${tableName} ${whereClause} ${orderByClause};`,
|
|
30
|
+
)
|
|
31
|
+
.all();
|
|
32
|
+
}
|
package/lib/gtfs.js
CHANGED
|
@@ -22,6 +22,7 @@ import { getFrequencies } from './gtfs/frequencies.js';
|
|
|
22
22
|
import { getLevels } from './gtfs/levels.js';
|
|
23
23
|
import { getLocationGroups } from './gtfs/location-groups.js';
|
|
24
24
|
import { getLocationGroupStops } from './gtfs/location-group-stops.js';
|
|
25
|
+
import { getLocations } from './gtfs/locations.js';
|
|
25
26
|
import { getNetworks } from './gtfs/networks.js';
|
|
26
27
|
import { getPathways } from './gtfs/pathways.js';
|
|
27
28
|
import { getRouteNetworks } from './gtfs/route-networks.js';
|
|
@@ -131,6 +132,9 @@ export { _getLocationGroups as getLocationGroups };
|
|
|
131
132
|
const _getLocationGroupStops = getLocationGroupStops;
|
|
132
133
|
export { _getLocationGroupStops as getLocationGroupStops };
|
|
133
134
|
|
|
135
|
+
const _getLocations = getLocations;
|
|
136
|
+
export { _getLocations as getLocations };
|
|
137
|
+
|
|
134
138
|
const _getNetworks = getNetworks;
|
|
135
139
|
export { _getNetworks as getNetworks };
|
|
136
140
|
|
package/lib/import.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import { createReadStream, existsSync, lstatSync } from 'node:fs';
|
|
3
|
-
import { readdir, rename, writeFile } from 'node:fs/promises';
|
|
4
|
-
import copy from 'recursive-copy';
|
|
3
|
+
import { cp, readdir, rename, readFile, writeFile } from 'node:fs/promises';
|
|
5
4
|
import fetch from 'node-fetch';
|
|
6
5
|
import { parse } from 'csv-parse';
|
|
7
6
|
import pluralize from 'pluralize';
|
|
@@ -15,6 +14,7 @@ import sqlString from 'sqlstring-sqlite';
|
|
|
15
14
|
import models from '../models/models.js';
|
|
16
15
|
import { openDb } from './db.js';
|
|
17
16
|
import { unzip } from './file-utils.js';
|
|
17
|
+
import { isValidJSON } from './geojson-utils.js';
|
|
18
18
|
import {
|
|
19
19
|
log as _log,
|
|
20
20
|
logError as _logError,
|
|
@@ -42,7 +42,7 @@ const downloadFiles = async (task) => {
|
|
|
42
42
|
});
|
|
43
43
|
|
|
44
44
|
if (response.status !== 200) {
|
|
45
|
-
throw new Error(
|
|
45
|
+
throw new Error(`Unable to download GTFS from ${task.agency_url}`);
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
const buffer = await response.arrayBuffer();
|
|
@@ -51,14 +51,19 @@ const downloadFiles = async (task) => {
|
|
|
51
51
|
task.log('Download successful');
|
|
52
52
|
};
|
|
53
53
|
|
|
54
|
-
const downloadGtfsRealtimeData = async (url,
|
|
54
|
+
const downloadGtfsRealtimeData = async (url, task) => {
|
|
55
55
|
const response = await fetch(url, {
|
|
56
56
|
method: 'GET',
|
|
57
|
-
headers: {
|
|
57
|
+
headers: {
|
|
58
|
+
...{},
|
|
59
|
+
...task.realtime_headers,
|
|
60
|
+
...{ 'Accept-Encoding': 'gzip' },
|
|
61
|
+
},
|
|
58
62
|
});
|
|
59
63
|
|
|
60
64
|
if (response.status !== 200) {
|
|
61
|
-
|
|
65
|
+
task.warn(`Unable to download GTFS-Realtime from ${url}`);
|
|
66
|
+
return null;
|
|
62
67
|
}
|
|
63
68
|
|
|
64
69
|
const buffer = await response.arrayBuffer();
|
|
@@ -190,17 +195,14 @@ const updateRealtimeData = async (task) => {
|
|
|
190
195
|
for (const realtimeUrl of task.realtime_urls) {
|
|
191
196
|
task.log(`Downloading GTFS-Realtime from ${realtimeUrl}`);
|
|
192
197
|
// eslint-disable-next-line no-await-in-loop
|
|
193
|
-
const gtfsRealtimeData = await downloadGtfsRealtimeData(
|
|
194
|
-
realtimeUrl,
|
|
195
|
-
task.realtime_headers,
|
|
196
|
-
);
|
|
197
|
-
|
|
198
|
-
task.log(`Download successful`);
|
|
198
|
+
const gtfsRealtimeData = await downloadGtfsRealtimeData(realtimeUrl, task);
|
|
199
199
|
|
|
200
|
-
if (!gtfsRealtimeData
|
|
200
|
+
if (!gtfsRealtimeData?.entity) {
|
|
201
201
|
continue;
|
|
202
202
|
}
|
|
203
203
|
|
|
204
|
+
task.log(`Download successful`);
|
|
205
|
+
|
|
204
206
|
let totalLineCount = 0;
|
|
205
207
|
|
|
206
208
|
for (const entity of gtfsRealtimeData.entity) {
|
|
@@ -353,7 +355,7 @@ const readFiles = async (task) => {
|
|
|
353
355
|
} else {
|
|
354
356
|
// Local file is unzipped, just copy it from there.
|
|
355
357
|
try {
|
|
356
|
-
await
|
|
358
|
+
await cp(gtfsPath, task.downloadDir, { recursive: true });
|
|
357
359
|
} catch {
|
|
358
360
|
throw new Error(
|
|
359
361
|
`Unable to load files from path \`${gtfsPath}\` defined in configuration. Verify that path exists and contains GTFS files.`,
|
|
@@ -444,7 +446,7 @@ const formatLine = (line, model, totalLineCount) => {
|
|
|
444
446
|
formattedLine[columnSchema.name] === null
|
|
445
447
|
) {
|
|
446
448
|
throw new Error(
|
|
447
|
-
`Missing required value in ${model.filenameBase}.
|
|
449
|
+
`Missing required value in ${model.filenameBase}.${model.filenameExtension} for ${columnSchema.name} on line ${lineNumber}.`,
|
|
448
450
|
);
|
|
449
451
|
}
|
|
450
452
|
|
|
@@ -454,7 +456,7 @@ const formatLine = (line, model, totalLineCount) => {
|
|
|
454
456
|
formattedLine[columnSchema.name] < columnSchema.min
|
|
455
457
|
) {
|
|
456
458
|
throw new Error(
|
|
457
|
-
`Invalid value in ${model.filenameBase}.
|
|
459
|
+
`Invalid value in ${model.filenameBase}.${model.filenameExtension} for ${columnSchema.name} on line ${lineNumber}: below minimum value of ${columnSchema.min}.`,
|
|
458
460
|
);
|
|
459
461
|
}
|
|
460
462
|
|
|
@@ -464,7 +466,7 @@ const formatLine = (line, model, totalLineCount) => {
|
|
|
464
466
|
formattedLine[columnSchema.name] > columnSchema.max
|
|
465
467
|
) {
|
|
466
468
|
throw new Error(
|
|
467
|
-
`Invalid value in ${model.filenameBase}.
|
|
469
|
+
`Invalid value in ${model.filenameBase}.${model.filenameExtension} for ${columnSchema.name} on line ${lineNumber}: above maximum value of ${columnSchema.max}.`,
|
|
468
470
|
);
|
|
469
471
|
}
|
|
470
472
|
}
|
|
@@ -538,12 +540,12 @@ const importLines = (task, lines, model, totalLineCount) => {
|
|
|
538
540
|
if (error.code === 'SQLITE_CONSTRAINT_PRIMARYKEY') {
|
|
539
541
|
const primaryColumns = model.schema.filter((column) => column.primary);
|
|
540
542
|
task.warn(
|
|
541
|
-
`Duplicate values for primary key (${primaryColumns.map((column) => column.name).join(', ')}) found in ${model.filenameBase}.
|
|
543
|
+
`Duplicate values for primary key (${primaryColumns.map((column) => column.name).join(', ')}) found in ${model.filenameBase}.${model.filenameExtension}. Set the \`ignoreDuplicates\` option to true in config.json to ignore this error`,
|
|
542
544
|
);
|
|
543
545
|
}
|
|
544
546
|
|
|
545
547
|
task.warn(
|
|
546
|
-
`Check ${model.filenameBase}.
|
|
548
|
+
`Check ${model.filenameBase}.${model.filenameExtension} for invalid data between lines ${
|
|
547
549
|
totalLineCount - linesToImportCount
|
|
548
550
|
} and ${totalLineCount}.`,
|
|
549
551
|
);
|
|
@@ -551,7 +553,7 @@ const importLines = (task, lines, model, totalLineCount) => {
|
|
|
551
553
|
}
|
|
552
554
|
|
|
553
555
|
task.log(
|
|
554
|
-
`Importing - ${model.filenameBase}.
|
|
556
|
+
`Importing - ${model.filenameBase}.${model.filenameExtension} - ${totalLineCount} lines imported\r`,
|
|
555
557
|
true,
|
|
556
558
|
);
|
|
557
559
|
};
|
|
@@ -561,10 +563,16 @@ const importFiles = (task) =>
|
|
|
561
563
|
models,
|
|
562
564
|
(model) =>
|
|
563
565
|
new Promise((resolve, reject) => {
|
|
566
|
+
const lines = [];
|
|
567
|
+
let totalLineCount = 0;
|
|
568
|
+
const maxInsertVariables = 32_000;
|
|
569
|
+
|
|
564
570
|
// Loop through each GTFS file
|
|
565
571
|
// Filter out excluded files from config
|
|
566
572
|
if (task.exclude && task.exclude.includes(model.filenameBase)) {
|
|
567
|
-
task.log(
|
|
573
|
+
task.log(
|
|
574
|
+
`Skipping - ${model.filenameBase}.${model.filenameExtension}\r`,
|
|
575
|
+
);
|
|
568
576
|
resolve();
|
|
569
577
|
return;
|
|
570
578
|
}
|
|
@@ -577,23 +585,43 @@ const importFiles = (task) =>
|
|
|
577
585
|
|
|
578
586
|
const filepath = path.join(
|
|
579
587
|
task.downloadDir,
|
|
580
|
-
`${model.filenameBase}.
|
|
588
|
+
`${model.filenameBase}.${model.filenameExtension}`,
|
|
581
589
|
);
|
|
582
590
|
|
|
583
591
|
if (!existsSync(filepath)) {
|
|
592
|
+
// Log only missing standard GTFS files
|
|
584
593
|
if (!model.nonstandard) {
|
|
585
|
-
task.log(
|
|
594
|
+
task.log(
|
|
595
|
+
`Importing - ${model.filenameBase}.${model.filenameExtension} - No file found\r`,
|
|
596
|
+
);
|
|
586
597
|
}
|
|
587
598
|
|
|
588
599
|
resolve();
|
|
589
600
|
return;
|
|
590
601
|
}
|
|
591
602
|
|
|
592
|
-
task.log(
|
|
603
|
+
task.log(
|
|
604
|
+
`Importing - ${model.filenameBase}.${model.filenameExtension}\r`,
|
|
605
|
+
);
|
|
606
|
+
|
|
607
|
+
// Handle geojson files
|
|
608
|
+
if (model.filenameExtension === 'geojson') {
|
|
609
|
+
readFile(filepath, 'utf8')
|
|
610
|
+
.then((data) => {
|
|
611
|
+
if (isValidJSON(data) === false) {
|
|
612
|
+
reject(
|
|
613
|
+
new Error(
|
|
614
|
+
`Invalid JSON in ${model.filenameBase}.${model.filenameExtension}`,
|
|
615
|
+
),
|
|
616
|
+
);
|
|
617
|
+
}
|
|
618
|
+
const line = formatLine({ geojson: data }, model, totalLineCount);
|
|
619
|
+
importLines(task, [line], model, totalLineCount);
|
|
620
|
+
resolve();
|
|
621
|
+
})
|
|
622
|
+
.catch(reject);
|
|
623
|
+
}
|
|
593
624
|
|
|
594
|
-
const lines = [];
|
|
595
|
-
let totalLineCount = 0;
|
|
596
|
-
const maxInsertVariables = 32_000;
|
|
597
625
|
const parser = parse({
|
|
598
626
|
columns: true,
|
|
599
627
|
relax_quotes: true,
|
package/models/gtfs/agency.js
CHANGED
|
@@ -1,45 +1,46 @@
|
|
|
1
1
|
const model = {
|
|
2
2
|
filenameBase: 'agency',
|
|
3
|
+
filenameExtension: 'txt',
|
|
3
4
|
schema: [
|
|
4
5
|
{
|
|
5
6
|
name: 'agency_id',
|
|
6
|
-
type: '
|
|
7
|
+
type: 'text',
|
|
7
8
|
primary: true,
|
|
8
9
|
prefix: true,
|
|
9
10
|
},
|
|
10
11
|
{
|
|
11
12
|
name: 'agency_name',
|
|
12
|
-
type: '
|
|
13
|
+
type: 'text',
|
|
13
14
|
required: true,
|
|
14
15
|
nocase: true,
|
|
15
16
|
},
|
|
16
17
|
{
|
|
17
18
|
name: 'agency_url',
|
|
18
|
-
type: '
|
|
19
|
+
type: 'text',
|
|
19
20
|
required: true,
|
|
20
21
|
},
|
|
21
22
|
{
|
|
22
23
|
name: 'agency_timezone',
|
|
23
|
-
type: '
|
|
24
|
+
type: 'text',
|
|
24
25
|
required: true,
|
|
25
26
|
},
|
|
26
27
|
{
|
|
27
28
|
name: 'agency_lang',
|
|
28
|
-
type: '
|
|
29
|
+
type: 'text',
|
|
29
30
|
nocase: true,
|
|
30
31
|
},
|
|
31
32
|
{
|
|
32
33
|
name: 'agency_phone',
|
|
33
|
-
type: '
|
|
34
|
+
type: 'text',
|
|
34
35
|
nocase: true,
|
|
35
36
|
},
|
|
36
37
|
{
|
|
37
38
|
name: 'agency_fare_url',
|
|
38
|
-
type: '
|
|
39
|
+
type: 'text',
|
|
39
40
|
},
|
|
40
41
|
{
|
|
41
42
|
name: 'agency_email',
|
|
42
|
-
type: '
|
|
43
|
+
type: 'text',
|
|
43
44
|
nocase: true,
|
|
44
45
|
},
|
|
45
46
|
],
|
package/models/gtfs/areas.js
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
const model = {
|
|
2
2
|
filenameBase: 'areas',
|
|
3
|
+
filenameExtension: 'txt',
|
|
3
4
|
schema: [
|
|
4
5
|
{
|
|
5
6
|
name: 'area_id',
|
|
6
|
-
type: '
|
|
7
|
+
type: 'text',
|
|
7
8
|
required: true,
|
|
8
9
|
primary: true,
|
|
9
10
|
prefix: true,
|
|
10
11
|
},
|
|
11
12
|
{
|
|
12
13
|
name: 'area_name',
|
|
13
|
-
type: '
|
|
14
|
+
type: 'text',
|
|
14
15
|
},
|
|
15
16
|
],
|
|
16
17
|
};
|
|
@@ -1,31 +1,32 @@
|
|
|
1
1
|
const model = {
|
|
2
2
|
filenameBase: 'attributions',
|
|
3
|
+
filenameExtension: 'txt',
|
|
3
4
|
schema: [
|
|
4
5
|
{
|
|
5
6
|
name: 'attribution_id',
|
|
6
|
-
type: '
|
|
7
|
+
type: 'text',
|
|
7
8
|
primary: true,
|
|
8
9
|
required: true,
|
|
9
10
|
prefix: true,
|
|
10
11
|
},
|
|
11
12
|
{
|
|
12
13
|
name: 'agency_id',
|
|
13
|
-
type: '
|
|
14
|
+
type: 'text',
|
|
14
15
|
prefix: true,
|
|
15
16
|
},
|
|
16
17
|
{
|
|
17
18
|
name: 'route_id',
|
|
18
|
-
type: '
|
|
19
|
+
type: 'text',
|
|
19
20
|
prefix: true,
|
|
20
21
|
},
|
|
21
22
|
{
|
|
22
23
|
name: 'trip_id',
|
|
23
|
-
type: '
|
|
24
|
+
type: 'text',
|
|
24
25
|
prefix: true,
|
|
25
26
|
},
|
|
26
27
|
{
|
|
27
28
|
name: 'organization_name',
|
|
28
|
-
type: '
|
|
29
|
+
type: 'text',
|
|
29
30
|
required: true,
|
|
30
31
|
nocase: true,
|
|
31
32
|
},
|
|
@@ -49,16 +50,16 @@ const model = {
|
|
|
49
50
|
},
|
|
50
51
|
{
|
|
51
52
|
name: 'attribution_url',
|
|
52
|
-
type: '
|
|
53
|
+
type: 'text',
|
|
53
54
|
},
|
|
54
55
|
{
|
|
55
56
|
name: 'attribution_email',
|
|
56
|
-
type: '
|
|
57
|
+
type: 'text',
|
|
57
58
|
nocase: true,
|
|
58
59
|
},
|
|
59
60
|
{
|
|
60
61
|
name: 'attribution_phone',
|
|
61
|
-
type: '
|
|
62
|
+
type: 'text',
|
|
62
63
|
nocase: true,
|
|
63
64
|
},
|
|
64
65
|
],
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
const model = {
|
|
2
2
|
filenameBase: 'booking_rules',
|
|
3
|
+
filenameExtension: 'txt',
|
|
3
4
|
schema: [
|
|
4
5
|
{
|
|
5
6
|
name: 'booking_rule_id',
|
|
6
|
-
type: '
|
|
7
|
+
type: 'text',
|
|
7
8
|
primary: true,
|
|
8
9
|
prefix: true,
|
|
9
10
|
},
|
|
@@ -31,7 +32,7 @@ const model = {
|
|
|
31
32
|
},
|
|
32
33
|
{
|
|
33
34
|
name: 'prior_notice_last_time',
|
|
34
|
-
type: '
|
|
35
|
+
type: 'text',
|
|
35
36
|
},
|
|
36
37
|
{
|
|
37
38
|
name: 'prior_notice_last_timestamp',
|
|
@@ -45,7 +46,7 @@ const model = {
|
|
|
45
46
|
},
|
|
46
47
|
{
|
|
47
48
|
name: 'prior_notice_start_time',
|
|
48
|
-
type: '
|
|
49
|
+
type: 'text',
|
|
49
50
|
},
|
|
50
51
|
{
|
|
51
52
|
name: 'prior_notice_start_timestamp',
|
|
@@ -54,36 +55,36 @@ const model = {
|
|
|
54
55
|
},
|
|
55
56
|
{
|
|
56
57
|
name: 'prior_notice_service_id',
|
|
57
|
-
type: '
|
|
58
|
+
type: 'text',
|
|
58
59
|
prefix: true,
|
|
59
60
|
},
|
|
60
61
|
{
|
|
61
62
|
name: 'message',
|
|
62
|
-
type: '
|
|
63
|
+
type: 'text',
|
|
63
64
|
nocase: true,
|
|
64
65
|
},
|
|
65
66
|
{
|
|
66
67
|
name: 'pickup_message',
|
|
67
|
-
type: '
|
|
68
|
+
type: 'text',
|
|
68
69
|
nocase: true,
|
|
69
70
|
},
|
|
70
71
|
{
|
|
71
72
|
name: 'drop_off_message',
|
|
72
|
-
type: '
|
|
73
|
+
type: 'text',
|
|
73
74
|
nocase: true,
|
|
74
75
|
},
|
|
75
76
|
{
|
|
76
77
|
name: 'phone_number',
|
|
77
|
-
type: '
|
|
78
|
+
type: 'text',
|
|
78
79
|
nocase: true,
|
|
79
80
|
},
|
|
80
81
|
{
|
|
81
82
|
name: 'info_url',
|
|
82
|
-
type: '
|
|
83
|
+
type: 'text',
|
|
83
84
|
},
|
|
84
85
|
{
|
|
85
86
|
name: 'booking_url',
|
|
86
|
-
type: '
|
|
87
|
+
type: 'text',
|
|
87
88
|
},
|
|
88
89
|
],
|
|
89
90
|
};
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
const model = {
|
|
2
2
|
filenameBase: 'calendar_dates',
|
|
3
|
+
filenameExtension: 'txt',
|
|
3
4
|
schema: [
|
|
4
5
|
{
|
|
5
6
|
name: 'service_id',
|
|
6
|
-
type: '
|
|
7
|
+
type: 'text',
|
|
7
8
|
required: true,
|
|
8
9
|
primary: true,
|
|
9
10
|
prefix: true,
|
|
@@ -24,7 +25,7 @@ const model = {
|
|
|
24
25
|
},
|
|
25
26
|
{
|
|
26
27
|
name: 'holiday_name',
|
|
27
|
-
type: '
|
|
28
|
+
type: 'text',
|
|
28
29
|
nocase: true,
|
|
29
30
|
},
|
|
30
31
|
],
|