gtfs 3.3.0 → 3.5.0

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.
@@ -0,0 +1,50 @@
1
+ const model = {
2
+ filenameBase: 'trip_updates',
3
+ extension: 'gtfs-realtime',
4
+ schema: [
5
+ {
6
+ name: 'update_id',
7
+ type: 'varchar(255)',
8
+ required: true,
9
+ primary: true,
10
+ index: true,
11
+ source: 'id',
12
+ },
13
+ {
14
+ name: 'vehicle_id',
15
+ type: 'varchar(255)',
16
+ index: true,
17
+ source: 'tripUpdate.vehicle.id',
18
+ default: null,
19
+ },
20
+ {
21
+ name: 'trip_id',
22
+ type: 'varchar(255)',
23
+ index: true,
24
+ source: 'tripUpdate.trip.tripId',
25
+ default: null,
26
+ },
27
+ {
28
+ name: 'start_date',
29
+ type: 'varchar(255)',
30
+ source: 'tripUpdate.trip.startDate',
31
+ default: null,
32
+ },
33
+ {
34
+ name: 'timestamp',
35
+ type: 'varchar(255)',
36
+ source: 'tripUpdate.timestamp',
37
+ default: null,
38
+ },
39
+ {
40
+ name: 'isUpdated',
41
+ type: 'integer',
42
+ required: true,
43
+ min: 0,
44
+ max: 1,
45
+ default: 1,
46
+ },
47
+ ],
48
+ };
49
+
50
+ export default model;
@@ -0,0 +1,73 @@
1
+ const model = {
2
+ filenameBase: 'vehicle_positions',
3
+ extension: 'gtfs-realtime',
4
+ schema: [
5
+ {
6
+ name: 'update_id',
7
+ type: 'varchar(255)',
8
+ required: true,
9
+ primary: true,
10
+ index: true,
11
+ source: 'id',
12
+ },
13
+ {
14
+ name: 'bearing',
15
+ type: 'real',
16
+ source: 'vehicle.position.bearing',
17
+ default: null,
18
+ },
19
+ {
20
+ name: 'latitude',
21
+ type: 'real',
22
+ min: -90,
23
+ max: 90,
24
+ source: 'vehicle.position.latitude',
25
+ default: null,
26
+ },
27
+ {
28
+ name: 'longitude',
29
+ type: 'real',
30
+ source: 'vehicle.position.longitude',
31
+ min: -180,
32
+ max: 180,
33
+ default: null,
34
+ },
35
+ {
36
+ name: 'speed',
37
+ type: 'real',
38
+ min: 0,
39
+ source: 'vehicle.position.speed',
40
+ default: null,
41
+ },
42
+ {
43
+ name: 'trip_id',
44
+ type: 'varchar(255)',
45
+ index: true,
46
+ source: 'vehicle.trip.tripId',
47
+ default: null,
48
+ },
49
+ {
50
+ name: 'vehicle_id',
51
+ type: 'varchar(255)',
52
+ index: true,
53
+ source: 'vehicle.vehicle.id',
54
+ default: null,
55
+ },
56
+ {
57
+ name: 'timestamp',
58
+ type: 'varchar(255)',
59
+ source: 'vehicle.timestamp',
60
+ default: null,
61
+ },
62
+ {
63
+ name: 'isUpdated',
64
+ type: 'integer',
65
+ required: true,
66
+ min: 0,
67
+ max: 1,
68
+ default: 1,
69
+ },
70
+ ],
71
+ };
72
+
73
+ export default model;
package/models/models.js CHANGED
@@ -23,6 +23,7 @@ import timetablePages from '../models/non-standard/timetable-pages.js';
23
23
  import timetableStopOrder from '../models/non-standard/timetable-stop-order.js';
24
24
  import timetableNotes from '../models/non-standard/timetable-notes.js';
25
25
  import timetableNotesReferences from '../models/non-standard/timetable-notes-references.js';
26
+ import tripsDatedVehicleJourney from '../models/non-standard/trips-dated-vehicle-journey.js';
26
27
 
