From 79db55d99ce2d37320a59bf4cbf46736103a8c76 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Sat, 11 Nov 2017 22:35:41 +0100 Subject: [PATCH] split parse.js --- index.js | 23 ++--- package.json | 2 +- parse.js | 223 --------------------------------------------- parse/date-time.js | 16 ++++ parse/departure.js | 37 ++++++++ parse/index.js | 13 +++ parse/journey.js | 108 ++++++++++++++++++++++ parse/line.js | 18 ++++ parse/location.js | 24 +++++ parse/movement.js | 49 ++++++++++ parse/nearby.js | 16 ++++ parse/operator.js | 13 +++ parse/remark.js | 7 ++ parse/stopover.js | 23 +++++ 14 files changed, 337 insertions(+), 235 deletions(-) delete mode 100644 parse.js create mode 100644 parse/date-time.js create mode 100644 parse/departure.js create mode 100644 parse/index.js create mode 100644 parse/journey.js create mode 100644 parse/line.js create mode 100644 parse/location.js create mode 100644 parse/movement.js create mode 100644 parse/nearby.js create mode 100644 parse/operator.js create mode 100644 parse/remark.js create mode 100644 parse/stopover.js diff --git a/index.js b/index.js index 51b58929..0953a860 100644 --- a/index.js +++ b/index.js @@ -4,7 +4,10 @@ const Promise = require('pinkie-promise') const {fetch} = require('fetch-ponyfill')({Promise}) const {stringify} = require('query-string') -const parse = require('./parse') +const parseLocation = require('./parse/location') +const parseLine = require('./parse/line') +const parseRemark = require('./parse/remark') +const parseOperator = require('./parse/operator') @@ -12,14 +15,12 @@ const id = (x) => x const defaults = { onBody: id, onReq: id, - onLocation: parse.location, - onLine: parse.line, - onRemark: parse.remark, - onOperator: parse.operator + parseLocation: parseLocation, + parseLine: parseLine, + parseRemark: parseRemark, + parseOperator: parseOperator } - - const hafasError = (err) => { err.isHafasError = true return err @@ -60,10 +61,10 @@ const createRequest = (opt) => { const d = b.svcResL[0].res const c = d.common || {} - if (Array.isArray(c.locL)) d.locations = c.locL.map(opt.onLocation) - if (Array.isArray(c.prodL)) d.lines = c.prodL.map(opt.onLine) - if (Array.isArray(c.remL)) d.remarks = c.remL.map(opt.onRemark) - if (Array.isArray(c.opL)) d.operators = c.opL.map(opt.onOperator) + if (Array.isArray(c.locL)) d.locations = c.locL.map(opt.parseLocation) + if (Array.isArray(c.prodL)) d.lines = c.prodL.map(opt.parseLine) + if (Array.isArray(c.remL)) d.remarks = c.remL.map(opt.parseRemark) + if (Array.isArray(c.opL)) d.operators = c.opL.map(opt.parseOperator) return d }) } diff --git a/package.json b/package.json index 031d9334..9ea12e46 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "main": "index.js", "files": [ "index.js", - "parse.js", + "parse", "stringify.js" ], "author": "Jannis R ", diff --git a/parse.js b/parse.js deleted file mode 100644 index f34040a1..00000000 --- a/parse.js +++ /dev/null @@ -1,223 +0,0 @@ -'use strict' - -const moment = require('moment-timezone') -const slugg = require('slugg') - - - -const dateTime = (tz, date, time) => { - let offset = 0 // in days - if (time.length > 6) { - offset = +time.slice(0, -6) - time = time.slice(-6) - } - return moment.tz(date + 'T' + time, tz) - .add(offset, 'days') -} - - - -const types = {P: 'poi', S: 'station', A: 'address'} -// todo: what is s.rRefL? -const location = (l) => { - const type = types[l.type] || 'unknown' - const result = { - type, - name: l.name, - coordinates: l.crd ? { - latitude: l.crd.y / 1000000, - longitude: l.crd.x / 1000000 - } : null - } - if (type === 'poi' || type === 'station') result.id = l.extId - if ('pCls' in l) result.products = l.pCls - return result -} - - - -// todo: what is p.number vs p.line? -// todo: what is p.icoX? -// todo: what is p.oprX? -const line = (p) => { - if (!p) return null - const result = {type: 'line', name: p.line || p.name} - if (p.cls) result.class = p.cls - if (p.prodCtx) { - result.productCode = +p.prodCtx.catCode - result.productName = p.prodCtx.catOutS - } - return result -} - - - -const remark = (r) => null // todo - - - -const operator = (a) => ({ - type: 'operator', - id: slugg(a.name), - name: a.name -}) - - - -// s = stations, ln = lines, r = remarks, c = connection -const stopover = (tz, s, ln, r, c) => (st) => { - const result = {station: s[parseInt(st.locX)]} - if (st.aTimeR || st.aTimeS) { - result.arrival = dateTime(tz, c.date, st.aTimeR || st.aTimeS).format() - } - if (st.dTimeR || st.dTimeS) { - result.departure = dateTime(tz, c.date, st.dTimeR || st.dTimeS).format() - } - return result -} - -// todo: finish parseRemark first -// s = stations, ln = lines, r = remarks, c = connection -const applyRemark = (s, ln, r, c) => (rm) => null - -// todo: pt.sDays -// todo: pt.dep.dProgType, pt.arr.dProgType -// todo: what is pt.jny.dirFlg? -// todo: how does pt.freq work? -// tz = timezone, s = stations, ln = lines, r = remarks, c = connection -const part = (tz, s, ln, r, c) => (pt) => { - const result = { - origin: Object.assign({}, s[parseInt(pt.dep.locX)]) - , destination: Object.assign({}, s[parseInt(pt.arr.locX)]) - , departure: dateTime(tz, c.date, pt.dep.dTimeR || pt.dep.dTimeS).format() - , arrival: dateTime(tz, c.date, pt.arr.aTimeR || pt.arr.aTimeS).format() - } - if (pt.dep.dTimeR && pt.dep.dTimeS) { - const realtime = dateTime(tz, c.date, pt.dep.dTimeR) - const planned = dateTime(tz, c.date, pt.dep.dTimeS) - result.delay = Math.round((realtime - planned) / 1000) - } - - if (pt.type === 'WALK') result.mode = 'walking' - else if (pt.type === 'JNY') { - result.id = pt.jny.jid - result.line = ln[parseInt(pt.jny.prodX)] - result.direction = pt.jny.dirTxt // todo: parse this - - if (pt.dep.dPlatfS) result.departurePlatform = pt.dep.dPlatfS - if (pt.arr.aPlatfS) result.arrivalPlatform = pt.arr.aPlatfS - - if (pt.jny.stopL) result.passed = pt.jny.stopL.map(stopover(tz, s, ln, r, c)) - if (Array.isArray(pt.jny.remL)) - pt.jny.remL.forEach(applyRemark(s, ln, r, c)) - - if (pt.jny.freq && pt.jny.freq.jnyL) - result.alternatives = pt.jny.freq.jnyL - .filter((a) => a.stopL[0].locX === pt.dep.locX) - .map((a) => ({ - line: ln[parseInt(a.prodX)], - when: dateTime(tz, c.date, a.stopL[0].dTimeS).format() - })) - } - return result -} - -// todo: c.sDays -// todo: c.dep.dProgType, c.arr.dProgType -// todo: c.conSubscr -// todo: c.trfRes x vbb-parse-ticket -// todo: use computed information from part -// s = stations, ln = lines, r = remarks, p = parsePart -const journey = (tz, s, ln, r, p = part) => (c) => { - const parts = c.secL.map(p(tz, s, ln, r, c)) - return { - parts - , origin: parts[0].origin - , destination: parts[parts.length - 1].destination - , departure: parts[0].departure - , arrival: parts[parts.length - 1].arrival - } -} - -// todos from derhuerst/hafas-client#2 -// - stdStop.dCncl -// - stdStop.dPlatfS, stdStop.dPlatfR -// todo: what is d.jny.dirFlg? -// todo: d.stbStop.dProgType -// tz = timezone, s = stations, ln = lines, r = remarks -const departure = (tz, s, ln, r) => (d) => { - const result = { - ref: d.jid - , station: s[parseInt(d.stbStop.locX)] - , when: dateTime(tz, d.date, d.stbStop.dTimeR || d.stbStop.dTimeS).format() - , direction: d.dirTxt - , line: ln[parseInt(d.prodX)] - , remarks: d.remL ? d.remL.map((rm) => r[parseInt(rm.remX)]) : null - , trip: +d.jid.split('|')[1] - } - if (d.stbStop.dTimeR && d.stbStop.dTimeS) { - const realtime = dateTime(tz, d.date, d.stbStop.dTimeR) - const planned = dateTime(tz, d.date, d.stbStop.dTimeS) - result.delay = Math.round((realtime - planned) / 1000) - } else result.delay = null - return result -} - -// todo: remarks -// todo: lines -// todo: what is s.pCls? -// todo: what is s.wt? -// todo: what is s.dur? -const nearby = (n) => { - const result = location(n) - result.distance = n.dist - return result -} - -// todo: what is m.dirGeo? maybe the speed? -// todo: what is m.stopL? -// todo: what is m.proc? wut? -// todo: what is m.pos? -// todo: what is m.ani.dirGeo[n]? maybe the speed? -// todo: what is m.ani.proc[n]? wut? -// todo: how does m.ani.poly work? -// tz = timezone, l = locations, ln = lines, r = remarks -const movement = (tz, l, ln, r) => (m) => { - const result = { - direction: m.dirTxt - , line: ln[m.prodX] - , coordinates: m.pos ? { - latitude: m.pos.y / 1000000, - longitude: m.pos.x / 1000000 - } : null - , nextStops: m.stopL.map((s) => ({ - station: l[s.locX] - , departure: s.dTimeR || s.dTimeS - ? dateTime(tz, m.date, s.dTimeR || s.dTimeS).format() - : null - , arrival: s.aTimeR || s.aTimeS - ? dateTime(tz, m.date, s.aTimeR || s.aTimeS).format() - : null - })) - , frames: [] - } - if (m.ani && Array.isArray(m.ani.mSec)) - for (let i = 0; i < m.ani.mSec.length; i++) - result.frames.push({ - origin: l[m.ani.fLocX[i]], - destination: l[m.ani.tLocX[i]], - t: m.ani.mSec[i] - }) - return result -} - - - -module.exports = { - dateTime, - location, line, remark, operator, - stopover, applyRemark, part, journey, - departure, - nearby, - movement -} diff --git a/parse/date-time.js b/parse/date-time.js new file mode 100644 index 00000000..bd616c5a --- /dev/null +++ b/parse/date-time.js @@ -0,0 +1,16 @@ +'use strict' + +const moment = require('moment-timezone') + +const parseDateTime = (tz, date, time) => { + let offset = 0 // in days + if (time.length > 6) { + offset = +time.slice(0, -6) + time = time.slice(-6) + } + + return moment.tz(date + 'T' + time, tz) + .add(offset, 'days') +} + +module.exports = parseDateTime diff --git a/parse/departure.js b/parse/departure.js new file mode 100644 index 00000000..a6932dcb --- /dev/null +++ b/parse/departure.js @@ -0,0 +1,37 @@ +'use strict' + +const parseDateTime = require('./date-time') + +// todos from derhuerst/hafas-client#2 +// - stdStop.dCncl +// - stdStop.dPlatfS, stdStop.dPlatfR +// todo: what is d.jny.dirFlg? +// todo: d.stbStop.dProgType + +// tz = timezone, s = stations, ln = lines, r = remarks +const createParseDeparture = (tz, s, ln, r) => { + const parseDeparture = (d) => { + const when = parseDateTime(tz, d.date, d.stbStop.dTimeR || d.stbStop.dTimeS) + const result = { + ref: d.jid + , station: s[parseInt(d.stbStop.locX)] + , when: when.format() + , direction: d.dirTxt + , line: ln[parseInt(d.prodX)] + , remarks: d.remL ? d.remL.map((rm) => r[parseInt(rm.remX)]) : null + , trip: +d.jid.split('|')[1] + } + + if (d.stbStop.dTimeR && d.stbStop.dTimeS) { + const realtime = parseDateTime(tz, d.date, d.stbStop.dTimeR) + const planned = parseDateTime(tz, d.date, d.stbStop.dTimeS) + result.delay = Math.round((realtime - planned) / 1000) + } else result.delay = null + + return result + } + + return parseDeparture +} + +module.exports = createParseDeparture diff --git a/parse/index.js b/parse/index.js new file mode 100644 index 00000000..7c9c35bb --- /dev/null +++ b/parse/index.js @@ -0,0 +1,13 @@ +'use strict' + +module.exports = { + dateTime: require('./date-time'), + location: require('./location'), + line: require('./line'), + remark: require('./remark'), + operator: require('./operator'), + stopover: require('./stopover'), + journey: require('./journey'), + nearby: require('./nearby'), + movement: require('./movement') +} diff --git a/parse/journey.js b/parse/journey.js new file mode 100644 index 00000000..fdfd8eea --- /dev/null +++ b/parse/journey.js @@ -0,0 +1,108 @@ +'use strict' + +const parseDateTime = require('./date-time') + +// s = stations, ln = lines, r = remarks, c = connection +const createParseStopover = (tz, s, ln, r, c) => { + const parseStopover = (st) => { + const res = { + station: s[parseInt(st.locX)] + } + if (st.aTimeR || st.aTimeS) { + const arr = parseDateTime(tz, c.date, st.aTimeR || st.aTimeS) + res.arrival = arr.format() + } + if (st.dTimeR || st.dTimeS) { + const dep = parseDateTime(tz, c.date, st.dTimeR || st.dTimeS) + res.departure = dep.format() + } + return res + } + + return parseStopover +} + +// s = stations, ln = lines, r = remarks, c = connection +const createApplyRemark = (s, ln, r, c) => { + // todo: finish parse/remark.js first + const applyRemark = (rm) => {} + return applyRemark +} + +// tz = timezone, s = stations, ln = lines, r = remarks, c = connection +const createParsePart = (tz, s, ln, r, c) => { + // todo: pt.sDays + // todo: pt.dep.dProgType, pt.arr.dProgType + // todo: what is pt.jny.dirFlg? + // todo: how does pt.freq work? + const parsePart = (pt) => { + const dep = parseDateTime(tz, c.date, pt.dep.dTimeR || pt.dep.dTimeS) + const arr = parseDateTime(tz, c.date, pt.arr.aTimeR || pt.arr.aTimeS) + const res = { + origin: Object.assign({}, s[parseInt(pt.dep.locX)]) // todo: what about null? + , destination: Object.assign({}, s[parseInt(pt.arr.locX)]) // todo: what about null? + , departure: dep.format() + , arrival: dep.format() + } + + if (pt.dep.dTimeR && pt.dep.dTimeS) { + const realtime = parseDateTime(tz, c.date, pt.dep.dTimeR) + const planned = parseDateTime(tz, c.date, pt.dep.dTimeS) + res.delay = Math.round((realtime - planned) / 1000) + } + + if (pt.type === 'WALK') { + res.mode = 'walking' + } else if (pt.type === 'JNY') { + res.id = pt.jny.jid + res.line = ln[parseInt(pt.jny.prodX)] // todo: default null + res.direction = pt.jny.dirTxt // todo: parse this + + if (pt.dep.dPlatfS) res.departurePlatform = pt.dep.dPlatfS + if (pt.arr.aPlatfS) res.arrivalPlatform = pt.arr.aPlatfS + + if (pt.jny.stopL) { + res.passed = pt.jny.stopL.map(createParseStopover(tz, s, ln, r, c)) + } + if (Array.isArray(pt.jny.remL)) { + pt.jny.remL.forEach(createApplyRemark(s, ln, r, c)) + } + + if (pt.jny.freq && pt.jny.freq.jnyL) { + const parseAlternative = (a) => ({ + line: ln[parseInt(a.prodX)], // todo: default null + when: parseDateTime(tz, c.date, a.stopL[0].dTimeS).format() // todo: realtime + }) + res.alternatives = pt.jny.freq.jnyL + .filter((a) => a.stopL[0].locX === pt.dep.locX) + .map(parseAlternative) + } + } + + return res + } + return parsePart +} + +// s = stations, ln = lines, r = remarks, p = createParsePart +const createParseJourney = (tz, s, ln, r, p = createParsePart) => { + // todo: c.sDays + // todo: c.dep.dProgType, c.arr.dProgType + // todo: c.conSubscr + // todo: c.trfRes x vbb-parse-ticket + // todo: use computed information from part + const parseJourney = (c) => { + const parts = c.secL.map(p(tz, s, ln, r, c)) + return { + parts + , origin: parts[0].origin + , destination: parts[parts.length - 1].destination + , departure: parts[0].departure + , arrival: parts[parts.length - 1].arrival + } + } + + return parseJourney +} + +module.exports = createParseJourney diff --git a/parse/line.js b/parse/line.js new file mode 100644 index 00000000..168a9d2f --- /dev/null +++ b/parse/line.js @@ -0,0 +1,18 @@ +'use strict' + +// todo: what is p.number vs p.line? +// todo: what is p.icoX? +// todo: what is p.oprX? +const parseLine = (p) => { + if (!p) return null + + const result = {type: 'line', name: p.line || p.name} + if (p.cls) result.class = p.cls + if (p.prodCtx) { + result.productCode = +p.prodCtx.catCode + result.productName = p.prodCtx.catOutS + } + return result +} + +module.exports = parseLine diff --git a/parse/location.js b/parse/location.js new file mode 100644 index 00000000..d4eff9e6 --- /dev/null +++ b/parse/location.js @@ -0,0 +1,24 @@ +'use strict' + +const types = Object.create(null) +types.P = 'poi' +types.S = 'station' +types.A = 'address' + +// todo: what is s.rRefL? +const parseLocation = (l) => { + const type = types[l.type] || 'unknown' + const result = { + type, + name: l.name, + coordinates: l.crd ? { + latitude: l.crd.y / 1000000, + longitude: l.crd.x / 1000000 + } : null + } + if (type === 'poi' || type === 'station') result.id = l.extId + if ('pCls' in l) result.products = l.pCls + return result +} + +module.exports = parseLocation diff --git a/parse/movement.js b/parse/movement.js new file mode 100644 index 00000000..eb701a38 --- /dev/null +++ b/parse/movement.js @@ -0,0 +1,49 @@ +'use strict' + +const parseDateTime = require('./date-time') + +// tz = timezone, l = locations, ln = lines, r = remarks +const createParseMovement = (tz, l, ln, r) => { + // todo: what is m.dirGeo? maybe the speed? + // todo: what is m.stopL? + // todo: what is m.proc? wut? + // todo: what is m.pos? + // todo: what is m.ani.dirGeo[n]? maybe the speed? + // todo: what is m.ani.proc[n]? wut? + // todo: how does m.ani.poly work? + const parseMovement = (m) => { + const res = { + direction: m.dirTxt + , line: ln[m.prodX] + , coordinates: m.pos ? { + latitude: m.pos.y / 1000000, + longitude: m.pos.x / 1000000 + } : null + , nextStops: m.stopL.map((s) => ({ + station: l[s.locX] + , departure: s.dTimeR || s.dTimeS + ? parseDateTime(tz, m.date, s.dTimeR || s.dTimeS).format() + : null + , arrival: s.aTimeR || s.aTimeS + ? parseDateTime(tz, m.date, s.aTimeR || s.aTimeS).format() + : null + })) + , frames: [] + } + + if (m.ani && Array.isArray(m.ani.mSec)) { + for (let i = 0; i < m.ani.mSec.length; i++) { + res.frames.push({ + origin: l[m.ani.fLocX[i]], + destination: l[m.ani.tLocX[i]], + t: m.ani.mSec[i] + }) + } + } + + return res + } + return parseMovement +} + +module.exports = createParseMovement diff --git a/parse/nearby.js b/parse/nearby.js new file mode 100644 index 00000000..896682c2 --- /dev/null +++ b/parse/nearby.js @@ -0,0 +1,16 @@ +'use strict' + +const parseLocation = require('./location') + +// todo: remarks +// todo: lines +// todo: what is s.pCls? +// todo: what is s.wt? +// todo: what is s.dur? +const parseNearby = (n) => { + const result = location(n) + result.distance = n.dist + return result +} + +module.exports = parseNearby diff --git a/parse/operator.js b/parse/operator.js new file mode 100644 index 00000000..a77f217b --- /dev/null +++ b/parse/operator.js @@ -0,0 +1,13 @@ +'use strict' + +const slugg = require('slugg') + +const parseOperator = (a) => { + return { + type: 'operator', + id: slugg(a.name), // todo: find a more reliable way + name: a.name + } +} + +module.exports = parseOperator diff --git a/parse/remark.js b/parse/remark.js new file mode 100644 index 00000000..d08b1500 --- /dev/null +++ b/parse/remark.js @@ -0,0 +1,7 @@ +'use strict' + +const parseRemark = (r) => { + return null // todo +} + +module.exports = parseRemark diff --git a/parse/stopover.js b/parse/stopover.js new file mode 100644 index 00000000..9a413f77 --- /dev/null +++ b/parse/stopover.js @@ -0,0 +1,23 @@ +'use strict' + +// s = stations, ln = lines, r = remarks, c = connection +const createParseStopover = (tz, s, ln, r, c) => { + const parseStopover = (st) => { + const res = { + station: s[parseInt(st.locX)] + } + if (st.aTimeR || st.aTimeS) { + const arr = parseDateTime(tz, c.date, st.aTimeR || st.aTimeS) + res.arrival = arr.format() + } + if (st.dTimeR || st.dTimeS) { + const dep = parseDateTime(tz, c.date, st.dTimeR || st.dTimeS) + res.departure = dep.format() + } + return res + } + + return parseStopover +} + +module.exports = createParseStopover