mirror of
https://github.com/public-transport/db-vendo-client.git
synced 2025-02-23 15:19:35 +02:00
parseLocation: parse entrances & sub-stops lists
This commit is contained in:
parent
07c77f8cf9
commit
76e310218a
3 changed files with 107 additions and 14 deletions
10
docs/stop.md
10
docs/stop.md
|
@ -95,3 +95,13 @@ The response may look like this:
|
||||||
} ]
|
} ]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
If the endpoint returns a list of entrances for a station, the resulting station object will have an `entrances` array looking similar to this:
|
||||||
|
|
||||||
|
```js
|
||||||
|
[
|
||||||
|
{type: 'location', latitude: 47.411069, longitude: 10.277412},
|
||||||
|
{type: 'location', latitude: 47.410493, longitude: 10.277223},
|
||||||
|
{type: 'location', latitude: 47.410754, longitude: 10.278023}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
const {parse} = require('qs')
|
const {parse} = require('qs')
|
||||||
|
const get = require('lodash/get')
|
||||||
|
|
||||||
const POI = 'P'
|
const POI = 'P'
|
||||||
const STATION = 'S'
|
const STATION = 'S'
|
||||||
|
@ -30,16 +31,38 @@ const parseLocation = (ctx, l) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (l.type === STATION) {
|
if (l.type === STATION) {
|
||||||
|
// todo: https://github.com/public-transport/hafas-client/issues/151
|
||||||
|
const locL = get(ctx.res, ['common', 'locL'], [])
|
||||||
|
|
||||||
|
const mMastLocX = 'mMastLocX' in l ? l.mMastLocX : NaN
|
||||||
|
const subStops = (l.stopLocL || [])
|
||||||
|
.filter(locX => locX !== mMastLocX)
|
||||||
|
.map(locX => locL[locX])
|
||||||
|
.filter(s => !!s)
|
||||||
|
.map(s => profile.parseLocation(ctx, s))
|
||||||
|
.filter(stop => !!stop)
|
||||||
|
|
||||||
const stop = {
|
const stop = {
|
||||||
type: l.isMainMast ? 'station' : 'stop',
|
type: l.isMainMast ? 'station' : 'stop',
|
||||||
id: res.id,
|
id: res.id,
|
||||||
name: l.name || lid.O ? profile.parseStationName(ctx, l.name || lid.O) : null,
|
name: l.name || lid.O ? profile.parseStationName(ctx, l.name || lid.O) : null,
|
||||||
location: 'number' === typeof res.latitude ? res : null // todo: remove `.id`
|
location: 'number' === typeof res.latitude ? res : null // todo: remove `.id`
|
||||||
}
|
}
|
||||||
|
if (opt.subStops && subStops.length > 0) stop.stops = subStops
|
||||||
|
|
||||||
if ('pCls' in l) stop.products = profile.parseProductsBitmask(ctx, l.pCls)
|
if ('pCls' in l) stop.products = profile.parseProductsBitmask(ctx, l.pCls)
|
||||||
if ('meta' in l) stop.isMeta = !!l.meta
|
if ('meta' in l) stop.isMeta = !!l.meta
|
||||||
|
|
||||||
|
if (opt.entrances) {
|
||||||
|
const entrances = (l.entryLocL || [])
|
||||||
|
.map(locX => locL[locX])
|
||||||
|
.filter(l => !!l)
|
||||||
|
.map(l => profile.parseLocation(ctx, l))
|
||||||
|
.filter(loc => !!loc)
|
||||||
|
.map(loc => loc.location)
|
||||||
|
if (entrances.length > 0) stop.entrances = entrances
|
||||||
|
}
|
||||||
|
|
||||||
if (opt.linesOfStops && Array.isArray(l.lines)) {
|
if (opt.linesOfStops && Array.isArray(l.lines)) {
|
||||||
stop.lines = l.lines
|
stop.lines = l.lines
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ const omit = require('lodash/omit')
|
||||||
const parse = require('../../parse/location')
|
const parse = require('../../parse/location')
|
||||||
|
|
||||||
const profile = {
|
const profile = {
|
||||||
|
parseLocation: parse,
|
||||||
parseStationName: (_, name) => name.toLowerCase(),
|
parseStationName: (_, name) => name.toLowerCase(),
|
||||||
parseProductsBitmask: (_, bitmask) => [bitmask]
|
parseProductsBitmask: (_, bitmask) => [bitmask]
|
||||||
}
|
}
|
||||||
|
@ -12,7 +13,9 @@ const profile = {
|
||||||
const ctx = {
|
const ctx = {
|
||||||
data: {},
|
data: {},
|
||||||
opt: {
|
opt: {
|
||||||
linesOfStops: false
|
linesOfStops: false,
|
||||||
|
subStops: true,
|
||||||
|
entrances: true,
|
||||||
},
|
},
|
||||||
profile
|
profile
|
||||||
}
|
}
|
||||||
|
@ -64,8 +67,7 @@ test('parses a POI correctly', (t) => {
|
||||||
t.end()
|
t.end()
|
||||||
})
|
})
|
||||||
|
|
||||||
test('parses a stop correctly', (t) => {
|
const fooBusStop = {
|
||||||
const input = {
|
|
||||||
type: 'S',
|
type: 'S',
|
||||||
name: 'Foo bus stop',
|
name: 'Foo bus stop',
|
||||||
lid: 'a=b@L=foo%20stop',
|
lid: 'a=b@L=foo%20stop',
|
||||||
|
@ -73,7 +75,8 @@ test('parses a stop correctly', (t) => {
|
||||||
pCls: 123
|
pCls: 123
|
||||||
}
|
}
|
||||||
|
|
||||||
const stop = parse(ctx, input)
|
test('parses a stop correctly', (t) => {
|
||||||
|
const stop = parse(ctx, fooBusStop)
|
||||||
t.deepEqual(stop, {
|
t.deepEqual(stop, {
|
||||||
type: 'stop',
|
type: 'stop',
|
||||||
id: 'foo stop',
|
id: 'foo stop',
|
||||||
|
@ -87,13 +90,13 @@ test('parses a stop correctly', (t) => {
|
||||||
products: [123]
|
products: [123]
|
||||||
})
|
})
|
||||||
|
|
||||||
const withoutLoc = parse(ctx, omit(input, ['crd']))
|
const withoutLoc = parse(ctx, omit(fooBusStop, ['crd']))
|
||||||
t.equal(withoutLoc.location, null)
|
t.equal(withoutLoc.location, null)
|
||||||
|
|
||||||
const mainMast = parse(ctx, {...input, isMainMast: true})
|
const mainMast = parse(ctx, {...fooBusStop, isMainMast: true})
|
||||||
t.equal(mainMast.type, 'station')
|
t.equal(mainMast.type, 'station')
|
||||||
|
|
||||||
const meta = parse(ctx, {...input, meta: 1})
|
const meta = parse(ctx, {...fooBusStop, meta: 1})
|
||||||
t.equal(meta.isMeta, true)
|
t.equal(meta.isMeta, true)
|
||||||
|
|
||||||
const lineA = {id: 'a'}
|
const lineA = {id: 'a'}
|
||||||
|
@ -101,7 +104,7 @@ test('parses a stop correctly', (t) => {
|
||||||
...ctx,
|
...ctx,
|
||||||
opt: {...ctx.opt, linesOfStops: true}
|
opt: {...ctx.opt, linesOfStops: true}
|
||||||
}, {
|
}, {
|
||||||
...input, lines: [lineA]
|
...fooBusStop, lines: [lineA]
|
||||||
})
|
})
|
||||||
t.deepEqual(withLines.lines, [lineA])
|
t.deepEqual(withLines.lines, [lineA])
|
||||||
|
|
||||||
|
@ -119,3 +122,60 @@ test('falls back to coordinates from `lid`', (t) => {
|
||||||
t.equal(location.longitude, 12.345678)
|
t.equal(location.longitude, 12.345678)
|
||||||
t.end()
|
t.end()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('handles recursive references properly', (t) => {
|
||||||
|
const southernInput = {
|
||||||
|
type: 'S',
|
||||||
|
name: 'Southern Platform',
|
||||||
|
lid: 'a=b@L=southern-platform',
|
||||||
|
crd: {x: 22222222, y: 11111111},
|
||||||
|
// This doesn't make sense semantically, but we test if
|
||||||
|
// `parseLocation` falls into an endless recursive loop.
|
||||||
|
stopLocL: [1]
|
||||||
|
}
|
||||||
|
const northernInput = {
|
||||||
|
type: 'S',
|
||||||
|
name: 'Northern Platform',
|
||||||
|
lid: 'a=b@L=northern-platform',
|
||||||
|
crd: {x: 44444444, y: 33333333}
|
||||||
|
}
|
||||||
|
const common = {locL: [southernInput, northernInput]}
|
||||||
|
const _ctx = {...ctx, res: {common}}
|
||||||
|
|
||||||
|
const northernExpected = {
|
||||||
|
type: 'stop',
|
||||||
|
id: 'northern-platform',
|
||||||
|
name: 'northern platform', // lower-cased!
|
||||||
|
location: {
|
||||||
|
type: 'location',
|
||||||
|
id: 'northern-platform',
|
||||||
|
latitude: 33.333333, longitude: 44.444444
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const southernExpected = {
|
||||||
|
type: 'stop',
|
||||||
|
id: 'southern-platform',
|
||||||
|
name: 'southern platform', // lower-cased!
|
||||||
|
location: {
|
||||||
|
type: 'location',
|
||||||
|
id: 'southern-platform',
|
||||||
|
latitude: 11.111111, longitude: 22.222222
|
||||||
|
},
|
||||||
|
stops: [northernExpected]
|
||||||
|
}
|
||||||
|
|
||||||
|
const {entrances} = parse(_ctx, {
|
||||||
|
...fooBusStop,
|
||||||
|
entryLocL: [0]
|
||||||
|
})
|
||||||
|
t.deepEqual(entrances, [southernExpected.location])
|
||||||
|
|
||||||
|
const {type, stops} = parse(_ctx, {
|
||||||
|
...fooBusStop,
|
||||||
|
stopLocL: [0]
|
||||||
|
})
|
||||||
|
t.equal(type, 'stop')
|
||||||
|
t.deepEqual(stops, [southernExpected])
|
||||||
|
|
||||||
|
t.end()
|
||||||
|
})
|
||||||
|
|
Loading…
Add table
Reference in a new issue