27
28
  import boardAlight from '../models/gtfs-ride/board-alight.js';
28
29
  import riderTrip from '../models/gtfs-ride/rider-trip.js';
@@ -30,6 +31,12 @@ import ridership from '../models/gtfs-ride/ridership.js';
30
31
  import tripCapacity from '../models/gtfs-ride/trip-capacity.js';
31
32
  import rideFeedInfo from './gtfs-ride/ride-feed-info.js';
32
33
 
34
+ import tripUpdates from './gtfs-realtime/trip-updates.js';
35
+ import stopTimesUpdates from './gtfs-realtime/stop-times-updates.js';
36
+ import vehiclePositions from './gtfs-realtime/vehicle-positions.js';
37
+ import serviceAlerts from './gtfs-realtime/service-alerts.js';
38
+ import serviceAlertTargets from './gtfs-realtime/service-alert-targets.js';
39
+
33
40
  const models = [
34
41
  agency,
35
42
  attributions,
@@ -55,11 +62,17 @@ const models = [
55
62
  timetableStopOrder,
56
63
  timetableNotes,
57
64
  timetableNotesReferences,
65
+ tripsDatedVehicleJourney,
58
66
  boardAlight,
59
67
  rideFeedInfo,
60
68
  riderTrip,
61
69
  ridership,
62
70
  tripCapacity,
71
+ tripUpdates,
72
+ stopTimesUpdates,
73
+ vehiclePositions,
74
+ serviceAlerts,
75
+ serviceAlertTargets,
63
76
  ];
64
77
 
65
78
  export default models;
@@ -14,7 +14,6 @@ const model = {
14
14
  {
15
15
  name: 'filename',
16
16
  type: 'varchar(255)',
17
- nocase: true,
18
17
  },
19
18
  ],
20
19
  };
@@ -0,0 +1,32 @@
1
+ const model = {
2
+ filenameBase: 'trips_dated_vehicle_journeys',
3
+ nonstandard: true,
4
+ schema: [
5
+ {
6
+ name: 'trip_id',
7
+ type: 'varchar(255)',
8
+ required: true,
9
+ index: true,
10
+ },
11
+ {
12
+ name: 'operating_day_date',
13
+ type: 'varchar(255)',
14
+ index: true,
15
+ required: true,
16
+ },
17
+ {
18
+ name: 'dated_vehicle_journey_gid',
19
+ type: 'varchar(255)',
20
+ required: true,
21
+ },
22
+ {
23
+ name: 'journey_number',
24
+ type: 'integer',
25
+ min: 0,
26
+ max: 65535,
27
+ index: true,
28
+ },
29
+ ],
30
+ };
31
+
32
+ export default model;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gtfs",
3
- "version": "3.3.0",
3
+ "version": "3.5.0",
4
4
  "description": "Import GTFS transit data into SQLite and query routes, stops, times, fares and more",
