diff --git a/p/bvg/example.js b/p/bvg/example.js new file mode 100644 index 00000000..5a79930f --- /dev/null +++ b/p/bvg/example.js @@ -0,0 +1,37 @@ +'use strict' + +const createClient = require('../..') +const vbbProfile = require('.') + +const client = createClient(vbbProfile, 'hafas-client-example') + +// Hauptbahnhof to Charlottenburg +client.journeys('900000003201', '900000024101', {results: 1, polylines: true}) +// client.departures('900000013102', {duration: 1}) +// client.arrivals('900000013102', {duration: 10, stationLines: true}) +// client.locations('Alexanderplatz', {results: 2}) +// client.station('900000042101', {stationLines: true}) // Spichernstr +// client.nearby({ +// type: 'location', +// latitude: 52.5137344, +// longitude: 13.4744798 +// }, {distance: 60}) +// client.radar({ +// north: 52.52411, +// west: 13.41002, +// south: 52.51942, +// east: 13.41709 +// }, {results: 10}) + +// .then(([journey]) => { +// const leg = journey.legs[0] +// return client.trip(leg.id, leg.line.name, {polyline: true}) +// }) + +// .then(([journey]) => { +// return client.refreshJourney(journey.refreshToken, {stopovers: true, remarks: true}) +// }) +.then((data) => { + console.log(require('util').inspect(data, {depth: null})) +}) +.catch(console.error) diff --git a/p/bvg/index.js b/p/bvg/index.js new file mode 100644 index 00000000..b3057a44 --- /dev/null +++ b/p/bvg/index.js @@ -0,0 +1,112 @@ +'use strict' + +const shorten = require('vbb-short-station-name') +const {to12Digit, to9Digit} = require('vbb-translate-ids') +const parseLineName = require('vbb-parse-line') +const getStations = require('vbb-stations') + +const _createParseLine = require('../../parse/line') +const _parseLocation = require('../../parse/location') +const _createParseDeparture = require('../../parse/departure') +const _formatStation = require('../../format/station') + +const products = require('./products') + +const transformReqBody = (body) => { + body.client = {type: 'IPA', id: 'BVG', name: 'FahrInfo', v: '4070700'} + body.ext = 'BVG.1' + body.ver = '1.15' // todo: 1.16 with `mic` and `mac` query params + body.auth = {type: 'AID', aid: '1Rxs112shyHLatUX4fofnmdxK'} + + return body +} + +const createParseLine = (profile, opt, data) => { + const parseLine = _createParseLine(profile, opt, data) + + const parseLineWithMoreDetails = (l) => { + const res = parseLine(l) + + res.name = l.name.replace(/^(bus|tram)\s+/i, '') + const details = parseLineName(res.name) + res.symbol = details.symbol + res.nr = details.nr + res.metro = details.metro + res.express = details.express + res.night = details.night + + return res + } + return parseLineWithMoreDetails +} + +const parseLocation = (profile, opt, data, l) => { + const res = _parseLocation(profile, opt, data, l) + + if (res.type === 'stop' || res.type === 'station') { + res.name = shorten(res.name) + res.id = to12Digit(res.id) + if (!res.location.latitude || !res.location.longitude) { + const [s] = getStations(res.id) + if (s) Object.assign(res.location, s.location) + } + } + return res +} + +const createParseDeparture = (profile, opt, data) => { + const parseDeparture = _createParseDeparture(profile, opt, data) + + const ringbahnClockwise = /^ringbahn s\s?41$/i + const ringbahnAnticlockwise = /^ringbahn s\s?42$/i + const parseDepartureRenameRingbahn = (j) => { + const res = parseDeparture(j) + + if (res.line && res.line.product === 'suburban') { + const d = res.direction && res.direction.trim() + if (ringbahnClockwise.test(d)) res.direction = 'Ringbahn S41 ⟳' + else if (ringbahnAnticlockwise.test(d)) res.direction = 'Ringbahn S42 ⟲' + } + + return res + } + + return parseDepartureRenameRingbahn +} + +const validIBNR = /^\d+$/ +const formatStation = (id) => { + if ('string' !== typeof id) throw new Error('station ID must be a string.') + const l = id.length + if ((l !== 7 && l !== 9 && l !== 12) || !validIBNR.test(id)) { + throw new Error('station ID must be a valid IBNR.') + } + // BVG has some 7-digit stations. We don't convert them to 12 digits, + // because it only recognizes in the 7-digit format. see derhuerst/vbb-hafas#22 + if (l !== 7) id = to9Digit(id) + return _formatStation(id) +} + +// todo: adapt/extend `vbb-parse-ticket` to support the BVG markup + +const bvgProfile = { + locale: 'de-DE', + timezone: 'Europe/Berlin', + endpoint: 'https://bvg-apps.hafas.de/bin/mgate.exe', + + transformReqBody, + + products, + + parseStationName: shorten, + parseLocation, + parseLine: createParseLine, + parseDeparture: createParseDeparture, + + formatStation, + + trip: true, + radar: true +} + +module.exports = bvgProfile diff --git a/p/bvg/products.js b/p/bvg/products.js new file mode 100644 index 00000000..6d091247 --- /dev/null +++ b/p/bvg/products.js @@ -0,0 +1,60 @@ +'use strict' + +module.exports = [ + { + id: 'suburban', + mode: 'train', + bitmasks: [1], + name: 'S-Bahn', + short: 'S', + default: true + }, + { + id: 'subway', + mode: 'train', + bitmasks: [2], + name: 'U-Bahn', + short: 'U', + default: true + }, + { + id: 'tram', + mode: 'train', + bitmasks: [4], + name: 'Tram', + short: 'T', + default: true + }, + { + id: 'bus', + mode: 'bus', + bitmasks: [8], + name: 'Bus', + short: 'B', + default: true + }, + { + id: 'ferry', + mode: 'watercraft', + bitmasks: [16], + name: 'Fähre', + short: 'F', + default: true + }, + { + id: 'express', + mode: 'train', + bitmasks: [32], + name: 'IC/ICE', + short: 'E', + default: true + }, + { + id: 'regional', + mode: 'train', + bitmasks: [64], + name: 'RB/RE', + short: 'R', + default: true + } +] diff --git a/p/bvg/readme.md b/p/bvg/readme.md new file mode 100644 index 00000000..35b4b89a --- /dev/null +++ b/p/bvg/readme.md @@ -0,0 +1,22 @@ +# VBB profile for `hafas-client` + +[*Verkehrsverbund Berlin-Brandenburg (VBB)*](https://en.wikipedia.org/wiki/Verkehrsverbund_Berlin-Brandenburg) is a group of public transport companies, running the public transport network in [Berlin](https://en.wikipedia.org/wiki/Berlin). This profile adds *VBB*-specific customizations to `hafas-client`. Consider using [`vbb-hafas`](https://github.com/derhuerst/vbb-hafas#vbb-hafas), to always get the customized client right away. + +## Usage + +```js +const createClient = require('hafas-client') +const vbbProfile = require('hafas-client/p/vbb') + +// create a client with VBB profile +const client = createClient(vbbProfile) +``` + + +## Customisations + +- parses *VBB*-specific products (such as *X-Bus*) +- strips parts from station names that are unnecessary in the Berlin context +- parses line names to give more information (e.g. "Is it an express bus?") +- parses *VBB*-specific tickets +- renames *Ringbahn* line names to contain `⟳` and `⟲`