@transportme/load-ptv-gtfs 1.2.5 → 1.3.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.
@@ -3,8 +3,16 @@ import { STOP_UTILS } from '@transportme/transportvic-utils'
3
3
 
4
4
  const { processName, getPrimaryStopName, getSecondaryStopName } = STOP_UTILS
5
5
 
6
+ export const STOP_TYPE = {
7
+ 0: 'stop',
8
+ 1: 'station',
9
+ 2: 'entrance',
10
+ 3: 'generic',
11
+ 4: 'boarding',
12
+ }
13
+
6
14
  export class GTFSStop {
7
-
15
+
8
16
  stopGTFSID
9
17
  originalName
10
18
  rawStopName
@@ -13,12 +21,19 @@ export class GTFSStop {
13
21
  location
14
22
  suburb
15
23
 
24
+ location
25
+ parentStopGTFSID
26
+
16
27
  #isInterstate = false
17
28
  #suburbInFrontMatch
18
29
 
19
- constructor({ stopGTFSID, stopName, stopLat, stopLon }, suburbs) {
30
+ constructor({ stopGTFSID, stopName, stopLat, stopLon, locationType, parentStopGTFSID }, suburbs) {
20
31
  this.stopGTFSID = stopGTFSID
21
- this.originalName = stopName.replace(/ +/g, ' ').trim()
32
+ this.originalName = stopName.replace(/ \(\w+\.\.\. #/, ' #').replace(/ +/g, ' ').trim()
33
+
34
+ this.locationType = locationType || '0'
35
+ this.parentStopGTFSID = parentStopGTFSID || null
36
+
22
37
  this.location = {
23
38
  type: 'Point',
24
39
  coordinates: [parseFloat(stopLon), parseFloat(stopLat)]
@@ -181,7 +196,8 @@ export class GTFSStop {
181
196
  mode,
182
197
  suburb: this.suburb,
183
198
  services: [],
184
- screenServices: []
199
+ screenServices: [],
200
+ stopType: STOP_TYPE[this.locationType]
185
201
  }
186
202
  }
187
203
 
@@ -228,7 +244,7 @@ export class GTFSRRBStop extends GTFSPlatformStop {
228
244
  }
229
245
 
230
246
  static canProcess(data) {
231
- return super.canProcess(data) && data.platform === 'R-Bus'
247
+ return super.canProcess(data) && data.platform.toLowerCase().includes('bus')
232
248
  }
233
249
 
234
250
  }
@@ -28,7 +28,9 @@ export default class GTFSStopsReader extends GTFSReader {
28
28
  stopName: data.stop_name,
29
29
  stopLat: data.stop_lat,
30
30
  stopLon: data.stop_lon,
31
- platform: data.platform_code
31
+ platform: data.platform_code,
32
+ locationType: data.location_type,
33
+ parentStopGTFSID: data.parent_station
32
34
  }
33
35
 
34
36
  return GTFSStop.create(stopData, this.#suburbData)
@@ -1,5 +1,5 @@
1
1
  import { STOP_UTILS } from '@transportme/transportvic-utils'
2
- import { GTFSStop } from '../gtfs-parser/GTFSStop.mjs'
2
+ import { GTFSStop, STOP_TYPE } from '../gtfs-parser/GTFSStop.mjs'
3
3
  import GTFSStopsReader from '../gtfs-parser/readers/GTFSStopsReader.mjs'
4
4
 
5
5
  const { getPrimaryStopName, isStreetStop, sanitiseName } = STOP_UTILS
@@ -10,6 +10,8 @@ export default class StopsLoader {
10
10
  #mode
11
11
  #stopsReader
12
12
 
13
+ #parentHolding = {}
14
+
13
15
  /**
14
16
  * Creates a new Stop Loader.
15
17
  *
@@ -42,6 +44,12 @@ export default class StopsLoader {
42
44
  })
43
45
  }
44
46
 
47
+ async findParentStop(stop) {
48
+ return await this.#stops.findDocument({
49
+ 'bays.stopGTFSID': stop.parentStopGTFSID
50
+ })
51
+ }
52
+
45
53
  generateDBStopData(stop, mergeName) {
46
54
  let cleanName = sanitiseName(stop.fullStopName)
47
55
 
@@ -68,6 +76,7 @@ export default class StopsLoader {
68
76
 
69
77
  updateStopName(stop) {
70
78
  stop.cleanNames = stop.bays
79
+ .filter(bay => bay.stopType === STOP_TYPE[0])
71
80
  .map(bay => sanitiseName(bay.fullStopName))
72
81
  .filter((e, i, a) => a.indexOf(e) === i)
73
82
 
@@ -84,10 +93,12 @@ export default class StopsLoader {
84
93
 
85
94
  updateStopSuburb(stop) {
86
95
  stop.cleanSuburbs = stop.bays
96
+ .filter(bay => bay.stopType === STOP_TYPE[0])
87
97
  .map(bay => sanitiseName(bay.suburb))
88
98
  .filter((e, i, a) => a.indexOf(e) === i)
89
99
 
90
100
  stop.suburb = stop.bays
101
+ .filter(bay => bay.stopType === STOP_TYPE[0])
91
102
  .map(bay => bay.suburb)
92
103
  .filter((e, i, a) => a.indexOf(e) === i)
93
104
  .sort()
@@ -129,6 +140,18 @@ export default class StopsLoader {
129
140
  async loadStop(stop, { processStop, getMergeName } = {}) {
130
141
  let processedData = processStop ? processStop(stop) : stop
131
142
 
143
+ if (processedData.parentStopGTFSID) {
144
+ let parentStop = await this.findParentStop(processedData)
145
+ if (!parentStop) {
146
+ if (!this.#parentHolding[processedData.parentStopGTFSID]) this.#parentHolding[processedData.parentStopGTFSID] = []
147
+ this.#parentHolding[processedData.parentStopGTFSID].push(processedData)
148
+ return
149
+ }
150
+
151
+ let stopData = this.generateDBStopData(processedData, parentStop.mergeName)
152
+ return await this.mergeStops(parentStop, stopData)
153
+ }
154
+
132
155
  let defaultMergeName = this.getMergeName(processedData)
133
156
  let mergeName = getMergeName ? (getMergeName(processedData) || defaultMergeName) : defaultMergeName
134
157
 
@@ -140,6 +163,14 @@ export default class StopsLoader {
140
163
  } else {
141
164
  await this.#stops.createDocument(stopData)
142
165
  }
166
+
167
+ if (this.#parentHolding[stop.stopGTFSID]) {
168
+ for (let childStop of this.#parentHolding[stop.stopGTFSID]) {
169
+ let childStopData = this.generateDBStopData(childStop, stopData.mergeName)
170
+ await this.mergeStops(stopData, childStopData)
171
+ }
172
+ delete this.#parentHolding[stop.stopGTFSID]
173
+ }
143
174
  }
144
175
 
145
176
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@transportme/load-ptv-gtfs",
3
- "version": "1.2.5",
3
+ "version": "1.3.0",
4
4
  "description": "",
5
5
  "main": "index.mjs",
6
6
  "scripts": {
@@ -26,6 +26,8 @@ const specialHeader = path.join(__dirname, 'sample-data', 'gtfs-splitting', 'spe
26
26
 
27
27
  const unionWithSpaces = path.join(__dirname, 'sample-data', 'gtfs-splitting', 'union_with_spaces.txt')
28
28
 
29
+ const parentStopsFile = path.join(__dirname, 'sample-data', 'parent-stop', 'stops.txt')
30
+
29
31
  describe('The GTFS Stops Loader', () => {
30
32
  it('Should process the stops and add them to the database', async () => {
31
33
  let database = new LokiDatabaseConnection('test-db')
@@ -318,6 +320,23 @@ describe('The GTFS Stops Loader', () => {
318
320
  await (new StopsLoader(unionWithSpaces, suburbs, TRANSIT_MODES.bus, database)).loadStops()
319
321
 
320
322
  expect(await stops.countDocuments({})).to.equal(1)
321
- expect((await stops.findDocument({ 'bays.stopGTFSID': '26535' })).bays.length).to.equal(4) // Piangil
323
+ expect((await stops.findDocument({ 'bays.stopGTFSID': '26535' })).bays.length).to.equal(4) // Union Station
324
+ })
325
+
326
+ it('Should merge stops into their designated parent stops even if the names do not match', async () => {
327
+ let database = new LokiDatabaseConnection('test-db')
328
+ let stops = await database.createCollection('stops')
329
+
330
+ await (new StopsLoader(parentStopsFile, suburbs, TRANSIT_MODES.metroTrain, database)).loadStops()
331
+
332
+ expect(await stops.countDocuments({})).to.equal(1)
333
+ let stopData = await stops.findDocument({})
334
+ expect(stopData.bays.find(bay => bay.stopGTFSID === '14331').stopType).to.equal('stop')
335
+ expect(stopData.bays.find(bay => bay.stopGTFSID === 'vic:rail:CHL').stopType).to.equal('station')
336
+ expect(stopData.bays.find(bay => bay.stopGTFSID === 'vic:rail:CHL_DP3').stopType).to.equal('generic')
337
+ expect(stopData.bays.find(bay => bay.stopGTFSID === 'vic:rail:CHL_EN1').stopType).to.equal('entrance')
338
+
339
+ // TODO: Find out what the Connex stop is for
340
+ expect(stopData.cleanNames).to.equal(['clifton-hill-railway-station', 'connex'])
322
341
  })
323
342
  })
@@ -9,6 +9,8 @@ const __dirname = path.dirname(__filename)
9
9
 
10
10
  const stopsFile = path.join(__dirname, 'sample-data', 'gtfs-splitting', 'line_reader_test.txt')
11
11
 
12
+ const parentStopsFile = path.join(__dirname, 'sample-data', 'parent-stop', 'stops.txt')
13
+
12
14
  let stopInput = {
13
15
  stop_id: '1000',
14
16
  stop_name: 'Dole Ave/Cheddar Rd (Reservoir)',
@@ -52,6 +54,16 @@ describe('The GTFSStopReader class', () => {
52
54
  expect(stopData.suburb).to.equal('Kings Park')
53
55
  expect(stopData.stopNumber).to.equal('51A')
54
56
  })
57
+
58
+ it('Should return the parent stop data if available', async () => {
59
+ let stopReader = new GTFSStopsReader(parentStopsFile, suburbs)
60
+ await stopReader.open()
61
+
62
+ let stopData = await stopReader.getNextEntity()
63
+ expect(stopData.rawStopName).to.equal('Clifton Hill Station')
64
+ expect(stopData.stopGTFSID).to.equal('14330')
65
+ expect(stopData.parentStopGTFSID).to.equal('vic:rail:CHL')
66
+ })
55
67
  })
56
68
 
57
69
  describe('The getNextEntity function', () => {
@@ -47,5 +47,21 @@ describe('The GTFS Loaders with the new Tram data', () => {
47
47
  expect(stop.bays[0].stopNumber).to.equal('61')
48
48
  expect(stop.bays[0].originalName).to.equal('Glen Huntly Railway Station/Glen Huntly Rd #61')
49
49
  })
50
+
51
+ it('Should remove half suburbs in the name', async () => {
52
+ let database = new LokiDatabaseConnection('test-db')
53
+ let stops = await database.createCollection('stops')
54
+
55
+ let loader = new StopsLoader(stopsFile, suburbs, TRANSIT_MODES.tram, database)
56
+ await loader.loadStops()
57
+
58
+ let stop = await stops.findDocument({
59
+ 'stopName': 'Royal Melbourne Hospital-Parkville Railway Station/Royal Parade'
60
+ })
61
+
62
+ expect(stop).to.not.be.null
63
+ expect(stop.bays[0].stopNumber).to.equal('10')
64
+ expect(stop.bays[0].originalName).to.equal('Royal Melbourne Hospital-Parkville Station/Royal Pde #10')
65
+ })
50
66
  })
51
67
  })
@@ -3,4 +3,6 @@ stop_id,stop_name,stop_lat,stop_lon
3
3
  "10371","Duncraig Ave/Wattletree Rd #44","-37.86206934","145.02538150"
4
4
  "1083","Clyde St/Raleigh Rd #42","-37.76969864","144.89884126"
5
5
  "11285","Egerton Rd/Wattletree Rd #43","-37.86170982","145.02275399"
6
- "18404","Glen Huntly Railway Station/Glen Huntly Rd #61","-37.88954800","145.04275047","","","0","Level 0
6
+ "18404","Glen Huntly Railway Station/Glen Huntly Rd #61","-37.88954800","145.04275047","","","0","Level 0
7
+ "17867","Royal Melbourne Hospital-Parkville Station/Royal Pde (Melbo... #10","-37.79992871","144.95750800","","","0","Level 0"
8
+ "17868","Royal Melbourne Hospital-Parkville Station/Royal Pde (Parkv... #10","-37.79954277","144.95759813","","","0","Level 0"
@@ -0,0 +1,15 @@
1
+ stop_id,stop_name,stop_lat,stop_lon,location_type,parent_station,wheelchair_boarding,level_id,platform_code
2
+ "14330","Clifton Hill Station","-37.78847865","144.99550102","","vic:rail:CHL","1","Level 0","1"
3
+ "14331","Clifton Hill Station","-37.78868312","144.99533652","","vic:rail:CHL","1","Level 0","2"
4
+ "26140","Clifton Hill Rail Replacement Bus Stop","-37.78899337","144.99503289","","vic:rail:CHL","1","Level 0","Replacement bus"
5
+ "vic:rail:CHL","Clifton Hill Railway Station","-37.78865745","144.99541670","1","","1","",""
6
+ "vic:rail:CHL_BR1","Bike & Ride","-37.78862432","144.99558793","3","vic:rail:CHL","","Level 0",""
7
+ "vic:rail:CHL_ConX","Connex","-37.78865745","144.99541670","","vic:rail:CHL","","Level 0",""
8
+ "vic:rail:CHL_DP1","Ramp","-37.78814003","144.99572593","3","vic:rail:CHL","","Level -1",""
9
+ "vic:rail:CHL_DP2","Underpass","-37.78810586","144.99530670","3","vic:rail:CHL","","Level -1",""
10
+ "vic:rail:CHL_DP3","Ramp","-37.78805406","144.99491065","3","vic:rail:CHL","","Level -1",""
11
+ "vic:rail:CHL_EN1","John St","-37.78849918","144.99564809","2","vic:rail:CHL","1","Level 0",""
12
+ "vic:rail:CHL_EN2","Hoddle St","-37.78860913","144.99522496","2","vic:rail:CHL","1","Level 0",""
13
+ "vic:rail:CHL_EN3","Hoddle St","-37.78877108","144.99520923","2","vic:rail:CHL","1","Level 0",""
14
+ "vic:rail:CHL_PR1","Park & Ride 1","-37.78928973","144.99550182","3","vic:rail:CHL","","Level 0",""
15
+ "vic:rail:CHL_PR2","Park & Ride 2","-37.78785120","144.99569967","3","vic:rail:CHL","","Level 0",""