5
5
  "keywords": [
6
6
  "transit",
@@ -60,7 +60,8 @@
60
60
  "main": "index.js",
61
61
  "bin": {
62
62
  "gtfs-export": "bin/gtfs-export.js",
63
- "gtfs-import": "bin/gtfs-import.js"
63
+ "gtfs-import": "bin/gtfs-import.js",
64
+ "gtfsrealtime-update": "bin/gtfsrealtime-update.js"
64
65
  },
65
66
  "types": "@types",
66
67
  "scripts": {
@@ -71,33 +72,35 @@
71
72
  },
72
73
  "dependencies": {
73
74
  "@turf/helpers": "^6.5.0",
74
- "chalk": "^5.0.1",
75
- "csv-parse": "^5.0.4",
76
- "csv-stringify": "^6.0.5",
75
+ "csv-parse": "^5.2.2",
76
+ "csv-stringify": "^6.1.3",
77
+ "gtfs-realtime-bindings": "^0.0.6",
77
78
  "lodash-es": "^4.17.21",
78
- "node-fetch": "^3.2.3",
79
+ "long": "^5.2.0",
80
+ "node-fetch": "^3.2.6",
79
81
  "pluralize": "^8.0.0",
80
82
  "pretty-error": "^4.0.0",
81
83
  "promise-map-series": "^0.3.0",
82
84
  "recursive-copy": "^2.0.14",
83
85
  "sanitize-filename": "^1.6.3",
84
86
  "sqlite": "^4.1.1",
85
- "sqlite3": "^5.0.5",
87
+ "sqlite3": "^5.0.8",
86
88
  "sqlstring-sqlite": "^0.1.1",
87
89
  "strip-bom-stream": "^5.0.0",
88
90
  "tmp-promise": "^3.0.3",
89
91
  "untildify": "^4.0.0",
90
92
  "unzipper": "^0.10.11",
91
- "yargs": "^17.4.1"
93
+ "yargs": "^17.5.1",
94
+ "yoctocolors": "^1.0.0"
92
95
  },
93
96
  "devDependencies": {
94
- "dtslint": "^3.4.2",
95
- "eslint": "^8.14.0",
97
+ "dtslint": "^4.2.1",
98
+ "eslint": "^8.19.0",
96
99
  "eslint-config-prettier": "^8.5.0",
97
- "eslint-config-xo": "^0.40.0",
98
- "husky": "^7.0.4",
99
- "mocha": "^9.2.2",
100
- "prettier": "^2.6.2",
100
+ "eslint-config-xo": "^0.41.0",
101
+ "husky": "^8.0.1",
102
+ "mocha": "^10.0.0",
103
+ "prettier": "^2.7.1",
101
104
  "pretty-quick": "^3.1.3",
102
105
  "should": "^13.2.3"
103
106
  },
@@ -0,0 +1,80 @@
1
+ /* eslint-env mocha */
2
+
3
+ import should from 'should';
4
+
5
+ import config from '../test-config.js';
6
+ import {
7
+ openDb,
8
+ getDb,
9
+ closeDb,
10
+ importGtfs,
11
+ advancedQuery,
12
+ } from '../../index.js';
13
+
14
+ describe('advancedQuery():', () => {
15
+ before(async () => {
16
+ await openDb(config);
17
+ await importGtfs(config);
18
+ });
19
+
20
+ after(async () => {
21
+ const db = getDb(config);
22
+ await closeDb(db);
23
+ });
24
+
25
+ it('should return empty array if no trips', async () => {
26
+ const routeId = 'fake-route-id';
27
+
28
+ const advancedQueryOptions = {
29
+ query: {
30
+ route_id: routeId,
31
+ },
32
+ fields: ['stop_times.trip_id', 'arrival_time'],
33
+ join: [
34
+ {
35
+ type: 'INNER',
36
+ table: 'trips',
37
+ on: 'stop_times.trip_id=trips.trip_id',
38
+ },
39
+ ],
40
+ };
41
+ const results = await advancedQuery('stop_times', advancedQueryOptions);
42
+
43
+ should.exists(results);
44
+ results.should.have.length(0);
45
+ });
46
+
47
+ it('should return expected trips with joined trip', async () => {
48
+ const tripId = '329';
49
+
50
+ const advancedQueryOptions = {
51
+ query: {
52
+ 'stop_times.trip_id': tripId,
53
+ },
54
+ fields: ['stop_times.trip_id', 'arrival_time'],
55
+ join: [
56
+ {
57
+ type: 'INNER',
58
+ table: 'trips',
59
+ on: 'stop_times.trip_id=trips.trip_id',
60
+ },
61
+ ],
62
+ };
63
+ const results = await advancedQuery('stop_times', advancedQueryOptions);
64
+
65
+ const expectedResults = [
66
+ { trip_id: '329', arrival_time: '9:09:00' },
67
+ { trip_id: '329', arrival_time: '8:52:00' },
68
+ { trip_id: '329', arrival_time: '8:44:00' },
69
+ { trip_id: '329', arrival_time: '8:35:00' },
70
+ { trip_id: '329', arrival_time: '8:27:00' },
71
+ { trip_id: '329', arrival_time: '8:16:00' },
72
+ { trip_id: '329', arrival_time: '8:03:00' },
73
+ { trip_id: '329', arrival_time: '7:56:00' },
74
+ ];
75
+
76
+ should.exist(results);
77
+ results.length.should.equal(8);
78
+ expectedResults.should.match(results);
79
+ });
80
+ });
@@ -0,0 +1,39 @@
1
+ /* eslint-env mocha */
2
+
3
+ import should from 'should';
4
+
5
+ import config from '../test-config.js';
6
+ import {
7
+ openDb,
8
+ getDb,
9
+ closeDb,
10
+ importGtfs,
11
+ execRawQuery,
12
+ runRawQuery,
13
+ } from '../../index.js';
14
+
15
+ describe('execRawQuery():', () => {
16
+ before(async () => {
17
+ await openDb(config);
18
+ await importGtfs(config);
19
+ });
20
+
21
+ after(async () => {
22
+ const db = getDb(config);
23
+ await closeDb(db);
24
+ });
25
+
26
+ it('should DELETE a trip', async () => {
27
+ const results = await runRawQuery('SELECT count(*) FROM trips');
28
+
29
+ should.exists(results);
30
+ results[0]['count(*)'].should.equal(218);
31
+
32
+ await execRawQuery('DELETE FROM trips where trip_id = "329"');
33
+
34
+ const newResults = await runRawQuery('SELECT count(*) FROM trips');
35
+
36
+ should.exists(newResults);
37
+ newResults[0]['count(*)'].should.equal(217);
38
+ });
39
+ });
@@ -75,4 +75,19 @@ describe('getShapesAsGeoJSON():', () => {
75
75
  geojson.features[0].geometry.coordinates[0].length.should.equal(2);
76
76
  geojson.features[0].properties.route_color.should.startWith('#');
77
77
  });
78
+
79
+ it('should return geojson with shapes for a specific shapeId', async () => {
80
+ const shapeId = 'cal_sf_tam';
81
+
82
+ const geojson = await getShapesAsGeoJSON({
83
+ shape_id: shapeId,
84
+ });
85
+
86
+ should.exist(geojson);
87
+ geojson.type.should.equal('FeatureCollection');
88
+ geojson.features.length.should.equal(3);
89
+ should.exist(geojson.features[0].geometry.coordinates);
90
+ geojson.features[0].geometry.coordinates[0].length.should.equal(2);
91
+ geojson.features[0].properties.route_color.should.startWith('#');
92
+ });
78
93
  });
