mirror of
https://github.com/public-transport/db-vendo-client.git
synced 2025-02-22 22:59:35 +02:00
enrich stations with db-hafas-stations
This commit is contained in:
parent
073d33e12f
commit
80195404bb
10 changed files with 86 additions and 58 deletions
3
api.js
3
api.js
|
@ -37,6 +37,7 @@ const config = {
|
|||
openapiSpec: true,
|
||||
logging: true,
|
||||
aboutPage: true,
|
||||
enrichStations: true,
|
||||
etags: 'strong',
|
||||
csp: 'default-src \'none\' style-src \'self\' \'unsafe-inline\' img-src https:',
|
||||
mapRouteParsers,
|
||||
|
@ -44,7 +45,7 @@ const config = {
|
|||
|
||||
|
||||
const start = async () => {
|
||||
const vendo = createClient(dbProfile, 'my-hafas-rest-api');
|
||||
const vendo = createClient(dbProfile, 'my-hafas-rest-api', config);
|
||||
const api = await createApi(vendo, config);
|
||||
|
||||
api.listen(config.port, (err) => {
|
||||
|
|
54
index.js
54
index.js
|
@ -2,6 +2,7 @@ import isObj from 'lodash/isObject.js';
|
|||
import sortBy from 'lodash/sortBy.js';
|
||||
import omit from 'lodash/omit.js';
|
||||
import distance from 'gps-distance';
|
||||
import readStations from 'db-hafas-stations'
|
||||
|
||||
import {defaultProfile} from './lib/default-profile.js';
|
||||
import {validateProfile} from './lib/validate-profile.js';
|
||||
|
@ -36,9 +37,30 @@ const validateWhen = (when, name = 'when') => {
|
|||
}
|
||||
};
|
||||
|
||||
const loadEnrichedStationData = () => new Promise((resolve, reject) => {
|
||||
const items = {}
|
||||
readStations.full()
|
||||
.on('data', (station) => {
|
||||
items[station.id] = station;
|
||||
})
|
||||
.once('end', () => {
|
||||
console.info('Loaded station index.');
|
||||
resolve(items);
|
||||
})
|
||||
.once('error', (err) => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
|
||||
const createClient = (profile, userAgent, opt = {}) => {
|
||||
profile = Object.assign({}, defaultProfile, profile);
|
||||
validateProfile(profile);
|
||||
const common = {};
|
||||
if (opt.enrichStations !== false) {
|
||||
loadEnrichedStationData().then(locations => {
|
||||
common.locations = locations;
|
||||
});
|
||||
}
|
||||
|
||||
if ('string' !== typeof userAgent) {
|
||||
throw new TypeError('userAgent must be a string');
|
||||
|
@ -87,7 +109,7 @@ const createClient = (profile, userAgent, opt = {}) => {
|
|||
|
||||
const req = profile.formatStationBoardReq({profile, opt}, station, resultsField);
|
||||
|
||||
const {res, common} = await profile.request({profile, opt}, userAgent, req);
|
||||
const {res, _} = await profile.request({profile, opt}, userAgent, req);
|
||||
|
||||
const ctx = {profile, opt, common, res};
|
||||
const results = (res[resultsField] || res.items).map(res => parse(ctx, res)); // todo sort?
|
||||
|
@ -210,7 +232,7 @@ const createClient = (profile, userAgent, opt = {}) => {
|
|||
// TODO query.numF = opt.results;
|
||||
}
|
||||
const req = profile.transformJourneysQuery({profile, opt}, query);
|
||||
const {res, common} = await profile.request({profile, opt}, userAgent, req);
|
||||
const {res, _} = await profile.request({profile, opt}, userAgent, req);
|
||||
const ctx = {profile, opt, common, res};
|
||||
const verbindungen = opt.results ? res.verbindungen.slice(0, opt.results) : res.verbindungen;
|
||||
const journeys = verbindungen
|
||||
|
@ -241,7 +263,7 @@ const createClient = (profile, userAgent, opt = {}) => {
|
|||
|
||||
const req = profile.formatRefreshJourneyReq({profile, opt}, refreshToken);
|
||||
|
||||
const {res, common} = await profile.request({profile, opt}, userAgent, req);
|
||||
const {res, _} = await profile.request({profile, opt}, userAgent, req);
|
||||
const ctx = {profile, opt, common, res};
|
||||
|
||||
return {
|
||||
|
@ -355,7 +377,7 @@ const createClient = (profile, userAgent, opt = {}) => {
|
|||
getTariff: Boolean(opt.tickets),
|
||||
};
|
||||
|
||||
const {res, common} = await profile.request({profile, opt}, userAgent, {
|
||||
const {res, _} = await profile.request({profile, opt}, userAgent, {
|
||||
cfg: {polyEnc: 'GPA'},
|
||||
meth: 'SearchOnTrip',
|
||||
req: query,
|
||||
|
@ -411,7 +433,7 @@ const createClient = (profile, userAgent, opt = {}) => {
|
|||
}, opt);
|
||||
const req = profile.formatLocationsReq({profile, opt}, query);
|
||||
|
||||
const {res, common} = await profile.request({profile, opt}, userAgent, req);
|
||||
const {res, _} = await profile.request({profile, opt}, userAgent, req);
|
||||
|
||||
|
||||
const ctx = {profile, opt, common, res};
|
||||
|
@ -436,7 +458,7 @@ const createClient = (profile, userAgent, opt = {}) => {
|
|||
|
||||
const req = profile.formatStopReq({profile, opt}, stop);
|
||||
|
||||
const {res, common} = await profile.request({profile, opt}, userAgent, req);
|
||||
const {res, _} = await profile.request({profile, opt}, userAgent, req);
|
||||
if (!res || !Array.isArray(res.locL) || !res.locL[0]) {
|
||||
throw new HafasError('invalid response, expected locL[0]', null, {
|
||||
// This problem occurs on invalid input. 🙄
|
||||
|
@ -462,7 +484,7 @@ const createClient = (profile, userAgent, opt = {}) => {
|
|||
}, opt);
|
||||
|
||||
const req = profile.formatNearbyReq({profile, opt}, location);
|
||||
const {res, common} = await profile.request({profile, opt}, userAgent, req);
|
||||
const {res, _} = await profile.request({profile, opt}, userAgent, req);
|
||||
|
||||
const ctx = {profile, opt, common, res};
|
||||
const results = res.map(loc => {
|
||||
|
@ -493,7 +515,7 @@ const createClient = (profile, userAgent, opt = {}) => {
|
|||
|
||||
const req = profile.formatTripReq({profile, opt}, id);
|
||||
|
||||
const {res, common} = await profile.request({profile, opt}, userAgent, req);
|
||||
const {res, _} = await profile.request({profile, opt}, userAgent, req);
|
||||
const ctx = {profile, opt, common, res};
|
||||
|
||||
const trip = profile.parseTrip(ctx, res.journey);
|
||||
|
@ -575,7 +597,7 @@ const createClient = (profile, userAgent, opt = {}) => {
|
|||
}
|
||||
req.jnyFltrL = [...req.jnyFltrL, ...opt.additionalFilters];
|
||||
|
||||
const {res, common} = await profile.request({profile, opt}, userAgent, {
|
||||
const {res, _} = await profile.request({profile, opt}, userAgent, {
|
||||
cfg: {polyEnc: 'GPA'},
|
||||
meth: 'JourneyMatch',
|
||||
req,
|
||||
|
@ -635,7 +657,7 @@ const createClient = (profile, userAgent, opt = {}) => {
|
|||
|
||||
const req = profile.formatRadarReq({profile, opt}, north, west, south, east);
|
||||
|
||||
const {res, common} = await profile.request({profile, opt}, userAgent, req);
|
||||
const {res, _} = await profile.request({profile, opt}, userAgent, req);
|
||||
if (!Array.isArray(res.jnyL)) {
|
||||
return [];
|
||||
}
|
||||
|
@ -669,7 +691,7 @@ const createClient = (profile, userAgent, opt = {}) => {
|
|||
|
||||
const req = profile.formatReachableFromReq({profile, opt}, address);
|
||||
|
||||
const {res, common} = await profile.request({profile, opt}, userAgent, req);
|
||||
const {res, _} = await profile.request({profile, opt}, userAgent, req);
|
||||
if (!Array.isArray(res.posL)) {
|
||||
throw new HafasError('invalid response, expected posL[0]', null, {
|
||||
shouldRetry: true,
|
||||
|
@ -724,9 +746,7 @@ const createClient = (profile, userAgent, opt = {}) => {
|
|||
}
|
||||
|
||||
const req = profile.formatRemarksReq({profile, opt});
|
||||
const {
|
||||
res, common,
|
||||
} = await profile.request({profile, opt}, userAgent, req);
|
||||
const {res, _} = await profile.request({profile, opt}, userAgent, req);
|
||||
|
||||
const ctx = {profile, opt, common, res};
|
||||
const remarks = (res.msgL || [])
|
||||
|
@ -746,9 +766,7 @@ const createClient = (profile, userAgent, opt = {}) => {
|
|||
}
|
||||
|
||||
const req = profile.formatLinesReq({profile, opt}, query);
|
||||
const {
|
||||
res, common,
|
||||
} = await profile.request({profile, opt}, userAgent, req);
|
||||
const {res, _} = await profile.request({profile, opt}, userAgent, req);
|
||||
|
||||
if (!Array.isArray(res.lineL)) {
|
||||
return [];
|
||||
|
@ -783,7 +801,7 @@ const createClient = (profile, userAgent, opt = {}) => {
|
|||
...opt,
|
||||
};
|
||||
|
||||
const {res, common} = await profile.request({profile, opt}, userAgent, {
|
||||
const {res, _} = await profile.request({profile, opt}, userAgent, {
|
||||
meth: 'ServerInfo',
|
||||
req: {
|
||||
getVersionInfo: opt.versionInfo,
|
||||
|
|
5
package-lock.json
generated
5
package-lock.json
generated
|
@ -1,11 +1,11 @@
|
|||
{
|
||||
"name": "db-vendo-client",
|
||||
"name": "@public-transport/db-vendo-client",
|
||||
"version": "6.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "db-vendo-client",
|
||||
"name": "@public-transport/db-vendo-client",
|
||||
"version": "6.0.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
|
@ -14,6 +14,7 @@
|
|||
"content-type": "^1.0.4",
|
||||
"create-hash": "^1.2.0",
|
||||
"cross-fetch": "^4.0.0",
|
||||
"db-hafas-stations": "^1.0.0",
|
||||
"google-polyline": "^1.0.3",
|
||||
"gps-distance": "0.0.4",
|
||||
"https-proxy-agent": "^7.0.0",
|
||||
|
|
|
@ -60,6 +60,7 @@
|
|||
"content-type": "^1.0.4",
|
||||
"create-hash": "^1.2.0",
|
||||
"cross-fetch": "^4.0.0",
|
||||
"db-hafas-stations": "^1.0.0",
|
||||
"google-polyline": "^1.0.3",
|
||||
"gps-distance": "0.0.4",
|
||||
"https-proxy-agent": "^7.0.0",
|
||||
|
|
|
@ -7,7 +7,7 @@ const ADDRESS = 'ADR';
|
|||
const leadingZeros = /^0+/;
|
||||
|
||||
const parseLocation = (ctx, l) => {
|
||||
const {profile} = ctx;
|
||||
const {profile, common} = ctx;
|
||||
|
||||
if (!l) {
|
||||
return null;
|
||||
|
@ -29,8 +29,8 @@ const parseLocation = (ctx, l) => {
|
|||
}
|
||||
|
||||
if (l.type === STATION || l.extId || l.evaNumber || l.evaNo || lid.A == 1) {
|
||||
const stop = {
|
||||
type: 'stop', // TODO station
|
||||
let stop = {
|
||||
type: 'station',
|
||||
id: res.id,
|
||||
name: name,
|
||||
location: 'number' === typeof res.latitude
|
||||
|
@ -43,9 +43,16 @@ const parseLocation = (ctx, l) => {
|
|||
stop.products = profile.parseProductsBitmask(ctx, l.products);
|
||||
}
|
||||
|
||||
if (common && common.locations && common.locations[stop.id]) {
|
||||
delete stop.type;
|
||||
stop = {
|
||||
...common.locations[stop.id],
|
||||
...stop,
|
||||
};
|
||||
}
|
||||
|
||||
// TODO isMeta
|
||||
// TODO station
|
||||
// TODO entrances, lines, transitAuthority, dhid, ids
|
||||
// TODO entrances, lines
|
||||
return stop;
|
||||
}
|
||||
|
||||
|
|
28
test/fixtures/db-arrivals.js
vendored
28
test/fixtures/db-arrivals.js
vendored
|
@ -2,7 +2,7 @@ const dbArrivals = [
|
|||
{
|
||||
tripId: '20241208-c33bba6c-a73a-3eec-8a64-76356c922ece',
|
||||
stop: {
|
||||
type: 'stop',
|
||||
type: 'station',
|
||||
id: '8089100',
|
||||
name: 'Berlin Jungfernheide (S)',
|
||||
location: null,
|
||||
|
@ -34,7 +34,7 @@ const dbArrivals = [
|
|||
},
|
||||
],
|
||||
origin: {
|
||||
type: 'stop',
|
||||
type: 'station',
|
||||
id: '8089118',
|
||||
name: 'Berlin Beusselstraße',
|
||||
location: null,
|
||||
|
@ -44,7 +44,7 @@ const dbArrivals = [
|
|||
{
|
||||
tripId: '20241208-89eeca5a-1768-3713-894a-dd088977f42b',
|
||||
stop: {
|
||||
type: 'stop',
|
||||
type: 'station',
|
||||
id: '730985',
|
||||
name: 'Jungfernheide Bahnhof (S+U), Berlin',
|
||||
location: null,
|
||||
|
@ -82,7 +82,7 @@ const dbArrivals = [
|
|||
},
|
||||
],
|
||||
origin: {
|
||||
type: 'stop',
|
||||
type: 'station',
|
||||
id: '732218',
|
||||
name: 'Rudow (U), Berlin',
|
||||
location: null,
|
||||
|
@ -92,7 +92,7 @@ const dbArrivals = [
|
|||
{
|
||||
tripId: '20241208-2dc4f2d4-a1e1-3bbf-a607-98ff71c927d0',
|
||||
stop: {
|
||||
type: 'stop',
|
||||
type: 'station',
|
||||
id: '730985',
|
||||
name: 'Jungfernheide Bahnhof (S+U), Berlin',
|
||||
location: null,
|
||||
|
@ -130,7 +130,7 @@ const dbArrivals = [
|
|||
},
|
||||
],
|
||||
origin: {
|
||||
type: 'stop',
|
||||
type: 'station',
|
||||
id: '730993',
|
||||
name: 'Goerdelersteg, Berlin',
|
||||
location: null,
|
||||
|
@ -140,7 +140,7 @@ const dbArrivals = [
|
|||
{
|
||||
tripId: '20241208-6fa6d37c-a1c0-3f84-bdac-0424705bffaf',
|
||||
stop: {
|
||||
type: 'stop',
|
||||
type: 'station',
|
||||
id: '8089100',
|
||||
name: 'Berlin Jungfernheide (S)',
|
||||
location: null,
|
||||
|
@ -172,7 +172,7 @@ const dbArrivals = [
|
|||
},
|
||||
],
|
||||
origin: {
|
||||
type: 'stop',
|
||||
type: 'station',
|
||||
id: '8089118',
|
||||
name: 'Berlin Beusselstraße',
|
||||
location: null,
|
||||
|
@ -182,7 +182,7 @@ const dbArrivals = [
|
|||
{
|
||||
tripId: '20241208-c4abf007-d667-3bf1-87a8-2d1b153c014d',
|
||||
stop: {
|
||||
type: 'stop',
|
||||
type: 'station',
|
||||
id: '730985',
|
||||
name: 'Jungfernheide Bahnhof (S+U), Berlin',
|
||||
location: null,
|
||||
|
@ -220,7 +220,7 @@ const dbArrivals = [
|
|||
},
|
||||
],
|
||||
origin: {
|
||||
type: 'stop',
|
||||
type: 'station',
|
||||
id: '732218',
|
||||
name: 'Rudow (U), Berlin',
|
||||
location: null,
|
||||
|
@ -230,7 +230,7 @@ const dbArrivals = [
|
|||
{
|
||||
tripId: '20241208-c8b6e3e4-6acb-3237-b89e-1fca72497555',
|
||||
stop: {
|
||||
type: 'stop',
|
||||
type: 'station',
|
||||
id: '8089100',
|
||||
name: 'Berlin Jungfernheide (S)',
|
||||
location: null,
|
||||
|
@ -262,7 +262,7 @@ const dbArrivals = [
|
|||
},
|
||||
],
|
||||
origin: {
|
||||
type: 'stop',
|
||||
type: 'station',
|
||||
id: '8089118',
|
||||
name: 'Berlin Beusselstraße',
|
||||
location: null,
|
||||
|
@ -272,7 +272,7 @@ const dbArrivals = [
|
|||
{
|
||||
tripId: '20241208-f9d83ab7-d603-3344-87c0-a65ecf0f8524',
|
||||
stop: {
|
||||
type: 'stop',
|
||||
type: 'station',
|
||||
id: '730985',
|
||||
name: 'Jungfernheide Bahnhof (S+U), Berlin',
|
||||
location: null,
|
||||
|
@ -310,7 +310,7 @@ const dbArrivals = [
|
|||
},
|
||||
],
|
||||
origin: {
|
||||
type: 'stop',
|
||||
type: 'station',
|
||||
id: '731176',
|
||||
name: 'Rathaus Spandau (S+U), Berlin',
|
||||
location: null,
|
||||
|
|
12
test/fixtures/db-departures-regio-guide.js
vendored
12
test/fixtures/db-departures-regio-guide.js
vendored
|
@ -2,7 +2,7 @@ const dbDepartures = [
|
|||
{
|
||||
tripId: '20241212-d1494ce6-1a01-38de-bf84-c0bceb12f503',
|
||||
stop: {
|
||||
type: 'stop',
|
||||
type: 'station',
|
||||
id: '8000365',
|
||||
name: 'Dombühl',
|
||||
location: null,
|
||||
|
@ -28,7 +28,7 @@ const dbDepartures = [
|
|||
remarks: [],
|
||||
origin: null,
|
||||
destination: {
|
||||
type: 'stop',
|
||||
type: 'station',
|
||||
id: '8000284',
|
||||
name: 'Nürnberg Hbf',
|
||||
location: null,
|
||||
|
@ -37,7 +37,7 @@ const dbDepartures = [
|
|||
{
|
||||
tripId: '20241212-abd01ce0-cca3-3759-aa4b-410ea4d0a720',
|
||||
stop: {
|
||||
type: 'stop',
|
||||
type: 'station',
|
||||
id: '682943',
|
||||
name: 'Bahnhof, Dombühl',
|
||||
location: null,
|
||||
|
@ -63,7 +63,7 @@ const dbDepartures = [
|
|||
remarks: [],
|
||||
origin: null,
|
||||
destination: {
|
||||
type: 'stop',
|
||||
type: 'station',
|
||||
id: '676542',
|
||||
name: 'Gymnasium, Dinkelsbühl',
|
||||
location: null,
|
||||
|
@ -72,7 +72,7 @@ const dbDepartures = [
|
|||
{
|
||||
tripId: '20241212-ab6272a5-4bf6-32c1-9344-b47e1fc49eeb',
|
||||
stop: {
|
||||
type: 'stop',
|
||||
type: 'station',
|
||||
id: '682943',
|
||||
name: 'Bahnhof, Dombühl',
|
||||
location: null,
|
||||
|
@ -98,7 +98,7 @@ const dbDepartures = [
|
|||
remarks: [],
|
||||
origin: null,
|
||||
destination: {
|
||||
type: 'stop',
|
||||
type: 'station',
|
||||
id: '683407',
|
||||
name: 'Bahnhof, Rothenburg ob der Tauber',
|
||||
location: null,
|
||||
|
|
12
test/fixtures/db-journey.js
vendored
12
test/fixtures/db-journey.js
vendored
|
@ -3,7 +3,7 @@ const dbJourney = {
|
|||
legs: [
|
||||
{
|
||||
origin: {
|
||||
type: 'stop',
|
||||
type: 'station',
|
||||
id: '8000207',
|
||||
name: 'Köln Hbf',
|
||||
location: {
|
||||
|
@ -14,7 +14,7 @@ const dbJourney = {
|
|||
},
|
||||
},
|
||||
destination: {
|
||||
type: 'stop',
|
||||
type: 'station',
|
||||
id: '8003368',
|
||||
name: 'Köln Messe/Deutz',
|
||||
location: {
|
||||
|
@ -167,7 +167,7 @@ const dbJourney = {
|
|||
},
|
||||
{
|
||||
origin: {
|
||||
type: 'stop',
|
||||
type: 'station',
|
||||
id: '8003368',
|
||||
name: 'Köln Messe/Deutz',
|
||||
location: {
|
||||
|
@ -178,7 +178,7 @@ const dbJourney = {
|
|||
},
|
||||
},
|
||||
destination: {
|
||||
type: 'stop',
|
||||
type: 'station',
|
||||
id: '8073368',
|
||||
name: 'Köln Messe/Deutz Gl.11-12',
|
||||
location: {
|
||||
|
@ -200,7 +200,7 @@ const dbJourney = {
|
|||
},
|
||||
{
|
||||
origin: {
|
||||
type: 'stop',
|
||||
type: 'station',
|
||||
id: '8073368',
|
||||
name: 'Köln Messe/Deutz Gl.11-12',
|
||||
location: {
|
||||
|
@ -211,7 +211,7 @@ const dbJourney = {
|
|||
},
|
||||
},
|
||||
destination: {
|
||||
type: 'stop',
|
||||
type: 'station',
|
||||
id: '8000284',
|
||||
name: 'Nürnberg Hbf',
|
||||
location: {
|
||||
|
|
8
test/fixtures/db-refresh-journey.js
vendored
8
test/fixtures/db-refresh-journey.js
vendored
|
@ -4,7 +4,7 @@ const dbJourney = {
|
|||
legs: [
|
||||
{
|
||||
origin: {
|
||||
type: 'stop',
|
||||
type: 'station',
|
||||
id: '8011160',
|
||||
name: 'Berlin Hbf',
|
||||
location: {
|
||||
|
@ -15,7 +15,7 @@ const dbJourney = {
|
|||
},
|
||||
},
|
||||
destination: {
|
||||
type: 'stop',
|
||||
type: 'station',
|
||||
id: '8000080',
|
||||
name: 'Dortmund Hbf',
|
||||
location: {
|
||||
|
@ -87,7 +87,7 @@ const dbJourney = {
|
|||
},
|
||||
{
|
||||
origin: {
|
||||
type: 'stop',
|
||||
type: 'station',
|
||||
id: '8000080',
|
||||
name: 'Dortmund Hbf',
|
||||
location: {
|
||||
|
@ -98,7 +98,7 @@ const dbJourney = {
|
|||
},
|
||||
},
|
||||
destination: {
|
||||
type: 'stop',
|
||||
type: 'station',
|
||||
id: '8000207',
|
||||
name: 'Köln Hbf',
|
||||
location: {
|
||||
|
|
|
@ -100,7 +100,7 @@ tap.test('parses a stop correctly', (t) => {
|
|||
|
||||
const stop = parse(ctx, input);
|
||||
t.same(stop, {
|
||||
type: 'stop',
|
||||
type: 'station',
|
||||
id: '8012622',
|
||||
name: 'Perleberg',
|
||||
location: {
|
||||
|
@ -133,7 +133,7 @@ tap.test('falls back to coordinates from `lid', (t) => {
|
|||
|
||||
const stop = parse(ctx, input);
|
||||
t.same(stop, {
|
||||
type: 'stop',
|
||||
type: 'station',
|
||||
id: '683407',
|
||||
name: 'Bahnhof, Rothenburg ob der Tauber',
|
||||
location: {
|
||||
|
|
Loading…
Add table
Reference in a new issue