@transportme/load-ptv-gtfs 1.3.5 → 1.3.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/lib/gtfs-parser/GTFSStop.mjs +13 -10
- package/lib/gtfs-parser/readers/GTFSStopsReader.mjs +4 -2
- package/lib/loader/StopsLoader.mjs +3 -2
- package/lib/loader/TripLoader.mjs +31 -0
- package/package.json +1 -1
- package/test/gtfs-stop-loader.test.mjs +16 -0
- package/test/gtfs-stops.test.mjs +9 -0
- package/test/gtfs-trip-loader.test.mjs +46 -0
|
@@ -27,7 +27,7 @@ export class GTFSStop {
|
|
|
27
27
|
#isInterstate = false
|
|
28
28
|
#suburbInFrontMatch
|
|
29
29
|
|
|
30
|
-
constructor({ stopGTFSID, stopName, stopLat, stopLon, locationType, parentStopGTFSID }, suburbs) {
|
|
30
|
+
constructor({ stopGTFSID, stopName, stopLat, stopLon, locationType, parentStopGTFSID }, suburbs, suburbHook) {
|
|
31
31
|
this.stopGTFSID = stopGTFSID
|
|
32
32
|
this.originalName = stopName.replace(/ \(\w+\.\.\. #/, ' #').replace(/ +/g, ' ').trim()
|
|
33
33
|
|
|
@@ -39,7 +39,7 @@ export class GTFSStop {
|
|
|
39
39
|
coordinates: [parseFloat(stopLon), parseFloat(stopLat)]
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
this.#setSuburb(suburbs)
|
|
42
|
+
this.#setSuburb(suburbs, suburbHook)
|
|
43
43
|
this.#setStopNumber()
|
|
44
44
|
|
|
45
45
|
this.fullStopName = this.getFullStopName()
|
|
@@ -105,8 +105,11 @@ export class GTFSStop {
|
|
|
105
105
|
return this.#expandSuburb(this.originalName.slice(suburbIndex + 1, -1))
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
#setSuburb(suburbs) {
|
|
109
|
-
|
|
108
|
+
#setSuburb(suburbs, suburbHook) {
|
|
109
|
+
let output
|
|
110
|
+
if (suburbHook && (output = suburbHook(this))) {
|
|
111
|
+
this.suburb = output
|
|
112
|
+
} else if (this.requiresSuburb()) {
|
|
110
113
|
this.suburb = this.getSuburbFromLocation(suburbs)
|
|
111
114
|
} else {
|
|
112
115
|
this.suburb = this.getSuburbFromName()
|
|
@@ -206,10 +209,10 @@ export class GTFSStop {
|
|
|
206
209
|
return true
|
|
207
210
|
}
|
|
208
211
|
|
|
209
|
-
static create(data, suburbs) {
|
|
212
|
+
static create(data, suburbs, suburbHook) {
|
|
210
213
|
let types = [ GTFSRRBStop, GTFSPlatformStop, GTFSStop ]
|
|
211
214
|
for (let type of types) {
|
|
212
|
-
if (type.canProcess(data)) return new type(data, suburbs)
|
|
215
|
+
if (type.canProcess(data)) return new type(data, suburbs, suburbHook)
|
|
213
216
|
}
|
|
214
217
|
}
|
|
215
218
|
}
|
|
@@ -218,8 +221,8 @@ export class GTFSPlatformStop extends GTFSStop {
|
|
|
218
221
|
|
|
219
222
|
platform
|
|
220
223
|
|
|
221
|
-
constructor(data, suburbs) {
|
|
222
|
-
super(data, suburbs)
|
|
224
|
+
constructor(data, suburbs, suburbHook) {
|
|
225
|
+
super(data, suburbs, suburbHook)
|
|
223
226
|
this.platform = data.platform
|
|
224
227
|
}
|
|
225
228
|
|
|
@@ -238,9 +241,9 @@ export class GTFSPlatformStop extends GTFSStop {
|
|
|
238
241
|
|
|
239
242
|
export class GTFSRRBStop extends GTFSPlatformStop {
|
|
240
243
|
|
|
241
|
-
constructor(data, suburbs) {
|
|
244
|
+
constructor(data, suburbs, suburbHook) {
|
|
242
245
|
data.stopName = data.stopName.replace('Rail Replacement Bus Stop', 'Railway Station')
|
|
243
|
-
super(data, suburbs)
|
|
246
|
+
super(data, suburbs, suburbHook)
|
|
244
247
|
this.platform = 'RRB'
|
|
245
248
|
}
|
|
246
249
|
|
|
@@ -4,10 +4,12 @@ import GTFSReader from './GTFSReader.mjs'
|
|
|
4
4
|
export default class GTFSStopsReader extends GTFSReader {
|
|
5
5
|
|
|
6
6
|
#suburbData
|
|
7
|
+
#suburbHook
|
|
7
8
|
|
|
8
|
-
constructor(file, suburbData) {
|
|
9
|
+
constructor(file, suburbData, suburbHook) {
|
|
9
10
|
super(file)
|
|
10
11
|
this.#suburbData = suburbData
|
|
12
|
+
this.#suburbHook = suburbHook
|
|
11
13
|
}
|
|
12
14
|
|
|
13
15
|
/**
|
|
@@ -33,6 +35,6 @@ export default class GTFSStopsReader extends GTFSReader {
|
|
|
33
35
|
parentStopGTFSID: data.parent_station
|
|
34
36
|
}
|
|
35
37
|
|
|
36
|
-
return GTFSStop.create(stopData, this.#suburbData)
|
|
38
|
+
return GTFSStop.create(stopData, this.#suburbData, this.#suburbHook)
|
|
37
39
|
}
|
|
38
40
|
}
|
|
@@ -19,11 +19,12 @@ export default class StopsLoader {
|
|
|
19
19
|
* @param {FeatureCollection} suburbs A GeoJSON FeatureCollection containing a list of all suburbs located within Victoria
|
|
20
20
|
* @param {string} mode The transit mode being loaded
|
|
21
21
|
* @param {DatabaseConnection} database A database connection
|
|
22
|
+
* @param {function} [suburbHook] A hook that returns the stop's suburb, or null if unavailable. This function is called before processing from the stop's name or location
|
|
22
23
|
*/
|
|
23
|
-
constructor(stopsFile, suburbs, mode, database) {
|
|
24
|
+
constructor(stopsFile, suburbs, mode, database, suburbHook) {
|
|
24
25
|
this.#stops = database.getCollection('stops')
|
|
25
26
|
this.#mode = mode
|
|
26
|
-
this.#stopsReader = new GTFSStopsReader(stopsFile, suburbs)
|
|
27
|
+
this.#stopsReader = new GTFSStopsReader(stopsFile, suburbs, suburbHook)
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
getMergeName(stop) {
|
|
@@ -28,6 +28,7 @@ export default class TripLoader {
|
|
|
28
28
|
|
|
29
29
|
#shapeIDMap = {}
|
|
30
30
|
#directionIDMap = {}
|
|
31
|
+
#stopServicesMap = {}
|
|
31
32
|
|
|
32
33
|
#tripsToLoad = []
|
|
33
34
|
|
|
@@ -172,9 +173,27 @@ export default class TripLoader {
|
|
|
172
173
|
if (!this.#directionIDMap[tripData.routeGTFSID]) this.#directionIDMap[tripData.routeGTFSID] = {}
|
|
173
174
|
this.#directionIDMap[tripData.routeGTFSID][rawTripData.getHeadsign()] = rawTripData.getGTFSDirection()
|
|
174
175
|
|
|
176
|
+
this.updateStopServices(processedData)
|
|
177
|
+
|
|
175
178
|
this.#tripsToLoad.push(processedData)
|
|
176
179
|
}
|
|
177
180
|
|
|
181
|
+
updateStopServices(trip) {
|
|
182
|
+
let { gtfsDirection, routeGTFSID, routeNumber } = trip
|
|
183
|
+
trip.stopTimings.forEach(stop => {
|
|
184
|
+
let { stopGTFSID } = stop
|
|
185
|
+
let serviceID = `${routeGTFSID}-${gtfsDirection}-${routeNumber}`
|
|
186
|
+
if (!this.#stopServicesMap[stopGTFSID]) this.#stopServicesMap[stopGTFSID] = { services: {}, screenServices: {} }
|
|
187
|
+
if (!this.#stopServicesMap[stopGTFSID].services[serviceID]) {
|
|
188
|
+
this.#stopServicesMap[stopGTFSID].services[serviceID] = { routeGTFSID, gtfsDirection, routeNumber }
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (stop.stopConditions.pickup === 0 && !this.#stopServicesMap[stopGTFSID].screenServices[serviceID]) {
|
|
192
|
+
this.#stopServicesMap[stopGTFSID].screenServices[serviceID] = { routeGTFSID, gtfsDirection, routeNumber }
|
|
193
|
+
}
|
|
194
|
+
})
|
|
195
|
+
}
|
|
196
|
+
|
|
178
197
|
/**
|
|
179
198
|
* Loads the trip data into the database.
|
|
180
199
|
*
|
|
@@ -222,4 +241,16 @@ export default class TripLoader {
|
|
|
222
241
|
getDirectionIDMap() {
|
|
223
242
|
return this.#directionIDMap
|
|
224
243
|
}
|
|
244
|
+
|
|
245
|
+
getStopServicesMap() {
|
|
246
|
+
let output = {}
|
|
247
|
+
for (let stopGTFSID of Object.keys(this.#stopServicesMap)) {
|
|
248
|
+
let stopServices = this.#stopServicesMap[stopGTFSID]
|
|
249
|
+
output[stopGTFSID] = {
|
|
250
|
+
services: Object.values(stopServices.services),
|
|
251
|
+
screenServices: Object.values(stopServices.screenServices)
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
return output
|
|
255
|
+
}
|
|
225
256
|
}
|
package/package.json
CHANGED
|
@@ -197,6 +197,22 @@ describe('The GTFS Stops Loader', () => {
|
|
|
197
197
|
expect(gumRd.stopName).to.equal('Gum Road/Main Road West')
|
|
198
198
|
})
|
|
199
199
|
|
|
200
|
+
it('Should allow for a custom suburb hook', async () => {
|
|
201
|
+
let database = new LokiDatabaseConnection('test-db')
|
|
202
|
+
let stops = await database.createCollection('stops')
|
|
203
|
+
|
|
204
|
+
let testSuburbs = ['A', 'B', 'C', null, 'D']
|
|
205
|
+
|
|
206
|
+
let loader = new StopsLoader(stopsFile, suburbs, TRANSIT_MODES.bus, database, () => testSuburbs.shift())
|
|
207
|
+
await loader.loadStops()
|
|
208
|
+
|
|
209
|
+
expect((await stops.findDocument({ 'bays.stopGTFSID': '1000' })).suburb[0]).to.equal('A')
|
|
210
|
+
expect((await stops.findDocument({ 'bays.stopGTFSID': '10001' })).suburb[0]).to.equal('B')
|
|
211
|
+
expect((await stops.findDocument({ 'bays.stopGTFSID': '10002' })).suburb[0]).to.equal('C')
|
|
212
|
+
expect((await stops.findDocument({ 'bays.stopGTFSID': '10009' })).suburb[0]).to.equal('Albanvale') // Null so not used
|
|
213
|
+
expect((await stops.findDocument({ 'bays.stopGTFSID': '1001' })).suburb[0]).to.equal('D')
|
|
214
|
+
})
|
|
215
|
+
|
|
200
216
|
it('Should allow for individual stops to be loaded in', async () => {
|
|
201
217
|
let database = new LokiDatabaseConnection('test-db')
|
|
202
218
|
let stops = await database.createCollection('stops')
|
package/test/gtfs-stops.test.mjs
CHANGED
|
@@ -64,6 +64,15 @@ describe('The GTFSStopReader class', () => {
|
|
|
64
64
|
expect(stopData.stopGTFSID).to.equal('14330')
|
|
65
65
|
expect(stopData.parentStopGTFSID).to.equal('vic:rail:CHL')
|
|
66
66
|
})
|
|
67
|
+
|
|
68
|
+
it('Should allow a custom stop suburb hook', async () => {
|
|
69
|
+
let stopReader = new GTFSStopsReader(parentStopsFile, suburbs, () => 'Test')
|
|
70
|
+
await stopReader.open()
|
|
71
|
+
|
|
72
|
+
let stopData = await stopReader.getNextEntity()
|
|
73
|
+
expect(stopData.rawStopName).to.equal('Clifton Hill Station')
|
|
74
|
+
expect(stopData.suburb).to.equal('Test')
|
|
75
|
+
})
|
|
67
76
|
})
|
|
68
77
|
|
|
69
78
|
describe('The getNextEntity function', () => {
|
|
@@ -139,4 +139,50 @@ describe('The TripLoader class', () => {
|
|
|
139
139
|
'2-ALM': { 'Alamein': 0, 'Camberwell': 1 }
|
|
140
140
|
})
|
|
141
141
|
})
|
|
142
|
+
|
|
143
|
+
it('Should return a mapping stop services', async () => {
|
|
144
|
+
let database = new LokiDatabaseConnection('test-db')
|
|
145
|
+
let stops = await database.createCollection('stops')
|
|
146
|
+
let routes = await database.createCollection('routes')
|
|
147
|
+
let trips = await database.createCollection('gtfs timetables')
|
|
148
|
+
|
|
149
|
+
let stopLoader = new StopsLoader(stopsFile, suburbs, TRANSIT_MODES.metroTrain, database)
|
|
150
|
+
await stopLoader.loadStops()
|
|
151
|
+
|
|
152
|
+
let routeLoader = new RouteLoader(routesFile, agencyFile, TRANSIT_MODES.metroTrain, database)
|
|
153
|
+
await routeLoader.loadRoutes()
|
|
154
|
+
|
|
155
|
+
let routeIDMap = routeLoader.getRouteIDMap()
|
|
156
|
+
|
|
157
|
+
let tripLoader = new TripLoader({
|
|
158
|
+
tripsFile, stopTimesFile,
|
|
159
|
+
calendarFile, calendarDatesFile
|
|
160
|
+
}, TRANSIT_MODES.metroTrain, database)
|
|
161
|
+
|
|
162
|
+
await tripLoader.loadTrips({ routeIDMap })
|
|
163
|
+
|
|
164
|
+
let stopServicesMap = tripLoader.getStopServicesMap()
|
|
165
|
+
|
|
166
|
+
expect(stopServicesMap['vic:rail:ALM'].services).to.deep.equal([{
|
|
167
|
+
routeGTFSID: '2-ALM',
|
|
168
|
+
gtfsDirection: 1,
|
|
169
|
+
routeNumber: null
|
|
170
|
+
}, {
|
|
171
|
+
routeGTFSID: '2-ALM',
|
|
172
|
+
gtfsDirection: 0,
|
|
173
|
+
routeNumber: null
|
|
174
|
+
}])
|
|
175
|
+
|
|
176
|
+
expect(stopServicesMap['vic:rail:ALM'].screenServices).to.deep.equal([{
|
|
177
|
+
routeGTFSID: '2-ALM',
|
|
178
|
+
gtfsDirection: 1,
|
|
179
|
+
routeNumber: null
|
|
180
|
+
}])
|
|
181
|
+
|
|
182
|
+
expect(stopServicesMap['vic:rail:CAM'].screenServices).to.deep.equal([{
|
|
183
|
+
routeGTFSID: '2-ALM',
|
|
184
|
+
gtfsDirection: 0,
|
|
185
|
+
routeNumber: null
|
|
186
|
+
}])
|
|
187
|
+
})
|
|
142
188
|
})
|