@@ -209,4 +209,32 @@ describe('getShapes():', () => {
209
209
  results.length.should.equal(713);
210
210
  results.should.containEql(expectedResult);
211
211
  });
212
+
213
+ it('should return array of shapes for specific shape_id', async () => {
214
+ const shapeId = 'cal_sf_tam';
215
+ const results = await getShapes(
216
+ {
217
+ shape_id: shapeId,
218
+ },
219
+ [
220
+ 'shape_id',
221
+ 'shape_pt_lat',
222
+ 'shape_pt_lon',
223
+ 'shape_pt_sequence',
224
+ 'shape_dist_traveled',
225
+ ]
226
+ );
227
+
228
+ const expectedResult = {
229
+ shape_id: 'cal_sf_tam',
230
+ shape_pt_lat: 37.682971245836484,
231
+ shape_pt_lon: -122.39507675170898,
232
+ shape_pt_sequence: 88,
233
+ shape_dist_traveled: null,
234
+ };
235
+
236
+ should.exist(results);
237
+ results.length.should.equal(401);
238
+ results.should.containEql(expectedResult);
239
+ });
212
240
  });
@@ -56,4 +56,18 @@ describe('getStopsAsGeoJSON(): ', () => {
56
56
  should.exist(geojson.features[0].geometry.coordinates);
57
57
  geojson.features[0].geometry.coordinates.length.should.equal(2);
58
58
  });
