merge master into nah.sh

This commit is contained in:
Jannis R 2018-03-17 17:21:18 +01:00
commit 0987383c72
No known key found for this signature in database
GPG key ID: 0FE83946296A88A5
11 changed files with 182 additions and 118 deletions

View file

@ -9,7 +9,6 @@ const getStations = require('vbb-stations')
const _createParseLine = require('../../parse/line') const _createParseLine = require('../../parse/line')
const _parseLocation = require('../../parse/location') const _parseLocation = require('../../parse/location')
const _createParseJourney = require('../../parse/journey') const _createParseJourney = require('../../parse/journey')
const _createParseStopover = require('../../parse/stopover')
const _createParseDeparture = require('../../parse/departure') const _createParseDeparture = require('../../parse/departure')
const _formatStation = require('../../format/station') const _formatStation = require('../../format/station')
const createParseBitmask = require('../../parse/products-bitmask') const createParseBitmask = require('../../parse/products-bitmask')
@ -99,20 +98,6 @@ const createParseJourney = (profile, stations, lines, remarks) => {
return parseJourneyWithTickets return parseJourneyWithTickets
} }
const createParseStopover = (profile, stations, lines, remarks, connection) => {
const parseStopover = _createParseStopover(profile, stations, lines, remarks, connection)
const parseStopoverWithShorten = (st) => {
const res = parseStopover(st)
if (res.station && res.station.name) {
res.station.name = shorten(res.station.name)
}
return res
}
return parseStopoverWithShorten
}
const createParseDeparture = (profile, stations, lines, remarks) => { const createParseDeparture = (profile, stations, lines, remarks) => {
const parseDeparture = _createParseDeparture(profile, stations, lines, remarks) const parseDeparture = _createParseDeparture(profile, stations, lines, remarks)
@ -184,7 +169,6 @@ const vbbProfile = {
parseProducts: createParseBitmask(modes.allProducts, defaultProducts), parseProducts: createParseBitmask(modes.allProducts, defaultProducts),
parseJourney: createParseJourney, parseJourney: createParseJourney,
parseDeparture: createParseDeparture, parseDeparture: createParseDeparture,
parseStopover: createParseStopover,
formatStation, formatStation,
formatProducts, formatProducts,

View file

@ -1,7 +1,7 @@
{ {
"name": "hafas-client", "name": "hafas-client",
"description": "JavaScript client for HAFAS public transport APIs.", "description": "JavaScript client for HAFAS public transport APIs.",
"version": "2.4.0", "version": "2.4.2",
"main": "index.js", "main": "index.js",
"files": [ "files": [
"index.js", "index.js",
@ -35,10 +35,10 @@
"capture-stack-trace": "^1.0.0", "capture-stack-trace": "^1.0.0",
"fetch-ponyfill": "^6.0.0", "fetch-ponyfill": "^6.0.0",
"lodash": "^4.17.5", "lodash": "^4.17.5",
"luxon": "^0.5.6", "luxon": "^0.5.8",
"p-throttle": "^1.1.0", "p-throttle": "^1.1.0",
"pinkie-promise": "^2.0.1", "pinkie-promise": "^2.0.1",
"query-string": "^5.1.0", "query-string": "^6.0.0",
"slugg": "^1.2.0", "slugg": "^1.2.0",
"vbb-parse-line": "^0.3.1", "vbb-parse-line": "^0.3.1",
"vbb-parse-ticket": "^0.2.1", "vbb-parse-ticket": "^0.2.1",
@ -52,7 +52,7 @@
"is-roughly-equal": "^0.1.0", "is-roughly-equal": "^0.1.0",
"tap-spec": "^4.1.1", "tap-spec": "^4.1.1",
"tape": "^4.8.0", "tape": "^4.8.0",
"tape-promise": "^2.0.1", "tape-promise": "^3.0.0",
"validate-fptf": "^1.2.1", "validate-fptf": "^1.2.1",
"vbb-stations-autocomplete": "^3.1.0" "vbb-stations-autocomplete": "^3.1.0"
}, },

View file

@ -22,14 +22,14 @@ const createParseDeparture = (profile, stations, lines, remarks) => {
} }
// todo: res.trip from rawLine.prodCtx.num? // todo: res.trip from rawLine.prodCtx.num?
// todo: DRY with parseStopover
// todo: DRY with parseJourneyLeg
if (d.stbStop.dTimeR && d.stbStop.dTimeS) { if (d.stbStop.dTimeR && d.stbStop.dTimeS) {
const realtime = profile.parseDateTime(profile, d.date, d.stbStop.dTimeR) const realtime = profile.parseDateTime(profile, d.date, d.stbStop.dTimeR)
const planned = profile.parseDateTime(profile, d.date, d.stbStop.dTimeS) const planned = profile.parseDateTime(profile, d.date, d.stbStop.dTimeS)
res.delay = Math.round((realtime - planned) / 1000) res.delay = Math.round((realtime - planned) / 1000)
} else res.delay = null } else res.delay = null
// todo: follow public-transport/friendly-public-transport-format#27 here
// see also derhuerst/vbb-rest#19
if (d.stbStop.aCncl || d.stbStop.dCncl) { if (d.stbStop.aCncl || d.stbStop.dCncl) {
res.cancelled = true res.cancelled = true
Object.defineProperty(res, 'canceled', {value: true}) Object.defineProperty(res, 'canceled', {value: true})

View file

@ -47,7 +47,7 @@ const createParseJourneyLeg = (profile, stations, lines, remarks) => {
if (pt.arr.aPlatfS) res.arrivalPlatform = pt.arr.aPlatfS if (pt.arr.aPlatfS) res.arrivalPlatform = pt.arr.aPlatfS
if (passed && pt.jny.stopL) { if (passed && pt.jny.stopL) {
const parse = profile.parseStopover(profile, stations, lines, remarks, j) const parse = profile.parseStopover(profile, stations, lines, remarks, j.date)
const passedStations = pt.jny.stopL.map(parse) const passedStations = pt.jny.stopL.map(parse)
// filter stations the train passes without stopping, as this doesn't comply with fptf (yet) // filter stations the train passes without stopping, as this doesn't comply with fptf (yet)
res.passed = passedStations.filter((x) => !x.passBy) res.passed = passedStations.filter((x) => !x.passBy)
@ -71,21 +71,21 @@ const createParseJourneyLeg = (profile, stations, lines, remarks) => {
} }
} }
// todo: follow public-transport/friendly-public-transport-format#27 here // todo: DRY with parseDeparture
// see also derhuerst/vbb-rest#19 // todo: DRY with parseStopover
if (pt.arr.aCncl) { if (pt.arr.aCncl || pt.dep.dCncl) {
res.cancelled = true res.cancelled = true
Object.defineProperty(res, 'canceled', {value: true}) Object.defineProperty(res, 'canceled', {value: true})
res.arrival = res.arrivalPlatform = res.arrivalDelay = null if (pt.arr.aCncl) {
const arr = profile.parseDateTime(profile, j.date, pt.arr.aTimeS) res.arrival = res.arrivalPlatform = res.arrivalDelay = null
res.formerScheduledArrival = arr.toISO() const arr = profile.parseDateTime(profile, j.date, pt.arr.aTimeS)
} res.formerScheduledArrival = arr.toISO()
if (pt.dep.dCncl) { }
res.cancelled = true if (pt.dep.dCncl) {
Object.defineProperty(res, 'canceled', {value: true}) res.departure = res.departurePlatform = res.departureDelay = null
res.departure = res.departurePlatform = res.departureDelay = null const dep = profile.parseDateTime(profile, j.date, pt.dep.dTimeS)
const dep = profile.parseDateTime(profile, j.date, pt.dep.dTimeS) res.formerScheduledDeparture = dep.toISO()
res.formerScheduledDeparture = dep.toISO() }
} }
return res return res

View file

@ -19,7 +19,7 @@ const parseLocation = (profile, l, lines) => {
const station = { const station = {
type: 'station', type: 'station',
id: l.extId, id: l.extId,
name: l.name, name: profile.parseStationName(l.name),
location: 'number' === typeof res.latitude ? res : null location: 'number' === typeof res.latitude ? res : null
} }

View file

@ -9,32 +9,7 @@ const createParseMovement = (profile, locations, lines, remarks) => {
// todo: what is m.ani.proc[n]? wut? // todo: what is m.ani.proc[n]? wut?
// todo: how does m.ani.poly work? // todo: how does m.ani.poly work?
const parseMovement = (m) => { const parseMovement = (m) => {
const parseNextStop = (s) => { const pStopover = profile.parseStopover(profile, locations, lines, remarks, m.date)
const dep = s.dTimeR || s.dTimeS
? profile.parseDateTime(profile, m.date, s.dTimeR || s.dTimeS)
: null
const arr = s.aTimeR || s.aTimeS
? profile.parseDateTime(profile, m.date, s.aTimeR || s.aTimeS)
: null
const res = {
station: locations[s.locX],
departure: dep ? dep.toISO() : null,
arrival: arr ? arr.toISO() : null
}
if (m.dTimeR && m.dTimeS) {
const plannedDep = profile.parseDateTime(profile, m.date, s.dTimeS)
res.departureDelay = Math.round((dep - plannedDep) / 1000)
} else res.departureDelay = null
if (m.aTimeR && m.aTimeS) {
const plannedArr = profile.parseDateTime(profile, m.date, s.aTimeS)
res.arrivalDelay = Math.round((arr - plannedArr) / 1000)
} else res.arrivalDelay = null
return res
}
const res = { const res = {
direction: profile.parseStationName(m.dirTxt), direction: profile.parseStationName(m.dirTxt),
@ -45,7 +20,7 @@ const createParseMovement = (profile, locations, lines, remarks) => {
latitude: m.pos.y / 1000000, latitude: m.pos.y / 1000000,
longitude: m.pos.x / 1000000 longitude: m.pos.x / 1000000
} : null, } : null,
nextStops: m.stopL.map(parseNextStop), nextStops: m.stopL.map(pStopover),
frames: [] frames: []
} }

View file

@ -2,32 +2,38 @@
// todo: arrivalDelay, departureDelay or only delay ? // todo: arrivalDelay, departureDelay or only delay ?
// todo: arrivalPlatform, departurePlatform // todo: arrivalPlatform, departurePlatform
const createParseStopover = (profile, stations, lines, remarks, connection) => { const createParseStopover = (profile, stations, lines, remarks, date) => {
const parseStopover = (st) => { const parseStopover = (st) => {
const res = { const res = {
station: stations[parseInt(st.locX)] || null station: stations[parseInt(st.locX)] || null
} }
if (st.aTimeR || st.aTimeS) { if (st.aTimeR || st.aTimeS) {
const arr = profile.parseDateTime(profile, connection.date, st.aTimeR || st.aTimeS) const arr = profile.parseDateTime(profile, date, st.aTimeR || st.aTimeS)
res.arrival = arr.toISO() res.arrival = arr.toISO()
} }
if (st.dTimeR || st.dTimeS) { if (st.dTimeR || st.dTimeS) {
const dep = profile.parseDateTime(profile, connection.date, st.dTimeR || st.dTimeS) const dep = profile.parseDateTime(profile, date, st.dTimeR || st.dTimeS)
res.departure = dep.toISO() res.departure = dep.toISO()
} }
// mark stations the train passes without stopping // mark stations the train passes without stopping
if(st.dInS === false && st.aOutS === false) res.passBy = true if(st.dInS === false && st.aOutS === false) res.passBy = true
// todo: follow public-transport/friendly-public-transport-format#27 here // todo: DRY with parseDeparture
// see also derhuerst/vbb-rest#19 // todo: DRY with parseJourneyLeg
if (st.aCncl) { if (st.aCncl || st.dCncl) {
res.cancelled = true res.cancelled = true
res.arrival = null Object.defineProperty(res, 'canceled', {value: true})
} if (st.aCncl) {
if (st.dCncl) { res.arrival = res.arrivalDelay = null
res.cancelled = true const arr = profile.parseDateTime(profile, d.date, st.aTimeS)
res.departure = null res.formerScheduledArrival = arr.toISO()
}
if (st.dCncl) {
res.departure = res.departureDelay = null
const arr = profile.parseDateTime(profile, d.date, st.dTimeS)
res.formerScheduledDeparture = arr.toISO()
}
} }
return res return res

View file

@ -218,18 +218,44 @@ test('Berlin Jungfernheide to ATZE Musiktheater', co(function* (t) {
t.end() t.end()
})) }))
test('Berlin Hbf to München Hbf with stopover at Hannover Hbf', co(function* (t) { test('journeys: via works with detour', co(function* (t) {
const [journey] = yield client.journeys(berlinHbf, münchenHbf, { // Going from Westhafen to Wedding via Württembergalle without detour
via: hannoverHbf, // is currently impossible. We check if the routing engine computes a detour.
results: 1 const westhafen = '008089116'
const wedding = '008089131'
const württembergallee = '731084'
const [journey] = yield client.journeys(westhafen, wedding, {
via: württembergallee,
results: 1,
when,
passedStations: true
}) })
const i = journey.legs.findIndex(leg => leg.destination.id === hannoverHbf) t.ok(journey)
t.ok(i >= 0, 'no leg with Hannover Hbf as destination')
const nextLeg = journey.legs[i + 1] const l = journey.legs.some(l => l.passed && l.passed.some(p => p.station.id === württembergallee))
t.ok(nextLeg) t.ok(l, 'Württembergalle is not being passed')
t.equal(nextLeg.origin.id, hannoverHbf)
t.end()
}))
test('journeys: via works without detour', co(function* (t) {
// When going from Ruhleben to Zoo via Kastanienallee, there is *no need*
// to change trains / no need for a "detour".
const ruhleben = '000731058'
const zoo = '008010406'
const kastanienallee = '730983'
const [journey] = yield client.journeys(ruhleben, zoo, {
via: kastanienallee,
results: 1,
when,
passedStations: true
})
t.ok(journey)
const l = journey.legs.some(l => l.passed && l.passed.some(p => p.station.id === kastanienallee))
t.ok(l, 'Kastanienallee is not being passed')
t.end() t.end()
})) }))

View file

@ -189,22 +189,47 @@ test('Kloster Unser Lieben Frauen to Magdeburg Hbf', co(function*(t) {
t.end() t.end()
})) }))
test('Magdeburg-Buckau to Magdeburg-Neustadt with stopover at Magdeburg Hbf', co(function*(t) { test('journeys: via works with detour', co(function* (t) {
const magdeburgBuckau = '8013456' // Going from Magdeburg, Hasselbachplatz (Sternstr.) (Tram/Bus) to Stendal via Dessau without detour
const magdeburgNeustadt = '8010226' // is currently impossible. We check if the routing engine computes a detour.
const magdeburgHbf = '8010224' const hasselbachplatzSternstrasse = '000006545'
const [journey] = yield client.journeys(magdeburgBuckau, magdeburgNeustadt, { const stendal = '008010334'
via: magdeburgHbf, const dessau = '008010077'
const dessauPassed = '8010077'
const [journey] = yield client.journeys(hasselbachplatzSternstrasse, stendal, {
via: dessau,
results: 1, results: 1,
when when,
passedStations: true
}) })
const i1 = journey.legs.findIndex(leg => leg.destination.id === magdeburgHbf) t.ok(journey)
t.ok(i1 >= 0, 'no leg with Magdeburg Hbf as destination')
const i2 = journey.legs.findIndex(leg => leg.origin.id === magdeburgHbf) const l = journey.legs.some(l => l.passed && l.passed.some(p => p.station.id === dessauPassed))
t.ok(i2 >= 0, 'no leg with Magdeburg Hbf as origin') t.ok(l, 'Dessau is not being passed')
t.ok(i2 > i1, 'leg with Magdeburg Hbf as origin must be after leg to it')
t.end()
}))
test('journeys: via works without detour', co(function* (t) {
// When going from Magdeburg, Hasselbachplatz (Sternstr.) (Tram/Bus) to Magdeburg, Universität via Magdeburg, Breiter Weg, there is *no need*
// to change trains / no need for a "detour".
const hasselbachplatzSternstrasse = '000006545'
const universitaet = '000019686'
const breiterWeg = '000013519'
const breiterWegPassed = '13519'
const [journey] = yield client.journeys(hasselbachplatzSternstrasse, universitaet, {
via: breiterWeg,
results: 1,
when,
passedStations: true
})
t.ok(journey)
const l = journey.legs.some(l => l.passed && l.passed.some(p => p.station.id === breiterWegPassed))
t.ok(l, 'Magdeburg, Breiter Weg is not being passed')
t.end() t.end()
})) }))

View file

@ -258,19 +258,47 @@ test('Albertina to Salzburg Hbf', co(function* (t) {
t.end() t.end()
})) }))
test('Wien to Klagenfurt Hbf with stopover at Salzburg Hbf', co(function* (t) { test('journeys: via works with detour', co(function* (t) {
const [journey] = yield client.journeys(wien, klagenfurtHbf, { // Going from Stephansplatz to Schottenring via Donauinsel without detour
via: salzburgHbf, // is currently impossible. We check if the routing engine computes a detour.
const stephansplatz = '001390167'
const schottenring = '001390163'
const donauinsel = '001392277'
const donauinselPassed = '922001'
const [journey] = yield client.journeys(stephansplatz, schottenring, {
via: donauinsel,
results: 1, results: 1,
when when,
passedStations: true
}) })
const i1 = journey.legs.findIndex(leg => leg.destination.id === salzburgHbf) t.ok(journey)
t.ok(i1 >= 0, 'no leg with Salzburg Hbf as destination')
const i2 = journey.legs.findIndex(leg => leg.origin.id === salzburgHbf) const l = journey.legs.some(l => l.passed && l.passed.some(p => p.station.id === donauinselPassed))
t.ok(i2 >= 0, 'no leg with Salzburg Hbf as origin') t.ok(l, 'Donauinsel is not being passed')
t.ok(i2 > i1, 'leg with Salzburg Hbf as origin must be after leg to it')
t.end()
}))
test('journeys: via works without detour', co(function* (t) {
// When going from Karlsplatz to Praterstern via Museumsquartier, there is *no need*
// to change trains / no need for a "detour".
const karlsplatz = '001390461'
const praterstern = '001290201'
const museumsquartier = '001390171'
const museumsquartierPassed = '901014'
const [journey] = yield client.journeys(karlsplatz, praterstern, {
via: museumsquartier,
results: 1,
when,
passedStations: true
})
t.ok(journey)
const l = journey.legs.some(l => l.passed && l.passed.some(p => p.station.id === museumsquartierPassed))
t.ok(l, 'Weihburggasse is not being passed')
t.end() t.end()
})) }))

View file

@ -300,27 +300,47 @@ test('journeys  station to POI', co(function* (t) {
t.end() t.end()
})) }))
test('journeys: via works with detour', co(function* (t) {
// Going from Westhafen to Wedding via Württembergalle without detour
test('journeys with stopover', co(function* (t) { // is currently impossible. We check if the routing engine computes a detour.
const halleschesTor = '900000012103' const westhafen = '900000001201'
const leopoldplatz = '900000009102' const wedding = '900000009104'
const [journey] = yield client.journeys(spichernstr, halleschesTor, { const württembergallee = '900000026153'
via: leopoldplatz, const [journey] = yield client.journeys(westhafen, wedding, {
results: 1 via: württembergallee,
results: 1,
when,
passedStations: true
}) })
const i = journey.legs.findIndex(leg => leg.destination.id === leopoldplatz) t.ok(journey)
t.ok(i >= 0, 'no leg with Leopoldplatz as destination')
const nextLeg = journey.legs[i + 1] const l = journey.legs.some(l => l.passed && l.passed.some(p => p.station.id === württembergallee))
t.ok(nextLeg) t.ok(l, 'Württembergalle is not being passed')
t.equal(nextLeg.origin.id, leopoldplatz)
t.end() t.end()
})) }))
test('journeys: via works without detour', co(function* (t) {
// When going from Ruhleben to Zoo via Kastanienallee, there is *no need*
// to change trains / no need for a "detour".
const ruhleben = '900000025202'
const zoo = '900000023201'
const kastanienallee = '900000020152'
const [journey] = yield client.journeys(ruhleben, zoo, {
via: kastanienallee,
results: 1,
when,
passedStations: true
})
t.ok(journey)
const l = journey.legs.some(l => l.passed && l.passed.some(p => p.station.id === kastanienallee))
t.ok(l, 'Kastanienallee is not being passed')
t.end()
}))
test('departures', co(function* (t) { test('departures', co(function* (t) {
const deps = yield client.departures(spichernstr, {duration: 5, when}) const deps = yield client.departures(spichernstr, {duration: 5, when})