enrich stations with db-hafas-stations

This commit is contained in:
Traines 2024-12-18 00:08:52 +00:00
parent 073d33e12f
commit 80195404bb
10 changed files with 86 additions and 58 deletions

3
api.js
View file

@ -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) => {

View file

@ -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
View file

@ -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",

View file

@ -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",

View file

@ -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;
}

View file

@ -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,

View file

@ -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,

View file

@ -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: {

View file

@ -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: {

View file

@ -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: {