59
+
60
+ it('should return geojson with stops if they exist for a specific shapeId', async () => {
61
+ const shapeId = 'cal_sf_tam';
62
+
63
+ const geojson = await getStopsAsGeoJSON({
64
+ shape_id: shapeId,
65
+ });
66
+
67
+ should.exist(geojson);
68
+ geojson.type.should.equal('FeatureCollection');
69
+ geojson.features.length.should.equal(25);
70
+ should.exist(geojson.features[0].geometry.coordinates);
71
+ geojson.features[0].geometry.coordinates.length.should.equal(2);
72
+ });
59
73
  });
@@ -217,4 +217,53 @@ describe('getStops():', () => {
217
217
  );
218
218
  }
219
219
  });
220
+
221
+ it('should return array of stops if it exists for a specific shape_id', async () => {
222
+ const shapeId = 'cal_sf_tam';
223
+
224
+ const results = await getStops(
225
+ {
226
+ shape_id: shapeId,
227
+ },
228
+ [],
229
+ [['stop_id', 'ASC']]
230
+ );
231
+
232
+ const expectedStopIds = [
233
+ '70012',
234
+ '70021',
235
+ '70022',
236
+ '70032',
237
+ '70042',
238
+ '70052',
239
+ '70062',
240
+ '70082',
241
+ '70092',
242
+ '70102',
243
+ '70112',
244
+ '70122',
245
+ '70132',
246
+ '70142',
247
+ '70162',
248
+ '70172',
249
+ '70192',
250
+ '70202',
251
+ '70212',
252
+ '70222',
253
+ '70232',
254
+ '70242',
255
+ '70252',
256
+ '70262',
257
+ '70272',
258
+ ];
259
+
260
+ should.exist(results);
261
+ results.length.should.equal(25);
262
+ for (const [idx, stop] of results.entries()) {
263
+ expectedStopIds[idx].should.equal(
264
+ stop.stop_id,
265
+ 'The order of stops are expected to be the same'
266
+ );
267
+ }
268
+ });
220
269
  });
@@ -0,0 +1,54 @@
1
+ /* eslint-env mocha */
2
+
3
+ import should from 'should';
4
+
5
+ import config from '../test-config.js';
6
+ import {
7
+ openDb,
8
+ getDb,
9
+ closeDb,
10
+ importGtfs,
11
+ runRawQuery,
12
+ } from '../../index.js';
13
+
14
+ describe('runRawQuery():', () => {
15
+ before(async () => {
16
+ await openDb(config);
17
+ await importGtfs(config);
18
+ });
19
+
20
+ after(async () => {
21
+ const db = getDb(config);
22
+ await closeDb(db);
23
+ });
24
+
25
+ it('should return empty array if no trips', async () => {
26
+ const results = await runRawQuery(
27
+ 'SELECT * FROM trips WHERE trip_id = "fake-trip-id"'
28
+ );
29
+
30
+ should.exists(results);
31
+ results.should.have.length(0);
32
+ });
33
+
34
+ it('should return expected results', async () => {
35
+ const results = await runRawQuery(
36
+ 'SELECT "trip_id", "arrival_time" FROM stop_times WHERE trip_id = "329"'
37
+ );
38
+
39
+ const expectedResults = [
40
+ { trip_id: '329', arrival_time: '9:09:00' },
41
+ { trip_id: '329', arrival_time: '8:52:00' },
42
+ { trip_id: '329', arrival_time: '8:44:00' },
43
+ { trip_id: '329', arrival_time: '8:35:00' },
44
+ { trip_id: '329', arrival_time: '8:27:00' },
45
+ { trip_id: '329', arrival_time: '8:16:00' },
46
+ { trip_id: '329', arrival_time: '8:03:00' },
47
+ { trip_id: '329', arrival_time: '7:56:00' },
48
+ ];
49
+
50
+ should.exist(results);
51
+ results.length.should.equal(8);
52
+ expectedResults.should.match(results);
53
+ });
54
+ });